/* Copyright (2012) Schibsted ASA
* This file is part of Possom.
*
* Possom 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 3 of the License, or
* (at your option) any later version.
*
* Possom 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 Possom. If not, see <http://www.gnu.org/licenses/>.
*
*/
package no.sesat.search.site;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.log4j.Logger;
/** Abstract implementation of a SiteKeyedFactory to help with
* the INSTANCES map and locking pattern typically used around it using ReentrantReadWriteLocks.
*
* @version $Id$
*/
public abstract class AbstractSiteKeyedFactory implements SiteKeyedFactory{
// Constants -----------------------------------------------------
private static final Logger LOG = Logger.getLogger(AbstractSiteKeyedFactory.class);
private static final String ERR_DOC_BUILDER_CREATION = " failed to construct new factory instance for ";
// Attributes ----------------------------------------------------
// Static --------------------------------------------------------
/** Handles the locking pattern around the INSTANCES map that's done with a ReentrantReadWriteLock.
* @see http://permalink.gmane.org/gmane.comp.java.sesat.kernel.devel/228
*
* @param <T> the type of SiteKeyedFactory that can be constructed.
* @param site the site this factory will answer to.
* @param instances the map of factories of type T already in existence.
* @param instancesLock the lock used around the instances map
* @param constructor the wrapper around the constructor used to create factories of type T.
* @return the singleton instance of factory of type T related to the given site.
*/
protected static final <T extends AbstractSiteKeyedFactory> T instanceOf(
final Site site,
final Map<Site,T> instances,
final ReentrantReadWriteLock instancesLock,
final FactoryConstructor<T> constructor){
try {
instancesLock.readLock().lock();
if (!instances.containsKey(site)) {
// It is not possible to upgrade a read lock...
instancesLock.readLock().unlock();
instancesLock.writeLock().lock();
try {
// ...so check the condition again.
if (!instances.containsKey(site)) {
instances.put(site, constructor.construct());
}
} catch (SiteKeyedFactoryInstantiationException e) {
LOG.error(constructor + ERR_DOC_BUILDER_CREATION + site, e);
} finally {
// Downgrade to a read lock.
instancesLock.readLock().lock();
instancesLock.writeLock().unlock();
}
}
return instances.get(site);
} finally {
// Finally release the read lock.
instancesLock.readLock().unlock();
}
}
// Constructors --------------------------------------------------
protected AbstractSiteKeyedFactory() {
}
// Public --------------------------------------------------------
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
/** Wraps a constructor so that it may be delegated to from the instanceOf method.
*
* @param <T> the type of SiteKeyedFactory that can be constructed.
*/
protected interface FactoryConstructor<T extends AbstractSiteKeyedFactory>{
/** Wraps a constructor so that it may be delegated to from the instanceOf method.
*
* @return the new SiteKeyedFactory instance
* @throws no.sesat.search.site.SiteKeyedFactoryInstantiationException any failure in construction.
*/
T construct() throws SiteKeyedFactoryInstantiationException;
}
}