001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2005, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.referencing.factory;
021:
022: // J2SE dependencies
023: import java.util.Map;
024: import java.util.Timer;
025: import java.util.TimerTask;
026: import java.util.Iterator;
027: import java.util.logging.Level;
028: import java.util.logging.LogRecord;
029:
030: // OpenGIS dependencies
031: import org.opengis.metadata.citation.Citation;
032: import org.opengis.referencing.FactoryException;
033:
034: // Geotools dependencies
035: import org.geotools.factory.Hints;
036: import org.geotools.factory.Factory;
037: import org.geotools.factory.OptionalFactory;
038: import org.geotools.resources.i18n.Errors;
039: import org.geotools.resources.i18n.ErrorKeys;
040: import org.geotools.resources.i18n.Logging;
041: import org.geotools.resources.i18n.LoggingKeys;
042:
043: /**
044: * A buffered authority factory which will defer the {@linkplain #createBackingStore creation
045: * of a backing store} until when first needed. This approach allow to etablish a connection to
046: * a database (for example) only when first needed. In addition, the backing store can be
047: * automatically disposed after a timeout and recreated when needed again.
048: *
049: * @since 2.1
050: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/factory/DeferredAuthorityFactory.java $
051: * @version $Id: DeferredAuthorityFactory.java 25972 2007-06-21 13:38:35Z desruisseaux $
052: * @author Martin Desruisseaux
053: *
054: * @todo Extends {@link BufferedAuthorityFactory} for now in order to improve the trunk stability
055: * during GEOT-1286 development, but we may revisit that after GEOT-1286 completion.
056: */
057: public abstract class DeferredAuthorityFactory extends
058: BufferedAuthorityFactory implements OptionalFactory {
059: /**
060: * The timer for {@linkplain AbstractAuthorityFactory#dispose disposing} backing stores.
061: *
062: * @todo Give a name to this timer when we will be allowed to compile for J2SE 1.5.
063: */
064: private static final Timer TIMER = new Timer(true);
065:
066: /**
067: * The task for disposing the backing store, or {@code null} if none.
068: * This task will be scheduled for repeated execution by {@link #setTimeout}.
069: */
070: private TimerTask disposer;
071:
072: /**
073: * {@code true} if the backing store was used since the last time the timer task was run.
074: * A value of {@code true} means that the task must wait again. A value of {@code false}
075: * means that it can dispose the backing store.
076: */
077: private boolean used;
078:
079: /**
080: * Constructs an instance without initial backing store. Subclasses are responsible for
081: * creating an appropriate backing store when the {@link #createBackingStore} method is
082: * invoked.
083: *
084: * @param userHints An optional set of hints, or {@code null} if none.
085: * @param priority The priority for this factory, as a number between
086: * {@link #MINIMUM_PRIORITY MINIMUM_PRIORITY} and
087: * {@link #MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
088: *
089: * @see #createBackingStore
090: *
091: * @since 2.2
092: */
093: protected DeferredAuthorityFactory(final Hints userHints,
094: final int priority) {
095: super (priority, DEFAULT_MAX);
096: }
097:
098: /**
099: * Constructs an instance without initial backing store. Subclasses are responsible for
100: * creating an appropriate backing store when the {@link #createBackingStore} method is
101: * invoked.
102: *
103: * @param userHints An optional set of hints, or {@code null} if none.
104: * @param priority The priority for this factory, as a number between
105: * {@link #MINIMUM_PRIORITY MINIMUM_PRIORITY} and
106: * {@link #MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
107: * @param maxStrongReferences The maximum number of objects to keep by strong reference.
108: *
109: * @see #createBackingStore
110: *
111: * @since 2.2
112: */
113: protected DeferredAuthorityFactory(final Hints userHints,
114: final int priority, final int maxStrongReferences) {
115: super (priority, maxStrongReferences);
116: }
117:
118: /**
119: * Returns {@code true} if this factory is available. The default implementation returns
120: * {@code false} if {@link #createBackingStore} throws an exception.
121: */
122: public boolean isAvailable() {
123: return super .isAvailable();
124: }
125:
126: /**
127: * Returns the backing store authority factory.
128: *
129: * @return The backing store to uses in {@code createXXX(...)} methods.
130: * @throws FactoryException if the creation of backing store failed.
131: */
132: final AbstractAuthorityFactory getBackingStore()
133: throws FactoryException {
134: assert Thread.holdsLock(this );
135: if (backingStore == null) {
136: backingStore = createBackingStore();
137: if (backingStore == null) {
138: throw new FactoryNotFoundException(Errors
139: .format(ErrorKeys.NO_DATA_SOURCE));
140: }
141: completeHints();
142: }
143: used = true; // Tell to the disposer to wait again.
144: return backingStore;
145: }
146:
147: /**
148: * Creates the backing store authority factory. This method is invoked the first time a
149: * {@code createXXX(...)} method is invoked.
150: *
151: * @return The backing store to uses in {@code createXXX(...)} methods.
152: * @throws FactoryNotFoundException if the backing store has not been found.
153: * @throws FactoryException if the creation of backing store failed for an other reason.
154: */
155: protected abstract AbstractAuthorityFactory createBackingStore()
156: throws FactoryException;
157:
158: /**
159: * Returns {@code true} if this deferred factory is connected to its backing store.
160: * This method returns {@code false} if no {@code createFoo} method has been invoked,
161: * if the backing store has been automatically disposed after the {@linkplain #setTimeout
162: * timeout} or if this factoy has been {@linkplain #dispose disposed}.
163: */
164: public synchronized boolean isConnected() {
165: return backingStore != null;
166: }
167:
168: /**
169: * Set a timer for disposing the backing store after the specified amount of milliseconds of
170: * inactivity. The {@link #createBackingStore} method will be responsible for creating a new
171: * backing store when needed. Note that the backing store disposal can be vetoed if
172: * {@link #canDisposeBackingStore} returns {@code false}.
173: *
174: * @param delay The minimal delay before to close the backing store. This delay is very
175: * approximative. The backing store will not be closed before, but may take as
176: * much as twice that time before to be closed.
177: */
178: public synchronized void setTimeout(final long delay) {
179: if (disposer != null) {
180: disposer.cancel();
181: }
182: disposer = new Disposer();
183: TIMER.schedule(disposer, delay, delay);
184: }
185:
186: /**
187: * Returns {@code true} if the backing store can be disposed now. This method is invoked
188: * automatically after the amount of time specified by {@link #setTimeout} if the factory
189: * were not used during that time. The default implementation always returns {@code true}.
190: * Subclasses should override this method and returns {@code false} if they want to prevent
191: * the backing store disposal under some circonstances.
192: *
193: * @param backingStore The backing store in process of being disposed.
194: */
195: protected boolean canDisposeBackingStore(
196: final AbstractAuthorityFactory backingStore) {
197: return true;
198: }
199:
200: /**
201: * Releases resources immediately instead of waiting for the garbage collector. This
202: * method disposes the backing store regardeless of {@link #canDisposeBackingStore} value.
203: */
204: public synchronized void dispose() throws FactoryException {
205: if (disposer != null) {
206: disposer.cancel();
207: disposer = null;
208: }
209: super .dispose();
210: }
211:
212: /**
213: * The task for closing the backing store after the timeout.
214: */
215: private final class Disposer extends TimerTask {
216: public void run() {
217: synchronized (DeferredAuthorityFactory.this ) {
218: if (used || !canDisposeBackingStore(backingStore)) {
219: used = false;
220: return;
221: }
222: if (cancel()) {
223: disposer = null;
224: if (backingStore != null)
225: try {
226: backingStore.dispose();
227: backingStore = null;
228: } catch (FactoryException exception) {
229: backingStore = null;
230: final LogRecord record = Logging
231: .format(
232: Level.WARNING,
233: LoggingKeys.CANT_DISPOSE_BACKING_STORE);
234: record.setSourceMethodName("run");
235: record.setSourceClassName(Disposer.class
236: .getName());
237: record.setThrown(exception);
238: LOGGER.log(record);
239: }
240: // Needed in order to lets GC do its job.
241: hints.remove(Hints.DATUM_AUTHORITY_FACTORY);
242: hints.remove(Hints.CS_AUTHORITY_FACTORY);
243: hints.remove(Hints.CRS_AUTHORITY_FACTORY);
244: hints
245: .remove(Hints.COORDINATE_OPERATION_AUTHORITY_FACTORY);
246: }
247: }
248: }
249: }
250: }
|