I use IS 10.1 to control a telnet session, using functionality from the Apache Commons TelnetClient library. The approach I use is to write Java wrapper services for key telnet actions (connect, read, write, disconnect etc). I then call the Java telnet services from Flow code. The initial call to the connect service returns a org.apache.commons.net.telnet.TelnetClient object. I pass this object to other Java services (read, write, disconnect) via Flow code. On the whole, this approach works pretty well.
But this approach fails in the catch block. There, I call pub.flow:getLastError and try to obtain the telnet session object from lastError/pipeline. This is so I can attempt a telnet disconnection when an exception occurs. But in the catch block, the telnet session retrieved is no longer an object - it’s a string! This causes the disconnect Java service to throw a ClassCastException at ‘>>>’ in Java code below.
java.lang.ClassCastException: java.lang.String cannot be cast to org.apache.commons.net.telnet.TelnetClient
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The problem seems to lie with pub.flow:getLastError mapping a string - not an object - into telnetSession in the mapping below.
In the ‘Try’ block, the Java disconnect service works fine with the telnetSession object.
Any thoughts how I can obtain the telnet session object in the ‘Catch’ block as an object, not a string?
Update: just saw this old post where Rupinder Singh and others seem to have stumbled across the same problem. But that was 2002! Is the behavior still the same two decades on?
I quickly tried this scenario and I did get the expected object type from the getLastError and its pipeline. Seems like something is a miss, maybe if you can attach the package you are using, will give it a try and update if anything needs to be changed.
I’m assuming that you have implemented the TRY CATCH in the following way
SEQUENCE ( Exit on failure)
SEQUENCE(Exit on success) - TRY
SEQUENCE ( Exit on Done) - CATCH
As long you have the TelnetClient object initialized before entering the first SEQUENCE ( Exit on failure), the call to getLastError should have the TelnetClient as an object and not a string.
As Sree requested , please provide more details about your flow .
Additionally , starting 10.3 , there are flow steps TRY CATCH and FINALLY , which can be used to emulate a Try Catch blocks directly , Please refer guide here- TRY CATCH FINALLY in 10.3
Thanks @Nagendra_Prasad. No – I am using a very different Try/Catch construct. Here’s what I do:
Try/Catch: EXIT on SUCCESS
Try: EXIT on FAILURE
<Create TelnetClient Java object>
Catch: EXIT on DONE
<Access TelnetClient Java object in /lastError/pipeline>
How does your Try/Catch construct work? Since your ‘TRY’ is EXIT on success, won’t it return after the first successful step?
In my code, getLastError’s lastError/pipeline does not properly return a custom Java object instantiated within the ‘Try’ block. Strings and other IData-based structures appear fine though. It’s just the custom Java object that turns into a string.
I’ll try to publish a simple replication testcase.
Good to see SAG making headway with a native TRY/CATCH/FINALLY in 10.3. It should help avoid bugs. Maybe one day I’ll also see some simple resolution of the infamous tranformers cannot loop over list elements error
For the sake of a simple repro , Prior to entering the outer SEQUENCE(TRY/CATCH), I created a simple Date object,
I had the first step in the TRY SEQUENCE fail(by means of a divideInts invocation ) , to check if there can be a Object in the get Last Error of the CATCH SEQUENCE.
In your case , I see that you have a custom Java object instantiated within the ‘Try’ block, That could be the difference, You could try having a reference to your Telnetclient object outside the TRY and see if it is retained as an object. (not a recommendation , just wondering if that could be the difference).
Thanks for the suggestion @Nagendra_Prasad - I tried it and it makes no difference. There’s still a ClassCastException thrown when attempting to use the lastError/pipeline/telnetSession object.
When I implement your suggestion, I get two pipeline variables at runtime: a ‘top-level’ telnetSession variable, and the variable in the lastError/pipeline. Both should refer to the same object.
Interestingly, both variables are displayed as strings in the debugger at runtime - not objects. But the ‘top-level’ variable is really an object (I know since it can be used to successfully disconnect the session). The variable in lastError/pipeline is a string - using it to disconnect the session throws the ClassCastException.
Top-level telnetSession object:
Same telnetSession object under lastError/pipeline:
Very interesting that you are seeing two variables and that both are strings, support ticket seems like the right way to go, I’m assuming you already have the latest fixes. And if you have newer versions, maybe you can give this a try in the new versions.
Not sure if this is helpful, but we’ve found that the new TRY/CATCH steps differ sufficiently in behavior from the traditional use of nested SEQUENCE that we now avoid TRY/CATCH. This is not to say no one should ever use TRY/CATCH but to advise that one will need to learn the nuances of TRY/CATCH behavior and should not assume those behave the same as the SEQUENCE approach.
That said, I would offer that the (very Java-like) behavior of TRY/CATCH/FINALLY is likely something that many do not need. Being able to segregate different exception types and such has been, IME, relatively rare. The coarse-grained nature of “any exception” has been largely sufficient for us.
@reamon - Thanks as always! I haven’t used the new TRY/CATCH yet and will keep your advice in mind.
@Nagendra_Prasad , @Sreekanth_Siddapur_Channakeshava - I’ve attached a replication package. The service CEExceptionTest:test replicates the problem. You point the service to a host running a telnet service on port 23, then get the following output:
Note, the package includes Apache Commons TelnetClient library functionality via this JAR file in the package’s ‘code/jars’ folder: CECommon/code/jars/commons-net-3.8.0.jar
“The problem is that the Object in this case is not Serializable, and only serializable objects are retained. From RnD, it is not a new issue and this limitation must have been there for a long time.”
I’ve asked them to add this to documentation on pub.flow:getLastError.
The object in question is an instance of TelnetClient (org.apache.commons.net.telnet.TelnetClient) from the Apache Commons library. Like other socket-based classes, this doesn’t implement serializable. But that means means network sockets that have long timeouts set stay connected at the OS/network level (at least that’s what I recall from testing).
You should establish the connection before the try/catch body, that way the connection will still be in the pipeline and you will be able to disconnect. This was the same issue with java until they reinvented the try/catch to include an init step.
If you want to trap connection errors then you will need two try/catch’s. One that wraps the connection and an embedded one for the processing.
This is an issue related to the pipeline. The lastError object is a copy of the pipeline as of when the error triggered and not simply a reference to the current pipeline, otherwise it would be just the same as the current pipeline post sequence. Unfortunately, it is this copying that breaks as we are reliant on the objects being serialisable which is often not the case. That’s why I never rely on the pipeline embedded in the getLastError other than for logging purposes.