/** * Copyright (C) 2012 Red Hat, Inc. (jdcasey@commonjava.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.commonjava.cartographer.graph.spi.neo4j.update; import static org.commonjava.cartographer.graph.spi.neo4j.io.Conversions.NID; import static org.commonjava.cartographer.graph.spi.neo4j.io.Conversions.RID; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.commonjava.cartographer.graph.ViewParams; import org.commonjava.cartographer.graph.model.GraphPathInfo; import org.commonjava.maven.atlas.graph.rel.ProjectRelationship; import org.commonjava.cartographer.graph.spi.neo4j.GraphAdmin; import org.commonjava.cartographer.graph.spi.neo4j.ViewIndexes; import org.commonjava.cartographer.graph.spi.neo4j.io.Conversions; import org.commonjava.cartographer.graph.spi.neo4j.model.CyclePath; import org.commonjava.cartographer.graph.spi.neo4j.model.Neo4jGraphPath; import org.commonjava.cartographer.graph.spi.neo4j.traverse.AbstractTraverseVisitor; import org.commonjava.cartographer.graph.spi.neo4j.traverse.AtlasCollector; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexHits; import org.neo4j.graphdb.index.RelationshipIndex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ViewUpdater extends AbstractTraverseVisitor { private final Logger logger = LoggerFactory.getLogger( getClass() ); private final Node viewNode; private final GraphAdmin admin; private final CycleCacheUpdater cycleUpdater; private final ViewIndexes indexes; private Node stopNode; public ViewUpdater( final ViewParams view, final Node viewNode, final ViewIndexes indexes, final GraphAdmin admin ) { this.viewNode = viewNode; this.indexes = indexes; this.admin = admin; this.cycleUpdater = new CycleCacheUpdater( view, viewNode, admin ); } public ViewUpdater( final Node stopNode, final ViewParams view, final Node viewNode, final ViewIndexes indexes, final GraphAdmin admin ) { this.stopNode = stopNode; this.viewNode = viewNode; this.indexes = indexes; this.admin = admin; this.cycleUpdater = new CycleCacheUpdater( view, viewNode, admin ); } public void cacheRoots( final Set<Node> roots ) { final Transaction tx = admin.beginTransaction(); try { final Index<Node> cachedNodes = indexes.getCachedNodes(); for ( final Node node : roots ) { cachedNodes.add( node, NID, node.getId() ); } tx.success(); } finally { tx.finish(); } } public boolean processAddedRelationships( final Map<Long, ProjectRelationship<?, ?>> createdRelationshipsMap ) { for ( final Entry<Long, ProjectRelationship<?, ?>> entry : createdRelationshipsMap.entrySet() ) { final Long rid = entry.getKey(); final Relationship add = admin.getRelationship( rid ); // TODO: WTF is the point of this?? // indexes.getSelections() // .remove( add ); // final Transaction tx = admin.beginTransaction(); try { logger.debug( "Checking node cache for: {}", add.getStartNode() ); final IndexHits<Node> hits = indexes.getCachedNodes() .get( NID, add.getStartNode() .getId() ); if ( hits.hasNext() ) { Conversions.setMembershipDetectionPending( viewNode, true ); Conversions.setCycleDetectionPending( viewNode, true ); tx.success(); return true; } } finally { tx.finish(); } } return false; } @Override public void includingChild( final Relationship child, final Neo4jGraphPath childPath, final GraphPathInfo childPathInfo, final Path parentPath ) { cachePath( childPath, childPathInfo ); } @Override public void configure( final AtlasCollector<?> collector ) { cycleUpdater.configure( collector ); } private void cachePath( final Neo4jGraphPath path, final GraphPathInfo pathInfo ) { final CyclePath cyclePath = CycleCacheUpdater.getTerminatingCycle( path, admin ); if ( cyclePath != null ) { // logger.info( "CYCLE: {}", cyclePath ); final Relationship injector = admin.getRelationship( path.getLastRelationshipId() ); cycleUpdater.addCycle( cyclePath, injector ); return; } final Transaction tx = admin.beginTransaction(); try { logger.debug( "Caching path: {}", path ); final RelationshipIndex cachedRels = indexes.getCachedRelationships(); final Index<Node> cachedNodes = indexes.getCachedNodes(); final Set<Long> nodes = new HashSet<Long>(); for ( final Long relId : path ) { final Relationship r = admin.getRelationship( relId ); logger.debug( "rel-membership += " + relId ); cachedRels.add( r, RID, relId ); final long startId = r.getStartNode() .getId(); if ( nodes.add( startId ) ) { logger.debug( "node-membership += " + startId ); cachedNodes.add( r.getStartNode(), NID, startId ); } final long endId = r.getEndNode() .getId(); if ( nodes.add( endId ) ) { logger.debug( "node-membership += " + endId ); cachedNodes.add( r.getEndNode(), NID, endId ); } } tx.success(); } finally { tx.finish(); } } @Override public void cycleDetected( final CyclePath path, final Relationship injector ) { cycleUpdater.cycleDetected( path, injector ); } @Override public boolean includeChildren( final Path path, final Neo4jGraphPath graphPath, final GraphPathInfo pathInfo ) { return !( stopNode != null && path.endNode().getId() == stopNode.getId() ); } @Override public void traverseComplete( final AtlasCollector<?> collector ) { if ( stopNode == null ) { final Transaction tx = admin.beginTransaction(); try { // we did a complete traversal. Conversions.setMembershipDetectionPending( viewNode, false ); tx.success(); } finally { tx.finish(); } cycleUpdater.traverseComplete( collector ); } } }