IT Berater – Java, J2EE, WebSphere Portal, Lotus Domino
RSS icon Home icon
  • Configuring EMF Teneo with Hibernate, Commons DBCP, Spring Hibernate Transaction Manager, and the OpenSessionInViewFilter

    (6 votes) 1 Star2 Stars3 Stars4 Stars5 Stars
    Loading...
    Posted on 2 October 2010 Sebastian Thomschke*/?> No comments

    While trying to get an application working with , Hibernate, Commons DBCP, Spring Hibernate Transaction Manager and the OpenSessionInViewFilter I encountered several unexpected issues. Here is an example of a working configuration for this software stack. Read the comments below the XML file for some explanations.

    HbDataStoreWithDataSource.java:

    package de.sebthom.util;
    
    import java.sql.*;
    import java.util.Properties;
    import javax.sql.DataSource;
    import org.eclipse.emf.teneo.PersistenceOptions;
    import org.eclipse.emf.teneo.hibernate.HbSessionDataStore;
    import org.hibernate.HibernateException;
    import org.hibernate.cfg.*;
    import org.hibernate.connection.ConnectionProvider;
    
    /**
     * @author Sebastian Thomschke
     */
    public class HbDataStoreWithDataSource extends HbSessionDataStore {
      public final static class CustomConnectionProvider implements ConnectionProvider {
        private DataSource ds;
    
        public void close() throws HibernateException { }
    
        public void closeConnection(final Connection conn) throws SQLException {
          conn.close();
        }
    
        public void configure(final Properties props) throws HibernateException {
          ds = _CONFIG_TIME_DS_HOLDER.get();
        }
    
        public Connection getConnection() throws SQLException {
          return ds.getConnection();
        }
    
       public boolean supportsAggressiveRelease() {
         return false;
       }
      }
    
      private static final long serialVersionUID = 1L;
      private static HbDataStoreWithDataSource INSTANCE;
    
      /**
       * the data source holder acts as a thread safe bridge between the DSConnectionProvider which is instantiated by Hibernate internally
       * and the data source injected into the session factory during spring configuration time. 
       */
      private static final ThreadLocal _CONFIG_TIME_DS_HOLDER = new ThreadLocal();
    
      public static HbDataStoreWithDataSource getInstance() {
    		return INSTANCE;
      }
    
      private final Properties props = new Properties();
    
      public HbDataStoreWithDataSource() {
        INSTANCE = this;
    
        props.setProperty(PersistenceOptions.ADD_INDEX_FOR_FOREIGN_KEY, "true");
        props.setProperty(PersistenceOptions.FETCH_CONTAINMENT_EAGERLY, "false");
        props.setProperty(PersistenceOptions.FETCH_ASSOCIATION_EXTRA_LAZY, "false");
    
        // tell Teneo to not change the table names, we do this using Hibernate's ImprovedNamingStrategy
        props.setProperty(PersistenceOptions.SQL_CASE_STRATEGY, "none");
        props.setProperty(PersistenceOptions.MAXIMUM_SQL_NAME_LENGTH, "-1");
      }
    
      protected Configuration createConfiguration() {
        Configuration cfg = super.createConfiguration();
        cfg.setNamingStrategy(ImprovedNamingStrategy.INSTANCE);
    
        // only if a DS is injected we register our connection provider
        if (_CONFIG_TIME_DS_HOLDER.get() != null)
          cfg.setProperty(Environment.CONNECTION_PROVIDER, CustomConnectionProvider.class.getName());
        return cfg;
      }
    
      public void setDataSource(final DataSource ds) {
        _CONFIG_TIME_DS_HOLDER.set(ds);
      }
    
      public final void setHibernateProperties(final Properties hibernateProperties) {
        throw new UnsupportedOperationException();
      }
    
      public final void setPersistenceProperties(final Properties persistenceOptions) {
        throw new UnsupportedOperationException();
      }
    
      public void setProperties(final Properties props) {
        this.props.putAll(props);
        super.setProperties(this.props);
      }
    }
    

    web.xml:

    
      openSessionInViewFilter
      org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
        
          singleSession
          true
        
        
          sessionFactoryBeanName
          realSessionFactory
        
    
    
      openSessionInViewFilter
      /*
      REQUEST
      ERROR
    
    

    applicationContext.xml:

    
    
      
    
      
        
          
            ${jdbc.driverClassName}
            ${jdbc.url}
            ${jdbc.username}
            ${jdbc.password}
            false
            ${jdbc.maxConnections}
            ${jdbc.minConnections}
            10000
            ${jdbc.minConnections}
            ${jdbc.validationQuery}
            false
            true
            1200000
            1800000
            5
          
        
      
    
      
        
          hibernate.dialect=${hibernate.dialect}
          hibernate.hbm2ddl.auto=${hibernate.hbm2ddl.auto}
          hibernate.jdbc.batch_size=2000
          hibernate.show_sql=${hibernate.show_sql}
          hibernate.transaction.auto_close_session=false
          hibernate.current_session_context_class=org.springframework.orm.hibernate3.SpringSessionContext
          hibernate.transaction.factory_class=org.springframework.orm.hibernate3.SpringTransactionFactory
        
        com.acme.ShopPackageImpl 
      
    
      
    
      
    
      
    
    
    

    Comments:

    1. VERY IMPORTANT:The HibernateTransactionManager does NOT get the teneoSessionFactory(HbSessionDataStore) object passed in as sessionFactory, but the underlying Hibernate SessionFactory that can be retrieved from the HbSessionDataStore via the getSessionFactory() method. Not doing so will result in duplicate Hibernate Sessions completely breaking the transaction managment. This happens because in that case the TransactionSynchronizationManager will sometimes internally bind the hibernate session to the current thread based on different keys and thus does not find it again. Here are some stacktraces showing the problem:
      TransactionSynchronizationManager.bindResource(Object, Object) line: 170	
      at OpenSessionInViewFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 183
      => results in TransactionSynchronizationManager.bindResource(HbSessionDataStore, SessionHolder) => as key the Teneo SessionFactory is used
      
      TransactionSynchronizationManager.bindResource(Object, Object) line: 170	
      at SessionFactoryUtils.doGetSession(SessionFactory, Interceptor, SQLExceptionTranslator, boolean) line: 339	
      at SessionFactoryUtils.doGetSession(SessionFactory, boolean) line: 256	
      at SpringSessionContext.currentSession() line: 60	
      at SessionFactoryImpl.getCurrentSession() line: 700	
      at HbSessionDataStore(HbBaseSessionDataStore).getCurrentSession() line: 161	
      => results in TransactionSynchronizationManager.bindResource(SessionFactoryImpl, SessionHolder) => as key the Hibernate SessionFactory is used
      
      TransactionSynchronizationManager.bindResource(Object, Object) line: 170	
      at HibernateTransactionManager.doBegin(Object, TransactionDefinition) line: 577	
      at HibernateTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 371
      at TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String) line: 316	
      at TransactionInterceptor.invoke(MethodInvocation) line: 105	
      at ReflectiveMethodInvocation.proceed() line: 172
      => results in TransactionSynchronizationManager.bindResource(HbSessionDataStore, SessionHolder) => as key the Teneo SessionFactory is used
      
    2. defaultAutoCommit is set to false to ensure that Spring has full control over the transaction and that the connection pool is not committing statements automatically based on it’s own strategies.
    3. The example uses an extended version of the org.eclipse.emf.teneo.hibernate.HbSessionDataStore class that allows the usage of a DataSource object and that configures Teneo to not alter the table, column names but let’s Hibernate handle this via its ImprovedNamingStrategy.
    4. hibernate.transaction.auto_close_session is set to false to avoid “SessionException: Session is closed!” in conjunction with the OpenSessionInViewListener.
    5. hibernate.current_session_context_class and hibernate.transaction.factory_class are set to special Spring implementations to avoid “org.hibernate.HibernateException: No CurrentSessionContext configured!” when not using Spring Hibernate Template via HibernateDAOSupport. It is not recommended to use the Hibernate Template anymore, see http://blog.springsource.com/2007/06/26/so-should-you-still-use-springs-hibernatetemplate-andor-jpatemplate/
  • Using EMF ECore model objects with Wicket components

    (2 votes) 1 Star2 Stars3 Stars4 Stars5 Stars
    Loading...
    Posted on 8 September 2010 Sebastian Thomschke*/?> 1 comment

    Apache Wicket uses so called model objects to bind data objects to Wicket components. The framework provides a number of model implementations to access data objects and their properties in various ways. The PropertyModel implementation for example is used to access and set the value of a Java object using reflection.

    EPropertyModel

    If you are working with EMF (Eclipse Modeling Framework) generated model classes you can also use PropertyModels to bind structural features (properties in EMF terminology) of an EObject to a Wicket component. E.g. if your EObject has an attribute called “comment” you could instantiate a model like this

    IModel model = new PropertyModel(myEObject, "comment");
    

    This approach has several disadvantages. First, the name of the attribute is declared as a String and not as a compile-time reference to the actual attribute. This means the application could break during runtime if you have renamed the attribute at one point and forgot to also change the String value. Second, this property model uses runtime reflection which is a slow alternative to compile time based access.
    Java classes generated from EMF Models by default extend the EObject class which provides a compile-time reflection API. Using the generic eGet and eSet methods any public property of the EObject can be accessed. The EMF Generator also generates a Package class for each EMF package that holds a Literals interface listing all available attributes and references of all generated EClasses of the given package. If you lets say have an EClass “Ticket” with the attribute “comment” in the package “com.acme.service” you could utilize the compile time reflection API as follows:

    EAttribute commentAttribute = com.acme.service.ServicePackage.Literals.TICKET__COMMENT;
    Ticket newTicket = com.acme.service.ServiceFactory.eINSTANCE.create(Ticket.class);
    newTicket.eSet(commentAttribute, "my comment");
    

    Using the EMF compile-time reflection API I created a generic EMF ECore Feature Model implementation that leverages the compile-time references to structural features. Most of the code is actually related to working around the fact that EAttributes and EReferences are not serializable but which they should to be part safely referenced in a model that potentially is serialized by Wicket between requests.

    import org.apache.wicket.model.IDetachable;
    import org.apache.wicket.model.IObjectClassAwareModel;
    import org.eclipse.emf.ecore.EClass;
    import org.eclipse.emf.ecore.EClassifier;
    import org.eclipse.emf.ecore.EObject;
    import org.eclipse.emf.ecore.EPackage;
    import org.eclipse.emf.ecore.EStructuralFeature;
    
    /**
     * A property model to bind an attribute of an EMF ECore object
     * 
     * @author Sebastian Thomschke
     */
    @SuppressWarnings("unchecked")
    public class EFeatureModel<T> implements IObjectClassAwareModel<T>
    {
    	private static final long serialVersionUID = 1L;
    
    	private transient EStructuralFeature feat;
    	private String featContainerClassName;
    	private String featName;
    	private String featPackageNsURI;
    
    	private EObject target;
    
    	public EFeatureModel(EObject target, EStructuralFeature efeature)
    	{
    		this.target = target;
    		this.feat = efeature;
    		this.featName = efeature.getName();
    		this.featContainerClassName = efeature.getContainerClass().getName();
    		this.featPackageNsURI = ((EPackage) efeature.eContainer().eContainer()).getNsURI();
    	}
    
    	public void detach()
    	{
    		if (target instanceof IDetachable) ((IDetachable) target).detach();
    	}
    
    	public EStructuralFeature getFeature()
    	{
    		if (feat == null)
    		{
    			EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(featPackageNsURI);
    			for (Object eClassifierItem : ePackage.getEClassifiers())
    			{
    				EClassifier eClassifier = (EClassifier) eClassifierItem;
    				if (eClassifier.getInstanceClass().getName().equals(featContainerClassName))
    					feat = ((EClass) eClassifier).getEStructuralFeature(featName);
    			}
    		}
    		return feat;
    	}
    
    	public T getObject()
    	{
    		return (T) target.eGet(getFeature());
    	}
    
    	public Class<T> getObjectClass()
    	{
    		return getFeature().getEType().getInstanceClass();
    	}
    
    	public void setObject(T object)
    	{
    		// do nothing if the object is the same instance that is returned by the getter
    		// this is esp. important for collection properties
    		if (target.eGet(getFeature()) == object) return;
    
    		target.eSet(getFeature(), object);
    	}
    
    	@Override
    	public String toString()
    	{
    		return new StringBuilder("Model:classname=[").append(getClass().getName()).append("]").append(":target=[")
    				.append(target).append("]").toString();
    	}
    }

    Using this new model implementation is as simple as using the PropertyModel provided by Wicket:

    IModel model = new EFeatureModel(myTicket, com.acme.service.ServicePackage.Literals.TICKET__COMMENT);
    

    You can now fully enjoy the compile-time safeness of your EMF model.

    Making EObjects serializable

    Wicket usually serializes components and their associated models between HTTP requests. If you are using the provided EPropertyModel your EObject classes must implement the Serializable interface. To achieve this, simply extend the EObject interface with the Serializables interface and set “Root Extends Interface” property in your genmodel configuration to this interface.

    /**
     * @author Sebastian Thomschke
     */
    public interface SerializableEObject extends EObject, Serializable {
    }
    

    When you now regenerate the model classes they will extend the SerializableEObject interface and Wicket will stop complaining about objects in the model being not serializable.