package org.geoserver.gss;
import static java.util.Collections.*;
import static org.easymock.EasyMock.*;
import static org.geoserver.gss.GSSCore.*;
import static org.geotools.data.DataUtilities.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import javax.xml.namespace.QName;
import net.opengis.wfs.DeleteElementType;
import net.opengis.wfs.TransactionType;
import net.opengis.wfs.WfsFactory;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.config.GeoServer;
import org.geoserver.data.test.LiveDbmsData;
import org.geoserver.data.test.TestData;
import org.geoserver.gss.GSSInfo.GSSMode;
import org.geoserver.test.GeoServerAbstractTestSupport;
import org.geotools.data.DefaultQuery;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Query;
import org.geotools.data.VersioningDataStore;
import org.geotools.data.VersioningFeatureStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.Id;
import org.opengis.filter.identity.FeatureId;
/**
* Tests the central manager
*/
public class SynchronizationManagerTest extends GeoServerAbstractTestSupport {
static XpathEngine xpath;
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
DefaultGeoServerSynchronizationService gss;
VersioningDataStore synchStore;
SynchronizationManager synch;
FeatureStore<SimpleFeatureType, SimpleFeature> fsUnits;
FeatureStore<SimpleFeatureType, SimpleFeature> fsUnitTables;
@Override
public TestData buildTestData() throws Exception {
File base = new File("./src/test/resources/");
LiveDbmsData data = new LiveDbmsData(new File(base, "data_dir"), "unit", new File(base,
"unit.sql"));
List<String> filteredPaths = data.getFilteredPaths();
filteredPaths.clear();
filteredPaths.add("workspaces/topp/synch/datastore.xml");
return data;
}
@Override
protected void setUpInternal() throws Exception {
// configure the GSS service
GeoServer gs = getGeoServer();
GSSInfo gssInfo = gs.getService(GSSInfo.class);
gssInfo.setMode(GSSMode.Central);
gssInfo.setVersioningDataStore(getCatalog().getDataStoreByName("synch"));
gs.save(gssInfo);
// initialize the GSS service
Map gssBeans = applicationContext
.getBeansOfType(DefaultGeoServerSynchronizationService.class);
gss = (DefaultGeoServerSynchronizationService) gssBeans.values().iterator().next();
gss.core.ensureCentralEnabled();
// grab the synch manager
synch = (SynchronizationManager) applicationContext.getBeansOfType(
SynchronizationManager.class).values().iterator().next();
// disable automated scheduling, we control how does what here
Timer timer = (Timer) applicationContext.getBean("gssTimerFactory");
timer.cancel();
// make some tables synchronised
synchStore = (VersioningDataStore) getCatalog().getDataStoreByName("synch").getDataStore(
null);
FeatureStore<SimpleFeatureType, SimpleFeature> fs = (FeatureStore<SimpleFeatureType, SimpleFeature>) synchStore
.getFeatureSource(SYNCH_TABLES);
long restrectedId = addFeature(fs, "restricted", "2");
long roadsId = addFeature(fs, "roads", "2");
synchStore.setVersioned("restricted", true, null, null);
synchStore.setVersioned("roads", true, null, null);
// add some units
fsUnits = (FeatureStore<SimpleFeatureType, SimpleFeature>) synchStore
.getFeatureSource(SYNCH_UNITS);
long mangoId = addFeature(fsUnits, "unit1", "http://localhost:8081/geoserver/ows",
null, null, null, null, 60, 10, false);
// link units and tables
fsUnitTables = (FeatureStore<SimpleFeatureType, SimpleFeature>) synchStore
.getFeatureSource(SYNCH_UNIT_TABLES);
addFeature(fsUnitTables, mangoId, restrectedId, null, null, null, null);
// addFeature(fsUnitTables, mangoId, roadsId, null, null, null, null);
}
/**
* Utility method to add a feature in a store and get back the generated id (which is supposed
* to be a integer/long number)
*
* @param fs
* @param attributes
* @return
* @throws IOException
*/
long addFeature(FeatureStore<SimpleFeatureType, SimpleFeature> fs, Object... attributes)
throws IOException {
SimpleFeatureBuilder fb = new SimpleFeatureBuilder(fs.getSchema());
List<FeatureId> ids = fs.addFeatures(collection((fb.buildFeature(null, attributes))));
String id = ids.get(0).getID();
return Long.parseLong(id.substring(fs.getSchema().getTypeName().length() + 1));
}
SimpleFeature getSingleFeature(FeatureSource<SimpleFeatureType, SimpleFeature> fs, Filter f) throws IOException {
FeatureIterator<SimpleFeature> fi = null;
try {
fi = fs.getFeatures(f).features();
return fi.next();
} finally {
if(fi != null) {
fi.close();
}
}
}
public void testConnectionFailure() throws Exception {
// create mock objects that will simulate a connection failure
GSSClient client = createMock(GSSClient.class);
expect(client.getCentralRevision((QName) anyObject())).andThrow(
new IOException("Host unreachable"));
replay(client);
GSSClientFactory factory = createMock(GSSClientFactory.class);
expect(factory.createClient(new URL("http://localhost:8081/geoserver/ows"), null, null))
.andReturn(client);
replay(factory);
synch.clientFactory = factory;
// perform synch
Date start = new Date();
synch.synchronizeOustandlingLayers();
Date end = new Date();
// check we stored the last failure marker
SimpleFeature f = getSingleFeature(fsUnitTables, ff.equal(ff.property("table_id"), ff.literal(1), false));
Date lastFailure = (Date) f.getAttribute("last_failure");
assertNotNull(lastFailure);
assertTrue(lastFailure.compareTo(start) >= 0 && lastFailure.compareTo(end) <= 0);
// check we marked the unit as failed
f = getSingleFeature(fsUnits, ff.equal(ff.property("unit_name"), ff.literal("unit1"), false));
assertTrue((Boolean) f.getAttribute("errors"));
}
public void testEmptyUpdates() throws Exception {
QName typeName = new QName("http://www.openplans.org/spearfish", "restricted");
// build a "no loca changes" post diff
PostDiffType postDiff = new PostDiffType();
postDiff.setFromVersion(-1);
postDiff.setToVersion(-1);
postDiff.setTypeName(typeName);
postDiff.setTransaction(WfsFactory.eINSTANCE.createTransactionType());
// create mock objects that will simulate a connection failure
GSSClient client = createMock(GSSClient.class);
expect(client.getCentralRevision((QName) anyObject())).andReturn(new Long(-1));
client.postDiff(postDiff);
expect(client.getDiff((GetDiffType) anyObject())).andReturn(new GetDiffResponseType());
replay(client);
GSSClientFactory factory = createMock(GSSClientFactory.class);
expect(factory.createClient(new URL("http://localhost:8081/geoserver/ows"), null, null))
.andReturn(client);
replay(factory);
synch.clientFactory = factory;
// perform synch
Date start = new Date();
synch.synchronizeOustandlingLayers();
Date end = new Date();
// check we stored the last synch marker
SimpleFeature f = getSingleFeature(fsUnitTables, ff.equal(ff.property("table_id"), ff.literal(1), false));
Date lastSynch = (Date) f.getAttribute("last_synchronization");
assertNotNull(lastSynch);
assertTrue(lastSynch.compareTo(start) >= 0 && lastSynch.compareTo(end) <= 0);
assertNull(f.getAttribute("last_failure"));
// check we marked the unit as succeded
f = getSingleFeature(fsUnits, ff.equal(ff.property("unit_name"), ff.literal("unit1"), false));
assertFalse((Boolean) f.getAttribute("errors"));
}
public void testLocalChanges() throws Exception {
// apply a local change on Central so that we'll get a non empty transaction sent to the client
VersioningFeatureStore restricted = (VersioningFeatureStore) synchStore.getFeatureSource("restricted");
SimpleFeatureType schema = restricted.getSchema();
// remove the third feature
Id removeFilter = ff.id(singleton(ff.featureId("restricted.c15e76ab-e44b-423e-8f85-f6d9927b878a")));
restricted.removeFeatures(removeFilter);
assertEquals(3, restricted.getCount(Query.ALL));
// build the expected PostDiff request
QName typeName = new QName("http://www.openplans.org/spearfish", "restricted");
PostDiffType postDiff = new PostDiffType();
postDiff.setFromVersion(-1);
postDiff.setToVersion(3);
postDiff.setTypeName(typeName);
TransactionType changes = WfsFactory.eINSTANCE.createTransactionType();
DeleteElementType delete = WfsFactory.eINSTANCE.createDeleteElementType();
delete.setTypeName(typeName);
delete.setFilter(removeFilter);
changes.getDelete().add(delete);
postDiff.setTransaction(changes);
// create mock objects that will check the calls are flowing as expected
GSSClient client = createMock(GSSClient.class);
expect(client.getCentralRevision((QName) anyObject())).andReturn(new Long(-1));
client.postDiff(postDiff);
expect(client.getDiff((GetDiffType) anyObject())).andReturn(new GetDiffResponseType());
replay(client);
GSSClientFactory factory = createMock(GSSClientFactory.class);
expect(factory.createClient(new URL("http://localhost:8081/geoserver/ows"), null, null))
.andReturn(client);
replay(factory);
synch.clientFactory = factory;
// perform synch
Date start = new Date();
synch.synchronizeOustandlingLayers();
Date end = new Date();
// check we stored the last synch marker
SimpleFeature f = getSingleFeature(fsUnitTables, ff.equal(ff.property("table_id"), ff.literal(1), false));
Date lastSynch = (Date) f.getAttribute("last_synchronization");
assertNotNull(lastSynch);
assertTrue(lastSynch.compareTo(start) >= 0 && lastSynch.compareTo(end) <= 0);
assertNull(f.getAttribute("last_failure"));
// check we marked the unit as succeded
f = getSingleFeature(fsUnits, ff.equal(ff.property("unit_name"), ff.literal("unit1"), false));
assertFalse((Boolean) f.getAttribute("errors"));
}
public void testRemoteChanges() throws Exception {
// make sure we start with 4 features
VersioningFeatureStore restricted = (VersioningFeatureStore) synchStore.getFeatureSource("restricted");
assertEquals(4, restricted.getCount(Query.ALL));
// build a "no local changes" postdiff
QName typeName = new QName("http://www.openplans.org/spearfish", "restricted");
PostDiffType postDiff = new PostDiffType();
postDiff.setFromVersion(-1);
postDiff.setToVersion(-1);
postDiff.setTypeName(typeName);
postDiff.setTransaction(WfsFactory.eINSTANCE.createTransactionType());
// build the expected GetDiff object
GetDiffType getDiff = new GetDiffType();
getDiff.setTypeName(typeName);
getDiff.setFromVersion(-1);
// build a GetDiffResponse that will trigger a deletion
GetDiffResponseType gdr = new GetDiffResponseType();
gdr.setFromVersion(-1);
gdr.setFromVersion(6);
gdr.setTypeName(typeName);
TransactionType changes = WfsFactory.eINSTANCE.createTransactionType();
DeleteElementType delete = WfsFactory.eINSTANCE.createDeleteElementType();
delete.setTypeName(typeName);
Id removeFilter = ff.id(singleton(ff.featureId("restricted.c15e76ab-e44b-423e-8f85-f6d9927b878a")));
delete.setFilter(removeFilter);
changes.getDelete().add(delete);
gdr.setTransaction(changes);
// create mock objects that will check the calls are flowing as expected
GSSClient client = createMock(GSSClient.class);
expect(client.getCentralRevision((QName) anyObject())).andReturn(new Long(-1));
client.postDiff(postDiff);
expect(client.getDiff((GetDiffType) anyObject())).andReturn(gdr);
replay(client);
GSSClientFactory factory = createMock(GSSClientFactory.class);
expect(factory.createClient(new URL("http://localhost:8081/geoserver/ows"), null, null))
.andReturn(client);
replay(factory);
synch.clientFactory = factory;
// perform synch
Date start = new Date();
synch.synchronizeOustandlingLayers();
Date end = new Date();
// check we stored the last synch marker
SimpleFeature f = getSingleFeature(fsUnitTables, ff.equal(ff.property("table_id"), ff.literal(1), false));
Date lastSynch = (Date) f.getAttribute("last_synchronization");
assertNotNull(lastSynch);
assertTrue(lastSynch.compareTo(start) >= 0 && lastSynch.compareTo(end) <= 0);
assertNull(f.getAttribute("last_failure"));
// check we marked the unit as succeded
f = getSingleFeature(fsUnits, ff.equal(ff.property("unit_name"), ff.literal("unit1"), false));
assertFalse((Boolean) f.getAttribute("errors"));
// check the deletion actually happened locally
assertEquals(3, restricted.getCount(Query.ALL));
assertEquals(0, restricted.getCount(new DefaultQuery("restricted", removeFilter)));
}
}