Blog

Multi-tenant cloud applications with Spring-Data-JPA and EclipseLink

verfasst von Peter Leibiger am 12.03.2013

We have been working on an application that requires a multi-tenancy architecture, tenants are created on the fly, potentially reaching a couple hundred. This mean, that multi-tenancy via database/schema separation is off the table, which leaves us with a single table approach and discriminator columns.

Usually Hibernate is our tool of choice for JPA persistence and Hibernate4 added multi-tenancy support - great! After fiddling around for a while and reading the documentation a second time, you will find a comment for the discriminator strategy: "This strategy is not yet implemented in Hibernate as of 4.0 and 4.1. Its support is planned for 5.0.".

Too bad, what now? Well since JPA 2.1 adds multi-tenancy support and EclipseLink is the reference implementation, maybe they support our need? Turns out they do since version 2.3.0 and they have a great documentation which has more than enough information to get you started.

The last piece of the puzzle is Spring-Data-JPA for the data-access-layer, one of our favorites - and it works with EclipseLink out of the box. Now we just have to make it behave nicely with multi-tenant entities.

For multi-tenancy to work EclipseLink requires a property on the PersistenceUnit/EntityManager to be set. This can be done by calling

em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenantId);
at runtime. With Spring-Data you don't need to inject the EntityManager anymore, you don't even need to know there is one being used under the hood, so where can this be done?

First things first

To get started we define an interface that we can implement to access the id of the current tenant:
public interface CurrentTenantResolver<T extends Serializable> {
T getCurrentTenantId();
}
In our case we use Apache Shiro as security framework and access the current tenant from the session.
public class ShiroCurrentTenantResolver
implements CurrentTenantResolver<Long> {
@Override
public Long getCurrentTenantId() {
Session session = SecurityUtils.getSubject().getSession();
return (Long) session.getAttribute("tenantId");
}
}

Approach 1: Custom EntityMangerFactory

There was a blog post, which I can't seem to find anymore at this time, that suggested to use a custom EntityManagerFactory and Spring-EntityManagerFactoryBean to set the tenant-id property like so:
public class TenantAwareEntityManagerFactory implements EntityManagerFactory {
private final EntityManagerFactory delegate;
private final CurrentTenantResolver<Long> resolver;

public TenantAwareEntityManagerFactory(EntityManagerFactory delegate,
CurrentTenantResolver<Long> resolver) {
this.delegate = delegate;
this.resolver = resolver;
}

@Override
public EntityManager createEntityManager() {
Long tenantID = resolver.getCurrentTenantId();
Map<String, Long> map = new HashMap<String, Long>();
map.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantID);
return delegate.createEntityManager(map);
}

@Override
public EntityManager createEntityManager(Map map) {
Long tenantID = resolver.getCurrentTenantId();
map.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantID);
return delegate.createEntityManager(map);
}
// delegate the rest
}
This would have been a really nice and clean solution but unfortunately this doesn't work in all cases - specifically it doesn't work when you use transactions via Spring's @Transactional annotation. Why is that?

EclipseLink requires the tenant property to be set on the EntityManager after the transaction begins. This can be seen in all the examples in the documentation but is not clearly stated. There are however a couple entries in the mailing-list like this one that point to the problem.

When using @Transactional Spring handles the call to EntityManager.beginTransaction() and in order to do that it has to create an EntityManager instance thus setting the property before the transaction begins.

Approach 2: Custom Repository/RepositoryFactory implementation

In order to get this to work we have to set the tenant property inside the repository and we have to do it for every call to a repository method. You can probably achieve this with some AOP magic but here is the pragmatic way - extend SimpleJpaRepository and override all query methods:
public class MultiTenantSimpleJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
private final CurrentTenantResolver tenantResolver;
private final EntityManager em;

public MultiTenantSimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation,
EntityManager em, CurrentTenantResolver tenantResolver) {
super(entityInformation, em);
this.tenantResolver = tenantResolver;
this.em = em;
}

public MultiTenantSimpleJpaRepository(Class<T> domainClass, EntityManager em,
CurrentTenantResolver tenantResolver) {
super(domainClass, em);
this.tenantResolver = tenantResolver;
this.em = em;
}

protected void setCurrentTenant() {
em.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantResolver.getCurrentTenantId());
}

