Package org.geoserver.gss

Source Code of org.geoserver.gss.GSSCore

/* 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 java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.opengis.wfs.DeleteElementType;
import net.opengis.wfs.InsertElementType;
import net.opengis.wfs.PropertyType;
import net.opengis.wfs.TransactionType;
import net.opengis.wfs.UpdateElementType;

import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.config.ConfigurationListenerAdapter;
import org.geoserver.config.GeoServer;
import org.geoserver.config.ServiceInfo;
import org.geoserver.gss.GSSInfo.GSSMode;
import org.geotools.data.DataAccess;
import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.data.VersioningDataStore;
import org.geotools.data.jdbc.JDBCUtils;
import org.geotools.data.postgis.VersionedPostgisDataStore;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;

/**
* Provides core services used by both Central and Unit behaviors
*
* @author Andrea Aime - OpenGeo
*/
public class GSSCore {

    // metadata tables and sql to build them
    static final String SYNCH_TABLES = "synch_tables";

    static final String SYNC_TABLES_CREATION = "CREATE TABLE synch_tables(\n"
            + "table_id SERIAL PRIMARY KEY, \n" //
            + "table_name VARCHAR(256) NOT NULL, \n" //
            + "type CHAR(1) NOT NULL CHECK (type in ('p', 'b', '2')))";

    static final String SYNCH_HISTORY = "synch_history";

    static final String SYNCH_HISTORY_CREATION = "CREATE TABLE synch_history(\n"
            + "id SERIAL PRIMARY KEY,\n" //
            + "table_name VARCHAR(256) NOT NULL,\n" //
            + "local_revision BIGINT NOT NULL,\n" //
            + "central_revision BIGINT,\n" //
            + "unique(table_name, local_revision, central_revision))";

    static final String SYNCH_CONFLICTS = "synch_conflicts";

    // conflict can be in 'c', conflict, 'r', resolved or 'm', clean merge state
    // clean merge is a marker stating that the same change occurred both locally
    // and in central, and as such it should not be reported in GetDiff
    static final String SYNCH_CONFLICTS_CREATION = "CREATE TABLE synch_conflicts(\n"
            + "id SERIAL PRIMARY KEY,\n" + "table_name VARCHAR(256) NOT NULL,\n" //
            + "feature_id UUID NOT NULL,\n" //
            + "local_revision BIGINT NOT NULL,\n" // 
            + "date_created TIMESTAMP NOT NULL,\n" //
            + "state CHAR(1) NOT NULL CHECK (state in ('c', 'r', 'm')),\n" // 
            + "date_resolved TIMESTAMP,\n" //
            + "local_feature TEXT,\n" //
            + "unique(table_name, feature_id, local_revision))";

    static final String SYNCH_UNITS = "synch_units";

    static final String SYNCH_UNITS_CREATION = "CREATE TABLE synch_units (\n" //
            + "  unit_id SERIAL PRIMARY KEY,\n" //
            + "  unit_name VARCHAR(1024) NOT NULL,\n" //
            + "  unit_address VARCHAR(2048) NOT NULL,\n" //
            + "  synch_user VARCHAR(256),\n" //
            + "  synch_password VARCHAR(256),\n" //
            + "  time_start TIME,\n" //
            + "  time_end TIME,\n" //
            + "  synch_interval REAL,\n" //
            + "  synch_retry REAL,\n" //
            + "  errors BOOLEAN\n" //
            + ");\n" //
            + "select AddGeometryColumn('synch_units','geom',4326,'GEOMETRY',2)";

    static final String SYNCH_UNIT_TABLES = "synch_unit_tables";

    static final String SYNCH_UNIT_TABLES_CREATION = //
    "CREATE TABLE synch_unit_tables (\n" + //
            "   id SERIAL PRIMARY KEY," + //
            "   unit_id INTEGER NOT NULL REFERENCES synch_units(unit_id),\n" + //
            "   table_id INTEGER NOT NULL REFERENCES synch_tables(table_id),\n" + //
            "   last_synchronization TIMESTAMP,\n" + //
            "   last_failure TIMESTAMP,\n" + //
            "   getdiff_central_revision BIGINT,\n" + //
            "   last_unit_revision BIGINT,\n" + //
            "   unique (unit_id, table_id)\n" + //
            ")";

    static final String SYNCH_OUTSTANDING = "synch_outstanding";

    static final String SYNCH_OUTSTANDING_CREATION = //
    "CREATE VIEW synch_outstanding \n"
            + "AS SELECT synch_tables.*, \n"
            + "          synch_units.*, \n"
            + "          synch_unit_tables.last_synchronization,\n"
            + "          synch_unit_tables.last_failure, \n"
            + "          synch_unit_tables.getdiff_central_revision, \n"
            + "          synch_unit_tables.last_unit_revision\n"
            + "FROM (synch_units inner join synch_unit_tables \n"
            + "                  on synch_units.unit_id = synch_unit_tables.unit_id)\n"
            + "     inner join synch_tables \n"
            + "     on synch_tables.table_id = synch_unit_tables.table_id\n"
            + "WHERE ((time_start < LOCALTIME AND LOCALTIME < time_end) \n"
            + "        OR (time_start IS NULL) OR (time_end IS NULL))\n"
            + "      AND ((now() - last_synchronization > synch_interval * interval '1 minute') \n"
            + "        OR last_synchronization IS NULL)\n"
            + "      AND (last_failure is null OR"
            + "           now() - last_failure > synch_retry * interval '1 minute');\n"
            + "INSERT INTO geometry_columns VALUES('', 'public', 'synch_outstanding', 'geom', 2, 4326, 'GEOMETRY')";

    GeoServer geoServer;

    public GSSCore(GeoServer geoServer) {
        this.geoServer = geoServer;

        // try to version enable the tables that need to
        versionEnableTables();

        // add a listener that will try to version enable tables on config changes
        geoServer.addListener(new ConfigurationListenerAdapter() {
           
            @Override
            public void handlePostServiceChange(ServiceInfo service) {
                if (service instanceof GSSInfo) {
                    versionEnableTables();
                }
            }
           
            @Override
            public void handlePostGlobalChange(org.geoserver.config.GeoServerInfo global) {
                versionEnableTables();
            }
        });
    }

    void versionEnableTables() {
        try {
            ensureEnabled();
        } catch (Exception e) {
            // nothing to do really, the service might not be configured enough
        }
    }

    GSSInfo getServiceInfo() {
        return geoServer.getService(GSSInfo.class);
    }

    /**
     * Checks the module is ready to be used. TODO: move this to a listener so that we don't do all
     * the ckecks for every request
     */
    void ensureEnabled() {
        GSSInfo info = getServiceInfo();

        // basic sanity checks on the config
        if (info == null) {
            throw new GSSException("The service is not properly configured, gssInfo not found");
        }

        if (info.getMode() == null) {
            throw new GSSException("The gss mode has not been configured");
        }

        if (info.getVersioningDataStore() == null || !info.getVersioningDataStore().isEnabled()) {
            throw new GSSException("The service is disabled as the "
                    + "versioning datastore is not available/disabled");
        }

        FeatureIterator<SimpleFeature> fi = null;
        try {
            // basic sanity checks on the datastore
            DataAccess ds = info.getVersioningDataStore().getDataStore(null);
            if (!(ds instanceof VersionedPostgisDataStore)) {
                throw new GSSException(
                        "The store attached to the gss module is not a PostGIS versioning one");
            }
            VersionedPostgisDataStore dataStore = (VersionedPostgisDataStore) ds;

            Set<String> typeNames = new HashSet<String>(Arrays.asList(dataStore.getTypeNames()));

            // the synchronized tables list
            if (!typeNames.contains(SYNCH_TABLES)) {
                runStatement(dataStore, SYNC_TABLES_CREATION);
            }
            dataStore.setVersioned(SYNCH_TABLES, false, null, null);

            // version enable all tables that are supposed to be shared
            fi = dataStore.getFeatureSource(SYNCH_TABLES).getFeatures().features();
            while (fi.hasNext()) {
                String tableName = (String) fi.next().getAttribute("table_name");
                dataStore.setVersioned(tableName, true, null, null);
            }
            fi.close();

            if (info.getMode() == GSSMode.Unit) {
                // hmm... we should really try to use createSchema() instead, but atm
                // we don't have the necessary control over it

                // the unit synchronisation history
                if (!typeNames.contains(SYNCH_HISTORY)) {
                    runStatement(dataStore, SYNCH_HISTORY_CREATION);
                }
                dataStore.setVersioned(SYNCH_HISTORY, false, null, null);

                // the conflict table
                if (!typeNames.contains(SYNCH_CONFLICTS)) {
                    runStatement(dataStore, SYNCH_CONFLICTS_CREATION);
                }
                dataStore.setVersioned(SYNCH_CONFLICTS, true, null, null);
            } else {
                if (!typeNames.contains(SYNCH_UNITS)) {
                    runStatement(dataStore, SYNCH_UNITS_CREATION);
                }
                dataStore.setVersioned(SYNCH_UNITS, false, null, null);

                if (!typeNames.contains(SYNCH_UNIT_TABLES)) {
                    runStatement(dataStore, SYNCH_UNIT_TABLES_CREATION);
                }
                dataStore.setVersioned(SYNCH_UNITS, false, null, null);

                if (!typeNames.contains(SYNCH_OUTSTANDING)) {
                    runStatement(dataStore, SYNCH_OUTSTANDING_CREATION);
                }
            }

        } catch (Exception e) {
            throw new GSSException("A problem occurred while checking the versioning store", e);
        } finally {
            if (fi != null) {
                fi.close();
            }
        }

    }

    void runStatement(VersionedPostgisDataStore dataStore, String sqlStatement) throws IOException,
            SQLException {
        Connection conn = null;
        Statement st = null;
        try {
            conn = dataStore.getConnection(Transaction.AUTO_COMMIT);
            st = conn.createStatement();
            st.execute(sqlStatement);
        } finally {
            JDBCUtils.close(st);
            JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
        }
    }

    public void ensureUnitEnabled() {
        ensureEnabled();

        if (getServiceInfo().getMode() != GSSMode.Unit) {
            throw new GSSException("gss configured in Central mode, won't do Unit service calls");
        }
    }

    public void ensureCentralEnabled() {
        ensureEnabled();

        if (getServiceInfo().getMode() != GSSMode.Central) {
            throw new GSSException("gss configured in Unit mode, won't do synchronisation services");
        }
    }

    /**
     * Finds the versioning datastore configured for this service
     *
     * @return
     * @throws IOException
     */
    public VersioningDataStore getVersioningStore() throws IOException {
        return (VersioningDataStore) getServiceInfo().getVersioningDataStore().getDataStore(null);
    }

    /**
     * Returns the datastore configuration for this service
     *
     * @return
     * @throws IOException
     */
    public DataStoreInfo getVersioningStoreInfo() {
        return getServiceInfo().getVersioningDataStore();
    }

    /**
     * Returns the operation mode
     *
     * @return
     */
    public GSSMode getMode() {
        return getServiceInfo().getMode();
    }

    /**
     * Applies the specified transaction to the provided feature store
     *
     * @param changes
     * @param store
     */
    public void applyChanges(TransactionType changes,
            FeatureStore<SimpleFeatureType, SimpleFeature> store) throws IOException {
        if (changes == null)
            return;

        List<DeleteElementType> deletes = changes.getDelete();
        List<UpdateElementType> updates = changes.getUpdate();
        List<InsertElementType> inserts = changes.getInsert();
        for (DeleteElementType delete : deletes) {
            store.removeFeatures(delete.getFilter());
        }
        for (UpdateElementType update : updates) {
            List<PropertyType> props = update.getProperty();
            List<AttributeDescriptor> atts = new ArrayList<AttributeDescriptor>(props.size());
            List<Object> values = new ArrayList<Object>(props.size());
            for (PropertyType prop : props) {
                atts.add(store.getSchema().getDescriptor(prop.getName().getLocalPart()));
                values.add(prop.getValue());
            }
            AttributeDescriptor[] attArray = (AttributeDescriptor[]) atts
                    .toArray(new AttributeDescriptor[atts.size()]);
            Object[] valArray = (Object[]) values.toArray(new Object[values.size()]);
            store.modifyFeatures(attArray, valArray, update.getFilter());
        }
        for (InsertElementType insert : inserts) {
            List<SimpleFeature> features = insert.getFeature();
            store.addFeatures(DataUtilities.collection(features));
        }
    }

    /**
     * Returns the number of changes contained in the transaction
     *
     * @param changes
     * @return
     */
    public int countChanges(TransactionType changes) {
        if (changes == null) {
            return 0;
        }

        int count = 0;
        count += changes.getDelete().size();
        count += changes.getUpdate().size();
        count += changes.getInsert().size();

        return count;
    }

    public static void main(String[] args) {
        System.out.println("Unit tables creation");
        System.out.println("--------------------");
        System.out.println();
        System.out.println(SYNC_TABLES_CREATION);
        System.out.println(SYNCH_HISTORY_CREATION);
        System.out.println(SYNCH_CONFLICTS_CREATION);
        System.out.println();
        System.out.println();
        System.out.println();
        System.out.println("Central tables creation");
        System.out.println("--------------------");
        System.out.println();
        System.out.println(SYNCH_UNITS_CREATION);
        System.out.println(SYNCH_UNIT_TABLES_CREATION);
        System.out.println(SYNCH_OUTSTANDING_CREATION);
    }
}
TOP

Related Classes of org.geoserver.gss.GSSCore

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.