]> git.argeo.org Git - lgpl/argeo-commons.git/blobdiff - gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java
Improve security
[lgpl/argeo-commons.git] / gis / runtime / org.argeo.gis.geotools / src / main / java / org / argeo / geotools / jcr / GeoJcrIndex.java
diff --git a/gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java b/gis/runtime/org.argeo.gis.geotools/src/main/java/org/argeo/geotools/jcr/GeoJcrIndex.java
new file mode 100644 (file)
index 0000000..e6afb94
--- /dev/null
@@ -0,0 +1,234 @@
+package org.argeo.geotools.jcr;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.geotools.GeoToolsUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.gis.GisNames;
+import org.argeo.jcr.gis.GisTypes;
+import org.argeo.security.SystemExecutionService;
+import org.geotools.data.DataStore;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.FeatureStore;
+import org.geotools.data.Transaction;
+import org.geotools.feature.FeatureCollection;
+import org.geotools.feature.FeatureCollections;
+import org.geotools.feature.simple.SimpleFeatureBuilder;
+import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
+import org.geotools.filter.FilterFactoryImpl;
+import org.geotools.referencing.CRS;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+import org.opengis.filter.FilterFactory2;
+import org.opengis.filter.identity.FeatureId;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Point;
+
+public class GeoJcrIndex implements EventListener {
+       public final static SimpleFeatureType JCR_POINT;
+
+       final static String JCR_POINT_NAME = "JCR_POINT";
+       // PostGIS convention
+       final static String DEFAULT_GEOM_NAME = "the_geom";
+
+       private final static Log log = LogFactory.getLog(GeoJcrIndex.class);
+
+       static {
+               SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
+               builder.setNamespaceURI(GisNames.GIS_NAMESPACE);
+               builder.setName(JCR_POINT_NAME);
+
+               builder.setDefaultGeometry(DEFAULT_GEOM_NAME);
+               builder.add(DEFAULT_GEOM_NAME, Point.class);
+
+               builder.add(JcrUtils.normalize(Property.JCR_UUID), String.class);
+               builder.add(JcrUtils.normalize(Property.JCR_PATH), String.class);
+               builder.add(JcrUtils.normalize(Property.JCR_PRIMARY_TYPE), String.class);
+               // mix:created
+               // builder.add(JcrUtils.normalize(Property.JCR_CREATED), Date.class);
+               // builder.add(JcrUtils.normalize(Property.JCR_CREATED_BY),
+               // String.class);
+               // mix:lastModified
+               builder.add(JcrUtils.normalize(Property.JCR_LAST_MODIFIED), Date.class);
+               builder.add(JcrUtils.normalize(Property.JCR_LAST_MODIFIED_BY),
+                               String.class);
+
+               JCR_POINT = builder.buildFeatureType();
+       }
+
+       private DataStore dataStore;
+       private FeatureStore<SimpleFeatureType, SimpleFeature> pointsIndex;
+       private Session session;
+       private SystemExecutionService systemExecutionService;
+
+       // TODO: use common factory finder?
+       private FilterFactory2 ff = new FilterFactoryImpl();
+
+       // TODO: use finder?
+       private GeometryFactory geometryFactory = new GeometryFactory();
+
+       public void init() {
+               GeoToolsUtils.createSchemaIfNeeded(dataStore, JCR_POINT);
+               pointsIndex = GeoToolsUtils.getFeatureStore(dataStore,
+                               JCR_POINT.getName());
+
+               systemExecutionService.executeAsSystem(new Runnable() {
+                       public void run() {
+                               try {
+                                       session.getWorkspace()
+                                                       .getObservationManager()
+                                                       .addEventListener(GeoJcrIndex.this,
+                                                                       Event.NODE_ADDED | Event.NODE_REMOVED, "/",
+                                                                       true, null,
+                                                                       new String[] { GisTypes.GIS_GEOMETRY },
+                                                                       false);
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException("Cannot initialize GeoJcr index",
+                                                       e);
+                               }
+
+                       }
+               });
+       }
+
+       public void dispose() {
+       }
+
+       public void onEvent(EventIterator events) {
+               FeatureCollection<SimpleFeatureType, SimpleFeature> pointsToAdd = FeatureCollections
+                               .newCollection();
+               Set<FeatureId> pointsToRemove = new HashSet<FeatureId>();
+               while (events.hasNext()) {
+                       Event event = events.nextEvent();
+                       try {
+                               Integer eventType = event.getType();
+                               if (Event.NODE_ADDED == eventType) {
+                                       Node node = session.getNodeByIdentifier(event
+                                                       .getIdentifier());
+                                       if (node.isNodeType(GisTypes.GIS_POINT)) {
+                                               Point point = nodeToPoint(node);
+                                               SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(
+                                                               JCR_POINT);
+                                               featureBuilder.set(DEFAULT_GEOM_NAME, point);
+                                               mapNodeToFeature(node, featureBuilder);
+                                               pointsToAdd.add(featureBuilder.buildFeature(node
+                                                               .getIdentifier()));
+                                       }
+                               } else if (Event.NODE_REMOVED == eventType) {
+                                       String id = event.getIdentifier();
+                                       pointsToRemove.add(ff.featureId(id));
+                               }
+                       } catch (Exception e) {
+                               log.error("Cannot process event " + event, e);
+                       }
+               }
+
+               // persist
+               // TODO: this may be more optimal to persist in one single transaction,
+               // but we will loose modifications on all nodes if one fails
+               try {
+                       Transaction transaction = new DefaultTransaction();
+                       pointsIndex.setTransaction(transaction);
+                       try {
+                               // points
+                               pointsIndex.addFeatures(pointsToAdd);
+                               if (pointsToRemove.size() != 0)
+                                       pointsIndex.removeFeatures(ff.id(pointsToRemove));
+                               transaction.commit();
+                       } catch (Exception e) {
+                               transaction.rollback();
+                               throw new ArgeoException("Cannot persist changes", e);
+                       } finally {
+                               transaction.close();
+                       }
+               } catch (ArgeoException e) {
+                       throw e;
+               } catch (IOException e) {
+                       throw new ArgeoException("Unexpected issue with the transaction", e);
+               }
+       }
+
+       protected void mapNodeToFeature(Node node,
+                       SimpleFeatureBuilder featureBuilder) {
+               try {
+                       featureBuilder.set(JcrUtils.normalize(Property.JCR_UUID),
+                                       node.getIdentifier());
+                       featureBuilder.set(JcrUtils.normalize(Property.JCR_PATH),
+                                       node.getPath());
+                       featureBuilder.set(JcrUtils.normalize(Property.JCR_PRIMARY_TYPE),
+                                       node.getPrimaryNodeType().getName());
+                       if (node.hasProperty(Property.JCR_LAST_MODIFIED))
+                               featureBuilder.set(
+                                               JcrUtils.normalize(Property.JCR_LAST_MODIFIED), node
+                                                               .getProperty(Property.JCR_LAST_MODIFIED)
+                                                               .getDate().getTime());
+                       if (node.hasProperty(Property.JCR_LAST_MODIFIED_BY))
+                               featureBuilder
+                                               .set(JcrUtils.normalize(Property.JCR_LAST_MODIFIED_BY),
+                                                               node.getProperty(Property.JCR_LAST_MODIFIED_BY)
+                                                                               .getString());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot map " + node + " to "
+                                       + featureBuilder.getFeatureType(), e);
+               }
+       }
+
+       /** Return the node as a point in the CRS of the related feature store. */
+       protected Point nodeToPoint(Node node) {
+               CoordinateReferenceSystem featureStoreCrs = pointsIndex.getSchema()
+                               .getCoordinateReferenceSystem();
+               DirectPosition nodePosition = GeoJcrUtils.nodeToPosition(node);
+               CoordinateReferenceSystem nodeCrs = nodePosition
+                               .getCoordinateReferenceSystem();
+
+               // transform if not same CRS
+               DirectPosition targetPosition;
+               if (!featureStoreCrs.getIdentifiers().contains(nodeCrs.getName())) {
+                       MathTransform transform;
+                       try {
+                               transform = CRS.findMathTransform(nodeCrs, featureStoreCrs);
+                               targetPosition = transform.transform(nodePosition, null);
+                       } catch (Exception e) {
+                               throw new ArgeoException("Cannot transform from " + nodeCrs
+                                               + " to " + featureStoreCrs, e);
+                       }
+               } else {
+                       targetPosition = nodePosition;
+               }
+               double[] coo = targetPosition.getCoordinate();
+               return geometryFactory.createPoint(new Coordinate(coo[0], coo[1]));
+       }
+
+       public void setDataStore(DataStore dataStore) {
+               this.dataStore = dataStore;
+       }
+
+       public void setSession(Session session) {
+               this.session = session;
+       }
+
+       public void setSystemExecutionService(
+                       SystemExecutionService systemExecutionService) {
+               this.systemExecutionService = systemExecutionService;
+       }
+
+}