ConFoo 2015 - Supporting Multi-tenancy Applications with Java EE

38
Supporting Multi-tenancy Applications with Java EE Rodrigo Cândido da Silva @rcandidosilva

Transcript of ConFoo 2015 - Supporting Multi-tenancy Applications with Java EE

Supporting Multi-tenancy Applications with Java EERodrigo Cândido da Silva @rcandidosilva

About Me• Software Architect

• Java Platform • JUG Leader of GUJavaSC

• http://gujavasc.org • Twitter

• @rcandidosilva • Personal

• http://rodrigocandido.me

Agenda• Cloud Services Model • Multi-tenancy

• Concepts • Challenges

• Java EE + Multi-tenancy • Tenant Identification • UI Customization • Custom Business Rules • Database Support

• Demo

Cloud Services Model

SaaS Market

Multi-tenancy

• One application instance to multiple clients (tenant)

• Inverse of the multiple instances architecture

Multi-instances vs. Multi-tenant

Multi-instances vs. Multi-tenantFeature Multi-instances Multi-tenant

Cost Structure Can support only flat pricing

Supports usage based pricing

Resources Dedicated resources Shared resources

Operation and Maintenance

Manage and administer as many instances as customers

Manager and administer a single instance for a number of customers

Scalable Model Not scalable Scalable

Cloud and Multi-tenancy

Challenges• Data separation • UI and business rules customization • Access control by tenant • Resource provisioning • Integrations • Application update • Failover tolerance

Pros and Cons• Pros

• Low maintenance cost • Same source code for all customers

• High scalability • Sharing resources between customers

• Cons • High complexity

• Separation by tenant-id • More failure risks

• If code breaks -> breaks to all customers • Low flexibility available to the customers

Multi-tenancy Concepts• Adoption levels

• Level 1 (Customized) • Level 2 (Configurable) • Level 3 (Scalable)

• Database Strategy • Separate Databases • Separate Tables • Shared Database

Level 1 - Customized• [N] applications and [N] databases

Level 2 - Configurable• [1] application and [N] databases

Level 3 - Scalable• [1] application and [1] database

Database StrategySeparate Databases

Separate Tables

Shared Database

Database StrategyFeature Separate DBs Separate Tables Shared Database

Data Customization

Security

Inter-dependency and Performance

Scalable Model

Customer On-boarding

What is the Best Choice?• Depends on…

• Data Customization • Addition or removal of columns in the data store

• Function Customization • The functionality executed for a specific business can vary by

customers

• Process Customization • The business process can vary for each customer

• Licensing Features • The product has multiple licenses which define the functionality

that is enabled for the customer

Java EE + Multi-tenancy• Java Servlets

• Tenant Identification • JavaServer Faces (JSF)

• UI Customization • Context and Dependency Injection (CDI)

• Custom Business Rules • Java Persistence API (JPA)

• Database with Multi-tenant Support

Java Servlets• Tenant Identification

• DNS Resolver • http://customer1.myapp.com • http://customer2.myapp.com

• Sub-contexts Resolver • http://www.myapp.com/customer1 • http://www.myapp.com/customer2

• Login Access Resolver

Java Servlets

public class TenantRequestListener implements ServletRequestListener { ...

@Override public void requestInitialized(final ServletRequestEvent servletRequestEvent) { final HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest(); loadTenant(request); }

protected void loadTenant(HttpServletRequest request) { ... }

}

• DNS and Sub-contexts resolver

public class TenantThreadLocal { public static final ThreadLocal<String> tenantThreadLocal = new ThreadLocal<String>(); }

JSF + Multi-tenancy• Flexible software architecture • Artifacts packaged in separated JAR’s • Composition at runtime • Templates and contracts • Resource library • Look-and-feel customization • RenderKit features • Localization support

JSF Facelets

JSF Multi-templating• Resource Library Contracts

• Convention • All available contracts discovered at startup

• Configuration • faces-config.xml by <resource-library-contract> • contracts attribute in <f:view>

JSF Multi-templating<html xmlns="http://www.w3.org/1999/xhtml” xmlns:h="http://java.sun.com/jsf/html” xmlns:ui="http://java.sun.com/jsf/facelets"> <body> <ui:composition template="#{template}”> ... </ui:composition> </body> </html>

<?xml version="1.0" encoding="UTF-8"?> <web-app> <context-param> <param-name>javax.faces.view.TEMPLATE</param-name> <param-value>mybusiness</param-value> </context-param> </web-app>

CDI + Multi-tenancy• Custom Business Rules

interface Service { public void businessMethod(); }

class Customer01Service implements Service { public void businessMethod() { ... } }

class Customer02Service implements Service { public void businessMethod() { ... } }

@Produces public Service getService() { switch(currentTenant) { case "customer01": return new Customer01Service(); case “customer02": return new Customer02Service(); } }

CDI + Multi-tenancy• Method Interceptors

@Interceptor @Priority(Interceptor.Priority.APPLICATION) public class MultiTenantInterceptor { @AroundInvoke protected Object invoke(InvocationContext ctx) throws Exception { ... } }

<beans xmlns="..."> <interceptors> <class>MultiTenantInterceptor</class> </interceptors> </beans>

JPA + Multi-tenancy• There is no standard at this time • EclipseLink

• Multi-tenancy support using @Multitenant • Multitenant strategies

• @Multitenant(SINGLE_TABLE) – default • @Multitenant(TABLE_PER_TENANT) • @Multitenant(VPD)

• Hibernate • Supports tenant identifier features

• MultiTenantConnectionProvider • CurrentTenantIdentifierResolver

EclipseLink SINGLE_TABLE

@Entity @Table(name=“EMP”) @Multitenant(SINGLE_TABLE) @TenantDiscriminatorColumn(name = “TENANT_ID”, contextProperty = “tenant-id”) public class Employee { ... }

HashMap properties = new HashMap(); properties.put("tenant.id", "707"); ... EntityManager em = Persistence .createEntityManagerFactory( "multi-tenant”,properties) .createEntityManager();

<persistence-unit name="multi-tenant"> ... <properties> <property name="tenant.id" value="707"/> ... </properties> </persistence-unit>

EclipseLink TABLE_PER_TENANT

<entity class="Employee"> <multitenant type="TABLE_PER_TENANT"> <tenant-table-discriminator type="SCHEMA" context-property="eclipselink.tenant-id"/> </multitenant> <table name="EMP"> ... </entity>

@Entity @Table(name=“EMP”) @Multitenant(TABLE_PER_TENANT) @TenantTableDiscriminator(type=SCHEMA, contextProperty="eclipselink.tenant-id") public class Employee { ... }

EclipseLink VPD

@Entity @Multitenant @TenantDiscriminatorColumn(name = "USER_ID", contextProperty = "tenant.id") @Cacheable(false)

public class Task implements Serializable { ...

CALL DBMS_RLS.ADD_POLICY ('SCOTT', 'TASK', 'todo_list_policy', 'SCOTT', 'ident_func', 'select, update, delete'));

<properties> <property name="eclipselink.session.customizer" value="example.VPDSessionCustomizer" /> <property name="eclipselink.session-event-listener" value="example.VPDSessionEventAdapter" /> <property name="eclipselink.jdbc.exclusive-connection.mode" value="Always" /> </properties>

Hibernate MultiTenantConnectionProvider

public class MultiTenantProvider implements MultiTenantConnectionProvider { public Connection getConnection(String tenantIdentifier) throws SQLException { final Connection connection = getAnyConnection(); connection.createStatement().execute( "SET SCHEMA '" + tenantIdentifier + "'"); return connection; } public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { releaseAnyConnection(connection); } }

Hibernate CurrentTenantIdentifierResolver

public class SchemaResolver implements CurrentTenantIdentifierResolver { @Override public String resolveCurrentTenantIdentifier() { return resolveTenant(); } @Override public boolean validateExistingCurrentSessions() { return false; } }

Hibernate persistence.xml

<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/ persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http:// java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="default"> <properties> <property name="javax.persistence.provider" value="org.hibernate.ejb.HibernatePersistence" /> <property name="hibernate.multiTenancy" value="SCHEMA"/> <property name="hibernate.tenant_identifier_resolver" value="SchemaResolver"/> <property name="hibernate.multi_tenant_connection_provider" value="MultiTenantProvider"/> </properties> </persistence-unit> </persistence>

Demo• EclipseLink MySports Demo

• http://wiki.eclipse.org/EclipseLink/Examples/MySports • http://git.eclipse.org/c/eclipselink/examples.git

Questions

?

References• http://msdn.microsoft.com/en-us/library/aa479086.aspx • https://developers.google.com/appengine/docs/java/multitenancy/ • http://www.ibm.com/developerworks/java/library/j-multitenant-java/index.html • http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_multitenant.htm • http://2012.con-fess.com/sessions/-/details/122/JSF-and-JavaEE-7-for-multi-tenant-

applications • http://jdevelopment.nl/jsf-22/ • http://picketlink.org • https://developers.google.com/appengine/docs/java/multitenancy/ • http://www.jboss.org/quickstarts/picketlink/picketlink-authentication-idm-multi-tenancy/ • http://wiki.eclipse.org/EclipseLink/Examples/MySports

Thank you! @rcandidosilva

rodrigocandido.me