Package org.springframework.xd.integration.util

Source Code of org.springframework.xd.integration.util.StreamUtils

/*
* Copyright 2011-2014 the original author 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 org.springframework.xd.integration.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jclouds.ContextBuilder;
import org.jclouds.aws.ec2.AWSEC2Api;
import org.jclouds.aws.ec2.domain.AWSRunningInstance;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.ec2.domain.Reservation;
import org.jclouds.ec2.domain.RunningInstance;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.io.payloads.ByteSourcePayload;
import org.jclouds.sshj.SshjSshClient;

import org.springframework.hateoas.PagedResources;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.xd.rest.client.impl.SpringXDTemplate;
import org.springframework.xd.rest.domain.DetailedContainerResource;
import org.springframework.xd.rest.domain.ModuleMetadataResource;
import org.springframework.xd.rest.domain.StreamDefinitionResource;

import com.google.common.collect.Iterables;
import com.google.common.io.ByteSource;
import com.google.common.net.HostAndPort;
import org.springframework.xd.rest.domain.metrics.CounterResource;
import org.springframework.xd.rest.domain.metrics.MetricResource;

/**
* Utilities for creating and monitoring streams and the JMX hooks for those strings.
*
* @author Glenn Renfro
*/
public class StreamUtils {

  public final static String TMP_DIR = "result/";

  /**
   * Creates the stream definition and deploys it to the cluster being tested.
   *
   * @param streamName The name of the stream
   * @param streamDefinition The definition that needs to be deployed for this stream.
   * @param adminServer The admin server that this stream will be deployed against.
   */
  public static void stream(final String streamName, final String streamDefinition,
      final URL adminServer) {
    Assert.hasText(streamName, "The stream name must be specified.");
    Assert.hasText(streamDefinition, "a stream definition must be supplied.");
    Assert.notNull(adminServer, "The admin server must be specified.");
    createSpringXDTemplate(adminServer).streamOperations().createStream(streamName, streamDefinition, true);
  }

  /**
   * Executes a http get for the client and returns the results as a string
   *
   * @param url The location to execute the get against.
   * @return the string result of the get
   */
  public static String httpGet(final URL url) {
    Assert.notNull(url, "The URL must be specified");
    RestTemplate template = new RestTemplate();

    try {
      String result = template.getForObject(url.toURI(), String.class);
      return result;
    }
    catch (URISyntaxException uriException) {
      throw new IllegalStateException(uriException.getMessage(), uriException);
    }
  }

  /**
   * Removes all the streams from the cluster. Used to guarantee a clean acceptance test.
   *
   * @param adminServer The admin server that the command will be executed against.
   */
  public static void destroyAllStreams(final URL adminServer) {
    Assert.notNull(adminServer, "The admin server must be specified.");
    createSpringXDTemplate(adminServer).streamOperations().destroyAll();
  }

  /**
   * Undeploys the specified stream name
   *
   * @param adminServer The admin server that the command will be executed against.
   * @param streamName The name of the stream to undeploy
   */
  public static void undeployStream(final URL adminServer, final String streamName)
  {
    Assert.notNull(adminServer, "The admin server must be specified.");
    Assert.hasText(streamName, "The streamName must not be empty nor null");
    createSpringXDTemplate(adminServer).streamOperations().undeploy(streamName);
  }

  /**
   * Retrieves the value for the counter
   *
   * @param adminServer the admin server for the cluster being tested.
   * @param name        the name of the counter in the metric repo.
   * @return the count associated with the name.
   */
  public static long getCount(final URL adminServer, final String name) {
    Assert.notNull(adminServer, "The admin server must be specified.");
    Assert.hasText(name, "The name must not be empty nor null");
    CounterResource resource = createSpringXDTemplate(adminServer).
        counterOperations().retrieve(name);
    return resource.getValue();
  }

