Tutorial

Tips for Writing Pluggable Java EE Applications

Chances are good, if you've been writing complex systems long enough, that you've had either the need or desire to implement some sort of plug-in support for your system. Unfortunately, it can be a daunting task that is often left undone. The existence of plug-in systems in the various major IDEs and continuous integration servers such as Hudson show that it is possible.

However, these systems are usually either overly complex or tailored to their specific problem domain. The lack of an existing off-the-shelf solution need not cause you to abandon your efforts. As it turns out, Java EE delivers technologies, such as JSF, CDI, and JAX-RS, that make it surprisingly simple.

In this article, we'll take a brief look at how you might apply these technologies to build your own lightweight plug-in system, with a minimum of external dependencies.

Step 1: Loading the Plug-ins

Perhaps the first question one should ask is: “How do I load the plug-in data?” After all, if you can’t load the plug-ins, who cares if you can write them? There are many, many options, of course, but we’ll take a look at three, to keep things simple: repackaging, manual class loading, and OSGi.

Repackaging

Perhaps the simplest, safest approach is simply to repackage the application. This is exactly what it sounds like: you take the original application archive, and .war, for example, and add the plug-in jars. This avoids some of the potential problems we’ll see in other approaches, as well as pushing the heavy lifting on to your container. You can do this through some sort of Ant- or Maven-based system, or a simple shell script.

#!/bin/bash
DIST=$1
if [ "$DIST" == "" ] ; then
    echo "You must specify the distribution .war"
    exit 1
