Thursday, 18 June 2015

Deltaspike configuration properties encryption with Jasypt

The following article shows how to support transparent encryption of properties files entries using Jasypt lite and the Deltaspike configuration mechanism.  My solution uses Deltaspike 1.2.1 and Jasypt 1.9.1, but it should work for later versions as well. Properties files entries are decrypted at runtime using a password stored in a system property which can be passed on the command line at application startup.

Deltaspike is a set of extensions built on top of CDI (Context and dependency injection), available at: https://deltaspike.apache.org/

Jasypt is tool written in Java to add transparent encryption to different parts of an application (e.g. database fields, properties files entries). The project website is located at: http://www.jasypt.org/ 

As first step, set up the application, using Deltaspike configuration, to read properties file and inject properties values.


package com.github.lbitonti.deltaspikeconfig;

import org.apache.deltaspike.core.api.config.ConfigResolver;
import org.apache.deltaspike.core.api.config.PropertyFileConfig;

public class ConfigurationFile implements PropertyFileConfig {

  static {
    ConfigResolver.addConfigFilter(new ConfigurationDecryptingFilter());
  }

  @Override
  public String getPropertyFileName() {
    return "config.properties";
  }

  @Override
  public boolean isOptional() {
    return false;
  }

}

This is loaded at startup using the ServiceLoader mechanism. For this to happen, add a file named 'org.apache.deltaspike.core.api.config.PropertyFileConfig' to the project 'META-INF/services' directory. The file should contain a single line declaring the class to be loaded. In my case:
com.github.lbitonti.deltaspikeconfig.ConfigurationFile

After this is done properties read from config.properties can be injected into (cdi) beans. For instance, to inject the value of a property with key a.config.value, you can use the following syntax:


@Inject @ConfigProperty(name = "a.config.value")
private String aConfigValue;
Check the Deltaspike documentation for more details.

As you can see in the static block at the top of the ConfigurationFile class, this triggers the load of another class, ConfigurationDecryptingFilter, which takes care of initializing Jasypt and providing the necessary code to decrypt the properties file entries that are actually encrypted. The content of this second class are as follows:


package com.github.lbitonti.deltaspikeconfig;

import org.apache.deltaspike.core.spi.config.ConfigFilter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;

import java.security.Security;

public class ConfigurationDecryptingFilter implements ConfigFilter {

  public static final String PROVIDER = BouncyCastleProvider.PROVIDER_NAME;

  static {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
  }

  private StandardPBEStringEncryptor encryptor;

  public ConfigurationDecryptingFilter() {
    String encPwd = System.getProperty("enc.pwd");
    encryptor = new StandardPBEStringEncryptor();
    encryptor.setPassword(encPwd);
    encryptor.setProviderName(PROVIDER);
    encryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC");
    encryptor.initialize();
  }

  @Override
  public String filterValue(String key, String value) {
    if (isEncrypted(value)) {
      return encryptor.decrypt(value.substring(4, value.length() - 1));
    }
    return value;
  }

  @Override
  public String filterValueForLog(String key, String value) {
    if (isEncrypted(value)) {
      return "<" + key + " value>";
    }
    return value;
  }

  protected boolean isEncrypted(String value) {
    if (value != null && value.startsWith("ENC(") && value.endsWith(")")) {
      return true;
    }
    return false;
  }

}

With this configuration, all values that are enclosed between the 2 following character sequences: 'ENC(' and ')' are decrypted using the password passed as system property with name 'enc.pwd'. All values that do not start and terminate with these tokens are simply returned without any processing.
ConfigurationDecryptingFilter also initializes Bouncycastle, as this allows us to use a stronger encryption algorithm (i.e. AES-256). For this to work, the JVM used should have the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files installed. For the Oracle JVM these can be downloaded from the Oracle website.

For all this to work the following dependencies are needed in your pom file (if you use maven that is):


<dependency>
    <groupId>org.jasypt</groupId>
    <artifactId>jasypt</artifactId>
    <version>1.9.1</version>
    <classifier>lite</classifier>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.51</version>
</dependency>

