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.
Related
I'm trying to use the Java TreePathScanner API to determine the list of class files that will be generated from a given compilation. For example, the following source code:
public class InnerClass {
private final InnerInnerClass clazz = new InnerInnerClass();
private class InnerInnerClass {
}
}
will generate the following files:
InnerClass.class
InnerClass$1.class
InnerClass$InnerInnerClass.class
However, in my TreePathScanner subclass, visitClass is only called twice, for the InnerClass class, and the InnerInnerClass classes, but not the anonymously named class created from the new class statement. Changing the source to the following works as expected:
public class InnerClass {
private final InnerInnerClass clazz = new InnerInnerClass() { };
private class InnerInnerClass {
}
}
My tool's full source code is available here for reference, specifically ArtifactScanner.java.
Either this is a bug or a flaw in the API as there doesn't seem to be any other way to get all of the binary names that will be generated from a given compilation unit's source code. Am I missing something?
One of the JDK developers explained on this bug report that the observed behavior really is not a bug, and that the additional class files are generated as a result of the Java compiler backend which rewrites more complex language constructs into simpler ones before generating class files.
The TreePathScanner API therefore does produce the correct output in this case, and the short of it is that TreePathScanner is the wrong solution to use for my use case (determining the list of class files that will be produced) and that com.sun.source.util.TaskListener, TaskEvent, and TaskEvent.Kind, and JavaFileManager.inferBinaryName should be used instead.
I have a bunch of constants throughout my code for various adjustable properties of my system. I'm moving all of them to a central .properties file. My current solution is to have a single Properties.java which statically loads the .properties file and exposes various getter methods like this:
public class Properties {
private static final String FILE_NAME = "myfile.properties";
private static final java.util.Properties props;
static {
InputStream in = Properties.class.getClassLoader().getResourceAsStream(
FILE_NAME);
props = new java.util.Properties();
try {
props.load(in);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String getString(Class<?> cls, String key) {
return props.getProperty(cls.getName() + '.' + key);
}
public static int getInteger(Class<?> cls, String key) {
return Integer.parseInt(getString(cls, key));
}
public static double getDouble(Class<?> cls, String key) {
return Double.parseDouble(getString(cls, key));
}
}
The only problem with that is that for every constant that I get from this file, I have some boilerplate:
private final static int MY_CONSTANT = Properties.getInteger(
ThisClass.class, "MY_CONSTANT");
I don't think I want to use Spring or the like as that seems like even more boilerplae. I was hoping to use a custom annotation to solve the issue. I found this tutorial, but I can't really sort out how to get the functionality that I want out of the annotation processing. The Java docs were even less helpful. This should be a thing I should be able to do at compile time, though. I know the names of the class and field.
What I'm thinking is something like this:
#MyAnnotation
private static final int MY_CONSTANT;
Anyone know how I would go about doing this or at least best practices for what I want to do?
First of all, you shouldn't do it. It's practical, but too hacky and if you ever want to write a test using different settings, you'll run into problems. Moreover, nobody's gonna understand how it works.
An annotation processor can probably do nothing for you. A Lombok-style-hacking processor can. You want to make
#MyAnnotation
private static final int MY_CONSTANT;
work like
private final static int MY_CONSTANT =
Properties.getInteger(ThisClass.class, "MY_CONSTANT");
The original expression doesn't compile (due to the uninitialized final variable), but it parses fine and Lombok can do its job. There's already something related there:
#Value changes the modifiers to final private
#UtilityClass makes all fields static
So actually, you could write just
#MyAnnotation
int MY_CONSTANT;
and let your annotation change also the modifiers. I'd look at the eclipse and javac handlers for #UtilityClass, I guess all you need is to generate the initializer (which is quite some work because it's all damn complicated).
I don't think Lombok itself will implement this anytime soon, since
all the static stuff is non-testable and mostly bad style
and not everyone wants this in their code
it's not that much boilerplate
it also magically refers to the class Properties, but this could be solved via configuration
but I guess a contribution might be accepted.
Actually not quite clear why and what do you want to archive.
As I correctly undestand, you want use special kind of annotations to automatically assign values for static final constants from some properties file. Unfortunatelly it is impossible without special hacks. And annotations have nothing to do with this.
The reason is that final fields must be initialized and it is compiler's request. There aren't special annotations in java which will provide such syntactic sugar which you want.
But if you insist on this there are two ways:
Extrim way. Init all properties field with default value. Then using this hack in some static init section initialize this value using reflection mechanism and you code via reading values from properties.
Less extrim way: refuse request of final modifiers for properties fields, and using only reflection fill these fields values.
And additionally, for these ways, yes you can use annotations. But you will have to solve following technical issues:
1) Find all fields in all classes in classpath, which are annotated with you special annotation. Look at:
Get all of the Classes in the Classpath and Get list of fields with annotation, by using reflection
2) Force your Properties class to be initialized in all possible enter points of your application. In static section in this class you will load your properties file, and then using (1) method with reflection and classloader, assign values to all constants.
I know I can get a the classloader of a class by
xxxclass.class.getClassLoader(), but where exactly does the xxxclass
hold the reference of its classloader who defines it?
e.g.
public class ClassA {
System.out.println("I am class A");
}
I don't see any clue of the classloader reference in ClassA. However,
I can get the classloader of ClassA by using
ClassA.class.getClassLoader().
How does ClassA.class.getClassLoader() work?
The sentence as it appears in the documentation of ClassLoader is:
Every Class object contains a reference to the ClassLoader that defined it.
Note the two links? What they tell you is that the documentation refers to the Class object, not to the plain object of class ClassA.
Every class that you define in Java has a Class object associated with it, which allows you to look at that class in the meta level. That is, treat the class itself as an object, pass it as parameter, use reflection on it etc.
As you have noticed, one way to access the Class object is use ClassA.class. If you have a reference to an object of type ClassA:
ClassA myObj = new ClassA();
Then you can get the Class object using myObj.getClass().
So there is no reference to the ClassLoader in the myObj object, Only in its associated Class object.
Now, the other link tells you how to get the reference to the ClassLoader object once you have a Class object - through the getClassLoader() method.
Now, if you look at the source code of the Class class, you will see:
#CallerSensitive
public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}
So it calls getClassLoader0() to get the class loader. Its source is:
native ClassLoader getClassLoader0();
That is, the class loader reference is actually part of the native structure of this Java class, and it is not available to see using Java language tools. Nevertheless, it exists there, and available to you through the aforesaid method.
If you write this:
public class Ball {
private Person thrower;
public Ball(Person thrower) {
this.thrower = thrower;
}
public Person getThrower() {return thrower;}
}
then every Ball object contains a reference to the Person that threw it, right?
Similarly, the Class class has something like this: (although I'm not showing how classLoader gets assigned)
public class Class {
... other stuff ...
private ClassLoader classLoader;
public ClassLoader getClassLoader() {return classLoader;}
... other stuff ...
}
and so every Class object has a reference to the ClassLoader that loaded it.
In your example, ClassA is not a Class object, so the statement doesn't apply to it. It does apply to ClassA.class which is a Class object (or refers to one at least).
In fact, it's not so simple in this case.
(Or at least, it was not so simple, until a recent update)
Java is a very high-level language, and the JVM is a rather complex beast, which is (fortunately!) hiding many details that you don't want to be concerned with when using a high-level, object-oriented language.
As already pointed out in the other answers, the Class#getClassLoader() method delegates to a private native method getClassLoader0(). Usually, you simply don't know (and should not have to care about) what a private native method does.
But thanks to the open source JDK, one can trace the path of this method call (here, for a recent version of the JDK8) :
The native getClassLoader0 method of Class is bound to JVM_GetClassLoader
The JVM_GetClassLoader method eventually calls Klass::class_loader()
The Klass::class_loader() method only returns the result of ClassLoaderData::class_loader()
The ClassLoaderData::class_loader() method finally returns the field that you are looking for
However, note that this has changed in a recent commit of the JDK9: Now, the ClassLoader is stored as a private instance field in the Class class, in order to improve performance.
If you look at the source code of java.lang.Class it appears that it delegates to a native method called getClassLoader0. So the implementation details are down to the JVM.
I'm no expert on this, but I suppose this might allow garbage collection to work by not having reference cycles in Java.
We extend JAXBEqualsStrategy via pom:
<xjcArg>-Xequals-equalsStrategyClass=com.acme.foo.CustomEqualsStrategy</xjcArg>
The CustomEqualsStrategy extends JAXBEqualsStrategy. After running MAVEN clean install generate-source in Eclipse (Keplar) our model classes have equals method like this:
public boolean equals(Object object) {
final EqualsStrategy strategy = new CustomEqualsStrategy();
return equals(null, null, object, strategy);
}
Whereas if we do not extend JAXBEqualsStrategy, our model classes have equals method like this:
public boolean equals(Object object) {
final EqualsStrategy strategy = JAXBEqualsStrategy.INSTANCE;
return equals(null, null, object, strategy);
}
JAXBEqualsStrategy has
public static EqualsStrategy INSTANCE = new JAXBEqualsStrategy();
We expected to get
final EqualsStrategy strategy = CustomEqualsStrategy.INSTANCE;
in the generated equals method and are struggling to accomplish it.
You do not want to use CustomEqualsStrategy.INSTANCE. Using new CustomEqualsStrategy() is correct and should be preferred unless you have very good reasons for doing otherwise.
Since CustomEqualsStrategy extends JAXBEqualsStrategy, that means that unless you define your own INSTANCE field inside CustomEqualsStrategy, CustomEqualsStrategy.INSTANCE is the same as JAXBEqualsStrategy.INSTANCE, which means that you would be using an instance of JAXBEqualsStrategy after all.
Plus, using an INSTANCE field like that effectively signals that your class is meant to be used as a singleton, and thus has to be stateless. Most classes are not stateless, and even for classes that are, many such classes don't need to be used in a singleton style.
In short, really just stick with new CustomEqualsStrategy(). The code will have fewer surprises and you'll be happier for it. (Also, from reading the code for JAXBEqualsStrategy, perhaps you should be extending DefaultEqualsStrategy instead.)
Disclaimer: Plugin author here.
Actually, you can also get .INSTANCE or .getInstance()-calls generated as well. Please see this code:
https://svn.java.net/svn/jaxb2-commons~svn/basics/trunk/basic/src/main/java/org/jvnet/jaxb2_commons/plugin/util/StrategyClassUtils.java
So:
If the class of your equals strategy is known to the plugin in the runtime AND
Your class has a public static getInstance() method of the correct type OR
Your class has a public static field INSTANCE of the correct type
Then the plugin will use YourStrategy.getInstance() or YourStrategy.INSTANCE instead of new YourStrategy().
I guess you already have an INSTANCE method. Please try to include the JAR with your strategy into the XJC classpath (so that plugin could resolve your strategy class during the compile time).
Please also feel free to file an issue to support syntax like -Xequals-equalsStrategyInstanceField=com.acme.foo.CustomEqualsStrategy.INSTANCE or InstanceMethod accordingly.
For reuse reasons I have wrapped my current serialization/deserialization services in an abstract generic class, which is compiled in a shared JAR across the project. I need to serialize objects to String
The class can be extended and a type can be specified for it in other JARs/WARs (yea, this is a web application).
When I made my first deserialization tests from within the same WAR it all worked fine, but now that I moved the abstract class into another JAR I get a ClassNotFoundError when deserializing.
The base class is structured as follows:
public abstract class ConverterBase<T extends Serializable> {
public final Object getAsObject(String str) {
//Use java.io serialization services from the base64 representation
try {
ByteArrayInputStream ba = new ByteArrayInputStream(decoder
.decodeBuffer(str));
try {
ObjectInputStream is = new ObjectInputStream(ba);
try {
Object ret = is.readObject();
return ret;
} finally {
is.close();
}
} finally {
ba.close();
}
} catch (Throwable ex) {
return null;
}
}
public final String getAsString(Object obj) {
//simply do the opposite
}
}
It is structured such a way in order to allow future changes impact all subclasses (ie. avoid base64, be more efficient...). For now, the java.io solution is a temporary implementation.
Then I have the following inside the same WAR:
public class MyPojo implements Serializable {
//Stuff
}
public final class MyPojoConverter extends ConverterBase<MyPojo> { }
The class that extends this one is in a different archive than the abstract class and is specialized on an type of that WAR.
What could I do to avoid that error?
Thank you
If you want to store the data as String, I would use XML or JSon to serialise your objects with a tool like XStream. These tools are not sensitive to change in packages, class names, parent classes, interfaces or method changes.
The ObjectInputStream must be able to access all the classes which are used in the serialized objects.
Normally it should be enough if the code creating the thread (e.g. its classloader) can load each class mentioned in the stream. Make sure this is the case. (I'm not really sure about your class loader structure in your application container. If you provide more information about this, maybe others can help.)
For more complicated cases, you can create a subclass and override resolveClass there.
This is probably a class loading issue (yeah, of course).
If I got you right, the problem occurs from within your WAR, i.e. a JSP or servlet.
Please provide your stack trace, I'm not sure, which class cannot be found.