A Look Inside the JBoss Microcontainer, Part I -- Component Models
Join the DZone community and get the full member experience.
Join For FreeLooking at the current state of Java, we can see that POJOs (Plain Old Java Objects) rule the land yet again. Their dominance stretches from enterprise applications to middleware services. At JBoss we were known for our modular JMX-based kernel. The application server was nothing more than a bunch of flexible MBeans and a powerful MicroKernel in the middle. But, as you could feel the change is coming, we still wanted to be ahead of the pack. We could see different POJO based component models popping up all over the place (EJB3, JPA, Spring, Guice, … to name a few), yet nothing was out there that would bind them all, flattening out the differences on a single component model. Hence the Microcontainer project was born.
The JBoss Microcontainer project is about many things. The Microcontainer's features range from reflection abstraction, a virtual file system, a simple state machine to transparent AOP integration, a new classloading layer, a deployment framework and an OSGi framework implementation. I'll try to address them all over a short series of articles that will be published here on DZone. This article will examine the Microcontainer's component models.
Read the other parts in DZone's exclusive JBoss Microcontainer Series:
What is a component model?
What do we consider a component model? First of all, what do we consider being a component? One abstract way to express this would be that “components are reusable software programs that you can develop and assemble easily to create sophisticated applications.” To consider a bunch of components as an actual model, we also need to declare what kind of interactions we allow.
JMX MBeans are one example of a component model. Their interactions include executing MBean operations, referencing attributes, setting attributes and declaring explicit dependencies between named MBeans.
As mentioned, we had advanced JMX handling already with the MicroKernel. And as expected, the Microcontainer brought extensive POJO support. The default behavior and interactions in the Microcontainer are what you also normally get from any other IoC container and are similar to the functionality that MBeans provided: plain method invocations for operations, setters/getters for attributes and explicit dependencies.
However, having only this functionality would mean we didn't get much further than just relieving the pain of declaring MBeans, hence it was only logical for us to do something more. I'll leave the discussion of these new IoC features for the next article in the series.
There are many existing POJO component models out there, Guice and Spring being amongst the most popular. Effective integration with these frameworks was an important goal for us.
Demo environment setup
Before we begin, I'd like to first describe the various parts that constitute the demo. All the source code can be found at this location of our Subversion repository:
• http://anonsvn.jboss.org/repos/jbossas/projects/demos/microcontainer/branches/DZone_1_0/
The project is fully mavenized, so it should be easy to adjust it to your IDE.
I will first go over the sub-projects within the demo and describe their usage. At the end of my article series, I will provide a more detailed look at what certain sub-projects do.
Below are the JBoss Microcontainer demos and sub-projects that are relevant for this article:
• bootstrap (as the name suggests, it bootstraps the Microcontainer with demo code)
• jmx (adds the JMX notion to demo's bootstrap)
• models (source code of our components / services)
The demo has only one variable you need to set - demos home - but even this one can be optional if you checked-out your project into the \projects\demos directory. Otherwise, you need to set the system property demos.home (e.g. -Ddemos.home=<my demos home>). You should now be able to run JMXMain as a main class. Make sure you include the models sub-project in the classpath, since some of the services require additional classes in the classpath, several more then what the jmx sub-project expects. Once the Microcontainer is booted it starts to scan the ${demos.home}/sandbox directory for any changes. Now all we need to do is provide a deployable unit and drop it in there.
Models
Let’s now turn our attention to the models sub-project. This is where the previously mentioned deployable unit should come from. You can see if everything is in place by building the models sub-project (mvn package) and dropping it into the sandbox. You should get a bunch of debug and info messages on the console log, showing how the Microcontainer got booted. Any error message indicates some problems.
Let’s go over exactly what the models sub-project does, its integration code and try to redeploy it. If we look at the models src/main/resources/META-INF directory, we'll see plenty of -beans.xml resource files and one -service.xml. You’ll notice that each has a meaningful name that matches the source code package from models's src/main/java/org/jboss/demos/models directory. Let's dissect them one by one, starting with the ones that have no dependencies.
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<bean name="PlainPojo" class="org.jboss.demos.models.plain.Pojo"/>
<beanfactory name="PojoFactory" class="org.jboss.demos.models.plain.Pojo">
<property name="factoryClass">org.jboss.demos.models.plain.PojoFactory</property>
</beanfactory>
</deployment>
This is a simple Micrcocontainer beans descriptor file. Anyone who crossed paths with some other IoC’s configuration file should be familiar with it. And, as I already mentioned, I'll follow up on more advanced usage in the next article.
The next file shows what we have done to support Spring integration:
<beans xmlns="urn:jboss:spring-beans:2.0">
<!-- Adding @Spring annotation handler -->
<bean id="SpringAnnotationPlugin" class="org.jboss.spring.annotations.SpringBeanAnnotationPlugin" />
<bean id="SpringPojo" class="org.jboss.demos.models.spring.Pojo"/>
</beans>
Note that this file's namespace is different from the previous Microcontainer bean’s plain-beans.xml file. The urn:jboss:spring-beans:2.0 namespace points to our version of the Spring schema port, meaning you can describe your beans Spring style, but it's the Microcontainer that's going deploy then, not Spring's bean factory notion.
public class Pojo extends AbstractPojo implements BeanNameAware
{
private String beanName;
public void setBeanName(String name)
{
beanName = name;
}
public String getBeanName()
{
return beanName;
}
public void start()
{
if ("SpringPojo".equals(getBeanName()) == false)
throw new IllegalArgumentException("Name doesn't match: " + getBeanName());
}
}
Although the SpringPojo bean has a dependency on Spring lib via implementing BeanNameAware interface, this dependency is only there to expose and mock some of the Spring's callback behavior (see SpringBeanAnnotationPlugin for more details).
Since we introduced Spring integration, let's have a look at Guice integration. As Guice users know, Guice is all about type matching. Configuration of Guice beans is done via Modules which means that in order to generate beans, one must implement a Module.
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<bean name="GuicePlugin" class="org.jboss.guice.spi.GuiceKernelRegistryEntryPlugin">
<constructor>
<parameter>
<array elementClass="com.google.inject.Module">
<bean class="org.jboss.demos.models.guice.PojoModule"/>
</array>
</parameter>
</constructor>
</bean>
</deployment>
Two important parts to watch from this file are PojoModule and GuiceKernelRegistryEntryPlugin. The first one is where we configure our beans:
public class PojoModule extends AbstractModule
{
private Controller controller;
@Constructor
public PojoModule(@Inject(bean = KernelConstants.KERNEL_CONTROLLER_NAME) Controller controller)
{
this.controller = controller;
}
protected void configure()
{
bind(Controller.class).toInstance(controller);
bind(IPojo.class).to(Pojo.class).in(Scopes.SINGLETON);
bind(IPojo.class).annotatedWith(FromMC.class).toProvider(GuiceIntegration.fromMicrocontainer(IPojo.class, "PlainPojo"));
}
}
The second class provides integration with the Microcontainer:
public class GuiceKernelRegistryEntryPlugin implements KernelRegistryPlugin
{
private Injector injector;
public GuiceKernelRegistryEntryPlugin(Module... modules)
{
injector = Guice.createInjector(modules);
}
public void destroy()
{
injector = null;
}
public KernelRegistryEntry getEntry(Object name)
{
KernelRegistryEntry entry = null;
try
{
if (name instanceof Class<?>)
{
Class<?> clazz = (Class<?>)name;
entry = new AbstractKernelRegistryEntry(name, injector.getInstance(clazz));
}
else if (name instanceof Key)
{
Key<?> key = (Key<?>)name;
entry = new AbstractKernelRegistryEntry(name, injector.getInstance(key));
}
}
catch (Exception ignored)
{
}
return entry;
}
}
Notice how we created an Injector from the Modules and then did a lookup on it for matching beans.
In mbeans-service.xml we declare our legacy usage of MBeans:
<server>
<mbean code="org.jboss.demos.models.mbeans.Pojo" name="jboss.demos:service=pojo">
<attribute name="OtherPojo"><inject bean="PlainPojo"/></attribute>
</mbean>
</server>
What’s interesting to note here is how we injected a POJO into an MBean. The preceding demonstrated our first interactions between different component models.
In order to allow for MBean deployment via the Microcontainer, we had to write entirely new component model handling code. See the system-jmx-beans.xml for more details. The code from this file lives in the JBossAS source code: system-jmx sub-project. One note here, this is currently only possible with JBoss's JMX implementation, since the system-jmx code uses some implementation details.
Now what if we wanted to expose our existing POJOs as MBeans, registering them into an Mbean server?
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<bean name="AnnotatedJMXPojo" class="org.jboss.demos.models.jmx.AtJmxPojo"/>
<bean name="XmlJMXPojo" class="org.jboss.demos.models.mbeans.Pojo">
<annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(exposedInterface=org.jboss.demos.models.mbeans.PojoMBean.class, registerDirectly=true)</annotation>
</bean>
<bean name="ExposedPojo" class="org.jboss.demos.models.jmx.Pojo"/>
<bean name="AnnotatedExposePojo" class="org.jboss.demos.models.jmx.ExposePojo">
<constructor>
<parameter><inject bean="ExposedPojo"/></parameter>
</constructor>
</bean>
</deployment>
As you can see from looking at any of the beans in this file, in order to expose your POJOs as MBeans, it’s simply a matter of annotating them with an @JMX annotation. You can either expose the bean directly or its property:
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<bean name="XMLLoginConfig" class="org.jboss.demos.models.old.XMLLoginConfig"/>
<bean name="SecurityConfig" class="org.jboss.demos.models.old.SecurityConfig">
<property name="defaultLoginConfig"><inject bean="XMLLoginConfig"/></property>
</bean>
<bean name="SecurityChecker" class="org.jboss.demos.models.old.Checker">
<property name="loginConfig"><inject bean="jboss.security:service=XMLLoginConfig"/></property>
<property name="securityConfig"><inject bean="jboss.security:service=SecurityConfig"/></property>
</bean>
</deployment>
Here we see how you can use any of the injection lookup types by either looking up a plain POJO or getting a handle to an MBean from the MBean server.
One of the injection options is to use type injection, which is also sometimes called autowiring:
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<bean name="FromGuice" class="org.jboss.demos.models.plain.FromGuice">
<constructor><parameter><inject bean="PlainPojo"/></parameter></constructor>
<property name="guicePojo"><inject/></property>
</bean>
<bean name="AllPojos" class="org.jboss.demos.models.plain.AllPojos">
<property name="directMBean"><inject bean="jboss.demos:service=pojo"/></property>
<property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property>
<property name="exposedMBean"><inject bean="jboss.demos:service=ExposedPojo"/></property>
</bean>
</deployment>
The FromGuice bean injects the Guice bean via type matching, where PlainPojo is injected with a common name injection. We then test if Guice binding works as expected:
public class FromGuice
{
private IPojo plainPojo;
private org.jboss.demos.models.guice.Pojo guicePojo;
public FromGuice(IPojo plainPojo)
{
this.plainPojo = plainPojo;
}
public void setGuicePojo(org.jboss.demos.models.guice.Pojo guicePojo)
{
this.guicePojo = guicePojo;
}
public void start()
{
f (plainPojo != guicePojo.getMcPojo())
throw new IllegalArgumentException("Pojos are not the same: " + plainPojo + "!=" + guicePojo.getMcPojo());
}
}
This only leaves us with an alias component model. Even though the alias is quite a trivial feature, in order to implement it as a true dependency, it has to be introduced as a new component model inside the Microcontainer. The implementation details for this is part of the AbstractController source code:
<deployment xmlns="urn:jboss:bean-deployer:2.0">
<alias name="SpringPojo">springPojo</alias>
</deployment>
Here we’ve mapped the SpringPojo name to the springPojo alias. The beauty of having aliases as true component models is that it doesn't matter when the real bean gets deployed. This means that the alias will wait in a non-installed state until the real bean triggers it.
Conclusion
We've seen how we can deploy simple Microcontainer beans, legacy MBeans, Guice POJOs, Spring beans and aliases. And since all of this is controlled by the Microcontainer, we saw how easily we can mix and match the different component models.
I can easily say, with the level of abstraction we put in our component model design, the sky is the limit on what we can handle. An example of this is the upcoming OSGi services, but that's another story, another article.
Stayed tuned for my next article which will provide a detailed look at the Microcontainer's IoC functionality.
About the Author
Ales Justin was born in Ljubljana, Slovenia and graduated with a degree in mathematics from the University of Ljubljana. He fell in love with Java seven years ago and has spent most of his time developing information systems, ranging from customer service to energy management. He joined JBoss in 2006 to work full time on the Microcontainer project, currently serving as its lead. He also contributes to JBoss AS and is Seam and Spring integration specialist. He represent JBoss on 'JSR-291 Dynamic Component Support for Java SE' and 'OSGi' expert groups.
Opinions expressed by DZone contributors are their own.
Comments