org.flowable.ui.modeler.service.AppDefinitionPublishService.java Source code

Java tutorial

Introduction

Here is the source code for org.flowable.ui.modeler.service.AppDefinitionPublishService.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.ui.modeler.service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Base64;

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

import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.flowable.idm.api.User;
import org.flowable.ui.common.properties.FlowableCommonAppProperties;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.common.tenant.TenantProvider;
import org.flowable.ui.modeler.domain.AppDefinition;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.flowable.ui.modeler.serviceapi.AppDefinitionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * Can't merge this with {@link AppDefinitionService}, as it doesn't have visibility of domain models needed to do the publication.
 * 
 * @author jbarrez
 */
@Service
@Transactional
public class AppDefinitionPublishService extends BaseAppDefinitionService {

    private static final Logger LOGGER = LoggerFactory.getLogger(AppDefinitionPublishService.class);

    protected final FlowableCommonAppProperties properties;
    protected final FlowableModelerAppProperties modelerAppProperties;

    public AppDefinitionPublishService(FlowableCommonAppProperties properties,
            FlowableModelerAppProperties modelerAppProperties) {
        this.properties = properties;
        this.modelerAppProperties = modelerAppProperties;
    }

    @Autowired
    protected TenantProvider tenantProvider;

    public void publishAppDefinition(String comment, Model appDefinitionModel, User user) {

        // Create new version of the app model
        modelService.createNewModelVersion(appDefinitionModel, comment, user);

        String deployableZipName = appDefinitionModel.getKey() + ".zip";

        AppDefinition appDefinition = null;
        try {
            appDefinition = resolveAppDefinition(appDefinitionModel);
        } catch (Exception e) {
            LOGGER.error("Error deserializing app {}", appDefinitionModel.getId(), e);
            throw new InternalServerErrorException("Could not deserialize app definition");
        }

        if (appDefinition != null) {
            byte[] deployZipArtifact = createDeployableZipArtifact(appDefinitionModel, appDefinition);

            if (deployZipArtifact != null) {
                deployZipArtifact(deployableZipName, deployZipArtifact, appDefinitionModel.getKey(),
                        appDefinitionModel.getName());
            }
        }
    }

    protected void deployZipArtifact(String artifactName, byte[] zipArtifact, String deploymentKey,
            String deploymentName) {
        String deployApiUrl = modelerAppProperties.getDeploymentApiUrl();
        Assert.hasText(deployApiUrl, "flowable.modeler.app.deployment-api-url must be set");
        String basicAuthUser = properties.getIdmAdmin().getUser();
        String basicAuthPassword = properties.getIdmAdmin().getPassword();

        String tenantId = tenantProvider.getTenantId();
        if (!deployApiUrl.endsWith("/")) {
            deployApiUrl = deployApiUrl.concat("/");
        }
        deployApiUrl = deployApiUrl
                .concat(String.format("app-repository/deployments?deploymentKey=%s&deploymentName=%s",
                        encode(deploymentKey), encode(deploymentName)));

        if (tenantId != null) {
            StringBuilder sb = new StringBuilder(deployApiUrl);
            sb.append("&tenantId=").append(encode(tenantId));
            deployApiUrl = sb.toString();
        }

        HttpPost httpPost = new HttpPost(deployApiUrl);
        httpPost.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + new String(Base64.getEncoder()
                .encode((basicAuthUser + ":" + basicAuthPassword).getBytes(Charset.forName("UTF-8")))));

        MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
        entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        entityBuilder.addBinaryBody("artifact", zipArtifact, ContentType.DEFAULT_BINARY, artifactName);

        HttpEntity entity = entityBuilder.build();
        httpPost.setEntity(entity);

        HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        try {
            SSLContextBuilder builder = new SSLContextBuilder();
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
            clientBuilder
                    .setSSLSocketFactory(new SSLConnectionSocketFactory(builder.build(), new HostnameVerifier() {
                        @Override
                        public boolean verify(String s, SSLSession sslSession) {
                            return true;
                        }
                    }));

        } catch (Exception e) {
            LOGGER.error("Could not configure SSL for http client", e);
            throw new InternalServerErrorException("Could not configure SSL for http client", e);
        }

        CloseableHttpClient client = clientBuilder.build();

        try {
            HttpResponse response = client.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
                return;
            } else {
                LOGGER.error("Invalid deploy result code: {} for url",
                        response.getStatusLine() + httpPost.getURI().toString());
                throw new InternalServerErrorException("Invalid deploy result code: " + response.getStatusLine());
            }

        } catch (IOException ioe) {
            LOGGER.error("Error calling deploy endpoint", ioe);
            throw new InternalServerErrorException("Error calling deploy endpoint: " + ioe.getMessage());
        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (IOException e) {
                    LOGGER.warn("Exception while closing http client", e);
                }
            }
        }
    }

    protected String encode(String string) {
        if (string != null) {
            try {
                return URLEncoder.encode(string, "UTF-8");
            } catch (UnsupportedEncodingException uee) {
                throw new IllegalStateException("JVM does not support UTF-8 encoding.", uee);
            }
        }
        return null;
    }
}