This time, as promised, I'd like to describe one of a few possible models of developing Multi-Tenant applications, with
HK2 and
EclipseLink.
Disclaimer:
HK2 and
EclipseLink don't have to be used together. This model is not intended for any PaaS. And this post does not cover all aspects of developing multi-tenant applications. This model attempts to simplify programmer experience when developing multi-tenant application with single application instance per tenant approach.
Let me start with less familiar
HK2 first. It is an implementation of the JSR 330: Dependency Injection standard for Java. I won't repeat it's
documentation, but highlight its
configuration subsystem.
HK2 offers support for software configuration in xml file. E.g. you can have pure class with few annotations:
@Configured
public interface HttpServer extends ConfigBeanProxy {
@Attribute
String getName();
void setName(String name);
}
Then it is mapped to xml:
<http-server name="..." />
It is similar to
JAXB, without explicit binding, or rather with binding by convention, and with restricted write access. Finally, with
HK2, consumer may have configuration injected:
@Service
public class Foo {
@Inject
HttpServer httpServer;
}
See my
previous post for how to start with HK2 support for xml configuration.
Recent version 2.2 of
HK2 among other things, introduces @Proxiable annotation. Combined with custom @Scope-d annotation, and applied to configuration object, it allows to have once injected configuration be different at execution time depending on the state of the system. So configuration may be different for Alice and Bob, tenants of your application.
See
Tenant Managed Scope Example for details on how to implement that @TenantScoped trick. And try it yourself, check out
examples/ctm@hk2.java.net.
From other hand, hopefully better known,
EclipseLink is implementation of Java persistence standard. Version
2.4 (Juno) brings better support for Multi-Tenancy. Beside
Documentation and
Examples I would like to recommend full-stack
MySports example, provided by
EclipseLink team also. Shortly, EclipseLink allows to impelement persistence with table per tenant or single table, using column discriminator and gives few architectural options:
- Dedicated Persistence Unit - separate persistence-unit in persistence.xml, so application must request the correct PersistenceContext or PersistenceUnit for tenant.
- Persistence Context per Tenant - single persistence unit definition in the persistence.xml and a shared persistence unit (EntityManagerFactory and cache).
- Persistence Unit per Tenant - single persistence unit defined in the persistence.xml and different persistence contexts with their own caches are created per tenant.
And now, applying @TenantScoped @Proxiable @Scope from
HK2 example above to one of the EclipseLink multi-tenant architecture,
HK2 will substitute persistence context for tenenat at runtime, like this:
public class TenantEntityManagerFactory implements Factory<entitymanager> {
@Inject
private TenantManager manager;
...
@TenantScoped
public EntityManager provide() {
String currentTenant = manager.getCurrentTenant();
Map properties = new HashMap();
properties.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT,
currentTenant);
properties.put(PersistenceUnitProperties.SESSION_NAME, currentTenant);
EntityManager em = Persistence.createEntityManagerFactory("multi-tenant-pu",
properties).createEntityManager();
return em;
}
}
So consuming code can have EntityManager provided once by HK2 dependency injection, and have it different depending on current tenant, thread safe! E.g.
public class Foo {
@Inject
EntityManager entityManager;
public void testPersistence() {
// assume tenantManager.setCurrentTenant() is called somewhere in stack
entityManager.getTransaction().begin();
Customer customer = new Customer();
customer.setName("ACME");
entityManager.persist(customer);
entityManager.getTransaction().commit();
}
}
See my
eclipselink-hk2@github for working example. It also demonstrates usage of
Extensible Entities.
That's it. Let me know if you try this model.