FeatureIterator<SimpleFeature> fi = null;
FeatureIterator<SimpleFeature> li = null;
try {
// grab the layers to be synchronised
VersioningDataStore ds = core.getVersioningStore();
FeatureSource<SimpleFeatureType, SimpleFeature> outstanding = ds
.getFeatureSource(SYNCH_OUTSTANDING);
DefaultQuery q = new DefaultQuery(SYNCH_OUTSTANDING);
q.setSortBy(new SortBy[] { ff.sort("last_synchronization", SortOrder.ASCENDING) });
LOGGER.info("Performing scheduled synchronisation");
fi = outstanding.getFeatures(q).features();
// the set of units we failed to synchronize with. The problem might be a connection
// timeout, we don't really want to multiply the timeout by the number of layers
Set<Integer> unitBlacklist = new HashSet<Integer>();
while (fi.hasNext()) {
// extract relevant attributes
SimpleFeature layer = fi.next();
int unitId = (Integer) layer.getAttribute("unit_id");
int tableId = (Integer) layer.getAttribute("table_id");
String unitName = (String) layer.getAttribute("unit_name");
String tableName = (String) layer.getAttribute("table_name");
String address = (String) layer.getAttribute("unit_address");
String user = (String) layer.getAttribute("synch_user");
String password = (String) layer.getAttribute("synch_password");
Long getDiffCentralRevision = (Long) layer.getAttribute("getdiff_central_revision");
Long lastUnitRevision = (Long) layer.getAttribute("last_unit_revision");
// avoid the unit that already failed this run, we'll try next run
if (unitBlacklist.contains(unitId)) {
LOGGER.log(Level.INFO, "Unit " + unitName + " is blacklisted "
+ "for this run, skipping " + tableName);
continue;
}
Transaction transaction = null;
try {
// build the transaction with the proper author and commit message
transaction = new DefaultTransaction();
// get the last central revision the client knows about
GSSClient client = getClient(address, user, password);
QName layerName = getLayerName(tableName);
long clientCentralRevision = client.getCentralRevision(layerName);
// compute the diff that we have to send the client. Notice that we have
// to skip over the local change occurred when we last performed a GetDiff
// against the client
VersioningFeatureStore fs = (VersioningFeatureStore) ds
.getFeatureSource(tableName);
fs.setTransaction(transaction);
String fromRevision = clientCentralRevision == -1 ? "FIRST" : String
.valueOf(clientCentralRevision);
TransactionType centralChanges;
LOGGER.log(Level.INFO, "About to compute PostDiff changes. Last central revision known to client " + clientCentralRevision + ", last GetDiff central revision " + getDiffCentralRevision);
if (getDiffCentralRevision == null || clientCentralRevision >= getDiffCentralRevision) {
// either first time or we don't need to make jumps
LOGGER.log(Level.INFO, "First PostDiff or clientRevion same as the last central one, computing diff from " + fromRevision + " to LAST");
FeatureDiffReader fdr = fs.getDifferences(fromRevision, "LAST", null, null);
centralChanges = new VersioningTransactionConverter().convert(fdr,
TransactionType.class);
} else {
// we need to jump over the last local changes
String before = String.valueOf(getDiffCentralRevision - 1);
String after = String.valueOf(getDiffCentralRevision);
LOGGER.log(Level.INFO, "Client revision lower than the server one, computing diff from " + fromRevision + " to " + before + " and merging with diffs from " + after + " to LAST");
FeatureDiffReader fdr1 = fs.getDifferences(fromRevision, before, null, null);
FeatureDiffReader fdr2 = fs.getDifferences(after, "LAST", null, null);
FeatureDiffReader[] fdr = new FeatureDiffReader[] { fdr1, fdr2 };
centralChanges = new VersioningTransactionConverter().convert(fdr,
TransactionType.class);
}
// what is the latest change on this layer? (worst case it's the last GetDiff
// from this Unit)
long lastCentralRevision = clientCentralRevision;
li = fs.getLog("LAST", fromRevision, null, null, 1).features();
if (li.hasNext()) {
lastCentralRevision = (Long) li.next().getAttribute("revision");
}
li.close();
// finally run the PostDiff
PostDiffType postDiff = new PostDiffType();
postDiff.setTypeName(layerName);
postDiff.setFromVersion(clientCentralRevision);
postDiff.setToVersion(lastCentralRevision);
postDiff.setTransaction(centralChanges);
client.postDiff(postDiff);
// grab the changes from the client and apply them locally
GetDiffType getDiff = new GetDiffType();
getDiff.setFromVersion(lastUnitRevision == null ? -1 : lastUnitRevision);
getDiff.setTypeName(layerName);
GetDiffResponseType gdr = client.getDiff(getDiff);
TransactionType unitChanges = gdr.getTransaction();
core.applyChanges(unitChanges, fs);
// mark down this layer as succesfully synchronised
FeatureStore<SimpleFeatureType, SimpleFeature> tuMetadata = (FeatureStore<SimpleFeatureType, SimpleFeature>) ds
.getFeatureSource(SYNCH_UNIT_TABLES);
tuMetadata.setTransaction(transaction);
SimpleFeatureType tuSchema = tuMetadata.getSchema();
int unitChangeCount = core.countChanges(unitChanges);
int centralChangeCount = core.countChanges(centralChanges);
if (unitChangeCount == 0 && centralChangeCount == 0) {
// just update the last_synch marker, as nothing else happened and
// this way we can avoid eating away central revision number (which
// might go up very rapidly otherwise)
AttributeDescriptor[] atts = new AttributeDescriptor[] { tuSchema
.getDescriptor("last_synchronization") };
Object[] values = new Object[] { new Date() };
Filter filter = ff.and(ff.equals(ff.property("table_id"), ff
.literal(tableId)), ff.equals(ff.property("unit_id"), ff
.literal(unitId)));
tuMetadata.modifyFeatures(atts, values, filter);
} else {
AttributeDescriptor[] atts = new AttributeDescriptor[] {
tuSchema.getDescriptor("last_synchronization"),
tuSchema.getDescriptor("getdiff_central_revision"),
tuSchema.getDescriptor("last_unit_revision") };
Object[] values = new Object[] { new Date(),
Long.parseLong(fs.getVersion()), gdr.getToVersion() };
Filter filter = ff.and(ff.equals(ff.property("table_id"), ff
.literal(tableId)), ff.equals(ff.property("unit_id"), ff
.literal(unitId)));
tuMetadata.modifyFeatures(atts, values, filter);
}
// mark the unit as succeffully updated
updateUnitStatus(ds, transaction, unitId, false);
// the the commit log
transaction.putProperty(VersioningDataStore.AUTHOR, "gss");
transaction.putProperty(VersioningDataStore.MESSAGE, "Synchronizing with Unit '"
+ unitName + "' on table '" + tableName + "': " + centralChangeCount
+ " changes sent and " + unitChangeCount + " changes received");
// close up
transaction.commit();
LOGGER.log(Level.INFO, "Successfull synchronisation of table " + tableName
+ " for unit " + unitName + "(" + centralChangeCount
+ " changes sent to the Unit, " + unitChangeCount
+ " change incoming from the Unit)");
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Synchronisation of table " + tableName + " for unit "
+ unitName + " failed", e);
// rollback all current changes
transaction.rollback();
// if anything at all went bad mark the layer synch as failed
FeatureStore<SimpleFeatureType, SimpleFeature> tuMetadata = (FeatureStore<SimpleFeatureType, SimpleFeature>) ds
.getFeatureSource(SYNCH_UNIT_TABLES);
SimpleFeatureType tuSchema = tuMetadata.getSchema();
AttributeDescriptor[] atts = new AttributeDescriptor[] { tuSchema
.getDescriptor("last_failure"), };
Object[] values = new Object[] { new Date() };