how do i marshal data for TCP streaming? - java

So for a homework assignment, I have a example of how to marshal data and unmarshal.
The structure they gave us was this:
Event is an interface.
Wireformat is a class that "inherits" an Event.
WireFormatWidget is a class with the actual code that has the marshal and unmarshal.
I have separate threads that handle the sending data in byte array using TCP.
What I have an issue is that when I create a Wireformat object. I run into issue with a thread trying to marshal the data.
Exception in thread "main" java.lang.NullPointerException
at myhw.WriteFormatWidget.getBytes(WriteFormatWidget.java:38)
The interface structure defines the data as a message, a type of message as an integer, a timestamp (of what I am assuming is Date and getTime of that date), and a tracker. I am not sure what the tracker is.
I am told this structure is the best method to sending data which is why I am trying to implement this code style.
The WriteFormatWidget consist of this:
private int type;
private long timestamp;
private String identifier;
private int tracker;
So for my wireformat, I created it as a class that extends WireFormatWidget and implements Event because that was the only way Eclipse did not spit an error or suggest changing WireFormatWidget or Event.
Now when I hardcode my specific wireformat, I instantiate it and it seems to not be able to call getBytes() with the hardcoded values I uses for the same variables.
public class MyWireFormat extends WireFormatWidget implements Event {
private String identifier = "here is my custom wireformat";
....
When I print out the identifier in the getBytes in WireFormatWidget, I get null and not the expected identifier I hardcoded. So I must not be "inheriting" appropriately. What am I doing wrong?
EDIT: WireFormatWidget (given)
public class WriteFormatWidget {
private int type;
private long timestamp;
private String identifier;
private int tracker;
public byte[] getBytes() throws IOException {
byte[] marshalledBytes = null;
ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(baOutputStream));
dout.writeInt(type);
dout.writeLong(timestamp);
System.out.println("getBytes using identifier: " + identifier);
byte[] identifierBytes = identifier.getBytes();
int elementLength = identifierBytes.length;
dout.writeInt(elementLength);
dout.write(identifierBytes);
dout.writeInt(tracker);
dout.flush();
marshalledBytes = baOutputStream.toByteArray();
baOutputStream.close();
dout.close();
return marshalledBytes;
}
}
I'll save space by not posting the unmarshalling portion. But its the same thing just in reverse.
The issue I am having is printing the data from the Client-side as proof of what I am sending beforehand.
So I will perform a simple test like print the type or print the identifier. It fails and I have null.

You're not initializing WireFormatWidget#identifier. It's declared but never initialized. Add a constructor to WireFormatWidget and provide a String as the identifier.

You need to implement something that implements Serializable, or implement directly Serializable (I think is simpler).
You do not specify many things about your interface event, but probably will inherit from Serializable, at least if you are going to implement standard java serialization.
If Event implements Serializable so it is ok, otherwise if you use another serialization method you need to specify more about it.
Assuming that you implement Serializable you need to create a ByteBuffer and call to writeObject. To create the stream you can check for example Java Serializable Object to Byte Array, so joining all:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(bos);
stream.writeObject(yourinstancetoserialize);
out.flush();
byte[] yourBytes = bos.toByteArray();
...
Probably you will need to implement the writeObject directly. In that case you use the ObjectOutputStream methods to serialize the properties, check them in https://docs.oracle.com/javase/7/docs/api/java/io/ObjectOutputStream.html for example.
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
stream.writeInt(this.type);
stream.writeLong(this.timestamp);
stream.writeBytes(this.identifier); or stream.writeChars(this.identifier);
stream.writeInt(this.tracker);
...
}

Related

AspectJ / Generate methods using compile-time reflection

