I have a class like
class Stundent
{
private static final long serialVersionUID = 1L;
private int no;
private String name;
//setters and getters
}
And then i used the following code for serialization & deserialization
public class Serialization{
public static void main(String args[]) throws Exception {
File file = new File("out.ser");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
SerializeMe serializeMe = new SerializeMe(1);
oos.writeObject(serializeMe);
oos.close();
}
}
public class DeSerialization{
public static void main(String args[]) throws Exception {
File file = new File("out.ser");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
SerializeMe dto = (SerializeMe) ois.readObject();
System.out.println("data : " + dto.getData());
ois.close();
}
}
After running the Serialization class i changed the serialiversionID to 2 and then i run the second program,then i got like
java.io.InvalidClassException:
SerializeMe; local class incompatible
That means whenever i am performing deserialization
serialversionID of Student class is checking.
My doubt is
1)Is it mandatory to have Student class at the time of deserialization ?
2)As per serialization definition, to transfer the java objects as stream of bytes through network we are performing serialization.So if an object is transferred through network ,how the Student class is available in other side.
For example in my application every entity(Hibernate entity) is
Serializable.
Because my web application exist in different server and my database
is in differentserver.That is the reason we have implemented
Serializable.In this case can u explain how serialization is working?
Thanks in advance...
When you deserialize a serialized object, the same class has to be present in order to construct an instance of that class.
The serialVersionUID is to tell whether the same version of the class is present in terms of the serialization process. If the class that is present has a different serialVersionUID value (compared to the value read from the binary data/stream of the serialized object), deserialization will be aborted by throwing an InvalidClassException.
1)Is it mandatory to have Student class at the time of deserialization?
Yes.
2)As per serialization definition, to transfer the java objects as stream of bytes through network we are performing serialization.So if an object is transferred through network ,how the Student class is available in other side.
You have to take care of that. You have to distribute the Student class prior to deserializing a serialized Student.
Deserialization can only happen if the same version (determined by the serialVersionUID value) exists of the serialized class. Serializing instances of the standard lib is not a problem, because those are present in all JRE's (although different versions might be present with different serialVersionUID fields!), so you can "transfer" such objects between different JRE's. If an instance of a custom class is transferred, the same class file must be made available at the destination prior to the deserialization process.
Edit:
You wrote that your "web application exists on different servers". Your web application includes your class files which implicitly means that the class files will be available on all servers. So a serialized object from one server can be deserialized on another.
As far as my understanding is concerned, usually when we transfer objects, it is the attribute values inside the Student object is what we are require.
The Student class should be present on the other side.
Related
I am working on building a music.player and have my music-library stored in a HashMap. The User shall be able to add and delete songs. I want to save this HashMap for when the program is beeing restartet.
However did I encounter this warning:
Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: musicplayer.Song
Research showed I have to implement the Serializable Interface in my Song class. Which I did, but with still this warning.
My Song class:
package musicplayer;
//Song-Klasse, speichert alle Attribute und Methoden eines Songs. Funktioniert soweit
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
public class Song implements Serializable {
private static final long serialVersionUID = 4390482518182625971L;
//Attribute
File file;
Clip clip;
String string;
//...
The MusicDaten - Class
package musicplayer;
public class MusicDaten implements Serializable {
private static Map<String,Song> all; //= new HashMap<String,Song>();
private File file = new File("C://Users//ThinkPad T450s//git//testproject//musicplayer//SongInfo.ser");
// ...
public MusicDaten() throws ClassNotFoundException, IOException {
this.setSavedSongs();
}
public void setSavedSongs() throws IOException, ClassNotFoundException { //initialisziert HashMap mit den gespeicherten Songs
FileInputStream fileIn = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fileIn);
all = (HashMap<String,Song>) in.readObject();
in.close();
fileIn.close();
}
public void save() throws IOException { //Speicher HashMap
FileOutputStream fileOut = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(all);
out.close();
fileOut.close();
System.out.println("Songinfo saved");
}
Thank you for the help.
(I have edited this question since before it wasn't quite clear)
Implementing Serializable is not sufficient.
If you attempt to serialize an object, all its non-transient attributes are serialized, too. If any of those attributes is not Serializable, it will not work.
In your case, Song contains an attribute of type File and File is not serializable. With Clip, you have the same problem.
In order to get around this, you can do custom serialization.
Looking at the docs of Serializable, you can find this:
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it. The default mechanism for saving the Object's fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
The readObject method is responsible for reading from the stream and restoring the classes fields. It may call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields.
This means that you can create the methods writeObject and readObject where you specify how to (de)serialize the object.
If you want to keep the default (de)serialization of the attributes supporting serialization, you can mark all fields not supporting serialization transient and call out.defaultWriteObject/in.defaultReadObject in the writeObject/readObject methods.
Marking an attribute transient means that serialization ignores it. You can then use your custom logic.
Note that serialization comes with some problems and you might not want to use it.
On one hand, it can lead to serious denial of service and even remote code execution vulnerabilities if you deserialize untrusted data. This is also noted in the docs of Serializable:
Warning: Deserialization of untrusted data is inherently dangerous and should be avoided. Untrusted data should be carefully validated according to the "Serialization and Deserialization" section of the Secure Coding Guidelines for Java SE. Serialization Filtering describes best practices for defensive use of serial filters.
Another problem of serialization is that it binds your application to a fixed format and makes it difficult to be compatible with old serialized data when update your application if you didn't carefully think it through when initially creating it.
For more information about this, you may want to consider reading the book Effective Java.
I have old binary data serialized and stored in a db with an old class 'mypackage.Myclass'
I would like to refactor this class to 'mypackage.topic.Myclass' and still able to read the binary data back (deserialization)
I get an error Classnotfound when :
ois = new ObjectInputStream(bos);
o = ois.readObject();
mypackage.topic.Myclass myclass = (mypackage.topic.Myclass) o;
Is there a way to force readObject() to match another class then the original serialized class ? Or any otherhint doing this ?
You can solve this by creating a data wrapper class called mypackage.Myclass. When deserializing the binary data, you can first try to deserialize it as mypackage.topic.Myclass. When this fails, deserialize it as mypackage.Myclass and convert it afterwards to the class mypackage.topic.Myclass.
You can also set the serialVersionUID of the mypackage.topic.Myclass to the serialVersionUID of the mypackage.Myclass. If you don't know the serialVersionUID of the class mypackage.Myclass, or if you doesn't have set the serialVersionUID, you can generate the serialVersionUID, which is probably used/automatically created by the JVM using this command: serialver -classpath whatever com.foo.bar.MyClass (See: Java - Modifying serialVersionUID of binary serialized object)
When the classes have the same serialVersionUID, there will be no error when you deserialize the class.
I'm trying to pick up Java and wanted to test around with Java's client/server to make the client send a simple object of a self defined class(Message) over to the server. The problem was that I kept getting a ClassNotFoundException on the server side.
I think the rest of the codes seem to be alright because other objects such as String can go through without problems.
I had two different netbeans projects in different locations for client and server each.
Each of them have their own copy of Message class under their respective packages.
Message class implements Serializable.
On the client side, I attempt to send a Message object through.
On the server side, upon calling the readObject method, it seems to be finding Message class from the client's package instead of it's own. printStackTrace showed: "java.lang.ClassNotFoundException: client.Message" on the server side
I have not even tried to cast or store the object received yet. Is there something I left out?
The package name and classname must be exactly the same at the both sides. I.e. write once, compile once and then give the both sides the same copy. Don't have separate server.Message and client.Message classes, but a single shared.Message class or something like that.
If you can guarantee the same package/class name, but not always whenever it's exactly the same copy, then you need to add a serialVersionUID field with the same value to the class(es) in question.
package shared;
import java.io.Serializable;
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
// ...
}
The reason is, that the readObject() in ObjectInputStream is practically implemented as:
String s = readClassName();
Class c = Class.forName(s); // Here your code breaks
Object o = c.newInstance();
...populate o...
public class Employee implements java.io.Serializable
{
public String name = "Tom";
public void mailCheck()
{
String address = SomeClass.ItsStaticField(name); //call to static
System.out.println("Mailing a check to " + name
+ " " + address);
}
}
public class SomeClass()
{
// Static Map of <Name, Address>
private static Map<String, String> NameAddressMap= new HashMap<String, String>();
string address;
Static ItsStaticField(name)
{
if (NameAddressMap.containsKey(name){
address = NameAddressMap.get(name);
}
else{
// address =...make a webservice call ... get address for name
NameAddressMap.put(name, address);
}
return address;
}
}
What happens when an object of the above is serialized? And deserialized on another node in a distributed environment? When is 'SomeClass.ItsStaticField()' invoked? Is the 'address' field's value calculated before serialization and then bundled into bytes as part of the serialization process?
Update: Apologize for taking the liberty to add some more info.
Added sample code for 'SomeClass'.
I have the above code in a distributed environment (specifically Hadoop). I understand this is a bad thing because there are going to be multiple web-service calls, and the intended 'caching mechanism' doesn't really work as expected. What I want to understand is, when and how many times the web-service call would be invoked? And how many instances of the static map 'NameAddressMap' is created on all nodes and when? Thanks much!
What happens when an object of the above is serialized?
The name is serialized
And deserialized on another node in a distributed environment?
The name is deserialized
When is 'SomeClass.ItsStaticField()' invoked?
When you call mailCheck();
Is the 'address' field's value calculated before serialization and then bundled into bytes as part of the serialization process?
No, it is not a member of the object serialized.
Serialization serialises the objects fields, not its methods. The contents of the methods remain in the class definition, not in the instance itself.
When a class is serialized, the node deserializing it need all the .class files required for the deserialization. In your case, the class SomeClass is part of the dependencies of Employee. So, in order to deserialize an Employee, both Employee.class and SomeClass.class are required on the local node.
Note: due to Java being dynamically compiled, it should work until you actually try to call the mailCheck method.
I want to write the code, that can deserialize the class even if the class was changed (but you have old class version).
The idea is simple.
Read the serialVersionUID of the serialized class
Check if that serialVersionUID is equal to the serialVersionUID of the current class version.
If not, create new ClassLoader and load the old class version into workspace.
I thought to use something like this:
FileInputStream file = new FileInputStream(filename);
ObjectInputStream o = new ObjectInputStream(file) {
protected Class<?> resolveClass(java.io.ObjectStreamClass desc) throws IOException, ClassNotFoundException {
//5639490496474438904L is the suid of the older version
if (desc.getSerialVersionUID() == 5639490496474438904L) {
Class c01 = null;
URL eineURL = new URL("file://localhost/U:/MyJavaProj/bin/test/oldversion/");
URL[] reiURL = new URL[] {eineURL};
URLClassLoader clazLader = new URLClassLoader(reiURL);
c01 = clazLader.loadClass("test.SerObj");
return c01;
}
return super.resolveClass(desc);
}
};
SerObj obj = (SerObj) o.readObject();
The problem is in the last line. My current class version is placed in U:/MyJavaProj/bin/test/SerObj.class
My old class version is placed in U:/MyJavaProj/bin/test/oldversion/test/SerObj.class
In the last line I read the old class version but cast it to the new version :(
Has anyone some idea or maby any other aproach to add the versioning support for the serialization in Java?
i don't know if you trying to fix an existing problem, or write new functionality. if the latter, then i would look into using the advanced functionality of java serialization. java serialization supports various facilities for being able to handle multiple serial versions of the same class within one classloader (critically, though, you need to keep the serialVersionUID the same for all instances).
here's an example for handling an "incompatible" change, where an integer value was changed to a String value:
public class MyClass {
private static final int CUR_VERSION = 5;
private String _value;
private void readObject(ObjectInputStream in) {
// first value is always the serial version:
int dataVersion = in.readInt();
if(dataVersion == CUR_VERSION) {
// _value is a String
_value = in.readString();
} else {
// in older versions, _value was an int
_value = String.valueOf(in.readInt());
}
}
private void writeObject(ObjectOutputStream out) {
// always write version first
out.writeInt(CUR_VERSION);
out.writeString(_value);
}
}
The simple answer to this is don't change the serialVersionUID.
If you think you've made a serialization-incompatible change to the class, first read the Object Versioning part of the Object Serialization Specification to double-check that, as you probably haven't, and if you really have, change/add the writeObject/readObject methods so that they can cope with the old serialization format.
And certainly don't try to mess around with two versions of the class at runtime. It won't work.
You won't be able to perform the cast in the last line, because it's of a different class (which is not castable) - you might get a confusing message such as test.SerObj is not an instance of test.SerObj.
This arises from the fact that a class is essentially a java.lang.Class instance, which critically is unique within a given classloader. Even if you referenced the exact same *.class file, an instance of SerObj loaded by the default classloader is a different class from an instance of SerObj loaded by clazLader. Neither class could be cast to the other one.
Thus it is not going to be possible for the method to return SerObj if you need to use multiple classloaders to deserialise. (Besides - how could it, when the two class files could have arbitrary differences?)
One possible workaround is to define an interface that defines the behaviour which is fixed between versions of the class. If you set up the temporary classloader such that it can only load SerObj from the oldversion class file, and that it has the right parent classloader, then your old SerObj should still implement the interface class as defined by the root classloader.
In this was, both the new SerObj and old SerObj instances would be castable to the SerObjIface interface, and so you could declare your method to return that. And in fact if you want callers to deal with different yet similar classes (which is what different versions of "the same class" are), you should arguably be returning an interface for this anyway.