org.wso2.carbon.uuf.internal.io.ArtifactAppDeployer.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.uuf.internal.io.ArtifactAppDeployer.java

Source

/*
 *  Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you 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.wso2.carbon.uuf.internal.io;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.uuf.core.App;
import org.wso2.carbon.uuf.exception.FileOperationException;
import org.wso2.carbon.uuf.exception.UUFException;
import org.wso2.carbon.uuf.internal.UUFServer;
import org.wso2.carbon.uuf.internal.core.deployment.AppCreator;
import org.wso2.carbon.uuf.internal.core.deployment.AppDeployer;
import org.wso2.carbon.uuf.internal.core.deployment.ClassLoaderProvider;
import org.wso2.carbon.uuf.internal.io.util.ZipArtifactHandler;
import org.wso2.carbon.uuf.internal.util.NameUtils;
import org.wso2.carbon.uuf.spi.RenderableCreator;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Stream;

/**
 * UUF app deployer.
 */
public class ArtifactAppDeployer implements AppDeployer {

    private static final Logger log = LoggerFactory.getLogger(ArtifactAppDeployer.class);

    private final Path appsRepository;
    private final AppCreator appCreator;
    private final ConcurrentMap<String, App> deployedApps;
    private final ConcurrentMap<String, AppArtifact> pendingToDeployArtifacts;
    private final Object lock;

    public ArtifactAppDeployer(Set<RenderableCreator> renderableCreators) {
        this(Paths.get(System.getProperty("carbon.home", "."), "deployment", "uufapps").toString(),
                renderableCreators);
    }

    public ArtifactAppDeployer(String appsRepositoryPath, Set<RenderableCreator> renderableCreators) {
        this(appsRepositoryPath, renderableCreators, new BundleClassLoaderProvider());
    }

    public ArtifactAppDeployer(String appsRepositoryPath, Set<RenderableCreator> renderableCreators,
            ClassLoaderProvider classLoaderProvider) {
        this(Paths.get(appsRepositoryPath), new AppCreator(renderableCreators, classLoaderProvider));
    }

    public ArtifactAppDeployer(Path appsRepository, AppCreator appCreator) {
        this.appsRepository = appsRepository.toAbsolutePath();
        this.appCreator = appCreator;
        this.deployedApps = new ConcurrentHashMap<>();
        this.pendingToDeployArtifacts = new ConcurrentHashMap<>();
        this.lock = new Object();
    }

    @Override
    public Set<String> deploy() {
        Stream<Path> contents;
        try {
            contents = Files.list(appsRepository);
        } catch (IOException e) {
            throw new FileOperationException("Cannot list UUF apps in '" + appsRepository + "' directory.", e);
        }

        contents.filter(Files::isDirectory).forEach(appPath -> {
            Pair<String, String> appNameContextPath = getAppNameContextPath(appPath);
            pendingToDeployArtifacts.put(appNameContextPath.getRight(),
                    new AppArtifact(appNameContextPath.getLeft(), appPath));
            log.debug("UUF app '{}' added to the pending deployments list.", appNameContextPath.getLeft());
        });
        return Collections.unmodifiableSet(pendingToDeployArtifacts.keySet());
    }

    @Override
    public App getApp(String contextPath) {
        App app = deployedApps.get(contextPath);
        if (app != null) {
            return app;
        } else {
            if (pendingToDeployArtifacts.containsKey(contextPath)) {
                return deployApp(contextPath);
            } else {
                return null;
            }
        }
    }

    private Pair<String, String> getAppNameContextPath(Path appPath) {
        // Fully qualified name of the app is equals to the name of the app directory. This is guaranteed by the UUF
        // Maven plugin.
        String appFullyQualifiedName;
        if (ZipArtifactHandler.isZipArtifact(appPath)) {
            appFullyQualifiedName = ZipArtifactHandler.getAppName(appPath);
        } else {
            appFullyQualifiedName = appPath.getFileName().toString();
        }
        // TODO: 6/28/16 deployment.properties can override app's context path
        return Pair.of(appFullyQualifiedName, ("/" + NameUtils.getSimpleName(appFullyQualifiedName)));
    }

    private App deployApp(String contextPath) {
        App createdApp;
        synchronized (lock) {
            AppArtifact appArtifact = pendingToDeployArtifacts.remove(contextPath);
            if (appArtifact == null) {
                // App is deployed before acquiring the lock.
                return deployedApps.get(contextPath);
            }
            if (!Files.exists(appArtifact.appPath)) {
                // Somehow artifact has been removed/deleted. So we cannot create an app from it.
                log.warn("Cannot deploy UUF app in '{}' as it does not exists anymore.", appArtifact.appPath);
                return null;
            }
            try {
                createdApp = createApp(appArtifact.appName, contextPath, appArtifact.appPath);
            } catch (Exception e) {
                // catching any/all exception/s
                if (UUFServer.isDevModeEnabled()) {
                    /* If the server is in the developer mode, add the artifact back to the 'pendingToDeployArtifacts'
                    map so the developer can correct the error and attempt to re-deploy the artifact. */
                    pendingToDeployArtifacts.put(contextPath, appArtifact);
                }
                throw new UUFException(
                        "An error occurred while deploying UUF app in '" + appArtifact.appPath + "'.", e);
            }
            deployedApps.put(createdApp.getContextPath(), createdApp);
        }
        log.info("UUF app '{}' deployed for context path '{}'.", createdApp.getName(), createdApp.getContextPath());
        return createdApp;
    }

    private App createApp(String appName, String appContextPath, Path appPath) {
        ArtifactAppReference appReference;
        if (ZipArtifactHandler.isZipArtifact(appPath)) {
            appReference = new ArtifactAppReference(ZipArtifactHandler.unzip(appName, appPath));
        } else {
            appReference = new ArtifactAppReference(appPath);
        }
        return appCreator.createApp(appReference, appContextPath);
    }

    private static class AppArtifact {

        private final String appName;
        private final Path appPath;

        public AppArtifact(String appName, Path appPath) {
            this.appName = appName;
            this.appPath = appPath;
        }
    }
}