how to share objects between different classloaders? - java

I need different classloaders to be able to unload classes. But i need to share objects between them (actually i am getting ClassCastException). So what are the solutions to deal with this?. Thanks

Objects from different classloaders can interact with each other through interfaces and classes loaded by a common classloader.

One of the primary objectives of using separate classloaders is to prevent exactly the kind of thing that you are trying to do. Security and name conflicts are good reasons for keeping this isolation. Here are a few ways that you can circumvent this isolation.
Using the Common Class Loader
Sharing Libraries Across a Cluster
Packaging the Client JAR for One Application in Another
Application
Refer to this link for more details.

Will also mention that if you are using interfaces, you can use java.lang.reflect.Proxy to create an instance of an interface local to your classloader which, under the hood, makes calls with reflection to the "real" (foreign) object from a different classloader. It's ugly, and if parameters or return types are not primitive, you will just be passing the ClassCastException further down the line. While you can rig something up to make this work, in general, it is better to either have a parent classloader with some shared types that you want to be able to use across classloaders, or use a more... serialized format for communication (slower), or only share interfaces that deal in primitives.

In some situation it may be acceptable to 'switch the context' by for using a thread with desired classloader. For example you create SingleThreadPoolExecutor by a context you need and use that singleton instance to execute the code from different context.
Here was my situation: maven multi module web application deployed to tomcat. One web app registered error notification service bean, and DefaultUncaughtExceptionHandler for the whole JVM using the bean to deliver error by an email (with additional logic inside). When exception was thrown by web application which created the bean email was successfully sent, when exception was thrown by different web app, email functionality failed to cope with javax.mail classes (surprisingly not the exception itself). When I executed a method which send email in an executor, both cases start to work.

Related

