/* package whatever; // don't place package name! */

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

class Ideone
{
    private static final Random RANDOM = new Random();
    public static void main(String[] args) {
/*
    Service A   01          03        1
    Service B   02          03        2
    Service C   03          04        3
    Service D   04          05        4
    Service E   05          07        5
    Service F   06          07        6
    Service G   07          (null)    7*/
        CallManager manager = new CallManager();
        manager.addService(simpleRunnable("A"), "A");
        manager.addService(simpleRunnable("B"), "B");
        manager.addService(simpleRunnable("C"), "C", "A", "B");
        manager.addService(simpleRunnable("D"), "D", "C");
        manager.addService(simpleRunnable("E"), "E", "D");
        manager.addService(simpleRunnable("F"), "F");
        manager.addService(simpleRunnable("G"), "G", "E", "F");

        manager.run();
    }

    // create some simple pseudo service
    private static Runnable simpleRunnable(String s) {
        return () -> {
            System.out.printf("running service %s%n", s);
            try {
                Thread.sleep(RANDOM.nextInt(2000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
    }
}

class CallManager {
    List<Service> services = new ArrayList<>();
    ExecutorService executorService = Executors.newFixedThreadPool(4);

    void addService(Runnable r, String serviceName, String... preconditions) {
        services.add(new Service(r, serviceName, this::preconditionFulfilled, preconditions));
    }

    void run() {
        for (Iterator<Service> serviceIterator = services.iterator(); serviceIterator.hasNext(); ) {
            Service service = serviceIterator.next();
            if (service.arePreconditionsFulfilled()) {
                executorService.submit(service);
                serviceIterator.remove();
            }
            if (services.isEmpty()) {
                executorService.shutdown();
            }
        }
    }

    private synchronized void preconditionFulfilled(String name) {
        System.out.printf("service %s finished%n", name);
        for (Iterator<Service> serviceIterator = services.iterator(); serviceIterator.hasNext(); ) {
            Service service = serviceIterator.next();
            service.preconditionFulfilled(name);
            if (service.arePreconditionsFulfilled()) {
                executorService.submit(service);
                serviceIterator.remove();
            }
        }
        if (services.isEmpty()) {
            executorService.shutdown();
        }
    }
}

class Service implements Runnable {

    private final Runnable wrappedRunnable;
    private final String name;
    private final List<String> preconditions = new CopyOnWriteArrayList<>();
    private final Consumer<String> finishedNotification;

    Service(Runnable r, String name, Consumer<String> finishedNotification, String... preconditions) {
        this.wrappedRunnable = r;
        this.name = name;
        this.finishedNotification = finishedNotification;
        this.preconditions.addAll(Arrays.asList(preconditions));
    }

    @Override
    public void run() {
        wrappedRunnable.run();
        finishedNotification.accept(name);
    }

    void preconditionFulfilled(String precondition) {
        preconditions.remove(precondition);
    }
    boolean arePreconditionsFulfilled() {
        return preconditions.isEmpty();
    }
}