fi
BASE=`echo $DIST | sed -e 's/\.war//'`
rm -rf work
mkdir work
cd work
jar xf ../$DIST
cp ../plugins/*jar WEB-INF/lib
jar cf ../$BASE-repackaged.war *
cd ..
rm -rf work

The upside to this is that you maintain access to all of the technologies provided by your container: EJB, JMS, etc. The downside is that deployment/upgrade takes a bit more work, and means that if you forget to run this process, you’ve suddenly lost all of your plug-ins. It is not a major concern, but certainly something to be aware of.

Manual ClassLoading

From a technical perspective, this is my favourite approach, simply because of the pure hardcode geekiness of it. In a nutshell, we read the bytecode from the jar file(s), and load it into the context ClassLoader. Fraught with peril, sure, but fun nonetheless.

As a proof of concept for this approach, I have a small project called Plummer, so called because it provides some of the plumbing for the plug-in system. Before we dive into what Plummer provides, we should note that the plug-in approach we’ll look at is based very heavily on CDI, a new specification in Java EE 6. What we need to do in this part of plug-in equation is to provide these plug-in classes to the CDI runtime. In Plummer, this happens in the PluginLoader class.

public class PluginLoader implements Extension {
    protected static final String SERVICES_NAME =
    "com.steeplesoft.plummer.finders";
    private static final Logger logger =
    Logger.getLogger(PluginLoader.class.getName());
    private static List<PluginFinder> pluginFinders;
    List<Class<? extends PluginFinder>> pluginFinderClasses;




    public void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd,
          BeanManager beanManager) {
        for (PluginFinder pluginFinder : getPluginFinders()) {
            try {
                for (Class<?> clazz : pluginFinder.getClasses()) {
                    final AnnotatedType<?> annotatedType =
        beanManager.createAnnotatedType(clazz);
                    logger.log(Level.INFO, "Adding AnnotatedType for {0}",
        annotatedType.toString());
                    bbd.addAnnotatedType(annotatedType);
                }
            } catch (Exception ex) {
                Logger.getLogger(PluginLoader.class.getName())
      .log(Level.SEVERE, null, ex);
            }
        }
    }
    // ... 
}

Here we see CDI show up for the first time. What we have here is a CDI portable extension (for more information, see the CDI RI (Weld) documentation). We also see another CDI concept that we’ll revisit in the next section, Events. Since this is a portable extension, the class is loaded by the runtime very early in the application startup process. In this case, our Extension observes the BeforeBeanDiscovery event, as you can see in the aptly-named method. This method asks the system for a list of the PluginFinder instances configured in the system (I got a little over-excited and added in the ability to have more than one, just in case). It then asks each PluginFinder for a list of the classes it found. For each class, it creates an AnnotatedType, which it then adds to the BeforeBeanDiscovery instance. When this method exits, the CDI runtime will, eventually, scan our plug-in classes for CDI annotations, and, voila! Our plug-ins are registered with the system.

The bytecode loading is handled, in this case, by FilesystemPluginFinder. This class iterates over a list of jar files found in, by default, ~/.plugins. For each .class file in each jar, the bytes are read and the class is defined in the ContextClassLoader. We’ll not show how that happens here, but I will say that it seems to work pretty well. I’ve not found any issues with it so far, but I also have not been able to drive it very hard yet.

When using this approach, it’s important to note some of its restrictions. Given how and when the classes are loaded, certain Java EE technologies are likely not available. Namely, this includes EJB, JMS, etc., as the related containers are unable to scan the classes for annotations. I have not tested JPA support yet. CDI seems to be completely and well supported.

OSGi

It seems that any discussion of plug-ins (or modules, if you will) would be incomplete without talking about OSGi. As I started thinking about this topic, Web Application Bundles came to mind pretty quickly. I will admit that I am far from an OSGi expert, but it seems that WABs aren’t quite what we want. In fact, they seem to be designed to solve different issues. WABs and Plummer are not mutually exclusive, however. In fact, I have very young, incomplete code in Plummer to allow a system to deliver plug-ins as OSGi bundles.

Plummer assists in this by providing, in plummer-api, the PluginActivator class. This bundle activator runs, of course, when the bundle is started. You can look at the code for details, but it passes the Bundle to Plummer’s PluginTracker, which calls Bundle.findEntries() to find the class files in the bundle. The information is stored, and ultimately passed to the PluginLoader mentioned above.

In terms of drawbacks, this one has the most severe of the approaches we’ve discussed. In addition to having the same limitations (most likely) as the manual class loading approach, it has one even more significant one: It does not currently work. I think the theory/approach is sound, but I have not had the chance to finish and test the code to date. For those interested, you know what to do.

Step 2: Application Design

Now that we’ve answered any questions anyone would have –?ever –?about plug-in loading, let’s spend some time on the more interesting part: How do I design the plug-ins? I would like to note that it may not be possible (or, at the very least, extremely difficult) to write a complete, generally useful plug-in system that works across applications and problem domains. If someone were determined enough to prove me wrong and actually did just that, I’d be willing to bet that it would be difficult to use, heavy, etc. If this person were truly determined, gifted, etc. to prove me wrong yet again, well… good for him.) For the rest of us (or, in this case, just me), Java EE makes this so easy that you really don’t need any extra frameworks. To attempt to prove my assertion, we’ll take a look at three Java EE technologies: JSF, CDI, and JAX-RS.

View Extensibility

Let’s start by taking a look at view extensibility, as, after all, even if you have the greatest plug-in system in the world, if it can’t affect the view, then it’s not worth much. To demonstrate this technique, we’re going to use JavaServer Faces, as it is the Java EE standard for web applications. You may prefer another framework, such as Spring MVC, Wicket, or GWT, or you may even be using desktop technologies such as Swing, SWT, or JavaFX to build views for your Java EE application. The technique here should work the same regardless of framework, more or less. You’ll just have to determine how to integrate it into your technology of choice.

For a plug-in to add content to the view, it will have to provide what we will call viewfragments. These fragments are exactly what they sound like, small pieces of UI widgets that are added at specific points in the view. These fragments are categorized, by the plug-in, into types, as defined by the consuming application. This means that the app might declare the types tabtreeNode, and widget. A plug-in, then, might add a tab to a configuration page, a treeNode to the navigation system, and a recent tweets widget to the sidebar. As we’ll see, how complex or simple the categorization/differentiation exposed by the application is is completely up to you as the application author/architect.

Having defined the terms, then, how might one implement this? First off, let’s take a look at ViewFragment.java in the plummer-api module.

public @interface ViewFragment {
    String type();
    String parent() default "";
}

This simple interface defines type and parent properties. A plug-in author would use it like this:

public class SamplePlugin implements Plugin {
@ViewFragment(type="foo")
public static String sample1 = "sample1.xhtml";
@Override
public int getPriority() {
return 500;
}}

There are several things to note here. First, let’s look at the annotation. Here, we are defining a ViewFragment of type foo. It is attached to a public static final String, whose value is sample.xhtml. When the system processes this annotation, it will store the value sample.xhtml in a Map, keyed by the value foo. When the view asks for view fragments of type foo, this piece of markup will be included. That file, by the way, is a simple JSF 2 Facelets file (listing 5).

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:fragment xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h1>Plugin Fragment</h1>
This text comes from a fragment. Shiny!
</ui:fragment>

Very simple. The question that should come to mind now is, “How does the system find this annotation, and then how do I tell the system to insert this into my view?” The answer to the first half of that question is the Plugin interface. Those interested in the nitty gritty can read the code (PluginService.java in plummer-kernel), but for those not that curious, CDI again saves the day. In a nutshell, we ask CDI for all the beans that implement Plugin, scan them for fields annotated with ViewFragment, and store the metadata. On the view side, we use the pl:viewFragment custom component that Plummer offers:

 <pl:viewFragment type="foo"/>

The system does the rest. What you put in your view fragments is completely up to you. We’ve put everything from simple markup to `h:form`s with no known issues. One note with regard to resources: since the resources are stored in JARs and not in the application’s document root, you will need to use JSF 2’s resource mechanism to reference images, javascript, CSS, etc.:

<h:graphicImage value="#{resource['myImage.JPG']}" height="200"
title="Here's a picture of something really cool!"/>

You can see a complete example of this in plummer-sample2. One final note before we move on: The code in Plummer is mostly standards-compliant, but some Mojarra-specific classes needed to be used to get access to the FaceletFactory needed to insert the view fragments into the component tree. MyFaces users can still use Plummer, but someone will need to implement the MyFaces-specific code to reproduce this functionality. Maybe someday the spec could expose this kind of functionality, but, for now, we have to address this on a per-implementation basis.

Application Extensibility

The real work, of course, is done at lower levels. Here we’ll see just how simple Java EE makes things. Specifically, we’ll look at two parts of CDI, events, and what we’ll simply refer to as programmatic bean lookup.

CDI events is, conceptually, just a simple pub/sub system. One part of the system fires, or publishes, events, and another observes (subscribes). This makes it very easy to loosely couple parts of the system: the core of your application need not worry about what, if anything handles, the event. It also easily allows multiple recipients to respond to the event fired. Again, the system doesn’t care. In Ron Popeil style, you just “set it and forget it”.

So what does this look like in practice? To demonstrate that in a meaningful way, we need a sample application, so we’ll write a very simple blogging system. If you’ve ever interacted with a blog, either as an author or a reader, you’ve likely seen the option by which a user can subscribe and get notifications of new posts. Let’s implement that. First up, we’ll need a way to create blog entries. You can find this BlogBean.java in the webapp, but here are the interesting parts:

//... 
@Inject
private Event<BlogPostedEvent> blogPostedEvents;

public String addEntry() {
   entries.add(entry);
   blogPostedEvents.fire(new BlogPostedEvent(entry));
   entry = null;
   return null;
}
//...

For the sake of brevity here, you can find the view in examples/webapp/src/main/webapp/blog.xhtml. First, notice the @Inject. Here, we’re asking CDI to inject an Event that takes a BlogPostEvent payload. We use this in addEntry(), when we call blogPostedEvents.fire(new BlogPostedEvent(entry)). The code, simple as it is, should be pretty self-explanatory: We’re firing an event of type BlogPostedEvent, which looks like this:

public class BlogPostedEvent {
    private String blogEntry;
public BlogPostedEvent(String blogEntry) { this.blogEntry = blogEntry; } public String getBlogEntry() { return blogEntry; } }

In this example, our payload is very simple. In a real world, this could be much more complex if your application’s needs warrant. Responding to this event is just as simple as firing it:

public void sendEmail(@Observes BlogPostedEvent event) {
    emailService.sendEmail(event.getBlogEntry());
}

That’s really all there is to it. By using CDI events, we are able to push data into our plug-ins in a loosely coupled manner. Again, in a real world application, the data push and the processing required to handle will likely be more complex, but the means of pushing it will not be. CDI for the win!

Perhaps you need to allow a plug-in to process data in the system. For example, in our system we want allow plug-ins to translate the blog entry into another language. To do so, we first need to define the interface by which the plug-in will be called:

public interface BlogEntryProcessor extends Serializable {
    String getName();
    String process(String text);
}

From our blogging system, we can get a list of all of the BlogEntryProcessor instances, if any, with this CDI injection:

@Inject @Translator
Instance<BlogEntryProcessor> translators;

This gives us an Instance instance that contains any BlogEntryProcessors defined in the system. We'll come back to @Translator in a bit. Next, we can provide a way for the user to pick a language with this code, using both JSF markup:

<h:form>
<h:selectOneMenu value="#{blogBean.translator}" converter="#{translatorConvertor}">
<f:ajax render=":entries" event="change" execute="@form"/>
<f:selectItems value="#{blogBean.translators}" var="t" itemLabel="#{t.name}" />
</h:selectOneMenu>
</h:form>

And also managed bean code:

public List<BlogEntryProcessor> getTranslators() {
List<BlogEntryProcessor> list = new ArrayList<BlogEntryProcessor>();
for (BlogEntryProcessor t : translators) {
list.add(t);
}
return list;}

This lets us change the language, but how do we get a default? Let’s define a Qualifier.

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface English { }

This simple class lets us differentiate at injection time:

@Inject
@English
private BlogEntryProcessor translator;

Instead of injecting Instance, we’re injecting a single…instance. Since there might be more than BlogEntryProcessor on the system, we have to qualify which one we mean.

@English
@Singleton
@Translator
public class EnglishTranslator implements BlogEntryProcessor {
// ...
}

EnglishTranslator is a BlogEntryProcessor, and it has been marked as @English, which means this instance, which is also a singleton, will satisfy the injection above. We could have annotated this with @Default, both here and at the injection point, but the creation of a custom @Qualifier is a good exercise.

But what’s up with that @Translator? That’s another @Qualifier, which must be applied to any BlogEntryProcessor that is intended to act as a translator (and which we document clearly in our system documentation, right?). Why is that important? In a simple system, we wouldn’t need that, but we’re going to intentionally muddy things a bit and introduce a different type of BlogEntryProcessor, one which allows for tags.

One common type of plug-in in systems like WordPress allows a user to wrap certain text in a tag. This entry, for example, uses the code tag to get syntax highlighting. In our system, we’ll implement a tag that creates links to Google Maps. For example:

Disneyland can be found at [map]1313 North Harbor Boulevard, Anaheim, CA[/map].

How is this implemented? Just like the translators:

@Tagpublic class GoogleMapsProcessor implements BlogEntryProcessor {
@Override
public String getName() {
return "Google Maps Processor";
}

@Override
public String process(String text) {
Pattern pattern = Pattern.compile("\\[map\\](.*?)\\[\\/map\\]");
String replaceStr = "<a href=\\\"https://maps.google.com/maps?q=$1\\\">$1</a>";
Matcher matcher = pattern.matcher(text);
String result = matcher.replaceAll(replaceStr);
return result;
}
}

This looks just like the translators, right? The only difference is the @Tag qualifier:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Tag {
}

In BlogBean, we access it and the translators in getEntries():

@Inject
@Tag
Instance<BlogEntryProcessor> tags;
public List<string> getEntries() {
    List<String> list = new ArrayList<String>();
    for (String text : entries) {
        for (BlogEntryProcessor tag : tags) {
            text = tag.process(text);
        }
        text = translator.process(text);
        list.add(text);
    }
    return list;
}

You can build and deploy the system to see this in action. Very simple, but very effective.

REST Resources

We’ve seen how to expose functionality to plug-ins loaded in the system, but what if we want to allow these plug-ins to expose this functionality to external clients, say, via REST? Again, Java EE makes this incredibly simple, using two specs in concert, CDI and JAX-RS.

One of the ways one might configure a JAX-RS application is to provide a custom Application class, one which extends javax.ws.rs.core.Application. Plummer provides such an Application, so all Plummer users need do is configure it in the web application (listing 16).

<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>
org.glassfish.jersey.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>
javax.ws.rs.Application
</param-name>
<param-value>
com.steeplesoft.plummer.kernel.rest.RestApplication
</param-value>
</init-param>
</servlet>

Oddly, note that the Application class is standardized, but the REST servlet is not (unlike, for example, JSF’s FacesServlet), so if you’re not using Jersey like we are here, then you’ll need to use the Servlet appropriate for your JAX-RS implementation.

So how does RestApplication work? It uses CDI, but since it’s not handled by the CDI runtime, we can’t rely on injection. Instead, we’ll perform a manual look up of the BeanManager, a class provided by CDI’s excellent portable extension mechanism. We then query the BeanManager for our desired classes. But how do we identify our REST resources? Remember the Plugin marker interface? Plummer defines another marker, RestResource, to mark the JAX-RS resources we want to load, which are typical JAX-RS resources with the exception of this extra interface:

@Path("myurl")
public class PluginRestResource implements RestResource {
@GET
public String test(@QueryParam("text") String text) {
return "You sent " + text;
}
}

When the REST application is initialized, this class is loaded and exposed at /myurl as you would expect.

Conclusion

There are many, many plug-in systems available for Java applications. It might be that one of these systems, modelled after or borrowed from, for example, Hudson or others, is the best choice for your application. I think that chances are good, though, that you need not resort to such a relatively complex system. The Java EE platform provides a rich set of APIs that will allow you to implement a domain-specific plug-in system very simply, and with the introduction of another external dependency.

You can find the complete source for Plummer and the examples discussed here on GitHub. Feel free to fork the code, fix bugs, add features, etc. If you find this approach (or the example code) useful or use it in a production system, I'd love to hear your feedback!

This tutorial originally appeared in the August edition of JAX Magazine. Photo by State Farm.

Jason Lee
Jason Lee

What do you think?

JAX Magazine - 2014 - 05 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions