/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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. * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.referencing.factory; import java.util.Set; import java.util.logging.Level; import javax.measure.unit.Unit; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.ObjectPoolFactory; import org.apache.commons.pool.PoolableObjectFactory; import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.commons.pool.impl.GenericObjectPoolFactory; import org.apache.commons.pool.impl.GenericObjectPool.Config; import org.geotools.factory.BufferedFactory; import org.geotools.factory.Hints; import org.geotools.util.ObjectCache; import org.geotools.util.ObjectCaches; import org.opengis.metadata.citation.Citation; import org.opengis.referencing.AuthorityFactory; import org.opengis.referencing.FactoryException; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CompoundCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.DerivedCRS; import org.opengis.referencing.crs.EngineeringCRS; import org.opengis.referencing.crs.GeocentricCRS; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.ImageCRS; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.crs.VerticalCRS; import org.opengis.referencing.cs.CSAuthorityFactory; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.cs.CylindricalCS; import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.cs.PolarCS; import org.opengis.referencing.cs.SphericalCS; import org.opengis.referencing.cs.TimeCS; import org.opengis.referencing.cs.VerticalCS; import org.opengis.referencing.datum.Datum; import org.opengis.referencing.datum.DatumAuthorityFactory; import org.opengis.referencing.datum.Ellipsoid; import org.opengis.referencing.datum.EngineeringDatum; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.datum.ImageDatum; import org.opengis.referencing.datum.PrimeMeridian; import org.opengis.referencing.datum.TemporalDatum; import org.opengis.referencing.datum.VerticalDatum; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory; import org.opengis.util.InternationalString; /** * An authority mediator that consults (a possibily shared) cache before delegating the generation * of the content to a "worker" authority factory. * </p> * The behaviour of the {@code createFoo(String)} methods first looks if a previously created object * exists for the given code. If such an object exists, it is returned directly. The testing of the * cache is synchronized and may block if the referencing object is under construction. * <p> * If the object is not yet created, the definition is delegated to the appropriate * {@code createFoo} method of the factory, which will cache the result for next time. * <p> * This object is responsible for maintaining an {{ObjectCache}} of "workers" based on the following: * <ul> * <li>Hints.AUTHORITY_MAX_ACTIVE (default 2) - indicates the maximum number of worker created, if non * positive the number of workers is unbounded. * <li>Hints. * </ul> * </p> * * @since 2.4 * * @source $URL$ * @version $Id$ * @author Jody Garnett (Refractions Research) * @author Cory Horner (Refractions Research) */ public abstract class AbstractAuthorityMediator extends AbstractAuthorityFactory implements AuthorityFactory, CRSAuthorityFactory, CSAuthorityFactory, DatumAuthorityFactory, CoordinateOperationAuthorityFactory, BufferedFactory { static final int PRIORITY = MAXIMUM_PRIORITY - 10; /** * Cache to be used for referencing objects defined by this authority. Please note that this * cache may be shared! * <p> * Your cache may grow to considerable size during actual use; in addition to storing * CoordinateReferenceSystems (by code); it will also store all the component parts * (each under its own code), along with MathTransformations between two * CoordinateReferenceSystems. So even if you are only planning on working with * 50 CoordianteReferenceSystems please keep in mind that you will need larger * cache size in order to prevent a bottleneck. */ ObjectCache cache; /** * The findCache is used to store search results; often match a "raw" CoordinateReferenceSystem * created from WKT (as the key) with a "real" CoordianteReferenceSystem as defined * by this authority. */ ObjectCache findCache; /** * Pool to hold workers which will be used to construct referencing objects which are not * present in the cache. */ private ObjectPool workers; /** * Configuration object for the object pool. The constructor reads its hints and sets the pool * configuration in this object; */ Config poolConfig = new Config(); /** * A container of the "real factories" actually used to construct objects. */ protected final ReferencingFactoryContainer factories; /** * Constructs an instance making use of the default cache and priority level. */ protected AbstractAuthorityMediator() { this(PRIORITY); } /** * Constructs an instance based on the provided Hints * * @param factory The factory to cache. Can not be {@code null}. */ protected AbstractAuthorityMediator( Hints hints ) { this(PRIORITY, hints); } /** * Constructs an instance making use of the default cache. * * @param factory The factory to cache. Can not be {@code null}. */ protected AbstractAuthorityMediator( int priority ) { this(priority, ObjectCaches.create("weak", 50), ReferencingFactoryContainer.instance(null)); } /** * Constructs an instance making use of the default cache. * * @param factory The factory to cache. Can not be {@code null}. */ protected AbstractAuthorityMediator( int priority, Hints hints ) { this(priority, ObjectCaches.create(hints), ReferencingFactoryContainer.instance(hints)); // configurable behaviour poolConfig.minIdle = Hints.AUTHORITY_MIN_IDLE.toValue(hints); poolConfig.maxIdle = Hints.AUTHORITY_MAX_IDLE.toValue(hints); poolConfig.maxActive = Hints.AUTHORITY_MAX_ACTIVE.toValue(hints); poolConfig.minEvictableIdleTimeMillis = Hints.AUTHORITY_MIN_EVICT_IDLETIME.toValue(hints); poolConfig.softMinEvictableIdleTimeMillis = Hints.AUTHORITY_SOFTMIN_EVICT_IDLETIME .toValue(hints); poolConfig.timeBetweenEvictionRunsMillis = Hints.AUTHORITY_TIME_BETWEEN_EVICTION_RUNS .toValue(hints); // static behaviour poolConfig.maxWait = -1; // block indefinitely until a worker is available poolConfig.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_BLOCK; } /** * Constructs an instance making use of the indicated cache. * <p> * This constructor is protected because subclasses must declare which of the * {@link DatumAuthorityFactory}, {@link CSAuthorityFactory}, {@link CRSAuthorityFactory} and * {@link CoordinateOperationAuthorityFactory} interfaces they choose to implement. * * @param factory The factory to cache. Can not be {@code null}. * @param maxStrongReferences The maximum number of objects to keep by strong reference. */ protected AbstractAuthorityMediator( int priority, ObjectCache cache, ReferencingFactoryContainer container ) { super(priority); this.factories = container; this.cache = cache; this.findCache = ObjectCaches.chain( ObjectCaches.create("weak",0), cache ); } protected void completeHints() { hints.put(Hints.DATUM_AUTHORITY_FACTORY, this); hints.put(Hints.CS_AUTHORITY_FACTORY, this); hints.put(Hints.CRS_AUTHORITY_FACTORY, this ); hints.put(Hints.COORDINATE_OPERATION_AUTHORITY_FACTORY, this ); } /** * True if this mediator is currently connected to one or more workers. * * @return */ public boolean isConnected() { return (workers.getNumActive() + workers.getNumIdle()) > 0; } ObjectPool getPool() { if (workers == null) { // create pool PoolableObjectFactory objectFactory = new AuthorityPoolableObjectFactory(); ObjectPoolFactory poolFactory = new GenericObjectPoolFactory(objectFactory, poolConfig); this.setPool(poolFactory.createPool()); } return workers; } void setPool( ObjectPool pool ) { this.workers = pool; } // // Utility Methods and Cache Care and Feeding // protected String toKey( String code ) { return ObjectCaches.toKey(getAuthority(), code); } /** * Trims the authority scope, if present. For example if this factory is an EPSG authority * factory and the specified code start with the "EPSG:" prefix, then the prefix is removed. * Otherwise, the string is returned unchanged (except for leading and trailing spaces). * * @param code The code to trim. * @return The code without the authority scope. */ protected String trimAuthority( String code ) { return toKey(code); } /** * The authority body of the objects this factory provides. */ public abstract Citation getAuthority(); public Set getAuthorityCodes( Class type ) throws FactoryException { Set codes = (Set) cache.get(type); if (codes == null) { try { cache.writeLock(type); codes = (Set) cache.peek(type); if (codes == null) { AbstractCachedAuthorityFactory worker = null; try { worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); codes = worker.getAuthorityCodes(type); cache.put(type, codes); } catch (FactoryException e) { throw e; } catch (Exception e) { throw new FactoryException(e); } finally { try { getPool().returnObject(worker); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); } } } } finally { cache.writeUnLock(type); } } return codes; } public abstract InternationalString getDescriptionText( String code ) throws FactoryException; public IdentifiedObject createObject( String code ) throws FactoryException { final String key = toKey(code); IdentifiedObject obj = (IdentifiedObject) cache.get(key); if (obj == null) { try { cache.writeLock(key); obj = (IdentifiedObject) cache.peek(key); if (obj == null) { AbstractCachedAuthorityFactory worker = null; try { worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); obj = worker.createDerivedCRS(code); cache.put(key, obj); } catch (FactoryException e) { throw e; } catch (Exception e) { throw new FactoryException(e); } finally { try { getPool().returnObject(worker); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); } } } } finally { cache.writeUnLock(key); } } return obj; } // // CRSAuthority // public synchronized CompoundCRS createCompoundCRS( final String code ) throws FactoryException { final String key = toKey(code); CompoundCRS crs = (CompoundCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (CompoundCRS) cache.peek(key); if (crs == null) { AbstractCachedAuthorityFactory worker = null; try { worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); crs = worker.createCompoundCRS(code); cache.put(key, crs); } catch (FactoryException e) { throw e; } catch (Exception e) { throw new FactoryException(e); } finally { try { getPool().returnObject(worker); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); } } } } finally { cache.writeUnLock(key); } } return crs; } public CoordinateReferenceSystem createCoordinateReferenceSystem( String code ) throws FactoryException { final String key = toKey(code); return createWith( key, new WorkerSafeRunnable(){ public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createCoordinateReferenceSystem(key); } }); } public DerivedCRS createDerivedCRS(String code) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createEngineeringCRS(key); } }); } public GeocentricCRS createGeocentricCRS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createGeocentricCRS(key); } }); } public GeographicCRS createGeographicCRS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable(){ public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createGeographicCRS(key); } }); } public ImageCRS createImageCRS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable(){ public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createImageCRS(key); } }); } public ProjectedCRS createProjectedCRS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable(){ public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createProjectedCRS(key); } }); } public TemporalCRS createTemporalCRS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable(){ public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createTemporalCRS(key); } }); } public VerticalCRS createVerticalCRS(String code) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createVerticalCRS(key); } }); } // // CSAuthority // public CartesianCS createCartesianCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createCartesianCS(key); } }); } public CoordinateSystem createCoordinateSystem( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createCoordinateSystem(key); } }); } // sample implemenation with get/test public CoordinateSystemAxis createCoordinateSystemAxis( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createCoordinateSystemAxis(key); } }); } public CylindricalCS createCylindricalCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createCylindricalCS(key); } }); } public EllipsoidalCS createEllipsoidalCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createEllipsoidalCS(key); } }); } public PolarCS createPolarCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createPolarCS(key); } }); } public SphericalCS createSphericalCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createSphericalCS(key); } }); } public TimeCS createTimeCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createTimeCS(key); } }); } public Unit<?> createUnit( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createUnit(key); } }); } public VerticalCS createVerticalCS( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createVerticalCS(key); } }); } // // DatumAuthorityFactory // public Datum createDatum( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createDatum(key); } }); } public Ellipsoid createEllipsoid( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createEllipsoid(key); } }); } public EngineeringDatum createEngineeringDatum( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createEngineeringDatum(key); } }); } public GeodeticDatum createGeodeticDatum( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createGeodeticDatum(key); } }); } public ImageDatum createImageDatum( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createImageDatum(key); } }); } public PrimeMeridian createPrimeMeridian( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createPrimeMeridian(key); } }); } public TemporalDatum createTemporalDatum( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createTemporalDatum(key); } }); } public VerticalDatum createVerticalDatum( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createVerticalDatum(key); } }); } public CoordinateOperation createCoordinateOperation( String code ) throws FactoryException { final String key = toKey(code); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createCoordinateOperation(key); } }); } public synchronized Set/* <CoordinateOperation> */createFromCoordinateReferenceSystemCodes( final String sourceCode, final String targetCode ) throws FactoryException { final Object key = ObjectCaches.toKey(getAuthority(), sourceCode, targetCode); return createWith(key, new WorkerSafeRunnable() { public Object run(AbstractCachedAuthorityFactory worker) throws FactoryException { return worker.createFromCoordinateReferenceSystemCodes(sourceCode, targetCode); } }); } /** * This method is used to cut down the amount of try/catch/finally code * needed when working with the cache and workers. * <p> * This code brings together two try/catch/finally blocks. * * For cache management:<pre><code> * T value = (T) cache.get(key); * if (value == null) { * try { * cache.writeLock(key); * value = (T) cache.peek(key); * if (value == null) { * ....generate value.... * cache.put( key, value ); * } * } finally { * cache.writeUnLock(key); * } * } * </code></pre> * And worker management when generating values:<pre><code> * AbstractCachedAuthorityFactory worker = null; * try { * worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); * value = (T) runner.run( worker ); * } catch (FactoryException e) { * throw e; * } catch (Exception e) { * throw new FactoryException(e); * } finally { * try { * getPool().returnObject(worker); * } catch (Exception e) { * LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); * } * } * </code></pre> * * @param key Used to look in the cache * @param runner Used to generate a value in the case of a cache miss * @return value from either the cache or generated */ protected <T> T createWith( Object key, WorkerSafeRunnable runner ) throws FactoryException { T value = (T) cache.get(key); if (value == null) { try { cache.writeLock(key); value = (T) cache.peek(key); if (value == null) { AbstractCachedAuthorityFactory worker = null; try { worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); value = (T) runner.run( worker ); } catch (FactoryException e) { throw e; } catch (Exception e) { throw new FactoryException(e); } finally { try { getPool().returnObject(worker); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); } } cache.put(key, value); } } finally { cache.writeUnLock(key); } } return value; } /** * An interface describing a portion of work for which a worker is needed. * <p> * The worker is borrowed from the pool */ protected abstract class WorkerSafeRunnable { public abstract Object run( AbstractCachedAuthorityFactory worker ) throws FactoryException; } public String getBackingStoreDescription() throws FactoryException { AbstractCachedAuthorityFactory worker = null; try { worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); return worker.getBackingStoreDescription(); } catch (FactoryException e) { throw e; } catch (Exception e) { throw new FactoryException(e); } finally { try { getPool().returnObject(worker); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); } } } /** * Clean up the object pool of workers (since we are shutting down). * <p> * Subclasses may wish to override this method if they have their own resources * to clean up (like a database connection). If you do this please remember to call * super.dispose(). * </p> */ public void dispose() throws FactoryException { if (workers != null) { try { workers.clear(); } catch (FactoryException e) { throw e; } catch (Exception e) { throw new FactoryException( e ); } workers = null; } } /** * Creates the objects, subclasses of AbstractCachedAuthorityFactory, which are held by the * ObjectPool. This implementation simply delegates each method to the subclass. * * @author Cory Horner (Refractions Research) */ private class AuthorityPoolableObjectFactory implements PoolableObjectFactory { AuthorityPoolableObjectFactory() { } public void activateObject( Object obj ) throws Exception { AbstractCachedAuthorityFactory worker = (AbstractCachedAuthorityFactory) obj; worker.cache = cache; activateWorker( worker ); } public void destroyObject( Object obj ) throws Exception { destroyWorker((AbstractCachedAuthorityFactory) obj); } public Object makeObject() throws Exception { AbstractCachedAuthorityFactory worker = makeWorker(); return worker; } public void passivateObject( Object obj ) throws Exception { passivateWorker((AbstractCachedAuthorityFactory) obj); } public boolean validateObject( Object obj ) { return validateWorker((AbstractCachedAuthorityFactory) obj); } } /** * Reinitialize an instance to be returned by the pool. * <p> * Please note that BEFORE this method has been called AbstractAuthorityMediator has already: * <ul> * <li>provided the worker with the single shared <code>cache</code> * <li>provided the worker with the single shared <code>findCache</code> * </ul> */ protected abstract void activateWorker( AbstractCachedAuthorityFactory worker ) throws Exception; /** * Destroys an instance no longer needed by the pool. */ protected abstract void destroyWorker( AbstractCachedAuthorityFactory worker ) throws Exception; /** * Creates an instance that can be returned by the pool. */ protected abstract AbstractCachedAuthorityFactory makeWorker() throws Exception; /** * Un-initialize an instance to be returned to the pool. */ protected abstract void passivateWorker( AbstractCachedAuthorityFactory worker ) throws Exception; /** * Ensures that the instance is safe to be returned by the pool. */ protected abstract boolean validateWorker( AbstractCachedAuthorityFactory worker ); /** * Returns a finder which can be used for looking up unidentified objects. * <p> * The returned implementation will make use of workers as needed. * * @param type The type of objects to look for. * @return A finder to use for looking up unidentified objects. * @throws FactoryException if the finder can not be created. * @since 2.4 */ public IdentifiedObjectFinder getIdentifiedObjectFinder( final Class/* <? extends IdentifiedObject> */type ) throws FactoryException { return new LazyCachedFinder(type); } /** * An {@link IdentifiedObjectFinder} which uses a worker when searching. * <p> * The worker used is configured to store answers in a separate <code>findCache</code>, rather * that disrupt the regular <code>cached</code>(which is focused on retaining codes requested * by the user application). This is because hundred of objects may be created during a * scan while only one will be typically retained. We don't want to overload the cache with * every false candidates that we encounter during the scan. * <p> * Because the worker is configured differently before use we must be careful to return it to * its original state before returning back to the <code>workers</code> pool. */ private final class LazyCachedFinder extends IdentifiedObjectFinder { private Class type; /** * Creates a finder for the underlying backing store. */ LazyCachedFinder(final Class type) { super( AbstractAuthorityMediator.this, type); this.type = type; } /** * Looks up an object from this authority factory which is equals, ignoring metadata, * to the specified object. The default implementation performs the same lookup than * the backing store and caches the result. */ @Override public IdentifiedObject find(final IdentifiedObject object) throws FactoryException { IdentifiedObject candidate; candidate = (IdentifiedObject) findCache.get(object); if (candidate != null) { return candidate; } try { findCache.writeLock(object); // avoid searching for the same object twice IdentifiedObject found; AbstractCachedAuthorityFactory worker = null; try { worker = (AbstractCachedAuthorityFactory) getPool().borrowObject(); worker.cache = ObjectCaches.chain( ObjectCaches.create("weak",3000), cache ); worker.findCache = findCache; setProxy(AuthorityFactoryProxy.getInstance(worker, type)); found = super.find(object); } catch (Exception e) { throw new FactoryException(e); } finally { setProxy(null); worker.cache = cache; worker.findCache = findCache; try { getPool().returnObject(worker); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unable to return worker " + e, e); } } if( found == null) { return null; // not found } candidate = (IdentifiedObject) findCache.peek(object); if( candidate == null ){ findCache.put(object, found); return found; } else { return candidate; } } finally { findCache.writeUnLock(object); } } protected Citation getAuthority(){ return AbstractAuthorityMediator.this.getAuthority(); } /** * Returns the identifier for the specified object. */ @Override public String findIdentifier(final IdentifiedObject object) throws FactoryException { IdentifiedObject candidate; candidate = (IdentifiedObject) findCache.get(object); if (candidate != null) { return getIdentifier(candidate); } return super.findIdentifier(object); } } }