[jboss-dev-forums] [Embedded JBoss Development] - Re: Embedded Server and classloading issues

ALRubinger do-not-reply at jboss.com
Mon Nov 30 01:06:00 EST 2009


These are good points that I've been trying to push off until a later release, but I think it's important enough to address for the upcoming EmbeddedAS 1.0.0-alpha-1.

https://jira.jboss.org/jira/browse/EMB-73

A bit of an overview first.

Booting AS is a tricky business which involves careful placement of libraries into the proper ClassLoaders.  If we leak out references in a parent CL to something that's visible to a child, then NCDFE pops up.  This is why we have the ugly DEFAULT_LIBRARY_BOOT_LIST in org.jboss.Main.

The first hacky solution was a sledgehammer approach where *all* AS dependencies make their way into the application classpath, hence the AppCL can see everything.  This is what's done in the EmbeddedAS "testsuite" module for instance, where the sole dependency declaration is upon "depchain".

EMB-73 introduces a better mechanism whereby the client becomes responsible for:

1) Putting EmbeddedAS APIs and referents on the application classpath (so the test may be loaded)
2) Constructing a URLClassLoader armed with the AS boot libraries required for the server to start
3) Loading a server instance and starting it within the context of that CL.

The overall process looks like this:

     // Get JBOSS_HOME
  |       final URL jbossHome = this.getJBossHome();
  | 
  |       // Make the new ClassLoader
  |       final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
  |       final ClassLoader jbossHomeClassLoader = JBossHomeClassLoader.newInstance(jbossHome, additionalUrls
  |             .toArray(new URL[]
  |             {}), oldCl);
  | 
  |       // Make Server
  |       server = JBossASEmbeddedServerFactory.createServer(jbossHomeClassLoader);
  | 
  |       // Start
  |       log.info("Starting Server: " + server);
  | 
  |       // Set TCCL
  |     Thread.currentThread().setContextClassLoader(jbossHomeClassLoader);
  | 
  |       try
  |       {
  |          // Start the Server
  |          server.start();
  |          log.info("...started.");
  | 
  |          // Test
  |          TestCase.assertEquals("Server did not report started as expected", LifecycleState.STARTED, server.getState());
  | 
  |          // Shutdown if started
  |          if (server != null && server.getState().equals(LifecycleState.STARTED))
  |          {
  |             // Shutdown
  |             log.info("Shutting down server: " + server);
  |             server.shutdown();
  |          }
  |       }
  |       finally
  |       {
  |          // Reset the TCCL 
  |          Thread.currentThread().setContextClassLoader(oldCl);
  |       }

And the application classpath has very little on it; only API-level stuff:

[alr at localhost testsuite-jbosshomecl]$ mvn dependency:tree
  | [INFO] +- org.jboss.embedded:jboss-embedded-api:jar:1.0.0-SNAPSHOT:test
  | [INFO] |  +- org.jboss.bootstrap:jboss-bootstrap-api-as:jar:2.0.0-alpha-2:test
  | [INFO] |  |  \- org.jboss.bootstrap:jboss-bootstrap-api:jar:2.0.0-alpha-2:test
  | [INFO] |  \- org.jboss.shrinkwrap:shrinkwrap-api:jar:1.0.0-alpha-2:test
  | [INFO] \- junit:junit:jar:4.6:test

Note that no longer are we polluting the classpath or repackaging binaries in this approach.  Looking forward I want AS Main to build off this same structure.

Regarding your questions, in general doing the type of testing of AS internals is a bit risky because if your test relies upon nonpublic code, it's likely that you'll need to load in nearly all of AS libs on the AppCL.  Even one reference to the MC Kernel infers things like logging, jboss-man, jboss-dep, vfs, aop, etc...so for this usage I think "depchain" is the easiest way to get everything in one go.

I don't have a specific solution to the versioning problems you illustrate.  AS very carefully selects its thirdparty dependencies and once we start changing things, I expect havoc.

"Jaikiran" wrote : 1) Relying on Maven (or more generically a dependency resolution tool) to bring in the right set of dependencies to boot the AS *from within some other project* probably isn't the right thing? i.e. project A which is a client of the Embedded server API, might need it's own version of an artifact which conflicts with the AS required version of the same artifact. And then letting the build tool decide which one is best out of these 2 versions could lead to issues.
  | Infact, in this scenario, *both* the versions should be brought in, for both the AS and the client application to work as expected (see #2 below). 

Agreed.  In the case of "depchain", I the idea isn't that the build tool is deciding upon the dependencies, only that it's configured to pull in the same ones that AS defines.  You should get the dependency set of AS in the end.  The other side of the coin there is that if you override the version of some artifact which is defined by AS, you open yourself to problems.

"jaikiran" wrote : 2) Theoretically, when the AS is being created/booted it should use its own different classloader than the classloader of the application which is triggering the boot. This way the client application can have it's own different versions of the same artifacts which the AS requires. Probably, the Embedded server implementation should internally create a classloader out of the right set of jars (/locations) for the AS and then use that classloader for AS boot/deploy and other operations. How to get hold of the right set of jars is a different step (step#3 below). 

Yep; I just hadn't provided that until tonight. :)  Your post here lead me to reconsider the ship criteria for 1.0.0-alpha-1, so take a look at:

http://anonsvn.jboss.org/repos/jbossas/projects/embedded/trunk/testsuite-jbosshomecl/ 

...to ensure this works similarly to how you'd expect as well.

"jaikiran" wrote : 3) Maybe if the Embedded server API knows of JBOSS_HOME locations, then probably it can create the classpath for the AS based on the known library locations of the AS (JBOSS_HOME/lib, JBOSS_HOME/common/lib etc...)? But this is going to tie the Embedded server implementation to a specific version of AS, since if tomorrow the AS decides to have it's libraries in a different location then Embedded server implementation will be affected too. Actually, it needs a bit more thinking, to get a solution. 

I think this is the same/similar to your question and my proposed solution above, no?

Input welcomed, we release soon.

S,
ALR

View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4268010#4268010

Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4268010



More information about the jboss-dev-forums mailing list