I just heard of AspectJ and it doesn't look too easy to understand, so I want to know beforehand if it (or anything else) will help me with my problem or not.
I have bunch of simple POJO classes and want to write binary serializers for them but without writing Write/Read methods by hand for each class. I could've done so with help of reflection but that will affect runtime performance. I believe I need something similar to Macroses in Scala with compile-time reflection and quasiquotes.
Update:
I'm unable to use any serialization available, because I have custom binary protocol which I can't modify (online game)
Update 2:
Example POJO with it's read, write and some helper methods. Not final version, there possibly could be some annotations, for example, but general structure should be the same. I also omitted inheritance for simplicity, in reality LoginPacket extends CommandPacket class which in turn extends Packet class.
public class LoginPacket {
public short length;
public int sessionId;
public short command;
public short error;
public String reason;
private String getString(ByteBuffer data) {
short length = data.getShort();
byte[] stringData = new byte[length];
data.get(stringData);
return new String(stringData, "UTF-8");
}
private void putString(ByteBuffer data, String someString) {
data.putShort(someString.length());
byte[] stringData = someString.getBytes("UTF-8");
data.put(stringData);
}
public static LoginPacket read(ByteBuffer data) {
LoginPacker loginPacket = new LoginPacket();
loginPacket.length = data.getShort();
loginPacket.sessionId = data.getInt();
loginPacket.command = data.getShort();
loginPacket.error = data.getShort();
loginPacket.reason = getString(data);
return loginPacket;
}
public void write(ByteBuffer data) {
data.putShort(this.length);
data.putInt(this.sessionId);
data.putShort(this.command);
data.putShort(this.error);
putString(data, this.reason);
}
}
I don't think you need to use AspectJ to modify your classes. I don't see what benefits using compile team weaving would add. I would suggest having your POJOs use implements Serializableand then serialize your objects using an ObjectOutputStream.
A simple example writing an object to a file:
outputStream = new ObjectOutputStream(new FileOutputStream(filePath));
outputStream.writeObject(yourObject);
...
// do whatever else and close stream
Similar questions:
Saving to binary/serialization java
Best way to store data for your game? (Images, maps, and such)

Jackson Json, marshalling ByteBuffer

