/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or authors. * * 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 io.janusproject.services; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import com.google.common.util.concurrent.Service; import com.google.common.util.concurrent.Service.State; import com.google.common.util.concurrent.ServiceManager; import io.janusproject.services.infrastructure.InfrastructureService; import io.janusproject.util.Comparators; /** * Tools for launching and stopping services. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public final class Services { private Services() { // } /** * Start the services associated to the given service manager. * * <p>This starting function supports the {@link DependentService prioritized services}. * * @param manager - the manager of the services to start. */ public static void startServices(ServiceManager manager) { startServices(new GoogleServiceManager(manager)); } /** * Start the services associated to the given service manager. * * <p>This starting function supports the {@link DependentService prioritized services}. * * @param manager - the manager of the services to start. */ public static void startServices(IServiceManager manager) { final List<Service> otherServices = new ArrayList<>(); final List<Service> infraServices = new ArrayList<>(); final LinkedList<DependencyNode> serviceQueue = new LinkedList<>(); final Accessors accessors = new StartingPhaseAccessors(); // Build the dependency graph buildDependencyGraph(manager, serviceQueue, infraServices, otherServices, accessors); // Launch the services runDependencyGraph(serviceQueue, infraServices, otherServices, accessors); manager.awaitHealthy(); } /** * Stop the services associated to the given service manager. * * <p>This stopping function supports the {@link DependentService prioritized services}. * * @param manager - the manager of the services to stop. */ public static void stopServices(ServiceManager manager) { stopServices(new GoogleServiceManager(manager)); } /** * Stop the services associated to the given service manager. * * <p>This stopping function supports the {@link DependentService prioritized services}. * * @param manager - the manager of the services to stop. */ public static void stopServices(IServiceManager manager) { final List<Service> otherServices = new ArrayList<>(); final List<Service> infraServices = new ArrayList<>(); final LinkedList<DependencyNode> serviceQueue = new LinkedList<>(); final Accessors accessors = new StoppingPhaseAccessors(); // Build the dependency graph buildInvertedDependencyGraph(manager, serviceQueue, infraServices, otherServices, accessors); // Launch the services runDependencyGraph(serviceQueue, infraServices, otherServices, accessors); manager.awaitStopped(); } private static void addNodeIntoDependencyGraph(DependentService depServ, Map<Class<? extends Service>, DependencyNode> dependentServices, List<DependencyNode> roots) { final Class<? extends Service> type = depServ.getServiceType(); assert type != null; assert type.isInterface() : type.getName(); DependencyNode node = dependentServices.get(type); if (node == null) { node = new DependencyNode(depServ, type); dependentServices.put(type, node); } else { assert node.getService() == null; node.setService(depServ); } boolean isRoot = true; Collection<Class<? extends Service>> deps = depServ.getServiceDependencies(); for (final Class<? extends Service> dep : deps) { isRoot = false; DependencyNode depNode = dependentServices.get(dep); if (depNode == null) { depNode = new DependencyNode(dep); dependentServices.put(dep, depNode); } depNode.getNextServices().add(node); } deps = depServ.getServiceWeakDependencies(); for (final Class<? extends Service> dep : deps) { isRoot = false; DependencyNode depNode = dependentServices.get(dep); if (depNode == null) { depNode = new DependencyNode(dep); dependentServices.put(dep, depNode); } depNode.getNextWeakServices().add(node); } if (isRoot) { roots.add(node); } } /** * Build the dependency graph for the services. * * @param manager - lsit of the services. * @param roots - filled with the services that have no dependency. * @param infraServices - filled with the infrastructure services. * @param freeServices - filled with the services that are executed before/after all the dependent services. * @param accessors - permits to retreive information on the services. */ private static void buildDependencyGraph(IServiceManager manager, List<DependencyNode> roots, List<Service> infraServices, List<Service> freeServices, Accessors accessors) { final Map<Class<? extends Service>, DependencyNode> dependentServices = new TreeMap<>(Comparators.CLASS_COMPARATOR); Service service; for (final Entry<State, Service> entry : manager.servicesByState().entries()) { if (accessors.matches(entry.getKey())) { service = entry.getValue(); if (service instanceof InfrastructureService) { infraServices.add(service); } else if (service instanceof DependentService) { addNodeIntoDependencyGraph((DependentService) service, dependentServices, roots); } else { freeServices.add(service); } } } if (accessors.isAsyncStateWaitingEnabled()) { for (final DependencyNode node : dependentServices.values()) { assert node.getService() != null; if (node.getService() instanceof AsyncStateService) { for (final DependencyNode next : node.getNextServices()) { next.getAsyncStateServices().add(new WeakReference<>(node)); } } } } } /** * Build the dependency graph for the services. * * @param manager - lsit of the services. * @param roots - filled with the services that have no dependency. * @param infraServices - filled with the infrastructure services. * @param freeServices - filled with the services that are executed before/after all the dependent services. * @param accessors - permits to retreive information on the services. */ private static void buildInvertedDependencyGraph(IServiceManager manager, List<DependencyNode> roots, List<Service> infraServices, List<Service> freeServices, Accessors accessors) { final Map<Class<? extends Service>, DependencyNode> dependentServices = new TreeMap<>(Comparators.CLASS_COMPARATOR); final Map<Class<? extends Service>, DependencyNode> rootServices = new TreeMap<>(Comparators.CLASS_COMPARATOR); Service service; for (final Entry<State, Service> entry : manager.servicesByState().entries()) { if (accessors.matches(entry.getKey())) { service = entry.getValue(); if (service instanceof InfrastructureService) { infraServices.add(service); } else if (service instanceof DependentService) { final DependentService depServ = (DependentService) service; final Class<? extends Service> type = depServ.getServiceType(); DependencyNode node = dependentServices.get(type); boolean isRoot = true; if (node == null) { node = new DependencyNode(depServ, type); dependentServices.put(type, node); } else { assert node.getService() == null; node.setService(depServ); isRoot = false; } Collection<Class<? extends Service>> deps = depServ.getServiceDependencies(); for (final Class<? extends Service> dep : deps) { DependencyNode depNode = dependentServices.get(dep); if (depNode == null) { depNode = new DependencyNode(dep); dependentServices.put(dep, depNode); } node.getNextServices().add(depNode); rootServices.remove(depNode.getType()); } deps = depServ.getServiceWeakDependencies(); for (final Class<? extends Service> dep : deps) { DependencyNode depNode = dependentServices.get(dep); if (depNode == null) { depNode = new DependencyNode(dep); dependentServices.put(dep, depNode); } node.getNextWeakServices().add(depNode); rootServices.remove(depNode.getType()); } if (isRoot) { rootServices.put(type, node); } } else { freeServices.add(service); } } } roots.addAll(rootServices.values()); } /** * Run the dependency graph for the services. * * @param roots - filled with the services that have no dependency. * @param infraServices - filled with the infrastructure services. * @param freeServices - filled with the services that are executed before/after all the dependent services. * @param accessors - permits to retreive information on the services. */ @SuppressWarnings("checkstyle:npathcomplexity") private static void runDependencyGraph(Queue<DependencyNode> roots, List<Service> infraServices, List<Service> freeServices, Accessors accessors) { final boolean async = accessors.isAsyncStateWaitingEnabled(); final Set<Class<? extends Service>> executed = new TreeSet<>(Comparators.CLASS_COMPARATOR); accessors.runInfrastructureServicesBefore(infraServices); accessors.runFreeServicesBefore(freeServices); while (!roots.isEmpty()) { final DependencyNode node = roots.remove(); assert node != null && node.getType() != null; if (!executed.contains(node.getType())) { executed.add(node.getType()); roots.addAll(node.getNextServices()); roots.addAll(node.getNextWeakServices()); assert node.getService() != null; if (async) { for (final WeakReference<DependencyNode> asyncService : node.getAsyncStateServices()) { final AsyncStateService as = (AsyncStateService) (asyncService.get().getService()); assert as != null; while (!as.isReadyForOtherServices()) { Thread.yield(); } } } accessors.run(node.getService()); } } accessors.runFreeServicesAfter(freeServices); accessors.runInfrastructureServicesAfter(infraServices); } /** * Node that describes a service dependency. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class DependencyNode { private Service service; private final Class<? extends Service> type; private final Collection<DependencyNode> nextServices = new ArrayList<>(); private final Collection<DependencyNode> nextWeakServices = new ArrayList<>(); private final Collection<WeakReference<DependencyNode>> asyncStateServices = new ArrayList<>(); DependencyNode(DependentService service, Class<? extends Service> type) { assert service != null; this.service = service; this.type = type; } DependencyNode(Class<? extends Service> type) { this.service = null; this.type = type; } public Service getService() { return this.service; } public void setService(Service service) { this.service = service; } public Class<? extends Service> getType() { return this.type; } public Collection<WeakReference<DependencyNode>> getAsyncStateServices() { return this.asyncStateServices; } public Collection<DependencyNode> getNextServices() { return this.nextServices; } public Collection<DependencyNode> getNextWeakServices() { return this.nextWeakServices; } @Override public String toString() { if (this.service == null) { return "!!!" + this.type.getName(); //$NON-NLS-1$ } return this.service.toString(); } } /** * Accessors for running services. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private interface Accessors { boolean matches(State element); void runInfrastructureServicesBefore(List<Service> infraServices); void runFreeServicesBefore(List<Service> freeServices); boolean isAsyncStateWaitingEnabled(); void run(Service service); void runFreeServicesAfter(List<Service> freeServices); void runInfrastructureServicesAfter(List<Service> infraServices); } /** * Accessors for running services at start-up. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class StartingPhaseAccessors implements Accessors { /** * Construct. */ StartingPhaseAccessors() { // } @Override public boolean matches(State element) { return element == State.NEW; } @Override public void runFreeServicesBefore(List<Service> freeServices) { // } @Override public void runInfrastructureServicesBefore(List<Service> infraServices) { for (final Service serv : infraServices) { serv.startAsync().awaitRunning(); } } @Override public boolean isAsyncStateWaitingEnabled() { return true; } @Override public void run(Service service) { service.startAsync().awaitRunning(); } @Override public void runFreeServicesAfter(List<Service> freeServices) { for (final Service serv : freeServices) { serv.startAsync(); } } @Override public void runInfrastructureServicesAfter(List<Service> infraServices) { // } } /** * Accessors for running agents at end. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ private static class StoppingPhaseAccessors implements Accessors { /** * Construct. */ StoppingPhaseAccessors() { // } @Override public boolean matches(State element) { return element != State.TERMINATED && element != State.STOPPING; } @Override public void runFreeServicesBefore(List<Service> freeServices) { for (final Service serv : freeServices) { serv.stopAsync(); } } @Override public void runInfrastructureServicesBefore(List<Service> infraServices) { // } @Override public boolean isAsyncStateWaitingEnabled() { return false; } @Override public void run(Service service) { service.stopAsync().awaitTerminated(); } @Override public void runFreeServicesAfter(List<Service> freeServices) { // } @Override public void runInfrastructureServicesAfter(List<Service> infraServices) { for (final Service serv : infraServices) { serv.stopAsync(); } } } }