com.graphaware.importer.plan.DefaultExecutionPlan Maven / Gradle / Ivy
/*
* Copyright (c) 2013-2016 GraphAware
*
* This file is part of the GraphAware Framework.
*
* GraphAware Framework is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the
* GNU General Public License along with this program. If not, see .
*/
package com.graphaware.importer.plan;
import com.graphaware.importer.cache.Caches;
import com.graphaware.importer.context.ImportContext;
import com.graphaware.importer.importer.Importer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* Default implementation of {@link com.graphaware.importer.plan.ExecutionPlan}. Makes sure that as many {@link com.graphaware.importer.importer.Importer}s
* as possible are run in parallel.
*/
public class DefaultExecutionPlan implements ExecutionPlan {
private static final Logger LOG = LoggerFactory.getLogger(DefaultExecutionPlan.class);
private final Caches caches;
private final Set importers;
private final Map> neededCaches;
private final Map cacheCreators;
private List orderedImporters;
/**
* Create a new plan.
*
* @param importers all importers.
* @param importContext import context.
*/
public DefaultExecutionPlan(Set importers, ImportContext importContext) {
if (importers.isEmpty()) {
throw new IllegalStateException("There are no importers");
}
this.importers = importers;
this.caches = importContext.caches();
this.neededCaches = neededCaches();
this.cacheCreators = cacheCreators();
}
/**
* {@inheritDoc}
*/
@Override
public final List getOrderedImporters() {
if (orderedImporters == null) {
synchronized (this) {
if (orderedImporters == null) {
orderedImporters = order(importers);
}
}
}
return orderedImporters;
}
/**
* Order the importers by looking at what caches they create, what caches they need, and how they thus depend on
* each other. The order of two importers that do not depend on each other is undefined.
*
* @param importers to order.
* @return ordered importers.
*/
protected List order(Set importers) {
Set remaining = new HashSet<>(importers);
List result = new LinkedList<>();
while (!remaining.isEmpty()) {
Importer next = findNext(remaining, neededCaches, cacheCreators);
if (next == null) {
reportCycle(remaining);
}
result.add(next);
remaining.remove(next);
}
return result;
}
/**
* Report a dependency cycle in the modules by throwing an {@link java.lang.IllegalStateException}.
*
* @param remaining remaining modules, some of which form a cycle.
*/
protected void reportCycle(Set remaining) {
StringBuilder message = new StringBuilder("It looks like there is a dependency cycle between some of the following importers: ");
for (Importer importer : remaining) {
message.append(importer.name()).append(", ");
}
String msg = message.toString().substring(0, message.length() - 2);
LOG.error(msg);
throw new IllegalStateException(msg);
}
/**
* Find the next importer from the set of remaining importers that has no dependency on any remaining importer.
*
* @param remaining remaining importers.
* @param neededCaches the full map of importers and their needed caches.
* @param cacheCreators a map of cache to its creator.
* @return next importer with no dependencies, null
if none exists.
*/
private Importer findNext(Set remaining, Map> neededCaches, Map cacheCreators) {
for (Importer candidate : remaining) {
if (noRemainingCreators(remaining, cacheCreators, candidate, neededCaches.get(candidate))) {
return candidate;
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public final boolean allFinished() {
for (Importer importer : importers) {
if (hasNotFinished(importer)) {
return false;
}
}
LOG.info("All importers finished.");
return true;
}
/**
* {@inheritDoc}
*/
@Override
public final boolean canRun(Importer importer) {
if (importer.getState() != Importer.State.NOT_STARTED) {
throw new IllegalStateException("Importer " + importer.name() + " is already " + importer.getState() + "!");
}
Set remaining = new HashSet<>();
for (Importer candidate : importers) {
if (hasNotFinished(candidate)) {
remaining.add(candidate);
}
}
boolean result = canRun(importer, remaining, neededCaches, cacheCreators);
if (result) {
LOG.info("Importer " + importer.name() + " CAN run now.");
} else {
LOG.info("Importer " + importer.name() + " CANNOT run yet.");
}
return result;
}
/**
* Check if the given importer can run now, i.e., that there are no unfinished importers that the given one depends on,
* and the given one hasn't started yet.
*
* @param importer to check.
* @param notFinished set of all unfinished importers.
* @param neededCaches the full map of importers and their needed caches.
* @param cacheCreators a map of cache to its creator.
* @return true
iff there are no dependencies.
*/
private boolean canRun(Importer importer, Set notFinished, Map> neededCaches, Map cacheCreators) {
return noRemainingCreators(notFinished, cacheCreators, importer, neededCaches.get(importer)) && importer.getState() == Importer.State.NOT_STARTED;
}
/**
* {@inheritDoc}
*/
@Override
public final void clearCaches() {
Collection remaining = new HashSet<>();
for (Importer importer : importers) {
if (hasNotFinished(importer)) {
remaining.add(importer);
}
}
caches.cleanup(remaining);
}
/**
* Check if there are no remaining (unfinished) importers that create at least one of the caches needed for the given importer.
*
* @param remaining remaining (unfinished) importers.
* @param cacheCreators a map of cache to its creator.
* @param candidate to check.
* @param needed needed caches of the candidate.
* @return true
iff there are no creators of needed caches in the remaining importers (apart from the candidate itself).
* @throws java.lang.IllegalStateException if there is a cache that has no creator.
*/
private boolean noRemainingCreators(Set remaining, Map cacheCreators, Importer candidate, Set needed) {
for (String neededCache : needed) {
Importer creator = cacheCreators.get(neededCache);
if (creator == null) {
throw new IllegalStateException("No creator defined for cache " + neededCache);
}
if (!creator.equals(candidate) && remaining.contains(creator)) {
return false;
}
}
return true;
}
/**
* Create a map of importers and all the caches they need.
*
* @return map.
*/
private Map> neededCaches() {
Map> result = new HashMap<>();
for (Importer importer : importers) {
result.put(importer, caches.neededCaches(importer));
}
return result;
}
/**
* Create a map of cache names and importers that create them.
*
* @return map.
* @throws java.lang.IllegalStateException if a cache is created by more than one importer.
*/
private Map cacheCreators() {
Map result = new HashMap<>();
for (Importer importer : importers) {
for (String cache : caches.createdCaches(importer)) {
if (result.containsKey(cache)) {
throw new IllegalStateException("Cache " + cache + " is created by more than one importer!");
}
result.put(cache, importer);
}
}
return result;
}
private boolean hasFinished(Importer importer) {
return importer.getState() == Importer.State.FINISHED;
}
private boolean hasNotFinished(Importer importer) {
return !hasFinished(importer);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy