6

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);
Arsen Davtyan
  • 1,891
  • 8
  • 23
  • 40
tgkprog
  • 4,493
  • 4
  • 41
  • 70

4 Answers4

7

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.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • thanks. I made the sample code without testing it and apparently not knowing the api. yep toByteArray. Will look in to the rest. So your saying we must base64 encode the output of serialization, right? I guess so just re-read "If you want a String you must encode it somehow." And yes i do want to pass it as a POST param. Will check if HttpParam does that. In the old code I had seen they were using a socket directly to post – tgkprog Jul 23 '13 at 07:15
  • but remember to go directly from the base64-encoded string to the byte array, no intermediate String. I guess need a function for this - Base64_toByte_array do you have any suggestions for a base64 freeware ? – tgkprog Jul 23 '13 at 08:22
  • 1
    You must encode the serialized output somehow if you're passing it as a POST parameter. Base64 is as good a way as any. There's a Base64-encoder in Apache Commons somewhere. – user207421 Jul 23 '13 at 08:27
  • For the decode is this alright: ` String objDataFromServletParam = request.getParameter("obj");` ` byte dataBytes[] = Base64.decode(objDataFromServletParam.toCharArray());` ` Object obj = SerUtl.retriveObject(dataBytes);//cast` – tgkprog Jul 24 '13 at 15:47
  • 1
    its not about debugging a few test cases. i just do not want to break for some edge cases later as i'm advocating the user of this over jaxb. why - both jvms have our code, java version the same under our control ... – tgkprog Jul 24 '13 at 22:53
  • Also, you don't need to encode the parameters if you stream them one by one in a binary body that you post to the server. – flup Jul 30 '13 at 08:18
  • @flup The relevance of your comments to this answer escapes me. They should be posted as comments to the question, or as an answer of yourown. – user207421 Jul 30 '13 at 22:11
  • @EJP The JAXB bit referred to the object streaming advocacy remark right above it, and I agree both are better placed at the original question. I've moved mine. But you don't mention that the encoding is not necessary, and only write "if you want a String". I think this is an omission in this answer. – flup Jul 30 '13 at 22:24
  • @flup It seems to me that the question specifically asks about POST parameters, which are provided as Strings, and which must therefore be encoded, in addition to URL-encoding. I've answered that. There are lots of other ways to do the entire thing but your comments about them don't belong under this answer. – user207421 Jul 31 '13 at 01:50
  • Thank you your answer cleared up many things for my initial approach. – tgkprog Jul 31 '13 at 18:01
3

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.

flup
  • 26,937
  • 7
  • 52
  • 74
  • Doubly escaped data is valid, I will run more tests. What I did not like about JaxB is that I need to put the annotations in. Where as with serialization can just send it, as long as object and its members implement serialization. I will look in to the OuputStream over UrlConnection. Again my requirement is only for our code to our code. We already have 2 interfaces that are web services not touching that – tgkprog Jul 31 '13 at 07:05
  • 1
    JAXB works without annotations, too, you annotate the exceptions. See http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html – flup Aug 01 '13 at 00:27
  • But either way: glad the answer is of use to you, and thanks for the points of course! – flup Aug 01 '13 at 00:57
  • wow that is how I wanted it to be. Glad i did not complain on the oracle site. Shows you how you should not blindly follow the first quick start you find! – tgkprog Aug 01 '13 at 16:02
2

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

tgkprog
  • 4,493
  • 4
  • 41
  • 70
1

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.

Learn More
  • 1,535
  • 4
  • 29
  • 51
  • true we can, but I want something quick and to get under the hood. I have this working with Java serialization. Just need to implement java.io.Serializable, mark fields as transient if you do not want them to be serialized and your good to go. – tgkprog Jul 30 '13 at 11:45
  • 'quick'- All you need to do is - Download activeMQ/extract it and run it. - Create a class which facilitates enqueuing/dequeuing. – Learn More Jul 30 '13 at 11:49
  • 1
    no i mean quick for the code to execute per object. have timed sending objects using apache http components using java serialization, jaxb, soap and our custom code with serialization that sends over http using sockets and http 1.1. Fastest was java serialization. The frameworks are great for quick development and to be able to pass between companies/ platforms. If its your own Java app then Java serialization is fastest. – tgkprog Jul 30 '13 at 15:00
  • You'll still need to serialize the objects to messages. – flup Jul 31 '13 at 08:10
  • i'm certain this will work, and i will test it sometime and update timings (of a 10 minute run after JVM warm up) but for now going with http://iharder.net/base64 and pass via own servlet – tgkprog Jul 31 '13 at 17:59