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 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 pointsToAdd = FeatureCollections .newCollection(); Set pointsToRemove = new HashSet(); 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; } }