This post describes some of the pitfalls when coping with intercommunication between different modules (EAR, WAR, EJB-JAR) deployed in the same Java EE 6 Application Server.

The problem

The problem is as follows: you have two modules (let’s take the EARs as an example) that are deployed in the same JVM / Application Server: App1.ear and App2.ear.

App1.ear contains:

  • service.jar; it’s an EJB-JAR with the business logic of the application,
  • service-api.jar; it’s an API for the service.jar, so the EJB’s in service.jar must implement this API.

App2.ear contains:

  • client.jar; it’s the EJB-JAR with the business logic of the client application,
  • service-api.jar; it’s the same API / interfaces as in App1.ear/service-api.jar. It’s here because you will need to know the API of the service.jar if you want to communicate with it.

Now when you think about it in terms of the EJB’s and their views, the question is: what kind of view should the App1.ear/service.jar expose? Normally we would need to decide between 5 possible views of the EJBs but let’s forget about those legacy EJB 2.x remote and local interfaces and focus on:

  • remote business interfaces,
  • local business interfaces and
  • no-interface views.

The last one (no-interface view) can be easily rejected as we want to have a bare contract / API we can share between the applications. We definitely don’t want to juggle with actual beans implementation.

So we’re left with the remote business and local business interfaces.

Remote or Local Business Interface?

It’s somehow tempting to think about the local business interfaces as in “local to the JVM” or as in “local to the Application Server“. It also nicely aligns with the fact that the local business interface has the “pass-by-reference” nature.

On the other hand we have remote business interfaces as in “remote JVM” or “remote standalone client“. Very similarly, the “pass-by-value” nature of the remote business interfaces fits in this picture quite well. Moreover, each remote call invokes the serialization and deserialization and these also sounds related with the connection through the network.

After knowing all that, we should go with the local business interface, right? We’re in the same JVM, on the same Application Server instance. And in fact the local business interfaces in such case will work on JBoss AS 6, 7, Glassfish 3.1.1.2 and perhaps few others. But the thing is: EJB specification doesn’t guarantee any behaviour when you use the local business interfaces for inter-modules communication even if those modules are part of the same Application Server / JVM. Those views are used only for inner-module communication (e.g. access EJB-JAR from WAR when they’re are all packaged in one EAR.)

In case of inter-modules communication, you should use the remote business interfaces. Yes, there is an additional penalty of serizalization/deserialization (and perhaps something more?) even if you’re still residing on the same JVM. Additionally, all of your parameters must be Serializable. The container is, of course, allowed to perform any optimization it wants as long as the semantics of the remote business interface is not harmed (e.g. it can bypass the network traffic when caller and callee are on the same machine but it cannot change pass-by-value to pass-by-reference semantics.)

When using remote business interfaces you’re not dependent on the server application vendor when running your application – this behaviour is mandated by the EJB spec, so if it doesn’t work – it’s most likely the application server issue.

There seems to be rationale behind the requirement to use remote business interfaces, especially if you think about clustering your application across multiple nodes and communication between applications that actually resides on different machines.

Local Business Interfaces Behaviour On Different Servers

If you still insist on using the local business interface, you can do so, but you need to remember that this is a non-portable solution and you need to be aware of few pitfalls. Assume you’ve prepared your EARs as described at the beginning of this post (code is available here) and deployed it into one of the applications server.

In short, you are trying to:

  • lookup the EJB from the JNDI (using its local business interface),
  • cast the result to the expected interface (coming from appropriate service-api.jar) and
  • invoke one of the bean’s methods.

In JBoss 6.1 you’ll be fine without any work because of the hierarchical classloaders. No exceptions, no problems – it just works.

In JBoss AS 7.1 during the casting to the interface type you’ll get a class-cast exception because you’re trying to cast a class from service-api.jar loaded by App1.ear to service-api.jar loaded by App2.ear.

Just as a reminder – class instance can be cast into another if the fully qualified names matches and the classloader that loaded the classes is the same. In case of the JBoss AS 7 we have classloaders isolation, so you really can’t make it work out-of-the-box just by preparing the EAR.

