/* * 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; // J2SE dependencies and extensions import java.util.Set; import org.geotools.factory.BufferedFactory; import org.geotools.factory.FactoryRegistryException; import org.geotools.factory.GeoTools; 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.datum.DatumAuthorityFactory; import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory; import org.opengis.util.InternationalString; /** * An authority factory that caches all objects created by the delegate CRSAuthorityFactory. * </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 the {@linkplain an AuthorityFactory authority factory} and the * result is cached for next time. * <p> * This object is responsible for owning a {{ReferencingObjectCache}}; there are * several implementations to choose from on construction. * </p> * * @since 2.4 * * @source $URL$ * @version $Id: BufferedAuthorityDecorator.java 26038 2007-06-27 01:58:12Z * jgarnett $ * @author Jody Garnett */ public final class CachedCRSAuthorityDecorator extends AbstractAuthorityFactory implements AuthorityFactory, CRSAuthorityFactory, BufferedFactory { /** Cache to be used for referencing objects. */ ObjectCache cache; /** The delegate authority for coordinate reference systems. */ private CRSAuthorityFactory crsAuthority; /** The delegate authority for searching. */ private AbstractAuthorityFactory delegate; /** * Constructs an instance wrapping the specified factory with a default * cache. * <p> * The provided authority factory must implement * {@link DatumAuthorityFactory}, {@link CSAuthorityFactory}, * {@link CRSAuthorityFactory} and * {@link CoordinateOperationAuthorityFactory} . * * @param factory * The factory to cache. Can not be {@code null}. */ public CachedCRSAuthorityDecorator(final CRSAuthorityFactory factory) { this(factory, createCache(GeoTools.getDefaultHints())); } /** * Constructs an instance wrapping the specified factory. The * {@code maxStrongReferences} argument specify the maximum number of * objects to keep by strong reference. If a greater amount of objects are * created, then the strong references for the oldest ones are replaced by * weak references. * <p> * This constructor is protected because subclasses must declare which of * the {@link DatumAuthorityFactory}, {@link CSAuthorityFactory}, * {@link CRSAuthorityFactory} {@link SearchableAuthorityFactory} 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 CachedCRSAuthorityDecorator(CRSAuthorityFactory factory, ObjectCache cache) { super( ((ReferencingFactory)factory).getPriority() ); // TODO this.cache = cache; crsAuthority = (CRSAuthorityFactory) factory; this.delegate = (AbstractAuthorityFactory) factory; } /** Utility method used to produce cache based on hint */ protected static ObjectCache createCache(final Hints hints) throws FactoryRegistryException { return ObjectCaches.create(hints); } // // Utility Methods and Cache Care and Feeding // protected String toKey(String code) { return ObjectCaches.toKey( getAuthority(), code); } // // AuthorityFactory // 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) { obj = crsAuthority.createObject(code); cache.put(key, obj); } } finally { cache.writeUnLock(key); } } return obj; } public Citation getAuthority() { return crsAuthority.getAuthority(); } public Set getAuthorityCodes(Class type) throws FactoryException { return crsAuthority.getAuthorityCodes(type); } public InternationalString getDescriptionText(String code) throws FactoryException { return crsAuthority.getDescriptionText(code); } // // 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) { crs = crsAuthority.createCompoundCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public CoordinateReferenceSystem createCoordinateReferenceSystem(String code) throws FactoryException { final String key = toKey(code); CoordinateReferenceSystem crs = (CoordinateReferenceSystem) cache .get(key); if (crs == null) { try { cache.writeLock(key); crs = (CoordinateReferenceSystem) cache.peek(key); if (crs == null) { crs = crsAuthority.createCoordinateReferenceSystem(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public DerivedCRS createDerivedCRS(String code) throws FactoryException { final String key = toKey(code); DerivedCRS crs = (DerivedCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (DerivedCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createDerivedCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public EngineeringCRS createEngineeringCRS(String code) throws FactoryException { final String key = toKey(code); EngineeringCRS crs = (EngineeringCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (EngineeringCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createEngineeringCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public GeocentricCRS createGeocentricCRS(String code) throws FactoryException { final String key = toKey(code); GeocentricCRS crs = (GeocentricCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (GeocentricCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createGeocentricCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public GeographicCRS createGeographicCRS(String code) throws FactoryException { final String key = toKey(code); GeographicCRS crs = (GeographicCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (GeographicCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createGeographicCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public ImageCRS createImageCRS(String code) throws FactoryException { final String key = toKey(code); ImageCRS crs = (ImageCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (ImageCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createImageCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public ProjectedCRS createProjectedCRS(String code) throws FactoryException { final String key = toKey(code); ProjectedCRS crs = (ProjectedCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (ProjectedCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createProjectedCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public TemporalCRS createTemporalCRS(String code) throws FactoryException { final String key = toKey(code); TemporalCRS crs = (TemporalCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (TemporalCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createTemporalCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } public VerticalCRS createVerticalCRS(String code) throws FactoryException { final String key = toKey(code); VerticalCRS crs = (VerticalCRS) cache.get(key); if (crs == null) { try { cache.writeLock(key); crs = (VerticalCRS) cache.peek(key); if (crs == null) { crs = crsAuthority.createVerticalCRS(code); cache.put(key, crs); } } finally { cache.writeUnLock(key); } } return crs; } // // AbstractAuthorityFactory // public void dispose() throws FactoryException { delegate.dispose(); cache.clear(); cache = null; delegate = null; } public String getBackingStoreDescription() throws FactoryException { return delegate.getBackingStoreDescription(); } /** * Returns a finder which can be used for looking up unidentified objects. * The default implementation delegates lookup to the underlying backing * store and caches the result. * * @since 2.4 */ @Override public synchronized IdentifiedObjectFinder getIdentifiedObjectFinder( final Class/*<? extends IdentifiedObject>*/ type) throws FactoryException { return new Finder( delegate.getIdentifiedObjectFinder(type), ObjectCaches.create("weak",250)); } /** * An implementation of {@link IdentifiedObjectFinder} which delegates * the work to the underlying backing store and caches the result. * <p> * A separate ObjectCache, findCache, is used to store the values created over the course * of finding. The findCache is set up as a "chain" allowing it to use our cache * to prevent duplication of effort. In the future this findCache may be shared between * instances. * <p> * <b>Implementation note:</b> we will create objects using directly the underlying backing * store, not using the cache. 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. */ private final class Finder extends IdentifiedObjectFinder.Adapter { /** Cache used when finding */ private ObjectCache findCache; /** * Creates a finder for the underlying backing store. */ Finder(final IdentifiedObjectFinder finder, ObjectCache tempCache) { super(finder); this.findCache = tempCache; } /** * 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 { /* * Do not synchronize on 'BufferedAuthorityFactory.this'. This method may take a * while to execute and we don't want to block other threads. The synchronizations * in the 'create' methods and in the 'findPool' map should be suffisient. * * TODO: avoid to search for the same object twice. For now we consider that this * is not a big deal if the same object is searched twice; it is "just" a * waste of CPU. */ IdentifiedObject candidate; candidate = (IdentifiedObject) findCache.get(object); if (candidate == null) { // Must delegates to 'finder' (not to 'super') in order to take // advantage of the method overriden by AllAuthoritiesFactory. IdentifiedObject found = finder.find(object); if (found != null) { try { findCache.writeLock(object); candidate = (IdentifiedObject) findCache.peek(object); if( candidate == null ){ findCache.put(object, found); return found; } } finally { findCache.writeLock(object); } } } return candidate; } /** * 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); } // We don't rely on super-class implementation, because we want to // take advantage of the method overriden by AllAuthoritiesFactory. return finder.findIdentifier(object); } } }