<dependency>
    <groupId>org.apache.deltaspike.core</groupId>
    <artifactId>deltaspike-core-api</artifactId>
    <version>1.2.1</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.apache.deltaspike.core</groupId>
    <artifactId>deltaspike-core-impl</artifactId>
    <version>1.2.1</version>
    <scope>runtime</scope>
</dependency>

A CDI provider (e.g. Weld, OpenWebBeans) has to be included as a dependency as well.

Thursday, 18 July 2013

Transparent DB encryption with Jasypt and JPA 2.1

Jasypt is tool written in Java to add transparent encryption to different parts of an application (e.g. database fields, properties files entries). The project website is located at: http://www.jasypt.org/ 

The following code shows how to support transparent encryption of some Database field using Jasypt in Eclipselink. Jasypt integrates well out of the box with Hibernate using some custom types supplied in the Jasypt distribution. There seems to be no equivalent support for Eclipselink.
My solution uses Eclipselink 2.5 and the new @Converter annotation introduced by the JPA 2.1 standard.

 package lb.util;  
   
 import javax.persistence.AttributeConverter;  
 import javax.persistence.Converter;  
   
   
 @Converter  
 public class EncryptedStringConverter implements AttributeConverter<String, String> {  
   
   private static final String STRING_ENCRYPTOR_NAME = "jpaStringEncryptor";  
   
   
   public EncryptedStringConverter() {  
   }  
   
   public String convertToDatabaseColumn(String value) {  
     if ( value == null ) {  
       return null;  
     }  
     return JpaPBEEncryptorRegistry.getInstance().getPBEStringEncryptor(STRING_ENCRYPTOR_NAME).encrypt(value);  
   }  
   
   public String convertToEntityAttribute(String value) {  
     if ( value == null ) {  
       return null;  
     }  
     return JpaPBEEncryptorRegistry.getInstance().getPBEStringEncryptor(STRING_ENCRYPTOR_NAME).decrypt(value);  
   }  
   
 }  
   


