Link to home
Start Free TrialLog in
Avatar of guydeschepper
guydeschepper

asked on

Apache HttpClient: send Vector object from servlet to client

Hi,

I'm writing an application where the client sends a request (GET) to a servlet.
For the request I'm using the Jakarta Commons HttpClient from Apache, v2.0.2

The request looks like this:

HttpClient client = new HttpClient();
client.setTimeout(timeout * 1000);
url = new URL(p_protocol, p_hostname, p_WebserverPort, file);
String strUrl = url.toExternalForm();
GetMethod method = new GetMethod(strUrl);

// Provide custom retry handler (seems to be necessary)
DefaultMethodRetryHandler retryhandler = new DefaultMethodRetryHandler();
retryhandler.setRequestSentRetryEnabled(false);
retryhandler.setRetryCount(1);
method.setMethodRetryHandler(retryhandler);

// Prevent CLOSE_WAITs
method.addRequestHeader( "Connection", "close");

// set content type
method.setRequestHeader("Content-type", "text/plain");

// add token
method.setRequestHeader("token", getHostname() + "-" + getID());

The servlet checks the token, and if that is valid it should send a
Vector with strings back to the client.

Currently, the code looks like this:

String receivedToken = request.getHeader("token");
if (validToken()) {
  ObjectOutputStream oOut = new ObjectOutputStream(response.getOutputStream());
  oOut.write(Base64.encodeBase64(SerializationUtils.serialize(servletVector)));
  oOut.flush();
  oOut.close();
}

and this is the client code for reading the response:

Vector servletVector = (Vector) SerializationUtils.deserialize(Base64.decodeBase64(method.getResponseBodyAsString().getBytes()));

But when I run this code, I get following exception:
java.lang.ArrayIndexOutOfBoundsException: -84
  at org.apache.commons.codec.binary.Base64.isBase64(Base64.java:137)
  at org.apache.commons.codec.binary.Base64.discardNonBase64(Base64.java:478)
  at org.apache.commons.codec.binary.Base64.decodeBase64(Base64.java:374)
  at net.testpackage.Client.pullData(Client.java:1465)
  at net.testpackage.Client.run(Client.java:1971)

When leaving out the Base64 Encoding/Decoding, I get following exception:
org.apache.commons.lang.SerializationException: java.io.UTFDataFormatException
  at org.apache.commons.lang.SerializationUtils.deserialize(SerializationUtils.java:204)
  at org.apache.commons.lang.SerializationUtils.deserialize(SerializationUtils.java:229)
  at net.testpackage.Client.pullData(Client.java:1471)
  at net.testpackage.Client.run(Client.java:1978)
Caused by: java.io.UTFDataFormatException
  at java.io.ObjectInputStream$BlockDataInputStream.readUTFSpan(Unknown Source)
  at java.io.ObjectInputStream$BlockDataInputStream.readUTFBody(Unknown Source)
  at java.io.ObjectInputStream$BlockDataInputStream.readUTF(Unknown Source)
  at java.io.ObjectInputStream.readString(Unknown Source)
  at java.io.ObjectInputStream.readObject0(Unknown Source)
  at java.io.ObjectInputStream.readObject(Unknown Source)
  at org.apache.commons.lang.SerializationUtils.deserialize(SerializationUtils.java:199)
  ... 3 more

I tried already several other methods (like converting the Vector to a
ByteArrayOutputStream) but until now I haven't succeeded to get it working.

Any help would be appreciated.
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

If you're base64 encoding it before sending, then you don't need an ObjectOutputStream really. Also you probably shouldn't set it as a header - just send it as part of the response
Avatar of guydeschepper
guydeschepper

ASKER

Sorry, I'm not sure what you're suggesting.

First of all, I'm not setting it as a header as you can see in the code, so that can't be the problem
So are you saying I can't use on ObjectOutputStream for sending a String ?
If so, what should I use then ?
To be honnest, I don't see why it would be a problem using an ObjectOutputStream ?
But maybe you can explain a bit more ?

Thanx for your help already...
>>First of all, I'm not setting it as a header as you can see in the code

Ignore that sorry - i misread it

>>So are you saying I can't use on ObjectOutputStream for sending a String ?

You could, but an ObjectOutputStream also sends metadata. Are you reading with OInputStream at the other end?

>>You could, but an ObjectOutputStream also sends metadata

(which would be a problem if you're not using ObjectInputStream at the other end). The other problem is you're mixing binary and text content. If you're sending binary output, you must use the output stream directly - you can't use a Writer as you're doing now. So there are really two approaches:

a. use binary output - OOS if you're using OIS at the other end
b. use the Writer to send Base64-encoded data as text
Ok, I see.
I'll try it out (will be later today) and let you know the results.
I tried both suggestions, but still without success:

a) use OOS & OIS:

servlet-code:
---------------
ObjectOutputStream oOut = new ObjectOutputStream(response.getOutputStream());
oOut.writeObject(servletVector);

client-code:
--------------
method.setRequestHeader("Content-type", "text/plain");
ObjectInputStream ois = (ObjectInputStream) method.getResponseBodyAsStream();
Vector servletVector = (Vector) ois.readObject();