class MyClass cannot be cast to class MyClass (MyClass is in unnamed module of loader org.glassfish.[...].WebappClassLoader#1)

Why this error sometimes in deploy phase appens in Glassfish / Payara application server?
I can guess that the application server is trying to use two different classes of two different classloaders but is there a way to prevent it to do this behavior?
I tried to lookup some source online whitout finding nothing.
Edit : this happens on the same application in time of redeploy. It gets solved by a restart of the application server but obliviously this is not a solution
java.lang.ClassCastException: class com.MyClass cannot be cast to class com.MyClass (com.MyClass is in unnamed module of loader org.glassfish.web.loader.WebappClassLoader#1, com.MyClass is in unnamed module of loader org.glassfish.web.loader.WebappClassLoader#2)
Last Edit, after the great response of Stephen C.
What are the tools to undestand why Payara/GC doesn't destroy the old object?
I can guess that the application server is trying to use two different classes of two different classloaders but is there a way to prevent it to do this behavior?
Yes, that's what I think is happening. If identical .class files are loaded by different classloaders, the resulting runtime types are different and cannot by cast.
There are three ways to avoid this:
Don't pass or share these objects between different webapps.
Move the JARs that define the classes that need to be shared into the web container's shared library area ... so that they are loaded by the webcontainer's classloader rather than the webapp classloader(s).
If the classes need to be loaded by multiple webapp classloaders (e.g. because the have the same name but different implementations) you may need to rearchitect your application so that the classes implement a common interface that is loaded by a single classloader. If your webapp code then only casts to the shared interface, you won't run into this problem.
What if the web app is the same? (so when the application redeploy the same app)
If that is the case, then it sounds like the problem is that your webapp's shutdown code is not doing the right thing. Java objects created by the earlier deployment of the webapp are leaking into the later one.
Check that something is not caching application objects.
Check that application objects are not hiding in session state or thread-locals, or something like that.

How to create a singleton from within a private classloader?

I have a Java class that gets instantiated by a third-party application as an extension. That is, as per the 3rd party software design, customers like us register our Java class to their application and their application will install it to execute custom logic at the right place and time.
Our custom Java class needs to marshal and unmarshal XML, for which it uses JAXB. It therefore needs a JAXB context.
I naively called JAXBContext.newInstance(MyClass.class) on every call and not-so-quickly discovered that that's a well known recipe for a memory leak. The common prescription is to make one (or at most only a few) JAXB contexts to share among your whole application.
Fine, except the third party application that invokes my class makes each invocation on a new ClassLoader instance that is private to that invocation.
So, even if I put the JAXBContext in a static field or static HashMap<>, it would still be private to the invocation!
QUESTION
How can I, in a class that is instantiated from a private ClassLoader instance, create a singleton to be shared across the JVM?
I am thinking along two possible lines, but I'd like advice on how to make either of them work or any completely different approach anyone has.
The three ideas I had were:
Find somewhere in a JVM class where I could write an object. E.g., if System.setProperty could write instance of Object instead of just String, the idea would be to create the JAXB context and put it in a property, since it is sure that System will already have been instantiated and that the custom classloader instance would inherit it. But System.setProperty does not take an Object value, so I don't know a practical way to do this.
Somehow force a class to load on the parent or root classloader where I could store by JAXB context. I don't know how to do this.
Use a ThreadLocal to store the JAXBContexts. I don't think each invocation is a brand new thread (they're probably reused from a thread pool), so this could maybe be the way to limit my contexts. But how to create the ThreadLocal variable so its shared across the instances? It seems like this leaves me with the same problem.
It sounds like your code is sandboxed with in the 3rd party application. So using static or ThreadLocal won't help since it will only exist within the same classloader, and if that classloader is changed... then the context is lost.
The closest solution is to inject your context into the application code. This is not the best idea, since it can be affected by outsiders and have unexpected consequences. Do note that this means a value you created in your classloader will remain in the application and thus the creating classloader will never be garbage collected. There's also the problem of where JAXB jar comes from. I assume from your code and not the 3rd party. So that jar is loaded each time in a different classloader, so to share an object from there might be a problem and require some proxy.
Honestly there can be so many unforeseen results.
The best idea is to ask the 3rd party to provide you with some API to enable that.
Before continuing, let's see other options:
Is there a replacement for using JAXB? Something that won't present the same memory leak problem.
Is the memory leak that serious? What if there is a temporary memory leak until an API is provided by the 3rd party application.
If you really want to try to inject the object, I'd be happy to help. But, from experience, these kinds of things are messy.

Hacking a singleton with classloaders - Multiton in a Java web app

I am creating a web front end using an existing back end containing several singleton classes. The DataStore is initialized by passing a user object into it, which is fine in a desktop application environment where the app is launched once on each machine, but will not work in a server side application designed to cater for multiple users.
The database guys are reluctant to change the service layer and remove these singletons to allow an instance per user, or allow a single instance of a service layer object to serve multiple users. This is with good reason, the desktop app has been in use for 10 years and changes could have serious side effects for the desktop app.
I have been asked to investigate using classloaders to create multiple instances of the singletons. I am not comfortable with this idea at all, hacking singletons seems like bad practice, but changing the service layer could take months of work.
I have tested this out already by putting two identical WAR files of my app (with different file names) into Tomcat. Tomcat creates a classloader for each webapp and they worked just fine separately. I only encountered problems when the singletons used System.setProperty/System.getProperty, which is to be expected as the System class comes from a classloader much higher up in the tree.
To get this separation within a single webapp, it starts to get a bit complicated. It seems I would have to create a different classloader for each session, and use the classloader to load either all the classes in the whole service layer or just the ones which are singletons and their dependencies.
The problem comes when I'm thinking about how to use these objects in a session in a servlet. Because each Servlet has a single instance within a Tomcat, getting objects from the session and casting them will not be straightforward. Eg, to get a DataStore object from the session, I would have to cast it to the correct DataStore class loaded by the correct ClassLoader, since a single class loaded by two different ClassLoaders counts as two completely separate classes.
I have read that using ClassLoaders can cause all sorts of problems with memory leaks if they are not used carefully, and from the sounds of this, if I have 500 users, that is a lot of classloaders and classes loaded by classloaders. Won't I then have also have issues with PermGen?
I suppose from this large explanation, I really have 4 questions:
Hacking a singleton with classloaders in a webapp. Creating potentially hundreds of instances of the same classes designed to be singletons. Is that a terrible idea? So terrible I shouldn't contemplate it?
What is the best way to implement this if I absolutely have to?
How do I cater for casting in a Servlet, if I want to get and set objects into a session?
Will I end up with issues with memory leaks, and PermGen space?
I would really appreciate any suggestions. Thanks :)
It's not ideal, but I don't think it's terrible. In practice, it's not much worse than loading multiple versions of the same class (even without singletons), and that's becoming increasingly common in complex application server environments, particularly those with OSGi.
It's hard to say which approach is best, but to begin with, I would start by creating child class loaders of your web application class loader. Arrange to load implementation classes in the child class loaders (i.e., the ones with the singleton), and it possible, have the implementation class implement an interface that is loaded from the web application class loader.
By loading the interface from the web application class loader. This is basically the same approach that the application server itself is using to invoke your HttpServlet: the interface is loaded by the server, so it can refer to it directly, but the implementation is in a child class loader for your application. You're just creating a secondary layer of interface/impl split for your own convenience.
You'll end up with memory leaks if you store references to the child class loader (or its loaded classes, or instantiated objects from those classes) in a "parent" class loader (e.g., if the singleton registers an MBean, which causes it to get referenced in a JVM-wide object), but that's no different from if you weren't creating child class loaders. If you're dynamically creating/destroying these singletons (thus child class loaders), you'll have to take care that you don't retain references to those child class loaders (or classes/objects) longer than necessary. PermGen is probably more problematic. If you can run with Java 8, that's gone away; otherwise, you might have to increase the default PermGen size depending on how many of these class loaders/singletons you need to create.

