All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.zeebe.servicecontainer.impl.ServiceDependencyResolver Maven / Gradle / Ivy

/*
 * Copyright © 2017 camunda services GmbH ([email protected])
 *
 * 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.zeebe.servicecontainer.impl;

import io.zeebe.servicecontainer.ServiceGroupReference;
import io.zeebe.servicecontainer.ServiceName;
import io.zeebe.servicecontainer.impl.ServiceEvent.ServiceEventType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;

/** Stream processor tracking the dependencies of services. */
public class ServiceDependencyResolver {
  public static final Logger LOG = Loggers.SERVICE_CONTAINER_LOGGER;

  /** all services installed into the container, indexed by name */
  private final Map, ServiceController> installedServices = new HashMap<>();

  /** map of service names which have not been resolved yet */
  private final Map, List> unresolvedDependencies =
      new HashMap<>();

  /** map of services and their resolved dependencies */
  private final Map> resolvedDependencies =
      new HashMap<>();

  /** map of services and the services which depend on them */
  private final Map> dependentServices = new HashMap<>();

  /** set of services which have started */
  private final Set startedServices = new HashSet<>();

  /** set of services which are currently stopping */
  private final Set stoppingServices = new HashSet<>();

  /** map of service groups */
  private final Map, ServiceGroup> groups = new HashMap<>();

  public void onServiceEvent(ServiceEvent event) {
    switch (event.getType()) {
      case SERVICE_INSTALLED:
        onServiceInstalled(event);
        break;

      case SERVICE_REMOVED:
        onServiceRemoved(event);
        break;

      case SERVICE_STARTED:
        onServiceStarted(event);
        break;

      case SERVICE_STOPPING:
        onServiceStopping(event);
        break;

      case SERVICE_STOPPED:
        onServiceStopped(event);
        break;

      default:
        // no-op
        break;
    }
  }

  private void onServiceStopping(ServiceEvent event) {
    final ServiceController controller = event.getController();
    stoppingServices.add(controller);

    // resolve group name
    final ServiceName groupName = controller.getGroupName();
    if (groupName != null) {
      final ServiceGroup serviceGroup = getOrCreateGroup(groupName);
      serviceGroup.removeService(controller);
    }

    // update injected references
    final Map, ServiceGroupReference> injectedReferences =
        controller.getInjectedReferences();
    injectedReferences
        .entrySet()
        .forEach(
            (e) -> {
              final ServiceName refGroupName = e.getKey();
              final ServiceGroupReference reference = e.getValue();
              final ServiceGroup refGroup = getOrCreateGroup(refGroupName);
              refGroup.removeReference(reference);
            });

    final List dependents = this.dependentServices.get(controller);
    for (ServiceController dependentService : dependents) {
      dependentService
          .getChannel()
          .add(new ServiceEvent(ServiceEventType.DEPENDENCIES_UNAVAILABLE, dependentService));
    }

    if (dependents.isEmpty()) {
      controller
          .getChannel()
          .add(new ServiceEvent(ServiceEventType.DEPENDENTS_STOPPED, controller));
    }
  }

  private void onServiceStopped(ServiceEvent event) {
    final ServiceController controller = event.getController();

    startedServices.remove(controller);
    stoppingServices.remove(controller);
  }

  private void onServiceRemoved(ServiceEvent event) {
    final ServiceController controller = event.getController();

    final List dependencies = resolvedDependencies.remove(controller);
    for (ServiceController dependency : dependencies) {
      final List deps = dependentServices.get(dependency);

      if (deps != null) {
        if (stoppingServices.contains(dependency)) {
          boolean allStopped = true;
          for (int i = 0; i < deps.size() && allStopped; i++) {
            allStopped &= !startedServices.contains(deps.get(i));
          }

          if (allStopped) {
            dependency
                .getChannel()
                .add(new ServiceEvent(ServiceEventType.DEPENDENTS_STOPPED, dependency));
          }
        }

        deps.remove(controller);
      }
    }

    installedServices.remove(controller.getServiceName());
    dependentServices.remove(controller);
  }

  private void onServiceStarted(ServiceEvent event) {
    final ServiceController controller = event.getController();
    startedServices.add(controller);

    // update dependent services
    final List dependentServices = this.dependentServices.get(controller);
    for (ServiceController dependentService : dependentServices) {
      checkDependenciesAvailable(dependentService);
    }

    // resolve group name
    final ServiceName groupName = controller.getGroupName();
    if (groupName != null) {
      final ServiceGroup serviceGroup = getOrCreateGroup(groupName);
      serviceGroup.addService(controller);
    }

    // update injected references
    final Map, ServiceGroupReference> injectedReferences =
        controller.getInjectedReferences();
    injectedReferences
        .entrySet()
        .forEach(
            (e) -> {
              final ServiceName refGroupName = e.getKey();
              final ServiceGroupReference reference = e.getValue();
              final ServiceGroup refGroup = getOrCreateGroup(refGroupName);
              refGroup.addReference(new ServiceGroupReferenceImpl(controller, reference, refGroup));
            });
  }

  private ServiceGroup getOrCreateGroup(final ServiceName groupName) {
    ServiceGroup serviceGroup = groups.get(groupName);
    if (serviceGroup == null) {
      serviceGroup = new ServiceGroup(groupName);
      groups.put(groupName, serviceGroup);
    }
    return serviceGroup;
  }

  private void onServiceInstalled(ServiceEvent event) {
    final ServiceController controller = event.getController();
    installedServices.put(controller.getServiceName(), controller);

    /** try to resolve this service's dependencies */
    final Set> dependencies = controller.getDependencies();
    final List resolvedDependencies = new ArrayList<>();
    for (ServiceName serviceName : dependencies) {
      final ServiceController resolvedController = installedServices.get(serviceName);

      if (resolvedController != null) {
        resolvedDependencies.add(resolvedController);
        dependentServices.get(resolvedController).add(controller);
      } else {
        List list = unresolvedDependencies.get(serviceName);
        if (list == null) {
          list = new ArrayList<>();
          unresolvedDependencies.put(serviceName, list);
        }

        list.add(controller);
      }
    }
    this.resolvedDependencies.put(controller, resolvedDependencies);

    /** resolve other services' dependencies which depend on this service */
    List dependents = unresolvedDependencies.remove(controller.getServiceName());
    if (dependents != null) {
      for (ServiceController dependent : dependents) {
        this.resolvedDependencies.get(dependent).add(controller);
      }
    } else {
      dependents = new ArrayList<>();
    }

    this.dependentServices.put(controller, dependents);
    checkDependenciesAvailable(controller);
  }

  private void checkDependenciesAvailable(ServiceController controller) {
    final Set> dependencies = controller.getDependencies();
    final List resolvedDependencies = this.resolvedDependencies.get(controller);

    boolean dependenciesAvailable = true;

    if (resolvedDependencies.size() == dependencies.size()) {
      for (int i = 0; i < resolvedDependencies.size() && dependenciesAvailable; i++) {
        dependenciesAvailable &= startedServices.contains(resolvedDependencies.get(i));
        dependenciesAvailable &= !stoppingServices.contains(resolvedDependencies.get(i));
      }
    } else {
      dependenciesAvailable = false;
    }

    if (dependenciesAvailable) {
      controller
          .getChannel()
          .add(
              new ServiceEvent(
                  ServiceEventType.DEPENDENCIES_AVAILABLE,
                  controller,
                  new ArrayList<>(resolvedDependencies)));
    }
  }

  final ServiceController getService(ServiceName name) {
    return installedServices.get(name);
  }

  public Collection getControllers() {
    return installedServices.values();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy