From 223d9f3fb084e36430029201bc99c2a2fccfe552 Mon Sep 17 00:00:00 2001
From: Nuno Oliveira
Date: Sun, 23 Jul 2017 00:22:24 +0100
Subject: [PATCH 01/15] Add WFS time versioning
---
src/community/nsg-profile/pom.xml | 86 ++++++
.../org/geoserver/nsg/TimeVersioning.java | 58 +++++
.../geoserver/nsg/TimeVersioningCallback.java | 244 ++++++++++++++++++
.../nsg/VersioningFilterAdapter.java | 110 ++++++++
.../nsg/web/WfsVersioningConfig.html | 32 +++
.../nsg/web/WfsVersioningConfig.java | 153 +++++++++++
.../resources/GeoServerApplication.properties | 4 +
.../src/main/resources/applicationContext.xml | 19 ++
.../java/org/geoserver/nsg/TestsUtils.java | 39 +++
.../org/geoserver/nsg/TimeVersioningTest.java | 99 +++++++
.../nsg/web/WfsVersioningConfigTest.java | 106 ++++++++
.../org/geoserver/nsg/versioned.properties | 6 +
.../test/resources/requests/get_request_1.xml | 13 +
.../resources/requests/insert_request_1.xml | 22 ++
.../resources/requests/update_request_1.xml | 18 ++
src/community/pom.xml | 7 +
src/community/release/ext-nsg-profile.xml | 16 ++
.../geoserver/catalog/FeatureTypeInfo.java | 7 +
.../catalog/impl/FeatureTypeInfoImpl.java | 33 ++-
.../decorators/DecoratingFeatureTypeInfo.java | 9 +
.../decorators/SecuredFeatureTypeInfo.java | 3 +-
src/web/app/pom.xml | 10 +
.../security/web/user/UserPanel.java | 2 +-
.../java/org/geoserver/wfs/GetFeature.java | 16 ++
.../org/geoserver/wfs/GetFeatureCallback.java | 20 ++
.../org/geoserver/wfs/GetFeatureContext.java | 44 ++++
.../wfs/GetFeatureContextBuilder.java | 54 ++++
.../geoserver/wfs/InsertElementHandler.java | 41 ++-
.../java/org/geoserver/wfs/Transaction.java | 17 ++
.../geoserver/wfs/TransactionCallback.java | 49 ++++
.../org/geoserver/wfs/TransactionContext.java | 49 ++++
.../wfs/TransactionContextBuilder.java | 61 +++++
32 files changed, 1425 insertions(+), 22 deletions(-)
create mode 100644 src/community/nsg-profile/pom.xml
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
create mode 100644 src/community/nsg-profile/src/main/resources/GeoServerApplication.properties
create mode 100644 src/community/nsg-profile/src/main/resources/applicationContext.xml
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
create mode 100644 src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties
create mode 100644 src/community/nsg-profile/src/test/resources/requests/get_request_1.xml
create mode 100644 src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml
create mode 100644 src/community/nsg-profile/src/test/resources/requests/update_request_1.xml
create mode 100644 src/community/release/ext-nsg-profile.xml
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
new file mode 100644
index 00000000000..b9e26ef3fbc
--- /dev/null
+++ b/src/community/nsg-profile/pom.xml
@@ -0,0 +1,86 @@
+
+
+ 4.0.0
+
+ org.geoserver
+ community
+ 2.12-SNAPSHOT
+
+ org.geoserver.community
+ gs-nsg-profile
+ jar
+ 2.12-SNAPSHOT
+ NSG Profile
+
+
+
+ org.geoserver
+ gs-main
+ tests
+ test
+
+
+ org.geoserver
+ gs-wfs
+ ${project.version}
+
+
+ org.geoserver.web
+ gs-web-core
+ ${project.version}
+
+
+
+ org.geoserver
+ gs-wfs
+ tests
+ ${project.version}
+
+
+ org.geoserver.web
+ gs-web-core
+ ${project.version}
+ tests
+ test
+
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+ org.springframework
+ spring-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+ javax.servlet
+ javax.servlet-api
+ test
+
+
+
+
+
+ ${basedir}/src/main/java
+
+ **/*.html
+
+
+
+ ${basedir}/src/main/resources
+
+ **/*
+
+
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
new file mode 100644
index 00000000000..ad891d29929
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
@@ -0,0 +1,58 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+
+public final class TimeVersioning {
+
+ public static final String ENABLED_KEY = "TIME_VERSIONING_ENABLED";
+ public static final String ID_PROPERTY_KEY = "TIME_VERSIONING_ID_PROPERTY";
+ public static final String TIME_PROPERTY_KEY = "TIME_VERSIONING_TIME_PROPERTY";
+
+ public static void enable(FeatureTypeInfo featureTypeInfo, String idProperty, String timeProperty) {
+ featureTypeInfo.putParameter(ENABLED_KEY, true);
+ featureTypeInfo.putParameter(ID_PROPERTY_KEY, idProperty);
+ featureTypeInfo.putParameter(TIME_PROPERTY_KEY, timeProperty);
+ }
+
+ public static void disable(FeatureTypeInfo featureTypeInfo) {
+ featureTypeInfo.putParameter(ENABLED_KEY, false);
+ featureTypeInfo.putParameter(ID_PROPERTY_KEY, null);
+ featureTypeInfo.putParameter(TIME_PROPERTY_KEY, null);
+ }
+
+ public static boolean isEnabled(FeatureTypeInfo featureTypeInfo) {
+ return featureTypeInfo.getParameter(ENABLED_KEY, Boolean.class, false);
+ }
+
+ public static String getIdPropertyName(FeatureTypeInfo featureTypeInfo) {
+ String idPropertyName = featureTypeInfo.getParameter(ID_PROPERTY_KEY, String.class, null);
+ if (idPropertyName == null) {
+ throw new RuntimeException("No id property name was provided.");
+ }
+ return idPropertyName;
+ }
+
+ public static String getTimePropertyName(FeatureTypeInfo featureTypeInfo) {
+ String timePropertyName = featureTypeInfo.getParameter(TIME_PROPERTY_KEY, String.class, null);
+ if (timePropertyName == null) {
+ throw new RuntimeException("No time property name was provided.");
+ }
+ return timePropertyName;
+ }
+
+ public static void setEnable(FeatureTypeInfo featureTypeInfo, boolean enable) {
+ featureTypeInfo.putParameter(ENABLED_KEY, enable);
+ }
+
+ public static void setIdAttribute(FeatureTypeInfo featureTypeInfo, String idAttributeName) {
+ featureTypeInfo.putParameter(ID_PROPERTY_KEY, idAttributeName);
+ }
+
+ public static void setTimeAttribute(FeatureTypeInfo featureTypeInfo, String timeAttributeName) {
+ featureTypeInfo.putParameter(TIME_PROPERTY_KEY, timeAttributeName);
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
new file mode 100644
index 00000000000..ee87f12f628
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
@@ -0,0 +1,244 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.Catalog;
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.platform.GeoServerExtensions;
+import org.geoserver.wfs.GetFeatureCallback;
+import org.geoserver.wfs.GetFeatureContext;
+import org.geoserver.wfs.InsertElementHandler;
+import org.geoserver.wfs.TransactionCallback;
+import org.geoserver.wfs.TransactionContext;
+import org.geoserver.wfs.TransactionContextBuilder;
+import org.geoserver.wfs.request.Insert;
+import org.geoserver.wfs.request.RequestObject;
+import org.geoserver.wfs.request.Update;
+import org.geotools.data.DataUtilities;
+import org.geotools.data.FeatureStore;
+import org.geotools.data.Query;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureIterator;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.factory.CommonFactoryFinder;
+import org.geotools.factory.GeoTools;
+import org.geotools.feature.NameImpl;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.util.Converters;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.type.AttributeDescriptor;
+import org.opengis.feature.type.FeatureType;
+import org.opengis.feature.type.Name;
+import org.opengis.filter.Filter;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.sort.SortBy;
+import org.opengis.filter.sort.SortOrder;
+
+import javax.xml.namespace.QName;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+final class TimeVersioningCallback implements GetFeatureCallback, TransactionCallback {
+
+ private static final FilterFactory2 FILTER_FACTORY = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
+
+ private final Catalog catalog;
+
+ TimeVersioningCallback(Catalog catalog) {
+ this.catalog = catalog;
+ }
+
+ @Override
+ public GetFeatureContext beforeQuerying(GetFeatureContext context) {
+ if (!isWfs20(context.getRequest())) {
+ return context;
+ }
+ FeatureTypeInfo featureTypeInfo = context.getFeatureTypeInfo();
+ if (!TimeVersioning.isEnabled(featureTypeInfo)) {
+ // time versioning is not enabled for this feature type or is not a WFS 2.0 request
+ return context;
+ }
+ VersioningFilterAdapter.adapt(featureTypeInfo, context.getQuery().getFilter());
+ SortBy sort = FILTER_FACTORY.sort(TimeVersioning.getTimePropertyName(featureTypeInfo), SortOrder.DESCENDING);
+ SortBy[] sorts = context.getQuery().getSortBy();
+ if (sorts == null) {
+ sorts = new SortBy[]{sort};
+ } else {
+ sorts = Arrays.copyOf(sorts, sorts.length + 1);
+ sorts[sorts.length - 1] = sort;
+ }
+ context.getQuery().setSortBy(sorts);
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeHandlerExecution(TransactionContext context) {
+ if (!isWfs20(context.getRequest())) {
+ return context;
+ }
+ if (context.getElement() instanceof Update) {
+ Insert insert = buildInsertForUpdate(context);
+ InsertElementHandler handler = GeoServerExtensions.bean(InsertElementHandler.class);
+ return new TransactionContextBuilder()
+ .withContext(context)
+ .withElement(insert)
+ .withHandler(handler).build();
+ }
+ if (context.getElement() instanceof Insert) {
+ Insert insert = (Insert) context.getElement();
+ for (Object element : insert.getFeatures()) {
+ if (element instanceof SimpleFeature) {
+ setTimeAttribute((SimpleFeature) element);
+ }
+ }
+ }
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeInsertFeatures(TransactionContext context) {
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeUpdateFeatures(TransactionContext context) {
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeDeleteFeatures(TransactionContext context) {
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeReplaceFeatures(TransactionContext context) {
+ return context;
+ }
+
+ private void setTimeAttribute(SimpleFeature feature) {
+ FeatureType featureType = feature.getFeatureType();
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(featureType);
+ if (TimeVersioning.isEnabled(featureTypeInfo)) {
+ String timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ AttributeDescriptor attributeDescriptor = feature.getType().getDescriptor(timePropertyName);
+ Object timeValue = Converters.convert(new Date(), attributeDescriptor.getType().getBinding());
+ feature.setAttribute(timePropertyName, timeValue);
+ }
+ }
+
+ private SimpleFeatureCollection getTransactionFeatures(TransactionContext context) {
+ QName typeName = context.getElement().getTypeName();
+ Filter filter = context.getElement().getFilter();
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(new NameImpl(typeName));
+ SimpleFeatureStore store = getTransactionStore(context);
+ try {
+ Query query = new Query();
+ query.setFilter(VersioningFilterAdapter.adapt(featureTypeInfo, filter));
+ SortBy sort = FILTER_FACTORY.sort(TimeVersioning.getTimePropertyName(featureTypeInfo), SortOrder.DESCENDING);
+ query.setSortBy(new SortBy[]{sort});
+ return store.getFeatures(query);
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format(
+ "Error getting features of type '%s'.", typeName), exception);
+ }
+ }
+
+ private Comparator buildFeatureTimeComparator(FeatureTypeInfo featureTypeInfo) {
+ String timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ return (featureA, featureB) -> {
+ Date timeA = Converters.convert(featureA.getAttribute(timePropertyName), Date.class);
+ Date timeB = Converters.convert(featureB.getAttribute(timePropertyName), Date.class);
+ if (timeA == null) {
+ return -1;
+ }
+ return timeA.compareTo(timeB);
+ };
+ }
+
+ private List getOnlyRecentFeatures(SimpleFeatureCollection features, FeatureTypeInfo featureTypeInfo) {
+ String idPropertyName = TimeVersioning.getIdPropertyName(featureTypeInfo);
+ Map> featuresIndexedById = new HashMap<>();
+ SimpleFeatureIterator iterator = features.features();
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ Object id = feature.getAttribute(idPropertyName);
+ List existing = featuresIndexedById.computeIfAbsent(id, key -> new ArrayList<>());
+ existing.add(feature);
+ }
+ Comparator comparator = buildFeatureTimeComparator(featureTypeInfo);
+ List finalFeatures = new ArrayList<>();
+ featuresIndexedById.values().forEach(indexed -> {
+ indexed.sort(comparator);
+ SimpleFeature feature = indexed.get(0);
+ SimpleFeatureBuilder builder = new SimpleFeatureBuilder(feature.getFeatureType());
+ builder.init(feature);
+ finalFeatures.add(builder.buildFeature(null));
+ });
+ return finalFeatures;
+ }
+
+ private Insert buildInsertForUpdate(TransactionContext context) {
+ Update update = (Update) context.getElement();
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(new NameImpl(update.getTypeName()));
+ SimpleFeatureCollection features = getTransactionFeatures(context);
+ List recent = getOnlyRecentFeatures(features, featureTypeInfo);
+ List newFeatures = recent.stream()
+ .map(this::prepareInsertFeature).collect(Collectors.toList());
+ return new UpdateInsert(context.getRequest(), newFeatures);
+ }
+
+ private SimpleFeature prepareInsertFeature(SimpleFeature feature) {
+ SimpleFeatureBuilder builder = new SimpleFeatureBuilder(feature.getFeatureType());
+ builder.init(feature);
+ SimpleFeature versionedFeature = builder.buildFeature(null);
+ setTimeAttribute(versionedFeature);
+ return versionedFeature;
+ }
+
+ private FeatureTypeInfo getFeatureTypeInfo(FeatureType featureType) {
+ Name featureTypeName = featureType.getName();
+ return getFeatureTypeInfo(featureTypeName);
+ }
+
+ private FeatureTypeInfo getFeatureTypeInfo(Name featureTypeName) {
+ FeatureTypeInfo featureTypeInfo = catalog.getFeatureTypeByName(featureTypeName);
+ if (featureTypeInfo == null) {
+ throw new RuntimeException(String.format(
+ "Couldn't find feature type info ''%s.", featureTypeName));
+ }
+ return featureTypeInfo;
+ }
+
+ private SimpleFeatureStore getTransactionStore(TransactionContext context) {
+ QName typeName = context.getElement().getTypeName();
+ FeatureStore store = (FeatureStore) context.getFeatureStores().get(typeName);
+ return DataUtilities.simple(store);
+ }
+
+ private boolean isWfs20(RequestObject request) {
+ return true;
+ }
+
+ private static final class UpdateInsert extends Insert {
+
+ private final List features;
+
+ protected UpdateInsert(RequestObject request, List features) {
+ super(request.getAdaptee());
+ this.features = features;
+ }
+
+ @Override
+ public List getFeatures() {
+ return features;
+ }
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
new file mode 100644
index 00000000000..6a0b1c68222
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
@@ -0,0 +1,110 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geotools.factory.CommonFactoryFinder;
+import org.geotools.factory.GeoTools;
+import org.geotools.filter.visitor.DuplicatingFilterVisitor;
+import org.opengis.filter.Filter;
+import org.opengis.filter.FilterFactory;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.Id;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.identity.Identifier;
+import org.opengis.filter.identity.ResourceId;
+import org.opengis.filter.sort.SortOrder;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+final class VersioningFilterAdapter extends DuplicatingFilterVisitor {
+
+ private final String idPropertyName;
+ private final String timePropertyName;
+
+ private VersioningFilterAdapter(FeatureTypeInfo featureTypeInfo) {
+ this.idPropertyName = TimeVersioning.getIdPropertyName(featureTypeInfo);
+ this.timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ }
+
+ @Override
+ public Object visit(Id filter, Object extraData) {
+ FilterFactory filterFactory = getFactory(extraData);
+ Set ids = filter.getIdentifiers();
+ Set finalIds = new HashSet<>();
+ Filter versioningFilter = null;
+ for (Identifier id : ids) {
+ if (id instanceof ResourceId) {
+ Filter newFilter = buildVersioningFilter(filterFactory, (ResourceId) id);
+ versioningFilter = addFilter(filterFactory, versioningFilter, newFilter);
+ } else {
+ finalIds.add(id);
+ }
+ }
+ if (finalIds.isEmpty()) {
+ return versioningFilter;
+ }
+ Filter newIdFilter = getFactory(extraData).id(finalIds);
+ if (versioningFilter != null) {
+ return filterFactory.and(newIdFilter, versioningFilter);
+ }
+ return newIdFilter;
+ }
+
+ private Filter buildVersioningFilter(FilterFactory filterFactory, ResourceId resourceId) {
+ Filter idFilter = buildIdFilter(filterFactory, resourceId.getID());
+ Filter timeFilter = buildTimeFilter(filterFactory, resourceId.getStartTime(), resourceId.getEndTime());
+ if (idFilter != null && timeFilter != null) {
+ return filterFactory.and(idFilter, timeFilter);
+ }
+ if (idFilter != null) {
+ return idFilter;
+ }
+ if (timeFilter != null) {
+ return timeFilter;
+ }
+ return null;
+ }
+
+ private Filter buildIdFilter(FilterFactory factory, String id) {
+ if (id == null) {
+ return null;
+ }
+ return factory.equals(factory.property(idPropertyName), factory.literal(id));
+ }
+
+ private Filter buildTimeFilter(FilterFactory filterFactory, Date start, Date end) {
+ Expression timeProperty = filterFactory.property(timePropertyName);
+ Expression startLiteral = filterFactory.literal(start);
+ Expression endLiteral = filterFactory.literal(end);
+ Filter after = filterFactory.after(timeProperty, startLiteral);
+ Filter before = filterFactory.before(timeProperty, endLiteral);
+ if (start != null && end != null) {
+ return filterFactory.and(after, before);
+ }
+ if (start != null) {
+ return after;
+ }
+ if (end != null) {
+ return before;
+ }
+ return null;
+ }
+
+ private Filter addFilter(FilterFactory filterFactory, Filter versioningFilter, Filter filter) {
+ if (versioningFilter != null) {
+ return filterFactory.and(versioningFilter, filter);
+ }
+ return filter;
+ }
+
+ static Filter adapt(FeatureTypeInfo featureTypeInfo, Filter filter) {
+ String timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ VersioningFilterAdapter adapter = new VersioningFilterAdapter(featureTypeInfo);
+ return (Filter) filter.accept(adapter, null);
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
new file mode 100644
index 00000000000..b67d24989c7
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
new file mode 100644
index 00000000000..8ef574e602c
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
@@ -0,0 +1,153 @@
+/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
+ * (c) 2001 - 2013 OpenPlans
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg.web;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.catalog.LayerInfo;
+import org.geoserver.nsg.TimeVersioning;
+import org.geoserver.web.publish.PublishedConfigurationPanel;
+
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class WfsVersioningConfig extends PublishedConfigurationPanel {
+
+ public WfsVersioningConfig(String id, IModel model) {
+ super(id, model);
+ // get the needed information from the model
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(model);
+ boolean isVersioningActivated = TimeVersioning.isEnabled(featureTypeInfo);
+ String idAttributeName = isVersioningActivated ? TimeVersioning.getIdPropertyName(featureTypeInfo) : null;
+ String timeAttributeName = isVersioningActivated ? TimeVersioning.getTimePropertyName(featureTypeInfo) : null;
+ List attributesNames = getAttributesNames(featureTypeInfo);
+ List timeAttributesNames = getTimeAttributesNames(featureTypeInfo);
+ // create dropdown choice for the id attribute name
+ DropDownChoice idAttributeChoice = new DropDownChoice<>("idAttributeChoice",
+ new Model<>(idAttributeName), attributesNames);
+ idAttributeChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ String selected = idAttributeChoice.getModel().getObject();
+ TimeVersioning.setIdAttribute(featureTypeInfo, selected);
+ }
+ });
+ idAttributeChoice.setOutputMarkupId(true);
+ idAttributeChoice.setOutputMarkupPlaceholderTag(true);
+ idAttributeChoice.setRequired(true);
+ idAttributeChoice.setVisible(isVersioningActivated);
+ add(idAttributeChoice);
+ // add label for id attribute name dropdown choice
+ Label idAttributeChoiceLabel = new Label("idAttributeChoiceLabel",
+ new StringResourceModel("WfsVersioningConfig.idAttributeChoiceLabel"));
+ idAttributeChoiceLabel.setOutputMarkupId(true);
+ idAttributeChoiceLabel.setOutputMarkupPlaceholderTag(true);
+ idAttributeChoiceLabel.setVisible(isVersioningActivated);
+ add(idAttributeChoiceLabel);
+ // create dropdown choice for the time attribute name
+ DropDownChoice timeAttributeChoice = new DropDownChoice<>("timeAttributeChoice",
+ new Model<>(timeAttributeName), timeAttributesNames);
+ timeAttributeChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ String selected = timeAttributeChoice.getModel().getObject();
+ TimeVersioning.setTimeAttribute(featureTypeInfo, selected);
+ }
+ });
+ timeAttributeChoice.setOutputMarkupId(true);
+ timeAttributeChoice.setOutputMarkupPlaceholderTag(true);
+ timeAttributeChoice.setRequired(true);
+ timeAttributeChoice.setVisible(isVersioningActivated);
+ add(timeAttributeChoice);
+ // add label for id attribute name dropdown choice
+ Label timeAttributeChoiceLabel = new Label("timeAttributeChoiceLabel",
+ new StringResourceModel("WfsVersioningConfig.timeAttributeChoiceLabel"));
+ timeAttributeChoiceLabel.setOutputMarkupId(true);
+ timeAttributeChoiceLabel.setOutputMarkupPlaceholderTag(true);
+ timeAttributeChoiceLabel.setVisible(isVersioningActivated);
+ add(timeAttributeChoiceLabel);
+ // checkbox for activating versioning
+ CheckBox versioningActivateCheckBox = new AjaxCheckBox("versioningActivateCheckBox",
+ new Model<>(isVersioningActivated)) {
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ boolean checked = getModelObject();
+ if (checked) {
+ // activate versioning attributes selection
+ idAttributeChoice.setVisible(true);
+ idAttributeChoiceLabel.setVisible(true);
+ timeAttributeChoice.setVisible(true);
+ timeAttributeChoiceLabel.setVisible(true);
+ // enable time versioning
+ TimeVersioning.setEnable(featureTypeInfo, true);
+ } else {
+ // deactivate versioning attributes selection
+ idAttributeChoice.setVisible(false);
+ idAttributeChoiceLabel.setVisible(false);
+ timeAttributeChoice.setVisible(false);
+ timeAttributeChoiceLabel.setVisible(false);
+ // disable time versioning
+ TimeVersioning.setEnable(featureTypeInfo, false);
+ }
+ // update the dropdown choices and labels
+ target.add(idAttributeChoice);
+ target.add(idAttributeChoiceLabel);
+ target.add(timeAttributeChoice);
+ target.add(timeAttributeChoiceLabel);
+ }
+ };
+ if (isVersioningActivated) {
+ versioningActivateCheckBox.setModelObject(true);
+ }
+ versioningActivateCheckBox.setEnabled(!timeAttributesNames.isEmpty());
+ add(versioningActivateCheckBox);
+ // add versioning activating checkbox label
+ Label versioningActivateCheckBoxLabel = new Label("versioningActivateCheckBoxLabel",
+ new StringResourceModel("WfsVersioningConfig.versioningActivateCheckBoxLabel"));
+ add(versioningActivateCheckBoxLabel);
+ }
+
+ private List getAttributesNames(FeatureTypeInfo featureTypeInfo) {
+ try {
+ return featureTypeInfo.getFeatureType().getDescriptors().stream()
+ .map(attribute -> attribute.getName().getLocalPart())
+ .collect(Collectors.toList());
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format(
+ "Error processing attributes of feature type '%s'.",
+ featureTypeInfo.getName()), exception);
+ }
+ }
+
+ private List getTimeAttributesNames(FeatureTypeInfo featureTypeInfo) {
+ try {
+ return featureTypeInfo.getFeatureType().getDescriptors().stream().filter(attribute -> {
+ Class binding = attribute.getType().getBinding();
+ return Long.class.isAssignableFrom(binding)
+ || Date.class.isAssignableFrom(binding)
+ || Timestamp.class.isAssignableFrom(binding);
+ }).map(attribute -> attribute.getName().getLocalPart()).collect(Collectors.toList());
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format(
+ "Error processing attributes of feature type '%s'.",
+ featureTypeInfo.getName()), exception);
+ }
+ }
+
+ private FeatureTypeInfo getFeatureTypeInfo(IModel model) {
+ return (FeatureTypeInfo) model.getObject().getResource();
+ }
+}
diff --git a/src/community/nsg-profile/src/main/resources/GeoServerApplication.properties b/src/community/nsg-profile/src/main/resources/GeoServerApplication.properties
new file mode 100644
index 00000000000..e8f4ddacbf6
--- /dev/null
+++ b/src/community/nsg-profile/src/main/resources/GeoServerApplication.properties
@@ -0,0 +1,4 @@
+WfsVersioningConfig.wfsVersioning=WFS Versioning
+WfsVersioningConfig.versioningActivateCheckBoxLabel=Activate Versioning
+WfsVersioningConfig.idAttributeChoiceLabel=Id Attribute
+WfsVersioningConfig.timeAttributeChoiceLabel=Time Attribute
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
new file mode 100644
index 00000000000..a33c1190b39
--- /dev/null
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+ org.geoserver.catalog.FeatureTypeInfo
+
+
+
+
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
new file mode 100644
index 00000000000..653ac8a8e4b
--- /dev/null
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
@@ -0,0 +1,39 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.Catalog;
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.util.IOUtils;
+
+import java.io.InputStream;
+
+import static org.geoserver.nsg.TimeVersioning.disable;
+import static org.geoserver.nsg.TimeVersioning.enable;
+
+final class TestsUtils {
+
+ private TestsUtils() {
+ }
+
+ static String readResource(String resourceName) {
+ try (InputStream input = TestsUtils.class.getResourceAsStream(resourceName)) {
+ return IOUtils.toString(input);
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format("Error reading resource '%s'.", resourceName));
+ }
+ }
+
+ static void updateFeatureTypeTimeVersioning(Catalog catalog, String featureTypeName,
+ boolean enabled, String idProperty, String timeProperty) {
+ FeatureTypeInfo featureType = catalog.getFeatureTypeByName(featureTypeName);
+ if (enabled) {
+ enable(featureType, idProperty, timeProperty);
+ } else {
+ disable(featureType);
+ }
+ catalog.save(featureType);
+ }
+}
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
new file mode 100644
index 00000000000..758d1fa3370
--- /dev/null
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
@@ -0,0 +1,99 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.custommonkey.xmlunit.XpathEngine;
+import org.geoserver.data.test.MockData;
+import org.geoserver.data.test.SystemTestData;
+import org.geoserver.test.GeoServerSystemTestSupport;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+
+import javax.xml.namespace.QName;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class TimeVersioningTest extends GeoServerSystemTestSupport {
+
+ private XpathEngine WFS20_XPATH_ENGINE;
+
+ @Override
+ protected void onSetUp(SystemTestData testData) throws Exception {
+ super.setUpTestData(testData);
+ // create bounding box definitions
+ ReferencedEnvelope envelope = new ReferencedEnvelope(-5, -5, 5, 5, DefaultGeographicCRS.WGS84);
+ Map properties = new HashMap<>();
+ properties.put(SystemTestData.LayerProperty.LATLON_ENVELOPE, envelope);
+ properties.put(SystemTestData.LayerProperty.ENVELOPE, envelope);
+ properties.put(SystemTestData.LayerProperty.SRS, 4326);
+ // create versioned layer
+ QName versionedLayerName = new QName(MockData.DEFAULT_URI, "versioned", MockData.DEFAULT_PREFIX);
+ testData.addVectorLayer(versionedLayerName, properties, "versioned.properties", getClass(), getCatalog());
+ // instantiate xpath engine
+ buildXpathEngine(
+ "wfs", "http://www.opengis.net/wfs/2.0",
+ "gml", "http://www.opengis.net/gml/3.2");
+ }
+
+ @Before
+ public void beforeTest() {
+ // activate versioning for versioned layer
+ TestsUtils.updateFeatureTypeTimeVersioning(getCatalog(), "gs:versioned", true, "ID", "TIME");
+ }
+
+ @Test
+ public void testGetFeatureVersioned() throws Exception {
+ Document result = postAsDOM("wfs", TestsUtils.readResource("/requests/get_request_1.xml"));
+ System.out.println("aa");
+ }
+
+ @Test
+ public void testInsertVersionedFeature() throws Exception {
+ Document result = postAsDOM("wfs", TestsUtils.readResource("/requests/insert_request_1.xml"));
+ System.out.println("aa");
+ }
+
+ @Test
+ public void testUpdateVersionedFeature() throws Exception {
+ Document result = postAsDOM("wfs", TestsUtils.readResource("/requests/update_request_1.xml"));
+ System.out.println("aa");
+ }
+
+ /**
+ * Helper method that builds a xpath engine using some predefined
+ * namespaces and all the catalog namespaces. The provided namespaces
+ * will be added overriding any existing namespace.
+ */
+ private XpathEngine buildXpathEngine(String... namespaces) {
+ // build xpath engine
+ XpathEngine xpathEngine = XMLUnit.newXpathEngine();
+ Map finalNamespaces = new HashMap<>();
+ // add common namespaces
+ finalNamespaces.put("ows", "http://www.opengis.net/ows");
+ finalNamespaces.put("ogc", "http://www.opengis.net/ogc");
+ finalNamespaces.put("xs", "http://www.w3.org/2001/XMLSchema");
+ finalNamespaces.put("xsd", "http://www.w3.org/2001/XMLSchema");
+ finalNamespaces.put("xlink", "http://www.w3.org/1999/xlink");
+ finalNamespaces.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ // add al catalog namespaces
+ getCatalog().getNamespaces().forEach(namespace ->
+ finalNamespaces.put(namespace.getPrefix(), namespace.getURI()));
+ // add provided namespaces
+ if (namespaces.length % 2 != 0) {
+ throw new RuntimeException("Invalid number of namespaces provided.");
+ }
+ for (int i = 0; i < namespaces.length; i += 2) {
+ finalNamespaces.put(namespaces[i], namespaces[i + 1]);
+ }
+ // add namespaces to the xpath engine
+ xpathEngine.setNamespaceContext(new SimpleNamespaceContext(finalNamespaces));
+ return xpathEngine;
+ }
+}
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
new file mode 100644
index 00000000000..efe29b00e6a
--- /dev/null
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
@@ -0,0 +1,106 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg.web;
+
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.util.tester.FormTester;
+import org.geoserver.web.GeoServerWicketTestSupport;
+import org.geoserver.web.publish.PublishedConfigurationPage;
+import org.geoserver.wfs.GMLInfo;
+import org.geoserver.wfs.WFSInfo;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class WfsVersioningConfigTest extends GeoServerWicketTestSupport {
+
+ @Test
+ public void testPageStart() throws Exception {
+ WFSInfo wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+
+ login();
+ tester.startPage(PublishedConfigurationPage.class);
+ }
+
+ /*@Test
+ public void testChangesToValues() throws Exception {
+ String testValue1 = "100", testValue2 = "0";
+ WFSInfo wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+ login();
+ tester.startPage(WFSAdminPage.class);
+ FormTester ft = tester.newFormTester("form");
+ ft.setValue("maxNumberOfFeaturesForPreview", (String)testValue1);
+ ft.submit("submit");
+ wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+ assertEquals("testValue1 = 100", 100, (int)wfs.getMaxNumberOfFeaturesForPreview());
+ tester.startPage(WFSAdminPage.class);
+ ft = tester.newFormTester("form");
+ ft.setValue("maxNumberOfFeaturesForPreview", (String)testValue2);
+ ft.submit("submit");
+ wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+ assertEquals("testValue2 = 0", 0, (int)wfs.getMaxNumberOfFeaturesForPreview());
+ }
+
+ @Test
+ public void testGML32ForceMimeType() throws Exception {
+ // make sure GML MIME type overriding is disabled
+ WFSInfo info = getGeoServer().getService(WFSInfo.class);
+ GMLInfo gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ gmlInfo.setMimeTypeToForce(null);
+ getGeoServer().save(info);
+ // login with administrator privileges
+ login();
+ // start WFS service administration page
+ tester.startPage(new WFSAdminPage());
+ // check that GML MIME type overriding is disabled
+ tester.assertComponent("form:gml32:forceGmlMimeType", CheckBox.class);
+ CheckBox checkbox = (CheckBox) tester.getComponentFromLastRenderedPage("form:gml32:forceGmlMimeType");
+ assertThat(checkbox.getModelObject(), is(false));
+ // MIME type drop down choice should be invisible
+ tester.assertInvisible("form:gml32:mimeTypeToForce");
+ // activate MIME type overriding by clicking in the checkbox
+ FormTester formTester = tester.newFormTester("form");
+ formTester.setValue("gml32:forceGmlMimeType", true);
+ tester.executeAjaxEvent("form:gml32:forceGmlMimeType", "click");
+ formTester = tester.newFormTester("form");
+ formTester.submit("submit");
+ // GML MIME typing overriding should be activated now
+ tester.startPage(new WFSAdminPage());
+ assertThat(checkbox.getModelObject(), is(true));
+ tester.assertVisible("form:gml32:mimeTypeToForce");
+ // WFS global service configuration should have been updated too
+ info = getGeoServer().getService(WFSInfo.class);
+ gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ assertThat(gmlInfo.getMimeTypeToForce().isPresent(), is(true));
+ // select text / xml as MIME type to force
+ formTester = tester.newFormTester("form");
+ formTester.select("gml32:mimeTypeToForce", 2);
+ tester.executeAjaxEvent("form:gml32:mimeTypeToForce", "change");
+ formTester = tester.newFormTester("form");
+ formTester.submit("submit");
+ // WFS global service configuration should be forcing text / xml
+ info = getGeoServer().getService(WFSInfo.class);
+ gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ assertThat(gmlInfo.getMimeTypeToForce().isPresent(), is(true));
+ assertThat(gmlInfo.getMimeTypeToForce().get(), is("text/xml"));
+ // deactivate GML MIME type overriding by clicking in the checkbox
+ tester.startPage(new WFSAdminPage());
+ formTester = tester.newFormTester("form");
+ formTester.setValue("gml32:forceGmlMimeType", false);
+ tester.executeAjaxEvent("form:gml32:forceGmlMimeType", "click");
+ formTester = tester.newFormTester("form");
+ formTester.submit("submit");
+ // GML MIME type overriding should be deactivated now
+ tester.startPage(new WFSAdminPage());
+ assertThat(checkbox.getModelObject(), is(true));
+ tester.assertInvisible("form:gml32:mimeTypeToForce");
+ // WFS global service configuration should have been updated too
+ info = getGeoServer().getService(WFSInfo.class);
+ gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ assertThat(gmlInfo.getMimeTypeToForce().isPresent(), is(false));
+ }*/
+}
diff --git a/src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties b/src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties
new file mode 100644
index 00000000000..ad32708279a
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties
@@ -0,0 +1,6 @@
+_=ID:String,NAME:String,TIME:java.sql.Timestamp,GEOMETRY:Geometry:srid=4326
+v.1=1|feature1|2017-06-25 14:30:00.0|POINT(-1 1)
+v.2=1|feature1|2017-07-25 14:30:00.0|POINT(-1 -1)
+v.3=1|feature1|2017-06-25 14:35:00.0|POINT(1 -1)
+v.4=2|feature2|2017-04-10 13:30:00.0|POINT(-2 2)
+v.5=2|feature2|2017-08-10 17:10:00.0|POINT(2 -2)
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/test/resources/requests/get_request_1.xml b/src/community/nsg-profile/src/test/resources/requests/get_request_1.xml
new file mode 100644
index 00000000000..bd69e4fdbeb
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/requests/get_request_1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml b/src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml
new file mode 100644
index 00000000000..c50429c8a86
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ 1
+ feature1
+ 2018-01-25T13:30:00Z
+
+
+ 1.5 -1.5
+
+
+
+
+
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/test/resources/requests/update_request_1.xml b/src/community/nsg-profile/src/test/resources/requests/update_request_1.xml
new file mode 100644
index 00000000000..34ed22793ac
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/requests/update_request_1.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ NAME
+ feature2_updated
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/community/pom.xml b/src/community/pom.xml
index f039b3bd6c5..327013956df 100644
--- a/src/community/pom.xml
+++ b/src/community/pom.xml
@@ -242,6 +242,7 @@
ows-simulate
jdbc-metrics
oseo
+ nsg-profile
@@ -531,5 +532,11 @@
jdbc-metrics
+
+ nsg-profile
+
+ nsg-profile
+
+
diff --git a/src/community/release/ext-nsg-profile.xml b/src/community/release/ext-nsg-profile.xml
new file mode 100644
index 00000000000..11f21b27b56
--- /dev/null
+++ b/src/community/release/ext-nsg-profile.xml
@@ -0,0 +1,16 @@
+
+ nsg-profile
+
+ zip
+
+ false
+
+
+ release/target/dependency
+
+
+ gs-nsg-profile*.jar
+
+
+
+
\ No newline at end of file
diff --git a/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java b/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java
index f780f80b9f5..3a2b0724b65 100644
--- a/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java
+++ b/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java
@@ -193,4 +193,11 @@ public interface FeatureTypeInfo extends ResourceInfo {
void setCircularArcPresent(boolean arcsPresent);
+ default T getParameter(String parameterName, Class expectType, T fallback) {
+ return null;
+ }
+
+ default void putParameter(String parameterName, Object parameterValue) {
+ // nothing to do
+ }
}
diff --git a/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java b/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java
index 8fc00f86217..b1a5cd481c2 100644
--- a/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java
+++ b/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java
@@ -5,10 +5,6 @@
*/
package org.geoserver.catalog.impl;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
import org.geoserver.catalog.AttributeTypeInfo;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogVisitor;
@@ -20,11 +16,18 @@
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.measure.Measure;
+import org.geotools.util.Converters;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
import org.opengis.util.ProgressListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
@SuppressWarnings("serial")
public class FeatureTypeInfoImpl extends ResourceInfoImpl implements
FeatureTypeInfo {
@@ -42,7 +45,9 @@ public class FeatureTypeInfoImpl extends ResourceInfoImpl implements
boolean overridingServiceSRS;
boolean skipNumberMatched = false;
boolean circularArcPresent;
-
+
+ protected Map parameters = new HashMap<>();
+
public boolean isCircularArcPresent() {
return circularArcPresent;
}
@@ -236,5 +241,21 @@ public void setCqlFilter(String cqlFilter) {
this.cqlFilter = cqlFilter;
this.filter = null;
}
-
+
+ @Override
+ public T getParameter(String parameterName, Class expectType, T fallback) {
+ if (parameters == null || parameters.isEmpty()) {
+ return fallback;
+ }
+ Object value = parameters.get(parameterName);
+ return value == null ? fallback : Converters.convert(value, expectType);
+ }
+
+ @Override
+ public void putParameter(String parameterName, Object parameterValue) {
+ if (parameters == null) {
+ parameters = new HashMap<>();
+ }
+ parameters.put(parameterName, parameterValue);
+ }
}
diff --git a/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java b/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java
index 4f7da4f075c..40b50d647cc 100644
--- a/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java
+++ b/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java
@@ -329,4 +329,13 @@ public void setCqlFilter(String cqlFilter) {
delegate.setCqlFilter(cqlFilter);
}
+ @Override
+ public T getParameter(String parameterName, Class expectType, T fallback) {
+ return delegate.getParameter(parameterName, expectType, fallback);
+ }
+
+ @Override
+ public void putParameter(String parameterName, Object parameterValue) {
+ delegate.putParameter(parameterName, parameterValue);
+ }
}
diff --git a/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java b/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java
index bcc7c5ed884..9198b365cc3 100644
--- a/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java
+++ b/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java
@@ -111,5 +111,6 @@ public FeatureSource getFeatureSource(ProgressListener listener, Hints hints)
public DataStoreInfo getStore() {
return (DataStoreInfo) SecuredObjects.secure(delegate.getStore(), policy);
}
-
+
+
}
diff --git a/src/web/app/pom.xml b/src/web/app/pom.xml
index 2930189850c..b92ca71d249 100644
--- a/src/web/app/pom.xml
+++ b/src/web/app/pom.xml
@@ -1522,5 +1522,15 @@
+
+ nsg-profile
+
+
+ org.geoserver.community
+ gs-nsg-profile
+ ${project.version}
+
+
+
diff --git a/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java b/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java
index c10f9520432..fd5cb632beb 100644
--- a/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java
+++ b/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java
@@ -105,7 +105,7 @@ public void onClick() {
});
//("addNew", NewUserPage.class));
- //add.setParameter(AbstractSecurityPage.ServiceNameKey, serviceName);
+ //add.putParameter(AbstractSecurityPage.ServiceNameKey, serviceName);
add.setVisible(canCreateStore);
// the removal button
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java
index 3e66b5a92ca..151444e61d0 100644
--- a/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java
@@ -34,6 +34,7 @@
import org.geoserver.ows.Request;
import org.geoserver.ows.URLMangler.URLType;
import org.geoserver.ows.util.KvpMap;
+import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.request.GetFeatureRequest;
import org.geoserver.wfs.request.Lock;
@@ -506,6 +507,21 @@ public FeatureCollectionResponse run(GetFeatureRequest request)
queryMaxFeatures, source, request, allPropNames.get(0), viewParam,
joins, primaryTypeName, primaryAlias);
+ // extension point
+ GetFeatureContext context = new GetFeatureContextBuilder()
+ .withFeatureSource(source)
+ .withFeatureTypeInfo(meta)
+ .withQuery(gtQuery)
+ .withRequest(request).build();
+ List callbacks = GeoServerExtensions.extensions(GetFeatureCallback.class);
+ for (GetFeatureCallback callback : callbacks) {
+ context = callback.beforeQuerying(context);
+ }
+ source = context.getFeatureSource();
+ meta = context.getFeatureTypeInfo();
+ gtQuery = context.getQuery();
+ request = context.getRequest();
+
LOGGER.fine("Query is " + query + "\n To gt2: " + gtQuery);
FeatureCollection extends FeatureType, ? extends Feature> features = getFeatures(request, source, gtQuery);
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java
new file mode 100644
index 00000000000..c0b5c8ed58f
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java
@@ -0,0 +1,20 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.platform.ExtensionPriority;
+
+public interface GetFeatureCallback extends ExtensionPriority {
+
+ default GetFeatureContext beforeQuerying(GetFeatureContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ @Override
+ default int getPriority() {
+ return ExtensionPriority.LOWEST;
+ }
+}
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java
new file mode 100644
index 00000000000..3f06b7c4325
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java
@@ -0,0 +1,44 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.wfs.request.GetFeatureRequest;
+import org.geotools.data.FeatureSource;
+import org.geotools.data.Query;
+import org.opengis.feature.Feature;
+import org.opengis.feature.type.FeatureType;
+
+public final class GetFeatureContext {
+
+ private final GetFeatureRequest request;
+ private final FeatureSource extends FeatureType, ? extends Feature> featureSource;
+ private final Query query;
+ private final FeatureTypeInfo featureTypeInfo;
+
+ GetFeatureContext(GetFeatureRequest request, FeatureSource extends FeatureType, ? extends Feature> featureSource,
+ Query query, FeatureTypeInfo featureTypeInfo) {
+ this.request = request;
+ this.featureSource = featureSource;
+ this.query = query;
+ this.featureTypeInfo = featureTypeInfo;
+ }
+
+ public GetFeatureRequest getRequest() {
+ return request;
+ }
+
+ public FeatureSource extends FeatureType, ? extends Feature> getFeatureSource() {
+ return featureSource;
+ }
+
+ public Query getQuery() {
+ return query;
+ }
+
+ public FeatureTypeInfo getFeatureTypeInfo() {
+ return featureTypeInfo;
+ }
+}
\ No newline at end of file
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java
new file mode 100644
index 00000000000..0c19934cf6c
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java
@@ -0,0 +1,54 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.wfs.request.GetFeatureRequest;
+import org.geotools.data.FeatureSource;
+import org.geotools.data.Query;
+import org.opengis.feature.Feature;
+import org.opengis.feature.type.FeatureType;
+
+public final class GetFeatureContextBuilder {
+
+ private GetFeatureRequest request;
+ private FeatureSource extends FeatureType, ? extends Feature> featureSource;
+ private Query query;
+ private FeatureTypeInfo featureTypeInfo;
+
+ public GetFeatureContextBuilder() {
+ }
+
+ public GetFeatureContextBuilder withRequest(GetFeatureRequest request) {
+ this.request = request;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withFeatureSource(FeatureSource extends FeatureType, ? extends Feature> featureSource) {
+ this.featureSource = featureSource;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withQuery(Query query) {
+ this.query = query;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withFeatureTypeInfo(FeatureTypeInfo featureTypeInfo) {
+ this.featureTypeInfo = featureTypeInfo;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withContext(GetFeatureContext context) {
+ return withRequest(context.getRequest())
+ .withFeatureSource(context.getFeatureSource())
+ .withQuery(context.getQuery())
+ .withFeatureTypeInfo(context.getFeatureTypeInfo());
+ }
+
+ public GetFeatureContext build() {
+ return new GetFeatureContext(request, featureSource, query, featureTypeInfo);
+ }
+}
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java b/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java
index 49ae7fa67be..e277dcdb16d 100644
--- a/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java
+++ b/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java
@@ -5,21 +5,11 @@
*/
package org.geoserver.wfs;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-
-import javax.xml.namespace.QName;
-
+import com.vividsolutions.jts.geom.Geometry;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.feature.ReprojectingFeatureCollection;
+import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wfs.request.Insert;
import org.geoserver.wfs.request.TransactionElement;
import org.geoserver.wfs.request.TransactionRequest;
@@ -40,7 +30,16 @@
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import com.vividsolutions.jts.geom.Geometry;
+import javax.xml.namespace.QName;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
/**
@@ -71,7 +70,21 @@ public void checkValidity(TransactionElement element, Map callbacks = GeoServerExtensions.extensions(TransactionCallback.class);
+
try {
for (Iterator it = elementHandlers.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
TransactionElement element = (TransactionElement) entry.getKey();
TransactionElementHandler handler = (TransactionElementHandler) entry.getValue();
+ TransactionContext context = new TransactionContextBuilder()
+ .withElement(element)
+ .withRequest(request)
+ .withFeatureStores(stores)
+ .withResponse(result)
+ .withHandler(handler).build();
+ context = TransactionCallback.executeCallbacks(
+ context, callbacks, TransactionCallback::beforeHandlerExecution);
+
+ element = context.getElement();
+ request = context.getRequest();
+ stores = context.getFeatureStores();
+ result = context.getResponse();
+ handler = context.getHandler();
+
handler.execute(element, request, stores, result, multiplexer);
}
} catch (WFSTransactionException e) {
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java b/src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java
new file mode 100644
index 00000000000..6193ab9dca4
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java
@@ -0,0 +1,49 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import java.util.List;
+
+public interface TransactionCallback {
+
+ default TransactionContext beforeHandlerExecution(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeInsertFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeUpdateFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeDeleteFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeReplaceFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ @FunctionalInterface
+ interface Executor {
+ TransactionContext apply(TransactionCallback callback, TransactionContext context);
+ }
+
+ static TransactionContext executeCallbacks(TransactionContext context,
+ List callbacks,
+ Executor executor) {
+ for (TransactionCallback callback : callbacks) {
+ context = executor.apply(callback, context);
+ }
+ return context;
+ }
+}
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java
new file mode 100644
index 00000000000..469fcc1a558
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java
@@ -0,0 +1,49 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.wfs.request.TransactionElement;
+import org.geoserver.wfs.request.TransactionRequest;
+import org.geoserver.wfs.request.TransactionResponse;
+
+import java.util.Map;
+
+public final class TransactionContext {
+
+ private final TransactionElement element;
+ private final TransactionRequest request;
+ private final Map, ?> featureStores;
+ private final TransactionResponse response;
+ private final TransactionElementHandler handler;
+
+ TransactionContext(TransactionElement element, TransactionRequest request, Map, ?> featureStores,
+ TransactionResponse response, TransactionElementHandler handler) {
+ this.element = element;
+ this.request = request;
+ this.featureStores = featureStores;
+ this.response = response;
+ this.handler = handler;
+ }
+
+ public TransactionElement getElement() {
+ return element;
+ }
+
+ public TransactionRequest getRequest() {
+ return request;
+ }
+
+ public Map, ?> getFeatureStores() {
+ return featureStores;
+ }
+
+ public TransactionResponse getResponse() {
+ return response;
+ }
+
+ public TransactionElementHandler getHandler() {
+ return handler;
+ }
+}
\ No newline at end of file
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java
new file mode 100644
index 00000000000..a1a2f018c96
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java
@@ -0,0 +1,61 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.wfs.request.TransactionElement;
+import org.geoserver.wfs.request.TransactionRequest;
+import org.geoserver.wfs.request.TransactionResponse;
+
+import java.util.Map;
+
+public final class TransactionContextBuilder {
+
+ private TransactionElement element;
+ private TransactionRequest request;
+ private Map, ?> featureStores;
+ private TransactionResponse response;
+ private TransactionElementHandler handler;
+
+ public TransactionContextBuilder() {
+ }
+
+ public TransactionContextBuilder withElement(TransactionElement element) {
+ this.element = element;
+ return this;
+ }
+
+ public TransactionContextBuilder withRequest(TransactionRequest request) {
+ this.request = request;
+ return this;
+ }
+
+ public TransactionContextBuilder withFeatureStores(Map, ?> featureStores) {
+ this.featureStores = featureStores;
+ return this;
+ }
+
+ public TransactionContextBuilder withResponse(TransactionResponse response) {
+ this.response = response;
+ return this;
+ }
+
+ public TransactionContextBuilder withHandler(TransactionElementHandler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public TransactionContextBuilder withContext(TransactionContext context) {
+ this.element = context.getElement();
+ this.featureStores = context.getFeatureStores();
+ this.handler = context.getHandler();
+ this.request = context.getRequest();
+ this.response = context.getResponse();
+ return this;
+ }
+
+ public TransactionContext build() {
+ return new TransactionContext(element, request, featureStores, response, handler);
+ }
+}
From d4e1cae2896196e4db588c561d408178ea781e55 Mon Sep 17 00:00:00 2001
From: Nuno Oliveira
Date: Thu, 3 Aug 2017 13:33:23 +0100
Subject: [PATCH 02/15] Add documentation index and reorganise packages
---
doc/en/user/source/community/index.rst | 1 +
doc/en/user/source/community/nsg-profile/index.rst | 12 ++++++++++++
.../nsg/{ => versioning}/TimeVersioning.java | 2 +-
.../nsg/{ => versioning}/TimeVersioningCallback.java | 2 +-
.../{ => versioning}/VersioningFilterAdapter.java | 6 +-----
.../{ => versioning}/web/WfsVersioningConfig.html | 0
.../{ => versioning}/web/WfsVersioningConfig.java | 4 ++--
.../src/main/resources/applicationContext.xml | 2 +-
.../src/test/java/org/geoserver/nsg/TestsUtils.java | 12 ++++++------
.../nsg/{ => versioning}/TimeVersioningTest.java | 3 ++-
.../web/WfsVersioningConfigTest.java | 5 +----
11 files changed, 28 insertions(+), 21 deletions(-)
create mode 100644 doc/en/user/source/community/nsg-profile/index.rst
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/TimeVersioning.java (98%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/TimeVersioningCallback.java (99%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/VersioningFilterAdapter.java (95%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/web/WfsVersioningConfig.html (100%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/web/WfsVersioningConfig.java (96%)
rename src/community/nsg-profile/src/test/java/org/geoserver/nsg/{ => versioning}/TimeVersioningTest.java (98%)
rename src/community/nsg-profile/src/test/java/org/geoserver/nsg/{ => versioning}/web/WfsVersioningConfigTest.java (96%)
diff --git a/doc/en/user/source/community/index.rst b/doc/en/user/source/community/index.rst
index f87a82b6b6b..9f9b639c386 100644
--- a/doc/en/user/source/community/index.rst
+++ b/doc/en/user/source/community/index.rst
@@ -45,3 +45,4 @@ officially part of the GeoServer releases. They are however built along with the
onelogin/index
wmts-multidimensional/index
notification/index
+ nsg-profile/index
diff --git a/doc/en/user/source/community/nsg-profile/index.rst b/doc/en/user/source/community/nsg-profile/index.rst
new file mode 100644
index 00000000000..02490d6cbc5
--- /dev/null
+++ b/doc/en/user/source/community/nsg-profile/index.rst
@@ -0,0 +1,12 @@
+.. _community_nsg_profile:
+
+NSG Profile
+===========
+
+
+Index Result Type
+-----------------
+
+
+PageResults Operation
+---------------------
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioning.java
similarity index 98%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioning.java
index ad891d29929..6abc73e47a4 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioning.java
@@ -2,7 +2,7 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.geoserver.catalog.FeatureTypeInfo;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioningCallback.java
similarity index 99%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioningCallback.java
index ee87f12f628..8b45559076a 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioningCallback.java
@@ -2,7 +2,7 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/VersioningFilterAdapter.java
similarity index 95%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/VersioningFilterAdapter.java
index 6a0b1c68222..42be444be5d 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/VersioningFilterAdapter.java
@@ -2,20 +2,16 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.geoserver.catalog.FeatureTypeInfo;
-import org.geotools.factory.CommonFactoryFinder;
-import org.geotools.factory.GeoTools;
import org.geotools.filter.visitor.DuplicatingFilterVisitor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
-import org.opengis.filter.FilterFactory2;
import org.opengis.filter.Id;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.identity.Identifier;
import org.opengis.filter.identity.ResourceId;
-import org.opengis.filter.sort.SortOrder;
import java.util.Date;
import java.util.HashSet;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.html
similarity index 100%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.html
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.java
similarity index 96%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.java
index 8ef574e602c..f575a2c96ec 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.java
@@ -3,7 +3,7 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg.web;
+package org.geoserver.nsg.versioning.web;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
@@ -16,7 +16,7 @@
import org.apache.wicket.model.StringResourceModel;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
-import org.geoserver.nsg.TimeVersioning;
+import org.geoserver.nsg.versioning.TimeVersioning;
import org.geoserver.web.publish.PublishedConfigurationPanel;
import java.sql.Timestamp;
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index a33c1190b39..875245237cd 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -9,7 +9,7 @@
-
+
org.geoserver.catalog.FeatureTypeInfo
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
index 653ac8a8e4b..3e64290a4fa 100644
--- a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
@@ -10,15 +10,15 @@
import java.io.InputStream;
-import static org.geoserver.nsg.TimeVersioning.disable;
-import static org.geoserver.nsg.TimeVersioning.enable;
+import static org.geoserver.nsg.versioning.TimeVersioning.disable;
+import static org.geoserver.nsg.versioning.TimeVersioning.enable;
-final class TestsUtils {
+public final class TestsUtils {
private TestsUtils() {
}
- static String readResource(String resourceName) {
+ public static String readResource(String resourceName) {
try (InputStream input = TestsUtils.class.getResourceAsStream(resourceName)) {
return IOUtils.toString(input);
} catch (Exception exception) {
@@ -26,8 +26,8 @@ static String readResource(String resourceName) {
}
}
- static void updateFeatureTypeTimeVersioning(Catalog catalog, String featureTypeName,
- boolean enabled, String idProperty, String timeProperty) {
+ public static void updateFeatureTypeTimeVersioning(Catalog catalog, String featureTypeName,
+ boolean enabled, String idProperty, String timeProperty) {
FeatureTypeInfo featureType = catalog.getFeatureTypeByName(featureTypeName);
if (enabled) {
enable(featureType, idProperty, timeProperty);
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/TimeVersioningTest.java
similarity index 98%
rename from src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
rename to src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/TimeVersioningTest.java
index 758d1fa3370..d69d6fe33e5 100644
--- a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/TimeVersioningTest.java
@@ -2,13 +2,14 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
+import org.geoserver.nsg.TestsUtils;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.crs.DefaultGeographicCRS;
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/web/WfsVersioningConfigTest.java
similarity index 96%
rename from src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
rename to src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/web/WfsVersioningConfigTest.java
index efe29b00e6a..b580d8e1f6e 100644
--- a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/web/WfsVersioningConfigTest.java
@@ -2,13 +2,10 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg.web;
+package org.geoserver.nsg.versioning.web;
-import org.apache.wicket.markup.html.form.CheckBox;
-import org.apache.wicket.util.tester.FormTester;
import org.geoserver.web.GeoServerWicketTestSupport;
import org.geoserver.web.publish.PublishedConfigurationPage;
-import org.geoserver.wfs.GMLInfo;
import org.geoserver.wfs.WFSInfo;
import org.junit.Test;
From 62df0e32ad5bcba5906504a41f4e66ca5eca1466 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Tue, 22 Aug 2017 15:57:46 +0200
Subject: [PATCH 03/15] Added dispatcher and output format
---
.../pagination/random/IndexOutputFormat.java | 38 +++++++++++
.../IndexResultTypeDisapatcherCallback.java | 63 +++++++++++++++++++
.../src/main/resources/applicationContext.xml | 40 +++++++-----
3 files changed, 126 insertions(+), 15 deletions(-)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
new file mode 100644
index 00000000000..06498301884
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -0,0 +1,38 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.platform.Operation;
+import org.geoserver.platform.ServiceException;
+import org.geoserver.wfs.response.v2_0.HitsOutputFormat;
+
+import net.opengis.wfs20.BaseRequestType;
+
+/**
+ * This output format handles requests if the original requested result type was "index"
+ * It checks {@link BaseRequestType#getExtendedProperties()} for
+ * {@link IndexResultTypeDisapatcherCallback#RESULT_TYPE_INDEX_PARAMETER} valued as true
+ *
+ * @author sandr
+ *
+ */
+public class IndexOutputFormat extends HitsOutputFormat {
+
+ public IndexOutputFormat(GeoServer gs) {
+ super(gs);
+ }
+
+ @Override
+ public void write(Object value, OutputStream output, Operation operation)
+ throws IOException, ServiceException {
+ super.write(value, output, operation);
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
new file mode 100644
index 00000000000..72847971fa6
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
@@ -0,0 +1,63 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.ows.AbstractDispatcherCallback;
+import org.geoserver.ows.Request;
+import org.geoserver.ows.Response;
+import org.geoserver.platform.Operation;
+
+import net.opengis.wfs20.ResultTypeType;
+
+/**
+ *
+ * When a request that contains the "resultType" parameter arrives, if the parameter value is
+ * "index" it is substituted by "hits".
+ *
+ *
+ * A new entry named RESULT_TYPE_INDEX specifying that the original result type was "index" is added
+ * to KVP maps
+ *
+ */
+
+public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallback {
+
+ private GeoServer gs;
+
+ private static final String RESULT_TYPE_PARAMETER = "resultType";
+
+ private static final String RESULT_TYPE_INDEX = "index";
+
+ static final String RESULT_TYPE_INDEX_PARAMETER = "RESULT_TYPE_INDEX";
+
+ public IndexResultTypeDisapatcherCallback(GeoServer gs) {
+ this.gs = gs;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Request init(Request request) {
+ Object resultType = request.getKvp().get(RESULT_TYPE_PARAMETER);
+ if (resultType != null && resultType.toString().equals(RESULT_TYPE_INDEX)) {
+ request.getKvp().put(RESULT_TYPE_PARAMETER, ResultTypeType.HITS);
+ request.getKvp().put(RESULT_TYPE_INDEX_PARAMETER, true);
+ }
+ return super.init(request);
+ }
+
+ @Override
+ public Response responseDispatched(Request request, Operation operation, Object result,
+ Response response) {
+ Response newResponse = response;
+ if (request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER) != null
+ && (Boolean) request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER)) {
+ newResponse = new IndexOutputFormat(this.gs);
+ }
+ return super.responseDispatched(request, operation, result, newResponse);
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index 875245237cd..c54d58806ee 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -1,19 +1,29 @@
-
-
-
-
-
-
-
-
-
- org.geoserver.catalog.FeatureTypeInfo
-
-
-
+
+
+
+
+
+
+
+
+
+ org.geoserver.catalog.FeatureTypeInfo
+
+
+
+
+
+ When a request using the index result type comes in a
+ dispatcher callback
+ this switch the "index" value by the "hits" value
+
+
+
From b22d6b67332edc0229a28faeae5af0ac568da3bf Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Fri, 25 Aug 2017 15:25:05 +0200
Subject: [PATCH 04/15] Setup of GetFeature requests storage: - configuration
file parser and change listerner - db storage - file storage - clean task
---
src/community/nsg-profile/pom.xml | 5 +
.../pagination/random/IndexConfiguration.java | 74 ++++
.../pagination/random/IndexInitializer.java | 344 ++++++++++++++++++
.../pagination/random/IndexOutputFormat.java | 116 +++++-
.../IndexResultTypeDisapatcherCallback.java | 8 +
.../src/main/resources/applicationContext.xml | 12 +-
.../main/resources/configuration.properties | 6 +
7 files changed, 559 insertions(+), 6 deletions(-)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
create mode 100644 src/community/nsg-profile/src/main/resources/configuration.properties
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
index b9e26ef3fbc..b22b61e0b84 100644
--- a/src/community/nsg-profile/pom.xml
+++ b/src/community/nsg-profile/pom.xml
@@ -30,6 +30,11 @@
gs-web-core
${project.version}
+
+
+ org.geotools.jdbc
+ gt-jdbc-h2
+
org.geoserver
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
new file mode 100644
index 00000000000..4b352213440
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
@@ -0,0 +1,74 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.util.Map;
+
+import org.geoserver.platform.resource.Resource;
+import org.geotools.data.DataStore;
+
+/**
+ *
+ * Class used to store the index result type configuration managed by {@link IndexInitializer}
+ */
+public class IndexConfiguration {
+
+ private static DataStore currentDataStore;
+
+ private static Resource storageResource;
+
+ private static Long timeToLive = 600l;
+
+ private static Map currentDataStoreParams;
+
+ /**
+ * Store the DB parameters and the relative {@link DataStore}
+ *
+ * @param currentDataStoreParams
+ * @param currentDataStore
+ */
+ public static void setCurrentDataStore(Map currentDataStoreParams,
+ DataStore currentDataStore) {
+ IndexConfiguration.currentDataStoreParams = currentDataStoreParams;
+ IndexConfiguration.currentDataStore = currentDataStore;
+ }
+
+ /**
+ * Store the reference to resource used to archive the serialized GetFeatureRequest
+ *
+ * @param currentDataStoreParams
+ * @param currentDataStore
+ */
+ public static void setStorageResource(Resource storageResource) {
+ IndexConfiguration.storageResource = storageResource;
+ }
+
+ /**
+ * Store the value of time to live of stored GetFeatureRequest
+ *
+ * @param timeToLive
+ */
+ public static void setTimeToLive(Long timeToLive) {
+ IndexConfiguration.timeToLive = timeToLive;
+ }
+
+ public static DataStore getCurrentDataStore() {
+ return currentDataStore;
+ }
+
+ public static Map getCurrentDataStoreParams() {
+ return currentDataStoreParams;
+ }
+
+ public static Resource getStorageResource() {
+ return storageResource;
+ }
+
+ public static Long getTimeToLive() {
+ return timeToLive;
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
new file mode 100644
index 00000000000..463772625dd
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -0,0 +1,344 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.config.GeoServerDataDirectory;
+import org.geoserver.config.GeoServerInitializer;
+import org.geoserver.platform.GeoServerExtensions;
+import org.geoserver.platform.GeoServerResourceLoader;
+import org.geoserver.platform.resource.FileSystemResourceStore;
+import org.geoserver.platform.resource.Resource;
+import org.geoserver.platform.resource.ResourceListener;
+import org.geoserver.platform.resource.ResourceNotification;
+import org.geoserver.platform.resource.ResourceNotification.Kind;
+import org.geotools.data.DataStore;
+import org.geotools.data.DataStoreFinder;
+import org.geotools.data.DataUtilities;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.Transaction;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureIterator;
+import org.geotools.data.simple.SimpleFeatureSource;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.filter.text.cql2.CQL;
+import org.geotools.jdbc.JDBCDataStoreFactory;
+import org.geotools.util.logging.Logging;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.filter.Filter;
+
+/**
+ *
+ * Class used to parse the configuration properties stored in nsg-profile module folder:
+ *
+ * resultSets.storage.path path where to store the serialized GetFeatureRequest with name
+ * of random UUID.
+ * resultSets.timeToLive time to live value, all the stored requests that have not been
+ * used for a period of time bigger than this will be deleted.
+ * resultSets.db.{@link JDBCDataStoreFactory#DBTYPE}
+ * resultSets.db.{@link JDBCDataStoreFactory#DATABASE}
+ * resultSets.db.{@link JDBCDataStoreFactory#HOST}
+ * resultSets.db.{@link JDBCDataStoreFactory#PORT}
+ * resultSets.db.{@link JDBCDataStoreFactory#SCHEMA}
+ * resultSets.db.{@link JDBCDataStoreFactory#USER}
+ * resultSets.db.{@link JDBCDataStoreFactory#PASSWD}
+ *
+ * All configuration properties is changeable at runtime so when this properties is updated the
+ * module take the appropriate action:
+ *
+ * When the index DB is changed the new DB should be used and the content of the old table moved
+ * to the new table. If the new DB already has the index table it should be emptied,
+ * When the storage path is changed, the new storage path should be used and the old storage
+ * path content should be moved to the new one,
+ * When the the time to live is changed the {@link #clean()} procedure will update.
+ *
+ *
+ * The class is also responsible to {@link #clean()} the stored requests (result sets) that have not
+ * been used for a period of time bigger than the configured time to live value
+ *
+ *
+ * @author sandr
+ *
+ */
+
+public class IndexInitializer implements GeoServerInitializer {
+
+ static Logger LOGGER = Logging.getLogger(IndexInitializer.class);
+
+ static final String PROPERTY_DB_PREFIX = "resultSets.db.";
+
+ static final String PROPERTY_FILENAME = "configuration.properties";
+
+ static final String MODULE_DIR = "nsg-profile";
+
+ public static final String STORE_SCHEMA_NAME = "RESULT_SET";
+
+ public static final String STORE_SCHEMA = "ID:\"\",created:0,updated:0";
+
+ /*
+ * Lock to synchronize activity of clean task with listener that changes the DB and file
+ * resources
+ */
+ private final Object lock = new Object();
+
+ @Override
+ public void initialize(GeoServer geoServer) throws Exception {
+ GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class);
+ GeoServerDataDirectory dd = new GeoServerDataDirectory(loader);
+ Resource resource = dd.get(MODULE_DIR + "/" + PROPERTY_FILENAME);
+ if (loader != null) {
+ File directory = loader.findOrCreateDirectory(MODULE_DIR);
+ File file = new File(directory, PROPERTY_FILENAME);
+ // Create default configuration file
+ if (!file.exists()) {
+ InputStream stream = IndexInitializer.class
+ .getResourceAsStream("/" + PROPERTY_FILENAME);
+ Properties properties = new Properties();
+ properties.load(stream);
+ // Replace GEOSERVER_DATA_DIR placeholder
+ properties.replaceAll((k, v) -> ((String) v).replace("${GEOSERVER_DATA_DIR}",
+ dd.root().getPath()));
+ // Create resource and save properties
+ OutputStream out = resource.out();
+ properties.store(out, null);
+ out.close();
+ }
+ loadConfigurations(resource);
+ // Listen for changes in configuration file and reload properties
+ resource.addListener(new ResourceListener() {
+ @Override
+ public void changed(ResourceNotification notify) {
+ if (notify.getKind() == Kind.ENTRY_MODIFY) {
+ try {
+ loadConfigurations(resource);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error reload confiugrations.", exception);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Helper method that
+ */
+ private void loadConfigurations(Resource resource) throws IOException {
+ synchronized (lock) {
+ Properties properties = new Properties();
+ properties.load(resource.in());
+ // Reload database
+ Map params = new HashMap<>();
+ params.put(JDBCDataStoreFactory.DBTYPE.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.DBTYPE.key));
+ params.put(JDBCDataStoreFactory.DATABASE.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.DATABASE.key));
+ params.put(JDBCDataStoreFactory.HOST.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.HOST.key));
+ params.put(JDBCDataStoreFactory.PORT.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.PORT.key));
+ params.put(JDBCDataStoreFactory.SCHEMA.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.SCHEMA.key));
+ params.put(JDBCDataStoreFactory.USER.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.USER.key));
+ params.put(JDBCDataStoreFactory.PASSWD.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.PASSWD.key));
+ /**
+ * When the index DB is changed the new DB should be used and the content of the old
+ * table moved to the new table. If the new DB already has the index table it should be
+ * emptied
+ */
+ manageDBChange(params);
+ /*
+ * If the storage path is changed, the new storage path should be used and the old
+ * storage path content should be moved to the new one
+ */
+ manageStorageChange(resource, properties.get("resultSets.storage.path"));
+ /*
+ * Change time to live
+ */
+ manageTimeToLiveChange(properties.get("resultSets.timeToLive"));
+ }
+
+ }
+
+ /**
+ * Helper method that
+ */
+ private void manageTimeToLiveChange(Object timneToLive) {
+ try {
+ if (timneToLive != null) {
+ String timneToLiveStr = (String) timneToLive;
+ IndexConfiguration.setTimeToLive(Long.parseLong(timneToLiveStr));
+ }
+ } catch (Exception exception) {
+ throw new RuntimeException("Error on change time to live", exception);
+ }
+ }
+
+ /**
+ * Helper method that move resources files form current folder to the new one, current storage
+ * is deleted
+ */
+ private void manageStorageChange(Resource resource, Object newStorage) {
+ try {
+ if (newStorage != null) {
+ String newStorageStr = (String) newStorage;
+ Resource newResource = new FileSystemResourceStore(new File(newStorageStr)).get("");
+ Resource exResource = IndexConfiguration.getStorageResource();
+ if (exResource != null && !newResource.dir().getAbsolutePath()
+ .equals(exResource.dir().getAbsolutePath())) {
+ exResource.delete();
+ IndexConfiguration.setStorageResource(newResource);
+ }
+ }
+ } catch (Exception exception) {
+ throw new RuntimeException("Error on change store", exception);
+ }
+ }
+
+ /**
+ * Helper method that move DB data from old store to new one
+ */
+ private void manageDBChange(Map params) {
+ try {
+ DataStore exDataStore = IndexConfiguration.getCurrentDataStore();
+ DataStore newDataStore = DataStoreFinder.getDataStore(params);
+ if (exDataStore != null) {
+ // New store is valid and is different from current one
+ if (newDataStore != null && !isStorageTheSame(params)) {
+ // Create table in new store
+ createTable(newDataStore, true);
+ // Move data to new store
+ moveData(exDataStore, newDataStore);
+ // Delete old store
+ exDataStore.dispose();
+ }
+ } else {
+ // Create schema
+ createTable(newDataStore, false);
+ }
+ IndexConfiguration.setCurrentDataStore(params, newDataStore);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error reload DB confiugrations.", exception);
+ }
+ }
+
+ /**
+ * Helper method that check id the DB is the same, matching the JDBC configurations parameters.
+ */
+ private Boolean isStorageTheSame(Map newParams) {
+ Map currentParams = IndexConfiguration.getCurrentDataStoreParams();
+ return currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
+ .equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key))
+ && currentParams.get(JDBCDataStoreFactory.DATABASE.key)
+ .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
+ && currentParams.get(JDBCDataStoreFactory.HOST.key)
+ .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
+ && currentParams.get(JDBCDataStoreFactory.PORT.key)
+ .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
+ && currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
+ .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
+ }
+
+ /**
+ * Helper method that create a new table on DB to store resource informations
+ */
+ private void createTable(DataStore dataStore, Boolean forceDelete) throws Exception {
+ SimpleFeatureType schema = dataStore.getSchema(STORE_SCHEMA_NAME);
+ // Schema exists
+ if (schema != null) {
+ // Delete of exist is required, and then create a new one
+ if (forceDelete) {
+ dataStore.removeSchema(STORE_SCHEMA_NAME);
+ schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ dataStore.createSchema(schema);
+ }
+ // Schema not exists, create a new one
+ } else {
+ schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ dataStore.createSchema(schema);
+ }
+ }
+
+ /**
+ * Helper method that move resource informations from current DB to the new one
+ */
+ private void moveData(DataStore exDataStore, DataStore newDataStore) throws Exception {
+ Transaction session = new DefaultTransaction("Adding");
+ try {
+ SimpleFeatureSource exFs = exDataStore.getFeatureSource(STORE_SCHEMA_NAME);
+ SimpleFeatureStore newFs = (SimpleFeatureStore) newDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ newFs.setTransaction(session);
+ newFs.addFeatures(exFs.getFeatures());
+ session.commit();
+ } catch (Throwable t) {
+ session.rollback();
+ throw new RuntimeException("Error on move data", t);
+ } finally {
+ session.close();
+ }
+ }
+
+ /**
+ * Delete all the stored requests (result sets) that have not been used for a period of time
+ * bigger than the configured time to live value. Clean also related resource files.
+ *
+ * Executed by scheduler, for details see Spring XML configuration
+ */
+ public void clean() throws Exception {
+ synchronized (lock) {
+ Transaction session = new DefaultTransaction("RemoveOld");
+ try {
+ // Remove record
+ Long timeToLive = IndexConfiguration.getTimeToLive();
+ DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ Long now = new Date().getTime();
+ Long liveTreshold = now - timeToLive * 1000;
+ Filter filter = CQL.toFilter("updated < " + liveTreshold);
+ SimpleFeatureCollection toRemoved = store.getFeatures(filter);
+ // Remove file
+ Resource currentResource = IndexConfiguration.getStorageResource();
+ SimpleFeatureIterator iterator = toRemoved.features();
+ try {
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ currentResource.get(feature.getID()).delete();
+ }
+ } finally {
+ iterator.close();
+ }
+ store.removeFeatures(filter);
+ if (LOGGER.isLoggable(Level.FINEST)) {
+ LOGGER.finest("CLEAN executed, removed stored requests older than "
+ + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+ .format(new Date(liveTreshold)));
+ }
+ } catch (Throwable t) {
+ session.rollback();
+ throw new RuntimeException("Error on move data", t);
+ } finally {
+ session.close();
+ }
+ }
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index 06498301884..1a4a60ff53c 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -7,32 +7,140 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
import org.geoserver.config.GeoServer;
+import org.geoserver.ows.util.OwsUtils;
+import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
+import org.geoserver.wfs.WFSInfo;
+import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.response.v2_0.HitsOutputFormat;
+import org.geotools.util.logging.Logging;
+import org.geotools.wfs.v2_0.WFS;
+import org.geotools.wfs.v2_0.WFSConfiguration;
+import org.geotools.xml.Encoder;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
-import net.opengis.wfs20.BaseRequestType;
+import net.opengis.wfs20.GetFeatureType;
/**
* This output format handles requests if the original requested result type was "index"
- * It checks {@link BaseRequestType#getExtendedProperties()} for
- * {@link IndexResultTypeDisapatcherCallback#RESULT_TYPE_INDEX_PARAMETER} valued as true
+ * See {@link IndexResultTypeDisapatcherCallback}
*
* @author sandr
*
*/
public class IndexOutputFormat extends HitsOutputFormat {
+ static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
+ GetFeatureType request;
+ String resultSetId;
+
public IndexOutputFormat(GeoServer gs) {
super(gs);
}
-
+
@Override
public void write(Object value, OutputStream output, Operation operation)
throws IOException, ServiceException {
+ // extract GetFeature request
+ request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
+ GetFeatureType.class);
+ // generate an UUID (resultSetID) for this request
+ resultSetId = UUID.randomUUID().toString();
super.write(value, output, operation);
}
+ @Override
+ protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSInfo wfs)
+ throws IOException {
+
+ hits.setNumberOfFeatures(BigInteger.ZERO);
+ // instantiate the XML encoder
+ Encoder encoder = new Encoder(new WFSConfiguration());
+ encoder.setEncoding(Charset.forName(wfs.getGeoServer().getSettings().getCharset()));
+ encoder.setSchemaLocation(WFS.NAMESPACE,
+ ResponseUtils.appendPath(wfs.getSchemaBaseURL(), "wfs/2.0/wfs.xsd"));
+ Document document;
+ try {
+ // encode the HITS result using FeatureCollection as the root XML element
+ document = encoder.encodeAsDOM(hits.getAdaptee(), WFS.FeatureCollection);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error encoding INDEX result.", exception);
+ }
+ // add the resultSetID attribute to the result
+ addResultSetIdElement(document, resultSetId);
+ // write the XML document to response output stream
+ writeDocument(document, output);
+ }
+
+ /**
+ * Helper method that serialize GetFeature request, store it in the file system and associate it with resultSetId
+ */
+ protected void storeGetFeature(){
+
+ }
+
+ /**
+ * Helper method that adds the resultSetID attribute to XML result. If no FeatureCollection
+ * element can be found nothing will be done.
+ */
+ private static void addResultSetIdElement(Document document, String resultSetId) {
+ // search FeatureCollection XML nodes
+ NodeList nodes = document.getElementsByTagName("wfs:FeatureCollection");
+ if (nodes.getLength() != 1) {
+ // only one node should exists, let's log an warning an move on
+ LOGGER.warning(
+ "No feature collection element could be found, resultSetID attribute will not be added.");
+ return;
+ }
+ // get the FeatureCollection node
+ Node node = nodes.item(0);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ // the found node is a XML element so let's add the resultSetID attribute
+ Element element = (Element) node;
+ element.setAttribute("resultSetID", resultSetId);
+ } else {
+ // unlikely but we got a XML node that is not a XML element
+ LOGGER.warning(
+ "Feature collection node is not a XML element, resultSetID attribute will not be added.");
+ }
+ }
+
+ /**
+ * Helper method that just writes a XML document to a given output stream.
+ */
+ private static void writeDocument(Document document, OutputStream output) {
+ // instantiate a new XML transformer
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer;
+ try {
+ transformer = transformerFactory.newTransformer();
+ } catch (Exception exception) {
+ throw new RuntimeException("Error creating XML transformer.", exception);
+ }
+ // write the XML document to the provided output stream
+ DOMSource source = new DOMSource(document);
+ StreamResult result = new StreamResult(output);
+ try {
+ transformer.transform(source, result);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error writing INDEX result to the output stream.",
+ exception);
+ }
+ }
+
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
index 72847971fa6..6a7f49e4cc2 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
@@ -5,11 +5,14 @@
package org.geoserver.nsg.pagination.random;
+import java.util.logging.Logger;
+
import org.geoserver.config.GeoServer;
import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.Request;
import org.geoserver.ows.Response;
import org.geoserver.platform.Operation;
+import org.geotools.util.logging.Logging;
import net.opengis.wfs20.ResultTypeType;
@@ -22,9 +25,14 @@
* A new entry named RESULT_TYPE_INDEX specifying that the original result type was "index" is added
* to KVP maps
*
+ *
+ * The object that manage response of type HitsOutputFormat is replaced with IndexOutputFormat before response has been dispatched
+ *
*/
public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallback {
+
+ static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
private GeoServer gs;
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index c54d58806ee..acdcb9c2caa 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -1,8 +1,9 @@
+ xmlns:task="http://www.springframework.org/schema/task"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
@@ -26,4 +27,11 @@
+
+
+
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/resources/configuration.properties b/src/community/nsg-profile/src/main/resources/configuration.properties
new file mode 100644
index 00000000000..3d9f99c95c2
--- /dev/null
+++ b/src/community/nsg-profile/src/main/resources/configuration.properties
@@ -0,0 +1,6 @@
+resultSets.storage.path=${GEOSERVER_DATA_DIR}/nsg-profile/resultSets
+resultSets.timeToLive=601
+resultSets.db.dbtype=h2
+resultSets.db.database=${GEOSERVER_DATA_DIR}/nsg-profile/db/resultSets
+resultSets.db.user=sa
+resultSets.db.password=sa
\ No newline at end of file
From abaf78feef0db3190f8c1ad133fbf83106a246e8 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Fri, 25 Aug 2017 18:10:54 +0200
Subject: [PATCH 05/15] Added EMF and H2 save procedure to store GetFeature
calls
---
src/community/nsg-profile/pom.xml | 8 ++-
.../pagination/random/IndexInitializer.java | 4 +-
.../pagination/random/IndexOutputFormat.java | 71 ++++++++++++++++---
3 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
index b22b61e0b84..b93c0c49d15 100644
--- a/src/community/nsg-profile/pom.xml
+++ b/src/community/nsg-profile/pom.xml
@@ -23,18 +23,22 @@
org.geoserver
gs-wfs
- ${project.version}
org.geoserver.web
gs-web-core
- ${project.version}
org.geotools.jdbc
gt-jdbc-h2
+
+
+ org.eclipse.emf
+ org.eclipse.emf.ecore.xmi
+ 2.12.0
+
org.geoserver
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
index 463772625dd..0abacf2fe93 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -205,8 +205,8 @@ private void manageStorageChange(Resource resource, Object newStorage) {
if (exResource != null && !newResource.dir().getAbsolutePath()
.equals(exResource.dir().getAbsolutePath())) {
exResource.delete();
- IndexConfiguration.setStorageResource(newResource);
}
+ IndexConfiguration.setStorageResource(newResource);
}
} catch (Exception exception) {
throw new RuntimeException("Error on change store", exception);
@@ -281,7 +281,7 @@ private void createTable(DataStore dataStore, Boolean forceDelete) throws Except
* Helper method that move resource informations from current DB to the new one
*/
private void moveData(DataStore exDataStore, DataStore newDataStore) throws Exception {
- Transaction session = new DefaultTransaction("Adding");
+ Transaction session = new DefaultTransaction("Moving");
try {
SimpleFeatureSource exFs = exDataStore.getFeatureSource(STORE_SCHEMA_NAME);
SimpleFeatureStore newFs = (SimpleFeatureStore) newDataStore
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index 1a4a60ff53c..7d557607294 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -9,6 +9,9 @@
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
import java.util.UUID;
import java.util.logging.Logger;
@@ -17,18 +20,29 @@
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
+import org.geoserver.platform.resource.Resource;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.response.v2_0.HitsOutputFormat;
+import org.geotools.data.DataStore;
+import org.geotools.data.collection.ListFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.util.logging.Logging;
import org.geotools.wfs.v2_0.WFS;
import org.geotools.wfs.v2_0.WFSConfiguration;
import org.geotools.xml.Encoder;
+import org.opengis.feature.simple.SimpleFeature;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -46,28 +60,39 @@
public class IndexOutputFormat extends HitsOutputFormat {
static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
- GetFeatureType request;
+
String resultSetId;
+ private static ResourceSet resSet;
+
+ static {
+ // Register XMI serializer
+ resSet = new ResourceSetImpl();
+ resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("feature",
+ new XMIResourceFactoryImpl());
+ }
+
public IndexOutputFormat(GeoServer gs) {
super(gs);
}
-
+
@Override
public void write(Object value, OutputStream output, Operation operation)
throws IOException, ServiceException {
// extract GetFeature request
- request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
+ GetFeatureType request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
GetFeatureType.class);
// generate an UUID (resultSetID) for this request
resultSetId = UUID.randomUUID().toString();
+ // store request and associate it to UUID
+ storeGetFeature(resultSetId, request);
super.write(value, output, operation);
}
@Override
protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSInfo wfs)
throws IOException {
-
+
hits.setNumberOfFeatures(BigInteger.ZERO);
// instantiate the XML encoder
Encoder encoder = new Encoder(new WFSConfiguration());
@@ -86,12 +111,42 @@ protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSIn
// write the XML document to response output stream
writeDocument(document, output);
}
-
+
/**
- * Helper method that serialize GetFeature request, store it in the file system and associate it with resultSetId
+ * Helper method that serialize GetFeature request, store it in the file system and associate it
+ * with resultSetId
+ *
+ * @param request
+ * @param resultSetId
+ * @throws Exception
*/
- protected void storeGetFeature(){
-
+ protected void storeGetFeature(String resultSetId, GetFeatureType ft) throws RuntimeException {
+ try {
+ DataStore dataStore = IndexConfiguration.getCurrentDataStore();
+ // Create and store new feature
+ SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore
+ .getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
+ SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureStore.getSchema());
+ Long now = new Date().getTime();
+ builder.add(resultSetId);
+ builder.add(now);
+ builder.add(now);
+ SimpleFeature feature = builder.buildFeature(null);
+ SimpleFeatureCollection collection = new ListFeatureCollection(featureStore.getSchema(),
+ Arrays.asList(feature));
+ featureStore.addFeatures(collection);
+
+ // Create and store file
+ Resource storageResource = IndexConfiguration.getStorageResource();
+
+ org.eclipse.emf.ecore.resource.Resource emfRes = resSet
+ .createResource(URI.createFileURI(storageResource.dir().getAbsolutePath() + "\\"
+ + resultSetId + ".feature"));
+ emfRes.getContents().add(ft);
+ emfRes.save(Collections.EMPTY_MAP);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error storing feature.", exception);
+ }
}
/**
From d4fd0935182efa45437ab76363fccf712582e3ed Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Wed, 30 Aug 2017 11:40:29 +0200
Subject: [PATCH 06/15] Implemented PageResults operation
---
.../pagination/random/IndexConfiguration.java | 3 +
.../pagination/random/IndexInitializer.java | 51 ++++---
.../pagination/random/IndexOutputFormat.java | 6 +-
...=> IndexResultTypeDispatcherCallback.java} | 14 +-
.../random/PageResultsDispatcherCallback.java | 52 +++++++
.../random/PageResultsWebFeatureService.java | 143 ++++++++++++++++++
.../random/ResultTypeKvpParser.java | 23 +++
.../src/main/resources/applicationContext.xml | 42 ++++-
8 files changed, 295 insertions(+), 39 deletions(-)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/{IndexResultTypeDisapatcherCallback.java => IndexResultTypeDispatcherCallback.java} (86%)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
index 4b352213440..a886fcaa10a 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
@@ -13,6 +13,9 @@
/**
*
* Class used to store the index result type configuration managed by {@link IndexInitializer}
+ *
+ * @author sandr
+ *
*/
public class IndexConfiguration {
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
index 0abacf2fe93..2ece19553c2 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -36,6 +36,7 @@
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.feature.NameImpl;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.jdbc.JDBCDataStoreFactory;
import org.geotools.util.logging.Logging;
@@ -89,7 +90,7 @@ public class IndexInitializer implements GeoServerInitializer {
public static final String STORE_SCHEMA_NAME = "RESULT_SET";
- public static final String STORE_SCHEMA = "ID:\"\",created:0,updated:0";
+ public static final String STORE_SCHEMA = "ID:java.lang.String,created:java.lang.Long,updated:java.lang.Long";
/*
* Lock to synchronize activity of clean task with listener that changes the DB and file
@@ -248,31 +249,31 @@ private Boolean isStorageTheSame(Map newParams) {
return currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
.equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key))
&& currentParams.get(JDBCDataStoreFactory.DATABASE.key)
- .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
+ .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
&& currentParams.get(JDBCDataStoreFactory.HOST.key)
- .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
+ .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
&& currentParams.get(JDBCDataStoreFactory.PORT.key)
- .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
+ .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
&& currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
- .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
+ .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
}
/**
* Helper method that create a new table on DB to store resource informations
*/
private void createTable(DataStore dataStore, Boolean forceDelete) throws Exception {
- SimpleFeatureType schema = dataStore.getSchema(STORE_SCHEMA_NAME);
+ Boolean exists = dataStore.getNames().contains(new NameImpl(STORE_SCHEMA_NAME));
// Schema exists
- if (schema != null) {
+ if (exists) {
// Delete of exist is required, and then create a new one
if (forceDelete) {
dataStore.removeSchema(STORE_SCHEMA_NAME);
- schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ SimpleFeatureType schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
dataStore.createSchema(schema);
}
// Schema not exists, create a new one
} else {
- schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ SimpleFeatureType schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
dataStore.createSchema(schema);
}
}
@@ -310,28 +311,30 @@ public void clean() throws Exception {
// Remove record
Long timeToLive = IndexConfiguration.getTimeToLive();
DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
- SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
- .getFeatureSource(STORE_SCHEMA_NAME);
Long now = new Date().getTime();
Long liveTreshold = now - timeToLive * 1000;
- Filter filter = CQL.toFilter("updated < " + liveTreshold);
- SimpleFeatureCollection toRemoved = store.getFeatures(filter);
- // Remove file
- Resource currentResource = IndexConfiguration.getStorageResource();
- SimpleFeatureIterator iterator = toRemoved.features();
- try {
- while (iterator.hasNext()) {
- SimpleFeature feature = iterator.next();
- currentResource.get(feature.getID()).delete();
+ if(currentDataStore != null){
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ Filter filter = CQL.toFilter("updated < " + liveTreshold);
+ SimpleFeatureCollection toRemoved = store.getFeatures(filter);
+ // Remove file
+ Resource currentResource = IndexConfiguration.getStorageResource();
+ SimpleFeatureIterator iterator = toRemoved.features();
+ try {
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ currentResource.get(feature.getID()).delete();
+ }
+ } finally {
+ iterator.close();
}
- } finally {
- iterator.close();
+ store.removeFeatures(filter);
}
- store.removeFeatures(filter);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("CLEAN executed, removed stored requests older than "
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
- .format(new Date(liveTreshold)));
+ .format(new Date(liveTreshold)));
}
} catch (Throwable t) {
session.rollback();
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index 7d557607294..ee2104b8ec6 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -52,7 +52,7 @@
/**
* This output format handles requests if the original requested result type was "index"
- * See {@link IndexResultTypeDisapatcherCallback}
+ * See {@link IndexResultTypeDispatcherCallback}
*
* @author sandr
*
@@ -83,7 +83,7 @@ public void write(Object value, OutputStream output, Operation operation)
GetFeatureType request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
GetFeatureType.class);
// generate an UUID (resultSetID) for this request
- resultSetId = UUID.randomUUID().toString();
+ resultSetId = UUID.randomUUID().toString().replaceAll("-", "");
// store request and associate it to UUID
storeGetFeature(resultSetId, request);
super.write(value, output, operation);
@@ -138,7 +138,7 @@ protected void storeGetFeature(String resultSetId, GetFeatureType ft) throws Run
// Create and store file
Resource storageResource = IndexConfiguration.getStorageResource();
-
+
org.eclipse.emf.ecore.resource.Resource emfRes = resSet
.createResource(URI.createFileURI(storageResource.dir().getAbsolutePath() + "\\"
+ resultSetId + ".feature"));
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
similarity index 86%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
index 6a7f49e4cc2..fc96a618f06 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
@@ -26,13 +26,17 @@
* to KVP maps
*
*
- * The object that manage response of type HitsOutputFormat is replaced with IndexOutputFormat before response has been dispatched
+ * The object that manage response of type HitsOutputFormat is replaced with IndexOutputFormat
+ * before response has been dispatched
*
+ *
+ * @author sandr
+ *
*/
-public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallback {
-
- static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
+public class IndexResultTypeDispatcherCallback extends AbstractDispatcherCallback {
+
+ static Logger LOGGER = Logging.getLogger(IndexResultTypeDispatcherCallback.class);
private GeoServer gs;
@@ -42,7 +46,7 @@ public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallba
static final String RESULT_TYPE_INDEX_PARAMETER = "RESULT_TYPE_INDEX";
- public IndexResultTypeDisapatcherCallback(GeoServer gs) {
+ public IndexResultTypeDispatcherCallback(GeoServer gs) {
this.gs = gs;
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
new file mode 100644
index 00000000000..e2a0078f835
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
@@ -0,0 +1,52 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.ows.AbstractDispatcherCallback;
+import org.geoserver.ows.Request;
+import org.geoserver.platform.Service;
+import org.geoserver.platform.ServiceException;
+import org.geotools.util.logging.Logging;
+
+/**
+ *
+ * This dispatcher manages service of type {@link PageResultsWebFeatureService} and sets the
+ * parameter ResultSetID present on KVP map.
+ *
+ * Dummy featureId value is added to KVP map to allow dispatcher to manage it as usual WFS 2.0
+ * request.
+ *
+ * @author sandr
+ *
+ */
+
+public class PageResultsDispatcherCallback extends AbstractDispatcherCallback {
+
+ static Logger LOGGER = Logging.getLogger(PageResultsDispatcherCallback.class);
+
+ private GeoServer gs;
+
+ public PageResultsDispatcherCallback(GeoServer gs) {
+ this.gs = gs;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Service serviceDispatched(Request request, Service service) throws ServiceException {
+ if (service.getService() instanceof PageResultsWebFeatureService) {
+ PageResultsWebFeatureService prService = (PageResultsWebFeatureService) service
+ .getService();
+ prService.setResultSetID((String) request.getKvp().get("resultSetID"));
+ request.getKvp().put("featureId", Arrays.asList("dummy"));
+ }
+ return super.serviceDispatched(request, service);
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
new file mode 100644
index 00000000000..e67ee835d8c
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
@@ -0,0 +1,143 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.logging.Logger;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
+import org.geoserver.config.GeoServer;
+import org.geoserver.platform.resource.Resource;
+import org.geoserver.wfs.DefaultWebFeatureService20;
+import org.geoserver.wfs.WFSException;
+import org.geoserver.wfs.request.FeatureCollectionResponse;
+import org.geotools.data.DataStore;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.Transaction;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.filter.text.cql2.CQL;
+import org.geotools.util.logging.Logging;
+import org.opengis.filter.Filter;
+
+import net.opengis.wfs20.GetFeatureType;
+import net.opengis.wfs20.ResultTypeType;
+
+/**
+ * This service supports the PageResults operation and manage it
+ *
+ * @author sandr
+ *
+ */
+
+public class PageResultsWebFeatureService extends DefaultWebFeatureService20 {
+
+ static Logger LOGGER = Logging.getLogger(PageResultsWebFeatureService.class);
+
+ private static ResourceSet resSet;
+
+ private static final String GML32_FORMAT = "application/gml+xml; version=3.2";
+
+ private static final BigInteger DEFAULT_START = new BigInteger("0");
+
+ private static final BigInteger DEFAULT_COUNT = new BigInteger("10");
+
+ private String resultSetID;
+
+ public PageResultsWebFeatureService(GeoServer geoServer) {
+ super(geoServer);
+ // Register XMI serializer
+ resSet = new ResourceSetImpl();
+ resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("feature",
+ new XMIResourceFactoryImpl());
+ }
+
+ /**
+ * Recovers the stored request with associated {@link #resultSetID} and overrides the parameters
+ * using the ones provided with current operation or the default values:
+ *
+ * {@link net.opengis.wfs20.GetFeatureType#getStartIndex StartIndex }
+ * {@link net.opengis.wfs20.GetFeatureType#getCount Count }
+ * {@link net.opengis.wfs20.GetFeatureType#getOutputFormat OutputFormat }
+ * {@link net.opengis.wfs20.GetFeatureType#getResultType ResultType }
+ *
+ * Then executes the GetFeature operation using the WFS 2.0 service implementation and return is
+ * result.
+ *
+ * @param request
+ * @return
+ * @throws WFSException
+ * @throws IOException
+ */
+ public FeatureCollectionResponse pageResults(GetFeatureType request)
+ throws WFSException, IOException {
+ // Retrieve stored request
+ GetFeatureType gft = getFeature(resultSetID);
+ // Update with incoming parameters or defaults
+ BigInteger startIndex = request.getStartIndex() != null ? request.getStartIndex()
+ : DEFAULT_START;
+ BigInteger count = request.getCount() != null ? request.getCount() : DEFAULT_COUNT;
+ String outputFormat = request.getOutputFormat() != null ? request.getOutputFormat()
+ : GML32_FORMAT;
+ ResultTypeType resultType = request.getResultType() != null ? request.getResultType()
+ : ResultTypeType.RESULTS;
+ gft.setStartIndex(startIndex);
+ gft.setCount(count);
+ gft.setOutputFormat(outputFormat);
+ gft.setResultType(resultType);
+ // Execute as getFeature
+ return super.getFeature(gft);
+ }
+
+ /**
+ * Sets the resultSetID
+ *
+ * @param resultSetID
+ */
+ public void setResultSetID(String resultSetID) {
+ this.resultSetID = resultSetID;
+ }
+
+ /**
+ * Helper method that deserializes GetFeature request and updates its last utilization
+ *
+ * @param resultSetID
+ * @return
+ * @throws IOException
+ * @throws Exception
+ */
+ private GetFeatureType getFeature(String resultSetID) throws IOException {
+ GetFeatureType feature = null;
+ Transaction transaction = new DefaultTransaction("Update");
+ try {
+ // Update GetFeature utilization
+ DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
+ store.setTransaction(transaction);
+ Filter filter = CQL.toFilter("ID = '" + resultSetID + "'");
+ store.modifyFeatures("updated", new Date().getTime(), filter);
+ // Retrieve GetFeature from file
+ Resource storageResource = IndexConfiguration.getStorageResource();
+ org.eclipse.emf.ecore.resource.Resource emfRes = resSet.getResource(URI.createFileURI(
+ storageResource.dir().getAbsolutePath() + "\\" + resultSetID + ".feature"),
+ true);
+ feature = (GetFeatureType) emfRes.getContents().get(0);
+ } catch (Exception t) {
+ transaction.rollback();
+ throw new RuntimeException("Error on retrive feature", t);
+ } finally {
+ transaction.close();
+ }
+ return feature;
+
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java
new file mode 100644
index 00000000000..bdc797dea78
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java
@@ -0,0 +1,23 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import org.geotools.util.Version;
+
+/**
+ *
+ * @author sandr
+ *
+ */
+
+public class ResultTypeKvpParser extends org.geoserver.wfs.kvp.v2_0.ResultTypeKvpParser {
+
+ public ResultTypeKvpParser() {
+ super();
+ setVersion(new Version("2.0.2"));
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index acdcb9c2caa..842c5b9ce9b 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -1,7 +1,6 @@
@@ -18,8 +17,9 @@
-
+
+
When a request using the index result type comes in a
dispatcher callback
@@ -27,11 +27,39 @@
+
+
+
+ The PageResults operation will allow clients to query
+ random positions of an existing result set (stored GetFeature
+ request) that was previously created using the index result type
+
+
+
+
+
+
+
+
+
+
-
-
+
-
+
From a7d31493c25c74ddde4f73ce4b3c4b5154b51c7f Mon Sep 17 00:00:00 2001
From: Nuno Oliveira
Date: Sun, 23 Jul 2017 00:22:24 +0100
Subject: [PATCH 07/15] Add WFS time versioning
---
src/community/nsg-profile/pom.xml | 86 ++++++
.../org/geoserver/nsg/TimeVersioning.java | 58 +++++
.../geoserver/nsg/TimeVersioningCallback.java | 244 ++++++++++++++++++
.../nsg/VersioningFilterAdapter.java | 110 ++++++++
.../nsg/web/WfsVersioningConfig.html | 32 +++
.../nsg/web/WfsVersioningConfig.java | 153 +++++++++++
.../resources/GeoServerApplication.properties | 4 +
.../src/main/resources/applicationContext.xml | 19 ++
.../java/org/geoserver/nsg/TestsUtils.java | 39 +++
.../org/geoserver/nsg/TimeVersioningTest.java | 99 +++++++
.../nsg/web/WfsVersioningConfigTest.java | 106 ++++++++
.../org/geoserver/nsg/versioned.properties | 6 +
.../test/resources/requests/get_request_1.xml | 13 +
.../resources/requests/insert_request_1.xml | 22 ++
.../resources/requests/update_request_1.xml | 18 ++
src/community/pom.xml | 10 +-
src/community/release/ext-nsg-profile.xml | 16 ++
.../geoserver/catalog/FeatureTypeInfo.java | 7 +
.../catalog/impl/FeatureTypeInfoImpl.java | 33 ++-
.../decorators/DecoratingFeatureTypeInfo.java | 9 +
.../decorators/SecuredFeatureTypeInfo.java | 3 +-
src/web/app/pom.xml | 8 +-
.../security/web/user/UserPanel.java | 2 +-
.../java/org/geoserver/wfs/GetFeature.java | 16 ++
.../org/geoserver/wfs/GetFeatureCallback.java | 20 ++
.../org/geoserver/wfs/GetFeatureContext.java | 44 ++++
.../wfs/GetFeatureContextBuilder.java | 54 ++++
.../geoserver/wfs/InsertElementHandler.java | 41 ++-
.../java/org/geoserver/wfs/Transaction.java | 17 ++
.../geoserver/wfs/TransactionCallback.java | 49 ++++
.../org/geoserver/wfs/TransactionContext.java | 49 ++++
.../wfs/TransactionContextBuilder.java | 61 +++++
32 files changed, 1415 insertions(+), 33 deletions(-)
create mode 100644 src/community/nsg-profile/pom.xml
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
create mode 100644 src/community/nsg-profile/src/main/resources/GeoServerApplication.properties
create mode 100644 src/community/nsg-profile/src/main/resources/applicationContext.xml
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
create mode 100644 src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties
create mode 100644 src/community/nsg-profile/src/test/resources/requests/get_request_1.xml
create mode 100644 src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml
create mode 100644 src/community/nsg-profile/src/test/resources/requests/update_request_1.xml
create mode 100644 src/community/release/ext-nsg-profile.xml
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java
create mode 100644 src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
new file mode 100644
index 00000000000..b9e26ef3fbc
--- /dev/null
+++ b/src/community/nsg-profile/pom.xml
@@ -0,0 +1,86 @@
+
+
+ 4.0.0
+
+ org.geoserver
+ community
+ 2.12-SNAPSHOT
+
+ org.geoserver.community
+ gs-nsg-profile
+ jar
+ 2.12-SNAPSHOT
+ NSG Profile
+
+
+
+ org.geoserver
+ gs-main
+ tests
+ test
+
+
+ org.geoserver
+ gs-wfs
+ ${project.version}
+
+
+ org.geoserver.web
+ gs-web-core
+ ${project.version}
+
+
+
+ org.geoserver
+ gs-wfs
+ tests
+ ${project.version}
+
+
+ org.geoserver.web
+ gs-web-core
+ ${project.version}
+ tests
+ test
+
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+ org.springframework
+ spring-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+ javax.servlet
+ javax.servlet-api
+ test
+
+
+
+
+
+ ${basedir}/src/main/java
+
+ **/*.html
+
+
+
+ ${basedir}/src/main/resources
+
+ **/*
+
+
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
new file mode 100644
index 00000000000..ad891d29929
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
@@ -0,0 +1,58 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+
+public final class TimeVersioning {
+
+ public static final String ENABLED_KEY = "TIME_VERSIONING_ENABLED";
+ public static final String ID_PROPERTY_KEY = "TIME_VERSIONING_ID_PROPERTY";
+ public static final String TIME_PROPERTY_KEY = "TIME_VERSIONING_TIME_PROPERTY";
+
+ public static void enable(FeatureTypeInfo featureTypeInfo, String idProperty, String timeProperty) {
+ featureTypeInfo.putParameter(ENABLED_KEY, true);
+ featureTypeInfo.putParameter(ID_PROPERTY_KEY, idProperty);
+ featureTypeInfo.putParameter(TIME_PROPERTY_KEY, timeProperty);
+ }
+
+ public static void disable(FeatureTypeInfo featureTypeInfo) {
+ featureTypeInfo.putParameter(ENABLED_KEY, false);
+ featureTypeInfo.putParameter(ID_PROPERTY_KEY, null);
+ featureTypeInfo.putParameter(TIME_PROPERTY_KEY, null);
+ }
+
+ public static boolean isEnabled(FeatureTypeInfo featureTypeInfo) {
+ return featureTypeInfo.getParameter(ENABLED_KEY, Boolean.class, false);
+ }
+
+ public static String getIdPropertyName(FeatureTypeInfo featureTypeInfo) {
+ String idPropertyName = featureTypeInfo.getParameter(ID_PROPERTY_KEY, String.class, null);
+ if (idPropertyName == null) {
+ throw new RuntimeException("No id property name was provided.");
+ }
+ return idPropertyName;
+ }
+
+ public static String getTimePropertyName(FeatureTypeInfo featureTypeInfo) {
+ String timePropertyName = featureTypeInfo.getParameter(TIME_PROPERTY_KEY, String.class, null);
+ if (timePropertyName == null) {
+ throw new RuntimeException("No time property name was provided.");
+ }
+ return timePropertyName;
+ }
+
+ public static void setEnable(FeatureTypeInfo featureTypeInfo, boolean enable) {
+ featureTypeInfo.putParameter(ENABLED_KEY, enable);
+ }
+
+ public static void setIdAttribute(FeatureTypeInfo featureTypeInfo, String idAttributeName) {
+ featureTypeInfo.putParameter(ID_PROPERTY_KEY, idAttributeName);
+ }
+
+ public static void setTimeAttribute(FeatureTypeInfo featureTypeInfo, String timeAttributeName) {
+ featureTypeInfo.putParameter(TIME_PROPERTY_KEY, timeAttributeName);
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
new file mode 100644
index 00000000000..ee87f12f628
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
@@ -0,0 +1,244 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.Catalog;
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.platform.GeoServerExtensions;
+import org.geoserver.wfs.GetFeatureCallback;
+import org.geoserver.wfs.GetFeatureContext;
+import org.geoserver.wfs.InsertElementHandler;
+import org.geoserver.wfs.TransactionCallback;
+import org.geoserver.wfs.TransactionContext;
+import org.geoserver.wfs.TransactionContextBuilder;
+import org.geoserver.wfs.request.Insert;
+import org.geoserver.wfs.request.RequestObject;
+import org.geoserver.wfs.request.Update;
+import org.geotools.data.DataUtilities;
+import org.geotools.data.FeatureStore;
+import org.geotools.data.Query;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureIterator;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.factory.CommonFactoryFinder;
+import org.geotools.factory.GeoTools;
+import org.geotools.feature.NameImpl;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.util.Converters;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.type.AttributeDescriptor;
+import org.opengis.feature.type.FeatureType;
+import org.opengis.feature.type.Name;
+import org.opengis.filter.Filter;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.sort.SortBy;
+import org.opengis.filter.sort.SortOrder;
+
+import javax.xml.namespace.QName;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+final class TimeVersioningCallback implements GetFeatureCallback, TransactionCallback {
+
+ private static final FilterFactory2 FILTER_FACTORY = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
+
+ private final Catalog catalog;
+
+ TimeVersioningCallback(Catalog catalog) {
+ this.catalog = catalog;
+ }
+
+ @Override
+ public GetFeatureContext beforeQuerying(GetFeatureContext context) {
+ if (!isWfs20(context.getRequest())) {
+ return context;
+ }
+ FeatureTypeInfo featureTypeInfo = context.getFeatureTypeInfo();
+ if (!TimeVersioning.isEnabled(featureTypeInfo)) {
+ // time versioning is not enabled for this feature type or is not a WFS 2.0 request
+ return context;
+ }
+ VersioningFilterAdapter.adapt(featureTypeInfo, context.getQuery().getFilter());
+ SortBy sort = FILTER_FACTORY.sort(TimeVersioning.getTimePropertyName(featureTypeInfo), SortOrder.DESCENDING);
+ SortBy[] sorts = context.getQuery().getSortBy();
+ if (sorts == null) {
+ sorts = new SortBy[]{sort};
+ } else {
+ sorts = Arrays.copyOf(sorts, sorts.length + 1);
+ sorts[sorts.length - 1] = sort;
+ }
+ context.getQuery().setSortBy(sorts);
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeHandlerExecution(TransactionContext context) {
+ if (!isWfs20(context.getRequest())) {
+ return context;
+ }
+ if (context.getElement() instanceof Update) {
+ Insert insert = buildInsertForUpdate(context);
+ InsertElementHandler handler = GeoServerExtensions.bean(InsertElementHandler.class);
+ return new TransactionContextBuilder()
+ .withContext(context)
+ .withElement(insert)
+ .withHandler(handler).build();
+ }
+ if (context.getElement() instanceof Insert) {
+ Insert insert = (Insert) context.getElement();
+ for (Object element : insert.getFeatures()) {
+ if (element instanceof SimpleFeature) {
+ setTimeAttribute((SimpleFeature) element);
+ }
+ }
+ }
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeInsertFeatures(TransactionContext context) {
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeUpdateFeatures(TransactionContext context) {
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeDeleteFeatures(TransactionContext context) {
+ return context;
+ }
+
+ @Override
+ public TransactionContext beforeReplaceFeatures(TransactionContext context) {
+ return context;
+ }
+
+ private void setTimeAttribute(SimpleFeature feature) {
+ FeatureType featureType = feature.getFeatureType();
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(featureType);
+ if (TimeVersioning.isEnabled(featureTypeInfo)) {
+ String timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ AttributeDescriptor attributeDescriptor = feature.getType().getDescriptor(timePropertyName);
+ Object timeValue = Converters.convert(new Date(), attributeDescriptor.getType().getBinding());
+ feature.setAttribute(timePropertyName, timeValue);
+ }
+ }
+
+ private SimpleFeatureCollection getTransactionFeatures(TransactionContext context) {
+ QName typeName = context.getElement().getTypeName();
+ Filter filter = context.getElement().getFilter();
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(new NameImpl(typeName));
+ SimpleFeatureStore store = getTransactionStore(context);
+ try {
+ Query query = new Query();
+ query.setFilter(VersioningFilterAdapter.adapt(featureTypeInfo, filter));
+ SortBy sort = FILTER_FACTORY.sort(TimeVersioning.getTimePropertyName(featureTypeInfo), SortOrder.DESCENDING);
+ query.setSortBy(new SortBy[]{sort});
+ return store.getFeatures(query);
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format(
+ "Error getting features of type '%s'.", typeName), exception);
+ }
+ }
+
+ private Comparator buildFeatureTimeComparator(FeatureTypeInfo featureTypeInfo) {
+ String timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ return (featureA, featureB) -> {
+ Date timeA = Converters.convert(featureA.getAttribute(timePropertyName), Date.class);
+ Date timeB = Converters.convert(featureB.getAttribute(timePropertyName), Date.class);
+ if (timeA == null) {
+ return -1;
+ }
+ return timeA.compareTo(timeB);
+ };
+ }
+
+ private List getOnlyRecentFeatures(SimpleFeatureCollection features, FeatureTypeInfo featureTypeInfo) {
+ String idPropertyName = TimeVersioning.getIdPropertyName(featureTypeInfo);
+ Map> featuresIndexedById = new HashMap<>();
+ SimpleFeatureIterator iterator = features.features();
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ Object id = feature.getAttribute(idPropertyName);
+ List existing = featuresIndexedById.computeIfAbsent(id, key -> new ArrayList<>());
+ existing.add(feature);
+ }
+ Comparator comparator = buildFeatureTimeComparator(featureTypeInfo);
+ List finalFeatures = new ArrayList<>();
+ featuresIndexedById.values().forEach(indexed -> {
+ indexed.sort(comparator);
+ SimpleFeature feature = indexed.get(0);
+ SimpleFeatureBuilder builder = new SimpleFeatureBuilder(feature.getFeatureType());
+ builder.init(feature);
+ finalFeatures.add(builder.buildFeature(null));
+ });
+ return finalFeatures;
+ }
+
+ private Insert buildInsertForUpdate(TransactionContext context) {
+ Update update = (Update) context.getElement();
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(new NameImpl(update.getTypeName()));
+ SimpleFeatureCollection features = getTransactionFeatures(context);
+ List recent = getOnlyRecentFeatures(features, featureTypeInfo);
+ List newFeatures = recent.stream()
+ .map(this::prepareInsertFeature).collect(Collectors.toList());
+ return new UpdateInsert(context.getRequest(), newFeatures);
+ }
+
+ private SimpleFeature prepareInsertFeature(SimpleFeature feature) {
+ SimpleFeatureBuilder builder = new SimpleFeatureBuilder(feature.getFeatureType());
+ builder.init(feature);
+ SimpleFeature versionedFeature = builder.buildFeature(null);
+ setTimeAttribute(versionedFeature);
+ return versionedFeature;
+ }
+
+ private FeatureTypeInfo getFeatureTypeInfo(FeatureType featureType) {
+ Name featureTypeName = featureType.getName();
+ return getFeatureTypeInfo(featureTypeName);
+ }
+
+ private FeatureTypeInfo getFeatureTypeInfo(Name featureTypeName) {
+ FeatureTypeInfo featureTypeInfo = catalog.getFeatureTypeByName(featureTypeName);
+ if (featureTypeInfo == null) {
+ throw new RuntimeException(String.format(
+ "Couldn't find feature type info ''%s.", featureTypeName));
+ }
+ return featureTypeInfo;
+ }
+
+ private SimpleFeatureStore getTransactionStore(TransactionContext context) {
+ QName typeName = context.getElement().getTypeName();
+ FeatureStore store = (FeatureStore) context.getFeatureStores().get(typeName);
+ return DataUtilities.simple(store);
+ }
+
+ private boolean isWfs20(RequestObject request) {
+ return true;
+ }
+
+ private static final class UpdateInsert extends Insert {
+
+ private final List features;
+
+ protected UpdateInsert(RequestObject request, List features) {
+ super(request.getAdaptee());
+ this.features = features;
+ }
+
+ @Override
+ public List getFeatures() {
+ return features;
+ }
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
new file mode 100644
index 00000000000..6a0b1c68222
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
@@ -0,0 +1,110 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geotools.factory.CommonFactoryFinder;
+import org.geotools.factory.GeoTools;
+import org.geotools.filter.visitor.DuplicatingFilterVisitor;
+import org.opengis.filter.Filter;
+import org.opengis.filter.FilterFactory;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.Id;
+import org.opengis.filter.expression.Expression;
+import org.opengis.filter.identity.Identifier;
+import org.opengis.filter.identity.ResourceId;
+import org.opengis.filter.sort.SortOrder;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+final class VersioningFilterAdapter extends DuplicatingFilterVisitor {
+
+ private final String idPropertyName;
+ private final String timePropertyName;
+
+ private VersioningFilterAdapter(FeatureTypeInfo featureTypeInfo) {
+ this.idPropertyName = TimeVersioning.getIdPropertyName(featureTypeInfo);
+ this.timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ }
+
+ @Override
+ public Object visit(Id filter, Object extraData) {
+ FilterFactory filterFactory = getFactory(extraData);
+ Set ids = filter.getIdentifiers();
+ Set finalIds = new HashSet<>();
+ Filter versioningFilter = null;
+ for (Identifier id : ids) {
+ if (id instanceof ResourceId) {
+ Filter newFilter = buildVersioningFilter(filterFactory, (ResourceId) id);
+ versioningFilter = addFilter(filterFactory, versioningFilter, newFilter);
+ } else {
+ finalIds.add(id);
+ }
+ }
+ if (finalIds.isEmpty()) {
+ return versioningFilter;
+ }
+ Filter newIdFilter = getFactory(extraData).id(finalIds);
+ if (versioningFilter != null) {
+ return filterFactory.and(newIdFilter, versioningFilter);
+ }
+ return newIdFilter;
+ }
+
+ private Filter buildVersioningFilter(FilterFactory filterFactory, ResourceId resourceId) {
+ Filter idFilter = buildIdFilter(filterFactory, resourceId.getID());
+ Filter timeFilter = buildTimeFilter(filterFactory, resourceId.getStartTime(), resourceId.getEndTime());
+ if (idFilter != null && timeFilter != null) {
+ return filterFactory.and(idFilter, timeFilter);
+ }
+ if (idFilter != null) {
+ return idFilter;
+ }
+ if (timeFilter != null) {
+ return timeFilter;
+ }
+ return null;
+ }
+
+ private Filter buildIdFilter(FilterFactory factory, String id) {
+ if (id == null) {
+ return null;
+ }
+ return factory.equals(factory.property(idPropertyName), factory.literal(id));
+ }
+
+ private Filter buildTimeFilter(FilterFactory filterFactory, Date start, Date end) {
+ Expression timeProperty = filterFactory.property(timePropertyName);
+ Expression startLiteral = filterFactory.literal(start);
+ Expression endLiteral = filterFactory.literal(end);
+ Filter after = filterFactory.after(timeProperty, startLiteral);
+ Filter before = filterFactory.before(timeProperty, endLiteral);
+ if (start != null && end != null) {
+ return filterFactory.and(after, before);
+ }
+ if (start != null) {
+ return after;
+ }
+ if (end != null) {
+ return before;
+ }
+ return null;
+ }
+
+ private Filter addFilter(FilterFactory filterFactory, Filter versioningFilter, Filter filter) {
+ if (versioningFilter != null) {
+ return filterFactory.and(versioningFilter, filter);
+ }
+ return filter;
+ }
+
+ static Filter adapt(FeatureTypeInfo featureTypeInfo, Filter filter) {
+ String timePropertyName = TimeVersioning.getTimePropertyName(featureTypeInfo);
+ VersioningFilterAdapter adapter = new VersioningFilterAdapter(featureTypeInfo);
+ return (Filter) filter.accept(adapter, null);
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
new file mode 100644
index 00000000000..b67d24989c7
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
new file mode 100644
index 00000000000..8ef574e602c
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
@@ -0,0 +1,153 @@
+/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
+ * (c) 2001 - 2013 OpenPlans
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg.web;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.catalog.LayerInfo;
+import org.geoserver.nsg.TimeVersioning;
+import org.geoserver.web.publish.PublishedConfigurationPanel;
+
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class WfsVersioningConfig extends PublishedConfigurationPanel {
+
+ public WfsVersioningConfig(String id, IModel model) {
+ super(id, model);
+ // get the needed information from the model
+ FeatureTypeInfo featureTypeInfo = getFeatureTypeInfo(model);
+ boolean isVersioningActivated = TimeVersioning.isEnabled(featureTypeInfo);
+ String idAttributeName = isVersioningActivated ? TimeVersioning.getIdPropertyName(featureTypeInfo) : null;
+ String timeAttributeName = isVersioningActivated ? TimeVersioning.getTimePropertyName(featureTypeInfo) : null;
+ List attributesNames = getAttributesNames(featureTypeInfo);
+ List timeAttributesNames = getTimeAttributesNames(featureTypeInfo);
+ // create dropdown choice for the id attribute name
+ DropDownChoice idAttributeChoice = new DropDownChoice<>("idAttributeChoice",
+ new Model<>(idAttributeName), attributesNames);
+ idAttributeChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ String selected = idAttributeChoice.getModel().getObject();
+ TimeVersioning.setIdAttribute(featureTypeInfo, selected);
+ }
+ });
+ idAttributeChoice.setOutputMarkupId(true);
+ idAttributeChoice.setOutputMarkupPlaceholderTag(true);
+ idAttributeChoice.setRequired(true);
+ idAttributeChoice.setVisible(isVersioningActivated);
+ add(idAttributeChoice);
+ // add label for id attribute name dropdown choice
+ Label idAttributeChoiceLabel = new Label("idAttributeChoiceLabel",
+ new StringResourceModel("WfsVersioningConfig.idAttributeChoiceLabel"));
+ idAttributeChoiceLabel.setOutputMarkupId(true);
+ idAttributeChoiceLabel.setOutputMarkupPlaceholderTag(true);
+ idAttributeChoiceLabel.setVisible(isVersioningActivated);
+ add(idAttributeChoiceLabel);
+ // create dropdown choice for the time attribute name
+ DropDownChoice timeAttributeChoice = new DropDownChoice<>("timeAttributeChoice",
+ new Model<>(timeAttributeName), timeAttributesNames);
+ timeAttributeChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ String selected = timeAttributeChoice.getModel().getObject();
+ TimeVersioning.setTimeAttribute(featureTypeInfo, selected);
+ }
+ });
+ timeAttributeChoice.setOutputMarkupId(true);
+ timeAttributeChoice.setOutputMarkupPlaceholderTag(true);
+ timeAttributeChoice.setRequired(true);
+ timeAttributeChoice.setVisible(isVersioningActivated);
+ add(timeAttributeChoice);
+ // add label for id attribute name dropdown choice
+ Label timeAttributeChoiceLabel = new Label("timeAttributeChoiceLabel",
+ new StringResourceModel("WfsVersioningConfig.timeAttributeChoiceLabel"));
+ timeAttributeChoiceLabel.setOutputMarkupId(true);
+ timeAttributeChoiceLabel.setOutputMarkupPlaceholderTag(true);
+ timeAttributeChoiceLabel.setVisible(isVersioningActivated);
+ add(timeAttributeChoiceLabel);
+ // checkbox for activating versioning
+ CheckBox versioningActivateCheckBox = new AjaxCheckBox("versioningActivateCheckBox",
+ new Model<>(isVersioningActivated)) {
+ @Override
+ protected void onUpdate(AjaxRequestTarget target) {
+ boolean checked = getModelObject();
+ if (checked) {
+ // activate versioning attributes selection
+ idAttributeChoice.setVisible(true);
+ idAttributeChoiceLabel.setVisible(true);
+ timeAttributeChoice.setVisible(true);
+ timeAttributeChoiceLabel.setVisible(true);
+ // enable time versioning
+ TimeVersioning.setEnable(featureTypeInfo, true);
+ } else {
+ // deactivate versioning attributes selection
+ idAttributeChoice.setVisible(false);
+ idAttributeChoiceLabel.setVisible(false);
+ timeAttributeChoice.setVisible(false);
+ timeAttributeChoiceLabel.setVisible(false);
+ // disable time versioning
+ TimeVersioning.setEnable(featureTypeInfo, false);
+ }
+ // update the dropdown choices and labels
+ target.add(idAttributeChoice);
+ target.add(idAttributeChoiceLabel);
+ target.add(timeAttributeChoice);
+ target.add(timeAttributeChoiceLabel);
+ }
+ };
+ if (isVersioningActivated) {
+ versioningActivateCheckBox.setModelObject(true);
+ }
+ versioningActivateCheckBox.setEnabled(!timeAttributesNames.isEmpty());
+ add(versioningActivateCheckBox);
+ // add versioning activating checkbox label
+ Label versioningActivateCheckBoxLabel = new Label("versioningActivateCheckBoxLabel",
+ new StringResourceModel("WfsVersioningConfig.versioningActivateCheckBoxLabel"));
+ add(versioningActivateCheckBoxLabel);
+ }
+
+ private List getAttributesNames(FeatureTypeInfo featureTypeInfo) {
+ try {
+ return featureTypeInfo.getFeatureType().getDescriptors().stream()
+ .map(attribute -> attribute.getName().getLocalPart())
+ .collect(Collectors.toList());
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format(
+ "Error processing attributes of feature type '%s'.",
+ featureTypeInfo.getName()), exception);
+ }
+ }
+
+ private List getTimeAttributesNames(FeatureTypeInfo featureTypeInfo) {
+ try {
+ return featureTypeInfo.getFeatureType().getDescriptors().stream().filter(attribute -> {
+ Class binding = attribute.getType().getBinding();
+ return Long.class.isAssignableFrom(binding)
+ || Date.class.isAssignableFrom(binding)
+ || Timestamp.class.isAssignableFrom(binding);
+ }).map(attribute -> attribute.getName().getLocalPart()).collect(Collectors.toList());
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format(
+ "Error processing attributes of feature type '%s'.",
+ featureTypeInfo.getName()), exception);
+ }
+ }
+
+ private FeatureTypeInfo getFeatureTypeInfo(IModel model) {
+ return (FeatureTypeInfo) model.getObject().getResource();
+ }
+}
diff --git a/src/community/nsg-profile/src/main/resources/GeoServerApplication.properties b/src/community/nsg-profile/src/main/resources/GeoServerApplication.properties
new file mode 100644
index 00000000000..e8f4ddacbf6
--- /dev/null
+++ b/src/community/nsg-profile/src/main/resources/GeoServerApplication.properties
@@ -0,0 +1,4 @@
+WfsVersioningConfig.wfsVersioning=WFS Versioning
+WfsVersioningConfig.versioningActivateCheckBoxLabel=Activate Versioning
+WfsVersioningConfig.idAttributeChoiceLabel=Id Attribute
+WfsVersioningConfig.timeAttributeChoiceLabel=Time Attribute
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
new file mode 100644
index 00000000000..a33c1190b39
--- /dev/null
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+ org.geoserver.catalog.FeatureTypeInfo
+
+
+
+
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
new file mode 100644
index 00000000000..653ac8a8e4b
--- /dev/null
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
@@ -0,0 +1,39 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.geoserver.catalog.Catalog;
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.util.IOUtils;
+
+import java.io.InputStream;
+
+import static org.geoserver.nsg.TimeVersioning.disable;
+import static org.geoserver.nsg.TimeVersioning.enable;
+
+final class TestsUtils {
+
+ private TestsUtils() {
+ }
+
+ static String readResource(String resourceName) {
+ try (InputStream input = TestsUtils.class.getResourceAsStream(resourceName)) {
+ return IOUtils.toString(input);
+ } catch (Exception exception) {
+ throw new RuntimeException(String.format("Error reading resource '%s'.", resourceName));
+ }
+ }
+
+ static void updateFeatureTypeTimeVersioning(Catalog catalog, String featureTypeName,
+ boolean enabled, String idProperty, String timeProperty) {
+ FeatureTypeInfo featureType = catalog.getFeatureTypeByName(featureTypeName);
+ if (enabled) {
+ enable(featureType, idProperty, timeProperty);
+ } else {
+ disable(featureType);
+ }
+ catalog.save(featureType);
+ }
+}
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
new file mode 100644
index 00000000000..758d1fa3370
--- /dev/null
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
@@ -0,0 +1,99 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg;
+
+import org.custommonkey.xmlunit.SimpleNamespaceContext;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.custommonkey.xmlunit.XpathEngine;
+import org.geoserver.data.test.MockData;
+import org.geoserver.data.test.SystemTestData;
+import org.geoserver.test.GeoServerSystemTestSupport;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.referencing.crs.DefaultGeographicCRS;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+
+import javax.xml.namespace.QName;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class TimeVersioningTest extends GeoServerSystemTestSupport {
+
+ private XpathEngine WFS20_XPATH_ENGINE;
+
+ @Override
+ protected void onSetUp(SystemTestData testData) throws Exception {
+ super.setUpTestData(testData);
+ // create bounding box definitions
+ ReferencedEnvelope envelope = new ReferencedEnvelope(-5, -5, 5, 5, DefaultGeographicCRS.WGS84);
+ Map properties = new HashMap<>();
+ properties.put(SystemTestData.LayerProperty.LATLON_ENVELOPE, envelope);
+ properties.put(SystemTestData.LayerProperty.ENVELOPE, envelope);
+ properties.put(SystemTestData.LayerProperty.SRS, 4326);
+ // create versioned layer
+ QName versionedLayerName = new QName(MockData.DEFAULT_URI, "versioned", MockData.DEFAULT_PREFIX);
+ testData.addVectorLayer(versionedLayerName, properties, "versioned.properties", getClass(), getCatalog());
+ // instantiate xpath engine
+ buildXpathEngine(
+ "wfs", "http://www.opengis.net/wfs/2.0",
+ "gml", "http://www.opengis.net/gml/3.2");
+ }
+
+ @Before
+ public void beforeTest() {
+ // activate versioning for versioned layer
+ TestsUtils.updateFeatureTypeTimeVersioning(getCatalog(), "gs:versioned", true, "ID", "TIME");
+ }
+
+ @Test
+ public void testGetFeatureVersioned() throws Exception {
+ Document result = postAsDOM("wfs", TestsUtils.readResource("/requests/get_request_1.xml"));
+ System.out.println("aa");
+ }
+
+ @Test
+ public void testInsertVersionedFeature() throws Exception {
+ Document result = postAsDOM("wfs", TestsUtils.readResource("/requests/insert_request_1.xml"));
+ System.out.println("aa");
+ }
+
+ @Test
+ public void testUpdateVersionedFeature() throws Exception {
+ Document result = postAsDOM("wfs", TestsUtils.readResource("/requests/update_request_1.xml"));
+ System.out.println("aa");
+ }
+
+ /**
+ * Helper method that builds a xpath engine using some predefined
+ * namespaces and all the catalog namespaces. The provided namespaces
+ * will be added overriding any existing namespace.
+ */
+ private XpathEngine buildXpathEngine(String... namespaces) {
+ // build xpath engine
+ XpathEngine xpathEngine = XMLUnit.newXpathEngine();
+ Map finalNamespaces = new HashMap<>();
+ // add common namespaces
+ finalNamespaces.put("ows", "http://www.opengis.net/ows");
+ finalNamespaces.put("ogc", "http://www.opengis.net/ogc");
+ finalNamespaces.put("xs", "http://www.w3.org/2001/XMLSchema");
+ finalNamespaces.put("xsd", "http://www.w3.org/2001/XMLSchema");
+ finalNamespaces.put("xlink", "http://www.w3.org/1999/xlink");
+ finalNamespaces.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ // add al catalog namespaces
+ getCatalog().getNamespaces().forEach(namespace ->
+ finalNamespaces.put(namespace.getPrefix(), namespace.getURI()));
+ // add provided namespaces
+ if (namespaces.length % 2 != 0) {
+ throw new RuntimeException("Invalid number of namespaces provided.");
+ }
+ for (int i = 0; i < namespaces.length; i += 2) {
+ finalNamespaces.put(namespaces[i], namespaces[i + 1]);
+ }
+ // add namespaces to the xpath engine
+ xpathEngine.setNamespaceContext(new SimpleNamespaceContext(finalNamespaces));
+ return xpathEngine;
+ }
+}
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
new file mode 100644
index 00000000000..efe29b00e6a
--- /dev/null
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
@@ -0,0 +1,106 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.nsg.web;
+
+import org.apache.wicket.markup.html.form.CheckBox;
+import org.apache.wicket.util.tester.FormTester;
+import org.geoserver.web.GeoServerWicketTestSupport;
+import org.geoserver.web.publish.PublishedConfigurationPage;
+import org.geoserver.wfs.GMLInfo;
+import org.geoserver.wfs.WFSInfo;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+public class WfsVersioningConfigTest extends GeoServerWicketTestSupport {
+
+ @Test
+ public void testPageStart() throws Exception {
+ WFSInfo wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+
+ login();
+ tester.startPage(PublishedConfigurationPage.class);
+ }
+
+ /*@Test
+ public void testChangesToValues() throws Exception {
+ String testValue1 = "100", testValue2 = "0";
+ WFSInfo wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+ login();
+ tester.startPage(WFSAdminPage.class);
+ FormTester ft = tester.newFormTester("form");
+ ft.setValue("maxNumberOfFeaturesForPreview", (String)testValue1);
+ ft.submit("submit");
+ wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+ assertEquals("testValue1 = 100", 100, (int)wfs.getMaxNumberOfFeaturesForPreview());
+ tester.startPage(WFSAdminPage.class);
+ ft = tester.newFormTester("form");
+ ft.setValue("maxNumberOfFeaturesForPreview", (String)testValue2);
+ ft.submit("submit");
+ wfs = getGeoServerApplication().getGeoServer().getService(WFSInfo.class);
+ assertEquals("testValue2 = 0", 0, (int)wfs.getMaxNumberOfFeaturesForPreview());
+ }
+
+ @Test
+ public void testGML32ForceMimeType() throws Exception {
+ // make sure GML MIME type overriding is disabled
+ WFSInfo info = getGeoServer().getService(WFSInfo.class);
+ GMLInfo gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ gmlInfo.setMimeTypeToForce(null);
+ getGeoServer().save(info);
+ // login with administrator privileges
+ login();
+ // start WFS service administration page
+ tester.startPage(new WFSAdminPage());
+ // check that GML MIME type overriding is disabled
+ tester.assertComponent("form:gml32:forceGmlMimeType", CheckBox.class);
+ CheckBox checkbox = (CheckBox) tester.getComponentFromLastRenderedPage("form:gml32:forceGmlMimeType");
+ assertThat(checkbox.getModelObject(), is(false));
+ // MIME type drop down choice should be invisible
+ tester.assertInvisible("form:gml32:mimeTypeToForce");
+ // activate MIME type overriding by clicking in the checkbox
+ FormTester formTester = tester.newFormTester("form");
+ formTester.setValue("gml32:forceGmlMimeType", true);
+ tester.executeAjaxEvent("form:gml32:forceGmlMimeType", "click");
+ formTester = tester.newFormTester("form");
+ formTester.submit("submit");
+ // GML MIME typing overriding should be activated now
+ tester.startPage(new WFSAdminPage());
+ assertThat(checkbox.getModelObject(), is(true));
+ tester.assertVisible("form:gml32:mimeTypeToForce");
+ // WFS global service configuration should have been updated too
+ info = getGeoServer().getService(WFSInfo.class);
+ gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ assertThat(gmlInfo.getMimeTypeToForce().isPresent(), is(true));
+ // select text / xml as MIME type to force
+ formTester = tester.newFormTester("form");
+ formTester.select("gml32:mimeTypeToForce", 2);
+ tester.executeAjaxEvent("form:gml32:mimeTypeToForce", "change");
+ formTester = tester.newFormTester("form");
+ formTester.submit("submit");
+ // WFS global service configuration should be forcing text / xml
+ info = getGeoServer().getService(WFSInfo.class);
+ gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ assertThat(gmlInfo.getMimeTypeToForce().isPresent(), is(true));
+ assertThat(gmlInfo.getMimeTypeToForce().get(), is("text/xml"));
+ // deactivate GML MIME type overriding by clicking in the checkbox
+ tester.startPage(new WFSAdminPage());
+ formTester = tester.newFormTester("form");
+ formTester.setValue("gml32:forceGmlMimeType", false);
+ tester.executeAjaxEvent("form:gml32:forceGmlMimeType", "click");
+ formTester = tester.newFormTester("form");
+ formTester.submit("submit");
+ // GML MIME type overriding should be deactivated now
+ tester.startPage(new WFSAdminPage());
+ assertThat(checkbox.getModelObject(), is(true));
+ tester.assertInvisible("form:gml32:mimeTypeToForce");
+ // WFS global service configuration should have been updated too
+ info = getGeoServer().getService(WFSInfo.class);
+ gmlInfo = info.getGML().get(WFSInfo.Version.V_20);
+ assertThat(gmlInfo.getMimeTypeToForce().isPresent(), is(false));
+ }*/
+}
diff --git a/src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties b/src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties
new file mode 100644
index 00000000000..ad32708279a
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/org/geoserver/nsg/versioned.properties
@@ -0,0 +1,6 @@
+_=ID:String,NAME:String,TIME:java.sql.Timestamp,GEOMETRY:Geometry:srid=4326
+v.1=1|feature1|2017-06-25 14:30:00.0|POINT(-1 1)
+v.2=1|feature1|2017-07-25 14:30:00.0|POINT(-1 -1)
+v.3=1|feature1|2017-06-25 14:35:00.0|POINT(1 -1)
+v.4=2|feature2|2017-04-10 13:30:00.0|POINT(-2 2)
+v.5=2|feature2|2017-08-10 17:10:00.0|POINT(2 -2)
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/test/resources/requests/get_request_1.xml b/src/community/nsg-profile/src/test/resources/requests/get_request_1.xml
new file mode 100644
index 00000000000..bd69e4fdbeb
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/requests/get_request_1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml b/src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml
new file mode 100644
index 00000000000..c50429c8a86
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/requests/insert_request_1.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ 1
+ feature1
+ 2018-01-25T13:30:00Z
+
+
+ 1.5 -1.5
+
+
+
+
+
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/test/resources/requests/update_request_1.xml b/src/community/nsg-profile/src/test/resources/requests/update_request_1.xml
new file mode 100644
index 00000000000..34ed22793ac
--- /dev/null
+++ b/src/community/nsg-profile/src/test/resources/requests/update_request_1.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ NAME
+ feature2_updated
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/community/pom.xml b/src/community/pom.xml
index 8637f77d6ea..327013956df 100644
--- a/src/community/pom.xml
+++ b/src/community/pom.xml
@@ -62,7 +62,7 @@
release/ext-gwc-distributed.xml
release/ext-geofence-server.xml
-
+ release/ext-gwc-s3.xml
release/ext-gdal-wcs.xml
release/ext-gdal-wps.xml
release/ext-wps-jdbc.xml
@@ -225,7 +225,7 @@
gwc-distributed
jdbcstore
gdal
-
+ gwc-s3
ncwms
web-resource
@@ -242,7 +242,7 @@
ows-simulate
jdbc-metrics
oseo
- s3-geotiff
+ nsg-profile
@@ -533,9 +533,9 @@
- s3-geotiff
+ nsg-profile
- s3-geotiff
+ nsg-profile
diff --git a/src/community/release/ext-nsg-profile.xml b/src/community/release/ext-nsg-profile.xml
new file mode 100644
index 00000000000..11f21b27b56
--- /dev/null
+++ b/src/community/release/ext-nsg-profile.xml
@@ -0,0 +1,16 @@
+
+ nsg-profile
+
+ zip
+
+ false
+
+
+ release/target/dependency
+
+
+ gs-nsg-profile*.jar
+
+
+
+
\ No newline at end of file
diff --git a/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java b/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java
index f780f80b9f5..3a2b0724b65 100644
--- a/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java
+++ b/src/main/src/main/java/org/geoserver/catalog/FeatureTypeInfo.java
@@ -193,4 +193,11 @@ public interface FeatureTypeInfo extends ResourceInfo {
void setCircularArcPresent(boolean arcsPresent);
+ default T getParameter(String parameterName, Class expectType, T fallback) {
+ return null;
+ }
+
+ default void putParameter(String parameterName, Object parameterValue) {
+ // nothing to do
+ }
}
diff --git a/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java b/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java
index 8fc00f86217..b1a5cd481c2 100644
--- a/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java
+++ b/src/main/src/main/java/org/geoserver/catalog/impl/FeatureTypeInfoImpl.java
@@ -5,10 +5,6 @@
*/
package org.geoserver.catalog.impl;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
import org.geoserver.catalog.AttributeTypeInfo;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogVisitor;
@@ -20,11 +16,18 @@
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.measure.Measure;
+import org.geotools.util.Converters;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
import org.opengis.util.ProgressListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
@SuppressWarnings("serial")
public class FeatureTypeInfoImpl extends ResourceInfoImpl implements
FeatureTypeInfo {
@@ -42,7 +45,9 @@ public class FeatureTypeInfoImpl extends ResourceInfoImpl implements
boolean overridingServiceSRS;
boolean skipNumberMatched = false;
boolean circularArcPresent;
-
+
+ protected Map parameters = new HashMap<>();
+
public boolean isCircularArcPresent() {
return circularArcPresent;
}
@@ -236,5 +241,21 @@ public void setCqlFilter(String cqlFilter) {
this.cqlFilter = cqlFilter;
this.filter = null;
}
-
+
+ @Override
+ public T getParameter(String parameterName, Class expectType, T fallback) {
+ if (parameters == null || parameters.isEmpty()) {
+ return fallback;
+ }
+ Object value = parameters.get(parameterName);
+ return value == null ? fallback : Converters.convert(value, expectType);
+ }
+
+ @Override
+ public void putParameter(String parameterName, Object parameterValue) {
+ if (parameters == null) {
+ parameters = new HashMap<>();
+ }
+ parameters.put(parameterName, parameterValue);
+ }
}
diff --git a/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java b/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java
index 4f7da4f075c..40b50d647cc 100644
--- a/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java
+++ b/src/main/src/main/java/org/geoserver/security/decorators/DecoratingFeatureTypeInfo.java
@@ -329,4 +329,13 @@ public void setCqlFilter(String cqlFilter) {
delegate.setCqlFilter(cqlFilter);
}
+ @Override
+ public T getParameter(String parameterName, Class expectType, T fallback) {
+ return delegate.getParameter(parameterName, expectType, fallback);
+ }
+
+ @Override
+ public void putParameter(String parameterName, Object parameterValue) {
+ delegate.putParameter(parameterName, parameterValue);
+ }
}
diff --git a/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java b/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java
index bcc7c5ed884..9198b365cc3 100644
--- a/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java
+++ b/src/main/src/main/java/org/geoserver/security/decorators/SecuredFeatureTypeInfo.java
@@ -111,5 +111,6 @@ public FeatureSource getFeatureSource(ProgressListener listener, Hints hints)
public DataStoreInfo getStore() {
return (DataStoreInfo) SecuredObjects.secure(delegate.getStore(), policy);
}
-
+
+
}
diff --git a/src/web/app/pom.xml b/src/web/app/pom.xml
index aee4265be9e..b92ca71d249 100644
--- a/src/web/app/pom.xml
+++ b/src/web/app/pom.xml
@@ -141,10 +141,6 @@
org.geotools.jdbc
gt-jdbc-postgis
-
- org.geotools
- gt-geopkg
-
org.geotools
gt-wfs-ng
@@ -1527,11 +1523,11 @@
- s3-geotiff
+ nsg-profile
org.geoserver.community
- gs-s3-geotiff
+ gs-nsg-profile
${project.version}
diff --git a/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java b/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java
index c10f9520432..fd5cb632beb 100644
--- a/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java
+++ b/src/web/security/core/src/main/java/org/geoserver/security/web/user/UserPanel.java
@@ -105,7 +105,7 @@ public void onClick() {
});
//("addNew", NewUserPage.class));
- //add.setParameter(AbstractSecurityPage.ServiceNameKey, serviceName);
+ //add.putParameter(AbstractSecurityPage.ServiceNameKey, serviceName);
add.setVisible(canCreateStore);
// the removal button
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java
index 3e66b5a92ca..151444e61d0 100644
--- a/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeature.java
@@ -34,6 +34,7 @@
import org.geoserver.ows.Request;
import org.geoserver.ows.URLMangler.URLType;
import org.geoserver.ows.util.KvpMap;
+import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.request.GetFeatureRequest;
import org.geoserver.wfs.request.Lock;
@@ -506,6 +507,21 @@ public FeatureCollectionResponse run(GetFeatureRequest request)
queryMaxFeatures, source, request, allPropNames.get(0), viewParam,
joins, primaryTypeName, primaryAlias);
+ // extension point
+ GetFeatureContext context = new GetFeatureContextBuilder()
+ .withFeatureSource(source)
+ .withFeatureTypeInfo(meta)
+ .withQuery(gtQuery)
+ .withRequest(request).build();
+ List callbacks = GeoServerExtensions.extensions(GetFeatureCallback.class);
+ for (GetFeatureCallback callback : callbacks) {
+ context = callback.beforeQuerying(context);
+ }
+ source = context.getFeatureSource();
+ meta = context.getFeatureTypeInfo();
+ gtQuery = context.getQuery();
+ request = context.getRequest();
+
LOGGER.fine("Query is " + query + "\n To gt2: " + gtQuery);
FeatureCollection extends FeatureType, ? extends Feature> features = getFeatures(request, source, gtQuery);
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java
new file mode 100644
index 00000000000..c0b5c8ed58f
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureCallback.java
@@ -0,0 +1,20 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.platform.ExtensionPriority;
+
+public interface GetFeatureCallback extends ExtensionPriority {
+
+ default GetFeatureContext beforeQuerying(GetFeatureContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ @Override
+ default int getPriority() {
+ return ExtensionPriority.LOWEST;
+ }
+}
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java
new file mode 100644
index 00000000000..3f06b7c4325
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContext.java
@@ -0,0 +1,44 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.wfs.request.GetFeatureRequest;
+import org.geotools.data.FeatureSource;
+import org.geotools.data.Query;
+import org.opengis.feature.Feature;
+import org.opengis.feature.type.FeatureType;
+
+public final class GetFeatureContext {
+
+ private final GetFeatureRequest request;
+ private final FeatureSource extends FeatureType, ? extends Feature> featureSource;
+ private final Query query;
+ private final FeatureTypeInfo featureTypeInfo;
+
+ GetFeatureContext(GetFeatureRequest request, FeatureSource extends FeatureType, ? extends Feature> featureSource,
+ Query query, FeatureTypeInfo featureTypeInfo) {
+ this.request = request;
+ this.featureSource = featureSource;
+ this.query = query;
+ this.featureTypeInfo = featureTypeInfo;
+ }
+
+ public GetFeatureRequest getRequest() {
+ return request;
+ }
+
+ public FeatureSource extends FeatureType, ? extends Feature> getFeatureSource() {
+ return featureSource;
+ }
+
+ public Query getQuery() {
+ return query;
+ }
+
+ public FeatureTypeInfo getFeatureTypeInfo() {
+ return featureTypeInfo;
+ }
+}
\ No newline at end of file
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java
new file mode 100644
index 00000000000..0c19934cf6c
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/GetFeatureContextBuilder.java
@@ -0,0 +1,54 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.catalog.FeatureTypeInfo;
+import org.geoserver.wfs.request.GetFeatureRequest;
+import org.geotools.data.FeatureSource;
+import org.geotools.data.Query;
+import org.opengis.feature.Feature;
+import org.opengis.feature.type.FeatureType;
+
+public final class GetFeatureContextBuilder {
+
+ private GetFeatureRequest request;
+ private FeatureSource extends FeatureType, ? extends Feature> featureSource;
+ private Query query;
+ private FeatureTypeInfo featureTypeInfo;
+
+ public GetFeatureContextBuilder() {
+ }
+
+ public GetFeatureContextBuilder withRequest(GetFeatureRequest request) {
+ this.request = request;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withFeatureSource(FeatureSource extends FeatureType, ? extends Feature> featureSource) {
+ this.featureSource = featureSource;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withQuery(Query query) {
+ this.query = query;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withFeatureTypeInfo(FeatureTypeInfo featureTypeInfo) {
+ this.featureTypeInfo = featureTypeInfo;
+ return this;
+ }
+
+ public GetFeatureContextBuilder withContext(GetFeatureContext context) {
+ return withRequest(context.getRequest())
+ .withFeatureSource(context.getFeatureSource())
+ .withQuery(context.getQuery())
+ .withFeatureTypeInfo(context.getFeatureTypeInfo());
+ }
+
+ public GetFeatureContext build() {
+ return new GetFeatureContext(request, featureSource, query, featureTypeInfo);
+ }
+}
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java b/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java
index 49ae7fa67be..e277dcdb16d 100644
--- a/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java
+++ b/src/wfs/src/main/java/org/geoserver/wfs/InsertElementHandler.java
@@ -5,21 +5,11 @@
*/
package org.geoserver.wfs;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Logger;
-
-import javax.xml.namespace.QName;
-
+import com.vividsolutions.jts.geom.Geometry;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.feature.ReprojectingFeatureCollection;
+import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wfs.request.Insert;
import org.geoserver.wfs.request.TransactionElement;
import org.geoserver.wfs.request.TransactionRequest;
@@ -40,7 +30,16 @@
import org.opengis.filter.identity.FeatureId;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import com.vividsolutions.jts.geom.Geometry;
+import javax.xml.namespace.QName;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
/**
@@ -71,7 +70,21 @@ public void checkValidity(TransactionElement element, Map callbacks = GeoServerExtensions.extensions(TransactionCallback.class);
+
try {
for (Iterator it = elementHandlers.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
TransactionElement element = (TransactionElement) entry.getKey();
TransactionElementHandler handler = (TransactionElementHandler) entry.getValue();
+ TransactionContext context = new TransactionContextBuilder()
+ .withElement(element)
+ .withRequest(request)
+ .withFeatureStores(stores)
+ .withResponse(result)
+ .withHandler(handler).build();
+ context = TransactionCallback.executeCallbacks(
+ context, callbacks, TransactionCallback::beforeHandlerExecution);
+
+ element = context.getElement();
+ request = context.getRequest();
+ stores = context.getFeatureStores();
+ result = context.getResponse();
+ handler = context.getHandler();
+
handler.execute(element, request, stores, result, multiplexer);
}
} catch (WFSTransactionException e) {
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java b/src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java
new file mode 100644
index 00000000000..6193ab9dca4
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/TransactionCallback.java
@@ -0,0 +1,49 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import java.util.List;
+
+public interface TransactionCallback {
+
+ default TransactionContext beforeHandlerExecution(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeInsertFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeUpdateFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeDeleteFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ default TransactionContext beforeReplaceFeatures(TransactionContext context) {
+ // by default nothing is done
+ return context;
+ }
+
+ @FunctionalInterface
+ interface Executor {
+ TransactionContext apply(TransactionCallback callback, TransactionContext context);
+ }
+
+ static TransactionContext executeCallbacks(TransactionContext context,
+ List callbacks,
+ Executor executor) {
+ for (TransactionCallback callback : callbacks) {
+ context = executor.apply(callback, context);
+ }
+ return context;
+ }
+}
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java
new file mode 100644
index 00000000000..469fcc1a558
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContext.java
@@ -0,0 +1,49 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.wfs.request.TransactionElement;
+import org.geoserver.wfs.request.TransactionRequest;
+import org.geoserver.wfs.request.TransactionResponse;
+
+import java.util.Map;
+
+public final class TransactionContext {
+
+ private final TransactionElement element;
+ private final TransactionRequest request;
+ private final Map, ?> featureStores;
+ private final TransactionResponse response;
+ private final TransactionElementHandler handler;
+
+ TransactionContext(TransactionElement element, TransactionRequest request, Map, ?> featureStores,
+ TransactionResponse response, TransactionElementHandler handler) {
+ this.element = element;
+ this.request = request;
+ this.featureStores = featureStores;
+ this.response = response;
+ this.handler = handler;
+ }
+
+ public TransactionElement getElement() {
+ return element;
+ }
+
+ public TransactionRequest getRequest() {
+ return request;
+ }
+
+ public Map, ?> getFeatureStores() {
+ return featureStores;
+ }
+
+ public TransactionResponse getResponse() {
+ return response;
+ }
+
+ public TransactionElementHandler getHandler() {
+ return handler;
+ }
+}
\ No newline at end of file
diff --git a/src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java
new file mode 100644
index 00000000000..a1a2f018c96
--- /dev/null
+++ b/src/wfs/src/main/java/org/geoserver/wfs/TransactionContextBuilder.java
@@ -0,0 +1,61 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+package org.geoserver.wfs;
+
+import org.geoserver.wfs.request.TransactionElement;
+import org.geoserver.wfs.request.TransactionRequest;
+import org.geoserver.wfs.request.TransactionResponse;
+
+import java.util.Map;
+
+public final class TransactionContextBuilder {
+
+ private TransactionElement element;
+ private TransactionRequest request;
+ private Map, ?> featureStores;
+ private TransactionResponse response;
+ private TransactionElementHandler handler;
+
+ public TransactionContextBuilder() {
+ }
+
+ public TransactionContextBuilder withElement(TransactionElement element) {
+ this.element = element;
+ return this;
+ }
+
+ public TransactionContextBuilder withRequest(TransactionRequest request) {
+ this.request = request;
+ return this;
+ }
+
+ public TransactionContextBuilder withFeatureStores(Map, ?> featureStores) {
+ this.featureStores = featureStores;
+ return this;
+ }
+
+ public TransactionContextBuilder withResponse(TransactionResponse response) {
+ this.response = response;
+ return this;
+ }
+
+ public TransactionContextBuilder withHandler(TransactionElementHandler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public TransactionContextBuilder withContext(TransactionContext context) {
+ this.element = context.getElement();
+ this.featureStores = context.getFeatureStores();
+ this.handler = context.getHandler();
+ this.request = context.getRequest();
+ this.response = context.getResponse();
+ return this;
+ }
+
+ public TransactionContext build() {
+ return new TransactionContext(element, request, featureStores, response, handler);
+ }
+}
From 39427d263d1a5263f6ff7d260980c56002ff4056 Mon Sep 17 00:00:00 2001
From: Nuno Oliveira
Date: Thu, 3 Aug 2017 13:33:23 +0100
Subject: [PATCH 08/15] Add documentation index and reorganise packages
---
doc/en/user/source/community/index.rst | 3 +--
doc/en/user/source/community/nsg-profile/index.rst | 12 ++++++++++++
.../nsg/{ => versioning}/TimeVersioning.java | 2 +-
.../nsg/{ => versioning}/TimeVersioningCallback.java | 2 +-
.../{ => versioning}/VersioningFilterAdapter.java | 6 +-----
.../{ => versioning}/web/WfsVersioningConfig.html | 0
.../{ => versioning}/web/WfsVersioningConfig.java | 4 ++--
.../src/main/resources/applicationContext.xml | 2 +-
.../src/test/java/org/geoserver/nsg/TestsUtils.java | 12 ++++++------
.../nsg/{ => versioning}/TimeVersioningTest.java | 3 ++-
.../web/WfsVersioningConfigTest.java | 5 +----
11 files changed, 28 insertions(+), 23 deletions(-)
create mode 100644 doc/en/user/source/community/nsg-profile/index.rst
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/TimeVersioning.java (98%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/TimeVersioningCallback.java (99%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/VersioningFilterAdapter.java (95%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/web/WfsVersioningConfig.html (100%)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/{ => versioning}/web/WfsVersioningConfig.java (96%)
rename src/community/nsg-profile/src/test/java/org/geoserver/nsg/{ => versioning}/TimeVersioningTest.java (98%)
rename src/community/nsg-profile/src/test/java/org/geoserver/nsg/{ => versioning}/web/WfsVersioningConfigTest.java (96%)
diff --git a/doc/en/user/source/community/index.rst b/doc/en/user/source/community/index.rst
index 10430c8850c..9f9b639c386 100644
--- a/doc/en/user/source/community/index.rst
+++ b/doc/en/user/source/community/index.rst
@@ -45,5 +45,4 @@ officially part of the GeoServer releases. They are however built along with the
onelogin/index
wmts-multidimensional/index
notification/index
- opensearch-eo/index
- s3-geotiff/index
+ nsg-profile/index
diff --git a/doc/en/user/source/community/nsg-profile/index.rst b/doc/en/user/source/community/nsg-profile/index.rst
new file mode 100644
index 00000000000..02490d6cbc5
--- /dev/null
+++ b/doc/en/user/source/community/nsg-profile/index.rst
@@ -0,0 +1,12 @@
+.. _community_nsg_profile:
+
+NSG Profile
+===========
+
+
+Index Result Type
+-----------------
+
+
+PageResults Operation
+---------------------
\ No newline at end of file
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioning.java
similarity index 98%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioning.java
index ad891d29929..6abc73e47a4 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioning.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioning.java
@@ -2,7 +2,7 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.geoserver.catalog.FeatureTypeInfo;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioningCallback.java
similarity index 99%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioningCallback.java
index ee87f12f628..8b45559076a 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/TimeVersioningCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/TimeVersioningCallback.java
@@ -2,7 +2,7 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/VersioningFilterAdapter.java
similarity index 95%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/VersioningFilterAdapter.java
index 6a0b1c68222..42be444be5d 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/VersioningFilterAdapter.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/VersioningFilterAdapter.java
@@ -2,20 +2,16 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.geoserver.catalog.FeatureTypeInfo;
-import org.geotools.factory.CommonFactoryFinder;
-import org.geotools.factory.GeoTools;
import org.geotools.filter.visitor.DuplicatingFilterVisitor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
-import org.opengis.filter.FilterFactory2;
import org.opengis.filter.Id;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.identity.Identifier;
import org.opengis.filter.identity.ResourceId;
-import org.opengis.filter.sort.SortOrder;
import java.util.Date;
import java.util.HashSet;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.html
similarity index 100%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.html
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.html
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.java
similarity index 96%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.java
index 8ef574e602c..f575a2c96ec 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/web/WfsVersioningConfig.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/versioning/web/WfsVersioningConfig.java
@@ -3,7 +3,7 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg.web;
+package org.geoserver.nsg.versioning.web;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
@@ -16,7 +16,7 @@
import org.apache.wicket.model.StringResourceModel;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
-import org.geoserver.nsg.TimeVersioning;
+import org.geoserver.nsg.versioning.TimeVersioning;
import org.geoserver.web.publish.PublishedConfigurationPanel;
import java.sql.Timestamp;
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index a33c1190b39..875245237cd 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -9,7 +9,7 @@
-
+
org.geoserver.catalog.FeatureTypeInfo
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
index 653ac8a8e4b..3e64290a4fa 100644
--- a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TestsUtils.java
@@ -10,15 +10,15 @@
import java.io.InputStream;
-import static org.geoserver.nsg.TimeVersioning.disable;
-import static org.geoserver.nsg.TimeVersioning.enable;
+import static org.geoserver.nsg.versioning.TimeVersioning.disable;
+import static org.geoserver.nsg.versioning.TimeVersioning.enable;
-final class TestsUtils {
+public final class TestsUtils {
private TestsUtils() {
}
- static String readResource(String resourceName) {
+ public static String readResource(String resourceName) {
try (InputStream input = TestsUtils.class.getResourceAsStream(resourceName)) {
return IOUtils.toString(input);
} catch (Exception exception) {
@@ -26,8 +26,8 @@ static String readResource(String resourceName) {
}
}
- static void updateFeatureTypeTimeVersioning(Catalog catalog, String featureTypeName,
- boolean enabled, String idProperty, String timeProperty) {
+ public static void updateFeatureTypeTimeVersioning(Catalog catalog, String featureTypeName,
+ boolean enabled, String idProperty, String timeProperty) {
FeatureTypeInfo featureType = catalog.getFeatureTypeByName(featureTypeName);
if (enabled) {
enable(featureType, idProperty, timeProperty);
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/TimeVersioningTest.java
similarity index 98%
rename from src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
rename to src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/TimeVersioningTest.java
index 758d1fa3370..d69d6fe33e5 100644
--- a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/TimeVersioningTest.java
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/TimeVersioningTest.java
@@ -2,13 +2,14 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg;
+package org.geoserver.nsg.versioning;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
+import org.geoserver.nsg.TestsUtils;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.crs.DefaultGeographicCRS;
diff --git a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/web/WfsVersioningConfigTest.java
similarity index 96%
rename from src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
rename to src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/web/WfsVersioningConfigTest.java
index efe29b00e6a..b580d8e1f6e 100644
--- a/src/community/nsg-profile/src/test/java/org/geoserver/nsg/web/WfsVersioningConfigTest.java
+++ b/src/community/nsg-profile/src/test/java/org/geoserver/nsg/versioning/web/WfsVersioningConfigTest.java
@@ -2,13 +2,10 @@
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
-package org.geoserver.nsg.web;
+package org.geoserver.nsg.versioning.web;
-import org.apache.wicket.markup.html.form.CheckBox;
-import org.apache.wicket.util.tester.FormTester;
import org.geoserver.web.GeoServerWicketTestSupport;
import org.geoserver.web.publish.PublishedConfigurationPage;
-import org.geoserver.wfs.GMLInfo;
import org.geoserver.wfs.WFSInfo;
import org.junit.Test;
From 3c7dfc06a376c5cb80fabfc75436ddf86e60f7f8 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Tue, 22 Aug 2017 15:57:46 +0200
Subject: [PATCH 09/15] Added dispatcher and output format
---
.../pagination/random/IndexOutputFormat.java | 38 +++++++++++
.../IndexResultTypeDisapatcherCallback.java | 63 +++++++++++++++++++
.../src/main/resources/applicationContext.xml | 40 +++++++-----
3 files changed, 126 insertions(+), 15 deletions(-)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
new file mode 100644
index 00000000000..06498301884
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -0,0 +1,38 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.platform.Operation;
+import org.geoserver.platform.ServiceException;
+import org.geoserver.wfs.response.v2_0.HitsOutputFormat;
+
+import net.opengis.wfs20.BaseRequestType;
+
+/**
+ * This output format handles requests if the original requested result type was "index"
+ * It checks {@link BaseRequestType#getExtendedProperties()} for
+ * {@link IndexResultTypeDisapatcherCallback#RESULT_TYPE_INDEX_PARAMETER} valued as true
+ *
+ * @author sandr
+ *
+ */
+public class IndexOutputFormat extends HitsOutputFormat {
+
+ public IndexOutputFormat(GeoServer gs) {
+ super(gs);
+ }
+
+ @Override
+ public void write(Object value, OutputStream output, Operation operation)
+ throws IOException, ServiceException {
+ super.write(value, output, operation);
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
new file mode 100644
index 00000000000..72847971fa6
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
@@ -0,0 +1,63 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.ows.AbstractDispatcherCallback;
+import org.geoserver.ows.Request;
+import org.geoserver.ows.Response;
+import org.geoserver.platform.Operation;
+
+import net.opengis.wfs20.ResultTypeType;
+
+/**
+ *
+ * When a request that contains the "resultType" parameter arrives, if the parameter value is
+ * "index" it is substituted by "hits".
+ *
+ *
+ * A new entry named RESULT_TYPE_INDEX specifying that the original result type was "index" is added
+ * to KVP maps
+ *
+ */
+
+public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallback {
+
+ private GeoServer gs;
+
+ private static final String RESULT_TYPE_PARAMETER = "resultType";
+
+ private static final String RESULT_TYPE_INDEX = "index";
+
+ static final String RESULT_TYPE_INDEX_PARAMETER = "RESULT_TYPE_INDEX";
+
+ public IndexResultTypeDisapatcherCallback(GeoServer gs) {
+ this.gs = gs;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Request init(Request request) {
+ Object resultType = request.getKvp().get(RESULT_TYPE_PARAMETER);
+ if (resultType != null && resultType.toString().equals(RESULT_TYPE_INDEX)) {
+ request.getKvp().put(RESULT_TYPE_PARAMETER, ResultTypeType.HITS);
+ request.getKvp().put(RESULT_TYPE_INDEX_PARAMETER, true);
+ }
+ return super.init(request);
+ }
+
+ @Override
+ public Response responseDispatched(Request request, Operation operation, Object result,
+ Response response) {
+ Response newResponse = response;
+ if (request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER) != null
+ && (Boolean) request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER)) {
+ newResponse = new IndexOutputFormat(this.gs);
+ }
+ return super.responseDispatched(request, operation, result, newResponse);
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index 875245237cd..c54d58806ee 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -1,19 +1,29 @@
-
-
-
-
-
-
-
-
-
- org.geoserver.catalog.FeatureTypeInfo
-
-
-
+
+
+
+
+
+
+
+
+
+ org.geoserver.catalog.FeatureTypeInfo
+
+
+
+
+
+ When a request using the index result type comes in a
+ dispatcher callback
+ this switch the "index" value by the "hits" value
+
+
+
From 500a364a5c3195e37c30dde5d70d319f3534cd23 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Fri, 25 Aug 2017 15:25:05 +0200
Subject: [PATCH 10/15] Setup of GetFeature requests storage: - configuration
file parser and change listerner - db storage - file storage - clean task
---
src/community/nsg-profile/pom.xml | 5 +
.../pagination/random/IndexConfiguration.java | 74 ++++
.../pagination/random/IndexInitializer.java | 344 ++++++++++++++++++
.../pagination/random/IndexOutputFormat.java | 116 +++++-
.../IndexResultTypeDisapatcherCallback.java | 8 +
.../src/main/resources/applicationContext.xml | 12 +-
.../main/resources/configuration.properties | 6 +
7 files changed, 559 insertions(+), 6 deletions(-)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
create mode 100644 src/community/nsg-profile/src/main/resources/configuration.properties
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
index b9e26ef3fbc..b22b61e0b84 100644
--- a/src/community/nsg-profile/pom.xml
+++ b/src/community/nsg-profile/pom.xml
@@ -30,6 +30,11 @@
gs-web-core
${project.version}
+
+
+ org.geotools.jdbc
+ gt-jdbc-h2
+
org.geoserver
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
new file mode 100644
index 00000000000..4b352213440
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
@@ -0,0 +1,74 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.util.Map;
+
+import org.geoserver.platform.resource.Resource;
+import org.geotools.data.DataStore;
+
+/**
+ *
+ * Class used to store the index result type configuration managed by {@link IndexInitializer}
+ */
+public class IndexConfiguration {
+
+ private static DataStore currentDataStore;
+
+ private static Resource storageResource;
+
+ private static Long timeToLive = 600l;
+
+ private static Map currentDataStoreParams;
+
+ /**
+ * Store the DB parameters and the relative {@link DataStore}
+ *
+ * @param currentDataStoreParams
+ * @param currentDataStore
+ */
+ public static void setCurrentDataStore(Map currentDataStoreParams,
+ DataStore currentDataStore) {
+ IndexConfiguration.currentDataStoreParams = currentDataStoreParams;
+ IndexConfiguration.currentDataStore = currentDataStore;
+ }
+
+ /**
+ * Store the reference to resource used to archive the serialized GetFeatureRequest
+ *
+ * @param currentDataStoreParams
+ * @param currentDataStore
+ */
+ public static void setStorageResource(Resource storageResource) {
+ IndexConfiguration.storageResource = storageResource;
+ }
+
+ /**
+ * Store the value of time to live of stored GetFeatureRequest
+ *
+ * @param timeToLive
+ */
+ public static void setTimeToLive(Long timeToLive) {
+ IndexConfiguration.timeToLive = timeToLive;
+ }
+
+ public static DataStore getCurrentDataStore() {
+ return currentDataStore;
+ }
+
+ public static Map getCurrentDataStoreParams() {
+ return currentDataStoreParams;
+ }
+
+ public static Resource getStorageResource() {
+ return storageResource;
+ }
+
+ public static Long getTimeToLive() {
+ return timeToLive;
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
new file mode 100644
index 00000000000..463772625dd
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -0,0 +1,344 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.config.GeoServerDataDirectory;
+import org.geoserver.config.GeoServerInitializer;
+import org.geoserver.platform.GeoServerExtensions;
+import org.geoserver.platform.GeoServerResourceLoader;
+import org.geoserver.platform.resource.FileSystemResourceStore;
+import org.geoserver.platform.resource.Resource;
+import org.geoserver.platform.resource.ResourceListener;
+import org.geoserver.platform.resource.ResourceNotification;
+import org.geoserver.platform.resource.ResourceNotification.Kind;
+import org.geotools.data.DataStore;
+import org.geotools.data.DataStoreFinder;
+import org.geotools.data.DataUtilities;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.Transaction;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureIterator;
+import org.geotools.data.simple.SimpleFeatureSource;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.filter.text.cql2.CQL;
+import org.geotools.jdbc.JDBCDataStoreFactory;
+import org.geotools.util.logging.Logging;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.filter.Filter;
+
+/**
+ *
+ * Class used to parse the configuration properties stored in nsg-profile module folder:
+ *
+ * resultSets.storage.path path where to store the serialized GetFeatureRequest with name
+ * of random UUID.
+ * resultSets.timeToLive time to live value, all the stored requests that have not been
+ * used for a period of time bigger than this will be deleted.
+ * resultSets.db.{@link JDBCDataStoreFactory#DBTYPE}
+ * resultSets.db.{@link JDBCDataStoreFactory#DATABASE}
+ * resultSets.db.{@link JDBCDataStoreFactory#HOST}
+ * resultSets.db.{@link JDBCDataStoreFactory#PORT}
+ * resultSets.db.{@link JDBCDataStoreFactory#SCHEMA}
+ * resultSets.db.{@link JDBCDataStoreFactory#USER}
+ * resultSets.db.{@link JDBCDataStoreFactory#PASSWD}
+ *
+ * All configuration properties is changeable at runtime so when this properties is updated the
+ * module take the appropriate action:
+ *
+ * When the index DB is changed the new DB should be used and the content of the old table moved
+ * to the new table. If the new DB already has the index table it should be emptied,
+ * When the storage path is changed, the new storage path should be used and the old storage
+ * path content should be moved to the new one,
+ * When the the time to live is changed the {@link #clean()} procedure will update.
+ *
+ *
+ * The class is also responsible to {@link #clean()} the stored requests (result sets) that have not
+ * been used for a period of time bigger than the configured time to live value
+ *
+ *
+ * @author sandr
+ *
+ */
+
+public class IndexInitializer implements GeoServerInitializer {
+
+ static Logger LOGGER = Logging.getLogger(IndexInitializer.class);
+
+ static final String PROPERTY_DB_PREFIX = "resultSets.db.";
+
+ static final String PROPERTY_FILENAME = "configuration.properties";
+
+ static final String MODULE_DIR = "nsg-profile";
+
+ public static final String STORE_SCHEMA_NAME = "RESULT_SET";
+
+ public static final String STORE_SCHEMA = "ID:\"\",created:0,updated:0";
+
+ /*
+ * Lock to synchronize activity of clean task with listener that changes the DB and file
+ * resources
+ */
+ private final Object lock = new Object();
+
+ @Override
+ public void initialize(GeoServer geoServer) throws Exception {
+ GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class);
+ GeoServerDataDirectory dd = new GeoServerDataDirectory(loader);
+ Resource resource = dd.get(MODULE_DIR + "/" + PROPERTY_FILENAME);
+ if (loader != null) {
+ File directory = loader.findOrCreateDirectory(MODULE_DIR);
+ File file = new File(directory, PROPERTY_FILENAME);
+ // Create default configuration file
+ if (!file.exists()) {
+ InputStream stream = IndexInitializer.class
+ .getResourceAsStream("/" + PROPERTY_FILENAME);
+ Properties properties = new Properties();
+ properties.load(stream);
+ // Replace GEOSERVER_DATA_DIR placeholder
+ properties.replaceAll((k, v) -> ((String) v).replace("${GEOSERVER_DATA_DIR}",
+ dd.root().getPath()));
+ // Create resource and save properties
+ OutputStream out = resource.out();
+ properties.store(out, null);
+ out.close();
+ }
+ loadConfigurations(resource);
+ // Listen for changes in configuration file and reload properties
+ resource.addListener(new ResourceListener() {
+ @Override
+ public void changed(ResourceNotification notify) {
+ if (notify.getKind() == Kind.ENTRY_MODIFY) {
+ try {
+ loadConfigurations(resource);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error reload confiugrations.", exception);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Helper method that
+ */
+ private void loadConfigurations(Resource resource) throws IOException {
+ synchronized (lock) {
+ Properties properties = new Properties();
+ properties.load(resource.in());
+ // Reload database
+ Map params = new HashMap<>();
+ params.put(JDBCDataStoreFactory.DBTYPE.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.DBTYPE.key));
+ params.put(JDBCDataStoreFactory.DATABASE.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.DATABASE.key));
+ params.put(JDBCDataStoreFactory.HOST.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.HOST.key));
+ params.put(JDBCDataStoreFactory.PORT.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.PORT.key));
+ params.put(JDBCDataStoreFactory.SCHEMA.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.SCHEMA.key));
+ params.put(JDBCDataStoreFactory.USER.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.USER.key));
+ params.put(JDBCDataStoreFactory.PASSWD.key,
+ properties.get(PROPERTY_DB_PREFIX + JDBCDataStoreFactory.PASSWD.key));
+ /**
+ * When the index DB is changed the new DB should be used and the content of the old
+ * table moved to the new table. If the new DB already has the index table it should be
+ * emptied
+ */
+ manageDBChange(params);
+ /*
+ * If the storage path is changed, the new storage path should be used and the old
+ * storage path content should be moved to the new one
+ */
+ manageStorageChange(resource, properties.get("resultSets.storage.path"));
+ /*
+ * Change time to live
+ */
+ manageTimeToLiveChange(properties.get("resultSets.timeToLive"));
+ }
+
+ }
+
+ /**
+ * Helper method that
+ */
+ private void manageTimeToLiveChange(Object timneToLive) {
+ try {
+ if (timneToLive != null) {
+ String timneToLiveStr = (String) timneToLive;
+ IndexConfiguration.setTimeToLive(Long.parseLong(timneToLiveStr));
+ }
+ } catch (Exception exception) {
+ throw new RuntimeException("Error on change time to live", exception);
+ }
+ }
+
+ /**
+ * Helper method that move resources files form current folder to the new one, current storage
+ * is deleted
+ */
+ private void manageStorageChange(Resource resource, Object newStorage) {
+ try {
+ if (newStorage != null) {
+ String newStorageStr = (String) newStorage;
+ Resource newResource = new FileSystemResourceStore(new File(newStorageStr)).get("");
+ Resource exResource = IndexConfiguration.getStorageResource();
+ if (exResource != null && !newResource.dir().getAbsolutePath()
+ .equals(exResource.dir().getAbsolutePath())) {
+ exResource.delete();
+ IndexConfiguration.setStorageResource(newResource);
+ }
+ }
+ } catch (Exception exception) {
+ throw new RuntimeException("Error on change store", exception);
+ }
+ }
+
+ /**
+ * Helper method that move DB data from old store to new one
+ */
+ private void manageDBChange(Map params) {
+ try {
+ DataStore exDataStore = IndexConfiguration.getCurrentDataStore();
+ DataStore newDataStore = DataStoreFinder.getDataStore(params);
+ if (exDataStore != null) {
+ // New store is valid and is different from current one
+ if (newDataStore != null && !isStorageTheSame(params)) {
+ // Create table in new store
+ createTable(newDataStore, true);
+ // Move data to new store
+ moveData(exDataStore, newDataStore);
+ // Delete old store
+ exDataStore.dispose();
+ }
+ } else {
+ // Create schema
+ createTable(newDataStore, false);
+ }
+ IndexConfiguration.setCurrentDataStore(params, newDataStore);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error reload DB confiugrations.", exception);
+ }
+ }
+
+ /**
+ * Helper method that check id the DB is the same, matching the JDBC configurations parameters.
+ */
+ private Boolean isStorageTheSame(Map newParams) {
+ Map currentParams = IndexConfiguration.getCurrentDataStoreParams();
+ return currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
+ .equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key))
+ && currentParams.get(JDBCDataStoreFactory.DATABASE.key)
+ .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
+ && currentParams.get(JDBCDataStoreFactory.HOST.key)
+ .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
+ && currentParams.get(JDBCDataStoreFactory.PORT.key)
+ .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
+ && currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
+ .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
+ }
+
+ /**
+ * Helper method that create a new table on DB to store resource informations
+ */
+ private void createTable(DataStore dataStore, Boolean forceDelete) throws Exception {
+ SimpleFeatureType schema = dataStore.getSchema(STORE_SCHEMA_NAME);
+ // Schema exists
+ if (schema != null) {
+ // Delete of exist is required, and then create a new one
+ if (forceDelete) {
+ dataStore.removeSchema(STORE_SCHEMA_NAME);
+ schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ dataStore.createSchema(schema);
+ }
+ // Schema not exists, create a new one
+ } else {
+ schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ dataStore.createSchema(schema);
+ }
+ }
+
+ /**
+ * Helper method that move resource informations from current DB to the new one
+ */
+ private void moveData(DataStore exDataStore, DataStore newDataStore) throws Exception {
+ Transaction session = new DefaultTransaction("Adding");
+ try {
+ SimpleFeatureSource exFs = exDataStore.getFeatureSource(STORE_SCHEMA_NAME);
+ SimpleFeatureStore newFs = (SimpleFeatureStore) newDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ newFs.setTransaction(session);
+ newFs.addFeatures(exFs.getFeatures());
+ session.commit();
+ } catch (Throwable t) {
+ session.rollback();
+ throw new RuntimeException("Error on move data", t);
+ } finally {
+ session.close();
+ }
+ }
+
+ /**
+ * Delete all the stored requests (result sets) that have not been used for a period of time
+ * bigger than the configured time to live value. Clean also related resource files.
+ *
+ * Executed by scheduler, for details see Spring XML configuration
+ */
+ public void clean() throws Exception {
+ synchronized (lock) {
+ Transaction session = new DefaultTransaction("RemoveOld");
+ try {
+ // Remove record
+ Long timeToLive = IndexConfiguration.getTimeToLive();
+ DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ Long now = new Date().getTime();
+ Long liveTreshold = now - timeToLive * 1000;
+ Filter filter = CQL.toFilter("updated < " + liveTreshold);
+ SimpleFeatureCollection toRemoved = store.getFeatures(filter);
+ // Remove file
+ Resource currentResource = IndexConfiguration.getStorageResource();
+ SimpleFeatureIterator iterator = toRemoved.features();
+ try {
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ currentResource.get(feature.getID()).delete();
+ }
+ } finally {
+ iterator.close();
+ }
+ store.removeFeatures(filter);
+ if (LOGGER.isLoggable(Level.FINEST)) {
+ LOGGER.finest("CLEAN executed, removed stored requests older than "
+ + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+ .format(new Date(liveTreshold)));
+ }
+ } catch (Throwable t) {
+ session.rollback();
+ throw new RuntimeException("Error on move data", t);
+ } finally {
+ session.close();
+ }
+ }
+ }
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index 06498301884..1a4a60ff53c 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -7,32 +7,140 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
import org.geoserver.config.GeoServer;
+import org.geoserver.ows.util.OwsUtils;
+import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
+import org.geoserver.wfs.WFSInfo;
+import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.response.v2_0.HitsOutputFormat;
+import org.geotools.util.logging.Logging;
+import org.geotools.wfs.v2_0.WFS;
+import org.geotools.wfs.v2_0.WFSConfiguration;
+import org.geotools.xml.Encoder;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
-import net.opengis.wfs20.BaseRequestType;
+import net.opengis.wfs20.GetFeatureType;
/**
* This output format handles requests if the original requested result type was "index"
- * It checks {@link BaseRequestType#getExtendedProperties()} for
- * {@link IndexResultTypeDisapatcherCallback#RESULT_TYPE_INDEX_PARAMETER} valued as true
+ * See {@link IndexResultTypeDisapatcherCallback}
*
* @author sandr
*
*/
public class IndexOutputFormat extends HitsOutputFormat {
+ static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
+ GetFeatureType request;
+ String resultSetId;
+
public IndexOutputFormat(GeoServer gs) {
super(gs);
}
-
+
@Override
public void write(Object value, OutputStream output, Operation operation)
throws IOException, ServiceException {
+ // extract GetFeature request
+ request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
+ GetFeatureType.class);
+ // generate an UUID (resultSetID) for this request
+ resultSetId = UUID.randomUUID().toString();
super.write(value, output, operation);
}
+ @Override
+ protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSInfo wfs)
+ throws IOException {
+
+ hits.setNumberOfFeatures(BigInteger.ZERO);
+ // instantiate the XML encoder
+ Encoder encoder = new Encoder(new WFSConfiguration());
+ encoder.setEncoding(Charset.forName(wfs.getGeoServer().getSettings().getCharset()));
+ encoder.setSchemaLocation(WFS.NAMESPACE,
+ ResponseUtils.appendPath(wfs.getSchemaBaseURL(), "wfs/2.0/wfs.xsd"));
+ Document document;
+ try {
+ // encode the HITS result using FeatureCollection as the root XML element
+ document = encoder.encodeAsDOM(hits.getAdaptee(), WFS.FeatureCollection);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error encoding INDEX result.", exception);
+ }
+ // add the resultSetID attribute to the result
+ addResultSetIdElement(document, resultSetId);
+ // write the XML document to response output stream
+ writeDocument(document, output);
+ }
+
+ /**
+ * Helper method that serialize GetFeature request, store it in the file system and associate it with resultSetId
+ */
+ protected void storeGetFeature(){
+
+ }
+
+ /**
+ * Helper method that adds the resultSetID attribute to XML result. If no FeatureCollection
+ * element can be found nothing will be done.
+ */
+ private static void addResultSetIdElement(Document document, String resultSetId) {
+ // search FeatureCollection XML nodes
+ NodeList nodes = document.getElementsByTagName("wfs:FeatureCollection");
+ if (nodes.getLength() != 1) {
+ // only one node should exists, let's log an warning an move on
+ LOGGER.warning(
+ "No feature collection element could be found, resultSetID attribute will not be added.");
+ return;
+ }
+ // get the FeatureCollection node
+ Node node = nodes.item(0);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ // the found node is a XML element so let's add the resultSetID attribute
+ Element element = (Element) node;
+ element.setAttribute("resultSetID", resultSetId);
+ } else {
+ // unlikely but we got a XML node that is not a XML element
+ LOGGER.warning(
+ "Feature collection node is not a XML element, resultSetID attribute will not be added.");
+ }
+ }
+
+ /**
+ * Helper method that just writes a XML document to a given output stream.
+ */
+ private static void writeDocument(Document document, OutputStream output) {
+ // instantiate a new XML transformer
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer;
+ try {
+ transformer = transformerFactory.newTransformer();
+ } catch (Exception exception) {
+ throw new RuntimeException("Error creating XML transformer.", exception);
+ }
+ // write the XML document to the provided output stream
+ DOMSource source = new DOMSource(document);
+ StreamResult result = new StreamResult(output);
+ try {
+ transformer.transform(source, result);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error writing INDEX result to the output stream.",
+ exception);
+ }
+ }
+
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
index 72847971fa6..6a7f49e4cc2 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
@@ -5,11 +5,14 @@
package org.geoserver.nsg.pagination.random;
+import java.util.logging.Logger;
+
import org.geoserver.config.GeoServer;
import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.Request;
import org.geoserver.ows.Response;
import org.geoserver.platform.Operation;
+import org.geotools.util.logging.Logging;
import net.opengis.wfs20.ResultTypeType;
@@ -22,9 +25,14 @@
* A new entry named RESULT_TYPE_INDEX specifying that the original result type was "index" is added
* to KVP maps
*
+ *
+ * The object that manage response of type HitsOutputFormat is replaced with IndexOutputFormat before response has been dispatched
+ *
*/
public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallback {
+
+ static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
private GeoServer gs;
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index c54d58806ee..acdcb9c2caa 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -1,8 +1,9 @@
+ xmlns:task="http://www.springframework.org/schema/task"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
@@ -26,4 +27,11 @@
+
+
+
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/resources/configuration.properties b/src/community/nsg-profile/src/main/resources/configuration.properties
new file mode 100644
index 00000000000..3d9f99c95c2
--- /dev/null
+++ b/src/community/nsg-profile/src/main/resources/configuration.properties
@@ -0,0 +1,6 @@
+resultSets.storage.path=${GEOSERVER_DATA_DIR}/nsg-profile/resultSets
+resultSets.timeToLive=601
+resultSets.db.dbtype=h2
+resultSets.db.database=${GEOSERVER_DATA_DIR}/nsg-profile/db/resultSets
+resultSets.db.user=sa
+resultSets.db.password=sa
\ No newline at end of file
From 50f6260d52a14dbd7e136036d632ab0189a67444 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Fri, 25 Aug 2017 18:10:54 +0200
Subject: [PATCH 11/15] Added EMF and H2 save procedure to store GetFeature
calls
---
src/community/nsg-profile/pom.xml | 8 ++-
.../pagination/random/IndexInitializer.java | 4 +-
.../pagination/random/IndexOutputFormat.java | 71 ++++++++++++++++---
3 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
index b22b61e0b84..b93c0c49d15 100644
--- a/src/community/nsg-profile/pom.xml
+++ b/src/community/nsg-profile/pom.xml
@@ -23,18 +23,22 @@
org.geoserver
gs-wfs
- ${project.version}
org.geoserver.web
gs-web-core
- ${project.version}
org.geotools.jdbc
gt-jdbc-h2
+
+
+ org.eclipse.emf
+ org.eclipse.emf.ecore.xmi
+ 2.12.0
+
org.geoserver
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
index 463772625dd..0abacf2fe93 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -205,8 +205,8 @@ private void manageStorageChange(Resource resource, Object newStorage) {
if (exResource != null && !newResource.dir().getAbsolutePath()
.equals(exResource.dir().getAbsolutePath())) {
exResource.delete();
- IndexConfiguration.setStorageResource(newResource);
}
+ IndexConfiguration.setStorageResource(newResource);
}
} catch (Exception exception) {
throw new RuntimeException("Error on change store", exception);
@@ -281,7 +281,7 @@ private void createTable(DataStore dataStore, Boolean forceDelete) throws Except
* Helper method that move resource informations from current DB to the new one
*/
private void moveData(DataStore exDataStore, DataStore newDataStore) throws Exception {
- Transaction session = new DefaultTransaction("Adding");
+ Transaction session = new DefaultTransaction("Moving");
try {
SimpleFeatureSource exFs = exDataStore.getFeatureSource(STORE_SCHEMA_NAME);
SimpleFeatureStore newFs = (SimpleFeatureStore) newDataStore
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index 1a4a60ff53c..7d557607294 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -9,6 +9,9 @@
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
import java.util.UUID;
import java.util.logging.Logger;
@@ -17,18 +20,29 @@
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.Operation;
import org.geoserver.platform.ServiceException;
+import org.geoserver.platform.resource.Resource;
import org.geoserver.wfs.WFSInfo;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geoserver.wfs.response.v2_0.HitsOutputFormat;
+import org.geotools.data.DataStore;
+import org.geotools.data.collection.ListFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureCollection;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.util.logging.Logging;
import org.geotools.wfs.v2_0.WFS;
import org.geotools.wfs.v2_0.WFSConfiguration;
import org.geotools.xml.Encoder;
+import org.opengis.feature.simple.SimpleFeature;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -46,28 +60,39 @@
public class IndexOutputFormat extends HitsOutputFormat {
static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
- GetFeatureType request;
+
String resultSetId;
+ private static ResourceSet resSet;
+
+ static {
+ // Register XMI serializer
+ resSet = new ResourceSetImpl();
+ resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("feature",
+ new XMIResourceFactoryImpl());
+ }
+
public IndexOutputFormat(GeoServer gs) {
super(gs);
}
-
+
@Override
public void write(Object value, OutputStream output, Operation operation)
throws IOException, ServiceException {
// extract GetFeature request
- request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
+ GetFeatureType request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
GetFeatureType.class);
// generate an UUID (resultSetID) for this request
resultSetId = UUID.randomUUID().toString();
+ // store request and associate it to UUID
+ storeGetFeature(resultSetId, request);
super.write(value, output, operation);
}
@Override
protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSInfo wfs)
throws IOException {
-
+
hits.setNumberOfFeatures(BigInteger.ZERO);
// instantiate the XML encoder
Encoder encoder = new Encoder(new WFSConfiguration());
@@ -86,12 +111,42 @@ protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSIn
// write the XML document to response output stream
writeDocument(document, output);
}
-
+
/**
- * Helper method that serialize GetFeature request, store it in the file system and associate it with resultSetId
+ * Helper method that serialize GetFeature request, store it in the file system and associate it
+ * with resultSetId
+ *
+ * @param request
+ * @param resultSetId
+ * @throws Exception
*/
- protected void storeGetFeature(){
-
+ protected void storeGetFeature(String resultSetId, GetFeatureType ft) throws RuntimeException {
+ try {
+ DataStore dataStore = IndexConfiguration.getCurrentDataStore();
+ // Create and store new feature
+ SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore
+ .getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
+ SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureStore.getSchema());
+ Long now = new Date().getTime();
+ builder.add(resultSetId);
+ builder.add(now);
+ builder.add(now);
+ SimpleFeature feature = builder.buildFeature(null);
+ SimpleFeatureCollection collection = new ListFeatureCollection(featureStore.getSchema(),
+ Arrays.asList(feature));
+ featureStore.addFeatures(collection);
+
+ // Create and store file
+ Resource storageResource = IndexConfiguration.getStorageResource();
+
+ org.eclipse.emf.ecore.resource.Resource emfRes = resSet
+ .createResource(URI.createFileURI(storageResource.dir().getAbsolutePath() + "\\"
+ + resultSetId + ".feature"));
+ emfRes.getContents().add(ft);
+ emfRes.save(Collections.EMPTY_MAP);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error storing feature.", exception);
+ }
}
/**
From 0585f094f388508dc432813b2ec4b2a264da87cd Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Wed, 30 Aug 2017 11:40:29 +0200
Subject: [PATCH 12/15] Implemented PageResults operation
---
.../pagination/random/IndexConfiguration.java | 3 +
.../pagination/random/IndexInitializer.java | 51 ++++---
.../pagination/random/IndexOutputFormat.java | 6 +-
...=> IndexResultTypeDispatcherCallback.java} | 14 +-
.../random/PageResultsDispatcherCallback.java | 52 +++++++
.../random/PageResultsWebFeatureService.java | 143 ++++++++++++++++++
.../random/ResultTypeKvpParser.java | 23 +++
.../src/main/resources/applicationContext.xml | 42 ++++-
8 files changed, 295 insertions(+), 39 deletions(-)
rename src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/{IndexResultTypeDisapatcherCallback.java => IndexResultTypeDispatcherCallback.java} (86%)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
index 4b352213440..a886fcaa10a 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
@@ -13,6 +13,9 @@
/**
*
* Class used to store the index result type configuration managed by {@link IndexInitializer}
+ *
+ * @author sandr
+ *
*/
public class IndexConfiguration {
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
index 0abacf2fe93..2ece19553c2 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -36,6 +36,7 @@
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.feature.NameImpl;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.jdbc.JDBCDataStoreFactory;
import org.geotools.util.logging.Logging;
@@ -89,7 +90,7 @@ public class IndexInitializer implements GeoServerInitializer {
public static final String STORE_SCHEMA_NAME = "RESULT_SET";
- public static final String STORE_SCHEMA = "ID:\"\",created:0,updated:0";
+ public static final String STORE_SCHEMA = "ID:java.lang.String,created:java.lang.Long,updated:java.lang.Long";
/*
* Lock to synchronize activity of clean task with listener that changes the DB and file
@@ -248,31 +249,31 @@ private Boolean isStorageTheSame(Map newParams) {
return currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
.equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key))
&& currentParams.get(JDBCDataStoreFactory.DATABASE.key)
- .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
+ .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
&& currentParams.get(JDBCDataStoreFactory.HOST.key)
- .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
+ .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
&& currentParams.get(JDBCDataStoreFactory.PORT.key)
- .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
+ .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
&& currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
- .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
+ .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
}
/**
* Helper method that create a new table on DB to store resource informations
*/
private void createTable(DataStore dataStore, Boolean forceDelete) throws Exception {
- SimpleFeatureType schema = dataStore.getSchema(STORE_SCHEMA_NAME);
+ Boolean exists = dataStore.getNames().contains(new NameImpl(STORE_SCHEMA_NAME));
// Schema exists
- if (schema != null) {
+ if (exists) {
// Delete of exist is required, and then create a new one
if (forceDelete) {
dataStore.removeSchema(STORE_SCHEMA_NAME);
- schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ SimpleFeatureType schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
dataStore.createSchema(schema);
}
// Schema not exists, create a new one
} else {
- schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ SimpleFeatureType schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
dataStore.createSchema(schema);
}
}
@@ -310,28 +311,30 @@ public void clean() throws Exception {
// Remove record
Long timeToLive = IndexConfiguration.getTimeToLive();
DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
- SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
- .getFeatureSource(STORE_SCHEMA_NAME);
Long now = new Date().getTime();
Long liveTreshold = now - timeToLive * 1000;
- Filter filter = CQL.toFilter("updated < " + liveTreshold);
- SimpleFeatureCollection toRemoved = store.getFeatures(filter);
- // Remove file
- Resource currentResource = IndexConfiguration.getStorageResource();
- SimpleFeatureIterator iterator = toRemoved.features();
- try {
- while (iterator.hasNext()) {
- SimpleFeature feature = iterator.next();
- currentResource.get(feature.getID()).delete();
+ if(currentDataStore != null){
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ Filter filter = CQL.toFilter("updated < " + liveTreshold);
+ SimpleFeatureCollection toRemoved = store.getFeatures(filter);
+ // Remove file
+ Resource currentResource = IndexConfiguration.getStorageResource();
+ SimpleFeatureIterator iterator = toRemoved.features();
+ try {
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ currentResource.get(feature.getID()).delete();
+ }
+ } finally {
+ iterator.close();
}
- } finally {
- iterator.close();
+ store.removeFeatures(filter);
}
- store.removeFeatures(filter);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("CLEAN executed, removed stored requests older than "
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
- .format(new Date(liveTreshold)));
+ .format(new Date(liveTreshold)));
}
} catch (Throwable t) {
session.rollback();
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index 7d557607294..ee2104b8ec6 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -52,7 +52,7 @@
/**
* This output format handles requests if the original requested result type was "index"
- * See {@link IndexResultTypeDisapatcherCallback}
+ * See {@link IndexResultTypeDispatcherCallback}
*
* @author sandr
*
@@ -83,7 +83,7 @@ public void write(Object value, OutputStream output, Operation operation)
GetFeatureType request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
GetFeatureType.class);
// generate an UUID (resultSetID) for this request
- resultSetId = UUID.randomUUID().toString();
+ resultSetId = UUID.randomUUID().toString().replaceAll("-", "");
// store request and associate it to UUID
storeGetFeature(resultSetId, request);
super.write(value, output, operation);
@@ -138,7 +138,7 @@ protected void storeGetFeature(String resultSetId, GetFeatureType ft) throws Run
// Create and store file
Resource storageResource = IndexConfiguration.getStorageResource();
-
+
org.eclipse.emf.ecore.resource.Resource emfRes = resSet
.createResource(URI.createFileURI(storageResource.dir().getAbsolutePath() + "\\"
+ resultSetId + ".feature"));
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
similarity index 86%
rename from src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
rename to src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
index 6a7f49e4cc2..fc96a618f06 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDisapatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
@@ -26,13 +26,17 @@
* to KVP maps
*
*
- * The object that manage response of type HitsOutputFormat is replaced with IndexOutputFormat before response has been dispatched
+ * The object that manage response of type HitsOutputFormat is replaced with IndexOutputFormat
+ * before response has been dispatched
*
+ *
+ * @author sandr
+ *
*/
-public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallback {
-
- static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
+public class IndexResultTypeDispatcherCallback extends AbstractDispatcherCallback {
+
+ static Logger LOGGER = Logging.getLogger(IndexResultTypeDispatcherCallback.class);
private GeoServer gs;
@@ -42,7 +46,7 @@ public class IndexResultTypeDisapatcherCallback extends AbstractDispatcherCallba
static final String RESULT_TYPE_INDEX_PARAMETER = "RESULT_TYPE_INDEX";
- public IndexResultTypeDisapatcherCallback(GeoServer gs) {
+ public IndexResultTypeDispatcherCallback(GeoServer gs) {
this.gs = gs;
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
new file mode 100644
index 00000000000..e2a0078f835
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
@@ -0,0 +1,52 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import org.geoserver.config.GeoServer;
+import org.geoserver.ows.AbstractDispatcherCallback;
+import org.geoserver.ows.Request;
+import org.geoserver.platform.Service;
+import org.geoserver.platform.ServiceException;
+import org.geotools.util.logging.Logging;
+
+/**
+ *
+ * This dispatcher manages service of type {@link PageResultsWebFeatureService} and sets the
+ * parameter ResultSetID present on KVP map.
+ *
+ * Dummy featureId value is added to KVP map to allow dispatcher to manage it as usual WFS 2.0
+ * request.
+ *
+ * @author sandr
+ *
+ */
+
+public class PageResultsDispatcherCallback extends AbstractDispatcherCallback {
+
+ static Logger LOGGER = Logging.getLogger(PageResultsDispatcherCallback.class);
+
+ private GeoServer gs;
+
+ public PageResultsDispatcherCallback(GeoServer gs) {
+ this.gs = gs;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Service serviceDispatched(Request request, Service service) throws ServiceException {
+ if (service.getService() instanceof PageResultsWebFeatureService) {
+ PageResultsWebFeatureService prService = (PageResultsWebFeatureService) service
+ .getService();
+ prService.setResultSetID((String) request.getKvp().get("resultSetID"));
+ request.getKvp().put("featureId", Arrays.asList("dummy"));
+ }
+ return super.serviceDispatched(request, service);
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
new file mode 100644
index 00000000000..e67ee835d8c
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
@@ -0,0 +1,143 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.logging.Logger;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
+import org.geoserver.config.GeoServer;
+import org.geoserver.platform.resource.Resource;
+import org.geoserver.wfs.DefaultWebFeatureService20;
+import org.geoserver.wfs.WFSException;
+import org.geoserver.wfs.request.FeatureCollectionResponse;
+import org.geotools.data.DataStore;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.Transaction;
+import org.geotools.data.simple.SimpleFeatureStore;
+import org.geotools.filter.text.cql2.CQL;
+import org.geotools.util.logging.Logging;
+import org.opengis.filter.Filter;
+
+import net.opengis.wfs20.GetFeatureType;
+import net.opengis.wfs20.ResultTypeType;
+
+/**
+ * This service supports the PageResults operation and manage it
+ *
+ * @author sandr
+ *
+ */
+
+public class PageResultsWebFeatureService extends DefaultWebFeatureService20 {
+
+ static Logger LOGGER = Logging.getLogger(PageResultsWebFeatureService.class);
+
+ private static ResourceSet resSet;
+
+ private static final String GML32_FORMAT = "application/gml+xml; version=3.2";
+
+ private static final BigInteger DEFAULT_START = new BigInteger("0");
+
+ private static final BigInteger DEFAULT_COUNT = new BigInteger("10");
+
+ private String resultSetID;
+
+ public PageResultsWebFeatureService(GeoServer geoServer) {
+ super(geoServer);
+ // Register XMI serializer
+ resSet = new ResourceSetImpl();
+ resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("feature",
+ new XMIResourceFactoryImpl());
+ }
+
+ /**
+ * Recovers the stored request with associated {@link #resultSetID} and overrides the parameters
+ * using the ones provided with current operation or the default values:
+ *
+ * {@link net.opengis.wfs20.GetFeatureType#getStartIndex StartIndex }
+ * {@link net.opengis.wfs20.GetFeatureType#getCount Count }
+ * {@link net.opengis.wfs20.GetFeatureType#getOutputFormat OutputFormat }
+ * {@link net.opengis.wfs20.GetFeatureType#getResultType ResultType }
+ *
+ * Then executes the GetFeature operation using the WFS 2.0 service implementation and return is
+ * result.
+ *
+ * @param request
+ * @return
+ * @throws WFSException
+ * @throws IOException
+ */
+ public FeatureCollectionResponse pageResults(GetFeatureType request)
+ throws WFSException, IOException {
+ // Retrieve stored request
+ GetFeatureType gft = getFeature(resultSetID);
+ // Update with incoming parameters or defaults
+ BigInteger startIndex = request.getStartIndex() != null ? request.getStartIndex()
+ : DEFAULT_START;
+ BigInteger count = request.getCount() != null ? request.getCount() : DEFAULT_COUNT;
+ String outputFormat = request.getOutputFormat() != null ? request.getOutputFormat()
+ : GML32_FORMAT;
+ ResultTypeType resultType = request.getResultType() != null ? request.getResultType()
+ : ResultTypeType.RESULTS;
+ gft.setStartIndex(startIndex);
+ gft.setCount(count);
+ gft.setOutputFormat(outputFormat);
+ gft.setResultType(resultType);
+ // Execute as getFeature
+ return super.getFeature(gft);
+ }
+
+ /**
+ * Sets the resultSetID
+ *
+ * @param resultSetID
+ */
+ public void setResultSetID(String resultSetID) {
+ this.resultSetID = resultSetID;
+ }
+
+ /**
+ * Helper method that deserializes GetFeature request and updates its last utilization
+ *
+ * @param resultSetID
+ * @return
+ * @throws IOException
+ * @throws Exception
+ */
+ private GetFeatureType getFeature(String resultSetID) throws IOException {
+ GetFeatureType feature = null;
+ Transaction transaction = new DefaultTransaction("Update");
+ try {
+ // Update GetFeature utilization
+ DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
+ store.setTransaction(transaction);
+ Filter filter = CQL.toFilter("ID = '" + resultSetID + "'");
+ store.modifyFeatures("updated", new Date().getTime(), filter);
+ // Retrieve GetFeature from file
+ Resource storageResource = IndexConfiguration.getStorageResource();
+ org.eclipse.emf.ecore.resource.Resource emfRes = resSet.getResource(URI.createFileURI(
+ storageResource.dir().getAbsolutePath() + "\\" + resultSetID + ".feature"),
+ true);
+ feature = (GetFeatureType) emfRes.getContents().get(0);
+ } catch (Exception t) {
+ transaction.rollback();
+ throw new RuntimeException("Error on retrive feature", t);
+ } finally {
+ transaction.close();
+ }
+ return feature;
+
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java
new file mode 100644
index 00000000000..bdc797dea78
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/ResultTypeKvpParser.java
@@ -0,0 +1,23 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
+package org.geoserver.nsg.pagination.random;
+
+import org.geotools.util.Version;
+
+/**
+ *
+ * @author sandr
+ *
+ */
+
+public class ResultTypeKvpParser extends org.geoserver.wfs.kvp.v2_0.ResultTypeKvpParser {
+
+ public ResultTypeKvpParser() {
+ super();
+ setVersion(new Version("2.0.2"));
+ }
+
+}
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index acdcb9c2caa..842c5b9ce9b 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -1,7 +1,6 @@
@@ -18,8 +17,9 @@
-
+
+
When a request using the index result type comes in a
dispatcher callback
@@ -27,11 +27,39 @@
+
+
+
+ The PageResults operation will allow clients to query
+ random positions of an existing result set (stored GetFeature
+ request) that was previously created using the index result type
+
+
+
+
+
+
+
+
+
+
-
-
+
-
+
From 7e15e647dd5d5cae4f595a911bbfb326dbbe7507 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Wed, 20 Sep 2017 16:27:52 +0200
Subject: [PATCH 13/15] Fixed review issues Implemented final response of
PageResult operation
---
src/community/nsg-profile/pom.xml | 179 +++++++++---------
.../pagination/random/IndexConfiguration.java | 15 +-
.../pagination/random/IndexInitializer.java | 148 ++++++++-------
.../pagination/random/IndexOutputFormat.java | 87 ++++++---
.../IndexResultTypeDispatcherCallback.java | 11 +-
.../random/PageResultsDispatcherCallback.java | 21 +-
.../random/PageResultsWebFeatureService.java | 87 ++++++---
.../nsg/pagination/random/RequestData.java | 30 +++
8 files changed, 347 insertions(+), 231 deletions(-)
create mode 100644 src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java
diff --git a/src/community/nsg-profile/pom.xml b/src/community/nsg-profile/pom.xml
index b93c0c49d15..4fcfea4c77c 100644
--- a/src/community/nsg-profile/pom.xml
+++ b/src/community/nsg-profile/pom.xml
@@ -1,95 +1,92 @@
- 4.0.0
-
- org.geoserver
- community
- 2.12-SNAPSHOT
-
- org.geoserver.community
- gs-nsg-profile
- jar
- 2.12-SNAPSHOT
- NSG Profile
-
-
-
- org.geoserver
- gs-main
- tests
- test
-
-
- org.geoserver
- gs-wfs
-
-
- org.geoserver.web
- gs-web-core
-
-
-
- org.geotools.jdbc
- gt-jdbc-h2
-
-
-
- org.eclipse.emf
- org.eclipse.emf.ecore.xmi
- 2.12.0
-
-
-
- org.geoserver
- gs-wfs
- tests
- ${project.version}
-
-
- org.geoserver.web
- gs-web-core
- ${project.version}
- tests
- test
-
-
-
- org.hamcrest
- hamcrest-library
- test
-
-
- org.springframework
- spring-test
- test
-
-
- junit
- junit
- test
-
-
- javax.servlet
- javax.servlet-api
- test
-
-
-
-
-
- ${basedir}/src/main/java
-
- **/*.html
-
-
-
- ${basedir}/src/main/resources
-
- **/*
-
-
-
-
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ 4.0.0
+
+ org.geoserver
+ community
+ 2.12-SNAPSHOT
+
+ org.geoserver.community
+ gs-nsg-profile
+ jar
+ NSG Profile
+
+
+
+ org.geoserver
+ gs-wfs
+
+
+ org.geoserver.web
+ gs-web-core
+
+
+
+ org.geotools.jdbc
+ gt-jdbc-h2
+
+
+ com.google.code.gson
+ gson
+
+
+
+ org.geoserver
+ gs-main
+ tests
+ test
+
+
+ org.geoserver
+ gs-wfs
+ tests
+ ${project.version}
+
+
+ org.geoserver.web
+ gs-web-core
+ ${project.version}
+ tests
+ test
+
+
+
+ org.hamcrest
+ hamcrest-library
+ test
+
+
+ org.springframework
+ spring-test
+ test
+
+
+ junit
+ junit
+ test
+
+
+ javax.servlet
+ javax.servlet-api
+ test
+
+
+
+
+
+ ${basedir}/src/main/java
+
+ **/*.html
+
+
+
+ ${basedir}/src/main/resources
+
+ **/*
+
+
+
+
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
index a886fcaa10a..4c5abb0cbba 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
@@ -6,6 +6,7 @@
package org.geoserver.nsg.pagination.random;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import org.geoserver.platform.resource.Resource;
import org.geotools.data.DataStore;
@@ -23,7 +24,7 @@ public class IndexConfiguration {
private static Resource storageResource;
- private static Long timeToLive = 600l;
+ private static Long timeToLiveInSec = 600l;
private static Map currentDataStoreParams;
@@ -42,8 +43,7 @@ public static void setCurrentDataStore(Map currentDataStoreParam
/**
* Store the reference to resource used to archive the serialized GetFeatureRequest
*
- * @param currentDataStoreParams
- * @param currentDataStore
+ * @param storageResource
*/
public static void setStorageResource(Resource storageResource) {
IndexConfiguration.storageResource = storageResource;
@@ -53,9 +53,10 @@ public static void setStorageResource(Resource storageResource) {
* Store the value of time to live of stored GetFeatureRequest
*
* @param timeToLive
+ * @param timeUnit
*/
- public static void setTimeToLive(Long timeToLive) {
- IndexConfiguration.timeToLive = timeToLive;
+ public static void setTimeToLive(Long timeToLive, TimeUnit timeUnit) {
+ IndexConfiguration.timeToLiveInSec = timeUnit.toSeconds(timeToLive);
}
public static DataStore getCurrentDataStore() {
@@ -70,8 +71,8 @@ public static Resource getStorageResource() {
return storageResource;
}
- public static Long getTimeToLive() {
- return timeToLive;
+ public static Long getTimeToLiveInSec() {
+ return timeToLiveInSec;
}
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
index 2ece19553c2..5e2337ec7be 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -14,6 +14,9 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -24,8 +27,6 @@
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.resource.FileSystemResourceStore;
import org.geoserver.platform.resource.Resource;
-import org.geoserver.platform.resource.ResourceListener;
-import org.geoserver.platform.resource.ResourceNotification;
import org.geoserver.platform.resource.ResourceNotification.Kind;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
@@ -78,7 +79,7 @@
*
*/
-public class IndexInitializer implements GeoServerInitializer {
+public final class IndexInitializer implements GeoServerInitializer {
static Logger LOGGER = Logging.getLogger(IndexInitializer.class);
@@ -96,7 +97,7 @@ public class IndexInitializer implements GeoServerInitializer {
* Lock to synchronize activity of clean task with listener that changes the DB and file
* resources
*/
- private final Object lock = new Object();
+ protected static final ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock();
@Override
public void initialize(GeoServer geoServer) throws Exception {
@@ -115,22 +116,24 @@ public void initialize(GeoServer geoServer) throws Exception {
// Replace GEOSERVER_DATA_DIR placeholder
properties.replaceAll((k, v) -> ((String) v).replace("${GEOSERVER_DATA_DIR}",
dd.root().getPath()));
+ stream.close();
// Create resource and save properties
- OutputStream out = resource.out();
- properties.store(out, null);
- out.close();
+ try {
+ OutputStream out = resource.out();
+ properties.store(out, null);
+ out.close();
+ } catch (Exception exception) {
+ throw new RuntimeException("Error to initialize configurations.", exception);
+ }
}
loadConfigurations(resource);
// Listen for changes in configuration file and reload properties
- resource.addListener(new ResourceListener() {
- @Override
- public void changed(ResourceNotification notify) {
- if (notify.getKind() == Kind.ENTRY_MODIFY) {
- try {
- loadConfigurations(resource);
- } catch (Exception exception) {
- throw new RuntimeException("Error reload confiugrations.", exception);
- }
+ resource.addListener(notify -> {
+ if (notify.getKind() == Kind.ENTRY_MODIFY) {
+ try {
+ loadConfigurations(resource);
+ } catch (Exception exception) {
+ throw new RuntimeException("Error reload configurations.", exception);
}
}
});
@@ -138,12 +141,15 @@ public void changed(ResourceNotification notify) {
}
/**
- * Helper method that
+ * Helper method that loads configuration file and changes environment setup
*/
private void loadConfigurations(Resource resource) throws IOException {
- synchronized (lock) {
+ try {
+ IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
Properties properties = new Properties();
- properties.load(resource.in());
+ InputStream is = resource.in();
+ properties.load(is);
+ is.close();
// Reload database
Map params = new HashMap<>();
params.put(JDBCDataStoreFactory.DBTYPE.key,
@@ -175,18 +181,19 @@ private void loadConfigurations(Resource resource) throws IOException {
* Change time to live
*/
manageTimeToLiveChange(properties.get("resultSets.timeToLive"));
+ } finally {
+ IndexInitializer.READ_WRITE_LOCK.writeLock().unlock();
}
-
}
/**
- * Helper method that
+ * Helper method that store the time to live value
*/
private void manageTimeToLiveChange(Object timneToLive) {
try {
if (timneToLive != null) {
String timneToLiveStr = (String) timneToLive;
- IndexConfiguration.setTimeToLive(Long.parseLong(timneToLiveStr));
+ IndexConfiguration.setTimeToLive(Long.parseLong(timneToLiveStr), TimeUnit.SECONDS);
}
} catch (Exception exception) {
throw new RuntimeException("Error on change time to live", exception);
@@ -222,13 +229,13 @@ private void manageDBChange(Map params) {
DataStore exDataStore = IndexConfiguration.getCurrentDataStore();
DataStore newDataStore = DataStoreFinder.getDataStore(params);
if (exDataStore != null) {
- // New store is valid and is different from current one
- if (newDataStore != null && !isStorageTheSame(params)) {
- // Create table in new store
+ // New database is valid and is different from current one
+ if (newDataStore != null && !isDBTheSame(params)) {
+ // Create table in new database
createTable(newDataStore, true);
- // Move data to new store
+ // Move data to new database
moveData(exDataStore, newDataStore);
- // Delete old store
+ // Delete old database
exDataStore.dispose();
}
} else {
@@ -237,38 +244,39 @@ private void manageDBChange(Map params) {
}
IndexConfiguration.setCurrentDataStore(params, newDataStore);
} catch (Exception exception) {
- throw new RuntimeException("Error reload DB confiugrations.", exception);
+ throw new RuntimeException("Error reload DB configurations.", exception);
}
}
/**
* Helper method that check id the DB is the same, matching the JDBC configurations parameters.
*/
- private Boolean isStorageTheSame(Map newParams) {
+ private Boolean isDBTheSame(Map newParams) {
Map currentParams = IndexConfiguration.getCurrentDataStoreParams();
return currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
.equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key))
&& currentParams.get(JDBCDataStoreFactory.DATABASE.key)
- .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
+ .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
&& currentParams.get(JDBCDataStoreFactory.HOST.key)
- .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
+ .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
&& currentParams.get(JDBCDataStoreFactory.PORT.key)
- .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
+ .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
&& currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
- .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
+ .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
}
/**
* Helper method that create a new table on DB to store resource informations
*/
- private void createTable(DataStore dataStore, Boolean forceDelete) throws Exception {
- Boolean exists = dataStore.getNames().contains(new NameImpl(STORE_SCHEMA_NAME));
+ private void createTable(DataStore dataStore, boolean forceDelete) throws Exception {
+ boolean exists = dataStore.getNames().contains(new NameImpl(STORE_SCHEMA_NAME));
// Schema exists
if (exists) {
// Delete of exist is required, and then create a new one
if (forceDelete) {
dataStore.removeSchema(STORE_SCHEMA_NAME);
- SimpleFeatureType schema = DataUtilities.createType(STORE_SCHEMA_NAME, STORE_SCHEMA);
+ SimpleFeatureType schema = DataUtilities.createType(STORE_SCHEMA_NAME,
+ STORE_SCHEMA);
dataStore.createSchema(schema);
}
// Schema not exists, create a new one
@@ -305,43 +313,47 @@ private void moveData(DataStore exDataStore, DataStore newDataStore) throws Exce
* Executed by scheduler, for details see Spring XML configuration
*/
public void clean() throws Exception {
- synchronized (lock) {
- Transaction session = new DefaultTransaction("RemoveOld");
- try {
- // Remove record
- Long timeToLive = IndexConfiguration.getTimeToLive();
- DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
- Long now = new Date().getTime();
- Long liveTreshold = now - timeToLive * 1000;
- if(currentDataStore != null){
- SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
- .getFeatureSource(STORE_SCHEMA_NAME);
- Filter filter = CQL.toFilter("updated < " + liveTreshold);
- SimpleFeatureCollection toRemoved = store.getFeatures(filter);
- // Remove file
- Resource currentResource = IndexConfiguration.getStorageResource();
- SimpleFeatureIterator iterator = toRemoved.features();
- try {
- while (iterator.hasNext()) {
- SimpleFeature feature = iterator.next();
- currentResource.get(feature.getID()).delete();
- }
- } finally {
- iterator.close();
+ Transaction session = new DefaultTransaction("RemoveOld");
+ try {
+ IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
+ // Remove record
+ Long timeToLive = IndexConfiguration.getTimeToLiveInSec();
+ DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ Long liveTreshold = System.currentTimeMillis() - timeToLive * 1000;
+ long featureRemoved = 0;
+ if (currentDataStore != null) {
+ SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
+ .getFeatureSource(STORE_SCHEMA_NAME);
+ Filter filter = CQL.toFilter("updated < " + liveTreshold);
+ SimpleFeatureCollection toRemoved = store.getFeatures(filter);
+ // Remove file
+ Resource currentResource = IndexConfiguration.getStorageResource();
+ SimpleFeatureIterator iterator = toRemoved.features();
+ try {
+ while (iterator.hasNext()) {
+ SimpleFeature feature = iterator.next();
+ currentResource.get(feature.getID()).delete();
+ featureRemoved++;
}
- store.removeFeatures(filter);
+ } finally {
+ iterator.close();
}
- if (LOGGER.isLoggable(Level.FINEST)) {
- LOGGER.finest("CLEAN executed, removed stored requests older than "
+ store.removeFeatures(filter);
+ }
+ if (LOGGER.isLoggable(Level.FINEST)) {
+ if (featureRemoved > 0) {
+ LOGGER.finest("CLEAN executed, removed " + featureRemoved
+ + " stored requests older than "
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
- .format(new Date(liveTreshold)));
+ .format(new Date(liveTreshold)));
}
- } catch (Throwable t) {
- session.rollback();
- throw new RuntimeException("Error on move data", t);
- } finally {
- session.close();
}
+ } catch (Throwable t) {
+ session.rollback();
+ throw new RuntimeException("Error on move data", t);
+ } finally {
+ session.close();
+ IndexInitializer.READ_WRITE_LOCK.writeLock().unlock();
}
}
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index ee2104b8ec6..d0e732c179f 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -5,13 +5,15 @@
package org.geoserver.nsg.pagination.random;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Date;
+import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
@@ -20,11 +22,8 @@
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.geoserver.config.GeoServer;
+import org.geoserver.ows.Request;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.Operation;
@@ -59,33 +58,31 @@
*/
public class IndexOutputFormat extends HitsOutputFormat {
- static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
+ private final static Logger LOGGER = Logging.getLogger(IndexOutputFormat.class);
- String resultSetId;
+ private String resultSetId;
- private static ResourceSet resSet;
-
- static {
- // Register XMI serializer
- resSet = new ResourceSetImpl();
- resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("feature",
- new XMIResourceFactoryImpl());
- }
+ private Request request;
public IndexOutputFormat(GeoServer gs) {
super(gs);
}
+ public void setRequest(Request request) {
+ this.request = request;
+ }
+
@Override
public void write(Object value, OutputStream output, Operation operation)
throws IOException, ServiceException {
// extract GetFeature request
- GetFeatureType request = (GetFeatureType) OwsUtils.parameter(operation.getParameters(),
+ GetFeatureType getFeatureType = OwsUtils.parameter(operation.getParameters(),
GetFeatureType.class);
- // generate an UUID (resultSetID) for this request
+ // generate an UUID (resultSetID) for this request, GeoTools complained about the - in the
+ // ID
resultSetId = UUID.randomUUID().toString().replaceAll("-", "");
// store request and associate it to UUID
- storeGetFeature(resultSetId, request);
+ storeGetFeature(resultSetId, this.request);
super.write(value, output, operation);
}
@@ -116,36 +113,66 @@ protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSIn
* Helper method that serialize GetFeature request, store it in the file system and associate it
* with resultSetId
*
- * @param request
* @param resultSetId
- * @throws Exception
+ * @param getFeatureType
+ * @throws RuntimeException
*/
- protected void storeGetFeature(String resultSetId, GetFeatureType ft) throws RuntimeException {
+ private void storeGetFeature(String resultSetId, Request request) throws RuntimeException {
try {
+ IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
DataStore dataStore = IndexConfiguration.getCurrentDataStore();
// Create and store new feature
SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore
.getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureStore.getSchema());
- Long now = new Date().getTime();
+ Long now = System.currentTimeMillis();
+ // Add ID field value (see IndexInitializer.STORE_SCHEMA)
builder.add(resultSetId);
+ // Add created field value (see IndexInitializer.STORE_SCHEMA)
builder.add(now);
+ // Add updated field value (see IndexInitializer.STORE_SCHEMA)
builder.add(now);
SimpleFeature feature = builder.buildFeature(null);
SimpleFeatureCollection collection = new ListFeatureCollection(featureStore.getSchema(),
Arrays.asList(feature));
featureStore.addFeatures(collection);
-
// Create and store file
Resource storageResource = IndexConfiguration.getStorageResource();
-
- org.eclipse.emf.ecore.resource.Resource emfRes = resSet
- .createResource(URI.createFileURI(storageResource.dir().getAbsolutePath() + "\\"
- + resultSetId + ".feature"));
- emfRes.getContents().add(ft);
- emfRes.save(Collections.EMPTY_MAP);
+
+ // Serialize KVP parameters and the POST content
+ Map kvp = request.getKvp();
+ Map rawKvp = request.getRawKvp();
+ RequestData data = new RequestData();
+ data.setKvp(kvp);
+ data.setRawKvp(rawKvp);
+ // Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ // Writer writer = new FileWriter(
+ // storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
+
+ FileOutputStream fos = new FileOutputStream(
+ storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
+ BufferedOutputStream bos = new BufferedOutputStream(fos);
+ // ObjectOutputStream oos = new ObjectOutputStream(bos);
+ // oos.writeObject(getFeatureType);
+ // oos.close();
+
+ /*
+ * Kryo kryo = new Kryo(); kryo.setInstantiatorStrategy( new
+ * Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
+ * kryo.getFieldSerializerConfig().setCopyTransient(false); Output output = new
+ * Output(bos); kryo.writeObject(output, data);
+ */
+
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(data);
+ oos.close();
+
+ // gson.toJson(paramMap, writer);
+ // writer.close();
} catch (Exception exception) {
throw new RuntimeException("Error storing feature.", exception);
+ } finally {
+ IndexInitializer.READ_WRITE_LOCK.writeLock().unlock();
}
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
index fc96a618f06..fa20ac40d83 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
@@ -31,12 +31,12 @@
*
*
* @author sandr
- *
+ *
*/
public class IndexResultTypeDispatcherCallback extends AbstractDispatcherCallback {
- static Logger LOGGER = Logging.getLogger(IndexResultTypeDispatcherCallback.class);
+ private final static Logger LOGGER = Logging.getLogger(IndexResultTypeDispatcherCallback.class);
private GeoServer gs;
@@ -64,12 +64,13 @@ public Request init(Request request) {
@Override
public Response responseDispatched(Request request, Operation operation, Object result,
Response response) {
- Response newResponse = response;
if (request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER) != null
&& (Boolean) request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER)) {
- newResponse = new IndexOutputFormat(this.gs);
+ IndexOutputFormat newResponse = new IndexOutputFormat(this.gs);
+ newResponse.setRequest(request);
+ return super.responseDispatched(request, operation, result, newResponse);
}
- return super.responseDispatched(request, operation, result, newResponse);
+ return super.responseDispatched(request, operation, result, response);
}
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
index e2a0078f835..ad3ef0adc1f 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
@@ -5,12 +5,13 @@
package org.geoserver.nsg.pagination.random;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.logging.Logger;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.AbstractDispatcherCallback;
import org.geoserver.ows.Request;
+import org.geoserver.platform.Operation;
import org.geoserver.platform.Service;
import org.geoserver.platform.ServiceException;
import org.geotools.util.logging.Logging;
@@ -29,7 +30,7 @@
public class PageResultsDispatcherCallback extends AbstractDispatcherCallback {
- static Logger LOGGER = Logging.getLogger(PageResultsDispatcherCallback.class);
+ private final static Logger LOGGER = Logging.getLogger(PageResultsDispatcherCallback.class);
private GeoServer gs;
@@ -43,10 +44,22 @@ public Service serviceDispatched(Request request, Service service) throws Servic
if (service.getService() instanceof PageResultsWebFeatureService) {
PageResultsWebFeatureService prService = (PageResultsWebFeatureService) service
.getService();
- prService.setResultSetID((String) request.getKvp().get("resultSetID"));
- request.getKvp().put("featureId", Arrays.asList("dummy"));
+ String resultSetId = (String) request.getKvp().get("resultSetID");
+ prService.setResultSetID(resultSetId);
+ request.getKvp().put("featureId", Collections.singletonList("dummy"));
+
}
return super.serviceDispatched(request, service);
}
+ @Override
+ public Operation operationDispatched(Request request, Operation operation) {
+ Operation newOperation = operation;
+ if (operation.getId().equals("PageResults")) {
+ newOperation = new Operation("GetFeature", operation.getService(),
+ operation.getMethod(), operation.getParameters());
+ }
+ return super.operationDispatched(request, newOperation);
+ }
+
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
index e67ee835d8c..70a89794df0 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
@@ -5,19 +5,21 @@
package org.geoserver.nsg.pagination.random;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.Date;
import java.util.logging.Logger;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.geoserver.config.GeoServer;
+import org.geoserver.ows.Dispatcher;
+import org.geoserver.ows.KvpRequestReader;
+import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.resource.Resource;
import org.geoserver.wfs.DefaultWebFeatureService20;
-import org.geoserver.wfs.WFSException;
import org.geoserver.wfs.request.FeatureCollectionResponse;
import org.geotools.data.DataStore;
import org.geotools.data.DefaultTransaction;
@@ -41,25 +43,20 @@ public class PageResultsWebFeatureService extends DefaultWebFeatureService20 {
static Logger LOGGER = Logging.getLogger(PageResultsWebFeatureService.class);
- private static ResourceSet resSet;
-
private static final String GML32_FORMAT = "application/gml+xml; version=3.2";
private static final BigInteger DEFAULT_START = new BigInteger("0");
private static final BigInteger DEFAULT_COUNT = new BigInteger("10");
- private String resultSetID;
+ private String resultSetId;
public PageResultsWebFeatureService(GeoServer geoServer) {
super(geoServer);
- // Register XMI serializer
- resSet = new ResourceSetImpl();
- resSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("feature",
- new XMIResourceFactoryImpl());
}
/**
+ *
* Recovers the stored request with associated {@link #resultSetID} and overrides the parameters
* using the ones provided with current operation or the default values:
*
@@ -73,14 +70,15 @@ public PageResultsWebFeatureService(GeoServer geoServer) {
*
* @param request
* @return
- * @throws WFSException
- * @throws IOException
+ * @throws Exception
*/
- public FeatureCollectionResponse pageResults(GetFeatureType request)
- throws WFSException, IOException {
+ public FeatureCollectionResponse pageResults(GetFeatureType request) throws Exception {
// Retrieve stored request
- GetFeatureType gft = getFeature(resultSetID);
+ GetFeatureType gft = getFeature(this.resultSetId);
+
// Update with incoming parameters or defaults
+ Method setBaseUrl = OwsUtils.setter(gft.getClass(), "baseUrl", String.class);
+ setBaseUrl.invoke(gft, new Object[] { request.getBaseUrl() });
BigInteger startIndex = request.getStartIndex() != null ? request.getStartIndex()
: DEFAULT_START;
BigInteger count = request.getCount() != null ? request.getCount() : DEFAULT_COUNT;
@@ -101,8 +99,8 @@ public FeatureCollectionResponse pageResults(GetFeatureType request)
*
* @param resultSetID
*/
- public void setResultSetID(String resultSetID) {
- this.resultSetID = resultSetID;
+ public void setResultSetID(String resultSetId) {
+ this.resultSetId = resultSetId;
}
/**
@@ -110,31 +108,68 @@ public void setResultSetID(String resultSetID) {
*
* @param resultSetID
* @return
- * @throws IOException
* @throws Exception
*/
- private GetFeatureType getFeature(String resultSetID) throws IOException {
+ private GetFeatureType getFeature(String resultSetId) throws IOException {
GetFeatureType feature = null;
Transaction transaction = new DefaultTransaction("Update");
try {
+ IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
// Update GetFeature utilization
DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
.getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
store.setTransaction(transaction);
- Filter filter = CQL.toFilter("ID = '" + resultSetID + "'");
+ Filter filter = CQL.toFilter("ID = '" + resultSetId + "'");
store.modifyFeatures("updated", new Date().getTime(), filter);
// Retrieve GetFeature from file
Resource storageResource = IndexConfiguration.getStorageResource();
- org.eclipse.emf.ecore.resource.Resource emfRes = resSet.getResource(URI.createFileURI(
- storageResource.dir().getAbsolutePath() + "\\" + resultSetID + ".feature"),
- true);
- feature = (GetFeatureType) emfRes.getContents().get(0);
+
+ // Deserialize KVP parameters and the POST content
+ // Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ // Reader reader = new FileReader(
+ // storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
+ // Map data = gson.fromJson(reader, Map.class);
+ // reader.close();
+ /*
+ * Kryo kryo = new Kryo(); kryo.setInstantiatorStrategy( new
+ * Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
+ * kryo.getFieldSerializerConfig().setCopyTransient(false);
+ */
+
+ FileInputStream fis = new FileInputStream(
+ storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
+ BufferedInputStream bis = new BufferedInputStream(fis);
+
+ ObjectInputStream objectinputstream = new ObjectInputStream(bis);
+ RequestData data = (RequestData) objectinputstream.readObject();
+
+ /*
+ * Input input = new Input(bis);
+ *
+ * RequestData data = kryo.readObject(input, RequestData.class);
+ */
+
+ objectinputstream.close();
+
+ KvpRequestReader kvpReader = Dispatcher.findKvpRequestReader(GetFeatureType.class);
+ Object requestBean = kvpReader.createRequest();
+ feature = (GetFeatureType) kvpReader.read(requestBean, data.getKvp(), data.getRawKvp());
+
+ /*
+ * Map kvp = data.get("kvp"); Map rawKvp = data.get("rawKvp");
+ *
+ * KvpRequestReader kvpReader = Dispatcher.findKvpRequestReader(GetFeatureType.class);
+ * Object requestBean = kvpReader.createRequest(); feature = (GetFeatureType)
+ * kvpReader.read(requestBean, kvp, rawKvp);
+ */
+
} catch (Exception t) {
transaction.rollback();
throw new RuntimeException("Error on retrive feature", t);
} finally {
transaction.close();
+ IndexInitializer.READ_WRITE_LOCK.writeLock().unlock();
}
return feature;
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java
new file mode 100644
index 00000000000..3ca196bf82a
--- /dev/null
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java
@@ -0,0 +1,30 @@
+package org.geoserver.nsg.pagination.random;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public class RequestData implements Serializable {
+
+ private static final long serialVersionUID = 6687946816946977568L;
+
+ private Map kvp;
+
+ private Map rawKvp;
+
+ public Map getKvp() {
+ return kvp;
+ }
+
+ public void setKvp(Map kvp) {
+ this.kvp = kvp;
+ }
+
+ public Map getRawKvp() {
+ return rawKvp;
+ }
+
+ public void setRawKvp(Map rawKvp) {
+ this.rawKvp = rawKvp;
+ }
+
+}
From 21754e0e0aa8a99e9d26ac029af06435c7346797 Mon Sep 17 00:00:00 2001
From: Sandro Salari
Date: Fri, 22 Sep 2017 16:41:42 +0200
Subject: [PATCH 14/15] Refactoring and test
---
.../pagination/random/IndexConfiguration.java | 30 +--
.../pagination/random/IndexInitializer.java | 76 ++++---
.../pagination/random/IndexOutputFormat.java | 24 +--
.../IndexResultTypeDispatcherCallback.java | 14 +-
.../random/PageResultsDispatcherCallback.java | 4 +-
.../random/PageResultsWebFeatureService.java | 49 ++---
.../nsg/pagination/random/RequestData.java | 13 +-
.../src/main/resources/applicationContext.xml | 5 +
.../main/resources/configuration.properties | 2 +-
.../random/GetFeatureRequestStorageTest.java | 189 ++++++++++++++++++
.../random/IndexResultTypeTest.java | 131 ++++++++++++
11 files changed, 438 insertions(+), 99 deletions(-)
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/pagination/random/GetFeatureRequestStorageTest.java
create mode 100644 src/community/nsg-profile/src/test/java/org/geoserver/nsg/pagination/random/IndexResultTypeTest.java
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
index 4c5abb0cbba..d7c33bb703a 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexConfiguration.java
@@ -20,13 +20,13 @@
*/
public class IndexConfiguration {
- private static DataStore currentDataStore;
+ private DataStore currentDataStore;
- private static Resource storageResource;
+ private Resource storageResource;
- private static Long timeToLiveInSec = 600l;
+ private Long timeToLiveInSec = 600l;
- private static Map currentDataStoreParams;
+ private Map currentDataStoreParams;
/**
* Store the DB parameters and the relative {@link DataStore}
@@ -34,10 +34,10 @@ public class IndexConfiguration {
* @param currentDataStoreParams
* @param currentDataStore
*/
- public static void setCurrentDataStore(Map currentDataStoreParams,
+ public void setCurrentDataStore(Map currentDataStoreParams,
DataStore currentDataStore) {
- IndexConfiguration.currentDataStoreParams = currentDataStoreParams;
- IndexConfiguration.currentDataStore = currentDataStore;
+ this.currentDataStoreParams = currentDataStoreParams;
+ this.currentDataStore = currentDataStore;
}
/**
@@ -45,8 +45,8 @@ public static void setCurrentDataStore(Map currentDataStoreParam
*
* @param storageResource
*/
- public static void setStorageResource(Resource storageResource) {
- IndexConfiguration.storageResource = storageResource;
+ public void setStorageResource(Resource storageResource) {
+ this.storageResource = storageResource;
}
/**
@@ -55,23 +55,23 @@ public static void setStorageResource(Resource storageResource) {
* @param timeToLive
* @param timeUnit
*/
- public static void setTimeToLive(Long timeToLive, TimeUnit timeUnit) {
- IndexConfiguration.timeToLiveInSec = timeUnit.toSeconds(timeToLive);
+ public void setTimeToLive(Long timeToLive, TimeUnit timeUnit) {
+ this.timeToLiveInSec = timeUnit.toSeconds(timeToLive);
}
- public static DataStore getCurrentDataStore() {
+ public DataStore getCurrentDataStore() {
return currentDataStore;
}
- public static Map getCurrentDataStoreParams() {
+ public Map getCurrentDataStoreParams() {
return currentDataStoreParams;
}
- public static Resource getStorageResource() {
+ public Resource getStorageResource() {
return storageResource;
}
- public static Long getTimeToLiveInSec() {
+ public Long getTimeToLiveInSec() {
return timeToLiveInSec;
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
index 5e2337ec7be..035d1520f90 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexInitializer.java
@@ -6,7 +6,6 @@
package org.geoserver.nsg.pagination.random;
import java.io.File;
-import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
@@ -93,6 +92,8 @@ public final class IndexInitializer implements GeoServerInitializer {
public static final String STORE_SCHEMA = "ID:java.lang.String,created:java.lang.Long,updated:java.lang.Long";
+ private IndexConfiguration indexConfiguration;
+
/*
* Lock to synchronize activity of clean task with listener that changes the DB and file
* resources
@@ -101,6 +102,7 @@ public final class IndexInitializer implements GeoServerInitializer {
@Override
public void initialize(GeoServer geoServer) throws Exception {
+ indexConfiguration = GeoServerExtensions.bean(IndexConfiguration.class);
GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class);
GeoServerDataDirectory dd = new GeoServerDataDirectory(loader);
Resource resource = dd.get(MODULE_DIR + "/" + PROPERTY_FILENAME);
@@ -143,7 +145,7 @@ public void initialize(GeoServer geoServer) throws Exception {
/**
* Helper method that loads configuration file and changes environment setup
*/
- private void loadConfigurations(Resource resource) throws IOException {
+ private void loadConfigurations(Resource resource) throws Exception {
try {
IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
Properties properties = new Properties();
@@ -181,6 +183,8 @@ private void loadConfigurations(Resource resource) throws IOException {
* Change time to live
*/
manageTimeToLiveChange(properties.get("resultSets.timeToLive"));
+ } catch (Exception exception) {
+ throw new RuntimeException("Error reload configurations.", exception);
} finally {
IndexInitializer.READ_WRITE_LOCK.writeLock().unlock();
}
@@ -193,7 +197,7 @@ private void manageTimeToLiveChange(Object timneToLive) {
try {
if (timneToLive != null) {
String timneToLiveStr = (String) timneToLive;
- IndexConfiguration.setTimeToLive(Long.parseLong(timneToLiveStr), TimeUnit.SECONDS);
+ indexConfiguration.setTimeToLive(Long.parseLong(timneToLiveStr), TimeUnit.SECONDS);
}
} catch (Exception exception) {
throw new RuntimeException("Error on change time to live", exception);
@@ -209,12 +213,12 @@ private void manageStorageChange(Resource resource, Object newStorage) {
if (newStorage != null) {
String newStorageStr = (String) newStorage;
Resource newResource = new FileSystemResourceStore(new File(newStorageStr)).get("");
- Resource exResource = IndexConfiguration.getStorageResource();
+ Resource exResource = indexConfiguration.getStorageResource();
if (exResource != null && !newResource.dir().getAbsolutePath()
.equals(exResource.dir().getAbsolutePath())) {
exResource.delete();
}
- IndexConfiguration.setStorageResource(newResource);
+ indexConfiguration.setStorageResource(newResource);
}
} catch (Exception exception) {
throw new RuntimeException("Error on change store", exception);
@@ -226,7 +230,7 @@ private void manageStorageChange(Resource resource, Object newStorage) {
*/
private void manageDBChange(Map params) {
try {
- DataStore exDataStore = IndexConfiguration.getCurrentDataStore();
+ DataStore exDataStore = indexConfiguration.getCurrentDataStore();
DataStore newDataStore = DataStoreFinder.getDataStore(params);
if (exDataStore != null) {
// New database is valid and is different from current one
@@ -235,14 +239,14 @@ private void manageDBChange(Map params) {
createTable(newDataStore, true);
// Move data to new database
moveData(exDataStore, newDataStore);
- // Delete old database
+ // Dispose old database
exDataStore.dispose();
}
} else {
// Create schema
createTable(newDataStore, false);
}
- IndexConfiguration.setCurrentDataStore(params, newDataStore);
+ indexConfiguration.setCurrentDataStore(params, newDataStore);
} catch (Exception exception) {
throw new RuntimeException("Error reload DB configurations.", exception);
}
@@ -252,17 +256,42 @@ private void manageDBChange(Map params) {
* Helper method that check id the DB is the same, matching the JDBC configurations parameters.
*/
private Boolean isDBTheSame(Map newParams) {
- Map currentParams = IndexConfiguration.getCurrentDataStoreParams();
- return currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
- .equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key))
- && currentParams.get(JDBCDataStoreFactory.DATABASE.key)
- .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key))
- && currentParams.get(JDBCDataStoreFactory.HOST.key)
- .equals(newParams.get(JDBCDataStoreFactory.HOST.key))
- && currentParams.get(JDBCDataStoreFactory.PORT.key)
- .equals(newParams.get(JDBCDataStoreFactory.PORT.key))
- && currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
- .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key));
+ Map currentParams = indexConfiguration.getCurrentDataStoreParams();
+ boolean isTheSame = (currentParams.get(JDBCDataStoreFactory.DBTYPE.key) == null
+ && newParams.get(JDBCDataStoreFactory.DBTYPE.key) == null)
+ || (currentParams.get(JDBCDataStoreFactory.DBTYPE.key) != null
+ && newParams.get(JDBCDataStoreFactory.DBTYPE.key) != null
+ && currentParams.get(JDBCDataStoreFactory.DBTYPE.key)
+ .equals(newParams.get(JDBCDataStoreFactory.DBTYPE.key)));
+ isTheSame = isTheSame
+ && (currentParams.get(JDBCDataStoreFactory.DATABASE.key) == null
+ && newParams.get(JDBCDataStoreFactory.DATABASE.key) == null)
+ || (currentParams.get(JDBCDataStoreFactory.DATABASE.key) != null
+ && newParams.get(JDBCDataStoreFactory.DATABASE.key) != null
+ && currentParams.get(JDBCDataStoreFactory.DATABASE.key)
+ .equals(newParams.get(JDBCDataStoreFactory.DATABASE.key)));
+ isTheSame = isTheSame
+ && (currentParams.get(JDBCDataStoreFactory.HOST.key) == null
+ && newParams.get(JDBCDataStoreFactory.HOST.key) == null)
+ || (currentParams.get(JDBCDataStoreFactory.HOST.key) != null
+ && newParams.get(JDBCDataStoreFactory.HOST.key) != null
+ && currentParams.get(JDBCDataStoreFactory.HOST.key)
+ .equals(newParams.get(JDBCDataStoreFactory.HOST.key)));
+ isTheSame = isTheSame
+ && (currentParams.get(JDBCDataStoreFactory.PORT.key) == null
+ && newParams.get(JDBCDataStoreFactory.PORT.key) == null)
+ || (currentParams.get(JDBCDataStoreFactory.PORT.key) != null
+ && newParams.get(JDBCDataStoreFactory.PORT.key) != null
+ && currentParams.get(JDBCDataStoreFactory.PORT.key)
+ .equals(newParams.get(JDBCDataStoreFactory.PORT.key)));
+ isTheSame = isTheSame
+ && (currentParams.get(JDBCDataStoreFactory.SCHEMA.key) == null
+ && newParams.get(JDBCDataStoreFactory.SCHEMA.key) == null)
+ || (currentParams.get(JDBCDataStoreFactory.SCHEMA.key) != null
+ && newParams.get(JDBCDataStoreFactory.SCHEMA.key) != null
+ && currentParams.get(JDBCDataStoreFactory.SCHEMA.key)
+ .equals(newParams.get(JDBCDataStoreFactory.SCHEMA.key)));
+ return isTheSame;
}
/**
@@ -317,17 +346,18 @@ public void clean() throws Exception {
try {
IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
// Remove record
- Long timeToLive = IndexConfiguration.getTimeToLiveInSec();
- DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ Long timeToLive = indexConfiguration.getTimeToLiveInSec();
+ DataStore currentDataStore = indexConfiguration.getCurrentDataStore();
Long liveTreshold = System.currentTimeMillis() - timeToLive * 1000;
long featureRemoved = 0;
if (currentDataStore != null) {
SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
.getFeatureSource(STORE_SCHEMA_NAME);
Filter filter = CQL.toFilter("updated < " + liveTreshold);
+
SimpleFeatureCollection toRemoved = store.getFeatures(filter);
// Remove file
- Resource currentResource = IndexConfiguration.getStorageResource();
+ Resource currentResource = indexConfiguration.getStorageResource();
SimpleFeatureIterator iterator = toRemoved.features();
try {
while (iterator.hasNext()) {
@@ -350,7 +380,7 @@ public void clean() throws Exception {
}
} catch (Throwable t) {
session.rollback();
- throw new RuntimeException("Error on move data", t);
+ LOGGER.warning("Error on clean data");
} finally {
session.close();
IndexInitializer.READ_WRITE_LOCK.writeLock().unlock();
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
index d0e732c179f..2ce258f4408 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexOutputFormat.java
@@ -64,8 +64,11 @@ public class IndexOutputFormat extends HitsOutputFormat {
private Request request;
- public IndexOutputFormat(GeoServer gs) {
+ private IndexConfiguration indexConfiguration;
+
+ public IndexOutputFormat(GeoServer gs, IndexConfiguration indexConfiguration) {
super(gs);
+ this.indexConfiguration = indexConfiguration;
}
public void setRequest(Request request) {
@@ -120,7 +123,7 @@ protected void encode(FeatureCollectionResponse hits, OutputStream output, WFSIn
private void storeGetFeature(String resultSetId, Request request) throws RuntimeException {
try {
IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
- DataStore dataStore = IndexConfiguration.getCurrentDataStore();
+ DataStore dataStore = this.indexConfiguration.getCurrentDataStore();
// Create and store new feature
SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore
.getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
@@ -137,7 +140,7 @@ private void storeGetFeature(String resultSetId, Request request) throws Runtime
Arrays.asList(feature));
featureStore.addFeatures(collection);
// Create and store file
- Resource storageResource = IndexConfiguration.getStorageResource();
+ Resource storageResource = this.indexConfiguration.getStorageResource();
// Serialize KVP parameters and the POST content
Map kvp = request.getKvp();
@@ -145,30 +148,15 @@ private void storeGetFeature(String resultSetId, Request request) throws Runtime
RequestData data = new RequestData();
data.setKvp(kvp);
data.setRawKvp(rawKvp);
- // Gson gson = new GsonBuilder().setPrettyPrinting().create();
- // Writer writer = new FileWriter(
- // storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
FileOutputStream fos = new FileOutputStream(
storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
BufferedOutputStream bos = new BufferedOutputStream(fos);
- // ObjectOutputStream oos = new ObjectOutputStream(bos);
- // oos.writeObject(getFeatureType);
- // oos.close();
-
- /*
- * Kryo kryo = new Kryo(); kryo.setInstantiatorStrategy( new
- * Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
- * kryo.getFieldSerializerConfig().setCopyTransient(false); Output output = new
- * Output(bos); kryo.writeObject(output, data);
- */
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(data);
oos.close();
- // gson.toJson(paramMap, writer);
- // writer.close();
} catch (Exception exception) {
throw new RuntimeException("Error storing feature.", exception);
} finally {
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
index fa20ac40d83..7cd244c55ce 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/IndexResultTypeDispatcherCallback.java
@@ -40,14 +40,17 @@ public class IndexResultTypeDispatcherCallback extends AbstractDispatcherCallbac
private GeoServer gs;
+ private IndexConfiguration indexConfiguration;
+
private static final String RESULT_TYPE_PARAMETER = "resultType";
private static final String RESULT_TYPE_INDEX = "index";
static final String RESULT_TYPE_INDEX_PARAMETER = "RESULT_TYPE_INDEX";
- public IndexResultTypeDispatcherCallback(GeoServer gs) {
+ public IndexResultTypeDispatcherCallback(GeoServer gs, IndexConfiguration indexConfiguration) {
this.gs = gs;
+ this.indexConfiguration = indexConfiguration;
}
@Override
@@ -64,13 +67,14 @@ public Request init(Request request) {
@Override
public Response responseDispatched(Request request, Operation operation, Object result,
Response response) {
+ Response newResponse = response;
if (request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER) != null
&& (Boolean) request.getKvp().get(RESULT_TYPE_INDEX_PARAMETER)) {
- IndexOutputFormat newResponse = new IndexOutputFormat(this.gs);
- newResponse.setRequest(request);
- return super.responseDispatched(request, operation, result, newResponse);
+ IndexOutputFormat r = new IndexOutputFormat(this.gs, this.indexConfiguration);
+ r.setRequest(request);
+ newResponse = r;
}
- return super.responseDispatched(request, operation, result, response);
+ return super.responseDispatched(request, operation, result, newResponse);
}
}
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
index ad3ef0adc1f..e6a25ce785c 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsDispatcherCallback.java
@@ -45,7 +45,7 @@ public Service serviceDispatched(Request request, Service service) throws Servic
PageResultsWebFeatureService prService = (PageResultsWebFeatureService) service
.getService();
String resultSetId = (String) request.getKvp().get("resultSetID");
- prService.setResultSetID(resultSetId);
+ prService.setResultSetId(resultSetId);
request.getKvp().put("featureId", Collections.singletonList("dummy"));
}
@@ -55,6 +55,8 @@ public Service serviceDispatched(Request request, Service service) throws Servic
@Override
public Operation operationDispatched(Request request, Operation operation) {
Operation newOperation = operation;
+ // Change operation from PageResults to GetFeature to allow management of request as
+ // standard get feature
if (operation.getId().equals("PageResults")) {
newOperation = new Operation("GetFeature", operation.getService(),
operation.getMethod(), operation.getParameters());
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
index 70a89794df0..db7e5979c3c 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/PageResultsWebFeatureService.java
@@ -51,8 +51,12 @@ public class PageResultsWebFeatureService extends DefaultWebFeatureService20 {
private String resultSetId;
- public PageResultsWebFeatureService(GeoServer geoServer) {
+ private IndexConfiguration indexConfiguration;
+
+ public PageResultsWebFeatureService(GeoServer geoServer,
+ IndexConfiguration indexConfiguration) {
super(geoServer);
+ this.indexConfiguration = indexConfiguration;
}
/**
@@ -76,12 +80,13 @@ public FeatureCollectionResponse pageResults(GetFeatureType request) throws Exce
// Retrieve stored request
GetFeatureType gft = getFeature(this.resultSetId);
- // Update with incoming parameters or defaults
+ // Update with incoming parameters or index request or defaults
Method setBaseUrl = OwsUtils.setter(gft.getClass(), "baseUrl", String.class);
setBaseUrl.invoke(gft, new Object[] { request.getBaseUrl() });
BigInteger startIndex = request.getStartIndex() != null ? request.getStartIndex()
- : DEFAULT_START;
- BigInteger count = request.getCount() != null ? request.getCount() : DEFAULT_COUNT;
+ : gft.getStartIndex() != null ? gft.getStartIndex() : DEFAULT_START;
+ BigInteger count = request.getCount() != null ? request.getCount()
+ : gft.getCount() != null ? gft.getCount() : DEFAULT_COUNT;
String outputFormat = request.getOutputFormat() != null ? request.getOutputFormat()
: GML32_FORMAT;
ResultTypeType resultType = request.getResultType() != null ? request.getResultType()
@@ -95,11 +100,11 @@ public FeatureCollectionResponse pageResults(GetFeatureType request) throws Exce
}
/**
- * Sets the resultSetID
+ * Sets the resultSetId
*
- * @param resultSetID
+ * @param resultSetId
*/
- public void setResultSetID(String resultSetId) {
+ public void setResultSetId(String resultSetId) {
this.resultSetId = resultSetId;
}
@@ -116,26 +121,14 @@ private GetFeatureType getFeature(String resultSetId) throws IOException {
try {
IndexInitializer.READ_WRITE_LOCK.writeLock().lock();
// Update GetFeature utilization
- DataStore currentDataStore = IndexConfiguration.getCurrentDataStore();
+ DataStore currentDataStore = this.indexConfiguration.getCurrentDataStore();
SimpleFeatureStore store = (SimpleFeatureStore) currentDataStore
.getFeatureSource(IndexInitializer.STORE_SCHEMA_NAME);
store.setTransaction(transaction);
Filter filter = CQL.toFilter("ID = '" + resultSetId + "'");
store.modifyFeatures("updated", new Date().getTime(), filter);
// Retrieve GetFeature from file
- Resource storageResource = IndexConfiguration.getStorageResource();
-
- // Deserialize KVP parameters and the POST content
- // Gson gson = new GsonBuilder().setPrettyPrinting().create();
- // Reader reader = new FileReader(
- // storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
- // Map data = gson.fromJson(reader, Map.class);
- // reader.close();
- /*
- * Kryo kryo = new Kryo(); kryo.setInstantiatorStrategy( new
- * Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
- * kryo.getFieldSerializerConfig().setCopyTransient(false);
- */
+ Resource storageResource = this.indexConfiguration.getStorageResource();
FileInputStream fis = new FileInputStream(
storageResource.dir().getAbsolutePath() + "\\" + resultSetId + ".feature");
@@ -144,26 +137,12 @@ private GetFeatureType getFeature(String resultSetId) throws IOException {
ObjectInputStream objectinputstream = new ObjectInputStream(bis);
RequestData data = (RequestData) objectinputstream.readObject();
- /*
- * Input input = new Input(bis);
- *
- * RequestData data = kryo.readObject(input, RequestData.class);
- */
-
objectinputstream.close();
KvpRequestReader kvpReader = Dispatcher.findKvpRequestReader(GetFeatureType.class);
Object requestBean = kvpReader.createRequest();
feature = (GetFeatureType) kvpReader.read(requestBean, data.getKvp(), data.getRawKvp());
- /*
- * Map kvp = data.get("kvp"); Map rawKvp = data.get("rawKvp");
- *
- * KvpRequestReader kvpReader = Dispatcher.findKvpRequestReader(GetFeatureType.class);
- * Object requestBean = kvpReader.createRequest(); feature = (GetFeatureType)
- * kvpReader.read(requestBean, kvp, rawKvp);
- */
-
} catch (Exception t) {
transaction.rollback();
throw new RuntimeException("Error on retrive feature", t);
diff --git a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java
index 3ca196bf82a..5613d142bf2 100644
--- a/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java
+++ b/src/community/nsg-profile/src/main/java/org/geoserver/nsg/pagination/random/RequestData.java
@@ -1,9 +1,20 @@
+/* (c) 2017 Open Source Geospatial Foundation - all rights reserved
+ * This code is licensed under the GPL 2.0 license, available at the root
+ * application directory.
+ */
+
package org.geoserver.nsg.pagination.random;
import java.io.Serializable;
import java.util.Map;
-public class RequestData implements Serializable {
+/**
+ * This class is used to store the data to serialize to recreate previous get feature request
+ *
+ * @author sandr
+ *
+ */
+class RequestData implements Serializable {
private static final long serialVersionUID = 6687946816946977568L;
diff --git a/src/community/nsg-profile/src/main/resources/applicationContext.xml b/src/community/nsg-profile/src/main/resources/applicationContext.xml
index 842c5b9ce9b..b3f815b3a10 100644
--- a/src/community/nsg-profile/src/main/resources/applicationContext.xml
+++ b/src/community/nsg-profile/src/main/resources/applicationContext.xml
@@ -17,6 +17,9 @@
+
+
@@ -26,6 +29,7 @@
this switch the "index" value by the "hits" value
+
+