co.cask.cdap.internal.AppFabricClient.java Source code

Java tutorial

Introduction

Here is the source code for co.cask.cdap.internal.AppFabricClient.java

Source

/*
 * Copyright  2014-2015 Cask Data, Inc.
 *
 * 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 co.cask.cdap.internal;

import co.cask.cdap.api.common.Bytes;
import co.cask.cdap.api.schedule.ScheduleSpecification;
import co.cask.cdap.api.workflow.WorkflowToken;
import co.cask.cdap.common.BadRequestException;
import co.cask.cdap.common.NotFoundException;
import co.cask.cdap.common.NotImplementedException;
import co.cask.cdap.common.conf.Constants;
import co.cask.cdap.common.namespace.NamespaceAdmin;
import co.cask.cdap.gateway.handlers.AppLifecycleHttpHandler;
import co.cask.cdap.gateway.handlers.NamespaceHttpHandler;
import co.cask.cdap.gateway.handlers.ProgramLifecycleHttpHandler;
import co.cask.cdap.gateway.handlers.WorkflowHttpHandler;
import co.cask.cdap.internal.app.BufferFileInputStream;
import co.cask.cdap.internal.app.runtime.schedule.SchedulerException;
import co.cask.cdap.internal.test.AppJarHelper;
import co.cask.cdap.proto.ApplicationDetail;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.Instances;
import co.cask.cdap.proto.NamespaceMeta;
import co.cask.cdap.proto.ProgramRunStatus;
import co.cask.cdap.proto.ProgramType;
import co.cask.cdap.proto.RunRecord;
import co.cask.cdap.proto.ServiceInstances;
import co.cask.cdap.proto.WorkflowNodeStateDetail;
import co.cask.cdap.proto.WorkflowTokenDetail;
import co.cask.cdap.proto.WorkflowTokenNodeDetail;
import co.cask.cdap.proto.artifact.AppRequest;
import co.cask.cdap.proto.codec.ScheduleSpecificationCodec;
import co.cask.cdap.proto.codec.WorkflowTokenDetailCodec;
import co.cask.cdap.proto.codec.WorkflowTokenNodeDetailCodec;
import co.cask.cdap.proto.id.ApplicationId;
import co.cask.cdap.proto.id.NamespaceId;
import co.cask.cdap.proto.id.ProgramId;
import co.cask.cdap.proto.id.ProgramRunId;
import co.cask.http.BodyConsumer;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import org.apache.twill.filesystem.Location;
import org.apache.twill.filesystem.LocationFactory;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
 * Client tool for AppFabricHttpHandler.
 */
public class AppFabricClient {
    private static final Logger LOG = LoggerFactory.getLogger(AppFabricClient.class);
    private static final Gson GSON = new GsonBuilder()
            .registerTypeAdapter(ScheduleSpecification.class, new ScheduleSpecificationCodec())
            .registerTypeAdapter(WorkflowTokenDetail.class, new WorkflowTokenDetailCodec())
            .registerTypeAdapter(WorkflowTokenNodeDetail.class, new WorkflowTokenNodeDetailCodec()).create();
    private static final Type MAP_TYPE = new TypeToken<Map<String, String>>() {
    }.getType();
    private static final Type RUN_RECORDS_TYPE = new TypeToken<List<RunRecord>>() {
    }.getType();
    private static final Type SCHEDULES_TYPE = new TypeToken<List<ScheduleSpecification>>() {
    }.getType();

    private final LocationFactory locationFactory;
    private final AppLifecycleHttpHandler appLifecycleHttpHandler;
    private final ProgramLifecycleHttpHandler programLifecycleHttpHandler;
    private final WorkflowHttpHandler workflowHttpHandler;
    private final NamespaceHttpHandler namespaceHttpHandler;
    private final NamespaceAdmin namespaceAdmin;

    @Inject
    public AppFabricClient(LocationFactory locationFactory, AppLifecycleHttpHandler appLifecycleHttpHandler,
            ProgramLifecycleHttpHandler programLifecycleHttpHandler, NamespaceHttpHandler namespaceHttpHandler,
            NamespaceAdmin namespaceAdmin, WorkflowHttpHandler workflowHttpHandler) {
        this.locationFactory = locationFactory;
        this.appLifecycleHttpHandler = appLifecycleHttpHandler;
        this.programLifecycleHttpHandler = programLifecycleHttpHandler;
        this.namespaceHttpHandler = namespaceHttpHandler;
        this.namespaceAdmin = namespaceAdmin;
        this.workflowHttpHandler = workflowHttpHandler;
    }