  /**
   * Copies the specified file from a remote machine to local machine.
   *
   * @param privateKey the ssh private key to the remote machine
   * @param url The remote machine's url.
   * @param fileName The fully qualified file name of the file to be transferred.
   * @return The location to the fully qualified file name where the remote file was copied.
   */
  public static String transferResultsToLocal(final String privateKey, final URL url, final String fileName)
  {
    Assert.hasText(privateKey, "The Acceptance Test, can not be empty nor null.");
    Assert.notNull(url, "The remote machine's URL must be specified.");
    Assert.hasText(fileName, "The remote file name must be specified.");

    File file = new File(fileName);
    FileOutputStream fileOutputStream = null;
    InputStream inputStream = null;

    try {
      File tmpFile = createTmpDir();
      String fileLocation = tmpFile.getAbsolutePath() + file.getName();
      fileOutputStream = new FileOutputStream(fileLocation);

      final SshjSshClient client = getSSHClient(url, privateKey);
      inputStream = client.get(fileName).openStream();

      FileCopyUtils.copy(inputStream, fileOutputStream);
      return fileLocation;
    }
    catch (IOException ioException) {
      throw new IllegalStateException(ioException.getMessage(), ioException);
    }
  }

  /**
   * Creates a file on a remote EC2 machine with the payload as its contents.
   *
   * @param privateKey The ssh private key for the remote container
   * @param host The remote machine's ip.
   * @param dir The directory to write the file
   * @param fileName The fully qualified file name of the file to be created.
   * @param payload the data to write to the file
   * @param retryTime the time in millis to retry to push data file to remote system.
   */
  public static boolean createDataFileOnRemote(String privateKey, String host, String dir, String fileName,
      String payload, int retryTime)
  {
    Assert.hasText(privateKey, "privateKey must not be empty nor null.");
    Assert.hasText(host, "The remote machine's URL must be specified.");
    Assert.notNull(dir, "dir should not be null");
    Assert.hasText(fileName, "The remote file name must be specified.");
    boolean isFileCopied = false;
    long timeout = System.currentTimeMillis() + retryTime;
    while (!isFileCopied && System.currentTimeMillis() < timeout) {
      SshjSshClient client = getSSHClient(host, privateKey);
      client.exec("mkdir " + dir);
      client.put(dir + "/" + fileName, payload);
      ExecResponse response = client.exec("ls -al " + dir + "/" + fileName);
      if (response.getExitStatus() > 0) {
        continue; //file was not created
      }
      response = client.exec("cat " + dir + "/" + fileName);
      if (response.getExitStatus() > 0 || !payload.equals(response.getOutput())) {
        continue;//data stored on machine is different than was expected.
      }
      isFileCopied = true;
    }
    return isFileCopied;
  }

  /**
   * Copy a file from test machine to remote machine.
   *
   * @param privateKey The ssh private key for the remote container
   * @param host The remote machine's ip.
   * @param uri to the location where the file will be copied to remote machine.
   * @param file The file to migrate to remote machine.
   */
  public static void copyFileToRemote(String privateKey, String host, URI uri,
      File file, long waitTime)
  {
    Assert.hasText(privateKey, "privateKey must not be empty nor null.");
    Assert.hasText(host, "The remote machine's URL must be specified.");
    Assert.notNull(uri, "uri should not be null");
    Assert.notNull(file, "file must not be null");
    boolean isFileCopied = false;
    long timeout = System.currentTimeMillis() + waitTime;
    while (!isFileCopied && System.currentTimeMillis() < timeout) {
      final SshjSshClient client = getSSHClient(host, privateKey);
      Assert.isTrue(file.exists(), "File to be copied to remote machine does not exist");
      ByteSource byteSource = com.google.common.io.Files.asByteSource(file);
      ByteSourcePayload payload = new ByteSourcePayload(byteSource);
      long byteSourceSize = -1;
      try {
        byteSourceSize = byteSource.size();
        payload.getContentMetadata().setContentLength(byteSourceSize);
      }
      catch (IOException ioe) {
        throw new IllegalStateException("Unable to retrieve size for file to be copied to remote machine.", ioe);
      }
      client.put(uri.getPath(), payload);
      if (client.exec("ls -al " + uri.getPath()).getExitStatus() == 0) {
        ExecResponse statResponse = client.exec("stat --format=%s " + uri.getPath());
        long copySize = Long.valueOf(statResponse.getOutput().trim());
        if (copySize == byteSourceSize) {
          isFileCopied = true;
        }
      }
    }

  }

