/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2003-2016, 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. */ package org.geotools.data.wfs.integration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.data.DataSourceException; import org.geotools.data.DataStore; import org.geotools.data.DataUtilities; import org.geotools.data.DefaultTransaction; import org.geotools.data.DiffFeatureReader; import org.geotools.data.FeatureEvent; import org.geotools.data.FeatureListener; import org.geotools.data.FeatureLock; import org.geotools.data.FeatureLocking; import org.geotools.data.FeatureReader; import org.geotools.data.FeatureWriter; import org.geotools.data.FilteringFeatureReader; import org.geotools.data.FilteringFeatureWriter; import org.geotools.data.InProcessLockingManager; import org.geotools.data.Query; import org.geotools.data.Transaction; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.simple.SimpleFeatureStore; import org.geotools.data.wfs.internal.WFSException; import org.geotools.factory.CommonFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.util.logging.Logging; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.filter.Id; import org.opengis.geometry.BoundingBox; import com.vividsolutions.jts.geom.MultiLineString; /** * Abstract Integration Test. * * @author Jesse Eichar, Refractions Research * @author Niels Charlier * * * @source $URL$ */ public abstract class AbstractIntegrationTest { /** The logger for the filter module. */ private final Logger LOGGER = Logging.getLogger(getClass()); protected DataStore data; protected static class TestDataType { public String typeName; public SimpleFeatureType featureType; public String stringAttribute; public SimpleFeature newFeature; public int numberOfFeatures; public TestDataType() {} SimpleFeature[] features; Filter feat1Filter; Filter feat2Filter; Filter feat12Filter; ReferencedEnvelope bounds; ReferencedEnvelope feat12Bounds; } protected TestDataType first; protected TestDataType second; public AbstractIntegrationTest() { } /** * Creates and populates a new instance of the datastore. The datastore must not have a roads or * rivers type. * <p> * Something like the following should suffice: * * <pre> * <code> * DataStore data = .... * data.createSchema(roadType); * data.createSchema(riverType); * * SimpleFeatureStore roads; * roads = ((SimpleFeatureStore) data.getFeatureSource(getRoadTypeName())); * * roads.addFeatures(DataUtilities.collection(roadFeatures)); * * SimpleFeatureStore rivers = ((SimpleFeatureStore) data.getFeatureSource(riverType * .getTypeName())); * * rivers.addFeatures(DataUtilities.collection(riverFeatures)); * * return data; * </code> * </pre> * * @throws Exception */ public abstract DataStore createDataStore() throws Exception; /** * This method must remove the roads and rivers types from the datastore. It must also close all * connections to the datastore if it has connections and get rid of any temporary files. * * @throws Exception */ public abstract DataStore tearDownDataStore(DataStore data) throws Exception; protected abstract TestDataType createFirstType() throws Exception; protected abstract TestDataType createSecondType() throws Exception; private void completeTestDataType(TestDataType test) throws IOException { FilterFactory2 ff = (FilterFactory2) CommonFactoryFinder.getFilterFactory2(null); SimpleFeatureSource source = data.getFeatureSource(test.typeName); test.features = grabArray(source.getFeatures()); test.feat1Filter = ff.id(ff.featureId(test.features[0].getID())); test.feat2Filter = ff.id(ff.featureId(test.features[1].getID())); test.feat12Filter = ff.id(ff.featureId(test.features[0].getID()), ff.featureId(test.features[1].getID())); test.bounds = new ReferencedEnvelope(test.features[0].getFeatureType().getCoordinateReferenceSystem()); for (int i=0; i<test.numberOfFeatures; i++) { test.bounds.expandToInclude( new ReferencedEnvelope(test.features[i].getBounds()) ); } test.feat12Bounds = new ReferencedEnvelope(test.features[0].getFeatureType().getCoordinateReferenceSystem()); test.feat12Bounds.expandToInclude(new ReferencedEnvelope(test.features[0].getBounds())); test.feat12Bounds.expandToInclude(new ReferencedEnvelope(test.features[1].getBounds())); } @Before public void setUp() throws Exception { first = createFirstType(); second = createSecondType(); try { data = createDataStore(); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Exception while making schema", e); throw e; } completeTestDataType(first); completeTestDataType(second); } @After public void tearDown() throws Exception { tearDownDataStore(data); data = null; } @Test public void testFeatureEvents() throws Exception { SimpleFeatureStore store1 = (SimpleFeatureStore) data.getFeatureSource(first.typeName); SimpleFeatureStore store2 = (SimpleFeatureStore) data.getFeatureSource(first.typeName); try( DefaultTransaction transaction = new DefaultTransaction() ){ store1.setTransaction(transaction); class Listener implements FeatureListener { List<FeatureEvent> events = new ArrayList<FeatureEvent>(); public void changed(FeatureEvent featureEvent) { this.events.add(featureEvent); } FeatureEvent getEvent(int i) { return (FeatureEvent) events.get(i); } } Listener listener1 = new Listener(); Listener listener2 = new Listener(); store1.addFeatureListener(listener1); store2.addFeatureListener(listener2); FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(); // test that only the listener listening with the current transaction gets the event. final SimpleFeature feature = first.features[0]; Id fidFilter = ff.id(feature.getIdentifier()); store1.removeFeatures(fidFilter); assertEquals(1, listener1.events.size()); assertEquals(0, listener2.events.size()); FeatureEvent event = listener1.getEvent(0); assertEquals(feature.getBounds(), event.getBounds()); assertEquals(FeatureEvent.Type.REMOVED, event.getType()); // test that commit only sends events to listener2. listener1.events.clear(); listener2.events.clear(); store1.getTransaction().commit(); assertEquals(0, listener1.events.size()); assertEquals(3, listener2.events.size()); event = listener2.getEvent(0); assertEquals(feature.getBounds(), event.getBounds()); assertEquals(FeatureEvent.Type.REMOVED, event.getType()); // test add same as modify listener1.events.clear(); listener2.events.clear(); store1.addFeatures(DataUtilities.collection(feature)); assertEquals(1, listener1.events.size()); event = listener1.getEvent(0); assertEquals(feature.getBounds(), event.getBounds()); assertEquals(FeatureEvent.Type.ADDED, event.getType()); assertEquals(0, listener2.events.size()); // test that rollback only sends events to listener1. listener1.events.clear(); listener2.events.clear(); store1.getTransaction().rollback(); assertEquals(1, listener1.events.size()); event = listener1.getEvent(0); assertNull(event.getBounds()); assertEquals(FeatureEvent.Type.CHANGED, event.getType()); assertEquals(0, listener2.events.size()); } } @Test public void testFixture() throws Exception { SimpleFeatureType type = DataUtilities.createType("namespace.typename", "name:String,id:0,geom:MultiLineString"); assertEquals("namespace", "namespace", type.getName().getNamespaceURI()); assertEquals("typename", "typename", type.getTypeName()); assertEquals("attributes", 3, type.getAttributeCount()); AttributeDescriptor[] a = type.getAttributeDescriptors().toArray( new AttributeDescriptor[type.getAttributeCount()]); assertEquals("a1", "name", a[0].getLocalName()); assertEquals("a1", String.class, a[0].getType().getBinding()); assertEquals("a2", "id", a[1].getLocalName()); assertEquals("a2", Integer.class, a[1].getType().getBinding()); assertEquals("a3", "geom", a[2].getLocalName()); assertEquals("a3", MultiLineString.class, a[2].getType().getBinding()); } @Test public void testGetFeatureTypes() { String[] names; try { names = data.getTypeNames(); assertTrue(contains(names, first.typeName)); assertTrue(contains(names, second.typeName)); } catch (IOException e) { e.printStackTrace(); fail("Fail with an IOException trying to getTypeNames()"); } } boolean contains(Object[] array, Object expected) { if ((array == null) || (array.length == 0)) { return false; } for (int i = 0; i < array.length; i++) { if (array[i].equals(expected)) { return true; } } return false; } /** * Checks that a given SimpleFeatureCollection contains the specified feature * * @param fc * the feature collection we're going to search * @param f * the feature we're looking for * * @return true if the feature is in the feature collection, false otherwise */ boolean containsFeatureCollection(SimpleFeatureCollection fc, SimpleFeature f) { if ((fc == null) || fc.isEmpty()) { return false; } return containsFeature(fc.toArray(), f); } /** * Checks that a given Feature array contains the specified feature * * @param array * must be an array of features * @param expected * the expected feature we're looking for * * @return true if the feature is found, false otherwise */ boolean containsFeature(Object[] array, Object expected) { if ((array == null) || (array.length == 0)) { return false; } for (int i = 0; i < array.length; i++) { if (isFeatureEqual((SimpleFeature) array[i], (SimpleFeature) expected)) { return true; } } return false; } /** * This function is stolen from DefaultFeature equals method. We want to check for equality * except for featureId which we expect to be different. * * @param feature1 * the Feature to test against. * @param feature2 * the Feature to test for equality. * * @return <code>true</code> if the object is equal, <code>false</code> otherwise. */ public boolean isFeatureEqual(SimpleFeature feature1, SimpleFeature feature2) { if (feature2 == null) { return false; } if (feature2 == feature1) { return true; } if (!feature2.getFeatureType().equals(feature1.getFeatureType())) { return false; } for (int i = 0, ii = feature1.getAttributeCount(); i < ii; i++) { Object otherAtt = feature2.getAttribute(i); if (feature1.getAttribute(i) == null) { if (otherAtt != null) { return false; } } else { if (!feature1.getAttribute(i).equals(otherAtt)) { return false; } } } return true; } /** * Like contain but based on match rather than equals */ boolean containsLax(SimpleFeature[] array, SimpleFeature expected) { if ((array == null) || (array.length == 0)) { return false; } //SimpleFeatureType type = expected.getFeatureType(); for (int i = 0; i < array.length; i++) { if (match(array[i], expected)) { return true; } } return false; } /** * Compare based on attributes not getID allows comparison of Diff contents */ boolean match(SimpleFeature expected, SimpleFeature actual) { SimpleFeatureType type = expected.getFeatureType(); for (int i = 0; i < type.getAttributeCount(); i++) { Object av = actual.getAttribute(i); Object ev = expected.getAttribute(i); if ((av == null) && (ev != null)) { return false; } else if ((ev == null) && (av != null)) { return false; } else if (!av.equals(ev)) { return false; } } return true; } @Test public void testGetSchema() throws IOException { SimpleFeatureType firstSchema = data.getSchema(first.typeName); SimpleFeatureType secondSchema = data.getSchema(second.typeName); assertNotNull(firstSchema); assertNotNull(secondSchema); assertEquals(first.typeName, firstSchema.getTypeName()); assertEquals(second.typeName, secondSchema.getTypeName()); assertSchemaEqual(first.featureType, firstSchema); assertSchemaEqual(second.featureType, secondSchema); } void assertSchemaEqual(SimpleFeatureType expected, SimpleFeatureType actual) { assertEquals(second.featureType.getAttributeCount(), actual.getAttributeCount()); for (int i = 0; i < first.featureType.getAttributeCount(); i++) { assertEquals(expected.getDescriptor(i), actual.getDescriptor(i)); } } void assertCovers(String msg, SimpleFeatureCollection c1, SimpleFeatureCollection c2) { if (c1 == c2) { return; } assertNotNull(msg, c1); assertNotNull(msg, c2); assertEquals(msg + " size", c1.size(), c2.size()); SimpleFeature f; for (SimpleFeatureIterator i = c1.features(); i.hasNext();) { f = i.next(); assertTrue(msg + " " + f.getID(), containsFeatureCollection(c2, f)); // c2.contains(f)); } } @Test public void testGetFeatureReader() throws Exception { Query query = new Query(first.typeName); FeatureReader<SimpleFeatureType, SimpleFeature> reader; reader = data.getFeatureReader(query, Transaction.AUTO_COMMIT); assertCovered(first.features, reader); assertEquals(false, reader.hasNext()); } @Test public void testGetFeatureReaderMutability() throws IOException { Query query = new Query(first.typeName); FeatureReader<SimpleFeatureType, SimpleFeature> reader = data.getFeatureReader(query, Transaction.AUTO_COMMIT); SimpleFeature feature; while (reader.hasNext()) { feature = (SimpleFeature) reader.next(); feature.setAttribute(first.stringAttribute, null); } reader.close(); reader = data.getFeatureReader(query, Transaction.AUTO_COMMIT); while (reader.hasNext()) { feature = (SimpleFeature) reader.next(); assertNotNull(feature.getAttribute(first.stringAttribute)); } reader.close(); try { reader.next(); fail("next should fail with an IOException or NoSuchElementException"); } catch (IOException expected) { } catch (NoSuchElementException expected) { } } @Test public void testGetFeatureReaderConcurency() throws Exception { Query query = new Query(first.typeName); FeatureReader<SimpleFeatureType, SimpleFeature> reader1 = data.getFeatureReader(query, Transaction.AUTO_COMMIT); FeatureReader<SimpleFeatureType, SimpleFeature> reader2 = data.getFeatureReader(query, Transaction.AUTO_COMMIT); query = new Query(second.typeName); FeatureReader<SimpleFeatureType, SimpleFeature> reader3 = data.getFeatureReader(query, Transaction.AUTO_COMMIT); while (reader1.hasNext() || reader2.hasNext() || reader3.hasNext()) { assertTrue(containsFeature(first.features, reader1.next())); assertTrue(containsFeature(first.features, reader2.next())); if (reader3.hasNext()) { assertTrue(containsFeature(second.features, reader3.next())); } } try { reader1.next(); fail("next should fail with an IOException or NoSuchElementException"); } catch (IOException expected) { } catch (NoSuchElementException expected) { } try { reader2.next(); fail("next should fail with an IOException or NoSuchElementException"); } catch (IOException expected) { } catch (NoSuchElementException expected) { } try { reader3.next(); fail("next should fail with an IOException or NoSuchElementException"); } catch (IOException expected) { } catch (NoSuchElementException expected) { } reader1.close(); reader2.close(); reader3.close(); } @Test public void testGetFeatureReaderFilterAutoCommit() throws Exception { SimpleFeatureType type = data.getSchema(first.typeName); FeatureReader<SimpleFeatureType, SimpleFeature> reader; Query query = new Query(first.typeName); reader = data.getFeatureReader(query, Transaction.AUTO_COMMIT); assertFalse(reader instanceof FilteringFeatureReader); assertEquals(type, reader.getFeatureType()); assertEquals(first.features.length, count(reader)); reader = data.getFeatureReader(new Query(first.typeName, Filter.EXCLUDE), Transaction.AUTO_COMMIT); assertEquals(type, reader.getFeatureType()); assertEquals(0, count(reader)); reader = data.getFeatureReader(new Query(first.typeName, first.feat1Filter), Transaction.AUTO_COMMIT); assertEquals(type, reader.getFeatureType()); assertEquals(1, count(reader)); } @Test public void testGetFeatureReaderFilterTransaction() throws Exception { try( Transaction t = new DefaultTransaction() ){ SimpleFeatureType type = data.getSchema(first.typeName); FeatureReader<SimpleFeatureType, SimpleFeature> reader; reader = data.getFeatureReader(new Query(first.typeName, Filter.EXCLUDE), t); assertEquals(type, reader.getFeatureType()); assertEquals(0, count(reader)); reader = data.getFeatureReader(new Query(first.typeName), t); assertTrue(reader instanceof DiffFeatureReader); assertEquals(type, reader.getFeatureType()); assertEquals(first.features.length, count(reader)); reader = data.getFeatureReader(new Query(first.typeName, first.feat1Filter), t); // assertTrue(reader instanceof DiffFeatureReader);//Currently wrapped by a filtering // feature reader assertEquals(type, reader.getFeatureType()); assertEquals(1, count(reader)); SimpleFeatureStore store = (SimpleFeatureStore) data.getFeatureSource(first.typeName); store.setTransaction(t); store.removeFeatures(first.feat1Filter); reader = data.getFeatureReader(new Query(first.typeName, Filter.EXCLUDE), t); assertEquals(0, count(reader)); reader = data.getFeatureReader(new Query(first.typeName), t); assertEquals(first.features.length - 1, count(reader)); reader = data.getFeatureReader(new Query(first.typeName, first.feat1Filter), t); assertEquals(0, count(reader)); t.rollback(); reader = data.getFeatureReader(new Query(first.typeName, Filter.EXCLUDE), t); assertEquals(0, count(reader)); reader = data.getFeatureReader(new Query(first.typeName), t); assertEquals(first.features.length, count(reader)); reader = data.getFeatureReader(new Query(first.typeName, first.feat1Filter), t); assertEquals(1, count(reader)); } } void assertCovered(SimpleFeature[] features, FeatureReader<SimpleFeatureType, SimpleFeature> reader) throws Exception { int count = 0; try { while (reader.hasNext()) { assertTrue(containsFeature(features, reader.next())); count++; } } finally { // This is not a good idea, since later tests try and run a reader.hasNext() !! // reader.close(); } assertEquals(features.length, count); } /** * Ensure that FeatureReader<SimpleFeatureType, SimpleFeature> reader contains extactly the * contents of array. */ boolean covers(SimpleFeatureCollection features, SimpleFeature[] array) throws Exception { SimpleFeature feature; int count = 0; SimpleFeatureIterator i = features.features(); try { while (i.hasNext()) { feature = (SimpleFeature) i.next(); if (!containsFeature(array, feature)) { return false; } count++; } } finally { i.close(); } return count == array.length; } /** * Ensure that FeatureReader<SimpleFeatureType, SimpleFeature> reader contains extactly the * contents of array. * */ boolean covers(FeatureReader<SimpleFeatureType, SimpleFeature> reader, SimpleFeature[] array) throws Exception { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); assertNotNull("feature", feature); if (!containsFeature(array, feature)) { fail("feature " + feature.getID() + " not listed"); return false; } count++; } } finally { reader.close(); } assertEquals("covers", count, array.length); return true; } boolean covers(SimpleFeatureIterator reader, SimpleFeature[] array) throws NoSuchElementException, IOException { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); assertNotNull("feature", feature); if (!containsFeature(array, feature)) { fail("feature " + feature.getID() + " not listed"); return false; } count++; } } finally { reader.close(); } assertEquals("covers", count, array.length); return true; } boolean coversLax(FeatureReader<SimpleFeatureType, SimpleFeature> reader, SimpleFeature[] array) throws Exception { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); if (!containsLax(array, feature)) { return false; } count++; } } finally { reader.close(); } return count == array.length; } boolean coversLax(SimpleFeatureIterator reader, SimpleFeature[] array) throws Exception { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); if (!containsLax(array, feature)) { return false; } count++; } } finally { reader.close(); } return count == array.length; } void dump(FeatureReader<SimpleFeatureType, SimpleFeature> reader) throws Exception { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); LOGGER.fine(count + " feature:" + feature); count++; } } finally { reader.close(); } } void dump(Object[] array) { for (int i = 0; i < array.length; i++) { LOGGER.fine(i + " feature:" + array[i]); } } /* * Test for FeatureWriter getFeatureWriter(String, Filter, Transaction) */ @Test public void testGetFeatureWriter() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter( first.typeName, Transaction.AUTO_COMMIT); assertEquals(first.features.length, count(writer)); try { writer.hasNext(); fail("Should not be able to use a closed writer"); } catch (IOException expected) { } try { writer.next(); fail("Should not be able to use a closed writer"); } catch (IOException expected) { } } @Test public void testGetFeatureWriterRemove() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter( first.typeName, Transaction.AUTO_COMMIT); SimpleFeature feature; while (writer.hasNext()) { feature = writer.next(); if (feature.getID().equals(first.features[0].getID())) { writer.remove(); } } writer = data.getFeatureWriter(first.typeName, Transaction.AUTO_COMMIT); assertEquals(first.features.length - 1, count(writer)); } @Test public void testGetFeaturesWriterAdd() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter( first.typeName, Transaction.AUTO_COMMIT); SimpleFeature feature; while (writer.hasNext()) { feature = (SimpleFeature) writer.next(); } assertFalse(writer.hasNext()); feature = (SimpleFeature) writer.next(); feature.setAttributes(first.newFeature.getAttributes()); writer.write(); assertFalse(writer.hasNext()); writer = data.getFeatureWriter(first.typeName, Transaction.AUTO_COMMIT); assertEquals(first.features.length + 1, count(writer)); } @Test public void testGetFeaturesWriterModify() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriter(first.typeName, Transaction.AUTO_COMMIT); SimpleFeature feature; while (writer.hasNext()) { feature = writer.next(); if (feature.getID().equals(first.features[0].getID())) { feature.setAttribute(first.stringAttribute, "changed"); writer.write(); } } feature = null; FeatureReader<SimpleFeatureType, SimpleFeature> reader = data.getFeatureReader(new Query( first.typeName, first.feat1Filter), Transaction.AUTO_COMMIT); if (reader.hasNext()) { feature = reader.next(); } assertEquals("changed", feature.getAttribute(first.stringAttribute)); } @Test public void testGetFeatureWriterTypeNameTransaction() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriter(first.typeName, Transaction.AUTO_COMMIT); assertEquals(first.features.length, count(writer)); writer.close(); } @Test public void testGetFeatureWriterAppendTypeNameTransaction() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriterAppend(first.typeName, Transaction.AUTO_COMMIT); assertEquals(0, count(writer)); writer.close(); } /* * Test for FeatureWriter getFeatureWriter(String, boolean, Transaction) */ @Test public void testGetFeatureWriterFilter() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriter(first.typeName, Filter.EXCLUDE, Transaction.AUTO_COMMIT); assertFalse(writer.hasNext()); assertEquals(0, count(writer)); writer = data.getFeatureWriter(first.typeName, Filter.INCLUDE, Transaction.AUTO_COMMIT); assertFalse(writer instanceof FilteringFeatureWriter); assertEquals(first.features.length, count(writer)); writer = data.getFeatureWriter(first.typeName, first.feat1Filter, Transaction.AUTO_COMMIT); assertEquals(1, count(writer)); } /** * Test two transactions one removing feature, and one adding a feature. */ @Test public void testGetFeatureWriterTransaction() throws Exception { try (Transaction t1 = new DefaultTransaction(); Transaction t2 = new DefaultTransaction()) { FeatureWriter<SimpleFeatureType, SimpleFeature> writer1 = data.getFeatureWriter( first.typeName, first.feat1Filter, t1); FeatureWriter<SimpleFeatureType, SimpleFeature> writer2 = data.getFeatureWriterAppend( first.typeName, t2); FeatureReader<SimpleFeatureType, SimpleFeature> reader; SimpleFeature feature; SimpleFeature[] ORIGIONAL = first.features; SimpleFeature[] REMOVE = new SimpleFeature[ORIGIONAL.length - 1]; SimpleFeature[] ADD = new SimpleFeature[ORIGIONAL.length + 1]; SimpleFeature[] FINAL = new SimpleFeature[ORIGIONAL.length]; int i; int index; index = 0; for (i = 0; i < ORIGIONAL.length; i++) { feature = ORIGIONAL[i]; if (!feature.getID().equals(first.features[0].getID())) { REMOVE[index++] = feature; } } for (i = 0; i < ORIGIONAL.length; i++) { ADD[i] = ORIGIONAL[i]; } ADD[i] = first.newFeature; for (i = 0; i < REMOVE.length; i++) { FINAL[i] = REMOVE[i]; } FINAL[i] = first.newFeature; // start of with ORIGINAL final Query allRoadsQuery = new Query(first.typeName); reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(covers(reader, ORIGIONAL)); // writer 1 removes road.rd1 on t1 // ------------------------------- // - tests transaction independence from DataStore while (writer1.hasNext()) { feature = (SimpleFeature) writer1.next(); assertEquals(first.features[0].getID(), feature.getID()); writer1.remove(); } // still have ORIGIONAL and t1 has REMOVE reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(covers(reader, ORIGIONAL)); reader = data.getFeatureReader(allRoadsQuery, t1); assertTrue(covers(reader, REMOVE)); // close writer1 // -------------- // ensure that modification is left up to transaction commmit writer1.close(); // We still have ORIGIONAL and t1 has REMOVE reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(covers(reader, ORIGIONAL)); reader = data.getFeatureReader(allRoadsQuery, t1); assertTrue(covers(reader, REMOVE)); // writer 2 adds road.rd4 on t2 // ---------------------------- // - tests transaction independence from each other feature = (SimpleFeature) writer2.next(); feature.setAttributes(first.newFeature.getAttributes()); writer2.write(); // We still have ORIGIONAL and t2 has ADD reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(covers(reader, ORIGIONAL)); reader = data.getFeatureReader(allRoadsQuery, t2); assertTrue(coversLax(reader, ADD)); // close writer2 // ------------- // ensure that modification is left up to transaction commmit writer2.close(); // Still have ORIGIONAL and t2 has ADD reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(covers(reader, ORIGIONAL)); reader = data.getFeatureReader(allRoadsQuery, t2); assertTrue(coversLax(reader, ADD)); // commit t1 // --------- // -ensure that delayed writing of transactions takes place // t1.commit(); // We now have REMOVE, as does t1 (which has not additional diffs) // t2 will have FINAL reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(covers(reader, REMOVE)); reader = data.getFeatureReader(allRoadsQuery, t1); assertTrue(covers(reader, REMOVE)); reader = data.getFeatureReader(allRoadsQuery, t2); assertTrue(coversLax(reader, FINAL)); // commit t2 // --------- // -ensure that everyone is FINAL at the end of the day t2.commit(); // We now have Number( remove one and add one) reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); reader = data.getFeatureReader(allRoadsQuery, Transaction.AUTO_COMMIT); assertTrue(coversLax(reader, FINAL)); reader = data.getFeatureReader(allRoadsQuery, t1); assertTrue(coversLax(reader, FINAL)); reader = data.getFeatureReader(allRoadsQuery, t2); assertTrue(coversLax(reader, FINAL)); } } public void testGetFeatureSource(TestDataType test) throws Exception { SimpleFeatureSource source = data.getFeatureSource(test.typeName); assertSchemaEqual(test.featureType, source.getSchema()); assertEquals(data, source.getDataStore()); if (source.getCount(Query.ALL) >= 0) { assertEquals(test.numberOfFeatures, source.getCount(Query.ALL)); } assertEquals(test.typeName, source.getSchema().getTypeName()); assertSame(data, source.getDataStore()); SimpleFeatureCollection collection = source.getFeatures(); assertEquals(test.numberOfFeatures, collection.size()); assertTrue(source.getBounds().covers(test.bounds)); assertTrue(covers(collection.features(), test.features)); SimpleFeatureCollection expected = DataUtilities.collection(test.features); assertCovers(test.typeName, expected, collection); assertTrue(collection.getBounds().covers(test.bounds)); SimpleFeatureCollection some = source.getFeatures(test.feat12Filter); assertEquals(2, some.size()); assertEquals(test.feat12Bounds, some.getBounds()); assertEquals(some.getSchema(), source.getSchema()); Query query = new Query(test.typeName, test.feat12Filter, new String[] { test.stringAttribute, test.featureType.getGeometryDescriptor().getName().getLocalPart() }); SimpleFeatureCollection half = source.getFeatures(query); assertEquals(2, half.size()); assertEquals(2, half.getSchema().getAttributeCount()); SimpleFeatureIterator reader = half.features(); SimpleFeatureType type = half.getSchema(); reader.close(); SimpleFeatureType actual = half.getSchema(); assertEquals(type.getTypeName(), actual.getTypeName()); assertEquals(type.getName().getNamespaceURI(), actual.getName().getNamespaceURI()); assertEquals(type.getAttributeCount(), actual.getAttributeCount()); for (int i = 0; i < type.getAttributeCount(); i++) { assertEquals(type.getDescriptor(i), actual.getDescriptor(i)); } assertEquals(type.getGeometryDescriptor(), actual.getGeometryDescriptor()); assertEquals(type, actual); BoundingBox b = half.getBounds(); assertEquals(test.feat12Bounds, b); } @Test public void testGetFeatureSource() throws Exception { testGetFeatureSource(first); testGetFeatureSource(second); } // // Feature Store Testing // @Test public void testGetFeatureStoreModifyFeatures1() throws IOException { SimpleFeatureStore road = (SimpleFeatureStore) data.getFeatureSource(first.typeName); Name name = first.featureType.getDescriptor(first.stringAttribute).getName(); road.modifyFeatures(name, "changed", first.feat1Filter); SimpleFeatureCollection results = road.getFeatures(first.feat1Filter); assertEquals("changed", results.features().next().getAttribute(first.stringAttribute)); } @Test public void testGetFeatureStoreModifyFeatures2() throws IOException { SimpleFeatureStore road = (SimpleFeatureStore) data.getFeatureSource(first.typeName); Name name = first.featureType.getDescriptor(first.stringAttribute).getName(); road.modifyFeatures(new Name[] { name }, new Object[] { "changed", }, first.feat1Filter); SimpleFeatureCollection results = road.getFeatures(first.feat1Filter); assertEquals("changed", results.features().next().getAttribute(first.stringAttribute)); } @Test public void testGetFeatureStoreRemoveFeatures() throws IOException { SimpleFeatureStore road = (SimpleFeatureStore) data.getFeatureSource(first.typeName); road.removeFeatures(first.feat1Filter); assertEquals(0, road.getFeatures(first.feat1Filter).size()); assertEquals(first.features.length - 1, road.getFeatures().size()); } @Test public void testGetFeatureStoreAddFeatures() throws IOException { FeatureReader<SimpleFeatureType, SimpleFeature> reader = DataUtilities .reader(new SimpleFeature[] { first.newFeature, }); SimpleFeatureStore road = (SimpleFeatureStore) data.getFeatureSource(first.typeName); road.addFeatures(DataUtilities.collection(reader)); assertEquals(first.features.length + 1, road.getFeatures().size()); } @Test public void testGetFeatureStoreSetFeatures() throws IOException { FeatureReader<SimpleFeatureType, SimpleFeature> reader; reader = DataUtilities.reader(new SimpleFeature[] { first.newFeature, }); SimpleFeatureStore road = (SimpleFeatureStore) data.getFeatureSource(first.typeName); assertEquals(3, road.getFeatures().size()); road.setFeatures(reader); assertEquals(1, count(data.getFeatureReader(new Query(first.typeName), Transaction.AUTO_COMMIT))); assertEquals(1, road.getFeatures().size()); } @Test public void testGetFeatureStoreTransactionSupport() throws Exception { try (Transaction t1 = new DefaultTransaction(); Transaction t2 = new DefaultTransaction()) { SimpleFeatureStore road = (SimpleFeatureStore) data.getFeatureSource(first.typeName); SimpleFeatureStore road1 = (SimpleFeatureStore) data.getFeatureSource(first.typeName); SimpleFeatureStore road2 = (SimpleFeatureStore) data.getFeatureSource(first.typeName); road1.setTransaction(t1); road2.setTransaction(t2); SimpleFeature feature; SimpleFeature[] ORIGINAL = first.features; SimpleFeature[] REMOVE = new SimpleFeature[ORIGINAL.length - 1]; SimpleFeature[] ADD = new SimpleFeature[ORIGINAL.length + 1]; SimpleFeature[] FINAL = new SimpleFeature[ORIGINAL.length]; int i; int index; index = 0; for (i = 0; i < ORIGINAL.length; i++) { feature = ORIGINAL[i]; if (!feature.getID().equals(first.features[0].getID())) { REMOVE[index++] = feature; } } for (i = 0; i < ORIGINAL.length; i++) { ADD[i] = ORIGINAL[i]; } ADD[i] = first.newFeature; for (i = 0; i < REMOVE.length; i++) { FINAL[i] = REMOVE[i]; } FINAL[i] = first.newFeature; // start of with ORIGINAL assertTrue(covers(road.getFeatures().features(), ORIGINAL)); // road1 removes road.rd1 on t1 // ------------------------------- // - tests transaction independence from DataStore road1.removeFeatures(first.feat1Filter); // still have ORIGIONAL and t1 has REMOVE assertTrue(covers(road.getFeatures().features(), ORIGINAL)); assertTrue(covers(road1.getFeatures().features(), REMOVE)); // road2 adds road.rd4 on t2 // ---------------------------- // - tests transaction independence from each other SimpleFeatureCollection collection = DataUtilities .collection(new SimpleFeature[] { first.newFeature, }); road2.addFeatures(collection); // We still have ORIGIONAL, t1 has REMOVE, and t2 has ADD assertTrue(covers(road.getFeatures().features(), ORIGINAL)); assertTrue(covers(road1.getFeatures().features(), REMOVE)); assertTrue(coversLax(road2.getFeatures().features(), ADD)); // commit t1 // --------- // -ensure that delayed writing of transactions takes place // t1.commit(); // We now have REMOVE, as does t1 (which has not additional diffs) // t2 will have FINAL assertTrue(covers(road.getFeatures().features(), REMOVE)); assertTrue(covers(road1.getFeatures().features(), REMOVE)); assertTrue(coversLax(road2.getFeatures().features(), FINAL)); // commit t2 // --------- // -ensure that everyone is FINAL at the end of the day t2.commit(); // We now have Number( remove one and add one) assertTrue(coversLax(road.getFeatures().features(), FINAL)); assertTrue(coversLax(road1.getFeatures().features(), FINAL)); assertTrue(coversLax(road2.getFeatures().features(), FINAL)); } } boolean isLocked(String typeName, String fid) { InProcessLockingManager lockingManager = (InProcessLockingManager) data.getLockingManager(); return lockingManager.isLocked(typeName, fid); } // // FeatureLocking Testing // /* * Test for void lockFeatures() */ @SuppressWarnings("unchecked") @Test public void testLockFeatures() throws IOException { FeatureLock lock = new FeatureLock("test", 3600); FeatureLocking<SimpleFeatureType, SimpleFeature> road; SimpleFeatureSource source = data.getFeatureSource(first.typeName); if (!(source instanceof FeatureLocking)) { LOGGER.info("testLockFeature ignored, store does not support locking"); return; } road = (FeatureLocking<SimpleFeatureType, SimpleFeature>) source; road.setFeatureLock(lock); assertFalse(isLocked(first.typeName, first.features[0].getID())); road.lockFeatures(); assertTrue(isLocked(first.typeName, first.features[0].getID())); } @Test public void testUnLockFeatures() throws IOException { FeatureLock lock = new FeatureLock("test", 360000); SimpleFeatureSource source = data.getFeatureSource(first.typeName); if (!(source instanceof FeatureLocking)) { LOGGER.info("testUnLockFeatures ignored, store does not support locking"); return; } @SuppressWarnings("unchecked") FeatureLocking<SimpleFeatureType, SimpleFeature> road = (FeatureLocking<SimpleFeatureType, SimpleFeature>) source; road.setFeatureLock(lock); road.lockFeatures(); try { road.unLockFeatures(); fail("unlock should fail due on AUTO_COMMIT"); } catch (IOException expected) { } try( Transaction t = new DefaultTransaction(); ){ road.setTransaction(t); try { road.unLockFeatures(); fail("unlock should fail due lack of authorization"); } catch (IOException expected) { } t.addAuthorization(lock.getAuthorization()); road.unLockFeatures(); } } @SuppressWarnings("unchecked") @Test public void testLockFeatureInteraction() throws IOException { FeatureLock lockA = new FeatureLock("LockA", 3600); FeatureLock lockB = new FeatureLock("LockB", 3600); try (Transaction t1 = new DefaultTransaction(); Transaction t2 = new DefaultTransaction()) { FeatureLocking<SimpleFeatureType, SimpleFeature> road1; FeatureLocking<SimpleFeatureType, SimpleFeature> road2; { SimpleFeatureSource source = data.getFeatureSource(first.typeName); if (!(source instanceof FeatureLocking)) { LOGGER.info("testLockFeatureInteraction ignored, store does not support locking"); return; } road1 = (FeatureLocking<SimpleFeatureType, SimpleFeature>) source; source = data.getFeatureSource(first.typeName); if (!(source instanceof FeatureLocking)) { LOGGER.info("testLockFeatureInteraction ignored, store does not support locking"); return; } road2 = (FeatureLocking<SimpleFeatureType, SimpleFeature>) source; } road1.setTransaction(t1); road2.setTransaction(t2); road1.setFeatureLock(lockA); road2.setFeatureLock(lockB); assertFalse(isLocked(first.typeName, first.features[0].getID())); assertFalse(isLocked(first.typeName, first.features[1].getID())); assertFalse(isLocked(first.typeName, first.features[2].getID())); road1.lockFeatures(first.feat1Filter); assertTrue(isLocked(first.typeName, first.features[0].getID())); assertFalse(isLocked(first.typeName, first.features[1].getID())); assertFalse(isLocked(first.typeName, first.features[2].getID())); road2.lockFeatures(first.feat2Filter); assertTrue(isLocked(first.typeName, first.features[0].getID())); assertTrue(isLocked(first.typeName, first.features[1].getID())); assertFalse(isLocked(first.typeName, first.features[2].getID())); try { road1.unLockFeatures(first.feat1Filter); fail("need authorization"); } catch (IOException expected) { } t1.addAuthorization(lockA.getAuthorization()); try { road1.unLockFeatures(first.feat2Filter); fail("need correct authorization"); } catch (IOException expected) { } road1.unLockFeatures(first.feat1Filter); assertFalse(isLocked(first.typeName, first.features[0].getID())); assertTrue(isLocked(first.typeName, first.features[1].getID())); assertFalse(isLocked(first.typeName, first.features[2].getID())); t2.addAuthorization(lockB.getAuthorization()); road2.unLockFeatures(first.feat2Filter); assertFalse(isLocked(first.typeName, first.features[0].getID())); assertFalse(isLocked(first.typeName, first.features[1].getID())); assertFalse(isLocked(first.typeName, first.features[2].getID())); } } @SuppressWarnings("unchecked") @Test public void testGetFeatureLockingExpire() throws Exception { FeatureLock lock = new FeatureLock("Timed", 100); FeatureLocking<SimpleFeatureType, SimpleFeature> road; { SimpleFeatureSource source = data.getFeatureSource(first.typeName); if (!(source instanceof FeatureLocking)) { LOGGER.info("testLockFeatureInteraction ignored, store does not support locking"); return; } road = (FeatureLocking<SimpleFeatureType, SimpleFeature>) source; } road.setFeatureLock(lock); String fid = first.features[0].getID(); assertFalse(isLocked(first.typeName, fid)); road.lockFeatures(first.feat1Filter); assertTrue(fid + " not locked", isLocked(first.typeName, fid)); Thread.sleep(150); assertFalse(isLocked(first.typeName, fid)); } @Test public void testException() throws IOException { SimpleFeatureStore rivers = (SimpleFeatureStore) data.getFeatureSource("sf_rivers"); SimpleFeatureCollection coll = rivers.getFeatures(); try{ coll.features(); } catch (Exception e) { assertTrue(e.getCause() instanceof WFSException); assertTrue(e.getMessage().contains("MyErrorMessage")); assertTrue(e.getMessage().contains("MyExceptionCode")); return; } assertTrue(false); } /** * Counts the number of Features returned by the specified reader. * <p> * This method will close the reader. * </p> */ protected static int count(FeatureReader<SimpleFeatureType, SimpleFeature> reader ) throws IOException { if( reader == null) { return -1; } int count = 0; try { while( reader.hasNext() ){ reader.next(); count++; } } catch (NoSuchElementException e) { // bad dog! throw new DataSourceException("hasNext() lied to me at:"+count, e ); } catch (Exception e) { throw new DataSourceException("next() could not understand feature at:"+count, e ); } finally { reader.close(); } return count; } /** * Counts the number of Features in the specified writer. * This method will close the writer. */ protected static int count(FeatureWriter<SimpleFeatureType, SimpleFeature> writer) throws NoSuchElementException, IOException { int count = 0; try { while (writer.hasNext()) { writer.next(); count++; } } finally { writer.close(); } return count; } protected static SimpleFeature[] grabArray(SimpleFeatureCollection features) { SimpleFeature array[] = new SimpleFeature[features.size()]; array = (SimpleFeature[]) features.toArray(array); assertNotNull(array); return array; } }