@Override
public <S extends T> S save(S entity) {
setCurrentTenant();
return super.save(entity);
}
// override the other methods
}
If you use the awesome QueryDslRepository then you should subclass it as well. To get Spring-Data-JPA to use our custom repository implementations we need to create our own RepositoryFactory and RepositoryFactoryBean.
public class MultiTenantJpaRepositoryFactory extends JpaRepositoryFactory {
private final CurrentTenantResolver currentTenantResolver;

public MultiTenantJpaRepositoryFactory(EntityManager entityManager, CurrentTenantResolver currentTenantResolver) {
super(entityManager);
this.currentTenantResolver = currentTenantResolver;
}

@Override
@SuppressWarnings("unchecked")
protected JpaRepository<?, ?> getTargetRepository(RepositoryMetadata metadata, EntityManager entityManager) {
final Class repositoryInterface = metadata.getRepositoryInterface();
final JpaEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());

final SimpleJpaRepository<?, ?> repo = isQueryDslExecutor(repositoryInterface) ?
new MultiTenantQueryDslJpaRepository(entityInformation, entityManager, currentTenantResolver) :
new MultiTenantSimpleJpaRepository(entityInformation, entityManager, currentTenantResolver);
repo.setLockMetadataProvider(LockModeRepositoryPostProcessor.INSTANCE.getLockMetadataProvider());
return repo;
}

@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
if (isQueryDslExecutor(metadata.getRepositoryInterface())) {
return MultiTenantQueryDslJpaRepository.class;
} else {
return MultiTenantSimpleJpaRepository.class;
}
}

private boolean isQueryDslExecutor(Class<?> repositoryInterface) {
return QUERY_DSL_PRESENT && QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface);
}
}
public class MultiTenantJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {
private CurrentTenantResolver currentTenantResolver;

@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MultiTenantJpaRepositoryFactory(entityManager, currentTenantResolver);
}

@Override
public void afterPropertiesSet() {
Assert.notNull(currentTenantResolver, "CurrentTenantResolver must not be null!");
super.afterPropertiesSet();
}

@Autowired
public void setCurrentTenantResolver(CurrentTenantResolver currentTenantResolver) {
this.currentTenantResolver = currentTenantResolver;
}
}
And finally we have to tell Spring-Data-JPA to use our new factory by specifying the factory class in your Spring XML:
<jpa:repositories base-package="com.codecraft.server.repository"
factory-class="com.codecraft.server.orm.MultiTenantJpaRepositoryFactoryBean"/>
Now we can use all the features of Spring-Data-JPA in a multi-tenancy environment, EclipseLink will automatically modify all queries except JPA native queries to return only results for the current tenant.
Also EclipseLink is new to us, it has been really easy and fun to use so far. It is gonna be a tough choice between Hibernate and EclipseLink in the future.

Befüllen dynamischer PDF-Formulare mit iText 2.1.7

verfasst von Thomas Kröniger am 25.01.2013

Zur Bearbeitung von PDF bietet sich unter Java insbesondere die iText-Bibliothek an. Bis Version 2.1.7 konnte iText unter LGPL frei genutzt werden, anschließend wurde das Lizenzmodell geändert. Für die kommerzielle Nutzung späterer Versionen werden Gebühren fällig. Für kleine und mittlere Projekte wird Version 2.1.7 daher noch gern genutzt. Die Schwächen liegen hier vor allem in der Bearbeitung von dynamischen PDF nach XFA-Standard (XML Forms Architecture) ab PDF-Version 1.5.
Zumindest das Befüllen dynamischer Formulare ist jedoch auch mit der angestaubten Bibliothek ohne weiteres möglich. Im Beispiel wurde aus einem XML-Schema ein einfaches Formular generiert:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="modelData" type="modelDataDef"/>

  <xs:complexType name="modelDataDef">
    <xs:sequence>
      <xs:element name="author" type="xs:string"/>
      <xs:element name="cars" type="carsDef"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="carsDef">
    <xs:sequence>
      <xs:element name="car" minOccurs="0" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="manufacturer" type="xs:string"/>
            <xs:element name="type" type="xs:string"/>
            <xs:element name="color" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:schema>



Mit iText soll das Formular mit schema-konformen Daten befüllt werden:


  T. Kröniger
  
    
      Audi
      A4
      Blau
    

    
      VW
      Golf IV
      Rot
    

    
      VW
      Polo
      Gelb
    

    
      BMW
      Z3
      Silber
    

  






Im ersten Schritt kann mit iText der XFA-Content des PDF ausgelesen werden:
        PdfReader reader = new PdfReader(PDF_FORM);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(PDF_FORM_FILLED));

Document xfaDomDocument = stamper.getAcroFields().getXfa().getDomDocument();