How to cast two instance of the same loaded different classloader?

I have two different webapps, and each load the same class A with different classloader. When I put one instance in the session and then get it from the other webapp, a ClassCastException is thrown.
For example, in webapp A, I store a in the session, then in webapp B, I get the a from the session and cast it to A, the ClassCastException is thrown.
Is there a way to resolve this?
Is there a way to resolve this?
Basically no.
As far as the JLS is concerned, the types are different types, and there is no way that the JVM will allow you to pretend otherwise. For instance, the classes could have different code and different object layouts. If you could trick the JVM into treating the types as the same, you would be able to blow away JVM runtime safety. That way lies insanity.
The solution is to make sure that you don't have two different class loaders loading the same class. In the context of Tomcat, this means that if two or more webapps need to share instances of a class, then that class must be defined in a classloader that is common to both; e.g. put the JAR file in the $CATALINA_HOME/lib or $CATALINA_HOME/common directory.
If there is a technical reason why the classes have to be loaded by different classloaders (maybe because the classes really are different), then you could work around the problem by defining an interface that both versions of the class implement, and then programming to the interface rather than the implementation class. Of course, the interface needs to be loaded by a shared classloader ... or else you run into the same problem again.
You should avoid this situation, basically - either put both bits of functionality in the same webapp, or move the library containing class A into an appropriate location such that only one classloader will be used. Two classes loaded by different classloaders are entirely distinct in the JVM - you simply won't be able to cast between them.
See the Tomcat classloader documentation for more details about the various classloaders used. It looks like you'd want to put this common class into the common classloader area. As the documentation notes, this is pretty unusual, but if you really want to share an object between two webapps (which is also unusual) it's probably the easiest way forward.
You can't. Two classes loaded by different classloaders are different.
Perhaps you can serialize the shared objects?
I'm not necessarily advocating this approach, but then again that's what serialization essentially does --- you serialize from some JVM or ClassLoader X and load it in (deserialize) it into another JVM/ClassLoader Y...
You can't cast two object from different classes even if the classes have the same package name and signature, however you can copy the data from one to another simply by using apache bean utils library, BeanUtils.copyProperties(o1, o2);
This is possible with a workaround.
While it is true that you can't cast an object from one class loaded by classloader A to the same class loaded by classloader B (classes with the same name are not compatible if loaded under different classloaders as explained here), in a webapp container such as Jetty or Tomcat, you can load that class once in a parent classloader, which will be used by all webapps in the JVM. Each webapp classloader will defer to the shared (parent) classloader for the class definition and casting it back and forth works just fine.
For example, with Jetty, use WebAppContext.addSystemClass() as described here.

