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.
No comments:
Post a Comment