Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Create Lightweight Spring Plug-Ins—à la Eclipse : Page 2

Leverage the Spring framework as a platform for creating lightweight plug-ins that work seamlessly with your existing J2EE applications.


advertisement

Get Your Platform Ready for Plug-Ins

Before your platform can support pluggable components, it needs to meet the following two criteria:
  • The component must be auto-discovered. You've already read how J2EE components fail to qualify as plug-ins for this exact reason. You'll typically find an external configuration file that needs to be updated to make the platform aware of the new code.
  • The component must contain enough information to integrate or configure itself within the application.

If you were just adding code that didn't need to collaborate in the system (i.e., loosely coupled), the auto-discovery would be simple. The challenge is auto-discovery combined with tight integration.

Auto-Discovery in Spring
Spring, it turns out, is actually pretty close to supporting plug-ins out of the box. Spring already can store configuration in several bean context files, and it makes auto-discovering configuration files very easy. For example, the following Spring statement auto-discovers any file ending with ctx.xml that exists under the META-INF/services directory of the classpath:


<import resource="classpath*:META-INF/services/*.ctx.xml" />

This existing functionality is a key feature you will leverage when building your lightweight plug-in framework.

Still, Spring does not concern itself with auto-discovery of code. This is usually not a problem because most J2EE containers provide a lib directory, where any jars placed in them are automatically added to the classpath. This means if you target your code contributions as jars, it should be pretty easy to achieve auto-discovery in any sort of J2EE container.

Outside of an application server, it's also quite easy to achieve jar auto-discovery with a tool like ant. The following Apache Ant XML detects all jars in the lib directory in a fashion similar to an application server:

<path id="classpath"> <fileset dir="${basedir}/lib"> <include name="**/*.jar"/> </fileset> </path> <target name="start.server" description="launches the server process"> <java classname="platform.bootstrap.Server"> <classpath refid="classpath" /> </java> </target>

So although Spring doesn't support auto-discovery directly, by leveraging standard technologies you can easily make your code auto-discoverable. That, combined with Spring's ability to auto-detect configuration, gives you both code you can contribute and code that will be discovered and activated in the system.

Self-Configuration in Spring
The second addition you need to make is the ability for the plug-in to self configure. Although Spring doesn't support this out of the box, it provides the tools to make it fairly straightforward. The key piece of infrastructure is the BeanFactoryPostProcessor, an interface that Spring invokes after all of the configuration has been discovered and loaded into an in-memory representation, but before the actual objects are created.

By using a BeanFactoryPostProcessor, you can dynamically wire together beans without modifying the original file system configuration. The following code is the guts of my BeanFactoryPostProcessor implementation, PluginBeanFactoryPostProcessor (The entire class is provided with the downloadable example.):

private String extensionBeanName; // set via spring (setter not shown) private String propertyName; // set via spring (setter not shown) private String pluginBeanName; // set via spring (setter not shown) /* * (non-Javadoc) * @see BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory) */ public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { // find the bean definition we wish to modify BeanDefinition beanDef = beanFactory.getBeanDefinition(extensionBeanName); // within that bean def look for its properties and find // the specific property we will modify. MutablePropertyValues propValues = beanDef.getPropertyValues(); if ( !propValues.contains(propertyName)) throw new IllegalArgumentException("Cannot find property " + propertyName + " in bean " + extensionBeanName); PropertyValue pv = propValues.getPropertyValue(propertyName); // pull out the value definition (in our case we only supporting // updating of List style properties) Object prop = pv.getValue(); if ( !(prop instanceof List)) throw new IllegalArgumentException("Property " + propertyName + " in extension bean " + extensionBeanName + " is not an instanceof List."); // add our bean reference to the list, when Spring creates the // objects and wires them together our bean is now in place. List l = (List) pv.getValue(); l.add(new RuntimeBeanReference(pluginBeanName)); }

Here's how the configuration would look in Spring. First, in your core project you define the extension point, an instance of example.craps.Table with two of its properties (dice, players) configured with empty lists. This is standard Spring usage:

<beans> <bean id="extension-point.craps.table" class="example.craps.Table" init-method="init"> <property name="dice"> <list> </list> </property> <property name="players"> <list> </list> </property> </bean> </beans>

Now, you package a jar with the plug-in classes, along with its Spring context (that will be auto-discovered), and you could have a configuration that looks like the following:

<beans> <bean id="real-dice" class="example.craps.RealDice" /> <bean class="platform.spring.PluginBeanFactoryPostProcessor"> <property name="extensionBeanName" value="extension-point.craps.table" /> <property name="propertyName" value="dice" /> <property name="pluginBeanName" value="real-dice" /> </bean> </beans>

This Spring configuration defines an instance of example.craps.RealDice, and then it defines your PluginBeanFactoryPostProcessor, which is configured to find the extension-point.craps.table bean. It also will add your real-dice bean to that craps table's dice property.

This is really the 'a-ha!' part of the article. This small extension to Spring is all that is required to write plug-in based components. Notice that if you remove the jar file containing this Spring context, you also unlace your bean from the extension-point.craps.table bean. Add the jar back in and it laces itself back into the appropriate point in the system.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap