Package org.geoserver.gss

Source Code of org.geoserver.gss.SynchronizationManager

/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.gss;

import static org.geoserver.gss.GSSCore.*;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;

import net.opengis.wfs.TransactionType;

import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.gss.GSSInfo.GSSMode;
import org.geoserver.wfsv.VersioningTransactionConverter;
import org.geotools.data.DefaultQuery;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureDiffReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.VersioningDataStore;
import org.geotools.data.VersioningFeatureStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureIterator;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;

/**
* This object gets periodically invoked to perform all outstanding layer synchronisations with the
* units
*
* @author Andrea Aime - OpenGeo
*/
public class SynchronizationManager extends TimerTask {

    static final Logger LOGGER = Logging.getLogger(DefaultGeoServerSynchronizationService.class);

    FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);

    Catalog catalog;

    GSSCore core;

    GSSClientFactory clientFactory;

    public SynchronizationManager(GeoServer geoServer, GSSClientFactory clientFactory) {
        this.catalog = geoServer.getCatalog();
        this.core = new GSSCore(geoServer);
        this.clientFactory = clientFactory;
    }

    /**
     * Runs the synchronisation. To be used by {@link Timer}, if you need to manually run the
     * synchronization please invoke {@link #synchronizeOustandlingLayers()} instead.
     */
    @Override
    public void run() {
        try {
            synchronizeOustandlingLayers();
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, "Error occurred while running the scheduled synchronisation",
                    e);
        }
    }

    /**
     * Runs the synchronisation on all unit layers that require it (all the ones that haven't
     * synchronised according to the requested frequency and that are inside the call window)
     *
     * @throws IOException
     */
    public void synchronizeOustandlingLayers() throws IOException {
        // make sure we're properly configured
        core.ensureEnabled();
       
        if (core.getMode() != GSSMode.Central) {
            return;
        }

        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() };
                    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 failed
                    updateUnitStatus(ds, Transaction.AUTO_COMMIT, unitId, true);

                    // blacklist the unit, we'll retry later
                    unitBlacklist.add(unitId);
                } finally {
                    if (transaction != null) {
                        transaction.close();
                    }
                }
            }
        } finally {
            if (fi != null) {
                fi.close();
            }
            if (li != null) {
                li.close();
            }
        }

    }

    /**
     * Updates the "errors" flag in the specified unit
     * @param ds
     * @param transaction
     * @param unitId
     * @param success
     * @throws IOException
     */
    void updateUnitStatus(VersioningDataStore ds, Transaction transaction, int unitId,
            boolean errors) throws IOException {
        FeatureStore<SimpleFeatureType, SimpleFeature> fs = (FeatureStore<SimpleFeatureType, SimpleFeature>) ds
                .getFeatureSource(SYNCH_UNITS);

        SimpleFeatureType tuSchema = fs.getSchema();
        AttributeDescriptor[] atts = new AttributeDescriptor[] { tuSchema.getDescriptor("errors") };
        Object[] values = new Object[] { errors };
        Filter filter = ff.id(Collections.singleton(ff.featureId(SYNCH_UNITS + "." + unitId)));

        fs.setTransaction(transaction);
        fs.modifyFeatures(atts, values, filter);
    }

    /**
     * Turns a table name into a fully qualified layer name
     *
     * @param tableName
     * @return
     */
    QName getLayerName(String tableName) {
        String wsName = core.getVersioningStoreInfo().getWorkspace().getName();
        NamespaceInfo ns = catalog.getNamespaceByPrefix(wsName);
        return new QName(ns.getURI(), tableName, ns.getPrefix());
    }

    protected GSSClient getClient(String address, String username, String password)
            throws MalformedURLException {
        return clientFactory.createClient(new URL(address), username, password);
    }

}
TOP

Related Classes of org.geoserver.gss.SynchronizationManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.