Skip to content

Dynamically loading Spring contexts from the classpath at runtime

I’m just going to document a way I’ve found to use Spring ApplicationContext to dynamically load other context XML configurations that it find in the classpath. We have a requirement to do this coming up on a product we’re building. Let me describe the sort of problem we are trying to solve (with many specifics omitted or glossed over):

There is a web service inside a component, let’s call that component a ‘Node’, that receives something like (but not identical to!) an Event on its interface. Inside the Event is some data that the Node does not particularly care about (and actually has no access to – it’s just a byte[] as far as the Node can tell). However, the Node contains a Registry which enables components, lets call them Event Handlers, to register themselves with the Node, as available to process certain Events (i.e. decode that byte[] and do something with it) according to criteria which the EventHandler injects into the Node’s Registry.  EventHandler is an interface with a handful of simple methods related to handling the Event, and also registration with the Registry.

So, the process flow looks something like this: The Node first records the reception of the Event at the interface in a log. Then it tells the Registry about the Event, and the Registry produces the EventHandler(s) it needs to use.  The Registry gives the Node back the instances of the EventHandler interface. The Node then hands off the Event to the EventHandler, which does whatever it does unbeknownst to the Node, and returns a fairly simple EventResponse object. The Node records the EventResponse object in its log (i.e. a database) and returns it as the response to the web service call.

Consider the Node service method as looking something like this (you’ll have to excuse the Java 1.4-ness of this code, the WordPress code highlighting plugin apparently hates Java 5) :

  public List receive(Event event) {
    List responses = new ArrayList();
    for (EventHandler handler : this.registry.lookup(event.getMetadata())) {
      Response response = handler.handle(event);
      saveResponse(response, event, handler);
      responses.add(response);
    }
    return responses;
  }

To enable the wiring, the Registry has a method:

  void register(EventMetadata metadata, EventHandler handler)

and among other methods the EventHandler interface defines:

  void setRegistry(Registry registry)

Currently the WAR file imports the Node.JAR and a group of EventHandler.JAR files which are specific implementations for handling different kinds of Events. We configure it in Spring currently so that the specific EventHandler is injected with the Registry object (from the Node.JAR). The EventHandler implementation then registers itself with the Registry in a call-back operation, telling it what sort of Events it will handle.

This all works just fine at the moment. The problem with what we have is that it is all currently statically compiled into the WAR file.The WAR file specifies a Spring application context XML file which in turn loads the Spring configuration for the Node and Registry component, and every Spring application context for each Handler JAR included inside the WAR file’s WEB-INF/lib directory.

Now, we now don’t want every deployed instance of every Node to handle every possible Event. Currently we’ve got a small .properties file that actually tells another Spring component which EventHandlers are be to be instantiated or not. This is working fine when we only desire some Nodes to handle maybe one or two of a larger group of related Events. That is, where we currently don’t mind that the WAR file is identical in every respect on every Node — it’s just that each node contains a .properties file in its classpath that tells it which EventHandlers it is allowed to load and use (and therefore what Events it is capable of receiving, bearing in mind that when I say ‘Events’ there is really only one concrete type of Event, I mean the encrypted data which is held within the Event which is actually consumed by the Handler).

However, we are now in a situation where we want to use this same architecture for a completely different group of Events. We definitely don’t want to have to compile and assemble a new WAR file for different Nodes based on the Event cluster. We want to deploy a standardized Node.WAR which has available on its classpath a dynamic set of XxxEventHandler.JAR which can dynamically register themselves with the Node’s registry. There may be also a requirement for some related custom extension points in the future.

Initially we considered OSGi as the technology to enable this. After some discussion yesterday with people who know better about OSGi, this approach was rejected as impractical for the moment. Therefore last night I set out tooling about with the Spring 2.5.6 ApplicationContext and its related objects to see what could be done to enable dynamically-loaded JAR files within a parent Spring application context. Here is what I’ve discovered we can do, with only some small restrictions on developers writing the individual EventHandler implementations.

The first issue is, we need to locate a set of Spring application context XML file which should be on the classpath but not yet instantiated into any live Spring context.

Assuming we’re in a bean that’s ContextAware or otherwise has access to the Spring application context which is loading it, after the parent context has loaded and initialized (there are method hooks for this sort of thing) we can use the org.springframework.core.io.support.PathMatchingResourcePatternResolver class to search the classpath for a resource:

  PathMatchingResourcePatternResolver pmrl = new PathMatchingResourcePatternResolver(context.getClassLoader());
  Resource[] resources = pmrl.getResources(
    "classpath*:/net/crazymcphee/dynamiccontext/*/Crazy*DynamicContext.xml"
  );

Looking at the Spring 2.5.6 source code we found that the String passed to the getResources() method isn’t a proper regular expression, which is a pity. So you can’t do something like look for **/Crazy*DynamicContext.xml and expect to match a file Crazy*DynamicContext.xml in any package. Also we found that it had to prefixed with that classpath*: … yes, the asterix, literally … else it wouldn’t search the classpath, as opposed to the file path. So we’re restricted in the above example to a file called Crazy<something>DynamicContext.xml in a package exactly one deep from net.crazymcphee.dynamiccontext … e.g. net.crazymcphee.dynamiccontext.package.CrazyMcpheeDynamicPackage.xml matches but net.crazymcphee.dynamiccontext.package.sub.CrazySubDynamicPackage.xml and net.crazymcphee.dynamiccontext.CrazySuperDynamicPackage.xml do not. Therefore we will have a restriction on what package the dynamic context can be in and what it’s name will be. I don’t think that’s too onerous on our developers – we just have to pick a sensible standard.

The next part of the problem is that we have to load the ‘Resource’ thus found into a Spring context. This is pretty easy:

 for (Resource r : resources) {
   GenericApplicationContext createdContext = new GenericApplicationContext(context);
   XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(createdContext);
   int i = reader.loadBeanDefinitions(r);
 }

The int i will be set to the number of beans found in the createdContext. The createdContext will have the original context as its parent context, so it can gain access to any beans defined there (and also, although we are yet to test this (!), it should also be intercepted by the AOP-based transaction interceptors in the parent, and so forth).

The only other part of the puzzle may be to query the createdContext to see if it has any target beans within it, luckily for us an ApplicationContext has a method getBeansOfType(Class clazz) which will load all the beans of a particular type:

  for (Resource r : resources) {
    GenericApplicationContext createdContext = new GenericApplicationContext(context);
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(createdContext);
    int i = reader.loadBeanDefinitions(r);
    Map map = createdContext.getBeansOfType(EventHandler.class);
    for (Object o : map.keySet()) {
      EventHandler handler = (EventHandler) createdContext.getBean((String) o);
      // do some programmatic manipulation with the EventHandler that we found here
    }
  }

Using these three Spring features will enable us to be able to place a JAR file containing an interface implementation, and a Spring context XML file matching a particular pattern, into the classpath of our WAR, and on restart, we can dynamically pick up the newly inserted features into our application installation. I’ll report back when we get an actual production prototype together that can do this. Hopefully it will be OK to put the code in the blog too (if we make it generic enough).

If you have any further ideas or refinements to this idea, please leave them in the comments!