/* Name: - PersistenceContext Description: - Requires: - Provides: - Part of: ProcessPuzzle Framework, Domain and Business Model Ready Architecture. Provides content, workflow and social networking functionality. http://www.processpuzzle.com ProcessPuzzle - Content and Workflow Management Integration Business Platform Author(s): - Zsolt Zsuffa Copyright: (C) 2011 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.processpuzzle.application.configuration.domain; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.configuration.HierarchicalConfiguration; import com.processpuzzle.application.domain.Application; import com.processpuzzle.commons.persistence.Entity; import com.processpuzzle.commons.persistence.PersistenceStrategy; import com.processpuzzle.commons.persistence.PersistentObject; import com.processpuzzle.commons.persistence.Repository; import com.processpuzzle.commons.persistence.RepositoryEventHandler; import com.processpuzzle.fundamental_types.domain.OpAssertion; import com.processpuzzle.persistence.domain.DefaultPersistenceStrategy; import com.processpuzzle.persistence.domain.DefaultRepositoryEventHandler; import com.processpuzzle.persistence.domain.GenericRepository; import com.processpuzzle.persistence.domain.PersistentClassList; import com.processpuzzle.persistence.domain.RepositoryClassList; import com.processpuzzle.persistence.domain.UndefinedPersistentDataInitializationStrategyException; public class PersistenceContext extends PersistentApplicationContext implements ApplicationContext { public static final String ENTITY_CLASS_PROPERTY_NAME = "entityClass"; public static final String ENTITY_CLASS_PROPERTY_NODE_SELECTOR = "persistence/entities"; public static final String HIBERNATE_STRATEGY = "hibernate"; public static final String HIERARCHICAL_PROPERTY_SELECTOR_DELIMITER = "."; public static final String NAME_SPACE_PREFIX = "pr:"; private ProcessPuzzleContext applicationContext; private Map<Class<? extends Repository<?>>, Repository<?>> availableRepositories = new HashMap<Class<? extends Repository<?>>, Repository<?>>(); private Map<Class<? extends Repository<?>>, GenericRepository<?>> domainClassRepositoryMappings = new HashMap<Class<? extends Repository<?>>, GenericRepository<?>>(); private List<Class<? extends PersistentObject>> persistentClasses = new ArrayList<Class<? extends PersistentObject>>(); private PersistentDataInitializationStrategies persistentDataInitializationStrategy = null; private PropertyContext propertyContext = null; private Map<String, PersistenceStrategy> supportedStrategies = new HashMap<String, PersistenceStrategy>(); // Constructors PersistenceContext( Application application ) { super( application ); this.applicationContext = application.getContext(); this.propertyContext = applicationContext.getPropertyContext(); } // Public mutators and accessors // Properties public Map<Class<? extends Repository<?>>, Repository<?>> getAvailableRepositories() { return availableRepositories; } public int getPersistentClassesSize() { return persistentClasses.size(); } public Iterator<Class<? extends PersistentObject>> getPersistentClassesIterator() { return persistentClasses.iterator(); } public PersistentDataInitializationStrategies getPersistentDataInitializationStrategy() { return persistentDataInitializationStrategy; } @SuppressWarnings( "unchecked" ) public <R extends Repository<?>> R getRepositoryInstance( Class<R> repositoryClass ) { R repository = (R) availableRepositories.get( repositoryClass ); if( repository != null ) return repository; else throw new UndeclaredRepositoryException( repositoryClass ); } public Repository<?> getRepositoryByAggregateRootClass( Class<? extends Entity> entityClass ) { Repository<?> repository = domainClassRepositoryMappings.get( entityClass ); if( repository != null ) return repository; else{ repository = findRepositoryBySuperClass( entityClass ); if( repository == null ){ throw new UndeclaredAggregateRootRepositoryMapping( entityClass ); }else{ return repository; } } } public PersistenceStrategy getStrategy( String strategyName ) { PersistenceStrategy strategy = supportedStrategies.get( strategyName ); if( strategy == null ) throw new Error( "PersistenceStrategy `" + strategyName + "` not found!" ); return strategy; } public Map<String, PersistenceStrategy> getSupportedStrategies() { return supportedStrategies; } public boolean isConfigured() { return isConfigured; } // Protected, private helper methods @Override protected void setUpTransientComponents() {} @Override protected void setUpPersistentComponents() { determinePersistentClasses(); determinePersistentDataInitializationStrategy(); instantiateSupportedStrategies(); Map<Class<? extends Repository<?>>, PersistenceStrategy> repositories = new HashMap<Class<? extends Repository<?>>, PersistenceStrategy>(); repositories = determineRepositories(); instantiateRepositories( repositories ); configureStrategies(); buildDomainClassRepositoryMapping(); } @Override protected void tearDownTransientComponents() { } @SuppressWarnings( "unchecked" ) @Override protected void tearDownPersistentComponents() { domainClassRepositoryMappings.clear(); availableRepositories.clear(); for( Iterator iter = supportedStrategies.entrySet().iterator(); iter.hasNext(); ){ Map.Entry<String, DefaultPersistenceStrategy> mapEntry = (Map.Entry<String, DefaultPersistenceStrategy>) iter.next(); DefaultPersistenceStrategy strategy = mapEntry.getValue(); strategy.release(); } supportedStrategies.clear(); } @SuppressWarnings( "unchecked" ) private void buildDomainClassRepositoryMapping() { for( Iterator iter = availableRepositories.entrySet().iterator(); iter.hasNext(); ){ Map.Entry<Class, GenericRepository> availableRepositoryEntry = (Map.Entry<Class, GenericRepository>) iter.next(); GenericRepository repository = availableRepositoryEntry.getValue(); domainClassRepositoryMappings.put( repository.getSupportedAggregateRootClass(), repository ); } } private void configureStrategies() { for( Map.Entry<String, PersistenceStrategy> strategyEntry : supportedStrategies.entrySet() ){ Map.Entry<String, PersistenceStrategy> strategiesEntry = strategyEntry; PersistenceStrategy strategy = strategiesEntry.getValue(); strategy.configure(); } } private void determinePersistentClasses() { determinePersistentClassesFromConfigurationFile(); determinePersistentClassesFromHardCodedClass(); } private void determinePersistentDataInitializationStrategy() { String strategyProperty = propertyContext.getProperty( PropertyKeys.APPLICATION_DATABASE_CREATION.getDefaultKey() ); if( strategyProperty.equals( PersistentDataInitializationStrategies.create.asString() ) ){ persistentDataInitializationStrategy = PersistentDataInitializationStrategies.create; }else if( strategyProperty.equals( PersistentDataInitializationStrategies.dropAndCreate.asString() ) ){ persistentDataInitializationStrategy = PersistentDataInitializationStrategies.dropAndCreate; }else if( strategyProperty.equals( PersistentDataInitializationStrategies.update.asString() ) ){ persistentDataInitializationStrategy = PersistentDataInitializationStrategies.update; }else throw new UndefinedPersistentDataInitializationStrategyException( strategyProperty ); } @SuppressWarnings("unchecked") private void determinePersistentClassesFromConfigurationFile() { List<String> persistentClassNames = propertyContext.getPropertyList( PropertyKeys.PERSISTENCE_PERSISTENT_CLASSES.getDefaultKey() ); for( Iterator<String> iter = persistentClassNames.iterator(); iter.hasNext(); ){ String className = (String) iter.next(); Class<? extends PersistentObject> persistentClass = null; try{ persistentClass = (Class<? extends PersistentObject>) Class.forName( className ); }catch( ClassNotFoundException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); } persistentClasses.add( persistentClass ); } } @SuppressWarnings("unchecked") private void determinePersistentClassesFromHardCodedClass() { String classListClassName = propertyContext.getProperty( PropertyKeys.PERSISTENCE_PERSISTENT_CLASSLIST_CLASS.getDefaultKey() ); if( classListClassName != null ){ PersistentClassList persistentClassList = null; try{ Class<? extends PersistentObject> classListClass = (Class<? extends PersistentObject>) Class.forName( classListClassName ); persistentClassList = (PersistentClassList) classListClass.newInstance(); }catch( Exception e ){ throw new PersistenceContextSetUpException( propertyContext, e ); } persistentClasses.addAll( persistentClassList.getAggregateRoots() ); persistentClasses.addAll( persistentClassList.getEntities() ); persistentClasses.addAll( persistentClassList.getValueObjects() ); } } private Map<Class<? extends Repository<?>>, PersistenceStrategy> determineRepositories() { Map<Class<? extends Repository<?>>, PersistenceStrategy> repositoryClasses = new HashMap<Class<? extends Repository<?>>, PersistenceStrategy>(); repositoryClasses.putAll( determineRepositoriesFromConfigurationFile() ); repositoryClasses.putAll( determineRepositoryFromHardCodedClass() ); return repositoryClasses; } @SuppressWarnings("unchecked") private Map<Class<? extends Repository<?>>, PersistenceStrategy> determineRepositoryFromHardCodedClass() { Map<Class<? extends Repository<?>>, PersistenceStrategy> repositoryClasses = new HashMap<Class<? extends Repository<?>>, PersistenceStrategy>(); String repositoryClassListClassName = propertyContext.getProperty( PropertyKeys.PERSISTENCE_REPOSITORY_LIST_CLASS.getDefaultKey() ); if( repositoryClassListClassName != null ){ RepositoryClassList repositoryClassList = null; PersistenceStrategy strategy = null; try{ Class<RepositoryClassList> classListClass = (Class<RepositoryClassList>) Class.forName( repositoryClassListClassName ); repositoryClassList = (RepositoryClassList) classListClass.newInstance(); Object[] parameters = { classListClass.getName() }; String strategyName = propertyContext.getProperty( PropertyKeys.createKey( PropertyKeys.PERSISTENCE_REPOSITORY_LIST_STRATEGY.getDefaultKey(), parameters ) ); strategy = supportedStrategies.get( strategyName ); }catch( Exception e ){ throw new PersistenceContextSetUpException( propertyContext, e ); } for( Class<? extends Repository<?>> repositoryClass : repositoryClassList.gerRepositories() ) { repositoryClasses.put( repositoryClass, strategy ); } } return repositoryClasses; } @SuppressWarnings("unchecked") private Map<? extends Class<Repository<?>>, PersistenceStrategy> determineRepositoriesFromConfigurationFile() { Map<Class<Repository<?>>, PersistenceStrategy> repositoryClasses = new HashMap<Class<Repository<?>>, PersistenceStrategy>(); List<String> repositoryClassNames = propertyContext.getPropertyList( PropertyKeys.PERSISTENCE_REPOSITORY_CLASS.getDefaultKey() ); for( String repositoryClassName : repositoryClassNames ){ Class<Repository<?>> repositoryClass = null; try{ repositoryClass = (Class<Repository<?>>) Class.forName( repositoryClassName ); Object[] parameters = { repositoryClass.getName() }; String strategyName = propertyContext.getPropertyByParameter( PropertyKeys.PERSISTENCE_REPOSITORY_CLASS_STRATEGY.getDefaultKey(), parameters ); PersistenceStrategy strategy = supportedStrategies.get( strategyName ); repositoryClasses.put( repositoryClass, strategy ); }catch( ClassNotFoundException e ){ throw new RepositoryInstantiationException( repositoryClassName, propertyContext, e ); }catch( Exception e ){ throw new RepositoryInstantiationException( repositoryClassName, propertyContext, e ); } } return repositoryClasses; } private Repository<?> findRepositoryBySuperClass( Class<? extends Entity> entityClass ) { GenericRepository<?> repository = null; boolean found = false; Class<?> superClass = entityClass.getSuperclass(); while( !found && superClass != null ){ repository = domainClassRepositoryMappings.get( superClass ); if( repository != null ){ found = true; }else{ superClass = superClass.getSuperclass(); } } return repository; } private void instantiateSupportedStrategies() { List<String> supportedStrategyNames = propertyContext.getPropertyList( PropertyKeys.PERSISTENCE_STRATEGY_NAME.getDefaultKey() ); Iterator<String> supportedStrategyIterator = supportedStrategyNames.iterator(); Integer strategyIndex = 0; while( supportedStrategyIterator.hasNext() ){ String supportedStrategyName = supportedStrategyIterator.next(); Object[] parameters = { supportedStrategyName }; List<String> eventHandlerNames = propertyContext.getPropertyListByParameter( PropertyKeys.PERSISTENCE_STRATEGY_EVENT_HANDLERS.getDefaultKey(), parameters ); List<RepositoryEventHandler> eventHandlers = new LinkedList<RepositoryEventHandler>(); Iterator<String> eventHandlersIterator = eventHandlerNames.iterator(); Integer eventHandlerIndex = 0; while( eventHandlersIterator.hasNext() ){ String eventHandlerName = eventHandlersIterator.next(); String configurationAt = MessageFormat.format( PropertyKeys.REPOSITORY_EVENT_HANDLER.getDefaultKey(), new Object[] { strategyIndex.toString(), eventHandlerIndex.toString() } ); HierarchicalConfiguration eventHandlerConfiguration = propertyContext.getConfigurationAt( configurationAt ); // ParametrizedConfigurationPropertyHandler propertyHandler = new ParametrizedConfigurationPropertyHandler( eventHandlerConfiguration ); String eventHandlerClassName = eventHandlerConfiguration.getString( PropertyKeys.EVENT_HANDLER_CLASS.getDefaultKey() ); DefaultRepositoryEventHandler eventHandler = instantiateEventHandler( eventHandlerName, eventHandlerClassName, eventHandlerConfiguration ); eventHandlers.add( eventHandler ); eventHandlerIndex++; } PersistenceStrategy strategy = new DefaultPersistenceStrategy( supportedStrategyName, eventHandlers ); supportedStrategies.put( supportedStrategyName, strategy ); strategyIndex++; } } @SuppressWarnings( "unchecked" ) private DefaultRepositoryEventHandler instantiateEventHandler( String eventHandlerName, String eventHandlerClassName, HierarchicalConfiguration eventHandlerConfiguration ) { OpAssertion.ppAssert( eventHandlerName != null, "PersistenceContext.instantitateEventHandler: RepositoryEventHandler name can't be null." ); OpAssertion.ppAssert( eventHandlerClassName != null, "PersistenceContext.instantitateEventHandler: RepositoryEventHandler class name can't be null." ); DefaultRepositoryEventHandler eventHandler = null; if( eventHandlerClassName != null ){ try{ Class eventHandlerClass = Class.forName( eventHandlerClassName ); Class[] parameterTypes = new Class[] { String.class, HierarchicalConfiguration.class, List.class, PersistentDataInitializationStrategies.class }; Constructor eventHandlerConstructor = eventHandlerClass.getConstructor( parameterTypes ); Object[] constructorArguments = { eventHandlerName, eventHandlerConfiguration, persistentClasses, persistentDataInitializationStrategy }; eventHandler = (DefaultRepositoryEventHandler) eventHandlerConstructor.newInstance( constructorArguments ); }catch( ClassNotFoundException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( SecurityException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( NoSuchMethodException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( IllegalArgumentException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( InstantiationException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( IllegalAccessException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( InvocationTargetException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); } } return eventHandler; } private void instantiateRepositories( Map<Class<? extends Repository<?>>, PersistenceStrategy> repositoryClasses ) { for( Map.Entry<Class<? extends Repository<?>>, PersistenceStrategy> repositoryEntry : repositoryClasses.entrySet() ) { Class<? extends Repository<?>> repositoryClass = repositoryEntry.getKey(); PersistenceStrategy strategy = repositoryEntry.getValue(); GenericRepository<?> repository = instantiateRepository( repositoryClass, strategy ); availableRepositories.put( repositoryClass, repository ); logger.debug( "Repository: '" + repositoryClass.getSimpleName() + "' instantiated." ); } } @SuppressWarnings( "unchecked" ) private GenericRepository<?> instantiateRepository( Class repositoryClass, PersistenceStrategy strategy ) { GenericRepository repository = null; if( repositoryClass != null ){ Class[] parameterTypes = new Class[] { PersistenceStrategy.class, ProcessPuzzleContext.class }; Constructor repositoryConstructor; try{ repositoryConstructor = repositoryClass.getConstructor( parameterTypes ); Object[] constructorArguments = { strategy, applicationContext }; repository = (GenericRepository) repositoryConstructor.newInstance( constructorArguments ); }catch( SecurityException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( NoSuchMethodException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( IllegalArgumentException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( InstantiationException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( IllegalAccessException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); }catch( InvocationTargetException e ){ throw new PersistenceContextSetUpException( propertyContext, e ); } } return repository; } }