org.flowable.http.impl.HttpActivityBehaviorImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.flowable.http.impl.HttpActivityBehaviorImpl.java

Source

/* 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.flowable.http.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpMessage;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.flowable.bpmn.model.FieldExtension;
import org.flowable.bpmn.model.FlowableHttpRequestHandler;
import org.flowable.bpmn.model.FlowableHttpResponseHandler;
import org.flowable.bpmn.model.HttpServiceTask;
import org.flowable.bpmn.model.ImplementationType;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.engine.cfg.HttpClientConfig;
import org.flowable.engine.common.api.FlowableException;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.Expression;
import org.flowable.engine.impl.bpmn.parser.FieldDeclaration;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.el.FixedValue;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.http.HttpActivityBehavior;
import org.flowable.http.HttpRequest;
import org.flowable.http.HttpResponse;
import org.flowable.http.delegate.HttpRequestHandler;
import org.flowable.http.delegate.HttpResponseHandler;
import org.flowable.http.impl.handler.ClassDelegateHttpHandler;
import org.flowable.http.impl.handler.DelegateExpressionHttpHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of HttpActivityBehavior using Apache HTTP Client
 *
 * @author Harsha Teja Kanna.
 */
public class HttpActivityBehaviorImpl extends HttpActivityBehavior {

    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpActivityBehaviorImpl.class);

    protected HttpServiceTask httpServiceTask;

    protected final Timer timer = new Timer(true);
    protected final CloseableHttpClient client;

    public HttpActivityBehaviorImpl() {
        HttpClientConfig config = CommandContextUtil.getProcessEngineConfiguration().getHttpClientConfig();
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

        // https settings
        if (config.isDisableCertVerify()) {
            try {
                SSLContextBuilder builder = new SSLContextBuilder();
                builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
                httpClientBuilder.setSSLSocketFactory(
                        new SSLConnectionSocketFactory(builder.build(), new HostnameVerifier() {
                            public boolean verify(String s, SSLSession sslSession) {
                                return true;
                            }
                        }));

            } catch (Exception e) {
                LOGGER.error("Could not configure HTTP client SSL self signed strategy", e);
            }
        }

        // request retry settings
        int retryCount = 0;
        if (config.getRequestRetryLimit() > 0) {
            retryCount = config.getRequestRetryLimit();
        }
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(retryCount, false));

        // Build http client
        client = httpClientBuilder.build();
        LOGGER.info("HTTP client is initialized");

        // Shutdown hook to close the http client
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                if (client != null) {
                    try {
                        client.close();
                        LOGGER.info("HTTP client is closed");
                    } catch (Throwable e) {
                        LOGGER.error("Could not close http client", e);
                    }
                }
            }
        });
    }

    @Override
    public HttpResponse perform(final DelegateExecution execution, final HttpRequest requestInfo) {

        HttpRequestBase request = null;
        CloseableHttpResponse response = null;

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil
                .getProcessEngineConfiguration();

        try {
            if (httpServiceTask.getHttpRequestHandler() != null) {
                HttpRequestHandler httpRequestHandler = createHttpRequestHandler(
                        httpServiceTask.getHttpRequestHandler(), processEngineConfiguration);
                httpRequestHandler.handleHttpRequest(execution, requestInfo, client);
            }
        } catch (Exception e) {
            throw new FlowableException("Exception while invoking HttpRequestHandler: " + e.getMessage(), e);
        }

        try {
            URIBuilder uri = new URIBuilder(requestInfo.getUrl());
            switch (requestInfo.getMethod()) {
            case "GET": {
                request = new HttpGet(uri.toString());
                break;
            }
            case "POST": {
                HttpPost post = new HttpPost(uri.toString());
                post.setEntity(new StringEntity(requestInfo.getBody()));
                request = post;
                break;
            }
            case "PUT": {
                HttpPut put = new HttpPut(uri.toString());
                put.setEntity(new StringEntity(requestInfo.getBody()));
                request = put;
                break;
            }
            case "DELETE": {
                HttpDelete delete = new HttpDelete(uri.toString());
                request = delete;
                break;
            }
            default: {
                throw new FlowableException(requestInfo.getMethod() + " HTTP method not supported");
            }
            }

            if (requestInfo.getHeaders() != null) {
                setHeaders(request, requestInfo.getHeaders());
            }

            setConfig(request, requestInfo,
                    CommandContextUtil.getProcessEngineConfiguration().getHttpClientConfig());

            if (requestInfo.getTimeout() > 0) {
                timer.schedule(new TimeoutTask(request), requestInfo.getTimeout());
            }

            response = client.execute(request);

            HttpResponse responseInfo = new HttpResponse();

            if (response.getStatusLine() != null) {
                responseInfo.setStatusCode(response.getStatusLine().getStatusCode());
                responseInfo.setProtocol(response.getStatusLine().getProtocolVersion().toString());
                responseInfo.setReason(response.getStatusLine().getReasonPhrase());
            }

            if (response.getAllHeaders() != null) {
                responseInfo.setHeaders(getHeadersAsString(response.getAllHeaders()));
            }

            if (response.getEntity() != null) {
                responseInfo.setBody(EntityUtils.toString(response.getEntity()));
            }

            try {
                if (httpServiceTask.getHttpResponseHandler() != null) {
                    HttpResponseHandler httpResponseHandler = createHttpResponseHandler(
                            httpServiceTask.getHttpResponseHandler(), processEngineConfiguration);
                    httpResponseHandler.handleHttpResponse(execution, responseInfo);
                }
            } catch (Exception e) {
                throw new FlowableException("Exception while invoking HttpResponseHandler: " + e.getMessage(), e);
            }

            return responseInfo;

        } catch (final ClientProtocolException e) {
            throw new FlowableException("HTTP exception occurred", e);
        } catch (final IOException e) {
            throw new FlowableException("IO exception occurred", e);
        } catch (final URISyntaxException e) {
            throw new FlowableException("Invalid URL exception occurred", e);
        } catch (final FlowableException e) {
            throw e;
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (Throwable e) {
                    LOGGER.error("Could not close http response", e);
                }
            }
        }
    }

    protected void setConfig(final HttpRequestBase base, final HttpRequest requestInfo,
            final HttpClientConfig config) {
        base.setConfig(RequestConfig.custom().setRedirectsEnabled(!requestInfo.isNoRedirects())
                .setSocketTimeout(config.getSocketTimeout()).setConnectTimeout(config.getConnectTimeout())
                .setConnectionRequestTimeout(config.getConnectionRequestTimeout()).build());
    }

    protected String getHeadersAsString(final Header[] headers) {
        StringBuilder hb = new StringBuilder();
        for (Header header : headers) {
            hb.append(header.getName()).append(": ").append(header.getValue()).append('\n');
        }
        return hb.toString();
    }

    protected void setHeaders(final HttpMessage base, final String headers) throws IOException {
        try (BufferedReader reader = new BufferedReader(new StringReader(headers))) {
            String line = reader.readLine();
            while (line != null) {
                String[] header = line.split(":");
                if (header.length == 2) {
                    base.addHeader(header[0], header[1]);
                    line = reader.readLine();
                } else {
                    throw new FlowableException(HTTP_TASK_REQUEST_HEADERS_INVALID);
                }
            }
        }
    }

    protected HttpRequestHandler createHttpRequestHandler(FlowableHttpRequestHandler handler,
            ProcessEngineConfigurationImpl processEngineConfiguration) {
        HttpRequestHandler requestHandler = null;

        if (ImplementationType.IMPLEMENTATION_TYPE_CLASS.equalsIgnoreCase(handler.getImplementationType())) {
            requestHandler = new ClassDelegateHttpHandler(handler.getImplementation(),
                    createFieldDeclarations(handler.getFieldExtensions(), processEngineConfiguration));

        } else if (ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION
                .equalsIgnoreCase(handler.getImplementationType())) {
            requestHandler = new DelegateExpressionHttpHandler(
                    processEngineConfiguration.getExpressionManager().createExpression(handler.getImplementation()),
                    createFieldDeclarations(handler.getFieldExtensions(), processEngineConfiguration));
        }
        return requestHandler;
    }

    protected HttpResponseHandler createHttpResponseHandler(FlowableHttpResponseHandler handler,
            ProcessEngineConfigurationImpl processEngineConfiguration) {
        HttpResponseHandler responseHandler = null;

        if (ImplementationType.IMPLEMENTATION_TYPE_CLASS.equalsIgnoreCase(handler.getImplementationType())) {
            responseHandler = new ClassDelegateHttpHandler(handler.getImplementation(),
                    createFieldDeclarations(handler.getFieldExtensions(), processEngineConfiguration));

        } else if (ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION
                .equalsIgnoreCase(handler.getImplementationType())) {
            responseHandler = new DelegateExpressionHttpHandler(
                    processEngineConfiguration.getExpressionManager().createExpression(handler.getImplementation()),
                    createFieldDeclarations(handler.getFieldExtensions(), processEngineConfiguration));
        }
        return responseHandler;
    }

    protected List<FieldDeclaration> createFieldDeclarations(List<FieldExtension> fieldList,
            ProcessEngineConfigurationImpl processEngineConfiguration) {
        List<FieldDeclaration> fieldDeclarations = new ArrayList<>();

        for (FieldExtension fieldExtension : fieldList) {
            FieldDeclaration fieldDeclaration = null;
            if (StringUtils.isNotEmpty(fieldExtension.getExpression())) {
                fieldDeclaration = new FieldDeclaration(fieldExtension.getFieldName(), Expression.class.getName(),
                        processEngineConfiguration.getExpressionManager()
                                .createExpression(fieldExtension.getExpression()));
            } else {
                fieldDeclaration = new FieldDeclaration(fieldExtension.getFieldName(), Expression.class.getName(),
                        new FixedValue(fieldExtension.getStringValue()));
            }

            fieldDeclarations.add(fieldDeclaration);
        }
        return fieldDeclarations;
    }

    protected static class TimeoutTask extends TimerTask {
        private HttpRequestBase request;

        public TimeoutTask(HttpRequestBase request) {
            this.request = request;
        }

        @Override
        public void run() {
            if (request != null) {
                request.abort();
            }
        }
    }

    public void setServiceTask(ServiceTask serviceTask) {
        this.httpServiceTask = (HttpServiceTask) serviceTask;
    }
}