org.commonjava.cartographer.INTERNAL.ops.GraphOpsImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.cartographer.INTERNAL.ops.GraphOpsImpl.java

Source

/**
 * Copyright (C) 2013 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.INTERNAL.ops;

import org.apache.commons.lang.StringUtils;
import org.commonjava.cartographer.CartoDataException;
import org.commonjava.cartographer.CartoRequestException;
import org.commonjava.cartographer.graph.GraphResolver;
import org.commonjava.cartographer.graph.fn.GraphFunction;
import org.commonjava.cartographer.graph.fn.MatchingProjectFunction;
import org.commonjava.cartographer.graph.fn.MultiGraphFunction;
import org.commonjava.cartographer.graph.fn.ProjectCollector;
import org.commonjava.cartographer.graph.fn.ProjectProjector;
import org.commonjava.cartographer.graph.fn.ProjectSelector;
import org.commonjava.cartographer.graph.fn.ValueHolder;
import org.commonjava.cartographer.graph.util.CartoGraphUtils;
import org.commonjava.cartographer.ops.GraphOps;
import org.commonjava.cartographer.request.GraphDescription;
import org.commonjava.cartographer.request.PathsRequest;
import org.commonjava.cartographer.request.ProjectGraphRelationshipsRequest;
import org.commonjava.cartographer.request.ProjectGraphRequest;
import org.commonjava.cartographer.request.SingleGraphRequest;
import org.commonjava.cartographer.result.GraphExport;
import org.commonjava.cartographer.result.MappedProjectRelationships;
import org.commonjava.cartographer.result.MappedProjectRelationshipsResult;
import org.commonjava.cartographer.result.MappedProjectResult;
import org.commonjava.cartographer.result.MappedProjects;
import org.commonjava.cartographer.result.MappedProjectsResult;
import org.commonjava.cartographer.result.ProjectError;
import org.commonjava.cartographer.result.ProjectErrors;
import org.commonjava.cartographer.result.ProjectListResult;
import org.commonjava.cartographer.result.ProjectPath;
import org.commonjava.cartographer.result.ProjectPathsResult;
import org.commonjava.cartographer.graph.RelationshipGraph;
import org.commonjava.cartographer.graph.RelationshipGraphException;
import org.commonjava.cartographer.graph.filter.AnyFilter;
import org.commonjava.cartographer.graph.filter.ParentFilter;
import org.commonjava.cartographer.graph.filter.ProjectRelationshipFilter;
import org.commonjava.maven.atlas.graph.model.EProjectCycle;
import org.commonjava.maven.atlas.graph.rel.ParentRelationship;
import org.commonjava.maven.atlas.graph.rel.ProjectRelationship;
import org.commonjava.cartographer.graph.spi.RelationshipGraphConnectionException;
import org.commonjava.cartographer.graph.spi.neo4j.io.Conversions;
import org.commonjava.cartographer.graph.traverse.BuildOrderTraversal;
import org.commonjava.cartographer.graph.traverse.PathsTraversal;
import org.commonjava.cartographer.graph.traverse.TraversalType;
import org.commonjava.cartographer.graph.traverse.model.BuildOrder;
import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@ApplicationScoped
public class GraphOpsImpl implements GraphOps {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Inject
    private GraphResolver resolver;

    protected GraphOpsImpl() {
    }

    public GraphOpsImpl(final GraphResolver resolver) {
        this.resolver = resolver;
    }

    @Override
    public ProjectListResult listProjects(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final ProjectListResult result = new ProjectListResult();

        final ProjectProjector<ProjectVersionRef> extractor = (ref, graph) -> graph.containsGraph(ref) ? ref : null;

        final ProjectCollector<ProjectVersionRef> consumer = (unused, ref) -> {
            if (ref != null) {
                result.addProject(ref);
            }
        };

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));
        return result;
    }

    @Override
    public ProjectPathsResult getPaths(final PathsRequest recipe) throws CartoDataException, CartoRequestException {
        //        Collections.sort( paths, RelationshipPathComparator.INSTANCE );

        ProjectPathsResult result = new ProjectPathsResult();

        final MultiGraphFunction<Set<ProjectRelationship<?, ?>>> extractor = (allRels, graphMap) -> {
            for (final GraphDescription desc : graphMap.keySet()) {
                final RelationshipGraph graph = graphMap.get(desc);
                final ProjectRelationshipFilter filter = desc.filter();

                final PathsTraversal paths = new PathsTraversal(recipe.buildFilter(filter), recipe.getTargets());
                try {
                    graph.traverse(paths, TraversalType.depth_first);
                } catch (final RelationshipGraphException ex) {
                    throw new CartoDataException(
                            "Failed to open / traverse the graph (for paths operation): " + ex.getMessage(), ex);
                }

                final Set<List<ProjectRelationship<?, ?>>> discoveredPaths = paths.getDiscoveredPaths();

                for (final List<ProjectRelationship<?, ?>> path : discoveredPaths) {
                    if (path == null || path.isEmpty()) {
                        continue;
                    }

                    for (final ProjectRelationship<?, ?> rel : path) {
                        if (!allRels.contains(rel)) {
                            // continue to the next path...
                            break;
                        }
                    }

                    List<ProjectRelationship<?, ?>> detachedPath = Conversions.convertToDetachedRelationships(path);
                    final ProjectVersionRef ref = detachedPath.get(path.size() - 1).getTarget();
                    result.addPath(ref, new ProjectPath(detachedPath));
                }
            }
        };

        resolver.resolveAndExtractMultiGraph(AnyFilter.INSTANCE, recipe,
                (allProjects, allRels, roots) -> allRels.get(), extractor);

        return result;
    }

    @Override
    public ProjectErrors getProjectErrors(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        return getAllProjectErrors(recipe);
    }

    private ProjectErrors getAllProjectErrors(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final ProjectErrors result = new ProjectErrors();

        final ProjectProjector<String> extractor = (ref, graph) -> {
            final String error = graph.getProjectError(ref);
            if (StringUtils.isEmpty(error)) {
                return null;
            }

            return error;
        };

        final ProjectCollector<String> consumer = (ref, error) -> {
            if (error != null) {
                result.addProject(new ProjectError(ref, error));
            }
        };

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));

        return result;
    }

    @Override
    public MappedProjectResult getProjectParent(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        MappedProjectResult result = new MappedProjectResult();

        final ProjectProjector<ProjectVersionRef> extractor = (ref, graph) -> {
            final Set<ProjectRelationship<?, ?>> rels = graph.getDirectRelationships(ref);
            for (final ProjectRelationship<?, ?> rel : rels) {
                if (rel instanceof ParentRelationship) {
                    return rel.getTarget();
                }
            }

            return null;
        };

        final ProjectCollector<ProjectVersionRef> consumer = result::addProject;

        resolver.resolveAndExtractSingleGraph(ParentFilter.EXCLUDE_TERMINAL_PARENTS, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));

        return result;
    }

    @Override
    public MappedProjectRelationshipsResult getDirectRelationshipsFrom(
            final ProjectGraphRelationshipsRequest recipe) throws CartoDataException, CartoRequestException {
        MappedProjectRelationshipsResult result = new MappedProjectRelationshipsResult();

        final ProjectProjector<Set<ProjectRelationship<?, ?>>> extractor = (ref, graph) -> {
            final Set<ProjectRelationship<?, ?>> rels = graph.findDirectRelationshipsFrom(ref,
                    recipe.isManagedIncluded(), recipe.isConcreteIncluded(), recipe.toTypeArray());
            return rels == null || rels.isEmpty() ? null : new HashSet<>(rels);
        };

        final ProjectCollector<Set<ProjectRelationship<?, ?>>> consumer = (ref, rels) -> {
            if (rels != null) {
                result.addProject(new MappedProjectRelationships(ref, rels));
            }
        };

        resolver.resolveAndExtractSingleGraph(recipe.getTypeFilter(), recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));
        return result;
    }

    @Override
    public MappedProjectRelationshipsResult getDirectRelationshipsTo(final ProjectGraphRelationshipsRequest recipe)
            throws CartoDataException, CartoRequestException {
        MappedProjectRelationshipsResult result = new MappedProjectRelationshipsResult();

        final ProjectProjector<Set<ProjectRelationship<?, ?>>> extractor = (ref, graph) -> {
            final Set<ProjectRelationship<?, ?>> rels = graph.findDirectRelationshipsTo(ref,
                    recipe.isManagedIncluded(), recipe.isConcreteIncluded(), recipe.toTypeArray());
            return rels == null || rels.isEmpty() ? null : new HashSet<>(rels);
        };

        final ProjectCollector<Set<ProjectRelationship<?, ?>>> consumer = (ref, rels) -> {
            if (rels != null) {
                result.addProject(new MappedProjectRelationships(ref, rels));
            }
        };

        resolver.resolveAndExtractSingleGraph(recipe.getTypeFilter(), recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));
        return result;
    }

    @Override
    public ProjectListResult reindex(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        return doReindex(recipe);
    }

    private ProjectListResult doReindex(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        recipe.setResolve(false);
        if (recipe.getGraph().filter() == null) {
            recipe.getGraph().setFilter(AnyFilter.INSTANCE);
        }

        final ProjectListResult result = new ProjectListResult();
        final ProjectProjector<ProjectVersionRef> extractor = (ref, graph) -> {
            try {
                graph.reindex(ref);
                return ref;
            } catch (final RelationshipGraphConnectionException e) {
                logger.error(String.format("Failed to re-index %s in: %s", ref, recipe.getWorkspaceId()), e);
            }

            return null;
        };

        final ProjectCollector<ProjectVersionRef> consumer = (unused, ref) -> {
            if (ref != null) {
                result.addProject(ref);
            }
        };

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));

        return result;
    }

    @Override
    public ProjectListResult getIncomplete(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final ProjectListResult result = new ProjectListResult();
        final ProjectProjector<ProjectVersionRef> extractor = (ref, graph) -> ref;

        final ProjectCollector<ProjectVersionRef> consumer = (unused, ref) -> result.addProject(ref);

        final ProjectSelector supplier = RelationshipGraph::getIncompleteSubgraphs;

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer, supplier));
        return result;
    }

    @Override
    public ProjectListResult getVariable(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final ProjectListResult result = new ProjectListResult();
        final ProjectProjector<ProjectVersionRef> extractor = (ref, graph) -> ref;

        final ProjectCollector<ProjectVersionRef> consumer = (unused, ref) -> result.addProject(ref);

        final ProjectSelector supplier = RelationshipGraph::getVariableSubgraphs;

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer, supplier));
        return result;
    }

    @Override
    public MappedProjectsResult getAncestry(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final MappedProjectsResult result = new MappedProjectsResult();
        final ProjectProjector<List<ProjectVersionRef>> extractor = (ref, graph) -> {
            try {
                return CartoGraphUtils.getAncestry(ref, graph);
            } catch (final RelationshipGraphException e) {
                logger.error(
                        String.format("Failed to retrieve ancestry of: %s in: %s", ref, recipe.getWorkspaceId()),
                        e);
            }

            return Collections.emptyList();
        };

        final ProjectCollector<List<ProjectVersionRef>> consumer = (ref, mapped) -> result
                .addProject(new MappedProjects(ref, mapped));

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer));

        return result;
    }

    @Override
    public BuildOrder getBuildOrder(final ProjectGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final BuildOrderTraversal traversal = new BuildOrderTraversal();
        final ProjectProjector<ProjectVersionRef> extractor = (ref, graph) -> {
            try {
                graph.traverse(ref, traversal, TraversalType.breadth_first);
                return ref;
            } catch (final RelationshipGraphException e) {
                logger.error(String.format("Failed to traverse graph: %s to discover build order for: %s",
                        recipe.getWorkspaceId(), ref), e);
            }

            return null;
        };

        final ProjectCollector<ProjectVersionRef> consumer = (ref, ref2) -> {
        };

        final ProjectSelector supplier = RelationshipGraph::getRoots;

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe,
                new MatchingProjectFunction<>(recipe, extractor, consumer, supplier));
        return traversal.getBuildOrder();
    }

    @Override
    public GraphExport exportGraph(final SingleGraphRequest recipe)
            throws CartoDataException, CartoRequestException {
        final ValueHolder<GraphExport> holder = new ValueHolder<>();
        final GraphFunction extractor = (graph) -> {
            final Set<ProjectRelationship<?, ?>> rels = graph.getAllRelationships();
            final Set<ProjectVersionRef> missing = graph.getAllIncompleteSubgraphs();

            if (missing != null && missing.containsAll(recipe.getGraph().getRoots())) {
                holder.consumer().accept(null);
            }

            final Set<ProjectVersionRef> variable = graph.getAllVariableSubgraphs();
            final Set<EProjectCycle> cycles = graph.getCycles();

            final Map<ProjectVersionRef, String> errorMap = graph.getAllProjectErrors();
            ProjectErrors errors = new ProjectErrors();
            for (ProjectVersionRef key : errorMap.keySet()) {
                errors.addProject(new ProjectError(key, errorMap.get(key)));
            }

            holder.consumer().accept(new GraphExport(rels, missing, variable, errors, cycles));
        };

        resolver.resolveAndExtractSingleGraph(AnyFilter.INSTANCE, recipe, extractor);
        return holder.get();
    }

}