Ensuring equal even with different classloaders
I have a specification that certain objects must be called equal even when they are loaded by seperate classloaders. I have only found 2 methods of ensuring this.
1. Use getClass().getName().equals() and toString().equals() where the toString method returns a string that identifies uniquely the object.
2. Serialize the class into the same classloader, and then compare using instanceof and toString().equals().
Method 1 seems flawed as any class with same package name structure can pass as the class. Its not catastrophic (AFAICT) , just not optimal.
Method 2 seems slow, especially in a database environment to have to serialize any object that fails an instanceof check to see if it was just in the wrong classloader. And even if the check proved true still seems a bit slow.
Is there perhaps a thrid technique that uses reflection or otherwise? I am not familiar with the abilities and uses of reflection yet.
Thanks for the help.
Presumably you are checking the data and not the class themselves.If so then you have to produce a 'serialized' form of the data. You can choose the built in one or create your own.I would suspect that reflection would be slower and not even necessarily even doable.
How can I serialize 'the data'? I dont even know the class type at this point. I have only an instance of Object.
So I need to know if one instance of class Object is equal to an instance of class OID, for example. Where the Object may indeed be an instance of OID but from a different classloader, in which case OID instanceof OID is false.
> How can I serialize 'the data'? I dont even know the
> class type at this point. I have only an instance of
> Object.
You can use the built in form.
Or you implement your own. That would require an interface.
>
> So I need to know if one instance of class Object is
> equal to an instance of class OID, for example.
> Where the Object may indeed be an instance of OID
> D but from a different classloader, in which case OID
> instanceof OID is false.
The type doesn't matter.
Keep in mind that this is really only applicable if you must do many classes. If you are doing only one (or a couple) then it might be better to use an interface with a specific method.
Of course in all of the above the interface must be loaded by a super parent (like the system class loader.)
Is what your suggesting different from my option #2 in my original post?
>Method 1 seems flawed as any class with same package name structure can pass as >the class. Its not catastrophic (AFAICT) , just not optimal.
Could you explain were it is flawed? I don't get it.
Do you need just to check for equality of the object's (then use the equals method) or do you wan't the behaviour of instanceof. If you need the beahviour of instancof do you need only to check if two classes are the same only loaded by a different classloader or do you wan't the full instanceof check.
> Is what your suggesting different from my option #2> in my original post?Only to the extent that you can use your own serialization process which might be faster than the java one.
> >Method 1 seems flawed as any class with same package
> name structure can pass as >the class. Its not
> catastrophic (AFAICT) , just not optimal.
> Could you explain were it is flawed? I don't get it.
> Do you need just to check for equality of the
> object's (then use the equals method)
That will not work with the same class loaded by different class loaders.
> or do you wan't
> the behaviour of instanceof. If you need the
> beahviour of instancof do you need only to check if
> two classes are the same only loaded by a different
> classloader or do you wan't the full instanceof check.
Nor will instanceof do anything if you are checking data equality, which the OP is.
>That will not work with the same class loaded by different class loaders.
Just for clarifing:
'Conventional' equals method:
public boolean equals(Object x){
if(x instancof MyClass){
return dataComparison
}
return false;
}
Using class name (Works also with different classloaders:
public boolean equals(Object x){
if(x.getClass().getName().equals(MyClass.class.getName())){
return dataComparison
}
return false;
}
I don't see any flaw here except if the instanceof statement is checking for a supertype or interface then you cannot checking just the names of the classes.
>Just for clarifing:clarifing for me, please correct me if my code examples are wrong.
> >That will not work with the same class loaded by
> different class loaders.
>
> Just for clarifing:
> 'Conventional' equals method:
> > public boolean equals(Object x){
>if(x instancof MyClass){
>return dataComparison
>}
>return false;
> }
>
>
This wont work because instanceof will always return false for classes loaded from seperate classloaders.
> Using class name (Works also with different
> classloaders:
> > public boolean equals(Object x){
>
>
> if(x.getClass().getName().equals(MyClass.class.getNam
> e())){
>return dataComparison
>}
>return false;
> }
>
This is flawed because I could have two classes loaded from seperate libraries with the same classname.
package apackage;
class AClass {
int x;
int y;
}
package apackage;
class AClass{
Object abc;
Object def;
}
Just because the package name and class name are the same does not mean the class came from the same library.
What I have is an IDObject. It contains some value, say an ID. Perhaps its like this
class OID implements Serializable{
private int idvalue;
public boolean equals(Object o){
}
}
I need to know that the OID came from the same library and has the same idvalue. It does not matter what classloader its from. This begs the question, how does serialization determine what class to create? Is it using simply the classname? and maybe the SerialVersionUID?
It seems that ObjectInputStream is resolving the class based on name and serialVersionUID. So my serialization of the class is useless as it will not reveal anymore information than a comparison based on name and serialversionUID. What I will do is this
class AClass {
public boolean equals(Object o){
ObjectStreamClass osc1 = ObjectStreamClass.lookup(o.getClass());
ObjectStreamClass osc2 = ObjectStreamClass.lookup(AClass.class);
return osc1.toString().equals(osc2.toString());
}
Durn! Can't do this because nobody can guarantee o is serializable. Well I think this is as good as it gets. ill have to either serialize the class, or do this which is lighter than actually serializing the class, but still requires the class to be serializable. Ill have to catch the exception in the equals method and return false when I see it.
This should work fine.
class AClass {
public boolean equals(Object o){
ObjectStreamClass osc1 = sc1 = ObjectStreamClass.lookup(o.getClass());
ObjectStreamClass osc2 = sc2 = ObjectStreamClass.lookup(AClass.class);
If(osc1 != null && osc2 != null)
return osc1.equals(osc2);
else
return false;
}
Actually this should work perfectly. the lookup method returns null if the class is not serializable. Also the ObjectStreamClass is per classloader so a simple equals() call on it should do the job nicely. If they are the same class they will have the same ObjectStreamClass. == would even work to be honest.
well see. Thanks for the help. ill report what I find.
> This is flawed because I could have two classes
> loaded from seperate libraries with the same
> classname.
>
> > package apackage;
> class AClass {
>int x;
>int y;
> }
>
> package apackage;
> class AClass{
>Object abc;
>Object def;
> }
>
>
> Just because the package name and class name are the
> same does not mean the class came from the same
> library.
>
> What I have is an IDObject. It contains some value,
> say an ID. Perhaps its like this
>
> > class OID implements Serializable{
>private int idvalue;
>
>public boolean equals(Object o){
>
>}
> }
>
>
> I need to know that the OID came from the same
> library and has the same idvalue. It does not matter
> what classloader its from. This begs the question,
> how does serialization determine what class to
> create? Is it using simply the classname? and maybe
> the SerialVersionUID?
>
> It seems that ObjectInputStream is resolving the
> class based on name and serialVersionUID. So my
> serialization of the class is useless as it will not
> reveal anymore information than a comparison based on
> name and serialversionUID. What I will do is this
> > class AClass {
>public boolean equals(Object o){
> ObjectStreamClass osc1 =
> sc1 = ObjectStreamClass.lookup(o.getClass());
> ObjectStreamClass osc2 =
> sc2 = ObjectStreamClass.lookup(AClass.class);
> return
> return osc1.toString().equals(osc2.toString());
>}
>
>
> Durn! Can't do this because nobody can guarantee o is
> serializable. Well I think this is as good as it
> gets. ill have to either serialize the class, or do
> this which is lighter than actually serializing the
> class, but still requires the class to be
> serializable. Ill have to catch the exception in the
> equals method and return false when I see it.
Thanks for the explanations.
> This should work fine.
>
> > class AClass {
>public boolean equals(Object o){
> ObjectStreamClass osc1 = sc1 =
> = sc1 = ObjectStreamClass.lookup(o.getClass());
> ObjectStreamClass osc2 = sc2 =
> = sc2 = ObjectStreamClass.lookup(AClass.class);
> If(osc1 != null && osc2 != null)
>return osc1.equals(osc2);
> else
>return false;
> }
>
>
> Actually this should work perfectly. the lookup
> method returns null if the class is not serializable.
> Also the ObjectStreamClass is per classloader so a
> a simple equals() call on it should do the job
> nicely. If they are the same class they will have
> the same ObjectStreamClass. == would even work to be
> honest.
>
> well see. Thanks for the help. ill report what I
> find.
Well it works neither fine nor perfectly. ObjectStreamClass uses the 'class' object to determine equality. So even if the stream format would be the same because its the same class, it will get a seperate ObjectStreamClass instance since its from a seperate class instance.. That seems flawed to me. But oh well.
I have to use the ObjectStreamClass.toString() method and check for equality on that. Which is basically just using the class name and the stream UID. C'est la vie.