Is there any restriction on the minimum size of the data that should be present in ByteBuffer so that Jackson will be able to serialize it ? I get BufferUnderflowException while doing so.
But it works fine when the size of data present is large.
public class MyTest {
private static class Wrapper {
private ByteBuffer buffer;
public void setBuffer(ByteBuffer buffer) {
this.buffer = buffer;
}
public ByteBuffer getBuffer() {
return buffer;
}
}
#Test
public void fails() throws Exception {
// Fails
ByteBuffer smallBuffer = ByteBuffer.wrap("small".getBytes());
Wrapper wrapper1 = new Wrapper();
wrapper1.setBuffer(smallBuffer);
System.out.println(new ObjectMapper().writeValueAsBytes(wrapper1));
}
#Test
public void works() throws Exception {
// Works
ByteBuffer smallBuffer = ByteBuffer.wrap("larger string works, wonder why".getBytes());
Wrapper wrapper1 = new Wrapper();
wrapper1.setBuffer(smallBuffer);
System.out.println(new ObjectMapper().writeValueAsBytes(wrapper1));
}
}
Exception stack trace:
org.codehaus.jackson.map.JsonMappingException: (was java.nio.BufferUnderflowException) (through reference chain: com.test.Wrapper["buffer"]->java.nio.HeapByteBuffer["int"])
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
at org.codehaus.jackson.map.ser.std.SerializerBase.wrapAndThrow(SerializerBase.java:140)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:158)
Jackson will normally run into problems when serializing complex objects with lots of internal state and/or non-standard getter/setter methods. In general, you should always stick to pure POJOs in the object hierarchy to be serialized.
In this particular case, you've created a wrapper that contains a ByteBuffer. Well, Jackson actually tries to serialize the entire byte buffer object (not just its byte contents), and if you look under the covers its actually trying to write out all these 'properties':
[
property 'short' (via method java.nio.HeapByteBuffer#getShort),
property 'char' (via method java.nio.HeapByteBuffer#getChar),
property 'int' (via method java.nio.HeapByteBuffer#getInt),
property 'long' (via method java.nio.HeapByteBuffer#getLong),
property 'float' (via method java.nio.HeapByteBuffer#getFloat),
property 'double' (via method java.nio.HeapByteBuffer#getDouble),
property 'direct' (via method java.nio.HeapByteBuffer#isDirect),
property 'readOnly' (via method java.nio.HeapByteBuffer#isReadOnly)
]
Its just by dumb luck that the second case works (because the buffer is long enough to get all the above shown methods invoked on it without underflowing). If you want to serialize the buffer as bytes, then either:
change the wrapper property to byte[]
keep the property as ByteBuffer, but mark it as #JsonIgnoreable and provide an alternate accessor method around it that converts to a byte[]
Example of first:
class Wrapper {
final byte[] buffer;
public Wrapper(final ByteBuffer buffer) {
super();
this.buffer = buffer != null ? buffer.array() : new byte[0];
}
}

StreamCorruptedException: invalid type code: 01 in Client Server connection

Firstly, I know there are some questions regarding StreamCorruptedExceptions, but barely any regarding the "Invalid type code: 01".
I have a local client connected to a local server. The Client tries to send a serializable "Task_Data" Object to the Server. The code that I use to send is (with clutter removed):
oos = new ObjectOutputStream(clientSocket.getOutputStream());
Task_Data task = new Task_Data();
task.setCheckMale(true);
task.setCheckAdult(true);
task.setAdditionalInfo("testing");
task.setTakeNotes(true);
task.setTakePhoto(true);
oos.writeObject(task);
oos.flush();
Now on the receiving end (server) I have:
Task_Data task = (Task_Data) ois.readObject();
System.out.println(task.getAdditionalInfo());
Eclipse throws this error: java.io.StreamCorruptedException: invalid type code: 01
What is strange is that the transmission work for "primitive" types like UTF without any problems.
Maybe the cause lies in my serialization, the Task_Data class is:
public class Task_Data implements Serializable {
private static final long serialVersionUID = -226273890693695870L;
private boolean checkMale = true;
private boolean checkAdult = true;
private boolean takePhoto = true;
private boolean takeNotes = true;
private String additionalInfo = null;
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.writeBoolean(checkMale);
out.writeBoolean(checkAdult);
out.writeBoolean(takePhoto);
out.writeBoolean(takeNotes);
out.writeUTF(additionalInfo);
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
checkMale = in.readBoolean();
checkAdult = in.readBoolean();
takePhoto = in.readBoolean();
takeNotes = in.readBoolean();
additionalInfo = in.readUTF();
}
Your writeObject() method will be executed during Serialization, because it agrees with the canonical signature for such methods, but your readObject() method won't, because it doesn't. So there is an asymmetry present, which could cause any error, including this one.
But I don't know why you've written explicit code to do what Serialization would already do by default anyway. I would get rid of the readObject() and writeObject() methods altogether, and any existing serialized streams, and re-test.
You must also make sure to use the same ObjectInputStream and ObjectOutputStream for the life of the socket. There are headers on these streams, and starting new ones at one end will confuse the other end.

Trouble accessing fields of a serialized object in Java

I have instantized a class that implements Serializable and I am trying to stream that object like this:
try{
Socket socket = new Socket("localhost", 8000);
ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
toServer.writeObject(myObject);
} catch (IOException ex) {
System.err.println(ex);
}
All good so far right? Then I am trying to read the fields of that object like this:
//This is an inner class
class HandleClient implements Runnable{
private ObjectInputStream fromClient;
private Socket socket; // This socket was established earlier
try {
fromClient = new ObjectInputStream(socket.getInputStream());
GetField inputObjectFields = fromClient.readFields();
double myFristVariable = inputObjectFields.get("myFirstVariable", 0);
int mySecondVariable = inputObjectFields.get("mySecondVariable", 0);
//do stuff
} catch (IOException ex) {
System.err.println(ex);
} catch (ClassNotFoundException ex) {
System.err.println(ex);
} finally {
try {
fromClient.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
But I always get the error:
java.io.NotActiveException: not in call to readObject
This is my first time streaming objects instead of primitive data types, what am I doing wrong?
BONUS
When I do get this working correctly, is the ENTIRE CLASS passed with the serialized object (i.e. will I have access to the methods of the object's class)? My reading suggests that the entire class is passed with the object, but I have been unable to use the objects methods thus far. How exactly do I call on the object's methods?
In addition to my code above I also experimented with the readObject method, but I was probably using it wrong too because I couldn't get it to work. Please enlighten me.
To answer your first question:
You need to use ObjectInputStream.readObject to deserialize. You cannot read individual fields from the stream*.
fromClient = new ObjectInputStream(socket.getInputStream());
Object myObject = fromClient.readObject();
Don't forget to flush the output stream when writing!
The second question is a little more complex. What the serialization mechanism does is write a class identifier to the stream followed by the serialized object data. When it deserializes it will read the class identifier and attempt to load that class (if it isn't already loaded). It will then instantiate the object using the no-arg constructor and call the private readObject(ObjectInputStream) method. Yes, that's right, it calls a private method from outside the class. Java serialization is special.
If the class cannot be found (i.e. if it's not on the classpath) then an exception will be thrown; otherwise you'll get a fully deserialized object of the correct type assuming no other errors are found.
For example, suppose you have the following classes:
class Server {
public static void main(String[] args) {
// Set up an OutputStream sink, e.g. writing to a socket (not shown)
...
ObjectOutputStream out = new ObjectOutputStream(sink);
out.writeObject(new Data("data goes here"));
out.flush();
out.close();
}
}
class Client {
public static void main(String[] args) {
// Set up an InputStream source (not shown)
...
ObjectInputStream in = new ObjectInputStream(source);
Data d = (Data)in.readObject();
System.out.println(d.getData());
}
}
class Data implements java.io.Serializable {
private String data;
public Data(String d) {
data = d;
}
public String getData() {
return data;
}
}
Now suppose you put those classes into three jars (one class per jar): server.jar, client.jar and data.jar. If you run the following commands then it should all work:
java -cp server.jar:data.jar Server
java -cp client.jar:data.jar Client
But if you do this:
java -cp server.jar:data.jar Server
java -cp client.jar Client
then you'll get a ClassNotFoundException because the client doesn't know how to find the Data class.
Long story short: the class itself is not written to the stream. If deserialization succeeds then you will have access to the object as though it had been created locally, but you will have to downcast the result of readObject to the expected type.
There is some complexity around versioning that I've ignored for now. Take a look at serialVersionUID and how to deal with changes to serializable classes if versioning is likely to be an issue.
*Not strictly true. You can call readFields inside the serializable object's readObject method (or readResolve), but you cannot call it from outside the deserialization mechanism. Does that make sense? It's a little hard to explain.
Looking at the code for ObjectInputStream.readFields(), that exception is called because the curContext field is null. You should call fromClient.readObject() before calling readFields(), as it will set the curContext. Note that readObject() will return the instance that is being serialized, which may be of more use to you.

Serialize an Object Array to sent over Sockets

I have an array that I have created from a database ResultSet. I am trying to Serialize it so that I can send it over a socket stream. At the moment I am getting an error telling me that the array is not Serializable. The code I have is down below, the first part is the class to create an object for the array:
class ProteinData
{
private int ProteinKey;
public ProteinData(Integer ProteinKey)
{
this.ProteinKey = ProteinKey;
}
public Integer getProteinKey() {
return this.ProteinKey;
}
public void setProteinKey(Integer ProteinKey) {
this.ProteinKey = ProteinKey;
}
}
The code to populate the array:
public List<ProteinData> readJavaObject(String query, Connection con) throws Exception
{
PreparedStatement stmt = con.prepareStatement(query);
query_results = stmt.executeQuery();
while (query_results.next())
{
ProteinData pro = new ProteinData();
pro.setProteinKey(query_results.getInt("ProteinKey"));
tableData.add(pro);
}
query_results.close();
stmt.close();
return tableData;
}
And the code to call this is:
List dataList = (List) this.readJavaObject(query, con);
ObjectOutputStream output_stream = new ObjectOutputStream(socket.getOutputStream());
output_stream.writeObject(dataList);
And the code recieving this is:
List dataList = (List) input_stream.readObject();
Can someone help me serailize this array. All I can find in forums is simple arrays(EG. int[]).
I tried to add the serializable to the class and the UID number but got java.lang.ClassNotFoundException: socketserver.ProteinData error message. Does anyone now why?
Thanks for any help.
Basically you need that the classes you want to serialize are implementing Serializable. And if you want to avoid the warning related to the serial you should have also a long serialVersionUIDfor each one, that is a code used to distinguish your specific version of the class. Read a tutorial like this one to get additional info, serialization is not so hard to handle..
However remember that serialization is faulty when used between two different versions of the JVM (and it has some flaws in general).
Just a side note: the interface Serializabledoesn't actually give any required feature to the class itself (it's not a typical interface) and it is used just to distinguish between classes that are supposed to be sent over streams and all the others. Of course, if a class is Serializable, all the component it uses (instance variables) must be serializable too to be able to send the whole object.
Change your class declaration to:
class ProteinData implements Serializable {
...
}
I would have thought as a minimum that you would need
class ProteinData implements Serializable
and a
private static final long serialVersionUID = 1234556L;
(Eclipse will generate the magic number for you).
in the class.

Categories