This is the only part that is strictly related to JPA. The rest of the code, which follows is ported from code available in the jasypt-hibernate-4 package.

 package lb.util;  
   
 import org.jasypt.encryption.pbe.PBEStringEncryptor;  
   
 import java.util.HashMap;  
   
   
 /**  
  * Maintains a registry of Jasypt JPA encryptors. Currently only String encryptor is supported.  
  * Derived from org.jasypt.hibernate4.encryptor.HibernatePBEEncryptorRegistry  
  *  
  */  
 public final class JpaPBEEncryptorRegistry {  
   
   
   // The singleton instance  
   private static final JpaPBEEncryptorRegistry instance =  
       new JpaPBEEncryptorRegistry();  
   
   
   // Registry maps  
   private final HashMap stringEncryptors = new HashMap();  
   
   
   /**  
    * Returns the singleton instance of the registry.  
    *  
    * @return the registry.  
    */  
   public static JpaPBEEncryptorRegistry getInstance() {  
     return instance;  
   }  
   
   // The registry cannot be externally instantiated.  
   private JpaPBEEncryptorRegistry() {  
     super();  
   }  
   
   
   /**  
    * Registers a <tt>PBEStringEncryptor</tt> object with the specified  
    * name.  
    *  
    * @param registeredName the registered name.  
    * @param encryptor the encryptor to be registered.  
    */  
   public synchronized void registerPBEStringEncryptor(  
       final String registeredName, final PBEStringEncryptor encryptor) {  
     final JpaPBEStringEncryptor jpaEncryptor =  
         new JpaPBEStringEncryptor(registeredName, encryptor);  
     this.stringEncryptors.put(registeredName, jpaEncryptor);  
   }  
   
   
   
   // Not public: this is used from  
   // JpaPBEStringEncryptor.setRegisteredName.  
   synchronized void registerJpaPBEStringEncryptor(  
       final JpaPBEStringEncryptor jpaEncryptor) {  
     this.stringEncryptors.put(  
         jpaEncryptor.getRegisteredName(),  
         jpaEncryptor);  
   }  
   
   
   // Not public: this is used from  
   // JpaPBEStringEncryptor.setRegisteredName.  
   synchronized void unregisterJpaPBEStringEncryptor(final String name) {  
     this.stringEncryptors.remove(name);  
   }  
   
   
   /**  
    * Returns the <tt>PBEStringEncryptor</tt> registered with the specified  
    * name (if exists).  
    *  
    * @param registeredName the name with which the desired encryptor was  
    *    registered.  
    * @return the encryptor, or null if no encryptor has been registered with  
    *     that name.  
    */  
   public synchronized PBEStringEncryptor getPBEStringEncryptor(  
       final String registeredName) {  
     final JpaPBEStringEncryptor jpaEncryptor =  
         (JpaPBEStringEncryptor) this.stringEncryptors.get(registeredName);  
     if (jpaEncryptor == null) {  
       return null;  
     }  
     return jpaEncryptor.getEncryptor();  
   }  
   
 }  
   


 package lb.util;  
   
 import org.jasypt.encryption.pbe.PBEStringEncryptor;  
 import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;  
 import org.jasypt.encryption.pbe.config.PBEConfig;  
 import org.jasypt.exceptions.EncryptionInitializationException;  
 import org.jasypt.salt.SaltGenerator;  
   
 import java.security.Provider;  
   
   
 /**  
  * Jasypt String encryptor that can be used within a Jpa application.  
  * Derived from org.jasypt.hibernate4.encryptor.HibernatePBEStringEncryptor  
  *  
  */  
 public final class JpaPBEStringEncryptor {  
   
     private String registeredName = null;  
     private PBEStringEncryptor encryptor = null;  
     private boolean encryptorSet = false;  
   
   
     /**  
      * Creates a new instance of <tt>JpaPBEStringEncryptor</tt>. It also  
      * creates a <tt>StandardPBEStringEncryptor</tt> for internal use, which  
      * can be overriden by calling <tt>setEncryptor(...)</tt>.  
      */  
     public JpaPBEStringEncryptor() {  
       super();  
       this.encryptor = new StandardPBEStringEncryptor();  
       this.encryptorSet = false;  
     }  
   
   
     /*  
      * For internal use only, by the Registry, when a PBEStringEncryptor  
      * is registered programmatically.  
      */  
     JpaPBEStringEncryptor(final String registeredName,  
                   final PBEStringEncryptor encryptor) {  
       this.encryptor = encryptor;  
       this.registeredName = registeredName;  
       this.encryptorSet = true;  
     }  
   
   
     /**  
      * Returns the encryptor which this object wraps.  
      *  
      * @return the encryptor.  
      */  
     public synchronized PBEStringEncryptor getEncryptor() {  
       return this.encryptor;  
     }  
   
   
     /**  
      * Sets the <tt>PBEStringEncryptor</tt> to be held (wrapped) by this  
      * object. This method is optional and can be only called once.  
      *  
      * @param encryptor the encryptor.  
      */  
     public synchronized void setEncryptor(final PBEStringEncryptor encryptor) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       this.encryptor = encryptor;  
       this.encryptorSet = true;  
     }  
   
   
     /**  
      * Sets the password to be used by the internal encryptor, if a specific  
      * encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @param password the password to be set for the internal encryptor  
      */  
     public void setPassword(final String password) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setPassword(password);  
     }  
   
   
     /**  
      * Sets the password to be used by the internal encryptor (as a char[]), if a specific  
      * encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @since 1.8  
      * @param password the password to be set for the internal encryptor  
      */  
     public void setPasswordCharArray(final char[] password) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setPasswordCharArray(password);  
     }  
   
   
     /**  
      * Sets the algorithm to be used by the internal encryptor, if a specific  
      * encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @param algorithm the algorithm to be set for the internal encryptor  
      */  
     public void setAlgorithm(final String algorithm) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setAlgorithm(algorithm);  
     }  
   
   
     /**  
      * Sets the key obtention iterations to be used by the internal encryptor,  
      * if a specific encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @param keyObtentionIterations to be set for the internal encryptor  
      */  
     public void setKeyObtentionIterations(final int keyObtentionIterations) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setKeyObtentionIterations(  
           keyObtentionIterations);  
     }  
   
   
     /**  
      * Sets the salt generator to be used by the internal encryptor,  
      * if a specific encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @param saltGenerator the salt generator to be set for the internal  
      *           encryptor.  
      */  
     public void setSaltGenerator(final SaltGenerator saltGenerator) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setSaltGenerator(saltGenerator);  
     }  
   
   
     /**  
      * Sets the name of the JCE provider to be used by the internal encryptor,  
      * if a specific encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @since 1.3  
      *  
      * @param providerName the name of the JCE provider (already registered)  
      */  
     public void setProviderName(final String providerName) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setProviderName(providerName);  
     }  
   
   
     /**  
      * Sets the JCE provider to be used by the internal encryptor,  
      * if a specific encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @since 1.3  
      *  
      * @param provider the JCE provider to be used  
      */  
     public void setProvider(final Provider provider) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setProvider(provider);  
     }  
   
   
     /**  
      * Sets the type of String output ("base64" (default), "hexadecimal") to  
      * be used by the internal encryptor,  
      * if a specific encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @since 1.3  
      *  
      * @param stringOutputType the type of String output  
      */  
     public void setStringOutputType(final String stringOutputType) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setStringOutputType(stringOutputType);  
     }  
   
   
     /**  
      * Sets the PBEConfig to be used by the internal encryptor,  
      * if a specific encryptor has not been set with <tt>setEncryptor(...)</tt>.  
      *  
      * @param config the PBEConfig to be set for the internal encryptor  
      */  
     public void setConfig(final PBEConfig config) {  
       if (this.encryptorSet) {  
         throw new EncryptionInitializationException(  
             "An encryptor has been already set: no " +  
                 "further configuration possible on jpa wrapper");  
       }  
       final StandardPBEStringEncryptor standardPBEStringEncryptor =  
           (StandardPBEStringEncryptor) this.encryptor;  
       standardPBEStringEncryptor.setConfig(config);  
     }  
   
   
     /**  
      * Encrypts a message, delegating to wrapped encryptor.  
      *  
      * @param message the message to be encrypted.  
      * @return the encryption result.  
      */  
     public String encrypt(final String message) {  
       if (this.encryptor == null) {  
         throw new EncryptionInitializationException(  
             "Encryptor has not been set into jpa wrapper");  
       }  
       return this.encryptor.encrypt(message);  
     }  
   
   
     /**  
      * Decypts a message, delegating to wrapped encryptor  
      *  
      * @param encryptedMessage the message to be decrypted.  
      * @return the result of decryption.  
      */  
     public String decrypt(final String encryptedMessage) {  
       if (this.encryptor == null) {  
         throw new EncryptionInitializationException(  
             "Encryptor has not been set into jpa wrapper");  
       }  
       return this.encryptor.decrypt(encryptedMessage);  
     }  
   
   
   
     /**  
      * Sets the registered name of the encryptor and adds it to the registry.  
      *  
      * @param registeredName the name with which the encryptor will be  
      *            registered.  
      */  
     public void setRegisteredName(final String registeredName) {  
       if (this.registeredName != null) {  
         // It had another name before, we have to clean  
         JpaPBEEncryptorRegistry.getInstance().  
             unregisterJpaPBEStringEncryptor(this.registeredName);  
       }  
       this.registeredName = registeredName;  
       JpaPBEEncryptorRegistry.getInstance().  
           registerJpaPBEStringEncryptor(this);  
     }  
   
     /**  
      * Returns the name with which the wrapped encryptor is registered at  
      * the registry.  
      *  
      * @return the registered name.  
      */  
     public String getRegisteredName() {  
       return this.registeredName;  
     }  
   
 }  
   


