@Path("/vm")
  @DELETE
  @ApiOperation(value = "stops a remote VM", notes = "")
  @ApiResponses(
      value = {
        @ApiResponse(code = 400, message = "No request id supplied"),
        @ApiResponse(code = 404, message = "Request not found"),
        @ApiResponse(
            code = 500,
            message = "Unable to complete request, see response body for error details")
      })
  public Response stopVM(
      @ApiParam(value = "The instance to stop", required = true) Instance instance,
      @ApiParam(value = "The authenticated session token", required = true)
          @HeaderParam("CICSTART.session")
          String sessionToken) {

    ServiceResponse<Void> sr = cloudService.stopInstance(instance, sessionToken);

    if (sr.isRequestOk()) {
      return Response.ok(sr.getPayload()).build();
    } else {
      return Response.status(500).entity(sr.getMessagesAsStrings()).build();
    }
  }
  @Path("/vm")
  @POST
  @ApiOperation(value = "starts a remote VM defined by a macro script", notes = "")
  @ApiResponses(
      value = {
        @ApiResponse(code = 400, message = "No request id supplied"),
        @ApiResponse(code = 404, message = "Request not found"),
        @ApiResponse(
            code = 500,
            message = "Unable to complete request, see response body for error details")
      })
  public Response startVM(
      @ApiParam(value = "The instance specification to use when starting the VM", required = true)
          InstanceSpec instanceSpec,
      @ApiParam(value = "The authenticated session token", required = true)
          @HeaderParam("CICSTART.session")
          String sessionToken) {

    String cloudName = instanceSpec.getCloud();
    String jobId = instanceSpec.getRequestId();
    String imageName = instanceSpec.getImage();
    String flavorName = instanceSpec.getFlavor();

    Image theImage = null;
    ServiceResponse<List<Image>> getImagesSr = cloudService.getImages(cloudName, sessionToken);
    if (!getImagesSr.isRequestOk()) {
      logger.error(getImagesSr.getMessagesAsStrings());
    }
    List<Image> images = getImagesSr.getPayload();
    for (Image image : images) {

      if (image.name.equals(imageName)) {
        theImage = image;
        break;
      }
    }

    if (theImage == null) {
      return Response.status(400).entity("No image named " + imageName).build();
    }

    Flavor flavor = null;
    try {
      flavor = Flavor.valueOf(flavorName.replaceAll("\\.", "_"));
    } catch (IllegalArgumentException e) {
      return Response.status(400).entity("No flavor named " + flavorName).build();
    }

    ServiceResponse<Instance> sr =
        cloudService.startInstance(cloudName, theImage, flavor, sessionToken, jobId);

    if (sr.isRequestOk()) {
      return Response.ok(sr.getPayload()).build();
    } else {
      logger.warn("VM not started because " + sr.getMessagesAsStrings());
      return Response.status(500).entity(sr.getMessagesAsStrings()).build();
    }
  }
  @Path("/{requestId}/status")
  @GET
  @ApiOperation(
      value = "Get the status of this request",
      notes = "The status is either queued, running, or stopped.",
      response = java.lang.String.class)
  @ApiResponses(
      value = {
        @ApiResponse(code = 400, message = "No request id supplied"),
        @ApiResponse(code = 404, message = "Request not found"),
        @ApiResponse(
            code = 500,
            message = "Unable to complete request, see response body for error details")
      })
  public Response getStatus(
      @ApiParam(value = "The request id", required = true) @PathParam("requestId") String requestId,
      @Context UriInfo uriInfo) {

    ServiceResponse<JobStatus> sr = macroService.getStatus(requestId);

    if (sr.isRequestOk()) {
      JobStatus status = sr.getPayload();
      if (status.equals(JobStatus.RUNNING)) {
        return Response.status(Status.SEE_OTHER)
            .entity(status.name())
            .location(
                UriBuilder.fromUri(uriInfo.getBaseUri())
                    .path(getClass())
                    .path(requestId)
                    .path("log/tail")
                    .build())
            .build();
      } else if (status.equals(JobStatus.PENDING)) {
        return Response.status(Status.SEE_OTHER)
            .entity(status.name())
            .location(
                UriBuilder.fromUri(uriInfo.getBaseUri())
                    .path(getClass())
                    .path(requestId)
                    .path("status")
                    .build())
            .build();

      } else {
        return Response.status(Status.SEE_OTHER)
            .entity(status.name())
            .location(
                UriBuilder.fromUri(uriInfo.getBaseUri())
                    .path(getClass())
                    .path(requestId)
                    .path("log")
                    .build())
            .build();
      }
    } else {
      return Response.status(500).entity(sr.getMessagesAsStrings()).build();
    }
  }
  @Path("/run")
  @POST
  @ApiOperation(
      value = "Run a macro",
      notes =
          "Run the given macro.  HTTP 201 is returned along with a "
              + "location header which can be used to access status and logs")
  @ApiResponses(
      value = {
        @ApiResponse(code = 400, message = "No CML script supplied"),
        @ApiResponse(
            code = 500,
            message = "Unable to complete request, see response body for error details")
      })
  public Response runScript(
      @ApiParam(value = "Script to run", required = true) String cmlScript,
      @ApiParam(value = "The authenticated session token", required = true)
          @HeaderParam("CICSTART.session")
          String sessionToken,
      @Context UriInfo uriInfo) {

    if (Strings.isNullOrEmpty(cmlScript)) {
      return Response.status(400).build();
    }

    ServiceResponse<String> sr = macroService.run(cmlScript, sessionToken);
    if (sr.isRequestOk()) {
      String jobId = sr.getPayload();
      return Response.status(201)
          .location(
              UriBuilder.fromUri(uriInfo.getBaseUri())
                  .path(getClass())
                  .path(jobId)
                  .path("status")
                  .build())
          .build();
    } else {
      return Response.status(500).entity(sr.getMessagesAsStrings()).build();
    }
  }
  @Path("/{requestId}/log")
  @PUT
  @ApiOperation(
      value = "Append to the log file",
      notes = "This is normally called by the binary running the macro.")
  @ApiResponses(
      value = {
        @ApiResponse(code = 400, message = "No request id supplied"),
        @ApiResponse(code = 404, message = "Request not found"),
        @ApiResponse(
            code = 500,
            message = "Unable to complete request, see response body for error details")
      })
  public Response appendLog(
      @ApiParam(value = "The request id", required = true) @PathParam("requestId") String requestId,
      @ApiParam(value = "value", required = true) @QueryParam("value") String value) {

    ServiceResponse<Void> sr = macroService.writeToLogBuffer(requestId, value);
    if (sr.isRequestOk()) {
      return Response.status(200).build();
    } else {
      return Response.status(404).build();
    }
  }
  @Path("/bin")
  @POST
  @Consumes(MediaType.APPLICATION_OCTET_STREAM)
  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  @ApiOperation(
      value = "Download the macro binary that would run on the server",
      notes =
          "optionally contains an embedded JRE if "
              + "none are available on the resource running the macro")
  @ApiResponses(
      value = {
        @ApiResponse(code = 400, message = "No request id supplied"),
        @ApiResponse(code = 404, message = "Request not found"),
        @ApiResponse(
            code = 500,
            message = "Unable to complete request, see response body for error details")
      })
  public Response getMacroBinaryClient(
      @ApiParam(value = "Script to run", required = true) byte[] cmlScript,
      @ApiParam(value = "The authenticated session token", required = true)
          @HeaderParam("CICSTART.session")
          String sessionToken,
      @ApiParam(value = "Include embedded JRE?", required = false, defaultValue = "false")
          @QueryParam("include_jre")
          boolean includeJre,
      @ApiParam(
              value = "Client running same network as CICSTART server?",
              required = false,
              defaultValue = "false")
          @QueryParam("use_internal_network")
          boolean useInternalNetwork,
      @ApiParam(value = "An existing job id if known", required = false) @QueryParam("job_id")
          String jobId,
      @Context final HttpServletResponse response) {

    ServiceResponse<File> sr =
        macroService.assembleClient(
            new String(cmlScript, Charset.forName("UTF-8")),
            sessionToken,
            includeJre,
            useInternalNetwork,
            jobId);
    if (sr.isRequestOk()) {

      File clientBinary = sr.getPayload();
      response.setHeader(
          "Content-Disposition", "attachment; " + "filename=\"" + clientBinary.getName() + "\"");
      response.setContentType(MediaType.APPLICATION_OCTET_STREAM);
      try {

        IOUtils.copy(new FileInputStream(clientBinary), response.getOutputStream());

      } catch (IOException e) {
        return Response.status(500).entity(Throwables.getStackTraceAsString(e)).build();

      } finally {
        clientBinary.delete();
      }
      return Response.ok().build();
    } else {
      return Response.status(500).entity(sr.getMessagesAsStrings()).build();
    }
  }