001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data;
017:
018: import java.io.IOException;
019: import java.util.Collections;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.logging.Logger;
026:
027: /**
028: * Quick implementation of Transaction api.
029: * <p>
030: * Please see Transaction interface for an outline of what this class is all about.
031: * </p>
032: *
033: * @author Jody Garnett, Refractions Research
034: * @source $URL:
035: * http://svn.geotools.org/geotools/trunk/gt/modules/library/main/src/main/java/org/geotools/data/DefaultTransaction.java $
036: */
037: public class DefaultTransaction implements Transaction {
038: /** The logger for the data module. */
039: protected static final Logger LOGGER = org.geotools.util.logging.Logging
040: .getLogger("org.geotools.data");
041:
042: /** Records State by key */
043: Map stateLookup = new HashMap();
044:
045: /** Records properties by key */
046: Map propertyLookup = new HashMap();
047:
048: /** Handle used to identify Transaction for the user */
049: String handle;
050:
051: /** Records current Authorizations */
052: Set authorizations = new HashSet();
053:
054: public DefaultTransaction() {
055: Throwable t = new Throwable("who called me?");
056: StackTraceElement e = t.getStackTrace()[1];
057: handle = e.getClassName() + "." + e.getMethodName()
058: + " Transaction";
059: }
060:
061: public DefaultTransaction(String handle) {
062: this .handle = handle;
063: }
064:
065: /**
066: * Remembers Externalized State for a DataSource.
067: *
068: * <p>
069: * This is the GOF Momento pattern: a FeatureSource is able to externalize its internal State
070: * required for Transaction support and have this class manage it. It may retrieve this State
071: * with getState( key ).
072: * </p>
073: *
074: * <p>
075: * In addition several FeatureSource implementations may share State, a common example is
076: * JDBCDataSources keeping a shared JDBC connection using the JDBC URL as a key.
077: * </p>
078: *
079: * @param key
080: * Key used to externalize State
081: * @param state
082: * Externalized State (Momeneto)
083: *
084: * @throws IllegalArgumentException
085: * When Transaction already using key
086: *
087: * @see org.geotools.data.Transaction#putState(java.lang.Object,
088: * org.geotools.data.Transaction.State)
089: */
090: public void putState(Object key, State state) {
091: if (stateLookup.containsKey(key)) {
092: State current = (State) stateLookup.get(key);
093:
094: if (state == current) {
095: throw new IllegalArgumentException(
096: "Transaction already has an this State for key: "
097: + key
098: + ". Please check for existing State before creating your own.");
099: } else {
100: throw new IllegalArgumentException(
101: "Transaction already has an entry for key:"
102: + key
103: + ". Please check for existing State before creating your own.");
104: }
105: } else {
106: stateLookup.put(key, state);
107:
108: // allow configuration
109: state.setTransaction(this );
110: }
111: }
112:
113: /**
114: * Removes state from DefaultTransaction's care.
115: *
116: * <p>
117: * Currently does not complain if there is no State associated with key to remove - this may
118: * change in the future.
119: * </p>
120: *
121: * @param key
122: *
123: * @throws IllegalArgumentException
124: * If no State was maintained for supplied <code>key</code>
125: *
126: * @see org.geotools.data.Transaction#removeState(java.lang.Object)
127: */
128: public void removeState(Object key) {
129: if (stateLookup == null) {
130: throw new IllegalStateException(
131: "Transaction has been closed");
132: }
133: if (stateLookup.containsKey(key)) {
134: State state = (State) stateLookup.remove(key);
135: state.setTransaction(null);
136: } else {
137: throw new IllegalArgumentException(
138: "Transaction does not no anything about key:" + key
139: + ". Has this key already been removed?");
140: }
141: }
142:
143: /**
144: * Returns externalized state or <code>null</code> if not available.
145: *
146: * <p>
147: * Used by DataStore implementations to externalize information required for Transaction support
148: * using the GOF Momento pattern.
149: * </p>
150: *
151: * @param key
152: *
153: * @return Previously externalized State.
154: *
155: * @see org.geotools.data.Transaction#getState(java.lang.Object)
156: */
157: public State getState(Object key) {
158: if (stateLookup == null) {
159: throw new IllegalStateException(
160: "Transaction has been closed");
161: }
162: return (State) stateLookup.get(key);
163: }
164:
165: /**
166: * Commits all modifications against this Transaction.
167: *
168: * <p>
169: * This implementation will call commit() on all State managed by this Transaction. This allows
170: * DataStores to provide their own implementation of commit().
171: * </p>
172: *
173: * @throws IOException
174: * Encountered problem maintaining transaction state
175: * @throws DataSourceException
176: * See IOException
177: *
178: * @see org.geotools.data.Transaction#commit()
179: */
180: public void commit() throws IOException {
181: State state;
182: int problemCount = 0;
183: IOException io = null;
184:
185: for (Iterator i = stateLookup.values().iterator(); i.hasNext();) {
186: state = (State) i.next();
187:
188: try {
189: state.commit();
190: } catch (IOException e) {
191: problemCount++;
192: io = e;
193: }
194: }
195:
196: if (io != null) {
197: if (problemCount == 1) {
198: throw io;
199: }
200:
201: throw new DataSourceException("Commit encountered "
202: + problemCount + " problems - the first was", io);
203: }
204: authorizations.clear();
205: }
206:
207: /**
208: * Rollsback all modifications against this Transaction.
209: *
210: * <p>
211: * This implementation will call rollback() on all State managed by this Transaction. This
212: * allows DataStores to provide their own implementation of rollback().
213: * </p>
214: *
215: * @throws IOException
216: * Encountered problem maintaining transaction State
217: * @throws DataSourceException
218: * IOException
219: *
220: * @see org.geotools.data.Transaction#rollback()
221: */
222: public void rollback() throws IOException {
223: int problemCount = 0;
224: IOException io = null;
225: State state;
226:
227: for (Iterator i = stateLookup.values().iterator(); i.hasNext();) {
228: state = (State) i.next();
229:
230: try {
231: state.rollback();
232: } catch (IOException e) {
233: problemCount++;
234: io = e;
235: }
236: }
237:
238: if (io != null) {
239: if (problemCount == 1) {
240: throw io;
241: }
242:
243: throw new DataSourceException("Rollback encountered "
244: + problemCount + " problems - the first was", io);
245: }
246: authorizations.clear();
247: }
248:
249: /**
250: * Frees all State held by this Transaction.
251: */
252: public synchronized void close() {
253: for (Iterator i = stateLookup.values().iterator(); i.hasNext();) {
254: State state = (State) i.next();
255: state.setTransaction(null);
256: }
257: stateLookup.clear();
258: stateLookup = null;
259: authorizations.clear();
260: authorizations = null;
261: propertyLookup.clear();
262: propertyLookup = null;
263: }
264:
265: /**
266: * The current set of Authorization IDs held by this Transaction.
267: *
268: * <p>
269: * This set is reset by the next call to commit or rollback.
270: * </p>
271: *
272: * @return Set of Authorization IDs
273: */
274: public Set getAuthorizations() {
275: if (authorizations == null) {
276: throw new IllegalStateException(
277: "Transaction has been closed");
278: }
279: return Collections.unmodifiableSet(authorizations);
280: }
281:
282: /**
283: * Provides an authorization ID allowing access to locked Features.
284: *
285: * <p>
286: * Remember authorizations are cleared after every commit/rollback.
287: * </p>
288: *
289: * @param authID
290: * Provided Authorization ID
291: *
292: * @throws IOException
293: * Encountered problems maintaing Transaction State
294: * @throws DataSourceException
295: * See IOException
296: *
297: * @see org.geotools.data.Transaction#setAuthorization(java.lang.String)
298: */
299: public void addAuthorization(String authID) throws IOException {
300: if (authorizations == null) {
301: throw new IllegalStateException(
302: "Transaction has been closed");
303: }
304: int problemCount = 0;
305: IOException io = null;
306: State state;
307: authorizations.add(authID);
308:
309: for (Iterator i = stateLookup.values().iterator(); i.hasNext();) {
310: state = (State) i.next();
311:
312: try {
313: state.addAuthorization(authID);
314: } catch (IOException e) {
315: problemCount++;
316: io = e;
317: }
318: }
319:
320: if (io != null) {
321: if (problemCount == 1) {
322: throw io;
323: }
324: throw new DataSourceException(
325: "setAuthorization encountered " + problemCount
326: + " problems - the first was", io);
327: }
328: }
329:
330: /*
331: * (non-Javadoc)
332: *
333: * @see java.lang.Object#toString()
334: */
335: public String toString() {
336: return handle;
337: }
338:
339: /**
340: * Implementation of getProperty.
341: *
342: * @see org.geotools.data.Transaction#getProperty(java.lang.Object)
343: *
344: * @param key
345: */
346: public Object getProperty(Object key) {
347: if (propertyLookup == null) {
348: throw new IllegalStateException(
349: "Transaction has been closed");
350: }
351: return propertyLookup.get(key);
352: }
353:
354: /**
355: * Implementation of addProperty.
356: *
357: * @see org.geotools.data.Transaction#addProperty(java.lang.Object, java.lang.Object)
358: *
359: * @param key
360: * @param value
361: * @throws IOException
362: */
363: public void putProperty(Object key, Object value)
364: throws IOException {
365: if (propertyLookup == null) {
366: throw new IllegalStateException(
367: "Transaction has been closed");
368: }
369: propertyLookup.put(key, value);
370: }
371:
372: protected void finalize() throws Throwable {
373: if (stateLookup != null) {
374: LOGGER
375: .severe("There's code leaving transaction unclosed. "
376: + "Call Transaction.close() after using them to ensure they do not hold state "
377: + "such as JDCB connections or file handles");
378: LOGGER.severe("Unclosed transaction handle is '" + handle
379: + "'");
380: close();
381: }
382: super.finalize();
383: }
384: }
|