@SuppressWarnings("unchecked")
 <P extends Plugin> P getPlugin(Class<P> pluginClass) {
   for (Plugin plugin : plugins) {
     if (plugin.getClass().equals(pluginClass)) {
       return (P) plugin;
     }
   }
   throw new NoSuchElementException("plugin " + pluginClass + " is not loaded");
 }
  private void start(final Id id) {
    if (id == null) {
      throw new IllegalArgumentException("id is null");
    }
    final Pattern waitForPattern = compileWaitForPattern(id);

    logger.info("Starting " + id);

    try {
      if (!imageExists(id)) {
        logger.info("Image does not exist, so building it");
        build(id);
      }
    } catch (DockerException e) {
      throw new OrchestrationException(e);
    }

    try {
      Container existingContainer = findContainer(id);

      if (existingContainer == null) {
        logger.info("No existing container so creating and starting new one");
        startContainer(createNewContainer(id));

      } else if (!isImageIdFromContainerMatchingProvidedImageId(existingContainer.getId(), id)) {
        logger.info("Image IDs do not match, removing container and creating new one from image");
        removeContainer(existingContainer);
        startContainer(createNewContainer(id));

      } else if (isRunning(id)) {
        logger.info("Container already running");

      } else {
        logger.info("Starting existing container " + existingContainer.getId());
        startContainer(existingContainer.getId());
      }

      try (Tail tail = tailFactory.newTail(docker, findContainer(id), logger)) {
        tail.start();

        for (Plugin plugin : plugins) {
          plugin.started(id, conf(id));
        }

        healthCheck(id);

        sleep(id);

        tail.setMaxLines(conf(id).getMaxLogLines());
      }
      waitFor(id, waitForPattern);
    } catch (DockerException e) {
      throw new OrchestrationException(e);
    }
  }
  DockerOrchestrator(
      DockerClient docker,
      Repo repo,
      FileOrchestrator fileOrchestrator,
      Set<BuildFlag> buildFlags,
      Logger logger,
      TailFactory tailFactory,
      DockerfileValidator dockerfileValidator,
      DefinitionFilter definitionFilter,
      boolean permissionErrorTolerant) {
    if (docker == null) {
      throw new IllegalArgumentException("docker is null");
    }
    if (repo == null) {
      throw new IllegalArgumentException("repo is null");
    }
    if (buildFlags == null) {
      throw new IllegalArgumentException("buildFlags is null");
    }
    if (fileOrchestrator == null) {
      throw new IllegalArgumentException("fileOrchestrator is null");
    }
    if (dockerfileValidator == null) {
      throw new IllegalArgumentException("dockerfileValidator is null");
    }
    if (definitionFilter == null) {
      throw new IllegalArgumentException("definitionFilter is null");
    }

    this.docker = docker;
    this.tailFactory = tailFactory;
    this.repo = repo;
    this.fileOrchestrator = fileOrchestrator;
    this.buildFlags = buildFlags;
    this.logger = logger;
    this.dockerfileValidator = dockerfileValidator;
    this.definitionFilter = definitionFilter;
    this.permissionErrorTolerant = permissionErrorTolerant;

    for (Plugin plugin : ServiceLoader.load(Plugin.class)) {
      plugins.add(plugin);
      logger.info("Loaded " + plugin.getClass() + " plugin");
    }
  }
  private void stop(final Id id) {
    if (id == null) {
      throw new IllegalArgumentException("id is null");
    }

    logger.info("Stopping " + id);

    for (Container container : findRunningContainers(id)) {
      logger.info("Stopping container " + Arrays.toString(container.getNames()));
      try {
        docker.stopContainerCmd(container.getId()).withTimeout(1).exec();
      } catch (DockerException e) {
        throw new OrchestrationException(e);
      }
    }
    for (Plugin plugin : plugins) {
      plugin.stopped(id, conf(id));
    }
  }