Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pass object to another JVM using serialization - same Java version and jars (both running our app)

Updates: For now using a Map. Class that wants to send something to other instance sends the object, the routing string.

Use an object stream, use Java serializable to write the object to servlet.

Write String first and then the object.

Receiving servlet wraps input stream around a ObjectInputStream. Reads string first and then the Object. Routing string decides were it goes.

A more generic way might have been to send a class name and its declared method or a Spring bean name, but this was enough for us.


Original question

Know the basic way but want details of steps. Also know I can use Jaxb or RMI or EJB ... but would like to do this using pure serialization to a bytearray and then encode that send it from servlet 1 in jvm 1 to servlet 2 in jvm 2 (two app server instances in same LAN, same java versions and jars set up in both J2EE apps)

Basic steps are (Approcah 1) :-

  1. serialize any Serializable object to a byte array and make a string. Exact code see below

  2. Base64 output of 1. Is it required to base 64 or can skip step 2?

  3. use java.util.URLEncode.encode to encode the string

  4. use apache http components or URL class to send from servlet 1 to 2 after naming params

  5. on Servlet 2 J2EE framework would have already URLDecoced it, now just do reverse steps and cast to object according to param name. Since both are our apps we would know the param name to type / class mapping. Basically looking for the fastest & most convenient way of sending objects between JVMs.

Example : POJO class to send

package tst.ser;

import java.io.Serializable;

public class Bean1 implements Serializable {
    /**
     * make it 2 if add something without default handling
     */
    private static final long serialVersionUID = 1L;
    private String s;

    public String getS() {
        return s;
    }

    public void setS(String s) {
        this.s = s;
    }   

}

* Utility *

package tst.ser;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URLEncoder;

public class SerUtl {

    public static String serialize(Object o) {
        String s = null;
        ObjectOutputStream os = null;
        try {
            os = new ObjectOutputStream(new ByteArrayOutputStream());
            os.writeObject(o);
            s = BAse64.encode(os.toByeArray());


            //s = URLEncoder.encode(s, "UTF-8");//keep this for sending part

        } catch (Exception e) {
            // TODO: logger
            e.printStackTrace();
            return null;
        } finally {
            // close OS but is in RAM
            try {
                os.close();// not required in RAM
            } catch (Exception e2) {// TODO: handle exception logger
            }
            os = null;
        }
        return s;
    }

    public static Object deserialize(String s) {
        Object o = null;
        ObjectInputStream is = null;

        try {
            // do base 64 decode if done in serialize
            is = new ObjectInputStream(new ByteArrayInputStream(
                    Base64.decode(s)));
            o = is.readObject();
        } catch (Exception e) {
            // TODO: logger
            e.printStackTrace();
            return null;
        } finally {
            // close OS but is in RAM
            try {
                is.close();// not required in RAM
            } catch (Exception e2) {// TODO: handle exception logger
            }
            is = null;
        }
        return o;
    }

}

**** sample sending servlet ***

    Bean1 b = new Bean1(); b.setS("asdd");
    String s = SerUtl.serialize(b);
            //do UrlEncode.encode here if sending lib does not.
    HttpParam p = new HttpParam ("bean1", s);
    //http components send obj

**** sample receiving servlet ***

    String s = request.getParameter("bean1");
    Bean1 b1 = (Beean1)SerUtl.deserialize(s);
like image 250
tgkprog Avatar asked Jul 22 '13 18:07

tgkprog


4 Answers

Serialize any Serializable object with to a byte array

Yes.

and make a string.

No.

Exact statements see below

os = new ObjectOutputStream(new ByteArrayOutputStream());
os.writeObject(o);
s = os.toString();

// s = Base64.encode(s);//Need this some base 64 impl like Apache ?
s = URLEncoder.encode(s, "UTF-8");

These statements don't even do what you have described, which is in any case incorrect. OutputStream.toString() doesn't turn any bytes into Strings, it just returns a unique object identifier.

Base64 output of 1.

The base64 output should use the byte array as the input, not a String. String is not a container for binary data. See below for corrected code.

ByteArrayOutputStream baos = new ByteArrayOutputStream();
os = new ObjectOutputStream(baos);
os.writeObject(o);
os.close();
s = Base64.encode(baos.toByeArray()); // adjust to suit your API
s = URLEncoder.encode(s, "UTF-8");

This at least accomplishes your objective.

