public interface MyBean { String hello(); }
Switching EJB remote interface to local interface accordingly to environment
TweetPosted on Monday Feb 23, 2015 at 01:33PM in Technology
Sometimes I need to create both of remote and local interface for EJBs. most of cases that were for integration testing. usually I used this way but that way still has some disadvantages as follows:
-
Annoying creation of both of interface (local and remote) is needed for all of each EJBs
-
Remote interface is redundant for most cases in production environment
To overcome these issues, I moved declarations of EJB from annotation to ejb-jar.xml
. XML files are easy to be filtered with build tools such as Maven plugins. consider following case:
Business interface
Implementation
public class MyBeanImpl implements MyBean { @Override public String hello() { return "Hello"; } }
Injection point
@WebServlet(name = "MyServlet", urlPatterns = "/") public class MyServlet extends HttpServlet { @EJB MyBean myBean; ... }
Test class
public class MyBeanIT { static Context ctx; static MyBean myBean; @BeforeClass public static void before() throws Exception { Properties props = new Properties(); // ... put application server specific configuration here ... ctx = new InitialContext(props); myBean = (MyBean) ctx.lookup("ejb:/ejbxmltest//MyBeanImpl!org.nailedtothex.ejbxmltest.MyBean"); } @AfterClass public static void after() throws Exception { ctx.close(); } @Test public void test() throws Exception { org.junit.Assert.assertEquals("Hello", myBean.hello()); } }
ejb-jar.xml
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1"> <enterprise-beans> <session> <ejb-name>MyBeanImpl</ejb-name> <!-- Will be replaced by business-local with replacer-plugin for production --> <business-remote>org.nailedtothex.ejbxmltest.MyBean</business-remote> <ejb-class>org.nailedtothex.ejbxmltest.MyBeanImpl</ejb-class> <session-type>Stateless</session-type> </session> </enterprise-beans> </ejb-jar>
Remote interface and bean definition are presented in ejb-jar.xml
instead of annotations. that definition will be used by IDE as default. EJBs will be deployed with a remote interface to the application server in development environment so we can test the bean through remote interface. a trick is in a profile named business-local
in pom.xml
which intended to use for production environment:
<profile> <id>business-local</id> <build> <!-- Following plugin definitions will be ignored by IDEA --> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <warSourceExcludes>WEB-INF/ejb-jar.xml</warSourceExcludes> </configuration> </plugin> <plugin> <groupId>com.google.code.maven-replacer-plugin</groupId> <artifactId>replacer</artifactId> <version>1.5.3</version> <executions> <execution> <phase>prepare-package</phase> <goals> <goal>replace</goal> </goals> </execution> </executions> <configuration> <file>src/main/webapp/WEB-INF/ejb-jar.xml</file> <outputFile>target/${project.artifactId}/WEB-INF/ejb-jar.xml</outputFile> <token><![CDATA[(?<=</?)business-remote(?=>)]]></token> <value>business-local</value> </configuration> </plugin> </plugins> </build> </profile>
maven-replacer-plugin
replaces all of business-remote
elements by business-local
and put it into WEB-INF
directory in the target directory, and tell maven-war-plugin
to stop copying original WEB-INF/ejb-jar.xml
so all of remote interfaces will be local interfaces in production environment. it is disabled by default and you have to enable it with invoke Maven with -P business-local
argument. as to injection point (MyServlet
) we don’t need to make any modification. @EJB
or @Inject
will work nevertheless whether the interface is local or remote.
Actually I really want to use Arquillian instead of remote EJB but unfortunately it doesn’t have some flexibility on my use case such as control of deployment timing at the present time.
The project used in test is available in my GitHub repository. the test was done with WildFly 8.2.0.Final.
CAUTION: invoking EJBs via local interface will call by reference but invoking via remote interface will call by value. you will get serious problems that different behavior between invoking local and remote interfaces. consider keep using remote interface on production (with accepting some performance degradation) or using modern testing framework such as Arquillian if necessary.