/* Copyright 2009 Alex Shneyderman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * * See the License for the specific language governing permissions and * limitations under the License. */ package org.qi4j.entitystore.qrm.internal; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; import org.qi4j.api.common.QualifiedName; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.service.ServiceComposite; import org.qi4j.entitystore.qrm.QrmEntityStoreDescriptor; import org.qi4j.entitystore.qrm.QrmMapper; import org.qi4j.spi.entity.EntityDescriptor; import org.qi4j.spi.entity.EntityState; import org.qi4j.spi.entity.EntityStatus; import org.qi4j.spi.entity.EntityType; import org.qi4j.spi.entitystore.DefaultEntityStoreUnitOfWork; import org.qi4j.spi.entitystore.EntityNotFoundException; import org.qi4j.spi.entitystore.helpers.DefaultEntityState; import org.qi4j.spi.property.PropertyDescriptor; import org.qi4j.spi.property.PropertyType; import org.qi4j.spi.structure.ApplicationSPI; import org.qi4j.spi.structure.DescriptorVisitor; @Mixins( { QrmMapperService.QrmMapperServiceMixin.class } ) public interface QrmMapperService extends QrmMapper, ServiceComposite { class QrmMapperServiceMixin implements QrmMapper { private final static Log LOG = LogFactory.getLog( QrmMapperService.class ); private SessionFactory sessoinFactory; private @Structure ApplicationSPI app; private Map<Class, QrmMapping> mappings = new HashMap<Class, QrmMapping>(); private Map<Class, Long> classId = new HashMap<Class, Long>(); private QrmEntityStoreDescriptor qrmCfg; public void bootstrap( final QrmEntityStoreDescriptor cfg ) { gatherMetaInfo( cfg ); qrmCfg = cfg; configureSessionFactory( cfg ); } public Class findMappedMixin( EntityDescriptor eDesc ) { for( String mixinClassName : eDesc.entityType().mixinTypes() ) { Class clazz = null; try { clazz = getClass().getClassLoader().loadClass( mixinClassName ); } catch( ClassNotFoundException e ) { continue; } if( mappings.get( clazz ) != null ) { return clazz; } } return null; } public String fetchNextId( Class clazz ) { if( mappings.get( clazz ) != null ) { classId.put( clazz, ( classId.get( clazz ) + 1L ) ); return "" + classId.get( clazz ); } return null; } public EntityDescriptor fetchDescriptor( Class mappedClazz ) { QrmMapping qrmMapping = mappings.get( mappedClazz ); if( qrmMapping == null ) { return null; } return qrmMapping.getEntityDescriptor(); } public EntityState get( DefaultEntityStoreUnitOfWork unitOfWork, Class mappedClazz, EntityReference identity ) { EntityDescriptor desc = fetchDescriptor( mappedClazz ); Session session = sessoinFactory.getCurrentSession(); session.beginTransaction(); Map dbState = (Map) session.get( mappedClazz.getName(), identity.identity() ); session.close(); if( dbState == null ) { throw new EntityNotFoundException( identity ); } Map<QualifiedName, Object> propState = new HashMap<QualifiedName, Object>(); for( PropertyDescriptor pd : desc.state().properties() ) { QualifiedName qualifiedName = pd.qualifiedName(); propState.put( qualifiedName, dbState.get( qualifiedName.name() ) ); } String version = "" + dbState.get( "version" ); long lastModified = dbState.get( "updatedOn" ) == null ? 0L : ( (Date) dbState.get( "updatedOn" ) ).getTime(); return new DefaultEntityState( unitOfWork, version, lastModified, identity, EntityStatus.LOADED, desc, propState, null, null, null ); } public boolean newEntity( Class mappedClazz, DefaultEntityState state, String version, long lastModified ) { EntityDescriptor desc = state.entityDescriptor(); EntityReference identity = state.identity(); QrmMapping mapping = mappings.get( mappedClazz ); Map dbState = new HashMap(); for( Map.Entry<QualifiedName, Object> entry : state.properties().entrySet() ) { dbState.put( entry.getKey().name(), entry.getValue() ); } dbState.put( mapping.identity().name(), state.identity().identity() ); dbState.put( mapping.createdOn().name(), new Date(lastModified) ); dbState.put( mapping.updatedOn().name(), new Date(lastModified) ); Session session = sessoinFactory.getCurrentSession(); session.beginTransaction(); session.save( mappedClazz.getName(), dbState ); session.getTransaction().commit(); return true; } public boolean delEntity( Class mappedClazz, DefaultEntityState state, String version ) { EntityDescriptor desc = state.entityDescriptor(); EntityReference identity = state.identity(); QrmMapping mapping = mappings.get( mappedClazz ); String entityName = mappedClazz.getName(); Session session = sessoinFactory.getCurrentSession(); session.beginTransaction(); Map dbState = (Map) session.get( entityName, identity.identity() ); if( dbState != null ) { session.delete( entityName, dbState ); } session.getTransaction().commit(); if( dbState == null ) { throw new EntityNotFoundException( identity ); } return true; } public boolean updEntity( Class mappedClazz, DefaultEntityState state, String version, long lastModified ) { EntityReference identity = state.identity(); QrmMapping mapping = mappings.get( mappedClazz ); String entityName = mappedClazz.getName(); Session session = sessoinFactory.getCurrentSession(); session.beginTransaction(); Map dbState = (Map) session.get( entityName, identity.identity() ); if( dbState == null ) { throw new EntityNotFoundException( identity ); } for( Map.Entry<QualifiedName, Object> entry : state.properties().entrySet() ) { dbState.put( entry.getKey().name(), entry.getValue() ); } dbState.put( mapping.identity().name(), state.identity().identity() ); dbState.put( mapping.updatedOn().name(), new Date(lastModified) ); session.save( entityName, dbState ); session.getTransaction().commit(); return true; } // HELPERS. private void gatherMetaInfo( QrmEntityStoreDescriptor cfg ) { final List<Class> types = cfg.types(); app.visitDescriptor( new DescriptorVisitor<RuntimeException>() { public void visit( EntityDescriptor entityDescriptor ) { for( Class mixinClazz : entityDescriptor.mixinTypes() ) { if( types.contains( mixinClazz ) ) { mappings.put( mixinClazz, createMapping( entityDescriptor, mixinClazz ) ); classId.put( mixinClazz, new Long( 0 ) ); } } } } ); } private QrmMapping createMapping( EntityDescriptor entityDescriptor, Class mixinClazz ) { EntityType entityType = entityDescriptor.entityType(); QrmMapping result = new QrmMapping( entityDescriptor ); for( PropertyType pType : entityType.properties() ) { if( pType.propertyType() == PropertyType.PropertyTypeEnum.COMPUTED ) { continue; } String name = pType.qualifiedName().name(); String hibType = pType.type().type().name(); if( "identity".equals( name ) ) { result.addIdentity( name, MapperUtils.idColumnName( mixinClazz, qrmCfg, mappings ), "string" ); } else { result.addProperty( name, hibType, true ); } } return result; } private void configureSessionFactory( QrmEntityStoreDescriptor cfg ) { Configuration hibCfg = new QrmHibernateConfiguration( createHibernateDescriptor( cfg.props() ) ).configure(); addMappings( hibCfg ); sessoinFactory = hibCfg.buildSessionFactory(); } private void addMappings( Configuration hibCfg ) { for( Map.Entry<Class, QrmMapping> entry : mappings.entrySet() ) { hibCfg.addXML( createHibMappingXml( entry.getKey(), entry.getValue() ) ); } } private String createHibMappingXml( Class clazz, QrmMapping mapping ) { StringBuilder sb = new StringBuilder( "" ); QrmProperty identity = mapping.identity(); sb.append( "<?xml version=\"1.0\"?>\n" ) .append( "<!DOCTYPE hibernate-mapping PUBLIC\n" ) .append( " \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\"\n" ) .append( " \"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd\">\n" ) .append( "<hibernate-mapping>" ) .append( " <class \n" ) .append( " entity-name='" + MapperUtils.entityName( clazz, qrmCfg, mappings ) + "'\n" ) .append( " table='" + MapperUtils.tableName( clazz, qrmCfg, mappings ) + "'>\n" ) .append( " <id name='" + identity.name() + "' \n" ) .append( " column='" + identity.column() + "' \n" ) .append( " type='" + identity.hibernateType() + "'>\n" ) .append( " <generator class='assigned' />\n" ) .append( " </id>\n" ) .append( " <version name='version' type='long' column='version' />\n" ) .append( " <property name='updatedOn' column='updated_on' type='timestamp' not-null='false' />\n" ) .append( " <property name='createdOn' column='created_on' type='timestamp' not-null='false' />\n" ); for( QrmProperty prop : mapping.properties() ) { if( prop.isIdentity() || prop.name().equals( "updatedOn" ) || prop.name().equals( "createdOn" ) ) { continue; } sb.append( " <property name='" + prop.name() + "' \n" + " column='" + prop.column() + "' \n" + " type='" + prop.hibernateType() + "' \n" + " not-null='" + prop.isNullable() + "' />\n" ); } sb.append( " </class>\n" ) .append( "</hibernate-mapping>\n" ); String result = sb.toString(); if( LOG.isDebugEnabled() ) { LOG.debug( "Hibenrate mapping for " + clazz.getName() ); LOG.debug( result ); } System.err.println( "Hibenrate mapping for " + clazz.getName() ); System.err.println( result ); return result; } private String createHibernateDescriptor( Properties props ) { StringBuilder sb = new StringBuilder( "" ); sb.append( "<?xml version='1.0' encoding='utf-8'?>\n" + "<!DOCTYPE hibernate-configuration PUBLIC\n" + " \"-//Hibernate/Hibernate Configuration DTD 3.0//EN\"\n" + " \"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd\">\n" + "<hibernate-configuration>\n" + " <session-factory>\n" ); for( Map.Entry<Object, Object> entry : props.entrySet() ) { sb.append( " <property name='" ).append( entry.getKey() ).append( "'>" ) .append( entry.getValue() ) .append( "</property>\n" ); } sb.append( " </session-factory>\n" ) .append( "</hibernate-configuration>" ); String result = sb.toString(); if( LOG.isDebugEnabled() ) { LOG.debug( "Hibenrate descriptor:" ); LOG.debug( result ); } System.err.println( "Hibenrate descriptor:" ); System.err.println( result ); return result; } } }