Is it required to base 64 or can skip step 2?

If you want a String you must encode it somehow.

Use java.util.URLEncode.encode to encode the string

This is only necessary if you're sending it as a GET or POST parameter.

Use apache http components or URL class to send from servlet 1 to 2 after naming params

Yes.

On Servlet 2 J2EE framework would have already URLDecoded it, now just do reverse steps and cast to object according to param name.

Yes, but remember to go directly from the base64-encoded string to the byte array, no intermediate String.

Basically looking for the fastest & most convenient way of sending objects between JVMs.

These objectives aren't necessarily reconcilable. The most convenient these days is probably XML or JSON but I doubt that these are faster than Serialization.

os = null;

Setting references that are about to fall out of scope to null is pointless.

HttpParam p = new HttpParam ("bean1", s);

It's possible that HttpParam does the URLEncoding for you. Check this.

like image 163
user207421 Avatar answered Oct 28 '22 01:10

user207421


You need not convert to string. You can post the binary data straight to the servlet, for example by creating an ObjectOutputStream on top of a HttpUrlConnection's outputstream. Set the request method to POST.

The servlet handling the post can deserialize from an ObjectStream created from the HttpServletRequest's ServletInputStream.

I'd recommend JAXB any time over binary serialization, though. The frameworks are not only great for interoperability, they also speed up development and create more robust solutions.

The advantages I see are way better tooling, type safety, and code generation, keeping your options open so you can call your code from another version or another language, and easier debugging. Don't underestimate the cost of hard to solve bugs caused by accidentally sending the wrong type or doubly escaped data to the servlet. I'd expect the performance benefits to be too small to compensate for this.

like image 29
flup Avatar answered Oct 28 '22 00:10

flup


Found this Base64 impl that does a lot of the heavy lifting for me : http://iharder.net/base64

Has utility methods :

 String encodeObject(java.io.Serializable serializableObject, int options )
Object decodeToObject(String encodedObject, int options, final ClassLoader loader )

Using :

try {
            String dat = Base64.encodeObject(srlzblObj, options);
            StringBuilder data = new StringBuilder().append("type=");
            data.append(appObjTyp).append("&obj=").append(java.net.URLEncoder.encode(dat, "UTF-8"));

Use the type param to tell the receiving JVM what type of object I'm sending. Each servlet/ jsps at most receives 4 types, usually 1. Again since its our own app and classes that we are sending this is quick (as in time to send over the network) and simple.

On the other end unpack it by :

        String objData = request.getParameter("obj");   
        Object obj = Base64.decodeToObject(objData, options, null);

Process it, encode the result, send result back:

        reply = Base64.encodeObject(result, options);
        out.print("rsp=" + reply);

Calling servlet / jsp gets the result:

            if (reply != null && reply.length() > 4) {
                String objDataFromServletParam = reply.substring(4);
                Object obj = Base64.decodeToObject(objDataFromServletParam, options, null);

options can be 0 or Base64.GZIP

like image 42
tgkprog Avatar answered Oct 28 '22 01:10

tgkprog


You can use JMS as well. Apache Active-MQ is one good solution. You will not have to bother with all this conversion.

  /**
 * @param objectToQueue
 * @throws JMSException
 */
public void sendMessage(Serializable objectToQueue) throws JMSException 
{
    ObjectMessage message = session.createObjectMessage();
    message.setObject(objectToQueue);
    producerForQueue.send(message);
}

/**
 * @param objectToQueue
 * @throws JMSException
 */
public Serializable receiveMessage() throws JMSException 
{
    Message message = consumerForQueue.receive(timeout);
    if (message instanceof ObjectMessage) 
          { 
              ObjectMessage objMsg = (ObjectMessage) message;
              Serializable sobject = objMsg.getObject();
              return sobject;
          }
    return null;
}

My point is do not write custom code for Serialization, iff it can be avoided.

When you use AMQ, all you need to do is make your POJO serializable. Active-MQ functions take care of serialization.

If you want fast response from AMQ, use vm-transport. It will minimize n/w overhead. You will automatically get benefits of AMQ features.

I am suggesting this because

  • You have your own Applications running on network.
  • You need a mechanism to transfer objects.
  • You will need a way to monitor it as well.

If you go for custom solution, you might have to solve above things yourselves.

like image 24
Learn More Avatar answered Oct 28 '22 01:10

Learn More