I've only ported the Jasypt String encryptor as that is all I needed, but other convertors can be easily ported from the corresponding Hibernate ones. Please refer to the original Jasypt source code for more information on how to port the other (e.g. byte[]) converters and add them to the registry. To configure transparent AES-256 encryption using spring xml then you would use the following:

   <!-- Jasypt Encryption/Decryption -->  
   <bean id="cryptProvider"  
      class="lb.security.BouncyCastleProvider" init-method="init"/>  
   
   
   <bean id="strongEncryptor"  
      class="org.jasypt.encryption.pbe.PooledPBEStringEncryptor">  
    <property name="algorithm">  
     <value>PBEWITHSHA256AND256BITAES-CBC-BC</value>  
    </property>  
    <property name="password">  
     <value>somepassword</value>  
    </property>  
    <property name="providerName">  
     <value>BC</value>  
    </property>  
    <property name="poolSize">  
     <value>4</value>  
    </property>  
   </bean>  
   
   <bean id="jpaStringEncryptor" class="lb.util.JpaPBEStringEncryptor" depends-on="cryptProvider,strongEncryptor">  
    <property name="registeredName">  
     <value>jpaStringEncryptor</value>  
    </property>  
    <property name="encryptor" ref="strongEncryptor"/>  
   </bean>  
   


