Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (c) 2014 Spotify AB.
   *
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 com.spotify.helios.client;
 
 
 
 
 import java.net.URI;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.util.concurrent.Futures.immediateFuture;
 import static com.google.common.util.concurrent.Futures.transform;
 import static com.google.common.util.concurrent.Futures.withFallback;
 import static com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService;
 import static com.spotify.helios.common.VersionCompatibility.HELIOS_SERVER_VERSION_HEADER;
 import static com.spotify.helios.common.VersionCompatibility.HELIOS_VERSION_STATUS_HEADER;
 import static java.lang.String.format;
 import static java.lang.System.currentTimeMillis;
 
 import static java.net.HttpURLConnection.HTTP_OK;
 import static java.net.HttpURLConnection.HTTP_BAD_METHOD;
 import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS;
public class HeliosClient implements AutoCloseable {
  private static final Logger log = LoggerFactory.getLogger(HeliosClient.class);
  private static final long RETRY_TIMEOUT_MILLIS = .toMillis(60);
  private static final long HTTP_TIMEOUT_MILLIS = .toMillis(10);
  private final AtomicBoolean versionWarningLogged = new AtomicBoolean();
  private final String user;
  private final Supplier<List<URI>> endpointSupplier;
  HeliosClient(final String user,
               final Supplier<List<URI>> endpointSupplier,
               final ListeningExecutorService executorService) {
    this. = checkNotNull(user);
    this. = checkNotNull(endpointSupplier);
    this. = checkNotNull(executorService);
  }
  HeliosClient(final String userfinal List<URIendpoints,
               final ListeningExecutorService executorService) {
    this(user, Suppliers.ofInstance(endpoints), executorService);
  }
  HeliosClient(final String userfinal Supplier<List<URI>> endpointSupplier) {
    this(userendpointSupplier, MoreExecutors.listeningDecorator(getExitingExecutorService(
        (ThreadPoolExecutornewFixedThreadPool(4), 0, )));
  }
  HeliosClient(final String userfinal List<URIendpoints) {
    this(user, Suppliers.ofInstance(endpoints));
  }
  public void close() {
  }
  private URI uri(final String path) {
    return uri(path, Collections.<StringString>emptyMap());
  }
  private URI uri(final String pathfinal Map<StringStringquery) {
    // TODO(dano): use a uri builder and clean this mess up
    checkArgument(path.startsWith("/"));
    final Map<StringStringqueryWithUser = Maps.newHashMap(query);
    queryWithUser.put("user");
    final String queryPart = Joiner.on('&').withKeyValueSeparator("=").join(queryWithUser);
    try {
      return new URI("http""helios"pathqueryPartnull);
    } catch (URISyntaxException e) {
      throw Throwables.propagate(e);
    }
  }
  private String path(final String resourcefinal Object... params) {
    final String path;
    if (params.length == 0) {
      path = resource;
    } else {
      final List<StringencodedParams = Lists.newArrayList();
      for (final Object param : params) {
        final URI u;
        try {
          final String p = param.toString().replace("/""%2F");
          // URI does path encoding right, but using it is painful
          u = new URI("http""ignore""/" + p"");
        } catch (URISyntaxException e) {
          throw Throwables.propagate(e);
        }
        encodedParams.add(u.getRawPath().substring(1));
      }
      path = format(resourceencodedParams.toArray());
    }
    return path;
  }
  private ListenableFuture<Responserequest(final URI urifinal String method) {
    return request(urimethodnull);
  }
  private ListenableFuture<Responserequest(final URI urifinal String method,
                                             final Object entity) {
    final Map<StringList<String>> headers = Maps.newHashMap();
    final byte[] entityBytes;
    if (entity != null) {
      headers.put("Content-Type"asList("application/json"));
      headers.put("Charset"asList("utf-8"));
      entityBytes = Json.asBytesUnchecked(entity);
    } else {
      entityBytes = new byte[]{};
    }
    return .submit(new Callable<Response>() {
      @Override
      public Response call() throws Exception {
        final HttpURLConnection connection = connect(urimethodentityBytesheaders);
        final int status = connection.getResponseCode();
        final InputStream rawStream;
        if (status / 100 != 2) {
          rawStream = connection.getErrorStream();
        } else {
          rawStream = connection.getInputStream();
        }
        final boolean gzip = isGzipCompressed(connection);
        final InputStream stream = gzip ? new GZIPInputStream(rawStream) : rawStream;
        final ByteArrayOutputStream payload = new ByteArrayOutputStream();
        if (stream != null) {
          int n;
          byte[] buffer = new byte[4096];
          while ((n = stream.read(buffer, 0, buffer.length)) != -1) {
            payload.write(buffer, 0, n);
          }
        }
        URI realUri = connection.getURL().toURI();
        if (.isTraceEnabled()) {
          .trace("rep: {} {} {} {} {} gzip:{}",
                    methodrealUristatuspayload.size(), decode(payload), gzip);
        } else {
          .debug("rep: {} {} {} {} gzip:{}",
                    methodrealUristatuspayload.size(), gzip);
        }
        checkprotocolVersionStatus(connection);
        return new Response(methoduristatuspayload.toByteArray());
      }
      private boolean isGzipCompressed(final HttpURLConnection connection) {
        final List<Stringencodings = connection.getHeaderFields().get("Content-Encoding");
        if (encodings == null) {
          return false;
        }
        for (String encoding : encodings) {
          if ("gzip".equals(encoding)) {
            return true;
          }
        }
        return false;
      }
    });
  }
  private void checkprotocolVersionStatus(final HttpURLConnection connection) {
    final Status versionStatus = getVersionStatus(connection);
    if (versionStatus == null) {
      .debug("Server didn't return a version header!");
      return// shouldn't happen really
    }
    final String serverVersion = connection.getHeaderField();
    if ((versionStatus == ..) &&
        (.compareAndSet(falsetrue))) {
      .warn("Your Helios client version [{}] is ahead of the server [{}].  This will"
               + " probably work ok but there is the potential for weird things.  If in doubt,"
               + " contact the Helios team if you think the cluster you're connecting to is out"
               + " of date and should be upgraded.".serverVersion);
    }
  }
  private Status getVersionStatus(final HttpURLConnection connection) {
    final String status = connection.getHeaderField();
    if (status != null) {
      return VersionCompatibility.Status.valueOf(status);
    }
    return null;
  }
  private String decode(final ByteArrayOutputStream payload) {
    final byte[] bytes = payload.toByteArray();
    try {
      return Json.asPrettyString(Json.read(bytesnew TypeReference<Map<StringObject>>() {}));
    } catch (IOException e) {
      return new String(bytes);
    }
  }

  
Sets up a connection, retrying on connect failure.
  private HttpURLConnection connect(final URI urifinal String methodfinal byte[] entity,
                                    final Map<StringList<String>> headers)
             HeliosException {
    final long deadline = currentTimeMillis() + ;
    final int offset = ThreadLocalRandom.current().nextInt();
    while (currentTimeMillis() < deadline) {
      final List<URIendpoints = .get();
      if (endpoints.isEmpty()) {
        throw new RuntimeException("failed to resolve master");
      }
      .debug("endpoint uris are {}"endpoints);
      for (int i = 0; i < endpoints.size() && currentTimeMillis() < deadlinei++) {
        final URI endpoint = endpoints.get(positive(offset + i) % endpoints.size());
        final String fullpath = endpoint.getPath() + uri.getPath();
        final String host = endpoint.getHost();
        final int port = endpoint.getPort();
        if (host == null || port == -1) {
          throw new HeliosException("Master endpoints must be of the form "
                                    + "\"http[s]://heliosmaster.domain.net:<port>\"");
        }
        final URI realUri = new URI("http"host + ":" + portfullpathuri.getQuery(), null);
        try {
          .debug("connecting to {}"realUri);
          return connect0(realUrimethodentityheaders);
        } catch (ConnectException | SocketTimeoutException | UnknownHostException e) {
          // UnknownHostException happens if we can't resolve hostname into IP address.
          // UnknownHostException's getMessage method returns just the hostname which is a useless
          // message, so log the exception class name to provide more info.
          .debug(e.getClass().getSimpleName() + " - " + e.getMessage());
          // Connecting failed, sleep a bit to avoid hammering and then try another endpoint
          Thread.sleep(200);
        }
      }
      .warn("Failed to connect, retrying in 5 seconds.");
      Thread.sleep(5000);
    }
    throw new TimeoutException("Timed out connecting to master");
  }
  private HttpURLConnection connect0(final URI urifinal String methodfinal byte[] entity,
                                     final Map<StringList<String>> headers)
      throws IOException {
    if (.isTraceEnabled()) {
      .trace("req: {} {} {} {} {} {}"methoduri,
                headers.size(),
                Joiner.on(',').withKeyValueSeparator("=").join(headers),
                entity.length, Json.asPrettyStringUnchecked(entity));
    } else {
      .debug("req: {} {} {} {}"methoduriheaders.size(), entity.length);
    }
    final HttpURLConnection connection;
    connection = (HttpURLConnectionuri.toURL().openConnection();
    connection.setRequestProperty("Accept-Encoding""gzip");
    connection.setInstanceFollowRedirects(false);
    connection.setConnectTimeout((int);
    connection.setReadTimeout((int);
    for (Map.Entry<StringList<String>> header : headers.entrySet()) {
      for (final String value : header.getValue()) {
        connection.addRequestProperty(header.getKey(), value);
      }
    }
    if (entity.length > 0) {
      connection.setDoOutput(true);
      connection.getOutputStream().write(entity);
    }
    setRequestMethod(connectionmethod);
    connection.getResponseCode();
    return connection;
  }
  private int positive(final int value) {
    return value < 0 ? value + . : value;
  }
  private void setRequestMethod(final HttpURLConnection connectionfinal String method) {
    // Nasty workaround for ancient HttpURLConnection only supporting few methods
    final Class<?> httpURLConnectionClass = connection.getClass();
    try {
      final Field methodField = httpURLConnectionClass.getSuperclass().getDeclaredField("method");
      methodField.setAccessible(true);
      methodField.set(connectionmethod);
    } catch (NoSuchFieldException | IllegalAccessException e) {
      throw Throwables.propagate(e);
    }
  }
  private <T> ListenableFuture<T> get(final URI urifinal TypeReference<T> typeReference) {
    return get(uri, Json.type(typeReference));
  }
  private <T> ListenableFuture<T> get(final URI urifinal Class<T> clazz) {
    return get(uri, Json.type(clazz));
  }
  private <T> ListenableFuture<T> get(final URI urifinal JavaType javaType) {
    return transform(request(uri"GET"), new ConvertResponseToPojo<T>(javaType));
  }
  private ListenableFuture<Integerput(final URI uri) {
    return status(request(uri"PUT"));
  }
  public ListenableFuture<JobDeployResponsedeploy(final Deployment jobfinal String host) {
    return deploy(jobhost"");
  }
  public ListenableFuture<JobDeployResponsedeploy(final Deployment jobfinal String host,
                                                    final String token) {
    final Set<IntegerdeserializeReturnCodes = ImmutableSet.of(,
                                                                ,
                                                                ,
                                                                );
    return transform(request(uri(path("/hosts/%s/jobs/%s"hostjob.getJobId()),
                                 ImmutableMap.of("token"token)),
                             "PUT"job),
                     ConvertResponseToPojo.create(JobDeployResponse.classdeserializeReturnCodes));
  }
  public ListenableFuture<SetGoalResponsesetGoal(final Deployment jobfinal String host) {
    return setGoal(jobhost"");
  }
  public ListenableFuture<SetGoalResponsesetGoal(final Deployment jobfinal String host,
                                                   final String token) {
    return transform(request(uri(path("/hosts/%s/jobs/%s"hostjob.getJobId()),
                                 ImmutableMap.of("token"token)),
                             "PATCH"job),
                     ConvertResponseToPojo.create(SetGoalResponse.class,
                                                  ImmutableSet.of(,
                                                                  )));
  }
    return transform(req,
                     new Function<ResponseInteger>() {
                       @Override
                       public Integer apply(final Response reply) {
                         return reply.status;
                       }
                     });
  }
  public ListenableFuture<Deploymentdeployment(final String hostfinal JobId job) {
    return get(uri(path("/hosts/%s/jobs/%s"hostjob)), Deployment.class);
  }
  public ListenableFuture<HostStatushostStatus(final String host) {
    return get(uri(path("/hosts/%s/status"host)), HostStatus.class);
  }
  public ListenableFuture<Map<StringHostStatus>> hostStatuses(final List<Stringhosts) {
    final ConvertResponseToPojo<Map<StringHostStatus>> converter = ConvertResponseToPojo.create(
        TypeFactory.defaultInstance().constructMapType(Map.classString.classHostStatus.class),
        ImmutableSet.of());
    return transform(request(uri("/hosts/statuses"), "POST"hosts), converter);
  }
  public ListenableFuture<IntegerregisterHost(final String hostfinal String id) {
    return put(uri(path("/hosts/%s"host), ImmutableMap.of("id"id)));
  }
    return deleteJob(id"");
  }
  public ListenableFuture<JobDeleteResponsedeleteJob(final JobId idfinal String token) {
    return transform(request(uri(path("/jobs/%s"id),
                                 ImmutableMap.of("token"token)),
                             "DELETE"),
                     ConvertResponseToPojo.create(JobDeleteResponse.class,
                                                  ImmutableSet.of(,
                                                                  ,
                                                                  )));
  }
  public ListenableFuture<JobUndeployResponseundeploy(final JobId jobIdfinal String host) {
    return undeploy(jobIdhost"");
  }
  public ListenableFuture<JobUndeployResponseundeploy(final JobId jobIdfinal String host,
                                                        final String token) {
    return transform(request(uri(path("/hosts/%s/jobs/%s"hostjobId),
                                 ImmutableMap.of("token"token)),
                             "DELETE"),
                     ConvertResponseToPojo.create(JobUndeployResponse.class,
                                                  ImmutableSet.of(,
                                                                  ,
                                                                  )));
  }
    return transform(request(uri(path("/hosts/%s"host)), "DELETE"),
                     ConvertResponseToPojo.create(HostDeregisterResponse.class,
                                                  ImmutableSet.of()));
  }
    return get(uri("/hosts/"), new TypeReference<List<String>>() {});
  }
    return get(uri("/masters/"), new TypeReference<List<String>>() {});
  }
    // Create a fallback in case we fail to connect to the master. Return null if this happens.
    // The transform below will handle this and return an appropriate error message to the caller.
    final ListenableFuture<ResponsefutureWithFallback = withFallback(
        request(uri("/version/"), "GET"),
        new FutureFallback<Response>() {
          @Override
          public ListenableFuture<Responsecreate(Throwable tthrows Exception {
            return immediateFuture(null);
          }
        }
    );
    return transform(
        futureWithFallback,
        new AsyncFunction<ResponseVersionResponse>() {
          @Override
          public ListenableFuture<VersionResponseapply(Response replythrows Exception {
            final String masterVersion =
                reply == null ? "Unable to connect to master" :
                reply.status ==  ? Json.read(reply.payloadString.class) :
                "Master replied with error code " + reply.status;
            return immediateFuture(new VersionResponse(.masterVersion));
          }
        });
  }
  public ListenableFuture<CreateJobResponsecreateJob(final Job descriptor) {
    return transform(request(uri("/jobs/"), "POST"descriptor),
                     ConvertResponseToPojo.create(CreateJobResponse.class,
                                                  ImmutableSet.of()));
  }
  public ListenableFuture<Map<JobIdJob>> jobs(final String query) {
    return get(uri("/jobs", ImmutableMap.of("q"query)), new TypeReference<Map<JobIdJob>>() {});
  }
  public ListenableFuture<Map<JobIdJob>> jobs() {
    return get(uri("/jobs"), new TypeReference<Map<JobIdJob>>() {});
  }
  public ListenableFuture<TaskStatusEventsjobHistory(final JobId jobId) {
    return transform(
        request(uri(path("/history/jobs/%s"jobId.toString())), "GET"),
        ConvertResponseToPojo.create(TaskStatusEvents.class,
                                     ImmutableSet.of()));
  }
  public ListenableFuture<JobStatusjobStatus(final JobId jobId) {
    return get(uri(path("/jobs/%s/status"jobId)), JobStatus.class);
  }
  public ListenableFuture<Map<JobIdJobStatus>> jobStatuses(final Set<JobIdjobs) {
    final ConvertResponseToPojo<Map<JobIdJobStatus>> converter = ConvertResponseToPojo.create(
        TypeFactory.defaultInstance().constructMapType(Map.classJobId.classJobStatus.class),
        ImmutableSet.of());
    
    return transform(request(uri("/jobs/statuses"), "POST"jobs), converter);
  }
  
  private static final class ConvertResponseToPojo<T> implements AsyncFunction<Response, T> {
    private final JavaType javaType;
    private final Set<IntegerdecodeableStatusCodes;
    private ConvertResponseToPojo(final JavaType javaType) {
      this(javaType, ImmutableSet.of());
    }
    public ConvertResponseToPojo(final JavaType typefinal Set<IntegerdecodeableStatusCodes) {
      this. = type;
      this. = decodeableStatusCodes;
    }
    public static <T> ConvertResponseToPojo<T> create(final JavaType type,
                                                      final Set<IntegerdecodeableStatusCodes) {
      return new ConvertResponseToPojo<>(typedecodeableStatusCodes);
    }
    public static <T> ConvertResponseToPojo<T> create(final Class<T> clazz,
                                                      final Set<IntegerdecodeableStatusCodes) {
      return new ConvertResponseToPojo<>(Json.type(clazz), decodeableStatusCodes);
    }
    @Override
    public ListenableFuture<T> apply(final Response reply)
        throws HeliosException {
      if (reply.status ==  && !.contains()) {
        return immediateFuture(null);
      }
      if (!.contains(reply.status)) {
        throw new HeliosException("request failed: " + reply);
      }
      if (reply.payload.length == 0) {
        throw new HeliosException("bad reply: " + reply);
      }
      final T result;
      try {
        result = Json.read(reply.payload);
      } catch (IOException e) {
        throw new HeliosException("bad reply: " + replye);
      }
      return immediateFuture(result);
    }
  }
  public static Builder newBuilder() {
    return new Builder();
  }
  public static class Builder {
    private String user;
    private Supplier<List<URI>> endpointSupplier;
    public Builder setUser(final String user) {
      this. = user;
      return this;
    }
    public Builder setDomain(final String domain) {
      return setEndpointSupplier(Resolver.supplier("helios"domain));
    }
    public Builder setEndpoints(final List<URIendpoints) {
      return setEndpointSupplier(Suppliers.ofInstance(endpoints));
    }
    public Builder setEndpoints(final URI... endpoints) {
      return setEndpointSupplier(Suppliers.ofInstance(asList(endpoints)));
    }
    public Builder setEndpoints(final String... endpoints) {
      return setEndpointStrings(asList(endpoints));
    }
    public Builder setEndpointStrings(final List<Stringendpoints) {
      final List<URIuris = Lists.newArrayList();
      for (String endpoint : endpoints) {
        uris.add(URI.create(endpoint));
      }
      return setEndpoints(uris);
    }
    public Builder setEndpointSupplier(final Supplier<List<URI>> endpointSupplier) {
      this. = endpointSupplier;
      return this;
    }
    public HeliosClient build() {
      return new HeliosClient();
    }
  }

  
Create a new helios client as a specific user, connecting to a helios master cluster in a specific domain.

Parameters:
domain The target domain.
user The user to identify as.
Returns:
A helios client.
  public static HeliosClient create(final String domainfinal String user) {
    return HeliosClient.newBuilder()
        .setDomain(domain)
        .setUser(user)
        .build();
  }
  private static class Response {
    private final String method;
    private final URI uri;
    private final int status;
    private final byte[] payload;
    public Response(final String methodfinal URI urifinal int statusfinal byte[] payload) {
      this. = method;
      this. = uri;
      this. = status;
      this. = payload;
    }
    @Override
    public String toString() {
      return "Response{" +
             "method='" +  + '\'' +
             ", uri=" +  +
             ", status=" +  +
             ", payload=" + decode() +
             '}';
    }
    private String decode(final byte[] payload) {
      if (payload == null) {
        return "";
      }
      final int length = Math.min(payload.length, 1024);
      return new String(payload, 0, length);
    }
  }
New to GrepCode? Check out our FAQ X