tags:

views:

211

answers:

6

Hello,

A server listens on a port, waiting for incoming requests from clients (the client as a matter of fact is an ejb). These requests pass a complete object model as parameter (e.g. a list of Employees, each Employee a list of Tasks etc.).

Now the server has to start another java program in a new JVM instance on the same machine, with this object model as a parameter.

This other java program should be a standalone java program, but I cannot pass the object model as a parameter to the main method (void main(String[] args)).

So what to do? I am looking for a "simple" solution, e.g. preferably without a database or a file for persistancy. The stand alone program is really cpu intensive and cannot be hosted by an appserver.

Thx.

+1  A: 

You could serialize the object and write it to the file system. Then, you can read in that serialized object at runtime. Alternatively, you could use JMS to transfer that object as a stream of bytes.

More on serialization.

More on JMS. I have always used ActiveMQ, an Apache project for my JMS needs. It is easy to use and highly scalabe. Oh yeah, and it's free.

geowa4
- JMS solution: does it require any overhead in the form of software installations?- serialization: will the original objects really be restored after reading?any beginner examples?
Gerard
see edits. i've never used JMS outside of ActiveMQ, which i recommend. the serialized objects are read back as though they were never gone. only `transient` variables are lost. but they shouldn't be necessary for use later (that's why they are marked `transient`).
geowa4
what do you think of the solution from kd304?
Gerard
A: 

One way would be that the original JVM opens a socket for listening, and the new JVM instance runs a program that connects to that socket and receives the parameters through the socket (Java's default serialization will probably be good enough). That way you only need to pass the socket port number as a parameter to main, and no files/databases are needed. The same method can also be used to communicate other commands between the JVMs.

Esko Luontola
- how will the original JVM know that the parameters have been read by the new machine, can it detect that? - how about many requests per minute: should i pass a unique id as a parameter, next to the port number, so that the correct object model will be passed?
Gerard
(1) It might be good for the client to send a message to the original JVM when it has read the data, that it has received it all, because there can be a short delay (some milliseconds) between the original JVM sending data over the socket and the client receiving it. (2) Have a different port for each JVM that you start. That way the port number is unique and you will know which JVM connected to it, because you tell about the port number only to one JVM.
Esko Luontola
+1  A: 

Run the application and capture its in/out streams, then stream the serialized object model through it. The new application should deserialize input coming from System.in .

Example of the concept (I just wanted to make sure my example works, sorry for the delay):

package tests;

import java.io.File;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class AppFirst {
    public static void main(String[] args) throws Exception {
        ProcessBuilder pb = new ProcessBuilder("java", "-cp", 
            "./bin", "tests.AppSecond");
        pb.directory(new File("."));
        pb.redirectErrorStream(true);
        Process proc = pb.start();

        ObjectOutputStream out = new ObjectOutputStream(
            proc.getOutputStream());
        ObjectInputStream oin = null;

        for (int i = 0; i < 1000; i++) {
            out.writeObject("Hello world " + i);
            out.flush();
            if (oin == null) {
                oin = new ObjectInputStream(proc.getInputStream());
            }
            String s = (String)oin.readObject();
            System.out.println(s);
        }
        out.writeObject("Stop");
        out.flush();

        proc.waitFor();
    }
}


package tests;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class AppSecond {
    public static void main(String[] args) throws Exception {
        ObjectInputStream oin = null;
        ObjectOutputStream out = new ObjectOutputStream(System.out);
        while (true) {
            if (oin == null) {
                oin = new ObjectInputStream(System.in);
            }
            String s = (String)oin.readObject();
            if ("Stop".equals(s)) {
                break;
            }
            out.writeObject("Received: " + s);
            out.flush();
        }
    }
}

Edit: Added a looping version. Note that there needs to be a trick with the OOS, as it instantly starts to read the passed in stream (and blocks your app If you do it at the wrong step - it should be wrapped AFTER the first object is sent out to the child process).

kd304
- sounds good, any examples?
Gerard
This is how *nix piping works: ls | grep java
kd304
what does your solution compare to the one 'George IV' suggests?Will this solution also perform in case of many requests?
Gerard
Frankly, I never tried to write more than one object to an OOS. You could wrap the request and response pair in both sample apps into a for loop and execute it 1000 times. I'll check this now and will return.
kd304
A: 

Either way, you can't pass an object model as it exists in memory. If you want to pass the object model, you're going to have to find a way to encode and decode that model into something you can pass.

Since the data's already being sent via EJB, object serialization's the obvious choice, although there's nothing wrong with XML, JSON, etc.

You also need to get the object from your server program to the newly-invoked JVM. If the object model is small enough, you could pass the serialized object to the java program as a command-line argument. You could also, as George IV has suggested, write it to the file system and then tell your new application where to find the file containing the serialized data. You could use JMS. Web services. Sockets.

But if your server application is already EJB-based, the simplest thing to do is to have your new application invoke on the EJB layer as an EJB client to request the data you're looking for.

Geoffrey Wiseman
this may be a good solution. 1.) the ejb invokes the remote socket, 2.) the server starts the standalone java program and 3.) the standalone program starts connecting to the ejb to get it's model. Now about the communication: which information do i have to pass in steps 1. and 2. so that step 3. will know what to do and where to do it?
Gerard
A: 

The serialization approach is good enaugh. What you need in addition is the root object to carry some references to the relevant objects to the call. What I did in the past was to serialize a complete object graph and to let the root object implement some interface like Runnable and then on the receiving node cast this root object to Runnable and execute run() on it.

Andre Pareis
what do you mean by 'the relevant objects to the call'?
Gerard
Depends on where you functionality is located. If it is within the serialized classes' code then you don't need to take care about that. On the other hand, if certain objects of the graph are relevant as parameters to functions on your server, that is outside of the serialized classes, then you can just use an `Object[]` as root object and pass the contents to a function accepting an `Object[]`.
Andre Pareis
A: 

Have a look at RMI. Your server could start up the new service which in turn publishes and exports itself to a local registry. Then your new service can receive the object model as a parameter to a Remote-extending method. Should be very simple, requires no file access or an additional service, and no more heavyweight than any of the other answers.

banjollity