/**
* 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;
import org.commonjava.cartographer.graph.filter.ProjectRelationshipFilter;
import org.commonjava.maven.atlas.graph.model.EProjectCycle;
import org.commonjava.maven.atlas.graph.model.EProjectDirectRelationships;
import org.commonjava.cartographer.graph.model.GraphPath;
import org.commonjava.cartographer.graph.model.GraphPathInfo;
import org.commonjava.cartographer.graph.mutate.GraphMutator;
import org.commonjava.maven.atlas.graph.rel.ProjectRelationship;
import org.commonjava.maven.atlas.graph.rel.RelationshipType;
import org.commonjava.cartographer.graph.spi.RelationshipGraphConnection;
import org.commonjava.cartographer.graph.spi.RelationshipGraphConnectionException;
import org.commonjava.cartographer.graph.traverse.RelationshipGraphTraversal;
import org.commonjava.cartographer.graph.traverse.TraversalType;
import org.commonjava.maven.atlas.ident.ref.ProjectRef;
import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import static org.apache.commons.lang.StringUtils.join;
public final class RelationshipGraph
implements Closeable
{
private final Logger logger = LoggerFactory.getLogger( getClass() );
private static final String GROUP_ID = "groupId";
private static final String ARTIFACT_ID = "artifactId";
private static final String VERSION = "version";
private List<RelationshipGraphListener> listeners;
private final ViewParams params;
private RelationshipGraphConnection connection;
// if we didn't have a user, we wouldn't have constructed this thing!
private int userCount = 1;
RelationshipGraph( final ViewParams params, final RelationshipGraphConnection driver )
{
this.params = params;
this.connection = driver;
getConnectionInternal().registerView( params );
}
public ViewParams getParams()
{
return params;
}
public void storeProjectError( final ProjectVersionRef ref, final Throwable error )
throws RelationshipGraphException
{
getConnectionInternal().addProjectError( ref, String.format( "%s\n%s", error.getMessage(),
join( error.getStackTrace(), "\n " ) ) );
for ( final RelationshipGraphListener listener : listeners )
{
listener.projectError( this, ref, error );
}
}
public String getProjectError( final ProjectVersionRef ref )
{
return getConnectionInternal().getProjectError( ref );
}
public boolean hasProjectError( final ProjectVersionRef ref )
{
return getConnectionInternal().hasProjectError( ref );
}
public void clearProjectError( final ProjectVersionRef ref )
throws RelationshipGraphException
{
getConnectionInternal().clearProjectError( ref );
}
public Set<ProjectRelationship<?, ?>> storeRelationships( final ProjectRelationship<?, ?>... relationships )
throws RelationshipGraphException
{
final List<ProjectRelationship<?, ?>> rels = Arrays.asList( relationships );
for ( final RelationshipGraphListener listener : listeners )
{
listener.storing( this, rels );
}
final Set<ProjectRelationship<?, ?>> rejected = getConnectionInternal().addRelationships( relationships );
for ( final RelationshipGraphListener listener : listeners )
{
listener.stored( this, rels, rejected );
}
return rejected;
}
public Set<ProjectRelationship<?, ?>> storeRelationships(
final Collection<? extends ProjectRelationship<?, ?>> relationships )
throws RelationshipGraphException
{
for ( final RelationshipGraphListener listener : listeners )
{
listener.storing( this, relationships );
}
final Set<ProjectRelationship<?, ?>> rejected = getConnectionInternal().addRelationships(
relationships.toArray( new ProjectRelationship<?, ?>[relationships.size()] ) );
for ( final RelationshipGraphListener listener : listeners )
{
listener.stored( this, relationships, rejected );
}
return rejected;
}
public void addListener( final RelationshipGraphListener listener )
{
if ( listeners == null )
{
listeners = new ArrayList<RelationshipGraphListener>();
}
listeners.add( listener );
}
public void removeListener( final RelationshipGraphListener listener )
{
if ( listeners != null )
{
listeners.remove( listener );
}
}
synchronized void incrementGraphOwnership()
{
userCount++;
logger.info( "User count incremented to: {} for: {}", userCount, params );
}
public synchronized void forceClose()
throws RelationshipGraphException
{
logger.info( "Closing: {}", params );
if ( listeners != null )
{
for ( final RelationshipGraphListener listener : listeners )
{
listener.closing( this );
}
}
connection = null;
if ( listeners != null )
{
for ( final RelationshipGraphListener listener : listeners )
{
listener.closed( this );
}
}
}
@Override
public synchronized void close()
throws IOException
{
userCount--;
logger.info( "User count decremented to: {} for: {}", userCount, params );
if ( userCount < 1 )
{
try
{
forceClose();
}
catch ( final RelationshipGraphException e )
{
throw new IOException( "Failed to close graph.", e );
}
}
else
{
logger.info( "NOT closing; there are other users registered!" );
}
}
// +++ IMPORTED FROM EProjectWeb...
public Set<ProjectRelationship<?, ?>> getAllRelationships()
{
return new HashSet<ProjectRelationship<?, ?>>( getConnectionInternal().getAllRelationships( params ) );
}
public boolean isComplete()
{
return !getConnectionInternal().hasMissingProjects( params );
}
public boolean isConcrete()
{
return !getConnectionInternal().hasVariableProjects( params );
}
public Set<ProjectVersionRef> getIncompleteSubgraphs()
{
return Collections.unmodifiableSet( getConnectionInternal().getMissingProjects( params ) );
}
public Set<ProjectVersionRef> getVariableSubgraphs()
{
return Collections.unmodifiableSet( getConnectionInternal().getVariableProjects( params ) );
}
public Set<ProjectRelationship<?, ?>> add( final EProjectDirectRelationships rels )
throws RelationshipGraphException
{
return addAll( rels.getAllRelationships() );
}
public boolean add( final ProjectRelationship<?, ?> rel )
throws RelationshipGraphException
{
if ( rel == null )
{
return false;
}
return getConnectionInternal().addRelationships( rel ).isEmpty();
}
public <T extends ProjectRelationship<?, ?>> Set<T> addAll( final Collection<T> rels )
throws RelationshipGraphException
{
if ( rels == null )
{
return null;
}
final Set<T> result = new HashSet<T>( rels );
final Set<ProjectRelationship<?, ?>> rejected =
getConnectionInternal().addRelationships( rels.toArray( new ProjectRelationship<?, ?>[rels.size()] ) );
result.removeAll( rejected );
if ( !result.isEmpty() )
{
getConnectionInternal().recomputeIncompleteSubgraphs();
}
return result;
}
public <T extends ProjectRelationship<?, ?>> Set<T> addAll( final T... rels )
throws RelationshipGraphException
{
if ( rels == null )
{
return null;
}
final Set<T> result = new HashSet<T>();
for ( final T rel : rels )
{
if ( add( rel ) )
{
result.add( rel );
}
}
getConnectionInternal().recomputeIncompleteSubgraphs();
return result;
}
public void traverse( final ProjectVersionRef start, final RelationshipGraphTraversal traversal,
final TraversalType type )
throws RelationshipGraphException
{
getConnectionInternal().traverse( traversal, start, this, type );
}
public void traverse( final ProjectVersionRef start, final RelationshipGraphTraversal traversal )
throws RelationshipGraphException
{
traverse( start, traversal, TraversalType.breadth_first );
}
public void traverse( final RelationshipGraphTraversal traversal, final TraversalType type )
throws RelationshipGraphException
{
for ( final ProjectVersionRef root : params.getRoots() )
{
getConnectionInternal().traverse( traversal, root, this, type );
}
}
public void traverse( final RelationshipGraphTraversal traversal )
throws RelationshipGraphException
{
traverse( traversal, TraversalType.breadth_first );
}
public Set<ProjectRelationship<?, ?>> getUserRelationships( final ProjectVersionRef ref )
{
if ( !getConnectionInternal().containsProject( params, ref ) )
{
return Collections.emptySet();
}
return new HashSet<ProjectRelationship<?, ?>>( getConnectionInternal().getRelationshipsTargeting( params, ref ) );
}
public Set<ProjectRelationship<?, ?>> getDirectRelationships( final ProjectVersionRef ref )
{
if ( !getConnectionInternal().containsProject( params, ref ) )
{
return Collections.emptySet();
}
return new HashSet<ProjectRelationship<?, ?>>( getConnectionInternal().getRelationshipsDeclaredBy( params, ref ) );
}
public Set<ProjectVersionRef> getRoots()
{
return params.getRoots();
}
public Set<ProjectRelationship<?, ?>> getExactAllRelationships()
{
return getAllRelationships();
}
public boolean isCycleParticipant( final ProjectVersionRef ref )
{
return getConnectionInternal().isCycleParticipant( params, ref );
}
public boolean isCycleParticipant( final ProjectRelationship<?, ?> rel )
{
return getConnectionInternal().isCycleParticipant( params, rel );
}
public void addCycle( final EProjectCycle cycle )
throws RelationshipGraphException
{
getConnectionInternal().addCycle( cycle );
}
public Set<EProjectCycle> getCycles()
{
return getConnectionInternal().getCycles( params );
}
public Set<ProjectRelationship<?, ?>> getRelationshipsTargeting( final ProjectVersionRef ref )
{
final Collection<? extends ProjectRelationship<?, ?>> rels =
getConnectionInternal().getRelationshipsTargeting( params, ref.asProjectVersionRef() );
if ( rels == null )
{
return Collections.emptySet();
}
return new HashSet<ProjectRelationship<?, ?>>( rels );
}
public RelationshipGraphConnection getDatabase()
{
return connection;
}
public Set<ProjectVersionRef> getAllProjects()
{
return getConnectionInternal().getAllProjects( params );
}
public void addMetadata( final ProjectVersionRef ref, final String name, final String value )
throws RelationshipGraphException
{
getConnectionInternal().addMetadata( ref, name, value );
}
public void addMetadata( final ProjectVersionRef ref, final Map<String, String> metadata )
throws RelationshipGraphException
{
getConnectionInternal().setMetadata( ref, metadata );
}
public Set<ProjectVersionRef> getProjectsWithMetadata( final String key )
{
return getConnectionInternal().getProjectsWithMetadata( params, key );
}
public void reindex()
throws RelationshipGraphException
{
getConnectionInternal().reindex();
}
public void reindex( final ProjectVersionRef ref )
throws RelationshipGraphConnectionException
{
getConnectionInternal().reindex( ref );
}
public Set<List<ProjectRelationship<?, ?>>> getPathsTo( final ProjectVersionRef... projectVersionRefs )
{
return getConnectionInternal().getAllPathsTo( params, projectVersionRefs );
}
public boolean introducesCycle( final ProjectRelationship<?, ?> rel )
{
return getConnectionInternal().introducesCycle( params, rel );
}
public void addDisconnectedProject( final ProjectVersionRef ref )
throws RelationshipGraphException
{
getConnectionInternal().addDisconnectedProject( ref );
}
public boolean isMissing( final ProjectVersionRef ref )
{
return getConnectionInternal().isMissing( params, ref );
}
public Set<URI> getSources()
{
return params.getActiveSources();
// final Set<ProjectRelationship<?>> rels = getAllRelationships();
// final Set<URI> sources = new HashSet<URI>();
// for ( final ProjectRelationship<?> rel : rels )
// {
// sources.addAll( rel.getSources() );
// }
//
// return sources;
}
public GraphMutator getMutator()
{
return params.getMutator();
}
public ProjectRelationshipFilter getFilter()
{
return params.getFilter();
}
public ViewParams addActivePomLocation( final URI location )
{
return params.addActivePomLocation( location );
}
public ViewParams addActivePomLocations( final Collection<URI> locations )
{
return params.addActivePomLocations( locations );
}
public ViewParams addActivePomLocations( final URI... locations )
{
return params.addActivePomLocations( locations );
}
public ViewParams addActiveSources( final Collection<URI> sources )
{
return params.addActiveSources( sources );
}
public ViewParams addActiveSources( final URI... sources )
{
return params.addActiveSources( sources );
}
public ViewParams addActiveSource( final URI source )
{
return params.addActiveSource( source );
}
public long getLastAccess()
{
return params.getLastAccess();
}
public String setProperty( final String key, final String value )
{
return params.setProperty( key, value );
}
public String getProperty( final String key )
{
return params.getProperty( key );
}
public String getProperty( final String key, final String def )
{
return params.getProperty( key, def );
}
public final String getWorkspaceId()
{
return params.getWorkspaceId();
}
public final Set<URI> getActivePomLocations()
{
return params.getActivePomLocations();
}
public final Set<URI> getActiveSources()
{
return params.getActiveSources();
}
public final ProjectVersionRef getSelection( final ProjectRef ref )
{
return params.getSelection( ref );
}
// --- IMPORTED FROM EProjectWeb
// +++ IMPORTED FROM EGraphManager
public boolean containsGraph( final ProjectVersionRef ref )
{
return getConnectionInternal().containsProject( params, ref );
}
public Set<ProjectRelationship<?, ?>> findDirectRelationshipsTo( final ProjectVersionRef to,
final boolean includeManagedInfo,
final RelationshipType... types )
{
return getConnectionInternal().getDirectRelationshipsTo( params, to, includeManagedInfo, true, types );
}
public Set<ProjectRelationship<?, ?>> findDirectRelationshipsTo( final ProjectVersionRef to,
final boolean includeManagedInfo,
final boolean includeConcreteInfo,
final RelationshipType... types )
{
return getConnectionInternal().getDirectRelationshipsTo( params, to, includeManagedInfo, includeConcreteInfo,
types );
}
public Set<ProjectRelationship<?, ?>> findDirectRelationshipsFrom( final ProjectVersionRef source,
final boolean managed,
final RelationshipType... types )
{
return getConnectionInternal().getDirectRelationshipsFrom( params, source, managed, true, types );
}
public Set<ProjectRelationship<?, ?>> findDirectRelationshipsFrom( final ProjectVersionRef source,
final boolean managed, final boolean concrete,
final RelationshipType... types )
{
return getConnectionInternal().getDirectRelationshipsFrom( params, source, managed, concrete, types );
}
public Set<ProjectVersionRef> getAllIncompleteSubgraphs()
{
return getConnectionInternal().getMissingProjects( params );
}
public Set<ProjectVersionRef> getAllVariableSubgraphs()
{
return getConnectionInternal().getVariableProjects( params );
}
public Map<String, String> getMetadata( final ProjectVersionRef ref )
{
final Map<String, String> result = new HashMap<String, String>();
final Map<String, String> metadata = getConnectionInternal().getMetadata( ref );
if ( metadata != null )
{
result.putAll( metadata );
}
result.put( GROUP_ID, ref.getGroupId() );
result.put( ARTIFACT_ID, ref.getArtifactId() );
result.put( VERSION, ref.getVersionString() );
return result;
}
public Map<String, String> getMetadata( final ProjectVersionRef ref, final Set<String> keys )
{
final Map<String, String> result = new HashMap<String, String>();
final Map<String, String> metadata = getConnectionInternal().getMetadata( ref, keys );
if ( metadata != null )
{
result.putAll( metadata );
}
if ( keys.contains( GROUP_ID ) )
{
result.put( GROUP_ID, ref.getGroupId() );
}
if ( keys.contains( ARTIFACT_ID ) )
{
result.put( ARTIFACT_ID, ref.getArtifactId() );
}
if ( keys.contains( VERSION ) )
{
result.put( VERSION, ref.getVersionString() );
}
return result;
}
public Map<Map<String, String>, Set<ProjectVersionRef>> collateByMetadata( final Set<ProjectVersionRef> refs,
final Set<String> keys )
{
final Map<Map<String, String>, Set<ProjectVersionRef>> result =
new HashMap<Map<String, String>, Set<ProjectVersionRef>>();
for ( final ProjectVersionRef ref : refs )
{
final Map<String, String> metadata = getMetadata( ref, keys );
Set<ProjectVersionRef> collated = result.get( metadata );
if ( collated == null )
{
collated = new HashSet<ProjectVersionRef>();
result.put( metadata, collated );
}
collated.add( ref );
}
return result;
}
public void setMetadata( final ProjectVersionRef project, final Map<String, String> metadata )
throws RelationshipGraphException
{
getConnectionInternal().setMetadata( project, metadata );
}
public void deleteRelationshipsDeclaredBy( final ProjectVersionRef ref )
throws RelationshipGraphException
{
getConnectionInternal().deleteRelationshipsDeclaredBy( ref );
}
public Set<ProjectVersionRef> getProjectsMatching( final ProjectRef projectRef )
{
return getConnectionInternal().getProjectsMatching( params, projectRef );
}
public Map<GraphPath<?>, GraphPathInfo> getPathMapTargeting( final Set<ProjectVersionRef> refs )
{
return getConnectionInternal().getPathMapTargeting( params, refs );
}
public ProjectVersionRef getPathTargetRef( final GraphPath<?> path )
{
return getConnectionInternal().getPathTargetRef( path );
}
public GraphPath<?> createPath( final GraphPath<?> parentPath, final ProjectRelationship<?, ?> relationship )
{
return getConnectionInternal().createPath( parentPath, relationship );
}
public GraphPath<?> createPath( final ProjectRelationship<?, ?>... relationships )
{
return getConnectionInternal().createPath( relationships );
}
public List<ProjectVersionRef> getPathRefs( final GraphPath<?> path )
{
return getConnectionInternal().getPathRefs( params, path );
}
public List<ProjectRelationship<?, ?>> getRelationships( final GraphPath<?> path )
{
return getConnectionInternal().getRelationships( params, path );
}
RelationshipGraphConnection getConnection()
{
return connection;
}
// --- IMPORTED FROM EGraphManager
@Override
public String toString()
{
return "RelationshipGraph [params=" + params + "]";
}
public void printStats()
{
getConnectionInternal().printStats();
}
public Map<ProjectVersionRef, String> getAllProjectErrors()
{
final Map<ProjectVersionRef, String> errors = new HashMap<ProjectVersionRef, String>();
final Set<ProjectVersionRef> projects = getConnectionInternal().getAllProjects( params );
for ( final ProjectVersionRef ref : projects )
{
final String error = getConnectionInternal().getProjectError( ref );
if ( error != null )
{
errors.put( ref, error );
}
}
return errors;
}
public synchronized boolean isOpen()
{
return connection != null;
}
private synchronized RelationshipGraphConnection getConnectionInternal()
{
if ( connection == null )
{
throw new IllegalStateException( "Relationship graph has been closed!" );
}
return connection;
}
public Collection<? extends ProjectRelationship<?, ?>> getRelationshipsDeclaring( final ProjectVersionRef root )
{
return getConnectionInternal().getRelationshipsDeclaredBy( params, root );
}
}