/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2013, Geomatys * * 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.geotoolkit.db.postgres; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.sis.feature.FeatureExt; import org.apache.sis.feature.builder.AttributeRole; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.geotoolkit.data.FeatureIterator; import org.geotoolkit.data.FeatureStoreRuntimeException; import org.geotoolkit.data.query.QueryBuilder; import org.geotoolkit.data.session.Session; import org.apache.sis.storage.DataStoreException; import org.junit.Test; import org.opengis.util.GenericName; import org.opengis.parameter.ParameterValueGroup; import static org.geotoolkit.db.postgres.PostgresFeatureStoreFactory.*; import org.geotoolkit.factory.FactoryFinder; import org.geotoolkit.utility.parameter.ParametersExt; import org.geotoolkit.version.Version; import org.geotoolkit.version.VersionControl; import org.geotoolkit.version.VersioningException; import static org.junit.Assert.*; import org.junit.Assume; import org.junit.BeforeClass; import org.geotoolkit.storage.DataStores; import org.opengis.filter.FilterFactory; import org.opengis.filter.identity.FeatureId; import org.apache.sis.referencing.CommonCRS; import org.opengis.feature.Feature; import org.opengis.feature.FeatureType; /** * * @author Johann Sorel (Geomatys) */ public class PostgresVersioningTest extends org.geotoolkit.test.TestBase { private static final FilterFactory FF = FactoryFinder.getFilterFactory(null); private static final GeometryFactory GF = new GeometryFactory(); private static final FeatureType FTYPE_SIMPLE; static{ FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName("testTable"); ftb.addAttribute(String.class).setName("id").addRole(AttributeRole.IDENTIFIER_COMPONENT); ftb.addAttribute(Boolean.class).setName("boolean"); ftb.addAttribute(Integer.class).setName("integer"); ftb.addAttribute(Point.class).setName("point").setCRS(CommonCRS.WGS84.normalizedGeographic()).addRole(AttributeRole.DEFAULT_GEOMETRY); ftb.addAttribute(String.class).setName("string"); FTYPE_SIMPLE = ftb.build(); } private PostgresFeatureStore store; public PostgresVersioningTest(){ } private static ParameterValueGroup params; /** * <p>Find JDBC connection parameters in specified file at * "/home/.geotoolkit.org/test-pgfeature.properties".<br/> * If properties file doesn't find all tests are skipped.</p> * * <p>To lunch tests user should create file with this architecture<br/> * for example : <br/> * database = junit (table name)<br/> * port = 5432 (port number)<br/> * schema = public (schema name)<br/> * user = postgres (user login)<br/> * password = postgres (user password)<br/> * simpletype = false <br/> * namespace = no namespace</p> * @throws IOException */ @BeforeClass public static void beforeClass() throws IOException { String path = System.getProperty("user.home"); path += "/.geotoolkit.org/test-pgfeature.properties"; final File f = new File(path); Assume.assumeTrue(f.exists()); final Properties properties = new Properties(); properties.load(new FileInputStream(f)); params = FeatureExt.toParameter((Map)properties, PARAMETERS_DESCRIPTOR, false); } private void reload(boolean simpleType) throws DataStoreException, VersioningException { if(store != null){ store.close(); } //open in complex type to delete all types ParametersExt.getOrCreateValue(params, PostgresFeatureStoreFactory.SIMPLETYPE.getName().getCode()).setValue(false); store = (PostgresFeatureStore) DataStores.open(params); for(GenericName n : store.getNames()){ VersionControl vc = store.getVersioning(n.toString()); vc.dropVersioning(); store.deleteFeatureType(n.toString()); } assertTrue(store.getNames().isEmpty()); store.close(); //reopen the way it was asked ParametersExt.getOrCreateValue(params, PostgresFeatureStoreFactory.SIMPLETYPE.getName().getCode()).setValue(simpleType); store = (PostgresFeatureStore) DataStores.open(params); assertTrue(store.getNames().isEmpty()); //delete historisation functions, he must create them himself store.dropHSFunctions(); } @Test public void testSimpleTypeVersioning() throws DataStoreException, VersioningException { reload(true); List<Version> versions; Version version; Feature feature; FeatureId fid; Version v1; Version v2; Version v3; FeatureIterator ite; final QueryBuilder qb = new QueryBuilder(); //create table final FeatureType refType = FTYPE_SIMPLE; store.createFeatureType(refType); assertEquals(1, store.getNames().size()); assertNotNull(store.getQueryCapabilities()); assertTrue(store.getQueryCapabilities().handleVersioning()); //get version control final VersionControl vc = store.getVersioning(refType.getName().toString()); assertNotNull(vc); assertTrue(vc.isEditable()); assertFalse(vc.isVersioned()); //////////////////////////////////////////////////////////////////////// //start versioning ///////////////////////////////////////////////////// vc.startVersioning(); assertTrue(vc.isVersioned()); versions = vc.list(); assertTrue(versions.isEmpty()); //check the version table is not visible in the feature types store.refreshMetaModel(); final Set<GenericName> names = store.getNames(); assertEquals(1, names.size()); //////////////////////////////////////////////////////////////////////// //make an insert /////////////////////////////////////////////////////// final Point firstPoint = GF.createPoint(new Coordinate(56, 45)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.TRUE); feature.setPropertyValue("integer",14); feature.setPropertyValue("point",firstPoint); feature.setPropertyValue("string","someteststring"); store.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have one version versions = vc.list(); assertEquals(1, versions.size()); version = versions.get(0); Date date = version.getDate(); //ensure normal reading is correct without version---------------------- qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.TRUE, feature.getPropertyValue("boolean")); assertEquals(14, feature.getPropertyValue("integer")); assertEquals(firstPoint, feature.getPropertyValue("point")); assertEquals("someteststring", feature.getPropertyValue("string")); fid = FeatureExt.getId(feature); }finally{ ite.close(); } //ensure normal reading is correct with version------------------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(version.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.TRUE, feature.getPropertyValue("boolean")); assertEquals(14, feature.getPropertyValue("integer")); assertEquals(firstPoint, feature.getPropertyValue("point")); assertEquals("someteststring", feature.getPropertyValue("string")); }finally{ ite.close(); } try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //////////////////////////////////////////////////////////////////////// //make an update /////////////////////////////////////////////////////// final Point secondPoint = GF.createPoint(new Coordinate(-12, 21)); final Map<String,Object> updates = new HashMap<>(); updates.put("boolean", Boolean.FALSE); updates.put("integer", -3); updates.put("point", secondPoint); updates.put("string", "anothertextupdated"); store.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); //we should have two versions versions = vc.list(); assertEquals(2, versions.size()); v1 = versions.get(0); v2 = versions.get(1); //should be ordered starting from the oldest assertTrue(v1.getDate().compareTo(v2.getDate()) < 0); //ensure normal reading is correct without version---------------------- qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.FALSE, feature.getPropertyValue("boolean")); assertEquals(-3, feature.getPropertyValue("integer")); assertEquals(secondPoint, feature.getPropertyValue("point")); assertEquals("anothertextupdated",feature.getPropertyValue("string")); }finally{ ite.close(); } //ensure normal reading is correct with version------------------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v2.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.FALSE, feature.getPropertyValue("boolean")); assertEquals(-3, feature.getPropertyValue("integer")); assertEquals(secondPoint, feature.getPropertyValue("point")); assertEquals("anothertextupdated",feature.getPropertyValue("string")); }finally{ ite.close(); } //ensure reading a previous version works ------------------------------ qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v1.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.TRUE, feature.getPropertyValue("boolean")); assertEquals(14, feature.getPropertyValue("integer")); assertEquals(firstPoint, feature.getPropertyValue("point")); assertEquals("someteststring", feature.getPropertyValue("string")); }finally{ ite.close(); } //ensure reading a previous version using not exact date---------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionDate(new Date(v1.getDate().getTime()+400)); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.TRUE, feature.getPropertyValue("boolean")); assertEquals(14, feature.getPropertyValue("integer")); assertEquals(firstPoint, feature.getPropertyValue("point")); assertEquals("someteststring", feature.getPropertyValue("string")); }finally{ ite.close(); } //////////////////////////////////////////////////////////////////////// //delete record //////////////////////////////////////////////////////// store.removeFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid))); qb.reset(); qb.setTypeName(refType.getName()); assertEquals(0, store.getCount(qb.buildQuery())); //we should have three versions versions = vc.list(); assertEquals(3, versions.size()); v1 = versions.get(0); v2 = versions.get(1); v3 = versions.get(2); //should be ordered starting from the oldest assertTrue(v2.getDate().compareTo(v1.getDate()) > 0); assertTrue(v3.getDate().compareTo(v2.getDate()) > 0); //ensure we have nothing if no version set ----------------------------- qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); //ensure we have nothing if latest version set ------------------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v3.getLabel()); assertTrue(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); //ensure we have nothing with date after deletion ---------------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionDate(new Date(v3.getDate().getTime()+400)); assertTrue(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); //ensure reading version 1 works --------------------------------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v1.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.TRUE, feature.getProperty("boolean").getValue()); assertEquals(14, feature.getProperty("integer").getValue()); assertEquals(firstPoint, feature.getProperty("point").getValue()); assertEquals("someteststring", feature.getProperty("string").getValue()); }finally{ ite.close(); } //ensure reading version 2 works --------------------------------------- qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v2.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.FALSE, feature.getProperty("boolean").getValue()); assertEquals(-3, feature.getProperty("integer").getValue()); assertEquals(secondPoint, feature.getProperty("point").getValue()); assertEquals("anothertextupdated",feature.getProperty("string").getValue()); }finally{ ite.close(); } //////////////////////////////////////////////////////////////////////// //drop versioning ////////////////////////////////////////////////////// vc.dropVersioning(); assertTrue(vc.isEditable()); assertFalse(vc.isVersioned()); versions = vc.list(); assertTrue(versions.isEmpty()); //ensure we have no record---------------------------------------------- qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); } /** * Check versions are created on each call on the session. * * @throws DataStoreException * @throws VersioningException */ @Test public void testVersioningSynchrone() throws DataStoreException, VersioningException{ reload(true); List<Version> versions; Version version; Feature feature; FeatureId fid; FeatureIterator ite; final QueryBuilder qb = new QueryBuilder(); final FeatureType refType = FTYPE_SIMPLE; store.createFeatureType(refType); final VersionControl vc = store.getVersioning(refType.getName().toString()); //////////////////////////////////////////////////////////////////////// //start versioning ///////////////////////////////////////////////////// vc.startVersioning(); versions = vc.list(); assertTrue(versions.isEmpty()); final Session session = store.createSession(false); //////////////////////////////////////////////////////////////////////// //make an insert /////////////////////////////////////////////////////// final Point firstPoint = GF.createPoint(new Coordinate(56, 45)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.TRUE); feature.setPropertyValue("integer",14); feature.setPropertyValue("point",firstPoint); feature.setPropertyValue("string","someteststring"); session.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have one version versions = vc.list(); assertEquals(1, versions.size()); version = versions.get(0); Date date = version.getDate(); //ensure normal reading is correct without version---------------------- qb.reset(); qb.setTypeName(refType.getName().toString()); ite = session.getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //////////////////////////////////////////////////////////////////////// //make an update 1 ///////////////////////////////////////////////////// final Point secondPoint = GF.createPoint(new Coordinate(-12, 21)); Map<String,Object> updates = new HashMap<>(); updates.put("boolean", Boolean.FALSE); updates.put("integer", -3); updates.put("point", secondPoint); updates.put("string", "anothertextupdated"); session.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); //we should have two versions versions = vc.list(); assertEquals(2, versions.size()); //////////////////////////////////////////////////////////////////////// //make an update 2 ///////////////////////////////////////////////////// final Point thirdPoint = GF.createPoint(new Coordinate(48, -51)); updates = new HashMap<>(); updates.put("boolean", Boolean.TRUE); updates.put("integer", -89); updates.put("point", thirdPoint); updates.put("string", "thridupdatetext"); session.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); //we should have three versions versions = vc.list(); assertEquals(3, versions.size()); //////////////////////////////////////////////////////////////////////// //delete record //////////////////////////////////////////////////////// session.removeFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid))); qb.reset(); qb.setTypeName(refType.getName().toString()); assertEquals(0, session.getCount(qb.buildQuery())); //we should have four versions versions = vc.list(); assertEquals(4, versions.size()); //////////////////////////////////////////////////////////////////////// //make an insert /////////////////////////////////////////////////////// Point fourthPoint = GF.createPoint(new Coordinate(66, 11)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.FALSE); feature.setPropertyValue("integer",22); feature.setPropertyValue("point",fourthPoint); feature.setPropertyValue("string","fourthupdateString"); session.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have five versions versions = vc.list(); assertEquals(5, versions.size()); } /** * Check versions are created only on session commit calls. * * @throws DataStoreException * @throws VersioningException */ @Test public void testVersioningASynchrone() throws DataStoreException, VersioningException{ reload(true); List<Version> versions; Version version; Feature feature; FeatureId fid; FeatureIterator ite; final QueryBuilder qb = new QueryBuilder(); final FeatureType refType = FTYPE_SIMPLE; store.createFeatureType(refType); final VersionControl vc = store.getVersioning(refType.getName().toString()); //////////////////////////////////////////////////////////////////////// //start versioning ///////////////////////////////////////////////////// vc.startVersioning(); versions = vc.list(); assertTrue(versions.isEmpty()); final Session session = store.createSession(true); //////////////////////////////////////////////////////////////////////// //make an insert /////////////////////////////////////////////////////// final Point firstPoint = GF.createPoint(new Coordinate(56, 45)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.TRUE); feature.setPropertyValue("integer",14); feature.setPropertyValue("point",firstPoint); feature.setPropertyValue("string","someteststring"); session.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have 0 version versions = vc.list(); assertEquals(0, versions.size()); session.commit(); // <-- creates a version //we should have 1 version versions = vc.list(); assertEquals(1, versions.size()); version = versions.get(0); Date date = version.getDate(); //ensure normal reading is correct without version---------------------- qb.reset(); qb.setTypeName(refType.getName().toString()); ite = session.getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //////////////////////////////////////////////////////////////////////// //make 2 updates at the time /////////////////////////////////////////// final Point secondPoint = GF.createPoint(new Coordinate(-12, 21)); Map<String,Object> updates = new HashMap<>(); updates.put("boolean", Boolean.FALSE); updates.put("integer", -3); updates.put("point", secondPoint); updates.put("string", "anothertextupdated"); session.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); //we should have 1 version versions = vc.list(); assertEquals(1, versions.size()); final Point thirdPoint = GF.createPoint(new Coordinate(48, -51)); updates = new HashMap<>(); updates.put("boolean", Boolean.TRUE); updates.put("integer", -89); updates.put("point", thirdPoint); updates.put("string", "thridupdatetext"); session.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); //we should have 1 version versions = vc.list(); assertEquals(1, versions.size()); session.commit(); // <-- creates a version //we should have two versions versions = vc.list(); assertEquals(2, versions.size()); //ensure we read the latest -------------------------------------------- qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.TRUE, feature.getProperty("boolean").getValue()); assertEquals(-89, feature.getProperty("integer").getValue()); assertEquals(thirdPoint, feature.getProperty("point").getValue()); assertEquals("thridupdatetext", feature.getProperty("string").getValue()); }finally{ ite.close(); } //////////////////////////////////////////////////////////////////////// // make delete + insert at the same time /////////////////////////////// session.removeFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid))); qb.reset(); qb.setTypeName(refType.getName().toString()); assertEquals(0, session.getCount(qb.buildQuery())); //we should have two versions versions = vc.list(); assertEquals(2, versions.size()); //////////////////////////////////////////////////////////////////////// //delete record //////////////////////////////////////////////////////// session.removeFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid))); qb.reset(); qb.setTypeName(refType.getName().toString()); assertEquals(0, session.getCount(qb.buildQuery())); //we should have two versions versions = vc.list(); assertEquals(2, versions.size()); Point fourthPoint = GF.createPoint(new Coordinate(66, 11)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.FALSE); feature.setPropertyValue("integer",22); feature.setPropertyValue("point",fourthPoint); feature.setPropertyValue("string","fourthupdateString"); session.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have two versions versions = vc.list(); assertEquals(2, versions.size()); session.commit(); // <-- creates a version //we should have three versions versions = vc.list(); assertEquals(3, versions.size()); //ensure we read the latest -------------------------------------------- qb.reset(); qb.setTypeName(refType.getName().toString()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); assertEquals(Boolean.FALSE, feature.getProperty("boolean").getValue()); assertEquals(22, feature.getProperty("integer").getValue()); assertEquals(fourthPoint, feature.getProperty("point").getValue()); assertEquals("fourthupdateString", feature.getProperty("string").getValue()); }finally{ ite.close(); } } @Test public void testTrimVersioning() throws DataStoreException, VersioningException { reload(true); List<Version> versions; Feature feature; FeatureId fid; Version v0; Version v1; Version v2; FeatureIterator ite; final QueryBuilder qb = new QueryBuilder(); //create table final FeatureType refType = FTYPE_SIMPLE; store.createFeatureType(refType); assertEquals(1, store.getNames().size()); //get version control final VersionControl vc = store.getVersioning(refType.getName().toString()); assertNotNull(vc); assertTrue(vc.isEditable()); assertFalse(vc.isVersioned()); //start versioning ///////////////////////////////////////////////////// vc.startVersioning(); assertTrue(vc.isVersioned()); versions = vc.list(); assertTrue(versions.isEmpty()); //make an insert /////////////////////////////////////////////////////// final Point firstPoint = GF.createPoint(new Coordinate(56, 45)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.TRUE); feature.setPropertyValue("integer",14); feature.setPropertyValue("point",firstPoint); feature.setPropertyValue("string","someteststring"); store.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have one version versions = vc.list(); assertEquals(1, versions.size()); // get identifier //ensure normal reading is correct without version---------------------- qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //make an update /////////////////////////////////////////////////////// final Point secondPoint = GF.createPoint(new Coordinate(-12, 21)); final Map<String,Object> updates = new HashMap<>(); updates.put("boolean", Boolean.FALSE); updates.put("integer", -3); updates.put("point", secondPoint); updates.put("string", "anothertextupdated"); store.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //make a 2nd update /////////////////////////////////////////////////////// final Point thirdPoint = GF.createPoint(new Coordinate(145, -221)); final Map<String,Object> updates2 = new HashMap<>(); updates2.put("boolean", Boolean.FALSE); updates2.put("integer", 150); updates2.put("point", thirdPoint); updates2.put("string", "secondtextupdated"); store.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates2); //get all versions organized in increase dates order. versions = vc.list(); assertEquals(3, versions.size()); v0 = versions.get(0); v1 = versions.get(1); v2 = versions.get(2); /* first trim between v1 date and v2 date (named middle date) to verify * deletion of first version and update v1 date at trim date.*/ final Date middle = new Date((v1.getDate().getTime() + v2.getDate().getTime()) >> 1); vc.trim(middle); versions = vc.list(); assertEquals(2, versions.size()); //ensure version 0 does not exist qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v0.getLabel()); try { store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty(); fail("should not find version"); } catch(FeatureStoreRuntimeException ex) { //ok } //ensure version v1 begin at middle date. assertEquals(vc.list().get(0).getDate().getTime(), middle.getTime()); /* second trim at exactely the begining of the third version to verify, * deletion of second version and third version existence.*/ vc.trim(v2); versions = vc.list(); assertEquals(1, versions.size()); //ensure version 1 does not exist qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v1.getLabel()); try { store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty(); fail("should not find version"); } catch(FeatureStoreRuntimeException ex) { //ok } //ensure version v2 begin time doesn't change. assertEquals(vc.list().get(0).getDate().getTime(), v2.getDate().getTime()); /* third trim just after v3 version date, to verify that v3 * version date become trim date */ final long lastDate = v2.getDate().getTime()+400; vc.trim(new Date(lastDate)); versions = vc.list(); assertEquals(1, versions.size()); //ensure version v2 begin time become lastDate. assertEquals(vc.list().get(0).getDate().getTime(), lastDate); } @Test public void testRevertVersioning() throws DataStoreException, VersioningException { reload(true); List<Version> versions; Feature feature; FeatureId fid; Version v0; Version v1; Version v2; FeatureIterator ite; final QueryBuilder qb = new QueryBuilder(); //create table final FeatureType refType = FTYPE_SIMPLE; store.createFeatureType(refType); assertEquals(1, store.getNames().size()); //get version control final VersionControl vc = store.getVersioning(refType.getName().toString()); assertNotNull(vc); assertTrue(vc.isEditable()); assertFalse(vc.isVersioned()); //start versioning ///////////////////////////////////////////////////// vc.startVersioning(); assertTrue(vc.isVersioned()); versions = vc.list(); assertTrue(versions.isEmpty()); //make an insert /////////////////////////////////////////////////////// final Point firstPoint = GF.createPoint(new Coordinate(56, 45)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.TRUE); feature.setPropertyValue("integer",14); feature.setPropertyValue("point",firstPoint); feature.setPropertyValue("string","someteststring"); store.addFeatures(refType.getName().toString(), Collections.singleton(feature)); //we should have one version versions = vc.list(); assertEquals(1, versions.size()); // get identifier //ensure normal reading is correct without version---------------------- qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //make an update /////////////////////////////////////////////////////// final Point secondPoint = GF.createPoint(new Coordinate(-12, 21)); final Map<String,Object> updates = new HashMap<>(); updates.put("boolean", Boolean.FALSE); updates.put("integer", -3); updates.put("point", secondPoint); updates.put("string", "anothertextupdated"); store.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } //make a remove /////////////////////////////////////////////////////// store.removeFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid))); //ensure test table is empty qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); //get all versions organized in increase dates order. versions = vc.list(); assertEquals(3, versions.size()); v0 = versions.get(0); v1 = versions.get(1); v2 = versions.get(2); /* first revert between v1 date and v2 date (named middle date) to verify * re - insertion of feature in the original base and update v2 ending date become null.*/ final Date middle = new Date((v1.getDate().getTime() + v2.getDate().getTime()) >> 1);// =/2 vc.revert(middle); versions = vc.list(); assertEquals(2, versions.size()); //ensure version v2 does not exist qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v2.getLabel()); try { store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty(); fail("should not find version"); } catch(FeatureStoreRuntimeException ex) { //ok } //ensure test table contain feature from version 1 qb.reset(); qb.setTypeName(refType.getName()); assertFalse(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); Feature featV1; Feature feat; // feature from test base result. qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feat = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } // feature from version v1. qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v1.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ featV1 = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } assertTrue(feat.getProperty("boolean").equals(featV1.getProperty("boolean"))); assertTrue(feat.getProperty("integer").equals(featV1.getProperty("integer"))); assertTrue(feat.getProperty("point").equals(featV1.getProperty("point"))); assertTrue(feat.getProperty("string").equals(featV1.getProperty("string"))); /* second revert at v0 begin date to verify update roll back, and verify * feature update from history table into original base.*/ vc.revert(v0.getDate()); versions = vc.list(); assertEquals(1, versions.size()); qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v1.getLabel()); try { store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty(); fail("should not find version"); } catch(FeatureStoreRuntimeException ex) { //ok } //ensure test table contain feature from version 1 qb.reset(); qb.setTypeName(refType.getName()); assertFalse(store.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); // feature from test base result. qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feat = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } // feature from version v1. qb.reset(); qb.setTypeName(refType.getName()); qb.setVersionLabel(v0.getLabel()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ featV1 = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } assertTrue(feat.getProperty("boolean").equals(featV1.getProperty("boolean"))); assertTrue(feat.getProperty("integer").equals(featV1.getProperty("integer"))); assertTrue(feat.getProperty("point").equals(featV1.getProperty("point"))); assertTrue(feat.getProperty("string").equals(featV1.getProperty("string"))); } @Test public void testDistinctSchema() throws DataStoreException, VersioningException, FileNotFoundException, IOException { reload(true); List<Version> versions; Feature feature; FeatureId fid; Version v0; Version v1; Version v2; FeatureIterator ite; final QueryBuilder qb = new QueryBuilder(); final FeatureType refType = FTYPE_SIMPLE; // ------------------- initialize public2 schema -------------------- /// creation 2eme table PostgresFeatureStore store2; final ParameterValueGroup params2 = params.clone(); params2.parameter("schema").setValue("public2"); store2 = (PostgresFeatureStore) DataStores.open(params2); //-------------- create schema in public2 schema -------------------- try{ store2.getFactory().create(params2); } catch (Exception ex) { //schema public2 already exist } for(GenericName n : store2.getNames()) { VersionControl vc = store2.getVersioning(n.toString()); vc.dropVersioning(); store2.deleteFeatureType(n.toString()); } assertTrue(store2.getNames().isEmpty()); //delete historisation functions, he must create them himself store2.dropHSFunctions(); //-------------- create table in public schema -------------------- store.createFeatureType(refType); assertEquals(1, store.getNames().size()); assertTrue(store2.getNames().isEmpty()); //get version control final VersionControl vcP1 = store.getVersioning(refType.getName().toString()); assertNotNull(vcP1); assertTrue(vcP1.isEditable()); assertFalse(vcP1.isVersioned()); //-------------------- start versioning in public schema --------------- vcP1.startVersioning(); assertTrue(vcP1.isVersioned()); versions = vcP1.list(); assertTrue(versions.isEmpty()); //--------------------- table creation in public2 schema --------------- store2.createFeatureType(refType); assertEquals(1, store2.getNames().size()); //get version control final VersionControl vcP2 = store2.getVersioning(refType.getName().toString()); assertNotNull(vcP2); assertTrue(vcP2.isEditable()); assertFalse(vcP2.isVersioned()); //-------------------- start versioning in public schema --------------- vcP2.startVersioning(); assertTrue(vcP2.isVersioned()); versions = vcP2.list(); assertTrue(versions.isEmpty()); /* insert, update and delete some elements in public schema and verify * public2 schema stay empty, to verify the 2th schema are actions distincts.*/ //make an insert /////////////////////////////////////////////////////// final Point firstPoint = GF.createPoint(new Coordinate(56, 45)); feature = refType.newInstance(); feature.setPropertyValue("id","0"); feature.setPropertyValue("boolean",Boolean.TRUE); feature.setPropertyValue("integer",14); feature.setPropertyValue("point",firstPoint); feature.setPropertyValue("string","someteststring"); store.addFeatures(refType.getName().toString(), Collections.singleton(feature)); // ensure test table in public2 schema is empty qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store2.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); // ensure history test table in public2 schema is empty assertTrue(vcP2.list().isEmpty()); //make an update /////////////////////////////////////////////////////// // get feature to update // get identifier qb.reset(); qb.setTypeName(refType.getName()); ite = store.createSession(true).getFeatureCollection(qb.buildQuery()).iterator(); try{ feature = ite.next(); fid = FeatureExt.getId(feature); }finally{ ite.close(); } try { //wait a bit just to have some space between version dates Thread.sleep(1000); } catch (InterruptedException ex) { fail(ex.getMessage()); } final Point secondPoint = GF.createPoint(new Coordinate(-12, 21)); final Map<String,Object> updates = new HashMap<>(); updates.put("boolean", Boolean.FALSE); updates.put("integer", -3); updates.put("point", secondPoint); updates.put("string", "anothertextupdated"); store.updateFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid)), updates); // ensure test table in public2 schema is empty qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store2.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); // ensure history test table in public2 schema is empty assertTrue(vcP2.list().isEmpty()); //make a remove /////////////////////////////////////////////////////// store.removeFeatures(refType.getName().toString(), FF.id(Collections.singleton(fid))); // ensure test table in public2 schema is empty qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store2.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); // ensure history test table in public2 schema is empty assertTrue(vcP2.list().isEmpty()); //get all versions organized in increase dates order. versions = vcP1.list(); assertEquals(3, versions.size()); v0 = versions.get(0); v1 = versions.get(1); v2 = versions.get(2); vcP1.revert(v1.getDate()); // ensure test table in public2 schema is empty qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store2.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); // ensure history test table in public2 schema is empty assertTrue(vcP2.list().isEmpty()); vcP1.trim(v1.getDate()); // ensure test table in public2 schema is empty qb.reset(); qb.setTypeName(refType.getName()); assertTrue(store2.createSession(true).getFeatureCollection(qb.buildQuery()).isEmpty()); // ensure history test table in public2 schema is empty assertTrue(vcP2.list().isEmpty()); } }