简体   繁体   中英

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:

  1. 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?
  2. What is the best way to implement this if I absolutely have to?
  3. How do I cater for casting in a Servlet, if I want to get and set objects into a session?
  4. Will I end up with issues with memory leaks, and PermGen space?

I would really appreciate any suggestions. Thanks :)

  1. 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.

  2. 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 (ie, the ones with the singleton), and it possible, have the implementation class implement an interface that is loaded from the web application class loader.

  3. 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.

  4. 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 (eg, 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM