001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-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.jdbc;
017:
018: import java.io.IOException;
019: import java.util.HashSet;
020: import java.util.Iterator;
021: import java.util.NoSuchElementException;
022: import java.util.Set;
023: import java.util.logging.Logger;
024:
025: import org.geotools.data.DataSourceException;
026: import org.geotools.data.DefaultQuery;
027: import org.geotools.data.DefaultTransaction;
028: import org.geotools.data.FeatureLockException;
029: import org.geotools.data.FeatureReader;
030: import org.geotools.data.FeatureStore;
031: import org.geotools.data.FeatureWriter;
032: import org.geotools.data.InProcessLockingManager;
033: import org.geotools.data.LockingManager;
034: import org.geotools.data.Query;
035: import org.geotools.data.Transaction;
036: import org.geotools.feature.AttributeType;
037: import org.geotools.feature.Feature;
038: import org.geotools.feature.FeatureCollection;
039: import org.geotools.feature.FeatureType;
040: import org.geotools.feature.IllegalAttributeException;
041: import org.geotools.feature.SimpleFeature;
042: import org.opengis.filter.Filter;
043:
044: /**
045: * This is a starting point for providing your own FeatureStore implementation.
046: *
047: * @author Jody Garnett, Refractions Research
048: *
049: * @task REVISIT: Make modify/add/remove atomic if the transaction is
050: * AUTO_COMMIT. This is done by the start of each of those method
051: * checking to see if the transaction is auto-commit, if it is then they
052: * make a new transaction and pass that to the writer. The writer does
053: * its thing, and then at the end of the method you just commit the
054: * transaction. This way if the writer messes up its changes are rolled
055: * back. The old jdbc datasources supported this, and it'd be nice to
056: * do here as well.
057: * @task UPDATE: made modify atomic as an example, I actually have
058: * the beginings of a smart idea in mind. Similar to
059: * SwingUtilities.runLater...
060: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/jdbc/src/main/java/org/geotools/data/jdbc/JDBCFeatureStore.java $
061: */
062: public class JDBCFeatureStore extends JDBCFeatureSource implements
063: FeatureStore {
064:
065: /** The logger for the postgis module. */
066: private static final Logger LOGGER = org.geotools.util.logging.Logging
067: .getLogger("org.geotools.data.jdbc");
068:
069: /** Current Transaction this FeatureSource is opperating against */
070: protected Transaction transaction = Transaction.AUTO_COMMIT;
071:
072: public JDBCFeatureStore(JDBC1DataStore jdbcDataStore,
073: FeatureType featureType) {
074: super (jdbcDataStore, featureType);
075: }
076:
077: /* Retrieve the Transaction this FeatureSource is opperating against. */
078: public Transaction getTransaction() {
079: return transaction;
080: }
081:
082: /**
083: * Used by subclasses to access locking manager.
084: * <p>
085: * All our implementations here are rely on
086: * FeatureWriter to check the locks.
087: * </p>
088: * <p>
089: * When making your own SQL opperations, have a look at
090: * assertFids( Set fids ), and assertFids( Filter ). You
091: * may use these to check against the lockingManager if one is used.
092: * </p>
093: * If the lockingManager is not used, ie is null, it assumed that you are
094: * making use of native database locks. Or doing your own thing.
095: * </p>
096: * <p>
097: * That is the assertFids functions only when lockingManager is non null.
098: * </p>
099: * @return LockingManager
100: */
101: protected InProcessLockingManager getInProcessLockingManager() {
102: LockingManager lockingManager = getJDBCDataStore()
103: .getLockingManager();
104: if (lockingManager instanceof InProcessLockingManager) {
105: return (InProcessLockingManager) lockingManager;
106: }
107: return null;
108: }
109:
110: protected Set fids(Filter filter) throws NoSuchElementException,
111: IOException, IllegalAttributeException {
112: Set fids = new HashSet();
113: String typeName = getSchema().getTypeName();
114: DefaultQuery query = new DefaultQuery(typeName, filter,
115: Integer.MAX_VALUE, Query.ALL_NAMES, "fids");
116: FeatureReader reader = getJDBCDataStore().getFeatureReader(
117: query, getTransaction());
118: try {
119: while (reader.hasNext()) {
120: fids.add(reader.next().getID());
121: }
122: } finally {
123: reader.close();
124: }
125: return fids;
126: }
127:
128: protected void assertFilter(Filter filter) throws IOException {
129: if (getInProcessLockingManager() == null) {
130: return; // subclass is doing its own thing
131: }
132: Set set = null;
133: try {
134: set = fids(filter);
135: } catch (NoSuchElementException huh) {
136: throw new FeatureLockException("Could not verify filter:",
137: huh);
138: } catch (IllegalAttributeException eh) {
139: throw new FeatureLockException("Could not verify filter:",
140: eh);
141: }
142: assertFids(set);
143: }
144:
145: protected void assertFids(Set fids) throws FeatureLockException {
146: InProcessLockingManager lockingManager = getInProcessLockingManager();
147: if (lockingManager == null) {
148: return; // subclass is doing its own thing
149: }
150: Transaction t = getTransaction();
151: String typeName = getSchema().getTypeName();
152: String fid;
153: for (Iterator i = fids.iterator(); i.hasNext();) {
154: fid = (String) i.next();
155: lockingManager.assertAccess(typeName, fid, transaction);
156: }
157: }
158:
159: //
160: // FeatureStore implementation against DataStore API
161: //
162:
163: /**
164: * Modifies features matching <code>filter</code>.
165: *
166: * <p>
167: * Equivelent to:
168: * </p>
169: * <pre><code>
170: * modifyFeatures( new AttributeType[]{ type, }, new Object[]{ value, }, filter );
171: * </code>
172: * </pre>
173: *
174: * <p>
175: * Subclasses may override this method to perform the appropriate
176: * optimization for this result.
177: * </p>
178: *
179: * @param type Attribute to modify
180: * @param value Modification being made to type
181: * @param filter Identifies features to modify
182: *
183: * @throws IOException
184: */
185: public void modifyFeatures(AttributeType type, Object value,
186: Filter filter) throws IOException {
187: modifyFeatures(new AttributeType[] { type },
188: new Object[] { value }, filter);
189: }
190:
191: /**
192: * Modifies features matching <code>filter</code>.
193: *
194: * <p>
195: * Equivelent to:
196: * </p>
197: * <pre><code>
198: * FeatureWriter writer = dataStore.getFeatureWriter( typeName, filter, transaction );
199: * Feature feature;
200: * while( writer.hasNext() ){
201: * feature = writer.next();
202: * feature.setAttribute( type[0].getName(), value[0] );
203: * feature.setAttribute( type[1].getName(), value[1] );
204: * ...
205: * feature.setAttribute( type[N].getName(), value[N] );
206: * writer.write();
207: * }
208: * writer.close();
209: * </code>
210: * </pre>
211: *
212: * <p>
213: * Subclasses may override this method to perform the appropriate
214: * optimization for this result.
215: * </p>
216: *
217: * @param type Attributes to modify
218: * @param value Modifications being made to type
219: * @param filter Identifies features to modify
220: *
221: * @throws IOException
222: */
223: public void modifyFeatures(AttributeType[] type, Object[] value,
224: Filter filter) throws IOException {
225: String typeName = getSchema().getTypeName();
226:
227: if (getTransaction() == Transaction.AUTO_COMMIT) {
228: // implement as atomic
229: Transaction atomic = new DefaultTransaction();
230: try {
231: FeatureWriter writer = getDataStore().getFeatureWriter(
232: typeName, filter, atomic);
233: modifyFeatures(type, value, writer);
234: atomic.commit();
235: } catch (Throwable t) {
236: atomic.rollback();
237: } finally {
238: atomic.close();
239: }
240: } else {
241: FeatureWriter writer = getDataStore().getFeatureWriter(
242: typeName, filter, getTransaction());
243: modifyFeatures(type, value, writer);
244: }
245: }
246:
247: protected void modifyFeatures(AttributeType[] type, Object[] value,
248: FeatureWriter writer) throws DataSourceException,
249: IOException {
250: Feature feature;
251: try {
252: while (writer.hasNext()) {
253: feature = writer.next();
254:
255: for (int i = 0; i < type.length; i++) {
256: try {
257: feature.setAttribute(type[i].getName(),
258: value[i]);
259: } catch (IllegalAttributeException e) {
260: throw new DataSourceException(
261: "Could not update feature "
262: + feature.getID() + " with "
263: + type[i].getName() + "="
264: + value[i], e);
265: }
266: }
267:
268: writer.write();
269: }
270: } finally {
271: writer.close();
272: }
273: }
274:
275: /**
276: * Add Features from reader to this FeatureStore.
277: *
278: * <p>
279: * Equivelent to:
280: * </p>
281: * <pre><code>
282: * Set set = new HashSet();
283: * FeatureWriter writer = dataStore.getFeatureWriter( typeName, true, transaction );
284: * Featrue feature, newFeature;
285: * while( reader.hasNext() ){
286: * feature = reader.next();
287: * newFeature = writer.next();
288: * newFeature.setAttributes( feature.getAttribtues( null ) );
289: * writer.write();
290: * set.add( newfeature.getID() );
291: * }
292: * reader.close();
293: * writer.close();
294: *
295: * return set;
296: * </code>
297: * </pre>
298: *
299: * <p>
300: * (If you don't have a FeatureReader handy DataUtilities.reader() may be
301: * able to help out)
302: * </p>
303: *
304: * <p>
305: * Subclasses may override this method to perform the appropriate
306: * optimization for this result.
307: * </p>
308: *
309: * @param reader
310: *
311: * @return The Set of FeatureIDs added
312: *
313: * @throws IOException
314: *
315: * @see org.geotools.data.FeatureStore#addFeatures(org.geotools.data.FeatureReader)
316: */
317: public Set addFeatures(FeatureReader reader) throws IOException {
318: Set addedFids = new HashSet();
319: String typeName = getSchema().getTypeName();
320: Feature feature = null;
321: SimpleFeature newFeature;
322: FeatureWriter writer = getDataStore().getFeatureWriterAppend(
323: typeName, getTransaction());
324:
325: try {
326: while (reader.hasNext()) {
327: try {
328: feature = reader.next();
329: } catch (Exception e) {
330: throw new DataSourceException(
331: "Could not add Features, problem with provided reader",
332: e);
333: }
334:
335: newFeature = (SimpleFeature) writer.next();
336:
337: try {
338: newFeature.setAttributes(feature
339: .getAttributes(null));
340: } catch (IllegalAttributeException writeProblem) {
341: throw new DataSourceException("Could not create "
342: + typeName + " out of provided feature: "
343: + feature.getID(), writeProblem);
344: }
345:
346: writer.write();
347: addedFids.add(newFeature.getID());
348: }
349: } finally {
350: reader.close();
351: writer.close();
352: }
353: return addedFids;
354: }
355:
356: public Set addFeatures(FeatureCollection collection)
357: throws IOException {
358: Set addedFids = new HashSet();
359: String typeName = getSchema().getTypeName();
360: Feature feature = null;
361: SimpleFeature newFeature;
362: FeatureWriter writer = getDataStore().getFeatureWriterAppend(
363: typeName, getTransaction());
364:
365: Iterator iterator = collection.iterator();
366: try {
367: while (iterator.hasNext()) {
368: try {
369: feature = (Feature) iterator.next();
370: } catch (Exception e) {
371: throw new DataSourceException(
372: "Could not add Features, problem with provided reader",
373: e);
374: }
375:
376: newFeature = (SimpleFeature) writer.next();
377:
378: try {
379: //JD: we may have a case that the source feature type does not
380: //match exactly the target feature type, so build attributes
381: // based oin target
382: //newFeature.setAttributes(feature.getAttributes(null));
383: Object[] attributes = new Object[newFeature
384: .getNumberOfAttributes()];
385: for (int i = 0; i < attributes.length; i++) {
386: AttributeType type = newFeature
387: .getFeatureType().getAttributeType(i);
388: attributes[i] = feature.getAttribute(type
389: .getName());
390: }
391: newFeature.setAttributes(attributes);
392:
393: } catch (IllegalAttributeException writeProblem) {
394: throw new DataSourceException("Could not create "
395: + typeName + " out of provided feature: "
396: + feature.getID(), writeProblem);
397: }
398:
399: writer.write();
400: addedFids.add(newFeature.getID());
401: }
402: } finally {
403: collection.close(iterator);
404: writer.close();
405: }
406:
407: return addedFids;
408: }
409:
410: /**
411: * Removes features indicated by provided filter.
412: *
413: * <p>
414: * Equivelent to:
415: * </p>
416: * <pre><code>
417: * FeatureWriter writer = dataStore.getFeatureWriter( typeName, filter, transaction );
418: * Feature feature;
419: * while( writer.hasNext() ){
420: * feature = writer.next();
421: * writer.remove();
422: * }
423: * writer.close();
424: * </code>
425: * </pre>
426: *
427: * <p>
428: * Subclasses may override this method to perform the appropriate
429: * optimization for this result.
430: * </p>
431: *
432: * @param filter Identifies features to remove
433: *
434: * @throws IOException
435: */
436: public void removeFeatures(Filter filter) throws IOException {
437: String typeName = getSchema().getTypeName();
438: FeatureWriter writer = getDataStore().getFeatureWriter(
439: typeName, filter, getTransaction());
440: Feature feature;
441:
442: try {
443:
444: while (writer.hasNext()) {
445: feature = writer.next();
446: writer.remove();
447: }
448: } finally {
449: writer.close();
450: }
451: }
452:
453: /**
454: * Replace with contents of reader.
455: *
456: * <p>
457: * Equivelent to:
458: * </p>
459: * <pre><code>
460: * FeatureWriter writer = dataStore.getFeatureWriter( typeName, false, transaction );
461: * Feature feature, newFeature;
462: * while( writer.hasNext() ){
463: * feature = writer.next();
464: * writer.remove();
465: * }
466: * while( reader.hasNext() ){
467: * newFeature = reader.next();
468: * feature = writer.next();
469: * newFeature.setAttributes( feature.getAttributes( null ) );
470: * writer.write();
471: * }
472: * reader.close();
473: * writer.close();
474: * </code>
475: * </pre>
476: *
477: * <p>
478: * Subclasses may override this method to perform the appropriate
479: * optimization for this result.
480: * </p>
481: *
482: * @param reader Contents to replace with
483: *
484: * @throws IOException
485: */
486: public void setFeatures(FeatureReader reader) throws IOException {
487: String typeName = getSchema().getTypeName();
488: FeatureWriter writer = getDataStore().getFeatureWriter(
489: typeName, getTransaction());
490: Feature feature;
491: SimpleFeature newFeature;
492:
493: try {
494: while (writer.hasNext()) {
495: feature = writer.next();
496: LOGGER.finer("removing feature " + feature);
497: writer.remove();
498: }
499:
500: while (reader.hasNext()) {
501: try {
502: feature = reader.next();
503: } catch (Exception readProblem) {
504: throw new DataSourceException(
505: "Could not add Features, problem with provided reader",
506: readProblem);
507: }
508:
509: newFeature = (SimpleFeature) writer.next();
510:
511: try {
512: newFeature.setAttributes(feature
513: .getAttributes(null));
514: } catch (IllegalAttributeException writeProblem) {
515: throw new DataSourceException("Could not create "
516: + typeName + " out of provided feature: "
517: + feature.getID(), writeProblem);
518: }
519: LOGGER.finer("writing feature " + newFeature);
520: writer.write();
521: }
522: } finally {
523: reader.close();
524: writer.close();
525: }
526: }
527:
528: public void setTransaction(Transaction transaction) {
529: if (transaction == null) {
530: throw new IllegalArgumentException(
531: "Transaction cannot be null, did you mean Transaction.AUTO_COMMIT?");
532: }
533:
534: this.transaction = transaction;
535: }
536: }
|