Tuesday, May 7, 2013

How to Invoke Java from PeopleCode to Write to a TCP Socket

I found several examples in Java of how to write to a TCP server socket, but I found none of them that show how to do it with PeopleCode calling Java.  Here's an example that I put together, which works great, so long as you have a server that is listening for your connection.  If you don't have a server, I'll show you how to use the Linux "nc" command to create one.


Here's the code that I attached to a button's record fieldchange event on my PeopleSoft component/page.  I'll explain below:
&MYFILE = GetFile("/tmp/mysocket.log", "a", %FilePath_Absolute);
&MYFILE.WriteLine("socket begin...");

/* Create InetSocketAddress object in order that timeout can be set on socket connect() */
Local JavaObject &jInetSocketAddress = CreateJavaObject("java.net.InetSocketAddress", "myserver.something.com",1234);
&MYFILE.WriteLine("InetSocketAddress created");
/* Create a socket */
Local JavaObject &jSocket = CreateJavaObject("java.net.Socket");
&MYFILE.WriteLine("socket created");
/* Connect to server/port, with timeout specified in milliseconds */
&jSocket.connect(&jInetSocketAddress, 5000);
&MYFILE.WriteLine("socket connected");
/* Connect to the Output, and Create a reader to receive response back from the listener */
Local JavaObject &jDataOutputStream = CreateJavaObject("java.io.DataOutputStream", &jSocket.getOutputStream());
&MYFILE.WriteLine("output stream created");
Local JavaObject &jInputStreamReader = CreateJavaObject("java.io.InputStreamReader", &jSocket.getInputStream());
Local JavaObject &jBufferedReader = CreateJavaObject("java.io.BufferedReader", &jInputStreamReader);
&MYFILE.WriteLine("buffered reader created");

&jDataOutputStream.writeBytes("This is a test.");
&MYFILE.WriteLine("output written to stream");
Local string &strListenerResponse = &jBufferedReader.readLine();
&MYFILE.WriteLine("listener response [" | &strListenerResponse | "]");
Warning ("Response: " | &strListenerResponse);
/* close the output, the input, and the socket. */
&jDataOutputStream.close();
&MYFILE.WriteLine("output stream closed");
&jBufferedReader.close();
&MYFILE.WriteLine("buffered reader closed");
&jSocket.close();
&MYFILE.WriteLine("socket closed");
&MYFILE.close();
For me, writing the output to a file on the development server and then doing a "tail -f" on that file was very helpful for understanding what was working and what was not.

In the example above, I've specified that I wanted to connect to server "myserver.something.com" on port "1234".  Just make sure that you provide a valid server name for your environment, and that the port you want to use is not being used already.  Use  the linux command "netstat -tnlp" to see which ports are in use.  I think you need to use a port above 1025 or something like that, because root uses the lower range of ports.

Once you have the code above saved behind a button on your page, there's a quick way to make sure that your client works, using "netcat" (nc).
  1. Login to the server that you specified in your copy of the code.
  2. Type "nc -lk 1234" where 1234 is the number of the actual port that you specified in your copy of the PeopleCode.  -l tells nc to listen for connections on the port you specify.  -k tells it to keep listening after it receives requests.
For more information, on your Linux server, type "man nc".  There are several good tips and tricks on the internet for using nc to its maximum potential.

I have not figured out how to get nc to send back a response to my client.  Once I got it working, though, I have a server that I can call that will automatically send a response to any request that I send. The following commands caused that response to be displayed:
Local string &strListenerResponse = &jBufferedReader.readLine(); 
Warning ("Response: " | &strListenerResponse);
WARNING!!! Do not call the "writeBytes" command on your DataOutputStream UNTIL you have instsantiated your InputStreamReader and BufferedReader objects. If you begin writing to the output stream before the input objects exist, the socket will become confused and will hang.

Close the OutputStream, BufferedReader, and Socket in that order.  At one point my code was only closing the socket.  This made it so only the first request yielded the expected response, while every other response said "...java.net.SocketException: Broken pipe...".

Let me know what questions you have about the code.

2 comments:

John K Peterson said...

I commend you for the connect timeout; a lot of people forget that. It's also important to set a reasonable receive timeout so your request doesn't hang if the other end never responds to what you send it. Simply set SO_TIMEOUT on the socket after creating it with setSoTimeout(int timeout) throws SocketException.

trustno1 said...
This comment has been removed by a blog administrator.