I have been reading the proposed final draft of the Java Platform Enterprise Edition 5 Specification (aka JSR 244) and noticed again that nothing has changed in the classloading area (OK, I did not send any feedback to the JSR group, so I’m guilty too). It is as vague as it has always been and do not impose anything which would clearly protect the deployed applications from server implementation libraries, otherwise known as class leaking.
From EE.188.8.131.52 Context Class Loader:
The classes used by an application will typically be loaded by a hierarchy of class loaders. There may be a top level application class loader, an extension class loader, and so on, down to a system class loader. The top level application class loader delegates to the lower class loaders as needed. Classes loaded by lower class loaders, such as portable EJB system value classes, need to be able to discover the top level application class loader used to dynamically load application classes.
This is clearly an insufficient requirement.
I clearly don’t want any class used within the container internals to leak until my applications.
So please just be more strict on these classloading requirements.
As a default, I want my container to just and only expose JSE and JEE APIs. Not more.
Otherwise you have NO guarantee that you are working in a strict isolated environment for each application.
Assume that your container is using commons-xxx-1.0 internally.
Within a most app. server classloader hierarchies, this will be made visible to your applications.
Now, assume that you have an application that also uses commons-xxx-1.0
For some reasons, the packaging messed up the ear (s* happens) and it is not included…it just work by accident.. maybe until you decide to upgrade your container to the next update that uses commons-xxx-2.0 and everything just stop working..or worth will introduce invisible erratic behavior in your application.
Assume your application ships with its own commons-xxx-2.0 jar, how do you know which one your application is using ? Which classloading model is your container using for your application ? Is it parent first (Java2 delegation model) ? or child first (servlet 2.3+ compliance) ?
You’ll know very well which model your container is using if, for instance, your application uses a commons-xxx-2.0 which is not API-compatible with commons-xxx-1.0. Everything will just break in your application, and any attempt to swap your 1.0 with 2.0 will just break your server internals too. This is guarantee to keep the lambda administrator busy for a while.
Assume your commons-xxx jar.. has a singleton in its API, and your application is doing bad things on it (write access) oops, you’re just messing with your server and all other applications. (No, Java security is not an option due to complexity)
JBoss AS has this well-know flat classloading mechanism as a default called: Unified ClassLoaders
People who have been working on it extensively know fairly well how such sneaky system is just plainly wrong and can create havoc in production if the people in charge do not know much about it (as a matter of fact, few developpers know about it, so you can imagine for administrators)
For example, I was just called recently by a friend whose customer has hundreds of JBoss server running (and incidentally is also a major JBoss Group customer), a couple of servers running a new web application could not be deployed without all the too commons errors (ClassCast, NoSuchMethod, etc…). My friend is a very competent administrator and knows also Java pretty well, but sure enough, this kind of issues was beyond his initial knowledge and now, he has to communicate all the way up to the developpers…and document the issue to potentially do something about the hundred of applications that are deployed and miraculously working !
To my knowledge (which is I admit out of date as I haven’t keep up with the latest dev, so please committers, feel free to comment if necessary), JOnAS and Geronimo are also suffering from exposing internals to their applications. Fortunately they don’t have this flat broken mechanism which is bound to create more problems. Details should probably be worth an entry of its own, meanwhile you can read about the Geronimo Classloader hierarchy here and the JOnAS one here
I tried to check a few months ago what was the Glassfish behavior but it actually was difficult to read the uncommented code, so I gave up, will do when I will have more time in my hands. But interestingly, there seems to be a bug in Sun AS8 (which hierarchy is detailed here)which prevents you to deploy 2 identical applications that have the same display-name. It assumes it is unique within the app. server and lookup where it should not… See this post.
As a matter of fact, I think the JEE specification must be more precise about Classloading hierarchy and require perfect isolation from server internals.
What should be the default delegation model within an EAR ? Good question. Here are my thoughts:
For the classloader to loads EJBs (and dependencies via the Class-Path extension mechanism) it should be servlet-2.3+ delegation model. This is to protect the application from potential accidental class leaking if you decide to use the extension mechanism of your container for other applications (generally dropping some jar files into a common/lib or similar)
For a WAR file, as a standalone file, servlet specifications obviouslly require a servlet-2.3+ delegation model.
Why should it be different if the WAR file is located within an EAR ? I cannot see any reason.
If you have many WAR files that have a lot of libraries in common between them, you MAY want to move the jar files up one level so that they be loaded by the parent classloader (that is, the classloader that loaded the EJB and its dependencies). You could save both disk space, memory and be able to communicate quickly within WAR by references rather than by value (serialization cost), but this is far from being a common case and this is a dangerous slope.
Same can be said about EAR files, at the container level, you could eventually move everything worthwile (don’t do that for stateful singletons eh ?) to your container commons/lib
This must clearly be an ‘optimization’ which could only be done if you really need and want it.
In short I would definitely favor safe and isolated applications as a default rather than worrying about speed or disk space or memory. If you need all that, you could use, if it exists, your container ability to change the delegation model to java2 and move everything up one level or directly into your container common/lib if you know what you are doing, but I would prevent that as a default, it must not be transparent as well to avoid mistakes and each EAR classloader MUST be configurable to expose the container common/lib .
To sum up:
- An application classloader must only expose the JEE API as a default.
- An application classloader must be configurable to expose the container content of commons/lib (must be off as a default, see above)
- Application classloader must use servlet 2.3+ delegation model as a default (but configurable to java2)
- WAR classloader must use servlet 2.3+ delegation model as a default (but configurable to java2)
Any comments ?