    private String getNamespacePath(String namespaceId) {
        return String.format("%s/namespaces/%s", Constants.Gateway.API_VERSION_3, namespaceId);
    }

    public void reset() throws Exception {
        MockResponder responder;
        HttpRequest request;

        // delete all namespaces
        for (NamespaceMeta namespaceMeta : namespaceAdmin.list()) {
            Id.Namespace namespace = Id.Namespace.from(namespaceMeta.getName());

            responder = new MockResponder();
            request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE, String.format(
                    "%s/unrecoverable/namespaces/%s/datasets", Constants.Gateway.API_VERSION_3, namespace.getId()));
            namespaceHttpHandler.deleteDatasets(request, responder, namespaceMeta.getName());
            verifyResponse(HttpResponseStatus.OK, responder.getStatus(),
                    String.format("could not delete datasets in namespace '%s'", namespace.getId()));

            responder = new MockResponder();
            request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE,
                    String.format("/v3/unrecoverable/namespaces/%s", namespace.getId()));
            namespaceHttpHandler.delete(request, responder, namespaceMeta.getName());
            verifyResponse(HttpResponseStatus.OK, responder.getStatus(),
                    String.format("could not delete namespace '%s'", namespace.getId()));
        }
    }

    public void startProgram(String namespaceId, String appId, String flowId, ProgramType type,
            Map<String, String> args) throws Exception {

        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/%s/%s/start", getNamespacePath(namespaceId), appId,
                type.getCategoryName(), flowId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri);
        String argString = GSON.toJson(args);
        if (argString != null) {
            request.setContent(ChannelBuffers.wrappedBuffer(argString.getBytes(Charsets.UTF_8)));
        }
        programLifecycleHttpHandler.performAction(request, responder, namespaceId, appId, type.getCategoryName(),
                flowId, "start");
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Start " + type + " failed");
    }

    public void stopProgram(String namespaceId, String appId, String flowId, ProgramType type) throws Exception {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/%s/%s/stop", getNamespacePath(namespaceId), appId,
                type.getCategoryName(), flowId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri);
        programLifecycleHttpHandler.performAction(request, responder, namespaceId, appId, type.getCategoryName(),
                flowId, "stop");
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Stop " + type + " failed");
    }

    public String getStatus(String namespaceId, String appId, String flowId, ProgramType type)
            throws BadRequestException, SchedulerException, NotFoundException {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/%s/%s/status", getNamespacePath(namespaceId), appId, type, flowId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri);
        programLifecycleHttpHandler.getStatus(request, responder, namespaceId, appId, type.getCategoryName(),
                flowId);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Get status " + type + " failed");
        Map<String, String> json = responder.decodeResponseContent(MAP_TYPE);
        return json.get("status");
    }

    public void setWorkerInstances(String namespaceId, String appId, String workerId, int instances)
            throws Exception {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/worker/%s/instances", getNamespacePath(namespaceId), appId,
                workerId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, uri);
        JsonObject json = new JsonObject();
        json.addProperty("instances", instances);
        request.setContent(ChannelBuffers.wrappedBuffer(json.toString().getBytes()));
        programLifecycleHttpHandler.setWorkerInstances(request, responder, namespaceId, appId, workerId);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Set worker instances failed");
    }

    public Instances getWorkerInstances(String namespaceId, String appId, String workerId) {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/worker/%s/instances", getNamespacePath(namespaceId), appId,
                workerId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        programLifecycleHttpHandler.getWorkerInstances(request, responder, namespaceId, appId, workerId);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Get worker instances failed");
        return responder.decodeResponseContent(Instances.class);
    }

    public void setServiceInstances(String namespaceId, String applicationId, String serviceName, int instances)
            throws Exception {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/services/%s/instances", getNamespacePath(namespaceId), applicationId,
                serviceName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, uri);
        JsonObject json = new JsonObject();
        json.addProperty("instances", instances);
        request.setContent(ChannelBuffers.wrappedBuffer(json.toString().getBytes()));
        programLifecycleHttpHandler.setServiceInstances(request, responder, namespaceId, applicationId,
                serviceName);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Set service instances failed");
    }

    public ServiceInstances getServiceInstances(String namespaceId, String applicationId, String serviceName) {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/services/%s/instances", getNamespacePath(namespaceId), applicationId,
                serviceName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        programLifecycleHttpHandler.getServiceInstances(request, responder, namespaceId, applicationId,
                serviceName);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Get service instances failed");
        return responder.decodeResponseContent(ServiceInstances.class);
    }

    public void setFlowletInstances(String namespaceId, String applicationId, String flowId, String flowletName,
            int instances) throws Exception {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/flows/%s/flowlets/%s/instances/%s", getNamespacePath(namespaceId),
                applicationId, flowId, flowletName, instances);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, uri);
        JsonObject json = new JsonObject();
        json.addProperty("instances", instances);
        request.setContent(ChannelBuffers.wrappedBuffer(json.toString().getBytes()));
        programLifecycleHttpHandler.setFlowletInstances(request, responder, namespaceId, applicationId, flowId,
                flowletName);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Set flowlet instances failed");
    }

    public Instances getFlowletInstances(String namespaceId, String applicationId, String flowName,
            String flowletName) {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/flows/%s/flowlets/%s/instances", getNamespacePath(namespaceId),
                applicationId, flowName, flowletName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        programLifecycleHttpHandler.getFlowletInstances(request, responder, namespaceId, applicationId, flowName,
                flowletName);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Get flowlet instances failed");
        return responder.decodeResponseContent(Instances.class);
    }

    public List<ScheduleSpecification> getSchedules(String namespaceId, String appId, String wflowId) {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/workflows/%s/schedules", getNamespacePath(namespaceId), appId,
                wflowId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        workflowHttpHandler.getWorkflowSchedules(request, responder, namespaceId, appId, wflowId);

        List<ScheduleSpecification> schedules = responder.decodeResponseContent(SCHEDULES_TYPE, GSON);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Getting workflow schedules failed");
        return schedules;
    }

    public WorkflowTokenDetail getWorkflowToken(String namespaceId, String appId, String wflowId, String runId,
            @Nullable WorkflowToken.Scope scope, @Nullable String key) throws NotFoundException {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/workflows/%s/runs/%s/token", getNamespacePath(namespaceId), appId,
                wflowId, runId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        scope = scope == null ? WorkflowToken.Scope.USER : scope;
        key = key == null ? "" : key;
        workflowHttpHandler.getWorkflowToken(request, responder, namespaceId, appId, wflowId, runId, scope.name(),
                key);

        Type workflowTokenDetailType = new TypeToken<WorkflowTokenDetail>() {
        }.getType();
        WorkflowTokenDetail workflowTokenDetail = responder.decodeResponseContent(workflowTokenDetailType, GSON);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Getting workflow token failed");
        return workflowTokenDetail;
    }

    public WorkflowTokenNodeDetail getWorkflowToken(String namespaceId, String appId, String wflowId, String runId,
            String nodeName, @Nullable WorkflowToken.Scope scope, @Nullable String key) throws NotFoundException {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/workflows/%s/runs/%s/nodes/%s/token", getNamespacePath(namespaceId),
                appId, wflowId, runId, nodeName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        scope = scope == null ? WorkflowToken.Scope.USER : scope;
        key = key == null ? "" : key;
        workflowHttpHandler.getWorkflowToken(request, responder, namespaceId, appId, wflowId, runId, nodeName,
                scope.name(), key);

        Type workflowTokenNodeDetailType = new TypeToken<WorkflowTokenNodeDetail>() {
        }.getType();
        WorkflowTokenNodeDetail workflowTokenDetail = responder.decodeResponseContent(workflowTokenNodeDetailType,
                GSON);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Getting workflow token at node failed");
        return workflowTokenDetail;
    }

    public Map<String, WorkflowNodeStateDetail> getWorkflowNodeStates(ProgramRunId workflowRunId)
            throws NotFoundException {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/workflows/%s/runs/%s/nodes/state",
                getNamespacePath(workflowRunId.getNamespace()), workflowRunId.getApplication(),
                workflowRunId.getProgram(), workflowRunId.getRun());
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        workflowHttpHandler.getWorkflowNodeStates(request, responder, workflowRunId.getNamespace(),
                workflowRunId.getApplication(), workflowRunId.getProgram(), workflowRunId.getRun());

        Type nodeStatesType = new TypeToken<Map<String, WorkflowNodeStateDetail>>() {
        }.getType();
        Map<String, WorkflowNodeStateDetail> nodeStates = responder.decodeResponseContent(nodeStatesType, GSON);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Getting workflow node states failed.");
        return nodeStates;
    }

    public List<RunRecord> getHistory(Id.Program programId, ProgramRunStatus status)
            throws BadRequestException, NotImplementedException, NotFoundException {
        String namespaceId = programId.getNamespaceId();
        String appId = programId.getApplicationId();
        String programName = programId.getId();
        String categoryName = programId.getType().getCategoryName();

        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/%s/%s/runs?status=" + status.name(), getNamespacePath(namespaceId),
                appId, categoryName, programName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        programLifecycleHttpHandler.programHistory(request, responder, namespaceId, appId, categoryName,
                programName, status.name(), null, null, 100);
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Getting workflow history failed");

        return responder.decodeResponseContent(RUN_RECORDS_TYPE);
    }

    public void suspend(String namespaceId, String appId, String scheduleName) throws Exception {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/schedules/%s/suspend", getNamespacePath(namespaceId), appId,
                scheduleName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        programLifecycleHttpHandler.performAction(request, responder, namespaceId, appId, "schedules", scheduleName,
                "suspend");
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Suspend workflow schedules failed");
    }

    public void resume(String namespaceId, String appId, String schedName) throws Exception {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/schedules/%s/resume", getNamespacePath(namespaceId), appId,
                schedName);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        programLifecycleHttpHandler.performAction(request, responder, namespaceId, appId, "schedules", schedName,
                "resume");
        verifyResponse(HttpResponseStatus.OK, responder.getStatus(), "Resume workflow schedules failed");
    }

    public String scheduleStatus(String namespaceId, String appId, String schedId, int expectedResponseCode)
            throws BadRequestException, SchedulerException {
        MockResponder responder = new MockResponder();
        String uri = String.format("%s/apps/%s/schedules/%s/status", getNamespacePath(namespaceId), appId, schedId);
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
        try {
            programLifecycleHttpHandler.getStatus(request, responder, namespaceId, appId, "schedules", schedId);
        } catch (NotFoundException e) {
            return "NOT_FOUND";
        }
        verifyResponse(HttpResponseStatus.valueOf(expectedResponseCode), responder.getStatus(),
                "Get schedules status failed");
        Map<String, String> json = responder.decodeResponseContent(MAP_TYPE);
        return json.get("status");
    }

    private void verifyResponse(HttpResponseStatus expected, HttpResponseStatus actual, String errorMsg) {
        if (!expected.equals(actual)) {
            throw new IllegalStateException(
                    String.format("Expected %s, got %s. Error: %s", expected, actual, errorMsg));
        }
    }

    public Location deployApplication(Id.Namespace namespace, Class<?> applicationClz, String config,
            File... bundleEmbeddedJars) throws Exception {

        Preconditions.checkNotNull(applicationClz, "Application cannot be null.");

        Location deployedJar = AppJarHelper.createDeploymentJar(locationFactory, applicationClz,
                bundleEmbeddedJars);
        LOG.info("Created deployedJar at {}", deployedJar);

        String archiveName = String.format("%s-1.0.%d.jar", applicationClz.getSimpleName(),
                System.currentTimeMillis());
        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
                String.format("/v3/namespaces/%s/apps", namespace.getId()));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        request.setHeader("X-Archive-Name", archiveName);
        if (config != null) {
            request.setHeader("X-App-Config", config);
        }
        MockResponder mockResponder = new MockResponder();
        BodyConsumer bodyConsumer = appLifecycleHttpHandler.deploy(request, mockResponder, namespace.getId(),
                archiveName, config);
        Preconditions.checkNotNull(bodyConsumer, "BodyConsumer from deploy call should not be null");

        try (BufferFileInputStream is = new BufferFileInputStream(deployedJar.getInputStream(), 100 * 1024)) {
            byte[] chunk = is.read();
            while (chunk.length > 0) {
                mockResponder = new MockResponder();
                bodyConsumer.chunk(ChannelBuffers.wrappedBuffer(chunk), mockResponder);
                Preconditions.checkState(mockResponder.getStatus() == null, "failed to deploy app");
                chunk = is.read();
            }
            mockResponder = new MockResponder();
            bodyConsumer.finished(mockResponder);
            verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Failed to deploy app");
        }
        return deployedJar;
    }

    public void deployApplication(Id.Application appId, AppRequest appRequest) throws Exception {

        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT,
                String.format("%s/apps/%s", getNamespacePath(appId.getNamespaceId()), appId.getId()));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        request.setContent(ChannelBuffers.wrappedBuffer(Bytes.toBytes(GSON.toJson(appRequest.getConfig()))));

        MockResponder mockResponder = new MockResponder();

        BodyConsumer bodyConsumer = appLifecycleHttpHandler.create(request, mockResponder, appId.getNamespaceId(),
                appId.getId());
        Preconditions.checkNotNull(bodyConsumer, "BodyConsumer from deploy call should not be null");

        byte[] contents = Bytes.toBytes(GSON.toJson(appRequest));
        Preconditions.checkNotNull(contents);
        bodyConsumer.chunk(ChannelBuffers.wrappedBuffer(contents), mockResponder);
        bodyConsumer.finished(mockResponder);
        verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Failed to deploy app");
    }

    public void updateApplication(ApplicationId appId, AppRequest appRequest) throws Exception {
        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT,
                String.format("%s/apps/%s/update", getNamespacePath(appId.getNamespace()), appId.getApplication()));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        byte[] contents = Bytes.toBytes(GSON.toJson(appRequest));
        Preconditions.checkNotNull(contents);
        request.setContent(ChannelBuffers.wrappedBuffer(contents));
        MockResponder mockResponder = new MockResponder();
        appLifecycleHttpHandler.updateApp(request, mockResponder, appId.getNamespace(), appId.getApplication());
        verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Updating app failed");
    }

    public void deleteApplication(ApplicationId appId) throws Exception {
        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE,
                String.format("%s/apps/%s", getNamespacePath(appId.getNamespace()), appId.getApplication()));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        MockResponder mockResponder = new MockResponder();
        appLifecycleHttpHandler.deleteApp(request, mockResponder, appId.getNamespace(), appId.getApplication());
        verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Deleting app failed");
    }

    public void deleteAllApplications(NamespaceId namespaceId) throws Exception {
        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.DELETE,
                String.format("%s/apps", getNamespacePath(namespaceId.getNamespace())));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        MockResponder mockResponder = new MockResponder();
        appLifecycleHttpHandler.deleteAllApps(request, mockResponder, namespaceId.getNamespace());
        verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Deleting all apps failed");
    }

    public ApplicationDetail getInfo(ApplicationId appId) throws Exception {
        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
                String.format("%s/apps/%s", getNamespacePath(appId.getNamespace()), appId.getApplication()));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        MockResponder mockResponder = new MockResponder();
        appLifecycleHttpHandler.getAppInfo(request, mockResponder, appId.getNamespace(), appId.getApplication());
        verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Getting app info failed");
        return mockResponder.decodeResponseContent(new TypeToken<ApplicationDetail>() {
        }.getType(), GSON);
    }

    public void setRuntimeArgs(ProgramId programId, Map<String, String> args) throws Exception {
        DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT,
                String.format("%s/apps/%s/%s/%s/runtimeargs", getNamespacePath(programId.getNamespace()),
                        programId.getApplication(), programId.getType().getCategoryName(), programId.getProgram()));
        request.setHeader(Constants.Gateway.API_KEY, "api-key-example");
        byte[] contents = Bytes.toBytes(GSON.toJson(args));
        Preconditions.checkNotNull(contents);
        request.setContent(ChannelBuffers.wrappedBuffer(contents));
        MockResponder mockResponder = new MockResponder();
        programLifecycleHttpHandler.saveProgramRuntimeArgs(request, mockResponder, programId.getNamespace(),
                programId.getApplication(), programId.getType().getCategoryName(), programId.getProgram());
        verifyResponse(HttpResponseStatus.OK, mockResponder.getStatus(), "Saving runtime arguments failed");
    }
}