Multi-Tenancy with Java EE and JBoss

Multi-tenancy is a frequent requirement in large business applications. Besides the requirement to separate different clients or environments, multi-tenancy may also be andvantageous in terms of distributing your data client wise across multiple databases. Currently frameworks such as hibernate or other API’s are not of much help.

A large business application for market risk evaluation that needed services and a Java EE compatible Front-End is exactly designed in this way: each tenant has its own database. What makes it even more complex: each tenant has not only one database to handle its entire data needs, but one for each product class. Huge amounts of data are produced every night and now need to be analysed, eventually corrected and calculations re-triggered.

I used an entity manager delegate that looks up the needed entity manager for the current tenant/product class combination via JNDI on each call. This is almost transparent to the clients of the delegate, except that, and that should be clear, somehow an identifier for the tenant/product class tuple needs to be supplied.
JBoss JNDI lookup of entity managers works flawless as of Wildfly 8.0 or JBoss EAP 6.1.1. In earlier versions issues existed and the lookup failed: https://issues.jboss.org/browse/WFLY-299.

Here is the code for the multi-tenant entity manager:

@Stateless
@LocalBean
public class MultiTenantEntityManager {

    private static final String JNDI_ENV = "java:app/entitymanager/";

    @Resource
    private SessionContext ctx;

    public MultiTenantEntityManager() {
    }

....

    public Query createQuery(String dataSourceName, String string) {
        return entityManager(dataSourceName).createQuery(string);
    }

    public <T> TypedQuery<T> createQuery(String dataSourceName, CriteriaQuery<T> cq) {
        return entityManager(dataSourceName).createQuery(cq);
    }

    public <T> TypedQuery<T> createQuery(String dataSourceName, String string, Class<T> type) {
        return entityManager(dataSourceName).createQuery(string, type);
    }

    public void detach(String dataSourceName, Object o) {
        entityManager(dataSourceName).detach(o);
    }

    public <T> T find(String dataSourceName, Class<T> type, Object o) {
        return entityManager(dataSourceName).find(type, o);
    }

    public <T> T find(String dataSourceName, Class<T> type, Object o, Map<String, Object> map) {
        return entityManager(dataSourceName).find(type, o, map);
    }

    public <T> T find(String dataSourceName, Class<T> type, Object o, LockModeType lmt) {
        return entityManager(dataSourceName).find(type, o, lmt);
    }

    public <T> T find(String dataSourceName, Class<T> type, Object o, LockModeType lmt, Map<String, Object> map) {
        return entityManager(dataSourceName).find(type, o, lmt, map);
    }

....

    private EntityManager entityManager(String dataSourceName) {

        final EntityManager entityManager = (EntityManager) ctx.lookup(JNDI_ENV + dataSourceName);

        if (entityManager == null) {
            throw new RuntimeException("Unknown data source name '" + dataSourceName + "'.");
        }

        return entityManager;

    }

}

I left out most of the overriden delegate methods in order to clearly outline the idea. Each method call to the delegate has an additional parameter: The name of the datasource. The name is a string that follows a simple schema: “tenant_productClass” and thus identifies the database we need to access. You are free to come up with your own naming scheme that suits your needs best.
I did tests with configurations that consisted of up to 700 datasource pools. JBoss EAP managed them without any difficulty (ok, at one point during testing the database servers gave up – but that was an easy fix ). The datasource’s name is also part of the JNDI lookup string for the real JPA entity manager. The delegate looks up first the entity manager in the JNDI tree and then calls the actual entity managers method with the supplied parameters.

Here is an example persistence.xml for a persistence unit (as there are many of them, I generate them by script):

  <persistence-unit name="tenant1_eq" transaction-type="JTA">
    <jta-data-source>java:/jboss/datasources/itpdb_pdbeq</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.SybaseASE15Dialect"/>
      <property name="hibernate.cache.use_second_level_cache" value="false"/>
      <property name="hibernate.jdbc.batch_size" value="50"/>
      <property name="hibernate.jdbc.batch_versioned_data" value="true"/>
      <property name="hibernate.order_inserts" value="true"/>
      <property name="hibernate.order_updates" value="true"/>
      <property name="hibernate.generate_statistics" value="true"/>
      <property name="jboss.entity.manager.jndi.name" value="java:app/entitymanager/tenant1_eq"/>
      <property name="jboss.entity.manager.factory.jndi.name" value="java:app/entitymanagerfactory/tenant1_eq"/>
    </properties>
  </persistence-unit>

The multi-tenant entity manager is injected into each bean that would normally have an entity manager injected. For each call, the datasource’s name, to which the entity manager references, has to be known as it is also part of the entity manager’s JNDI name. Below is a snippet of a boundary, that uses the multi-tenant entity manager.

@Stateless
@Remote(AggregationConfig.class)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class AggregationConfigBean implements AggregationConfig {

    private static final Logger LOG = LoggerFactory.getLogger(AggregationConfigBean.class.getSimpleName());

    @EJB
    MultiTenantEntityManager entityManager;

    /**
     * Default constructor.
     */
    public AggregationConfigBean() {
    }

    @Override
    public List<AggConfigMeta> findConfigMeta(String dataSourceName) {

        List<AggConfigMeta> res = Lists.newArrayList();

        Query q = entityManager.createNativeQuery(dataSourceName, "SELECT meta FROM ConfigMeta WHERE type='config'");
        List<String> result = q.getResultList();

        LOG.info("Found " + result.size() + " meta entries.");

        ObjectMapper mapper = new ObjectMapper();

        LOG.info("Instantiated ObjectMapper...");

        for (String meta : result) {
            try {
                res.add(mapper.readValue(meta, AggConfigMeta.class));
            } catch (IOException ex) {
                LOG.error("Failed reading AggConfig meta data.", ex);
            }
        }

        return res;

    }

.....

}

The usage is actually straight forward. This approach may also be used to handle database-sharding in a similiar way, see database-sharding.
It opens a window to a whole lot of new ideas on how to handle large amounts of data with Java EE.

5 thoughts on “Multi-Tenancy with Java EE and JBoss

    • It’s not really a business method. The bean is marked as local bean and should only be used as a replacement for an injected entity manager, as entity managers are not capable of multi-tenancy. The bean implements therefore all methods of an entity manager.

  1. Spring Framework 3.1 will also support the building of a default JPA persistence unit without persistence.xml, well more magic less control

  2. Hi, congratulation for the post.

    Another oprtion would be to use CDI do produce each entity manager and using a qualifier at injection point to chose tenants, this way you dont need to pass datasource name as parameter on each boundary methods.

Leave a Reply

Your email address will not be published. Required fields are marked *