  /**
   * Creates a directory on a remote machine.  It a failure occurs it will retry until waitTime is exhausted.
   * @param path the directory that will be created
   * @param host the IP where the directory will be created
   * @param privateKey Private key to be used for signing onto remote machine.
   * @param waitTime The max time to try creating the directory
   * @return true if the directory was successfully created else false. 
   */
  public static boolean createRemoteDirectory(String path, String host, String privateKey, int waitTime) {
    boolean isDirectoryCreated = false;
    Assert.hasText(path, "path must not be empty nor null");
    Assert.hasText(host, "host must not be empty nor null");
    Assert.hasText(privateKey, "privateKey must not be empty nor null");
    long timeout = System.currentTimeMillis() + waitTime;
    while (!isDirectoryCreated && System.currentTimeMillis() < timeout) {
      SshjSshClient client = getSSHClient(host, privateKey);
      client.exec("mkdir " + path);
      ExecResponse response = client.exec("ls -al " + path);
      if (response.getExitStatus() > 0) {
        continue; //directory was not created
      }
      isDirectoryCreated = true;
    }
    return isDirectoryCreated;
  }

  /**
   * Appends the payload to an existing file on a remote EC2 Instance.
   *
   * @param privateKey The ssh private key for the remote container
   * @param host The remote machine's ip.
   * @param dir The directory to write the file
   * @param fileName The fully qualified file name of the file to be created.
   * @param payload the data to append to the file
   */
  public static void appendToRemoteFile(String privateKey, String host, String dir, String fileName,
      String payload)
  {
    Assert.hasText(privateKey, "privateKey must not be empty nor null.");
    Assert.hasText(host, "The remote machine's URL must be specified.");
    Assert.notNull(dir, "dir should not be null");
    Assert.hasText(fileName, "The remote file name must be specified.");

    final SshjSshClient client = getSSHClient(host, privateKey);
    client.exec("echo '" + payload + "' >> " + dir + "/" + fileName);
  }

  /**
   * Returns a list of active instances from the specified ec2 region.
   * @param awsAccessKey the unique id of the ec2 user.
   * @param awsSecretKey the password of ec2 user.
   * @param awsRegion The aws region to inspect for acceptance test instances.
   * @return a list of active instances in the account and region specified.
   */
  public static List<RunningInstance> getEC2RunningInstances(String awsAccessKey, String awsSecretKey,
      String awsRegion) {
    Assert.hasText(awsAccessKey, "awsAccessKey must not be empty nor null");
    Assert.hasText(awsSecretKey, "awsSecretKey must not be empty nor null");
    Assert.hasText(awsRegion, "awsRegion must not be empty nor null");

    AWSEC2Api client = ContextBuilder.newBuilder("aws-ec2")
        .credentials(awsAccessKey, awsSecretKey)
        .buildApi(AWSEC2Api.class);
    Set<? extends Reservation<? extends AWSRunningInstance>> reservations = client
        .getInstanceApi().get().describeInstancesInRegion(awsRegion);
    int instanceCount = reservations.size();
    ArrayList<RunningInstance> result = new ArrayList<RunningInstance>();
    for (int awsRunningInstanceCount = 0; awsRunningInstanceCount < instanceCount; awsRunningInstanceCount++) {
      Reservation<? extends AWSRunningInstance> instances = Iterables
          .get(reservations, awsRunningInstanceCount);
      int groupCount = instances.size();
      for (int runningInstanceCount = 0; runningInstanceCount < groupCount; runningInstanceCount++) {
        result.add(Iterables.get(instances, runningInstanceCount));
      }
    }
    return result;
  }