Troubles with HTTP-MMO game in Java

I'm about to write a MMO, using HTTP-requests that are responsed with JSON.
I was writing it all in Java EE-style, hoping it won't be hard to port to Java EE than. But then I've found out that my static instance variables for a couple of sinletons weren't created properly - classloader made a bunch of them when calling SingletonClass.getInstance() from servlets.
I was totally desperate and thought adding #Singleton descriptions would help. But things weren't so easy. My classes simply not working while adding them with #EJB ClassName var. Context lookup doesn't work either.
I was trying developing in Eclipse, NetBeans, used Glassfish, tried to set it up, but nothing really helped. I do not know what to do and really desperate now.
All I need is just few classes, that work all the time application is loaded to handle game events and hold logged users data (which is distributed in non-EJB objects that hold user data, monsters and so on), some timed events for each logged user and ability to respond to HTTP POST requests with JSON. I even do not need the ORM, I wrote all queries by myself, but still... Something that had to work simply doesn't work out.
I'm aware that all that sounds messy and non-informative, but I do not know what to do - where is my problem? Maybe, I should fill web.xml, or use different port, or fly to the moon? Or just change programming language? Sorry for your time spent reading this.
UPD. Some application scheme parts. First two from package "server".
#Startup
#Singleton
public class DbWrapper
handles all database connections, DbConnectionPool is non-singleton class, which handles pool of java.sql.Conneciton.
#Startup
#Singleton
#DependsOn("DbWrapper")
public class World
is yet another class to handle all the in-game events, that holds HashMap of logged users. User and Monster classes are from package "entities" (User holds a list of monsters).
Package "servlets" hold HttpServlet descendants, annotated #WebServlet("/pathname"), that try to use
#EJB World world
for example. But such things as world.getUser(id_user) simply won't work.
As for JDBC - postgres jar is included in GlassFish domain's /lib.
As for JSON - I use org.json found here: https://github.com/douglascrockford/JSON-java
I've found out that my static instance variables for a couple of singletons weren't created properly - classloader made a bunch of them when calling SingletonClass.getInstance() from servlets.
First, you should show us the code for one of these singleton classes. You may have made a mistake in the implementation that causes this problem.
It is true that you can get what appear to be multiple instances of a (properly implemented) singleton class in a servlet framework. But in fact they are not what they appear to be. What is actually going on is that you have loaded the class from multiple classloaders, either because you have multiple webapps each loading the class, or because you are redeploying your webapp and the previous deployment is not clearing up properly.
So what can / could you do about this?
You could use a dependency injection framework to configure your webapp, and hence avoid the need for singleton classes.
You could continue using singletons, but track down why you are getting multiple instances, and fix that problem.
You should use singletons really rarely (best would be not to use them). As an alternative use application scoped beans (#Singleton beans should normally work - they should use instance variables though, not static ones).
With Java EE 6 you also can use CDI and thus you don't have to use EJBs if you don't need the additional features they provide (like automatic transaction demarcation, security etc.) or can live with adding those features yourself.
Additionally, you can use CDI in a SE application. Keep in mind though, that you need to define the scope for CDI beans (e.g. #Application, #Request etc.) otherwise the default scope (#Dependant) is used which causes the beans to be copied on every access.

Categories

Resources