com.continuuity.loom.scheduler.callback.HttpPostClusterCallback.java Source code

Java tutorial

Introduction

Here is the source code for com.continuuity.loom.scheduler.callback.HttpPostClusterCallback.java

Source

/*
 * Copyright 2012-2014, Continuuity, 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 com.continuuity.loom.scheduler.callback;

import com.continuuity.loom.cluster.Node;
import com.continuuity.loom.common.conf.Configuration;
import com.continuuity.loom.common.conf.Constants;
import com.continuuity.loom.scheduler.ClusterAction;
import com.continuuity.loom.store.cluster.ClusterStoreService;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.SocketConfig;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.Set;

/**
 * Executes before and after hooks by sending an HTTP POST request to some configurable endpoints, with the post body
 * containing the cluster and job objects, assuming there is a valid url assigned to the start, success, and/or failure
 * urls. If no url is specified, no request will be sent. Additionally, trigger actions can be configured so that
 * the HTTP POST request is sent only for specific cluster actions. This is done by specifying a comma separated list
 * of {@link ClusterAction}s in the configuration for start, success, and/or triggers.
 */
public class HttpPostClusterCallback implements ClusterCallback {
    private static final Logger LOG = LoggerFactory.getLogger(HttpPostClusterCallback.class);
    private final Gson gson = new Gson();
    private String onStartUrl;
    private String onSuccessUrl;
    private String onFailureUrl;
    private Set<ClusterAction> startTriggerActions;
    private Set<ClusterAction> successTriggerActions;
    private Set<ClusterAction> failureTriggerActions;
    private HttpClient httpClient;
    private ClusterStoreService clusterStoreService;

    public void initialize(Configuration conf, ClusterStoreService clusterStoreService) {
        this.clusterStoreService = clusterStoreService;
        this.onStartUrl = conf.get(Constants.HttpCallback.START_URL);
        this.onSuccessUrl = conf.get(Constants.HttpCallback.SUCCESS_URL);
        this.onFailureUrl = conf.get(Constants.HttpCallback.FAILURE_URL);
        this.startTriggerActions = parseActionsString(
                conf.get(Constants.HttpCallback.START_TRIGGERS, Constants.HttpCallback.DEFAULT_START_TRIGGERS));
        this.successTriggerActions = parseActionsString(
                conf.get(Constants.HttpCallback.SUCCESS_TRIGGERS, Constants.HttpCallback.DEFAULT_SUCCESS_TRIGGERS));
        this.failureTriggerActions = parseActionsString(
                conf.get(Constants.HttpCallback.FAILURE_TRIGGERS, Constants.HttpCallback.DEFAULT_FAILURE_TRIGGERS));
        if (onStartUrl != null) {
            LOG.debug("before hook will be triggered on actions {}", Joiner.on(',').join(startTriggerActions));
        }
        if (onSuccessUrl != null) {
            LOG.debug("after hook will be triggered on actions {}", Joiner.on(',').join(successTriggerActions));
        }
        if (onFailureUrl != null) {
            LOG.debug("after hook will be triggered on actions {}", Joiner.on(',').join(failureTriggerActions));
        }

        int maxConnections = conf.getInt(Constants.HttpCallback.MAX_CONNECTIONS,
                Constants.HttpCallback.DEFAULT_MAX_CONNECTIONS);
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setDefaultMaxPerRoute(maxConnections);
        connectionManager.setMaxTotal(maxConnections);

        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(
                conf.getInt(Constants.HttpCallback.SOCKET_TIMEOUT, Constants.HttpCallback.DEFAULT_SOCKET_TIMEOUT))
                .build();
        connectionManager.setDefaultSocketConfig(socketConfig);
        this.httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager).build();
    }

    private Set<ClusterAction> parseActionsString(String actionsStr) {
        if (actionsStr == null) {
            return ImmutableSet.of();
        }

        Iterator<String> actionIter = Splitter.on(',').split(actionsStr).iterator();
        Set<ClusterAction> actions = Sets.newHashSet();
        while (actionIter.hasNext()) {
            String actionStr = actionIter.next();
            try {
                ClusterAction action = ClusterAction.valueOf(actionStr.toUpperCase());
                actions.add(action);
            } catch (IllegalArgumentException e) {
                LOG.warn("Unknown cluster action " + actionStr + ". Hooks will not be executed for that action");
            }
        }
        return actions;
    }

    public boolean onStart(CallbackData data) {
        ClusterAction jobAction = data.getJob().getClusterAction();
        if (startTriggerActions.contains(jobAction)) {
            if (onStartUrl != null) {
                LOG.debug("sending request to {} before performing {} on cluster {}", onStartUrl, jobAction,
                        data.getCluster().getId());
                sendPost(onStartUrl, data);
            }
        }
        return true;
    }

    public void onSuccess(CallbackData data) {
        ClusterAction jobAction = data.getJob().getClusterAction();
        if (successTriggerActions.contains(data.getJob().getClusterAction())) {
            if (onSuccessUrl != null) {
                LOG.debug("{} completed successfully on cluster {}, sending request to {}", jobAction,
                        data.getCluster().getId(), onSuccessUrl);
                sendPost(onSuccessUrl, data);
            }
        }
    }

    @Override
    public void onFailure(CallbackData data) {
        ClusterAction jobAction = data.getJob().getClusterAction();
        if (failureTriggerActions.contains(data.getJob().getClusterAction())) {
            if (onFailureUrl != null) {
                LOG.debug("{} failed on cluster {}, sending request to {}", jobAction, data.getCluster().getId(),
                        onFailureUrl);
                sendPost(onFailureUrl, data);
            }
        }
    }

    private void sendPost(String url, CallbackData data) {
        HttpPost post = new HttpPost(url);
        Set<Node> nodes;
        try {
            nodes = clusterStoreService.getView(data.getCluster().getAccount())
                    .getClusterNodes(data.getCluster().getId());
        } catch (Exception e) {
            LOG.error("Unable to fetch nodes for cluster {}, not sending post request.", data.getCluster().getId());
            return;
        }

        try {
            JsonObject body = new JsonObject();
            body.add("cluster", gson.toJsonTree(data.getCluster()));
            body.add("job", gson.toJsonTree(data.getJob()));
            body.add("nodes", gson.toJsonTree(nodes));
            post.setEntity(new StringEntity(gson.toJson(body)));
            httpClient.execute(post);
        } catch (UnsupportedEncodingException e) {
            LOG.warn("Exception setting http post body", e);
        } catch (ClientProtocolException e) {
            LOG.warn("Exception executing http post callback to " + url, e);
        } catch (IOException e) {
            LOG.warn("Exception executing http post callback to " + url, e);
        } catch (Exception e) {
            LOG.warn("Exception executing http post callback to " + url, e);
        } finally {
            post.releaseConnection();
        }
    }
}