/* Copyright (2007-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/>. * * DataModelFactory.java * * Created on 23 January 2007, 09:27 * */ package no.sesat.search.datamodel; import no.sesat.search.datamodel.access.ControlLevel; import no.sesat.search.datamodel.generic.DataObject; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; import no.sesat.commons.ioc.BaseContext; import no.sesat.commons.ioc.ContextWrapper; import no.sesat.search.site.Site; import no.sesat.search.site.SiteContext; import no.sesat.search.site.SiteKeyedFactory; import no.sesat.search.site.SiteKeyedFactoryInstantiationException; import no.sesat.search.site.config.PropertiesContext; import no.sesat.search.site.config.SiteConfiguration; import org.apache.log4j.Logger; /** Base definition of a factory used to create a datamodel, its datanodes, and its dataobjects. * Also provides the SiteKeyedFactory implementation, via static methods, * to store the current instances in the jvm per site. * Each site defines the final DataModelFactory implementation through the "sesam.datamodel.impl" property * in its configuration.properties. * * * @version <tt>$Id$</tt> */ public abstract class DataModelFactory implements SiteKeyedFactory{ /** * The context any DataModelFactory must work within. */ public interface Context extends BaseContext, SiteContext, PropertiesContext { } // Constants ----------------------------------------------------- private static final Map<Site, DataModelFactory> INSTANCES = new HashMap<Site,DataModelFactory>(); private static final ReentrantReadWriteLock INSTANCES_LOCK = new ReentrantReadWriteLock(); private static final String DATA_MODEL_FACTORY_IMPL = "sesam.datamodel.impl"; private static final Logger LOG = Logger.getLogger(DataModelFactory.class); // Attributes ---------------------------------------------------- private final Context context; // Static -------------------------------------------------------- /** Instance applicable to the provided context. * * @param cxt * @return * @throws no.sesat.search.site.SiteKeyedFactoryInstantiationException */ public static DataModelFactory instanceOf(final Context cxt) throws SiteKeyedFactoryInstantiationException { final Site site = cxt.getSite(); assert null != site; DataModelFactory instance; try{ INSTANCES_LOCK.readLock().lock(); instance = INSTANCES.get(site); }finally{ INSTANCES_LOCK.readLock().unlock(); } if (null == instance) { try{ INSTANCES_LOCK.writeLock().lock(); instance = newInstance(cxt); // update the store of DataModelFactory instances INSTANCES.put(cxt.getSite(), instance); }finally{ INSTANCES_LOCK.writeLock().unlock(); } } return instance; } public boolean remove(final Site site){ try{ INSTANCES_LOCK.writeLock().lock(); return null != INSTANCES.remove(site); }finally{ INSTANCES_LOCK.writeLock().unlock(); } } // Constructors -------------------------------------------------- private DataModelFactory(){ throw new IllegalArgumentException("Must use constructor with Context argument."); } /** Creates a new instance of DataModelToolkitFactory * @param cxt */ protected DataModelFactory(final Context cxt) { context = cxt; } // Public -------------------------------------------------------- /** * * @return */ public abstract DataModel instantiate(); /** * <pre> * Example usage: * DataModelFactory dm = DataModelFactory.instanceOf(null); * BrowserDataObject bdo = dm.instantiate( * BrowserDataObject.class, * datamodel, * new DataObject.Property("locale", null), * new DataObject.Property("supportedLocales", null)); * </pre> ** @param cls * @param datamodel * @param properties * @return */ public abstract <T> T instantiate(Class<T> cls, DataModel datamodel, DataObject.Property... properties); /** Lets the datamodel instance know that it has moved on and is now being accessed by a different level in the * control process stack. * * <b>This method is only to be used by Possom classes, not skin implementations!</b> * Future version must impose this restriction. * ** @param datamodel * @param controlLevel * @return */ public abstract DataModel assignControlLevel(final DataModel datamodel, final ControlLevel controlLevel); /** What is the current ControlLevel for the given datamodel. * * <b>This method is only to be used by Possom classes, not skin implementations!</b> * Future version must impose this restriction. * * @param datamodel related datamodel we are looking for its control level * @return the current control level */ public abstract ControlLevel currentControlLevel(final DataModel datamodel); // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- /** Constructs an DataModelFactory implementation. * Matching the class as specified * by the value for DATA_MODEL_FACTORY_IMPL in the Site's configuration.properties. **/ private static DataModelFactory newInstance(final Context cxt) throws SiteKeyedFactoryInstantiationException{ try{ final SiteConfiguration siteConf = SiteConfiguration.instanceOf( ContextWrapper.wrap(SiteConfiguration.Context.class, cxt)); final String clsName = siteConf.getProperty(DATA_MODEL_FACTORY_IMPL); LOG.info("constructing for " + cxt.getSite() + " instance of " + clsName); if(null == clsName){ throw new SiteKeyedFactoryInstantiationException( "Couldn't find " + DATA_MODEL_FACTORY_IMPL + " in " + siteConf.getProperties().toString(), new NullPointerException()); } @SuppressWarnings("unchecked") final Class<DataModelFactory> cls = (Class<DataModelFactory>) Class.forName(clsName); return cls.getDeclaredConstructor(Context.class).newInstance(cxt); }catch (ClassNotFoundException ex) { throw new SiteKeyedFactoryInstantiationException(ex.getMessage(), ex); }catch (NoSuchMethodException ex) { throw new SiteKeyedFactoryInstantiationException(ex.getMessage(), ex); }catch (InstantiationException ex) { throw new SiteKeyedFactoryInstantiationException(ex.getMessage(), ex); }catch(IllegalArgumentException ex){ throw new SiteKeyedFactoryInstantiationException(ex.getMessage(), ex); }catch(IllegalAccessException ex){ throw new SiteKeyedFactoryInstantiationException(ex.getMessage(), ex); }catch(InvocationTargetException ex){ throw new SiteKeyedFactoryInstantiationException(ex.getMessage(), ex); } } // Inner classes ------------------------------------------------- }