Can 'getLastError' return objects in its 'pipeline' output?

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.

Flow Service

Java ‘disconnect’ Service

import org.apache.commons.net.telnet.TelnetClient;
...
	public static final void disconnect(IData pipeline) throws ServiceException {
		// pipeline
		IDataCursor pipelineCursor = pipeline.getCursor();
>>>		TelnetClient telnetSession = (TelnetClient) IDataUtil.get(pipelineCursor, "telnetSession"); <<< 
		try {
			// Disconnect telnet session
			telnetSession.disconnect();
		} catch (Exception e) 

Java Exception

java.lang.ClassCastException: java.lang.String cannot be cast to org.apache.commons.net.telnet.TelnetClient
	at CECommon.utils.telnet.disconnect(telnet.java:114)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
...

The problem seems to lie with pub.flow:getLastError mapping a string - not an object - into telnetSession in the mapping below.
image

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?

Hi @Sonam_Chauhan ,

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.

Thanks,
Sree

1 Like

Hi @Sonam_Chauhan
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
         pub.flow:getLastError()

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

-NP

Thanks @Sreekanth_Siddapur_Channakeshava. I repeated my test and something is definitely amiss. Let me try obtaining a simple replication package I can share.

Note, I am using a different Try/Catch structure than what @Nagendra_Prasad mentions below.

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
		<Invoke getLastError>
		<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 :slight_smile:

Thanks, @Sonam_Chauhan , will wait for the test package to check it and provide you with some inputs.

Regards,
Sree

1 Like

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).

-NP

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:
image

Same telnetSession object under lastError/pipeline:

I’m seeking approval for releasing the replication code here. In the meantime, I’ve opened a ticket with SAG. I plan to update how this goes

1 Like

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.

-NP

Haha. No, we do not have the latest fixes, though we are pretty current. The last time we updated a core fix, it broke an obscure adapter. :smiley: … so we have to be careful with patching.

I intend to update outcome of the support ticket here. I have a feeling the workaround may be to do object initialisation earlier - before the try block.

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.

1 Like

@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:
image

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

SAG are still investigating the SI.

CEExceptionTest.zip (303.7 KB)

Thanks, @Sonam_Chauhan - If you have already raised an SI and is under investigation then we could wait I guess :slight_smile:

Regards,
sree

Yes of course. The support person helping me reached out internally - I plan to update here when a better picture forms.

1 Like

webMethods support came back today:

“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.

regards,
John.

3 Likes

Thanks @John_Carter4 that’s exactly what I did, using a two-step try/catch:

Try (OUTER)
---
   Connect
   Try (INNER)
       Use connection
       End connection
   Catch (INNER)
       End connection (if it exists)
---
Catch (OUTER)
     Throw exception for retry to UM

The OUTER Try/Catch is actually a trigger processing service (hence the throwExceptionForRetry). The INNER Try/Catch is a different service (indicated by the ‘—’).

This is similar to the solution Rupinder Singh and others noted in this old post . Thanks a lot for pointing me again in the right direction.

Interesting! Do you think this issue (not being able to access non-serializable ‘Try’ objects in ‘Catch’) is a limitation of pub.flow:getLastError, or a more generic Java limitation? Any thoughts?

The issue workaround is great, but obviously not ideal for code clarity.

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.
regards,
John.

Thanks very much for the insight @John_Carter4