  /**
   * Substitutes the port associated with the URL with another port.
   *
   * @param url The URL that needs a port replaced.
   * @param port The new port number
   * @return A new URL with the host from the URL passed in and the new port.
   */
  public static URL replacePort(final URL url, final int port) {
    Assert.notNull(url, "the url must not be null");
    try {
      return new URL("http://" + url.getHost() + ":" + port);
    }
    catch (MalformedURLException malformedUrlException) {
      throw new IllegalStateException(malformedUrlException.getMessage(), malformedUrlException);
    }
  }

  /**
   * Creates a map of container Id's and the associated host.
   * @param adminServer The admin server to be queried.
   * @return Map where the key is the container id and the value is the host ip.
   */
  public static Map<String, String> getAvailableContainers(URL adminServer) {
    Assert.notNull(adminServer, "adminServer must not be null");
    HashMap<String, String> results = new HashMap<String, String>();
    Iterator<DetailedContainerResource> iter = createSpringXDTemplate(adminServer).runtimeOperations().listContainers().iterator();
    while (iter.hasNext()) {
      DetailedContainerResource container = iter.next();
      results.put(container.getAttribute("id"), container.getAttribute("host"));
    }
    return results;
  }

  /**
   * Return a list of container id's where the module is deployed
   * @param adminServer The admin server that will be queried.
   * @return A list of containers where the module is deployed.
   */
  public static PagedResources<ModuleMetadataResource> getRuntimeModules(URL adminServer) {
    Assert.notNull(adminServer, "adminServer must not be null");
    return createSpringXDTemplate(adminServer).runtimeOperations().listDeployedModules();
  }

  /**
   * Waits up to the wait time for a stream to be deployed.
   *
   * @param streamName The name of the stream to be evaluated.
   * @param adminServer The admin server URL that will be queried.
   * @param waitTime the amount of time in millis to wait.
   * @return true if the stream is deployed else false.
   */
  public static boolean waitForStreamDeployment(String streamName, URL adminServer, int waitTime) {
    boolean result = isStreamDeployed(streamName, adminServer);
    long timeout = System.currentTimeMillis() + waitTime;
    while (!result && System.currentTimeMillis() < timeout) {
      try {
        Thread.sleep(1000);
      }
      catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new IllegalStateException(e.getMessage(), e);
      }
      result = isStreamDeployed(streamName, adminServer);
    }

