/* * * * Copyright (c) 2016. David Sowerby * * * * 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 uk.q3c.krail.core.services; import com.google.common.collect.ImmutableList; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import org.slf4j.Logger; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; import java.util.*; import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; import static uk.q3c.krail.core.services.Dependency.Type.REQUIRED_ONLY_AT_START; import static uk.q3c.krail.core.services.ServicesGraph.Selection.ALL; /** * Created by David Sowerby on 16 Dec 2015 */ @Singleton @ThreadSafe public class DefaultServicesModel implements ServicesModel { private static Logger log = getLogger(DefaultServicesModel.class); private final ServicesClassGraph classGraph; private final ServicesInstanceGraph instanceGraph; private Map<ServiceKey, Provider<Service>> registeredServices; @Inject public DefaultServicesModel(Set<DependencyDefinition> configuredDependencies, ServicesClassGraph classGraph, ServicesInstanceGraph instanceGraph, Map<ServiceKey, Provider<Service>> registeredServices) { this.classGraph = classGraph; this.instanceGraph = instanceGraph; this.registeredServices = registeredServices; processConfiguredDependencies(configuredDependencies); } /** * Places an entry for every registered service into the class graph. Reads dependency definitions from set provided via Guice and creates dependencies * * @param configuredDependencies dependency definitions provided via Guice */ private void processConfiguredDependencies(Set<DependencyDefinition> configuredDependencies) { configuredDependencies.forEach(this::createDependency); } @Override public void alwaysDependsOn(@Nonnull ServiceKey dependant, @Nonnull ServiceKey dependency) { checkNotNull(dependant); checkNotNull(dependency); classGraph.createDependency(dependant, dependency, Dependency.Type.ALWAYS_REQUIRED); } private void createDependency(DependencyDefinition dependencyDefinition) { classGraph.createDependency(dependencyDefinition.getDependant(), dependencyDefinition.getDependency(), dependencyDefinition.getType()); } @Override public void requiresOnlyAtStart(@Nonnull ServiceKey dependant, @Nonnull ServiceKey dependency) { checkNotNull(dependant); checkNotNull(dependency); classGraph.createDependency(dependant, dependency, REQUIRED_ONLY_AT_START); } @Override public void optionallyUses(@Nonnull ServiceKey dependant, @Nonnull ServiceKey dependency) { checkNotNull(dependant); checkNotNull(dependency); classGraph.createDependency(dependant, dependency, Dependency.Type.OPTIONAL); } @Override public boolean addService(@Nonnull ServiceKey serviceClass) { checkNotNull(serviceClass); return classGraph.addService(serviceClass); } @Override public synchronized boolean addService(@Nonnull Service service) { checkNotNull(service); //don't try and add it twice if (!instanceGraph.addService(service)) { return false; } //it's a new entry so we need to create dependencies as defined by class graph createInstanceDependencies(service); return true; } private void createInstanceDependencies(Service service) { List<ServiceKey> classDependencies = classGraph.findDependencies(service.getServiceKey(), ALL); classDependencies.forEach(classDependency -> { Service instance = getInstanceOf(classDependency); Optional<ServiceEdge> edge = classGraph.getEdge(service.getServiceKey(), classDependency); if (edge.isPresent()) { //will also add the service if not already contained instanceGraph.createDependency(service, instance, edge.get() .getType()); } createInstanceDependencies(instance); }); } @Override public boolean contains(@Nonnull Service service) { checkNotNull(service); return instanceGraph.contains(service); } @Override public boolean contains(ServiceKey serviceClass) { return classGraph.contains(serviceClass); } @Override public ImmutableList<Service> registeredServiceInstances() { return instanceGraph.getServices(); } @Override public synchronized List<DependencyInstanceDefinition> findInstanceDependencyDefinitions(@Nonnull Service service) { checkNotNull(service); List<Service> instanceDependencies = instanceGraph.findDependencies(service, ALL); List<DependencyInstanceDefinition> definitions = new ArrayList<>(); for (Service instanceDependency : instanceDependencies) { Optional<ServiceEdge> edge = instanceGraph.getEdge(service, instanceDependency); if (edge.isPresent()) { definitions.add(new DependencyInstanceDefinition(instanceDependency, edge.get() .getType())); } else { log.error("Very strange - edge must be present in order to have retrieved the dependencies. This should be impossible"); } } return definitions; } @Override public ServicesInstanceGraph getInstanceGraph() { return instanceGraph; } @Override public ServicesClassGraph getClassGraph() { return classGraph; } private Service getInstanceOf(@Nonnull ServiceKey serviceKey) { checkNotNull(serviceKey); Provider<? extends Service> provider = registeredServices.get(serviceKey); if (provider != null) { Service instance = provider.get(); instanceGraph.addService(instance); log.debug("returning instance for {}", serviceKey); return instance; } else { throw new ServiceRegistrationException("no service has been registered for " + serviceKey + ", have you forgotten to register a service in a " + "Guice " + "module, or forgotten to add a Guice module to the BindingManager?" + ' '); } } @Override public ImmutableList<ServiceKey> registeredServices() { return classGraph.getServices(); } @Override public List<Service> findInstanceDependencies(@Nonnull Service service) { return instanceGraph.findDependencies(service, ALL); } @Override public List<Service> findInstanceDependencies(@Nonnull Service service, @Nonnull ServicesGraph.Selection selection) { return instanceGraph.findDependencies(service, selection); } @Override public synchronized void stopAllServices() { log.info("Stopping all services"); registeredServiceInstances() .forEach(Service::stop); } }