Nach XFA-Spezifikation müssten die Daten folgendermaßen integriert werden:


  
    
      T. Kröniger
      
        
          Audi
          A4
          Blau
        

        
          VW
          Golf IV
          Rot
        

        
          VW
          Polo
          Gelb
        

        
          BMW
          Z3
          Silber
        

      

    

  

  
    
      
      
        
          
          
          
        

      

    

  



Im XFA-Document wird zunächst die "data"-Node ausfindig gemacht und, sofern nicht vorhanden, angelegt.
        NodeList dataNode = xfaDocument.getElementsByTagNameNS(XFA_DATA_SCHEMA, "data");
if (dataNode.getLength() == 0) {
return createXfaDataNode();
}
return (Element) dataNode.item(0);

    private Element createXfaDataNode() {
Node parentNode = xfaDocument.getFirstChild();
while (xfaDocument.getChildNodes().getLength() == 0) {
parentNode = parentNode.getNextSibling();
}
if (parentNode == null) {
throw new RuntimeException("unable to create datasetNode");
}

Element xfaDatasetNode = parentNode.getOwnerDocument().createElement("xfa:datasets");
xfaDatasetNode.setAttribute("xmlns:xfa", XFA_DATA_SCHEMA);

Node dataNode = xfaDatasetNode.getOwnerDocument().createElementNS(XFA_DATA_SCHEMA, "xfa:data");
xfaDatasetNode.appendChild(dataNode);

parentNode.appendChild(xfaDatasetNode);

return xfaDatasetNode;
}

Jetzt können die zu befüllenden Daten im XFA-Stream untergebracht werden:
        Document xmlData = retreiveMyData();
Node xmlDataNode = xfaDocument.importNode(xmlData.getDocumentElement(), true);
xfaDataNode.appendChild(xmlDataNode);

Wichtig ist das Setzen der Changed-Flag:
        stamper.getAcroFields().getXfa().setChanged(true);

Über z.B. JAXB-Modellklassen kann somit eine saubere Datenbindung an dynamische PDF-Formulare geschaffen werden. Erfahrungsaustausch zum Thema ist stets willkommen.

Realizing async server architecture with GWTP and Atmosphere

verfasst von Peter Leibiger am 27.11.2012

We have been using the GWTP framework for several production applications so far. While the MVP module of GWTP allows for a maintainable and scalable client architecture, the GWTP-Dispatch module centralizes client-server communication around the command pattern with features like action handlers, caching and batching. 

Recently we have been working on integrating the Atmosphere framework and GWTP-Dispatch with the goal of allowing asynchronous execution of server code while maintaining the command pattern approach that GWTP-Dispatch uses. Specifically we had to work with some Camel endpoints and Akka actors that do some work and asynchronously provide a result which was to be displayed on the client.


Traditionally one would use polling to fetch the result later on but Atmosphere actually uses long polling - the request stays open until a response is written. Some servlet containers only have a very limited thread pool to handle requests, in these cases long running requests block the threads from handling other requests resulting in waits on the clients. With Atmosphere the thread that handles the request is suspended thus freeing the resources to handle other requests. To do that, Atmosphere uses different approaches depending on the servlet container that is being used, for example Servlet 3.0 with async support or Jetty Continuations. 

Now on to the implementation, Atmosphere already has an atmosphere-gwt-poll module that is based on the GWT RPC mechanism and provides a class called AtmospherePollService which is an implementation of the GWT RemoteServiceServlet class and can be used as base class for the GWTP DispatchService. It suspends the request and dispatches the actions to the fitting ActionHandlers. In contrast to the synchronous action handlers, the methods in the asynchronous implementation do not return a result, instead they receive a callback as parameter which expects the result of the action.

@Override
public void execute(final HeavyWork action, PollExecutionContext context,
final PollCallback<HeavyWork, HeavyWorkResult> callback)
throws ActionException {

// delegate the heavy work to a new thread
new Thread() {

@Override
public void run() {
final HeavyWorkResult result = new HeavyWorkResult();

// calculate result

callback.onSuccess(result);
}
}.start();
}

With the recent move of GWTP to Github it was easy to fork and provide the module together with an example on Github. It is currently based on Guice, we will however split it up into separate Spring and Guice modules over the next few days. There is also more work to be done regarding batch actions, batch action should be able to be executed in sequence or in parallel.

So why would it make sense to use this? Well if you have long running requests, maybe because of expensive computation or slow backends this will free up some resources and still allow you to send results back to the client. You can even split up your computation and use multiple processors, that would be very hard to do with a synchronous solution. Asynchronous execution may also be a first step towards a distributed and scalable service layer.


Comments and discussions are welcome!

ältere Beiträge