/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.portletbridge.bridge.factory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.portlet.faces.BridgeException;
import org.jboss.portletbridge.bridge.logger.BridgeLogger;
public final class BridgeFactoryFinder {
private static final Logger logger = Logger.getLogger(BridgeFactoryFinder.class.getName(),
BridgeLogger.LOGGING_BUNDLE);
private static Map<Class<?>, List<String>> factoryDefinitions = new HashMap<Class<?>, List<String>>(6);
private static Map<Class<?>, BridgeFactory<?>> factoryInstances = new HashMap<Class<?>, BridgeFactory<?>>(6);
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private BridgeFactoryFinder() {
//Prevent instantiation
}
public static void addFactoryDefinition(Class<? extends BridgeFactory<?>> type, String factoryImplClassName) {
lock.writeLock().lock();
try {
List<String> defs = factoryDefinitions.get(type);
if (defs == null) {
defs = (List<String>) new ArrayList<String>(4);
factoryDefinitions.put(type, defs);
}
if (!defs.contains(factoryImplClassName)) {
defs.add(factoryImplClassName);
}
} finally {
lock.writeLock().unlock();
}
}
public static List<String> getFactoryDefinition(Class<? extends BridgeFactory<?>> type) {
lock.readLock().lock();
try {
return factoryDefinitions.get(type);
} finally {
lock.readLock().unlock();
}
}
public static void addFactoryInstance(Class<? extends BridgeFactory<?>> type, BridgeFactory<?> factoryInstance) {
lock.writeLock().lock();
try {
factoryInstances.put(type, factoryInstance);
} finally {
lock.writeLock().unlock();
}
}
public static BridgeFactory<?> getFactoryInstance(Class<? extends BridgeFactory<?>> type) {
lock.readLock().lock();
try {
BridgeFactory<?> instance = factoryInstances.get(type);
if (instance != null) {
return instance;
}
} finally {
lock.readLock().unlock();
}
lock.writeLock().lock();
// Check that instance wasn't created between readLock().unlock() and writeLock().lock()
try {
BridgeFactory<?> instance = factoryInstances.get(type);
if (instance != null) {
return instance;
}
List<String> defs = getFactoryDefinition(type);
if (defs == null) {
logger.log(Level.SEVERE, "No Factory Implementation class specified in faces-config for " + type);
return null;
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
for (String factoryImplClassName : defs) {
instance = getImplGivenPreviousImpl(cl, factoryImplClassName, type, instance);
}
// Now store instance in cache
addFactoryInstance(type, instance);
return instance;
} finally {
lock.writeLock().unlock();
}
}
private static BridgeFactory<?> getImplGivenPreviousImpl(ClassLoader classLoader, String implName,
Class<? extends BridgeFactory<?>> type, BridgeFactory<?> previousImpl) {
Class<? extends BridgeFactory<?>> implClass = null;
try {
implClass = (Class<? extends BridgeFactory<?>>) classLoader.loadClass(implName).asSubclass(type);
try {
Constructor<? extends BridgeFactory<?>> constructor = implClass.getConstructor(BridgeFactory.class);
previousImpl = constructor.newInstance(previousImpl);
if (logger.isLoggable(Level.FINE)) {
logger.fine("New factory instance " + implName + " has been created with parent implementation");
}
} catch (NoSuchMethodException e) {
Constructor<? extends BridgeFactory<?>> constructor = implClass.getConstructor();
previousImpl = constructor.newInstance();
if (logger.isLoggable(Level.FINE)) {
logger.fine("New factory instance " + implName + " has been created");
}
}
} catch (ClassNotFoundException e) {
throw new BridgeException("Bridge Factory class " + implName + " not found");
} catch (NoSuchMethodException e) {
throw new BridgeException("Bridge Factory " + implName
+ " has neither BridgeFactory() nor BridgeFactory(BridgeFactory) constructor");
} catch (IllegalArgumentException e) {
throw new BridgeException("Illegal argument for Bridge Factory " + implName + " constructor", e);
} catch (InstantiationException e) {
throw new BridgeException("Can't instantiate Bridge Factory class " + implName, e);
} catch (IllegalAccessException e) {
throw new BridgeException("Illegal access to Bridge Factory constructor", e);
} catch (InvocationTargetException e) {
Throwable targetException = e.getTargetException();
if (targetException instanceof NoClassDefFoundError) {
logger.info("Bridge Factory was not instantiated due to " + targetException.getMessage());
} else {
throw new BridgeException("Can't instantiate Bridge Factory class " + implName, targetException);
}
} catch (NoClassDefFoundError e) {
logger.info("Bridge Factory was not instantiated due to " + e.getMessage());
}
return previousImpl;
}
}