Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-dev] InvalidClassException during load time weaving

Hi Choudary,
just a few considerations. You don't change explicitly the public interface of the class, because you are not using ITDs or the like. But, AspectJ changes it partially, probably adding references to aspects and similar stuff. This is true for any and every class enhancing tool. If you enhance your classes with OpenJPA enhancer for example, it will (quite radically) change your classes adding methods and fields, and this is true for Hibernate and TopLinktoo, just to name JPA related technologies, and many other tools.

Sun realized quite soon that bytecode manipulation was going to be a must in the Java world (they were the first ones proposing it for JDO, back around 10 years ago), and that this could interfere with the UID check done by serialization. That's why currently specifying the UID of a class that implements Serializable (either directly or indirectly) is quite mandatory, modern compilers issue a warning if you don't do so, and everyone who worked with serialization for a few days soon discovers how important it is.

So, I don't think you can find a way to avoid the automatically generated UID to stay the same after bytecode manipulation of any value, but you can quite rely on users of the Serializable interface to provide it. It does not matter how much generic your framework can be, placing the UID on a class that is going to be serialized is a strongly recommended technology standard related to serialization itself, and also very widely adopted and generic standards like JDO/JPA created by Sun itself requires you to place that UID. It is quite strange that your customers use serialization and does not define a UID in their classes, how does that software works if they merely add a new field?

java.io.Serializable docs explicitly say :
"it is /strongly recommended/ that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected |InvalidClassException|s during deserialization"

AspectJ could ease the pain making its fields transient, but that would help you but not others, and anyway since the UID is computed also on declared interfaces and AspectJ adds some interfaces that would not solve your problem anyway.

Other possibilities I can think of are all really really dirty and tricky. For example, find out the UID before weaving and the impose it using an ITD. There can be many problems with this approach, first of all catching the generated UID, then setting up a static registry, then ITDeclaring the field so that it reads from this registry. Or catch calls to objectInputStream.readObject and rewrite some parts of the deserialization process so that you can soften (or ignore) the UID check, but this solution requires you to be able to weave all the places where such a call is placed, so if it happens inside other frameworks (including J2EE itself) that you don't weave you can't apply it.

All in all I see these dirty solutions only a way to make your code look (and perform in a manner) unreliable or at least weak, while clearly stating that every serializable class should define UID is nothing new or strange.

Just my 2 cents,
Simone

Choudary Kothapalli wrote:
Andy,

Here is my simple aspect:
----------
package mnj;
public aspect TestAspect {
	pointcut allExecutions():(execution(public * *.*(..))) &&
!within(mnj.TestAspect);

	Object around(): allExecutions(){
		System.out.println(thisJoinPoint);
		return proceed();
	}
}
---------
The test class is as follows:

package test;
import java.io.*;
public class Tester implements Serializable{	
	//private static final long serialVersionUID = 1L;
	
	public String data = "testData";
	
	public void write(String file) throws IOException{
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
		oos.writeObject(new Tester());
	}	
	public Tester read(String file) throws IOException, ClassNotFoundException{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
		return (Tester)ois.readObject();
	}	
	public static void main(String[] args) throws IOException,
ClassNotFoundException{
		String fileName = "data.ser";
		Tester tester = new Tester();
		//tester.write(fileName);
		tester.read(fileName);
	}
}
--------------
My scenario is as follows:
1. The application serializes Tester (before aspect weaving). Tester
does not define SUID.
2. Tester is weaved with TestAspect and deserialized.  It fails with
InvalidClassException. This is obviously because the Tester class
definitions differ (and generated SUIDs don't match) before and after
weaving.
3. If the SUID is explicitly defined, they match and there is no problem

As my product MaintainJ  is generic and should work with different
java runtimes, I cannot guess how the SUIDs are generated and avoid
affecting those classes. As I said in my first post, I can simply
avoid this problem by not weaving Tester class at all, but this
solution won't show the calls to those classes in the generated
sequence diagrams. Please let me know if you could think of any
workarounds.

Regards,
Choudary Kothapalli.

Here is the stack trace:
Exception in thread "main" java.io.InvalidClassException: test.Tester;
local class incompatible: stream classdesc serialVersionUID =
5068263196426222505, local class serialVersionUID = 489981860994650887
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:546)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1552)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1466)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1699)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
	at test.Tester.read_aroundBody2(Tester.java:15)
	at test.Tester.read_aroundBody3$advice(Tester.java:109)
	at test.Tester.read(Tester.java:1)
	at test.Tester.main_aroundBody4(Tester.java:21)
	at test.Tester.main_aroundBody5$advice(Tester.java:109)
	at test.Tester.main(Tester.java:1)

On Sun, Jan 18, 2009 at 9:52 PM, Andy Clement <andrew.clement@xxxxxxxxx> wrote:
Tricky problem.  Are you able to limit what your aspect does such that it
doesn't affect the parts of the classes that contribute to the calculated
SUID?  If you are just using advice and not intertype declarations, I can
imagine it might be possible - but then it would depend on perclauses, use
of around advice and whether that advice can be inlined.  Do you know which
part of your aspect is affecting the SUID?

cheers,
Andy.

2009/1/16 Choudary Kothapalli <choudary.kothapalli@xxxxxxxxx>
My product MaintainJ instruments an application using load time
weaving to capture the call trace and generate the sequence diagram. I
am having problems while deserializing classes that do not define a
SUID. The SUIDs of the original class and the weaved class are not
matching and InvalidClassException is thrown.

Currently I suggest my users to exclude those classes, but some
applications serialize too many classes and this solution is not ideal
for them. Any ideas on how this problem can be solved?

Thanks,
Choudary Kothapalli.
_______________________________________________
aspectj-dev mailing list
aspectj-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/aspectj-dev
_______________________________________________
aspectj-dev mailing list
aspectj-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/aspectj-dev


_______________________________________________
aspectj-dev mailing list
aspectj-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/aspectj-dev


--
Simone Gianni            CEO Semeru s.r.l.           Apache Committer
http://www.simonegianni.it/



Back to the top