/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-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. */ package org.geotools.data.mysql; import java.io.IOException; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.NoSuchElementException; import java.util.PropertyResourceBundle; import java.util.logging.Logger; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.geotools.data.DataTestCase; import org.geotools.data.DataUtilities; import org.geotools.data.DefaultQuery; import org.geotools.data.DefaultTransaction; import org.geotools.data.EmptyFeatureReader; import org.geotools.data.FeatureLock; import org.geotools.data.FeatureLockFactory; import org.geotools.data.FeatureLocking; import org.geotools.data.FeatureReader; import org.geotools.data.FeatureSource; import org.geotools.data.FeatureStore; import org.geotools.data.FeatureWriter; import org.geotools.data.FilteringFeatureReader; import org.geotools.data.InProcessLockingManager; import org.geotools.data.Query; import org.geotools.data.Transaction; import org.geotools.data.jdbc.datasource.ManageableDataSource; import org.geotools.data.jdbc.fidmapper.BasicFIDMapper; import org.geotools.data.jdbc.fidmapper.TypedFIDMapper; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.IllegalAttributeException; import org.geotools.filter.AbstractFilter; import org.geotools.filter.CompareFilter; import org.geotools.filter.Expression; import org.geotools.filter.FilterFactory; import org.geotools.filter.FilterFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.filter.Filter; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; /** * This class tests the MySQLDataStore API against the same tests as * MemoryDataStore. * * <p> * The test fixture is available in the shared DataTestCase, really the common * elements should move to a shared DataStoreAPITestCase. * </p> * * <p> * This class does require your own DataStore, it will create a table populated * with the Features from the test fixture, and run a test, and then remove * the table. * </p> * * <p> * Because of the nature of this testing process you cannot run these tests in * conjunction with another user, so they cannot be implemented against the * public server. * </p> * * <p> * A simple properties file has been constructed, * <code>fixture.properties</code>, which you may direct to your own potgis * database installation. * </p> * * @author Jody Garnett, Refractions Research * @source $URL$ */ public class MySQLDataStoreAPITest extends DataTestCase { private static final int LOCK_DURATION = 3600 * 1000; // one hour /** The logger for the filter module. */ private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.mysql"); static boolean CHECK_TYPE = false; MySQLDataStore data; ManageableDataSource pool; String database; String victim = null; //"testGetFeatureWriterRemoveAll"; /** * Constructor for MemoryDataStoreTest. * * @param test * * @throws AssertionError DOCUMENT ME! */ public MySQLDataStoreAPITest(String test) { super(test); if ((victim != null) && !test.equals(victim)) { throw new AssertionError("test supressed " + test); } } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public static Test suite() { LOGGER.info("starting suite..."); TestSuite suite = new TestSuite(MySQLDataStoreAPITest.class); LOGGER.info("made suite..."); return suite; } /** * @see TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); PropertyResourceBundle resource; resource = new PropertyResourceBundle(this.getClass() .getResourceAsStream("fixture.properties")); String namespace = resource.getString("namespace"); String host = resource.getString("host"); int port = Integer.parseInt(resource.getString("port")); String database = resource.getString("database"); this.database = database; String user = resource.getString("user"); String password = resource.getString("passwd"); if (namespace.equals("http://www.geotools.org/data/postgis")) { throw new IllegalStateException( "The fixture.properties file needs to be configured for your own database"); } pool = MySQLDataStoreFactory.getDefaultDataSource(host, user, password, port, database, 10, 2, false); setUpRoadTable(); setUpRiverTable(); if (CHECK_TYPE) { checkTypesInDataBase(); CHECK_TYPE = false; // just once } data = new MySQLDataStore(pool, null, getName()); data.setFIDMapper("road", new TypedFIDMapper(new BasicFIDMapper("fid", 255, false), "road")); data.setFIDMapper("river", new TypedFIDMapper(new BasicFIDMapper("fid", 255, false), "river")); data.setFIDMapper("testset", new TypedFIDMapper(new BasicFIDMapper("gid", 255, true), "testset")); // // Update Fixture to reflect the actual data in the database // I am doing this because it updateRoadFeaturesFixture(); updateRiverFeaturesFixture(); } /** * This is a quick hack to have our fixture reflect the FIDs in the * database. * * <p> * When the dataStore learns how to preserve our FeatureIds this won't be * required. * </p> * * @throws Exception */ protected void updateRoadFeaturesFixture() throws Exception { Connection conn = pool.getConnection(); FeatureReader<SimpleFeatureType, SimpleFeature> reader = data.getFeatureReader(new DefaultQuery("road", Filter.INCLUDE), Transaction.AUTO_COMMIT); ReferencedEnvelope bounds = new ReferencedEnvelope(); try { SimpleFeature f; while (reader.hasNext()) { f = (SimpleFeature) reader.next(); int index = ((Integer) f.getAttribute("id")).intValue() - 1; roadFeatures[index] = f; bounds.expandToInclude((Envelope) f.getBounds()); } } finally { reader.close(); conn.close(); } if (!roadBounds.equals(bounds)) { System.out.println("warning! Database changed bounds()"); System.out.println("was:" + roadBounds); System.out.println("now:" + bounds); roadBounds = bounds; } ReferencedEnvelope bounds12 = new ReferencedEnvelope(); bounds12.expandToInclude((Envelope) roadFeatures[0].getBounds()); bounds12.expandToInclude((Envelope) roadFeatures[1].getBounds()); if (!rd12Bounds.equals(bounds12)) { System.out.println("warning! Database changed bounds of rd1 & rd2"); System.out.println("was:" + rd12Bounds); System.out.println("now:" + bounds12); rd12Bounds = bounds12; } SimpleFeatureType schema = roadFeatures[0].getFeatureType(); FilterFactory ff = FilterFactoryFinder.createFilterFactory(); CompareFilter tFilter = ff.createCompareFilter(AbstractFilter.COMPARE_EQUALS); Expression rd1Literal = ff.createLiteralExpression("r1"); tFilter.addLeftValue(rd1Literal); Expression rdNameAtt = ff.createAttributeExpression(schema, "name"); tFilter.addRightValue(rdNameAtt); rd1Filter = tFilter; tFilter = ff.createCompareFilter(AbstractFilter.COMPARE_EQUALS); Expression rd2Literal = ff.createLiteralExpression("r2"); tFilter.addLeftValue(rd2Literal); tFilter.addRightValue(rdNameAtt); rd2Filter = tFilter; rd12Filter = ff.or(rd2Filter, rd1Filter); } /** * This is a quick hack to have our fixture reflect the FIDs in the * database. * * <p> * When the dataStore learns how to preserve our FeatureIds this won't be * required. * </p> * * @throws Exception DOCUMENT ME! */ protected void updateRiverFeaturesFixture() throws Exception { Connection conn = pool.getConnection(); FeatureReader<SimpleFeatureType, SimpleFeature> reader = data.getFeatureReader(new DefaultQuery("river", Filter.INCLUDE), Transaction.AUTO_COMMIT); ReferencedEnvelope bounds = new ReferencedEnvelope(); try { SimpleFeature f; while (reader.hasNext()) { f = reader.next(); int index = ((Integer) f.getAttribute("id")).intValue() - 1; riverFeatures[index] = f; bounds.expandToInclude((Envelope)f.getBounds()); } } finally { reader.close(); conn.close(); } if (!riverBounds.equals(bounds)) { System.out.println("warning! Database changed bounds of river"); System.out.println("was:" + riverBounds); System.out.println("now:" + bounds); riverBounds = bounds; } FilterFactory factory = FilterFactoryFinder.createFilterFactory(); CompareFilter tFilter = factory.createCompareFilter(AbstractFilter.COMPARE_EQUALS); Expression rvLiteral = factory.createLiteralExpression("rv1"); tFilter.addLeftValue(rvLiteral); SimpleFeatureType schema = riverFeatures[0].getFeatureType(); Expression rvNameAtt = factory.createAttributeExpression(schema, "river"); tFilter.addRightValue(rvNameAtt); rv1Filter = tFilter; } protected void checkTypesInDataBase() throws SQLException { Connection conn = pool.getConnection(); try { DatabaseMetaData md = conn.getMetaData(); ResultSet rs = //md.getTables( catalog, null, null, null ); md.getTables(null, "public", "%", new String[] { "TABLE", }); ResultSetMetaData rsmd = rs.getMetaData(); int NUM = rsmd.getColumnCount(); System.out.print(" "); for (int i = 1; i <= NUM; i++) { System.out.print(rsmd.getColumnName(i)); System.out.flush(); System.out.print(":"); System.out.flush(); System.out.print(rsmd.getColumnClassName(i)); System.out.flush(); if (i < NUM) { System.out.print(","); System.out.flush(); } } System.out.println(); while (rs.next()) { System.out.print(rs.getRow()); System.out.print(":"); System.out.flush(); for (int i = 1; i <= NUM; i++) { System.out.print(rsmd.getColumnName(i)); System.out.flush(); System.out.print("="); System.out.flush(); System.out.print(rs.getString(i)); System.out.flush(); if (i < NUM) { System.out.print(","); System.out.flush(); } } System.out.println(); } } finally { conn.close(); } } protected void setUpRoadTable() throws Exception { Connection conn = pool.getConnection(); conn.setAutoCommit(true); try { Statement s = conn.createStatement(); s.execute("DROP TABLE road"); } catch (Exception ignore) { } try { Statement s = conn.createStatement(); s.execute( "CREATE TABLE road (fid varchar(255) PRIMARY KEY, id int, geom LINESTRING not null, name varchar(255), spatial index(geom) ) engine = myisam"); for (int i = 0; i < roadFeatures.length; i++) { SimpleFeature f = roadFeatures[i]; //strip out the road. String fid = f.getID().substring("road.".length()); String ql = "INSERT INTO road (fid,id,geom,name) VALUES (" + "'" + fid + "'," + f.getAttribute("id") + "," + "GeometryFromText('" + ((Geometry) f.getAttribute("geom")).toText() + "', 0 )," + "'" + f.getAttribute("name") + "')"; s.execute(ql); } } finally { conn.close(); } } protected void killTestTables() throws Exception { Connection conn = pool.getConnection(); try { Statement s = conn.createStatement(); s.execute("SELECT dropgeometrycolumn( '" + database + "','road','geom')"); s.execute("SELECT dropgeometrycolumn( '" + database + "','river','geom')"); } catch (Exception ignore) { } try { Statement s = conn.createStatement(); s.execute("DROP TABLE road"); s.execute("DROP TABLE river"); } catch (Exception ignore) { } finally { conn.close(); } } protected void setUpRiverTable() throws Exception { Connection conn = pool.getConnection(); try { Statement s = conn.createStatement(); s.execute("DROP TABLE river"); } catch (Exception ignore) { } try { Statement s = conn.createStatement(); // force a binary collation otherwise MySql will do case insensitive comparisons by default s.execute( "CREATE TABLE river(fid varchar(255) PRIMARY KEY, id int, geom MULTILINESTRING, river varchar(255) collate latin1_bin, flow double)"); for (int i = 0; i < riverFeatures.length; i++) { SimpleFeature f = riverFeatures[i]; String fid = f.getID().substring("river.".length()); s.execute("INSERT INTO river (fid, id, geom, river, flow) VALUES (" + "'" + fid + "'," + f.getAttribute("id") + "," + "GeometryFromText('" + f.getAttribute("geom").toString() + "', 0 )," + "'" + f.getAttribute("river") + "'," + f.getAttribute("flow") + ")"); } } finally { conn.close(); } } /* * @see TestCase#tearDown() */ protected void tearDown() throws Exception { data = null; pool.close(); super.tearDown(); } public void testGetFeatureTypes() { try { String[] names = data.getTypeNames(); assertTrue(contains(names, "road")); assertTrue(contains(names, "river")); } catch (IOException e) { e.printStackTrace(); fail("An IOException has been thrown!"); } } 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; } void assertContains(Object[] array, Object expected) { assertFalse(array == null); assertFalse(array.length == 0); assertNotNull(expected); for (int i = 0; i < array.length; i++) { if (array[i].equals(expected)) { return; } } fail("Contains " + expected); } /** * Like contain but based on match rather than equals * * @param array DOCUMENT ME! * @param expected DOCUMENT ME! * * @return DOCUMENT ME! */ 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 * * @param expected DOCUMENT ME! * @param actual DOCUMENT ME! * * @return DOCUMENT ME! */ 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 instanceof Geometry && ev instanceof Geometry) { Geometry ag = (Geometry) av; Geometry eg = (Geometry) ev; if (!ag.equals(eg)) { return false; } } else if (!av.equals(ev)) { return false; } } return true; } public void testGetSchemaRoad() throws IOException { SimpleFeatureType expected = roadType; SimpleFeatureType actual = data.getSchema("road"); assertEquals("namespace", expected.getName().getNamespaceURI(), actual.getName().getNamespaceURI()); assertEquals("typeName", expected.getTypeName(), actual.getTypeName()); //assertEquals( "compare", 0, DataUtilities.compare( expected, actual )); assertEquals("attributeCount", expected.getAttributeCount(), actual.getAttributeCount()); for (int i = 0; i < expected.getAttributeCount(); i++) { AttributeDescriptor expectedAttribute = expected.getDescriptor(i); AttributeDescriptor actualAttribute = actual.getDescriptor(i); assertEquals("attribute " + expectedAttribute.getLocalName(), expectedAttribute, actualAttribute); } assertEquals(expected, actual); } public void testGetSchemaRiver() throws IOException { SimpleFeatureType expected = riverType; SimpleFeatureType actual = data.getSchema("river"); assertEquals("namespace", expected.getName().getNamespaceURI(), actual.getName().getNamespaceURI()); assertEquals("typeName", expected.getTypeName(), actual.getTypeName()); //assertEquals( "compare", 0, DataUtilities.compare( expected, actual )); assertEquals("attributeCount", expected.getAttributeCount(), actual.getAttributeCount()); for (int i = 0; i < expected.getAttributeCount(); i++) { AttributeDescriptor expectedAttribute = expected.getDescriptor(i); AttributeDescriptor actualAttribute = actual.getDescriptor(i); assertEquals("attribute " + expectedAttribute.getLocalName(), expectedAttribute, actualAttribute); } assertEquals(expected, actual); } static public void assertEquals(String message, String expected, String actual) { if (expected == actual) { return; } assertNotNull(message, expected); assertNotNull(message, actual); if (!expected.equals(actual)) { fail(message + " expected:<" + expected + "> but was <" + actual + ">"); } } void assertCovers(String msg, FeatureCollection<SimpleFeatureType, SimpleFeature> c1, FeatureCollection<SimpleFeatureType, SimpleFeature> c2) { if (c1 == c2) { return; } assertNotNull(msg, c1); assertNotNull(msg, c2); assertEquals(msg + " size", c1.size(), c2.size()); SimpleFeature f; for (FeatureIterator<SimpleFeature> i = c1.features(); i.hasNext();) { f = i.next(); assertTrue(msg + " " + f.getID(), c2.contains(f)); } } public FeatureReader<SimpleFeatureType, SimpleFeature> reader(String typeName) throws IOException { SimpleFeatureType type = data.getSchema(typeName); return data.getFeatureReader(type, Filter.INCLUDE, Transaction.AUTO_COMMIT); } public FeatureWriter<SimpleFeatureType, SimpleFeature> writer(String typeName) throws IOException { return data.getFeatureWriter(typeName, Transaction.AUTO_COMMIT); } public void testGetFeatureReader() throws IOException, IllegalAttributeException { assertCovered(roadFeatures, reader("road")); assertEquals(3, count(reader("road"))); } public void testGetFeatureReaderMutability() throws IOException, IllegalAttributeException { FeatureReader<SimpleFeatureType, SimpleFeature> reader = reader("road"); SimpleFeature feature; while (reader.hasNext()) { feature = (SimpleFeature) reader.next(); feature.setAttribute("name", null); } reader.close(); reader = reader("road"); while (reader.hasNext()) { feature = (SimpleFeature) reader.next(); assertNotNull(feature.getAttribute("name")); } reader.close(); try { reader.next(); fail("next should fail with an IOException"); } catch (IOException expected) { } } public void testGetFeatureReaderConcurancy() throws NoSuchElementException, IOException, IllegalAttributeException { FeatureReader<SimpleFeatureType, SimpleFeature> reader1 = reader("road"); FeatureReader<SimpleFeatureType, SimpleFeature> reader2 = reader("road"); FeatureReader<SimpleFeatureType, SimpleFeature> reader3 = reader("river"); SimpleFeature feature1; SimpleFeature feature2; SimpleFeature feature3; while (reader1.hasNext() || reader2.hasNext() || reader3.hasNext()) { assertTrue(contains(roadFeatures, reader1.next())); assertTrue(contains(roadFeatures, reader2.next())); if (reader3.hasNext()) { assertTrue(contains(riverFeatures, reader3.next())); } } try { reader1.next(); fail("next should fail with an NoSuchElementException"); } catch (NoSuchElementException expectedNoElement) { // this is new to me, I had expected an IOException } try { reader2.next(); fail("next should fail with an NoSuchElementException"); } catch (NoSuchElementException expectedNoElement) { } try { reader3.next(); fail("next should fail with an NoSuchElementException"); } catch (NoSuchElementException expectedNoElement) { } reader1.close(); reader2.close(); reader3.close(); try { reader1.next(); fail("next should fail with an IOException"); } catch (IOException expectedClosed) { } try { reader2.next(); fail("next should fail with an IOException"); } catch (IOException expectedClosed) { } try { reader3.next(); fail("next should fail with an IOException"); } catch (IOException expectedClosed) { } } public void testGetFeatureReaderFilterAutoCommit() throws NoSuchElementException, IOException, IllegalAttributeException { SimpleFeatureType type = data.getSchema("road"); FeatureReader<SimpleFeatureType, SimpleFeature> reader; reader = data.getFeatureReader(type, Filter.INCLUDE, Transaction.AUTO_COMMIT); assertFalse(reader instanceof FilteringFeatureReader); assertEquals(type, reader.getFeatureType()); assertEquals(roadFeatures.length, count(reader)); reader = data.getFeatureReader(type, Filter.EXCLUDE, Transaction.AUTO_COMMIT); assertTrue(reader instanceof EmptyFeatureReader); assertEquals(type, reader.getFeatureType()); assertEquals(0, count(reader)); reader = data.getFeatureReader(type, rd1Filter, Transaction.AUTO_COMMIT); //assertTrue(reader instanceof FilteringFeatureReader); assertEquals(type, reader.getFeatureType()); assertEquals(1, count(reader)); } // TODO: uncomment when and if MySQLDataStore gets transaction capabilities // public void testGetFeatureReaderFilterTransaction() // throws NoSuchElementException, IOException, IllegalAttributeException { // Transaction t = new DefaultTransaction(); // FeatureType type = data.getSchema("road"); // FeatureReader<SimpleFeatureType, SimpleFeature> reader; // // reader = data.getFeatureReader(type, Filter.EXCLUDE, t); // assertTrue(reader instanceof EmptyFeatureReader); // assertEquals(type, reader.getFeatureType()); // assertEquals(0, count(reader)); // // reader = data.getFeatureReader(type, Filter.INCLUDE, t); // assertEquals(type, reader.getFeatureType()); // assertEquals(roadFeatures.length, count(reader)); // // reader = data.getFeatureReader(type, rd1Filter, t); // assertEquals(type, reader.getFeatureType()); // assertEquals(1, count(reader)); // // FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter("road", Filter.INCLUDE, t); // Feature feature; // // while (writer.hasNext()) { // feature = writer.next(); // // if (feature.getID().equals(roadFeatures[0].getID())) { // writer.remove(); // } // } // // reader = data.getFeatureReader(type, Filter.EXCLUDE, t); // assertEquals(0, count(reader)); // // reader = data.getFeatureReader(type, Filter.INCLUDE, t); // assertEquals(roadFeatures.length - 1, count(reader)); // // reader = data.getFeatureReader(type, rd1Filter, t); // assertEquals(0, count(reader)); // // t.rollback(); // reader = data.getFeatureReader(type, Filter.EXCLUDE, t); // assertEquals(0, count(reader)); // // reader = data.getFeatureReader(type, Filter.INCLUDE, t); // assertEquals(roadFeatures.length, count(reader)); // // reader = data.getFeatureReader(type, rd1Filter, t); // assertEquals(1, count(reader)); // } /** * Ensure readers contents equal those in the feature array * * @param features DOCUMENT ME! * @param reader DOCUMENT ME! * * @throws NoSuchElementException DOCUMENT ME! * @throws IOException DOCUMENT ME! * @throws IllegalAttributeException DOCUMENT ME! */ void assertCovered(SimpleFeature[] features, FeatureReader<SimpleFeatureType, SimpleFeature> reader) throws NoSuchElementException, IOException, IllegalAttributeException { int count = 0; try { while (reader.hasNext()) { assertContains(features, reader.next()); count++; } } finally { reader.close(); } assertEquals(features.length, count); } /** * Ensure readers contents match those in the feature array * * <p> * Implemented using match on attribute types, not feature id * </p> * * @param array DOCUMENT ME! * @param reader DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ void assertMatched(SimpleFeature[] array, FeatureReader<SimpleFeatureType, SimpleFeature> reader) throws Exception { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); assertMatch(array, feature); count++; } } finally { reader.close(); } assertEquals("array not matched by reader", array.length, count); } void assertMatch(SimpleFeature[] array, SimpleFeature feature) { assertTrue(array != null); assertTrue(array.length != 0); SimpleFeatureType schema = feature.getFeatureType(); for (int i = 0; i < array.length; i++) { if (match(array[i], feature)) { return; } } System.out.println("not found:" + feature); for (int i = 0; i < array.length; i++) { System.out.println(i + ":" + array[i]); } fail("array has no match for " + feature); } /** * Ensure that FeatureReader<SimpleFeatureType, SimpleFeature> reader contains extactly the contents of * array. * * @param reader DOCUMENT ME! * @param array DOCUMENT ME! * * @return DOCUMENT ME! * * @throws NoSuchElementException DOCUMENT ME! * @throws IOException DOCUMENT ME! * @throws IllegalAttributeException DOCUMENT ME! */ boolean covers(FeatureReader <SimpleFeatureType, SimpleFeature> reader, SimpleFeature[] array) throws NoSuchElementException, IOException, IllegalAttributeException { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); if (!contains(array, feature)) { return false; } count++; } } finally { reader.close(); } return count == array.length; } boolean covers(FeatureIterator<SimpleFeature> reader, SimpleFeature[] array) throws NoSuchElementException, IOException, IllegalAttributeException { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); if (!contains(array, feature)) { return false; } count++; } } finally { reader.close(); } return count == array.length; } boolean coversLax(FeatureReader <SimpleFeatureType, SimpleFeature> reader, SimpleFeature[] array) throws NoSuchElementException, IOException, IllegalAttributeException { 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(String message, FeatureReader<SimpleFeatureType, SimpleFeature> reader) throws NoSuchElementException, IOException, IllegalAttributeException { SimpleFeature feature; int count = 0; try { while (reader.hasNext()) { feature = reader.next(); String msg = message + ": feture " + count + "=" + feature; //LOGGER.info( msg ); System.out.println(msg); count++; } } finally { reader.close(); } } void dump(String message, Object[] array) { for (int i = 0; i < array.length; i++) { String msg = message + ": " + i + "=" + array[i]; //LOGGER.info( msg ); System.out.println(msg); } } /* * Test for FeatureWriter getFeatureWriter(String, Filter, Transaction) */ public void testGetFeatureWriter() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter("road", Filter.INCLUDE, Transaction.AUTO_COMMIT); assertEquals(roadFeatures.length, count(writer)); } public void testGetFeatureWriterClose() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter("road", Filter.INCLUDE, Transaction.AUTO_COMMIT); writer.close(); try { assertFalse(writer.hasNext()); fail("Should not be able to use a closed writer"); } catch (IOException expected) { } try { assertNull(writer.next()); fail("Should not be able to use a closed writer"); } catch (IOException expected) { } try { writer.close(); } catch (IOException expected) { } } public void testGetFeatureWriterRemove() throws IOException, IllegalAttributeException { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = writer("road"); SimpleFeature feature; while (writer.hasNext()) { feature = writer.next(); if (feature.getID().equals(roadFeatures[0].getID())) { writer.remove(); } } assertEquals(roadFeatures.length - 1, count("road")); } public void testGetFeatureWriterRemoveAll() throws IOException, IllegalAttributeException { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = writer("road"); SimpleFeature feature; try { while (writer.hasNext()) { feature = writer.next(); writer.remove(); } } finally { writer.close(); } assertEquals(0, count("road")); } public int count(String typeName) throws IOException { //return count(reader(typeName)); // makes use of optimization if any return data.getFeatureSource(typeName).getFeatures().size(); } public void testGetFeaturesWriterAdd() throws IOException, IllegalAttributeException { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = data.getFeatureWriter("road", Transaction.AUTO_COMMIT); SimpleFeature feature; LOGGER.info("about to call has next on writer " + writer); while (writer.hasNext()) { feature = (SimpleFeature) writer.next(); } assertFalse(writer.hasNext()); feature = (SimpleFeature) writer.next(); feature.setAttributes(newRoad.getAttributes()); writer.write(); assertFalse(writer.hasNext()); assertEquals(roadFeatures.length + 1, count("road")); } /** * Seach for feature based on AttributeType. * * <p> * If attributeName is null, we will search by feature.getID() * </p> * * <p> * The provided reader will be closed by this opperations. * </p> * * @param reader reader to search through * @param attributeName attributeName, or null for featureID * @param value value to match * * @return Feature * * @throws NoSuchElementException if a match could not be found * @throws IOException We could not use reader * @throws IllegalAttributeException if attributeName did not match schema */ public SimpleFeature findFeature(FeatureReader <SimpleFeatureType, SimpleFeature> reader, String attributeName, Object value) throws NoSuchElementException, IOException, IllegalAttributeException { SimpleFeature f; try { while (reader.hasNext()) { f = reader.next(); if (attributeName == null) { if (value.equals(f.getID())) { return f; } } else { if (value.equals(f.getAttribute(attributeName))) { return f; } } } } finally { reader.close(); } if (attributeName == null) { throw new NoSuchElementException("No match for FID=" + value); } else { throw new NoSuchElementException("No match for " + attributeName + "=" + value); } } public SimpleFeature feature(String typeName, String fid) throws NoSuchElementException, IOException, IllegalAttributeException { FeatureReader<SimpleFeatureType, SimpleFeature> reader = reader(typeName); SimpleFeature f; try { while (reader.hasNext()) { f = reader.next(); if (fid.equals(f.getID())) { return f; } } } finally { reader.close(); } return null; } public void testGetFeaturesWriterModify() throws IOException, IllegalAttributeException { FeatureWriter<SimpleFeatureType, SimpleFeature> writer = writer("road"); SimpleFeature feature; while (writer.hasNext()) { feature = writer.next(); if (feature.getID().equals(roadFeatures[0].getID())) { feature.setAttribute("name", "changed"); writer.write(); } } feature = (SimpleFeature) feature("road", roadFeatures[0].getID()); assertNotNull(feature); assertEquals("changed", feature.getAttribute("name")); } public void testGetFeatureWriterTypeNameTransaction() throws NoSuchElementException, IOException, IllegalAttributeException { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriter("road", Transaction.AUTO_COMMIT); assertEquals(roadFeatures.length, count(writer)); //writer.close(); called by count. } public void testGetFeatureWriterAppendTypeNameTransaction() throws Exception { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriterAppend("road", Transaction.AUTO_COMMIT); assertEquals(0, count(writer)); //writer.close(); called by count } /* * Test for FeatureWriter getFeatureWriter(String, boolean, Transaction) * @task REVISIT: JDBCDataStore currently does not return these proper * instanceof's. If we want to guarantee that people can't append to * a request with a FeatureWriter then we could add the functionality to * JDBCDataStore by having getFeatureWriter(.. Filter ...) check to see if * the FeatureWriter returned is instanceof FilteringFeatureWriter, and if * not then just wrap it in a FilteringFeatureWriter(writer, Filter.INCLUDE). * I think it'd be a bit of unnecessary overhead, but if we want it it's * easy to do. It will guarantee that calls with Filter won't ever append. * Doing with Filter.INCLUDE, however, would require a bit of reworking, as * the Filter getFeatureWriter is currently where we do the bulk of * the work. */ public void testGetFeatureWriterFilter() throws NoSuchElementException, IOException, IllegalAttributeException { FeatureWriter<SimpleFeatureType, SimpleFeature> writer; writer = data.getFeatureWriter("road", Filter.EXCLUDE, Transaction.AUTO_COMMIT); //see task above // assertTrue(writer instanceof EmptyFeatureWriter); assertEquals(0, count(writer)); writer = data.getFeatureWriter("road", Filter.INCLUDE, Transaction.AUTO_COMMIT); //assertFalse(writer instanceof FilteringFeatureWriter); assertEquals(roadFeatures.length, count(writer)); writer = data.getFeatureWriter("road", rd1Filter, Transaction.AUTO_COMMIT); //assertTrue(writer instanceof FilteringFeatureWriter); assertEquals(1, count(writer)); } // Uncomment when and if MySQLDataStore will support transaction // /** // * Test two transactions one removing feature, and one adding a feature. // * // * @throws Exception DOCUMENT ME! // */ // public void testGetFeatureWriterTransaction() throws Exception { // Transaction t1 = new DefaultTransaction(); // Transaction t2 = new DefaultTransaction(); // FeatureWriter<SimpleFeatureType, SimpleFeature> writer1 = data.getFeatureWriter("road", rd1Filter, t1); // FeatureWriter<SimpleFeatureType, SimpleFeature> writer2 = data.getFeatureWriterAppend("road", t2); // // FeatureType road = data.getSchema("road"); // FeatureReader<SimpleFeatureType, SimpleFeature> reader; // Feature feature; // Feature[] ORIGIONAL = roadFeatures; // Feature[] REMOVE = new Feature[ORIGIONAL.length - 1]; // Feature[] ADD = new Feature[ORIGIONAL.length + 1]; // Feature[] FINAL = new Feature[ORIGIONAL.length]; // int i; // int index; // index = 0; // // for (i = 0; i < ORIGIONAL.length; i++) { // feature = ORIGIONAL[i]; // // if (!feature.getID().equals(roadFeatures[0].getID())) { // REMOVE[index++] = feature; // } // } // // for (i = 0; i < ORIGIONAL.length; i++) { // ADD[i] = ORIGIONAL[i]; // } // // ADD[i] = newRoad; // will need to update with Fid from database // // for (i = 0; i < REMOVE.length; i++) { // FINAL[i] = REMOVE[i]; // } // // FINAL[i] = newRoad; // will need to update with Fid from database // // // start of with ORIGINAL // reader = data.getFeatureReader(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(covers(reader, ORIGIONAL)); // // // writer 1 removes road.rd1 on t1 // // ------------------------------- // // - tests transaction independence from DataStore // while (writer1.hasNext()) { // feature = writer1.next(); // assertEquals(roadFeatures[0].getID(), feature.getID()); // writer1.remove(); // } // // // still have ORIGIONAL and t1 has REMOVE // reader = data.getFeatureReader(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(covers(reader, ORIGIONAL)); // // reader = data.getFeatureReader(road, Filter.INCLUDE, 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(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(covers(reader, ORIGIONAL)); // // reader = data.getFeatureReader(road, Filter.INCLUDE, t1); // assertTrue(covers(reader, REMOVE)); // // // writer 2 adds road.rd4 on t2 // // ---------------------------- // // - tests transaction independence from each other // feature = writer2.next(); // feature.setAttributes(newRoad.getAttributes(null)); // writer2.write(); // // // HACK: ?!? update ADD and FINAL with new FID from database // // // reader = data.getFeatureReader(road, Filter.INCLUDE, t2); // newRoad = findFeature(reader, "id", new Integer(4)); // System.out.println("newRoad:" + newRoad); // ADD[ADD.length - 1] = newRoad; // FINAL[FINAL.length - 1] = newRoad; // // // We still have ORIGIONAL and t2 has ADD // reader = data.getFeatureReader(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(covers(reader, ORIGIONAL)); // // reader = data.getFeatureReader(road, Filter.INCLUDE, t2); // assertMatched(ADD, reader); // broken due to FID problem // // writer2.close(); // // // Still have ORIGIONAL and t2 has ADD // reader = data.getFeatureReader(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(covers(reader, ORIGIONAL)); // reader = data.getFeatureReader(road, Filter.INCLUDE, 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(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(covers(reader, REMOVE)); // reader = data.getFeatureReader(road, Filter.INCLUDE, t1); // assertTrue(covers(reader, REMOVE)); // reader = data.getFeatureReader(road, Filter.INCLUDE, 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(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // reader = data.getFeatureReader(road, Filter.INCLUDE, Transaction.AUTO_COMMIT); // assertTrue(coversLax(reader, FINAL)); // // reader = data.getFeatureReader(road, Filter.INCLUDE, t1); // assertTrue(coversLax(reader, FINAL)); // // reader = data.getFeatureReader(road, Filter.INCLUDE, t2); // assertTrue(coversLax(reader, FINAL)); // } // Feature Source Testing public void testGetFeatureSourceRoad() throws IOException { FeatureSource<SimpleFeatureType, SimpleFeature> road = data.getFeatureSource("road"); assertEquals(roadType, road.getSchema()); assertSame(data, road.getDataStore()); int count = road.getCount(Query.ALL); assertTrue((count == 3) || (count == -1)); Envelope bounds = road.getBounds(Query.ALL); assertTrue((bounds == null) || bounds.equals(roadBounds)); FeatureCollection<SimpleFeatureType, SimpleFeature> all = road.getFeatures(); assertEquals(3, all.size()); assertEquals(roadBounds, all.getBounds()); FeatureCollection<SimpleFeatureType, SimpleFeature> expected = DataUtilities.collection(roadFeatures); assertCovers("all", expected, all); assertEquals(roadBounds, all.getBounds()); FeatureCollection<SimpleFeatureType, SimpleFeature> some = road.getFeatures(rd12Filter); assertEquals(2, some.size()); Envelope e = new Envelope(); e.expandToInclude((Envelope) roadFeatures[0].getBounds()); e.expandToInclude((Envelope)roadFeatures[1].getBounds()); assertEquals(e, some.getBounds()); assertEquals(some.getSchema(), road.getSchema()); DefaultQuery query = new DefaultQuery("road", rd12Filter, new String[] { "name" }); FeatureCollection<SimpleFeatureType, SimpleFeature> half = road.getFeatures(query); assertEquals(2, half.size()); assertEquals(1, half.getSchema().getAttributeCount()); FeatureIterator<SimpleFeature> 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)); } assertNull(type.getGeometryDescriptor()); assertEquals(type.getGeometryDescriptor(), actual.getGeometryDescriptor()); assertEquals(type, actual); Envelope b = half.getBounds(); assertEquals(roadBounds, b); } public void testGetFeatureSourceRiver() throws NoSuchElementException, IOException, IllegalAttributeException { FeatureSource<SimpleFeatureType, SimpleFeature> river = data.getFeatureSource("river"); assertEquals(riverType, river.getSchema()); assertSame(data, river.getDataStore()); FeatureCollection<SimpleFeatureType, SimpleFeature> all = river.getFeatures(); assertEquals(2, all.size()); assertEquals(riverBounds, all.getBounds()); assertTrue("rivers", covers(all.features(), riverFeatures)); FeatureCollection<SimpleFeatureType, SimpleFeature> expected = DataUtilities.collection(riverFeatures); assertCovers("all", expected, all); assertEquals(riverBounds, all.getBounds()); } public void testCaseInsensitiveFilter() throws Exception { final String riverName = riverType.getName().getLocalPart(); FeatureSource<SimpleFeatureType, SimpleFeature> rivers = data.getFeatureSource(riverName); org.opengis.filter.Filter caseSensitive = ff.equal(ff.property("river"), ff.literal("Rv1"), true); assertEquals(0, rivers.getCount(new DefaultQuery(riverName, caseSensitive))); org.opengis.filter.Filter caseInsensitive = ff.equal(ff.property("river"), ff.literal("Rv1"), false); assertEquals(1, rivers.getCount(new DefaultQuery(riverName, caseInsensitive))); } // // Feature Store Testing // public void testGetFeatureStoreModifyFeatures1() throws IOException { FeatureStore<SimpleFeatureType, SimpleFeature> road = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); //FilterFactory factory = FilterFactoryFinder.createFilterFactory(); //rd1Filter = factory.createFidFilter( roadFeatures[0].getID() ); Object changed = new Integer(5); AttributeDescriptor name = roadType.getDescriptor("id"); road.modifyFeatures(name, changed, rd1Filter); FeatureCollection<SimpleFeatureType, SimpleFeature> results = road.getFeatures(rd1Filter); assertEquals(changed, results.features().next().getAttribute("id")); } public void testGetFeatureStoreModifyFeatures2() throws IOException { FeatureStore<SimpleFeatureType, SimpleFeature> road = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); FilterFactory factory = FilterFactoryFinder.createFilterFactory(); rd1Filter = factory.createFidFilter(roadFeatures[0].getID()); AttributeDescriptor name = roadType.getDescriptor("name"); road.modifyFeatures(new AttributeDescriptor[] { name, }, new Object[] { "changed", }, rd1Filter); FeatureCollection<SimpleFeatureType, SimpleFeature> results = road.getFeatures(rd1Filter); assertEquals("changed", results.features().next().getAttribute("name")); } public void testGetFeatureStoreRemoveFeatures() throws IOException { FeatureStore<SimpleFeatureType, SimpleFeature> road = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); road.removeFeatures(rd1Filter); assertEquals(0, road.getFeatures(rd1Filter).size()); assertEquals(roadFeatures.length - 1, road.getFeatures().size()); } public void testGetFeatureStoreAddFeatures() throws IOException { FeatureStore<SimpleFeatureType, SimpleFeature> road = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); road.addFeatures(DataUtilities.collection(newRoad)); assertEquals(roadFeatures.length + 1, count("road")); } public void testGetFeatureStoreSetFeatures() throws NoSuchElementException, IOException, IllegalAttributeException { FeatureReader<SimpleFeatureType, SimpleFeature> reader = DataUtilities.reader(new SimpleFeature[] { newRoad, }); FeatureStore<SimpleFeatureType, SimpleFeature> road = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); assertEquals(3, count("road")); road.setFeatures(reader); assertEquals(1, count("road")); } // Uncomment when and if MySQLDataStore will support transaction // public void testGetFeatureStoreTransactionSupport() throws Exception { // Transaction t1 = new DefaultTransaction(); // Transaction t2 = new DefaultTransaction(); // // FeatureStore<SimpleFeatureType, SimpleFeature> road = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); // FeatureStore road1 = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); // FeatureStore road2 = (FeatureStore<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); // // road1.setTransaction(t1); // road2.setTransaction(t2); // // Feature feature; // Feature[] ORIGIONAL = roadFeatures; // Feature[] REMOVE = new Feature[ORIGIONAL.length - 1]; // Feature[] ADD = new Feature[ORIGIONAL.length + 1]; // Feature[] FINAL = new Feature[ORIGIONAL.length]; // int i; // int index; // index = 0; // // for (i = 0; i < ORIGIONAL.length; i++) { // feature = ORIGIONAL[i]; // LOGGER.info("id is " + feature.getID()); // // if (!feature.getID().equals("road.rd1")) { // REMOVE[index++] = feature; // } // } // // for (i = 0; i < ORIGIONAL.length; i++) { // ADD[i] = ORIGIONAL[i]; // } // // ADD[i] = newRoad; // // for (i = 0; i < REMOVE.length; i++) { // FINAL[i] = REMOVE[i]; // } // // FINAL[i] = newRoad; // // // start of with ORIGINAL // assertTrue(covers(road.getFeatures().reader(), ORIGIONAL)); // // // road1 removes road.rd1 on t1 // // ------------------------------- // // - tests transaction independence from DataStore // road1.removeFeatures(rd1Filter); // // // still have ORIGIONAL and t1 has REMOVE // assertTrue(covers(road.getFeatures().reader(), ORIGIONAL)); // assertTrue(covers(road1.getFeatures().reader(), REMOVE)); // // // road2 adds road.rd4 on t2 // // ---------------------------- // // - tests transaction independence from each other // FeatureReader<SimpleFeatureType, SimpleFeature> reader = DataUtilities.reader(new Feature[] { newRoad, }); // road2.addFeatures(reader); // // // We still have ORIGIONAL, t1 has REMOVE, and t2 has ADD // assertTrue(covers(road.getFeatures().reader(), ORIGIONAL)); // assertTrue(covers(road1.getFeatures().reader(), REMOVE)); // assertTrue(coversLax(road2.getFeatures().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 // assertTrue(covers(road.getFeatures().reader(), REMOVE)); // assertTrue(covers(road1.getFeatures().reader(), REMOVE)); // assertTrue(coversLax(road2.getFeatures().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) // assertTrue(coversLax(road.getFeatures().reader(), FINAL)); // assertTrue(coversLax(road1.getFeatures().reader(), FINAL)); // assertTrue(coversLax(road2.getFeatures().reader(), FINAL)); // } boolean isLocked(String typeName, String fid) { InProcessLockingManager lockingManager = (InProcessLockingManager) data.getLockingManager(); return lockingManager.isLocked(typeName, fid); } // // FeatureLocking Testing // /* * Test for void lockFeatures() */ public void testLockFeatures() throws IOException { FeatureLock lock = FeatureLockFactory.generate("test", LOCK_DURATION); FeatureLocking<SimpleFeatureType, SimpleFeature> road = (FeatureLocking<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); road.setFeatureLock(lock); assertFalse(isLocked("road", "road.rd1")); road.lockFeatures(); assertTrue(isLocked("road", "road.rd1")); } public void testUnLockFeatures() throws IOException { FeatureLock lock = FeatureLockFactory.generate("test", LOCK_DURATION); FeatureLocking<SimpleFeatureType, SimpleFeature> road = (FeatureLocking<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); road.setFeatureLock(lock); road.lockFeatures(); try { road.unLockFeatures(); fail("unlock should fail due on AUTO_COMMIT"); } catch (IOException expected) { } 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(); } public void testLockFeatureInteraction() throws IOException { FeatureLock lockA = FeatureLockFactory.generate("LockA", LOCK_DURATION); FeatureLock lockB = FeatureLockFactory.generate("LockB", LOCK_DURATION); Transaction t1 = new DefaultTransaction(); Transaction t2 = new DefaultTransaction(); FeatureLocking<SimpleFeatureType, SimpleFeature> road1 = (FeatureLocking<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); FeatureLocking<SimpleFeatureType, SimpleFeature> road2 = (FeatureLocking<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); road1.setTransaction(t1); road2.setTransaction(t2); road1.setFeatureLock(lockA); road2.setFeatureLock(lockB); assertFalse(isLocked("road", "road.rd1")); assertFalse(isLocked("road", "road.rd2")); assertFalse(isLocked("road", "road.rd3")); road1.lockFeatures(rd1Filter); assertTrue(isLocked("road", "road.rd1")); assertFalse(isLocked("road", "road.rd2")); assertFalse(isLocked("road", "road.rd3")); road2.lockFeatures(rd2Filter); assertTrue(isLocked("road", "road.rd1")); assertTrue(isLocked("road", "road.rd2")); assertFalse(isLocked("road", "road.rd3")); try { road1.unLockFeatures(rd1Filter); fail("need authorization"); } catch (IOException expected) { } t1.addAuthorization(lockA.getAuthorization()); try { road1.unLockFeatures(rd2Filter); fail("need correct authorization"); } catch (IOException expected) { } road1.unLockFeatures(rd1Filter); assertFalse(isLocked("road", "road.rd1")); assertTrue(isLocked("road", "road.rd2")); assertFalse(isLocked("road", "road.rd3")); t2.addAuthorization(lockB.getAuthorization()); road2.unLockFeatures(rd2Filter); assertFalse(isLocked("road", "road.rd1")); assertFalse(isLocked("road", "road.rd2")); assertFalse(isLocked("road", "road.rd3")); } public void testGetFeatureLockingExpire() throws Exception { FeatureLock lock = FeatureLockFactory.generate("Timed", 200); FeatureLocking<SimpleFeatureType, SimpleFeature> road = (FeatureLocking<SimpleFeatureType, SimpleFeature>) data.getFeatureSource("road"); road.setFeatureLock(lock); assertFalse(isLocked("road", "road.rd1")); road.lockFeatures(rd1Filter); assertTrue(isLocked("road", "road.rd1")); long then = System.currentTimeMillis(); do { Thread.sleep(200); } while ((System.currentTimeMillis() - then) < 200); assertFalse(isLocked("road", "road.rd1")); } }