/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, version 2 as published by the Free Software * Foundation. * * You should have received a copy of the GNU General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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. * * * Copyright 2006 - 2013 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.engine.core.system.objfac; import org.pentaho.platform.api.engine.IPentahoObjectFactory; import org.pentaho.platform.api.engine.IPentahoObjectReference; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.engine.core.messages.Messages; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * AggregateObectFactory holds a collection of IPentahoObjectFactory implementations, delegating calls to each and * collecting the results. Results are ordered by "priority" attribute if present, with the highest priority object * returned in the calls to retrieve a single object. * <p/> * {@inheritDoc} * <p/> * User: nbaker Date: 1/15/13 */ public class AggregateObjectFactory implements IPentahoObjectFactory { protected final Set<IPentahoObjectFactory> factories = Collections.synchronizedSet( new HashSet<IPentahoObjectFactory>() ); protected IPentahoObjectFactory primaryFactory; private Logger logger = LoggerFactory.getLogger( AggregateObjectFactory.class ); private final ReadWriteLock factoryLock = new ReentrantReadWriteLock( false ); // we're not fair private Lock writeLock = factoryLock.writeLock(); private Lock readLock = factoryLock.readLock(); public AggregateObjectFactory() { } public void registerObjectFactory( IPentahoObjectFactory fact, boolean primary ) { writeLock.lock(); try { factories.add( fact ); } finally { writeLock.unlock(); } if ( primary ) { primaryFactory = fact; } logger.debug( "New IPentahoObjectFactory registered: " + fact.getName() ); } public void registerObjectFactory( IPentahoObjectFactory fact ) { registerObjectFactory( fact, false ); } /** * De-Register an ObjectFactory * * @param factory * @return true if the factory was registered and successfully removed. */ public boolean deregisterObjectFactory( IPentahoObjectFactory factory ) { writeLock.lock(); try { return factories.remove( factory ); } finally { writeLock.unlock(); } } public Set<IPentahoObjectFactory> getFactories() { return new HashSet( factories ); } public IPentahoObjectFactory getPrimaryFactory() { return primaryFactory; } @Override public <T> T get( Class<T> interfaceClass, String key, IPentahoSession session ) throws ObjectFactoryException { // if they want it by id, check for that first if ( key != null ) { readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( key ) ) { T object = fact.get( interfaceClass, key, session ); logger.debug( MessageFormat.format( "Found object for key: {0} in factory: {1}", key, fact.getName() ) ); return object; } } } finally { readLock.unlock(); } } T fromType = get( interfaceClass, session, null ); if ( fromType != null ) { return fromType; } String msg = Messages.getInstance().getString( "AbstractSpringPentahoObjectFactory.WARN_FAILED_TO_RETRIEVE_OBJECT", interfaceClass.getSimpleName() ); throw new ObjectFactoryException( msg ); } @Override public <T> T get( Class<T> interfaceClass, IPentahoSession session ) throws ObjectFactoryException { return get( interfaceClass, session, null ); } private int computePriority( IPentahoObjectReference ref ) { return ref.getRanking(); } @Override public boolean objectDefined( String key ) { readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( key ) ) { logger.debug( MessageFormat.format( "Object defined for key: {0} in factory: {1}", key, fact.getName() ) ); return true; } } } finally { readLock.unlock(); } return false; } /** * {@inheritDoc} * * @deprecated All usage of key methods are deprecated, use object attributes instead */ @Override public Class<?> getImplementingClass( String key ) { readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( key ) ) { logger.debug( MessageFormat.format( "Found implementing class for key: {0} in factory: {1}", key, fact .getName() ) ); return fact.getImplementingClass( key ); } } } finally { readLock.unlock(); } return null; } @Override public void init( String configFile, Object context ) { } @Override public <T> List<T> getAll( Class<T> interfaceClass, IPentahoSession curSession ) throws ObjectFactoryException { return getAll( interfaceClass, curSession, null ); } @Override public <T> List<T> getAll( Class<T> interfaceClass, IPentahoSession curSession, Map<String, String> properties ) throws ObjectFactoryException { List<IPentahoObjectReference<T>> referenceList = new ArrayList<IPentahoObjectReference<T>>(); readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( interfaceClass ) ) { List<IPentahoObjectReference<T>> refs = fact.getObjectReferences( interfaceClass, curSession, properties ); if ( refs != null ) { referenceList.addAll( refs ); } } } } finally { readLock.unlock(); } Collections.sort( referenceList, referencePriorityComparitor ); // create final list of impls List<T> entryList = new ArrayList<T>(); for ( IPentahoObjectReference<T> ref : referenceList ) { if ( !entryList.contains( ref.getObject() ) ) { entryList.add( ref.getObject() ); } } return entryList; } @Override public <T> IPentahoObjectReference<T> getObjectReference( Class<T> clazz, IPentahoSession curSession ) throws ObjectFactoryException { Set<IPentahoObjectReference<T>> references = new HashSet<IPentahoObjectReference<T>>(); readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( clazz ) ) { IPentahoObjectReference<T> found = fact.getObjectReference( clazz, curSession ); if ( found != null ) { references.add( found ); } } } } finally { readLock.unlock(); } IPentahoObjectReference<T> highestRef = null; int highestRefPriority = -1; for ( IPentahoObjectReference<T> ref : references ) { int pri = computePriority( ref ); if ( pri > highestRefPriority ) { highestRef = ref; highestRefPriority = pri; } } return highestRef; } @Override public <T> T get( Class<T> clazz, IPentahoSession session, Map<String, String> properties ) throws ObjectFactoryException { IPentahoObjectReference<T> highestRef = this.getObjectReference( clazz, session, properties ); if ( highestRef != null ) { return highestRef.getObject(); } readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( clazz.getSimpleName() ) ) { T object = fact.get( clazz, clazz.getSimpleName(), session ); return object; } } } finally { readLock.unlock(); } String msg = Messages.getInstance().getString( "AbstractSpringPentahoObjectFactory.WARN_FAILED_TO_RETRIEVE_OBJECT", clazz.getSimpleName() ); throw new ObjectFactoryException( msg ); } @Override public boolean objectDefined( Class<?> clazz ) { readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( clazz ) ) { logger.debug( MessageFormat.format( "Found object for class: {0} in factory: {1}", clazz.getName(), fact .getName() ) ); return true; } } } finally { readLock.unlock(); } return false; } @Override public <T> IPentahoObjectReference<T> getObjectReference( Class<T> interfaceClass, IPentahoSession curSession, Map<String, String> properties ) throws ObjectFactoryException { Set<IPentahoObjectReference<T>> references = new HashSet<IPentahoObjectReference<T>>(); readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( interfaceClass ) ) { List<IPentahoObjectReference<T>> found = fact.getObjectReferences( interfaceClass, curSession, properties ); if ( found != null ) { references.addAll( found ); } } } } finally { readLock.unlock(); } IPentahoObjectReference<T> highestRef = null; int highestRefPriority = -1; for ( IPentahoObjectReference<T> ref : references ) { int pri = computePriority( ref ); if ( pri > highestRefPriority ) { highestRef = ref; highestRefPriority = pri; } } return highestRef; } public void clear() { writeLock.lock(); try { this.factories.clear(); } finally { writeLock.unlock(); } } private static ReferencePriorityComparitor referencePriorityComparitor = new ReferencePriorityComparitor(); private static class ReferencePriorityComparitor implements Comparator<IPentahoObjectReference> { private static final String PRIORITY = "priority"; @Override public int compare( IPentahoObjectReference ref1, IPentahoObjectReference ref2 ) { int pri1 = extractPriority( ref1 ); int pri2 = extractPriority( ref2 ); if ( pri1 == pri2 ) { return 0; } else if ( pri1 < pri2 ) { return 1; } else { return -1; } } private int extractPriority( IPentahoObjectReference ref ) { if ( ref == null || ref.getAttributes() == null || !ref.getAttributes().containsKey( PRIORITY ) ) { // return default return DEFAULT_PRIORTIY; } try { return Integer.parseInt( ref.getAttributes().get( PRIORITY ).toString() ); } catch ( NumberFormatException e ) { // return default return DEFAULT_PRIORTIY; } } } @Override public <T> List<IPentahoObjectReference<T>> getObjectReferences( Class<T> interfaceClass, IPentahoSession curSession ) throws ObjectFactoryException { return getObjectReferences( interfaceClass, curSession, null ); } @Override public <T> List<IPentahoObjectReference<T>> getObjectReferences( Class<T> interfaceClass, IPentahoSession curSession, Map<String, String> properties ) throws ObjectFactoryException { // Use a set to avoid duplicates Set<IPentahoObjectReference<T>> referenceSet = new HashSet<IPentahoObjectReference<T>>(); readLock.lock(); try { for ( IPentahoObjectFactory fact : factories ) { if ( fact.objectDefined( interfaceClass ) ) { List<IPentahoObjectReference<T>> found = fact.getObjectReferences( interfaceClass, curSession, properties ); if ( found != null ) { referenceSet.addAll( found ); } } } } finally { readLock.unlock(); } // transform to a list to sort List<IPentahoObjectReference<T>> referenceList = new ArrayList<IPentahoObjectReference<T>>(); referenceList.addAll( referenceSet ); Collections.sort( referenceList, referencePriorityComparitor ); return referenceList; } @Override public String getName() { return getClass().getSimpleName(); } }