For this to work you need Bouncycastle (I am using bcprov-jdk16.jar) in your classpath and also the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files in your JRE lib/security directory. If you don't want to (or cannot) use Bouncycastle or the JCE extended policy files you will have to make the following changes in the Spring configuration: 
  • remove the "providerName" property;
  • remove the cryptProvider bean;
  • change the encryption algorithm to "PBEWithMD5AndDES"
Now String fields in JPA entities can be transparently encrypted by just annotating them as follows:

 @Convert(converter=EncryptedStringConverter.class)  
 @Column(length = 250)  
 private String someSecretField;  
   


Spring 3.x + Hibernate 4.x consistent cache configuration

The following sections describe a solution to unify caching in a Spring and Hibernate (2nd level cache) application. My solution, which works with Hibernate 4, is based on the code presented in the following article which is related to Hibernate 3:

http://leshazlewood.com/2007/12/06/spring-cache-configuration-hibernate-ehcache-et-al/

As explained in that article, the main benefits of this approach are:
  1. EhCache is used for both Spring and Hibernate caching
  2. Caching is initialized and configured through Spring in a single place and a single cache manager is used to manage all caches consistently.
This is the updated code that works with Hibernate 4.x and Spring 3.x. It uses code from Hibernate 4, therefore it's released under the GNU Lesser General Public License (LGPL) as the original.


 /*  
  * Hibernate, Relational Persistence for Idiomatic Java  
  *  
  * Copyright (c) 2011, Red Hat Inc. or third-party contributors as  
  * indicated by the @author tags or express copyright attribution  
  * statements applied by the authors. All third-party contributions are  
  * distributed under license by Red Hat Inc.  
  *  
  * This copyrighted material is made available to anyone wishing to use, modify,  
  * copy, or redistribute it subject to the terms and conditions of the GNU  
  * Lesser General Public License, as published by the Free Software Foundation.  
  *  
  * This program is distributed in the hope that it will be useful,  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  
  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License  
  * for more details.  
  *  
  * You should have received a copy of the GNU Lesser General Public License  
  * along with this distribution; if not, write to:  
  * Free Software Foundation, Inc.  
  * 51 Franklin Street, Fifth Floor  
  * Boston, MA 02110-1301 USA  
  */  
 package lb.cache;  
   
   
 import java.net.URL;  
 import java.util.Properties;  
   
 import net.sf.ehcache.CacheManager;  
 import net.sf.ehcache.Ehcache;  
 import net.sf.ehcache.util.ClassLoaderUtil;  
 import org.hibernate.cache.ehcache.EhCacheMessageLogger;  
 import org.jboss.logging.Logger;  
   
 import org.hibernate.cache.CacheException;  
 import org.hibernate.cache.ehcache.internal.nonstop.NonstopAccessStrategyFactory;  
 import org.hibernate.cache.ehcache.internal.regions.EhcacheCollectionRegion;  
 import org.hibernate.cache.ehcache.internal.regions.EhcacheEntityRegion;  
 import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;  
 import org.hibernate.cache.ehcache.internal.regions.EhcacheQueryResultsRegion;  
 import org.hibernate.cache.ehcache.internal.regions.EhcacheTimestampsRegion;  
 import org.hibernate.cache.ehcache.internal.strategy.EhcacheAccessStrategyFactory;  
 import org.hibernate.cache.ehcache.internal.strategy.EhcacheAccessStrategyFactoryImpl;  
 import org.hibernate.cache.ehcache.internal.util.HibernateUtil;  
 import org.hibernate.cache.ehcache.management.impl.ProviderMBeanRegistrationHelper;  
 import org.hibernate.cache.spi.CacheDataDescription;  
 import org.hibernate.cache.spi.CollectionRegion;  
 import org.hibernate.cache.spi.EntityRegion;  
 import org.hibernate.cache.spi.NaturalIdRegion;  
 import org.hibernate.cache.spi.QueryResultsRegion;  
 import org.hibernate.cache.spi.RegionFactory;  
 import org.hibernate.cache.spi.TimestampsRegion;  
 import org.hibernate.cache.spi.access.AccessType;  
 import org.hibernate.cfg.Settings;  
 import org.hibernate.service.classloading.spi.ClassLoaderService;  
 import org.hibernate.service.spi.InjectService;  
   
 public class ExternalEhCacheRegionFactory implements RegionFactory {  
   
   private static volatile CacheManager manager = null;  
   
   /**  
    * This is the method that is called by an external framework (e.g. Spring) to set the  
    * constructed CacheManager for all instances of this class. Therefore, when  
    * Hibernate instantiates this class, the previously statically injected CacheManager  
    * will be used for all hibernate calls to build caches.  
    * @param cacheManager the CacheManager instance to use for a HibernateSession factory using  
    * this class as its cache.provider_class.  
    */  
   public static void setManager(CacheManager cacheManager) {  
     ExternalEhCacheRegionFactory.manager = cacheManager;  
   }  
   
   public void start(Settings settings, Properties properties) throws CacheException {  
   //ignored, CacheManager lifecycle handled by the IoC container  
   }  
   
   public void stop() {  
   //ignored, CacheManager lifecycle handled by the IoC container  
   }  
   
   
   /**  
    * The Hibernate system property specifying the location of the ehcache configuration file name.  
    * <p/>  
    * If not set, ehcache.xml will be looked for in the root of the classpath.  
    * <p/>  
    * If set to say ehcache-1.xml, ehcache-1.xml will be looked for in the root of the classpath.  
    */  
   public static final String NET_SF_EHCACHE_CONFIGURATION_RESOURCE_NAME = "net.sf.ehcache.configurationResourceName";  
   
   private static final EhCacheMessageLogger LOG = Logger.getMessageLogger(  
       EhCacheMessageLogger.class,  
       ExternalEhCacheRegionFactory.class.getName()  
   );  
   
   /**  
    * MBean registration helper class instance for Ehcache Hibernate MBeans.  
    */  
   protected final ProviderMBeanRegistrationHelper mbeanRegistrationHelper = new ProviderMBeanRegistrationHelper();  
   
   /**  
    * Settings object for the Hibernate persistence unit.  
    */  
   protected Settings settings;  
   
   /**  
    * {@link EhcacheAccessStrategyFactory} for creating various access strategies  
    */  
   protected final EhcacheAccessStrategyFactory accessStrategyFactory =  
       new NonstopAccessStrategyFactory( new EhcacheAccessStrategyFactoryImpl() );  
   
   /**  
    * Whether to optimize for minimals puts or minimal gets.  
    * <p/>  
    * Indicates whether when operating in non-strict read/write or read-only mode  
    * Hibernate should optimize the access patterns for minimal puts or minimal gets.  
    * In Ehcache we default to minimal puts since this should have minimal to no  
    * affect on unclustered users, and has great benefit for clustered users.  
    * <p/>  
    * This setting can be overridden by setting the "hibernate.cache.use_minimal_puts"  
    * property in the Hibernate configuration.  
    *  
    * @return true, optimize for minimal puts  
    */  
   public boolean isMinimalPutsEnabledByDefault() {  
     return true;  
   }  
   
   /**  
    * {@inheritDoc}  
    */  
   public long nextTimestamp() {  
     return net.sf.ehcache.util.Timestamper.next();  
   }  
   
   /**  
    * {@inheritDoc}  
    */  
   public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)  
       throws CacheException {  
     return new EhcacheEntityRegion( accessStrategyFactory, getCache( regionName ), settings, metadata, properties );  
   }  
   
   @Override  
   public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata)  
       throws CacheException {  
     return new EhcacheNaturalIdRegion( accessStrategyFactory, getCache( regionName ), settings, metadata, properties );  
   }  
   
   /**  
    * {@inheritDoc}  
    */  
   public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata)  
       throws CacheException {  
     return new EhcacheCollectionRegion(  
         accessStrategyFactory,  
         getCache( regionName ),  
         settings,  
         metadata,  
         properties  
     );  
   }  
   
   /**  
    * {@inheritDoc}  
    */  
   public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException {  
     return new EhcacheQueryResultsRegion( accessStrategyFactory, getCache( regionName ), properties );  
   }  
   
   @InjectService  
   public void setClassLoaderService(ClassLoaderService classLoaderService) {  
     this.classLoaderService = classLoaderService;  
   }  
   
   private ClassLoaderService classLoaderService;  
   
   /**  
    * {@inheritDoc}  
    */  
   public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException {  
     return new EhcacheTimestampsRegion( accessStrategyFactory, getCache( regionName ), properties );  
   }  
   
   private Ehcache getCache(String name) throws CacheException {  
     try {  
       Ehcache cache = manager.getEhcache( name );  
       if ( cache == null ) {  
         LOG.unableToFindEhCacheConfiguration( name );  
         manager.addCache( name );  
         cache = manager.getEhcache( name );  
         LOG.debug( "started EHCache region: " + name );  
       }  
       HibernateUtil.validateEhcache( cache );  
       return cache;  
     }  
     catch ( net.sf.ehcache.CacheException e ) {  
       throw new CacheException( e );  
     }  
   
   }  
   
   /**  
    * Load a resource from the classpath.  
    */  
   protected URL loadResource(String configurationResourceName) {  
     URL url = null;  
     if ( classLoaderService != null ) {  
       url = classLoaderService.locateResource( configurationResourceName );  
     }  
     if ( url == null ) {  
       ClassLoader standardClassloader = ClassLoaderUtil.getStandardClassLoader();  
       if ( standardClassloader != null ) {  
         url = standardClassloader.getResource( configurationResourceName );  
       }  
       if ( url == null ) {  
         url = ExternalEhCacheRegionFactory.class.getResource( configurationResourceName );  
       }  
     }  
     if ( LOG.isDebugEnabled() ) {  
       LOG.debugf(  
           "Creating EhCacheRegionFactory from a specified resource: %s. Resolved to URL: %s",  
           configurationResourceName,  
           url  
       );  
     }  
     if ( url == null ) {  
   
       LOG.unableToLoadConfiguration( configurationResourceName );  
     }  
     return url;  
   }  
   
   /**  
    * Default access-type used when the configured using JPA 2.0 config. JPA 2.0 allows <code>@Cacheable(true)</code> to be attached to an  
    * entity without any access type or usage qualification.  
    * <p/>  
    * We are conservative here in specifying {@link AccessType#READ_WRITE} so as to follow the mantra of "do no harm".  
    * <p/>  
    * This is a Hibernate 3.5 method.  
    */  
   public AccessType getDefaultAccessType() {  
     return AccessType.READ_WRITE;  
   }  
   
 }  
   


The cache manager can be injected using Spring xml.


  <!-- Use Spring Cache manager in Hibernate -->  
  <bean id="cacheManagerInjector"  
   class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">  
   <property name="staticMethod" value="lb.cache.ExternalEhCacheRegionFactory.setManager"/>  
   <property name="arguments">  
    <list>  
     <ref bean="ehCacheManagerFactoryBean"/>  
    </list>  
   </property>  
  </bean>  
      


I suggest reading the original article as it is interesting and provides more information.