@Override
  public <S extends StreamDefinition> S save(S entity) {
    try {
      Map<String, String> map = new HashMap<>();
      map.put(DEFINITION_KEY, entity.getDefinition());

      map.put(
          MODULE_DEFINITIONS_KEY, objectWriter.writeValueAsString(entity.getModuleDefinitions()));

      CuratorFramework client = zkConnection.getClient();
      String path = Paths.build(Paths.STREAMS, entity.getName());
      byte[] binary = ZooKeeperUtils.mapToBytes(map);

      BackgroundPathAndBytesable<?> op =
          client.checkExists().forPath(path) == null ? client.create() : client.setData();

      op.forPath(path, binary);

      logger.trace("Saved stream {} with properties {}", path, map);

      StreamDefinitionRepositoryUtils.saveDependencies(moduleDependencyRepository, entity);
    } catch (Exception e) {
      // NodeExistsException indicates that we tried to create the
      // path just after another thread/jvm successfully created it
      ZooKeeperUtils.wrapAndThrowIgnoring(e, NodeExistsException.class);
    }
    return entity;
  }
  /**
   * Test the usage of {@link StreamDeploymentsPath} when providing the entire path. Assert that the
   * path is parsed and generated as expected.
   */
  @Test
  public void testFullPath() {
    String streamName = "my-stream";
    String moduleType = ModuleType.source.toString();
    String moduleLabel = "my-label";
    String moduleSequence = "0";
    String container = UUID.randomUUID().toString();
    String path =
        Paths.build(
            Paths.STREAM_DEPLOYMENTS,
            streamName,
            Paths.MODULES,
            String.format("%s.%s.%s.%s", moduleType, moduleLabel, moduleSequence, container));

    StreamDeploymentsPath streamDeploymentsPath = new StreamDeploymentsPath(path);

    assertEquals(streamName, streamDeploymentsPath.getStreamName());
    assertEquals(moduleType, streamDeploymentsPath.getModuleType());
    assertEquals(moduleLabel, streamDeploymentsPath.getModuleLabel());
    assertEquals(moduleSequence, streamDeploymentsPath.getModuleSequenceAsString());
    assertEquals(container, streamDeploymentsPath.getContainer());

    StreamDeploymentsPath streamDeploymentsPathEmptyCtor =
        new StreamDeploymentsPath()
            .setStreamName(streamName)
            .setModuleType(moduleType)
            .setModuleLabel(moduleLabel)
            .setModuleSequence(moduleSequence)
            .setContainer(container);

    assertEquals(path, streamDeploymentsPathEmptyCtor.build());
  }
  /**
   * Load the {@link org.springframework.xd.dirt.core.Stream} instance for a given stream name <i>if
   * the stream is deployed</i>. It will include the stream definition as well as any deployment
   * properties data for the stream deployment.
   *
   * @param client curator client
   * @param streamName the name of the stream to load
   * @param streamFactory stream factory used to create instance of stream
   * @return the stream instance, or {@code null} if the stream does not exist or is not deployed
   * @throws Exception if ZooKeeper access fails for any reason
   */
  public static Stream loadStream(
      CuratorFramework client, String streamName, StreamFactory streamFactory) throws Exception {
    try {
      byte[] definition = client.getData().forPath(Paths.build(Paths.STREAMS, streamName));
      Map<String, String> definitionMap = ZooKeeperUtils.bytesToMap(definition);

      byte[] deploymentPropertiesData =
          client.getData().forPath(Paths.build(Paths.STREAM_DEPLOYMENTS, streamName));
      if (deploymentPropertiesData != null && deploymentPropertiesData.length > 0) {
        definitionMap.put("deploymentProperties", new String(deploymentPropertiesData, "UTF-8"));
      }
      return streamFactory.createStream(streamName, definitionMap);
    } catch (KeeperException.NoNodeException e) {
      // stream is not deployed or does not exist
    }
    return null;
  }
  /**
   * Load the {@link org.springframework.xd.dirt.core.Job} instance for a given job name<i>if the
   * job is deployed</i>.
   *
   * @param client curator client
   * @param jobName the name of the job to load
   * @param jobFactory job factory used to create instance of job
   * @return the job instance, or {@code null} if the job does not exist or is not deployed
   * @throws Exception
   */
  public static Job loadJob(CuratorFramework client, String jobName, JobFactory jobFactory)
      throws Exception {
    try {
      byte[] definition = client.getData().forPath(Paths.build(Paths.JOBS, jobName));
      Map<String, String> definitionMap = ZooKeeperUtils.bytesToMap(definition);

      byte[] deploymentPropertiesData =
          client.getData().forPath(Paths.build(Paths.JOB_DEPLOYMENTS, jobName));
      if (deploymentPropertiesData != null && deploymentPropertiesData.length > 0) {
        definitionMap.put("deploymentProperties", new String(deploymentPropertiesData, "UTF-8"));
      }
      return jobFactory.createJob(jobName, definitionMap);
    } catch (KeeperException.NoNodeException e) {
      // job is not deployed
    }
    return null;
  }
 @Override
 public boolean exists(String id) {
   try {
     return (null
         != zkConnection.getClient().checkExists().forPath(Paths.build(Paths.STREAMS, id)));
   } catch (Exception e) {
     throw ZooKeeperUtils.wrapThrowable(e);
   }
 }
 @Override
 public void delete(String id) {
   logger.trace("Deleting stream {}", id);
   String path = Paths.build(Paths.STREAMS, id);
   try {
     zkConnection.getClient().delete().deletingChildrenIfNeeded().forPath(path);
   } catch (Exception e) {
     // NoNodeException - nothing to delete
     ZooKeeperUtils.wrapAndThrowIgnoring(e, NoNodeException.class);
   }
 }
 @Override
 public StreamDefinition findOne(String id) {
   try {
     byte[] bytes = zkConnection.getClient().getData().forPath(Paths.build(Paths.STREAMS, id));
     if (bytes == null) {
       return null;
     }
     Map<String, String> map = ZooKeeperUtils.bytesToMap(bytes);
     StreamDefinition streamDefinition = new StreamDefinition(id, map.get(DEFINITION_KEY));
     if (map.get(MODULE_DEFINITIONS_KEY) != null) {
       List<ModuleDefinition> moduleDefinitions =
           objectReader.readValue(map.get(MODULE_DEFINITIONS_KEY));
       streamDefinition.setModuleDefinitions(moduleDefinitions);
     }
     return streamDefinition;
   } catch (Exception e) {
     // NoNodeException - the definition does not exist
     ZooKeeperUtils.wrapAndThrowIgnoring(e, NoNodeException.class);
   }
   return null;
 }
  /**
   * Provides basic deployment behavior, whereby running state of deployed definitions is not
   * persisted.
   *
   * @return the definition object for the given name
   * @throws NoSuchDefinitionException if there is no definition by the given name
   */
  protected D basicDeploy(String name, Map<String, String> properties) {
    Assert.hasText(name, "name cannot be blank or null");
    logger.trace("Deploying {}", name);

    final D definition = getDefinitionRepository().findOne(name);
    if (definition == null) {
      throwNoSuchDefinitionException(name);
    }
    validateDeploymentProperties(definition, properties);
    try {
      String deploymentPath = getDeploymentPath(definition);
      String statusPath = Paths.build(deploymentPath, Paths.STATUS);
      byte[] propertyBytes =
          DeploymentPropertiesFormat.formatDeploymentProperties(properties).getBytes("UTF-8");
      byte[] statusBytes =
          ZooKeeperUtils.mapToBytes(
              new DeploymentUnitStatus(DeploymentUnitStatus.State.deploying).toMap());

      zkConnection
          .getClient()
          .inTransaction()
          .create()
          .forPath(deploymentPath, propertyBytes)
          .and()
          .create()
          .withMode(CreateMode.EPHEMERAL)
          .forPath(statusPath, statusBytes)
          .and()
          .commit();
    } catch (KeeperException.NodeExistsException e) {
      throwAlreadyDeployedException(name);
    } catch (Exception e) {
      throw ZooKeeperUtils.wrapThrowable(e);
    }
    return definition;
  }
 /** {@inheritDoc} */
 @Override
 protected String getDeploymentPath(StreamDefinition definition) {
   return Paths.build(Paths.STREAM_DEPLOYMENTS, definition.getName());
 }