Create Lightweight Spring Plug-Ins—à la Eclipse

nterprise software products always require custom integration work at the customer site, and upgrades are nearly impossible when customers have to modify the core product’s configuration to introduce their customizations. By allowing for highly modular software that is easy to extend and upgrade, plug-ins offer a perfect solution for this all-too-common scenario (see “Sidebar 1. What Is a Plug-In?“).

With a plug-in environment that possesses cleanly defined extension points, the core product is free to undergo upgrades and the plug-ins themselves can be released and upgraded on an independent schedule. With my open source Classpath Helper project (based on Eclipse’s plug-in framework), I routinely upgrade Eclipse while also releasing newer versions of my own plug-ins.

For Java developers in particular, plug-ins provide a much better upgrade story than existing J2EE components (see “Sidebar 2. Aren’t J2EE Components Plug-Ins?“). Imagine having your EJBs built by different teams and then just dropping them into an application, knowing they will work. A plug-in architecture allows this level of componentization.

The popular Spring framework’s BeanFactoryPostProcessor interface enables developers to easily create a lightweight plug-in framework. . This article discusses how to do this, and it includes a working code sample that shows lightweight plug-ins in action.

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:

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:

                                                                        

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:

                                                                                        

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:

                                    

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.

Developing with Lightweight Plug-Ins

I’m always shocked at how little thought most architecture teams put into how easily developers can use their frameworks. EJBs are a perfect example of a spec that has academic merit, but whose real world development flaws make using it very expensive. With this in mind, I think it’s always important to ask what sort of burden or impact a framework carries when it comes to the typical code/build/debug cycle.

In this way, lightweight plug-ins are pretty painless. Think of each plug-in as its own build-able project that simply depends on the core product’s jars. This is very easy to model in a tool like Eclipse, where the core product has its own Java project and each plug-in has its own as well. You’d just need a final assembler project that depends on the core product and the various plug-in projects to include. By having the assembler project depend on the core and the plug-in projects, your classpath automatically will be built appropriately. The downloadable sample code includes a project layout like this. Keep in mind that you could have an assembler project for each customer, allowing you to combine plug-ins differently for different customers. This approach dovetails nicely with Eclipse, allowing incremental compiles and hot swapping of code during debugging. It keeps your development process agile by not adding build steps that stymie Eclipse’s native Java support.

Is Everything Always a Plug-In?

One tenant of Eclipse is that everything is a plug-in (see “Sidebar 3. Comparison with Eclipse Plug-Ins“). From the initial bootstrap of the system, to the Java development environment, to the online help?every contribution of code (even if that code is not Java code) is modeled as a plug-in. This approach has its elegance, but it dictates an industrial plug-in development environment complete with tools for managing the components, debugger support, etc. To their credit, Eclipse provides this, but server-side frameworks with this level of support do not exist (to the best of my knowledge).

In server-side development, the established components of EJBs, JSP/Servlets, etc. don’t lend themselves well to true plug-ins, which require real work to define and document an extension point. So treating everything as a plug-in would add significant work that probably wouldn’t look very familiar to most J2EE engineers (a legitimate concern when trying to staff a team).

With that in mind, I try to use the plug-in concept as a tool to target specific areas of customization. As such, leveraging Spring to create lightweight plug-ins that seamlessly work with your existing application and technology makes them very compelling. For the most part, your application remains a fairly vanilla Spring/J2EE application. This is not a trivial accomplishment, as in-house frameworks often crumble under their own weight as lack of documentation and attrition make them more of a burden than a benefit.

You should also be aware of alternative plug-in frameworks. In particular, the Java plugin framework project comes to mind http://jpf.sourceforge.net/. A 10-minute tour suggests that it is based on Eclipse style plug-ins. I’ve never used this framework to see if it plays nicely with Spring and/or how much rethinking your application would require to adopt it. But if Spring-based plug-ins are too light for your tastes, this might be a nice option.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

homes in the real estate industry

Exploring the Latest Tech Trends Impacting the Real Estate Industry

The real estate industry is changing thanks to the newest technological advancements. These new developments — from blockchain and AI to virtual reality and 3D printing — are poised to change how we buy and sell homes. Real estate brokers, buyers, sellers, wholesale real estate professionals, fix and flippers, and beyond may

man on floor with data

DevX Quick Guide to Data Ingestion

One of the biggest trends of the 21st century is the massive surge in internet usage. With major innovations such as smart technology, social media, and online shopping sites, the internet has become an essential part of everyday life for a large portion of the population. Due to this internet

payment via phone

7 Ways Technology Has Changed Traditional Payments

In today’s digital world, technology has changed how we make payments. From contactless cards to mobile wallets, it’s now easier to pay for goods and services without carrying cash or using a checkbook. This article will look at seven of the most significant ways technology has transformed traditional payment methods.