There is a JBoss specific way to achieve what we want and its called JBoss Modules. You need to create appropriate directories structure under $JBOSS_HOME/modules and prepare the module.xml descriptor. In this way – both App1.ear and App2.ear can define dependency on the same module. The EARs themselves don’t consist of the service-api.jar – they’re just referencing it from the JBoss Modules.

In case of Glassfish 3.1.1.2 you’ll also get the class-cast exception. I didn’t dig the GF topic any more, but I would expect to have something very similar to the JBoss Modules.

So, even if we know that our application server is supporting local business interfaces between applications in different modules, we still need to be aware of the consequences of such feature. It might work out-of-the-box (JBoss 6) or it might require some additional tasks (JBoss AS 7, Glassfish 3).

I would find those tasks to be completed by the deployer of the application, so someone that knows how particular application server should be configured (I know – it’s very often yourself – just changing the ‘bean provider’ hat to the ‘deployer’.)

Standardized Modules Management

If you think about these differences in the deployment of shared modules you might ask if there is some standard way of defining them which would be the same across multiple server vendors. Well, the more I think about it, the more I am convinced it might be addressed by the Jigsaw project. However, because the Jigsaw was deferred and won’t be a part of Java SE 8 – Java SE 9 is the earliest possible release. This means that Java EE 7 — that is currently under work — won’t address any of the modularity problems. The earliest convenience will be EE 8, so we’re probably talking about 2016 – 2017 when application server vendors will implement EE 8.

Such modular system might be used by application server vendors to standardize the modules installation. Nevertheless, you’d still end with specifying what modules should be installed by the deployer. I really doubt we’ll ever invent such archive type that consists of the application and the modules to be installed (if you think about it – it simply doesn’t make any sense and it mixes the responsibilities of the artifacts.).

Either way, even if Project Jigsaw would address the problem of installation of the modules in application servers, you’ll still end with preparation of such modules apart from your application. The only simplification for the bean provider I can think of, is perhaps some kind of unified way of defining modules dependencies in descriptors or manifest file.

You can find the exemplary application — that creates two EARs that communicates with each other — on my GitHub account here. Just try fiddling with the exposed views of the beans and try deploying it into different servers.

You can find a filled JIRA for adding cross-application local beans accessibility here.

Did you like this? Share it:

4 thoughts on “Communication Between EJB Modules In The Same Application Server

  1. Very interesting post Piotr.

    I have found this situation many times, and resolved differently depending on AppServer. For instance, we once used the WebLogic library depency mechanism to resolve some class loader issues (not directly related to EJB).

    Agree this should be address by EE sooner or later, I don’t know if OSGI is already providing some of the required features. I read some doc about it, but never really used it.

    By the way, could you elaborate about the specific workaround for Jboss 7, and all that module.xml stuff? I’ve reviewed your code attached, but I’m unable to find any details about this. I’m assuming you have deployed the service-api.jar as a module resource, then specify this library as “provided” (resolved by container), and somehow declare the client and provider ear modules dependent to the JBOSS created module (to get the proper library in runtime).

    Reply
  2. Hi Jaime,

    Thanks for your input. If you’re more interested in JBoss AS modules you can take a look at their documentation: https://docs.jboss.org/author/display/AS71/Class+Loading+in+AS7

    It basically works as you described. You create a module with the API. Both EAR’s points to this module as a dependency (this is done in jboss specific descriptors). In this way the container knows that it should use this module and gives your EARs access to it.

    I think it’s best to try it on your own – you can use the example from the github and try to create modules based on the documentation links above. In case of any problems just contact me by mail.

    All the best,
    Piotr

    Reply
  3. Explanation:

    We have one master db, and mulitple client databases,
    Our goal is, At runtime we have to create “entityManagerFactory” for each client. Each client has seaperate database.
    (Runtype we are getting entityManagerFactory by passing clientcode and creating entityManager)
    also avoiding to configure the entityManagerFactory and transaction manager for each client.

    my follow structure. (1 ==> 2 ==> 3 ==> 4)
    1) controller( @Controller
    @RequestMapping(“/test”)
    @Scope(“prototype”) class level)

    2) serviceimpl(added @Service, @Scope(“prototype”) class level)

    3) components(added @Service, @Scope(“prototype”) class level)

    4) dao layer(added @Repository in class level).

    As of now, We are able to create entityManagerFactory for each client and put to map (in EntityManagerHelp.)
    1) works fine for single user update.
    2) Problem is mutliple users trying to use same operation it was saying “transaction not active”
    3) some time says “TransactionCordintor” exception.

    EntityManagerHelper:
    =========================

    private Map entityManagerFactoryMap = new HashMap();

    private EntityManagerHelper() {
    }

    @PersistenceContext(unitName = “masterPU”)
    private EntityManager masterEntityManager = null;

    @Autowired
    private PropertySource propertySource;

    @Autowired
    private ClientDAO clientDAO;

    public EntityManager getMasterEntityManager() {
    return masterEntityManager;
    }

    public void setMasterEntityManager(EntityManager masterEntityManager) {
    this.masterEntityManager = masterEntityManager;
    }

    public EntityManager getClientEntityManager(String clientCode) {
    EntityManagerFactory emf = getEntityManagerFactory(clientCode);
    return emf.createEntityManager();
    }

    private synchronized EntityManagerFactory getEntityManagerFactory(String clientCode) {
    if (entityManagerFactoryMap.containsKey(clientCode)) {
    if (entityManagerFactoryMap.get(clientCode) != null) {
    return entityManagerFactoryMap.get(clientCode);
    }
    throw new NullPointerException(“Error in getEntityManagerFactory”);
    }
    EntityManagerFactory entityManagerFactory = null;
    try {
    clientDAO.setEntityManager(getMasterEntityManager());
    Client client = clientDAO.getClient(clientCode);
    client.setDbUserName(“xyz” ); // dynamcially setting username with respect to client db.
    client.setDbPassword(“password”); //// dynamcially setting password with respect to client db.

    LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
    bean.setDataSource(getDataSource(client));
    bean.setPersistenceXmlLocation(“/META-INF/persistence.xml”);
    bean.setPersistenceUnitName(“clientPU”);
    bean.afterPropertiesSet();
    entityManagerFactory = (EntityManagerFactory)bean.getObject();
    entityManagerFactoryMap.put(clientCode, entityManagerFactory);

    // is there any way to create transactionmanager and inject entityManagerFactory and datasource.
    // idea is: we need to achieve transactionmanager dynamcially for each client
    } catch (Exception e) {
    e.printStackTrace();
    logger.error(“Error:” + e, e);
    }
    return entityManagerFactory;
    }

    private DataSource getDataSource(Client client) throws SQLException {
    String url = “jdbc:mysql://” + client.getDb_server() + “/” + client.getDb_name();
    String username = client.getDbUserName();
    String password = client.getDbPassword();
    Driver driver = new com.mysql.jdbc.Driver();
    return new SimpleDriverDataSource(driver, url, username, password);
    }

    Help me how to set TransactionManager for each EntityManagater Factory in below class.?

    Each request – in service layer
    EntityManager entityManager = entityManagerHelper.getEntityManagerFactory(“XYZ”)
    try {
    etx = entityManager.getEntityTransaction();
    baseDAO.setEntitityManager(entityManager)
    etx.begin();
    // operation – eg: baseDAO.update(list of entities)
    etx.commit();

    } catch(Exception e) {
    etx.rollback();
    }

    update method is looks like below:

    public void update(List detachedInstanceList) throws DBException {
    try {
    for (Entity detachedInstance : detachedInstanceList) {
    entityManager.merge(detachedInstance);
    }
    entityManager.flush();
    } catch (PersistenceException ex) {
    ex.printStackTrace();
    logger.error(“Exception occured while updating List of Entities – ” + clazz.getName(), ex);
    throw new customeException(ExceptionConstants.xxx, clazz.getSimpleName());
    }

    }

    java.lang.IllegalStateException: Transaction not active
    at org.hibernate.ejb.TransactionImpl.rollback(TransactionImpl.java:103)
    at com.company.package.xxx.service.impl.ServiceImpl.update(ServiceImpl.java:402)
    at com.company.package.xxx.controller.Controller.update(Controller.java:148)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:822)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:662)

    Reply

Leave a reply

required


*

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>