    return result;
  }

  /**
   * Checks to see if the specified stream is deployed on the XD cluster.
   *
   * @param streamName The name of the stream to be evaluated.
   * @param adminServer The admin server URL that will be queried.
   * @return true if the stream is deployed else false
   */
  public static boolean isStreamDeployed(String streamName, URL adminServer) {
    Assert.hasText(streamName, "The stream name must be specified.");
    Assert.notNull(adminServer, "The admin server must be specified.");
    boolean result = false;
    SpringXDTemplate xdTemplate = createSpringXDTemplate(adminServer);
    PagedResources<StreamDefinitionResource> resources = xdTemplate.streamOperations().list();
    Iterator<StreamDefinitionResource> resourceIter = resources.iterator();
    while (resourceIter.hasNext()) {
      StreamDefinitionResource resource = resourceIter.next();
      if (streamName.equals(resource.getName())) {
        if ("deployed".equals(resource.getStatus())) {
          result = true;
          break;
        }
        else {
          result = false;
          break;
        }
      }
    }
    return result;
  }

  /**
   * Waits up to the wait time for a metric to be created.
   *
   * @param name        The name of the metric to be evaluated.
   * @param adminServer The admin server URL that will be queried.
   * @param waitTime    the amount of time in millis to wait.
   * @return true if the metric is created else false.
   */
  public static boolean waitForMetric(String name, URL adminServer, int waitTime) {
    Assert.hasText(name, "name must not be empty nor null");
    Assert.notNull(adminServer, "The admin server must be specified.");

    SpringXDTemplate xdTemplate = createSpringXDTemplate(adminServer);
    boolean result = isMetricPresent(xdTemplate, name);
    long timeout = System.currentTimeMillis() + waitTime;
    while (!result && System.currentTimeMillis() < timeout) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new IllegalStateException(e.getMessage(), e);
      }
      result = isMetricPresent(xdTemplate, name);
    }
    return result;
  }

  /**
   * Retrieves the container pids on the local machine. 
   * @param jpsCommand  jps command that will reveal the pids.
   * @return An Integer array that contains the pids.
   */
  public static Integer[] getLocalContainerPids(String jpsCommand) {
    Assert.hasText(jpsCommand, "jpsCommand can not be empty nor null");
    Integer[] result = null;
    try {
      Process p = Runtime.getRuntime().exec(jpsCommand);
      p.waitFor();
      String pidInfo = org.springframework.util.StreamUtils.copyToString(p.getInputStream(),
          Charset.forName("UTF-8"));
      result = extractPidsFromJPS(pidInfo);
    }
    catch (IOException e) {
      throw new IllegalStateException(e.getMessage(), e);
    }
    catch (InterruptedException e) {
      throw new IllegalStateException(e.getMessage(), e);
    }
    return result;
  }

  /**
   * Retrieves the container pids for a remote machine.    
   * @param url The URL where the containers are deployed.
   * @param privateKey ssh private key credentional
   * @param jpsCommand The command to retrieve java processes on container machine.
   * @return An Integer array that contains the pids.
   */
  public static Integer[] getContainerPidsFromURL(URL url, String privateKey, String jpsCommand) {
    Assert.notNull(url, "url can not be null");
    Assert.hasText(privateKey, "privateKey can not be empty nor null");
    SshjSshClient client = getSSHClient(url, privateKey);
    ExecResponse response = client.exec(jpsCommand);
    return extractPidsFromJPS(response.getOutput());
  }

  private static Integer[] extractPidsFromJPS(String jpsResult) {
    String[] pidList = StringUtils.tokenizeToStringArray(jpsResult, "\n");
    ArrayList<Integer> pids = new ArrayList<Integer>();
    for (String pidData : pidList) {
      if (pidData.contains("ContainerServerApplication")) {
        pids.add(Integer.valueOf(StringUtils.tokenizeToStringArray(pidData, " ")[0]));
      }
    }
    return pids.toArray(new Integer[pids.size()]);
  }


  private static File createTmpDir() throws IOException {
    File tmpFile = new File(System.getProperty("user.dir") + "/" + TMP_DIR);
    if (!tmpFile.exists()) {
      tmpFile.createNewFile();
    }
    return tmpFile;
  }


  private static SshjSshClient getSSHClient(URL url, String privateKey) {
    return getSSHClient(url.getHost(), privateKey);
  }

  private static SshjSshClient getSSHClient(String host, String privateKey) {
    final LoginCredentials credential = LoginCredentials
        .fromCredentials(new Credentials("ubuntu", privateKey));
    final HostAndPort socket = HostAndPort.fromParts(host, 22);
    final SshjSshClient client = new SshjSshClient(
        new BackoffLimitedRetryHandler(), socket, credential, 5000);
    return client;
  }

  /**
   * Create an new instance of the SpringXDTemplate given the Admin Server URL
   *
   * @param adminServer URL of the Admin Server
   * @return A new instance of SpringXDTemplate
   */
  private static SpringXDTemplate createSpringXDTemplate(URL adminServer) {
    try {
      return new SpringXDTemplate(adminServer.toURI());
    }
    catch (URISyntaxException uriException) {
      throw new IllegalStateException(uriException.getMessage(), uriException);
    }
  }

  private static boolean isMetricPresent(SpringXDTemplate xdTemplate, String name) {

    boolean result = false;
    PagedResources<MetricResource> resources = xdTemplate.counterOperations().list();
    Iterator<MetricResource> resourceIter = resources.iterator();
    while (resourceIter.hasNext()) {
      MetricResource resource = resourceIter.next();
      if (name.equals(resource.getName())) {
        result = true;
        break;
      }
    }
    return result;
  }

}
TOP

Related Classes of org.springframework.xd.integration.util.StreamUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.