Exception:
----------
java.lang.ClassCastException
      at net.testpackage.Client.pullData(Client.java:1462)
      at net.testpackage.Client.run(Client.java:1999)

Note:
------
I also tried the same code, but with
    method.setRequestHeader("Content-type", "application/octet-stream");
But that gave the same exception


b) use Writer to send Base64-encoded data as text:

servlet-code:
---------------
byte[] encodedSerializedData = Base64.encodeBase64(SerializationUtils.serialize(servletVector));
response.getWriter().write(new String(encodedSerializedData));

client-code:
--------------
byte[] servletData = Base64.decodeBase64(method.getResponseBodyAsString().getBytes());
Vector servletVector = (Vector) SerializationUtils.deserialize(servletData);

Exception:
----------
java.lang.ArrayIndexOutOfBoundsException: -84
  at org.apache.commons.codec.binary.Base64.isBase64(Base64.java:137)
  at org.apache.commons.codec.binary.Base64.discardNonBase64(Base64.java:478)
  at org.apache.commons.codec.binary.Base64.decodeBase64(Base64.java:374)
  at net.testpackage.Client.pullData(Client.java:1465)
  at net.testpackage.Client.run(Client.java:1971)

Let's take them one at a time:

a. I don't think you need to set the request header, but if you do then your second mime type is the correct one. That should be set at the server Try the following:

ObjectInputStream ois = new ObjectInputStream(method.getResponseBodyAsStream());
Object o = ois.readObject();
System.out.println(o.getClass());
ok, I will do that, but to be honest I don't think that will help, because I get the nullpointerexception
at the line
ObjectInputStream ois = new ObjectInputStream(method.getResponseBodyAsStream());
If that's the case, it suggests that 'method' is null
(or that the method has not been executed and therefore the stream is not available)
Sorry, I meant ClassCastException instead of NullPointerException.

In the meantime, I tried the code you suggested, but obviously (and as expected) I still get the exception (ClassCastException that is)

So it seams I cannot cast the stream to an ObjectInputStream for some reason.
>> ObjectInputStream ois = new ObjectInputStream(method.getResponseBodyAsStream());
I can't see why this will throw ClassCastException as you are not casting anything (but rather wrapping).

In  anycase do you want to try this (and avoid the ObjectStreams):
server:
    response.getOutputStream().write(Base64.encodeBase64(SerializationUtils.serialize(servletVector)));
client:
   Vector servletVector = (Vector) SerializationUtils.deserialize(Base64.decodeBase64(method.getResponseBodyAsString().getBytes("ISO-8859-1")));

ok, I tried that code, but still get the ArrayIndexOutOfBoundsException:

java.lang.ArrayIndexOutOfBoundsException: -84
      at org.apache.commons.codec.binary.Base64.isBase64(Base64.java:137)
      at org.apache.commons.codec.binary.Base64.discardNonBase64(Base64.java:478)
      at org.apache.commons.codec.binary.Base64.decodeBase64(Base64.java:374)
      at net.testpackage.Client.pullData(Client.java:1504)
      at net.testpackage.Client.run(Client.java:2007)

and line 1504 of Client.java is the code you suggested:
Vector servletVector = (Vector) SerializationUtils.deserialize(Base64.decodeBase64(method.getResponseBodyAsString().getBytes("ISO-8859-1")));

I really don't understand why this would fail (and especially the ClassCastException when using OOS & OIS seems very strange to me.)
Can you check what you get back when you do:
System.out.println(method.getResponseBodyAsString().getBytes("ISO-8859-1"));
That suppose to return you a sting base64 encoded.
You can compare that to what you send in your server:
System.out.println(Base64.encodeBase64(SerializationUtils.serialize(servletVector)));
I wonder if the method inputStream contains more or less and what is the difference...
ASKER CERTIFIED SOLUTION
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Oh boy, am I feeling silly.

I must have misread your previous post, as I still used
  ObjectInputStream ois = (ObjectInputStream) method.getResponseBodyAsStream();
instead of the suggested
  ObjectInputStream ois = new ObjectInputStream(method.getResponseBodyAsStream());


I tried again, but this time with the correct code (i.e. wrapping instead of casting) and it seems to be working !

Note that I also added the following code to the server part:
  response.setHeader("Content-type", "application/octet-stream");

But I'm not sure this is necessary.

So, the code that seems to be working for me is the following:

Servlet (i.e. server side):
  ObjectOutputStream oOut = new ObjectOutputStream(response.getOutputStream());
  response.setHeader("Content-type", "application/octet-stream");
  oOut.writeObject(servletVector);

Client:
  method.setRequestHeader("Content-type", "application/octet-stream");
  ObjectInputStream ois = new ObjectInputStream(method.getResponseBodyAsStream());
  Vector servletVector = (Vector) ois.readObject();


I should have done a copy/paste of the code you posted instead of manually modifying my existing code.

Thanx for all your help !

:-) No problem.

>>But I'm not sure this is necessary.

Not necessary, as the entity that's reading it knows the mime type, but

>>method.setRequestHeader("Content-type", "application/octet-stream");

is redundant