One of the best features the Enterprise Edition of Java offers is, without doubt, the modular approach it takes. Mainly it allows an architect to divide a complex application in several modules or components. These are usually a mixture of Web Applications (packed as WARs), Enterprise JavaBeans (packed as JARs) and Message Driven Beans, all wrapped in a EAR (Enterprise Application) file. Even other more esoteric components do exist (for example, Resource Adaptars).
Up until today (with JEE5), the developer had to be very careful when separating the elements, ensuring they could work more or less independently because, unfortunately, the communication between them was lacking (to say the least) in several areas. The API in charge of the handling those communications and offering a common repository to store objects (called JNDI) was slow and, in addition, it lacked some basic functions. With the advent of version 5 things improved considerably as the introduction of Dependency Injection which removed a lot of boilerplate code (like context initialization, unneeded casts) making everything more developer friendly.
Today Spring has become ubiquitous in projects. This adds a new and more powerful IoC container, capable of being embedded inside JEE components and, at the same time, able to work with JNDI (Spring can function as a JNDI context itself). Of course, adding Spring raises the question of having a new type of component, the bean, and how can it interact with the environment. As a matter of fact, once Spring is in the scope it handles nearly everything but there are always areas that the container will rather manage (for example, JTA).
The goal here is to be able to expose Spring beans as services available to the rest of the environment. One possible solution would be to store the bean in the JNDI context so any potential client can look it up. Or better, it can be injected. This can be done easily using the JNDITemplate but suffers from some handicaps: it inherits those of JNDI, it has to be Serializable, it loses advanced capabilites (scoping, transactions, proxying in general), it seems clunky for Spring injection (in other context). All in all, although useful for some purposes, it is not the best solution overall. The ideal we are looking for is a root context, loaded once, parent of the rest of them, that holds singleton beans and exposes them to clients deployed anywhere inside the same application (allegedly in other components). This last sentence is important, JNDI context is global to the application server, our context will be global to each enterprise application.
To achieve it an XML file with all the shared beans has to be created first. Ideally this file will be deployed inside a JAR in the lib directory of the EAR. This way it's just deployed once and available (in the classpath) to each web application or EJB. I said one but in practice two files are needed, the first one includes a reference to the second where the context is actually defined.
Up until today (with JEE5), the developer had to be very careful when separating the elements, ensuring they could work more or less independently because, unfortunately, the communication between them was lacking (to say the least) in several areas. The API in charge of the handling those communications and offering a common repository to store objects (called JNDI) was slow and, in addition, it lacked some basic functions. With the advent of version 5 things improved considerably as the introduction of Dependency Injection which removed a lot of boilerplate code (like context initialization, unneeded casts) making everything more developer friendly.
Today Spring has become ubiquitous in projects. This adds a new and more powerful IoC container, capable of being embedded inside JEE components and, at the same time, able to work with JNDI (Spring can function as a JNDI context itself). Of course, adding Spring raises the question of having a new type of component, the bean, and how can it interact with the environment. As a matter of fact, once Spring is in the scope it handles nearly everything but there are always areas that the container will rather manage (for example, JTA).
The goal here is to be able to expose Spring beans as services available to the rest of the environment. One possible solution would be to store the bean in the JNDI context so any potential client can look it up. Or better, it can be injected. This can be done easily using the JNDITemplate but suffers from some handicaps: it inherits those of JNDI, it has to be Serializable, it loses advanced capabilites (scoping, transactions, proxying in general), it seems clunky for Spring injection (in other context). All in all, although useful for some purposes, it is not the best solution overall. The ideal we are looking for is a root context, loaded once, parent of the rest of them, that holds singleton beans and exposes them to clients deployed anywhere inside the same application (allegedly in other components). This last sentence is important, JNDI context is global to the application server, our context will be global to each enterprise application.
To achieve it an XML file with all the shared beans has to be created first. Ideally this file will be deployed inside a JAR in the lib directory of the EAR. This way it's just deployed once and available (in the classpath) to each web application or EJB. I said one but in practice two files are needed, the first one includes a reference to the second where the context is actually defined.
<beans ...>
<bean name="mainApplicationContext"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>classpath*:spring/globalContextDefinition.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
<bean name="mainApplicationContext"
class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>classpath*:spring/globalContextDefinition.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
Once created it has to be loaded on startup and just once independently of the number of child contexts. To do it we'll use a ContextLoaderListener and two context params.
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>spring/mainContext.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>mainApplicationContext</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<param-name>locatorFactorySelector</param-name>
<param-value>spring/mainContext.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>mainApplicationContext</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
The first param indicates where is the XML that serves as reference located (needs to be in the classpath of the listener), the second which bean defines the parent context.
Just one more thing is needed as the listener expects to find a file called applicationContext.xml in the WEB-INF directory. This file should include the local beans. Normally, in these beans are included those that a web application do not want to share and those that can't be shared (because they depend on the HTTP requests for example). As a caveat, this configuration has to be replicated on every web application although just the first will load the context. Is worth noticing that if using Spring MVC (and so a DispatcherServlet) it's probably best to left the applicationContext.xml void and define the beans in the <servlet-name>-servlet.xml file.
The last step is to be able to reach the context from an EJB, an MDB for example. To do it use the ContextSingletonBeanFactoryLocator:
Just one more thing is needed as the listener expects to find a file called applicationContext.xml in the WEB-INF directory. This file should include the local beans. Normally, in these beans are included those that a web application do not want to share and those that can't be shared (because they depend on the HTTP requests for example). As a caveat, this configuration has to be replicated on every web application although just the first will load the context. Is worth noticing that if using Spring MVC (and so a DispatcherServlet) it's probably best to left the applicationContext.xml void and define the beans in the <servlet-name>-servlet.xml file.
The last step is to be able to reach the context from an EJB, an MDB for example. To do it use the ContextSingletonBeanFactoryLocator:
public class MDB extends AbstractJmsMessageDrivenBean {
public void setMessageDrivenContext(MessageDrivenContext mdbContext) {
super.setMessageDrivenContext(mdbContext);
setBeanFactoryLocator(
ContextSingletonBeanFactoryLocator.getInstance(
"classpath*:spring/mainContext.xml"));
setBeanFactoryLocatorKey("mainApplicationContext");
}
...
}
public void setMessageDrivenContext(MessageDrivenContext mdbContext) {
super.setMessageDrivenContext(mdbContext);
setBeanFactoryLocator(
ContextSingletonBeanFactoryLocator.getInstance(
"classpath*:spring/mainContext.xml"));
setBeanFactoryLocatorKey("mainApplicationContext");
}
...
}
