com.ejisto.modules.cargo.CargoManager.java Source code

Java tutorial

Introduction

Here is the source code for com.ejisto.modules.cargo.CargoManager.java

Source

/*
 * Ejisto, a powerful developer assistant
 *
 * Copyright (C) 2010-2014 Celestino Bellone
 *
 * Ejisto is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ejisto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.ejisto.modules.cargo;

import com.ejisto.constants.StringConstants;
import com.ejisto.core.container.ContainerManager;
import com.ejisto.core.container.WebApplication;
import com.ejisto.event.EventManager;
import com.ejisto.event.def.ApplicationScanRequired;
import com.ejisto.event.def.ChangeServerStatus;
import com.ejisto.modules.cargo.logging.ServerLogger;
import com.ejisto.modules.cargo.util.ContainerInstaller;
import com.ejisto.modules.dao.entities.Container;
import com.ejisto.modules.dao.entities.ContainerType;
import com.ejisto.modules.dao.entities.WebApplicationDescriptor;
import com.ejisto.modules.repository.ContainersRepository;
import com.ejisto.modules.repository.SettingsRepository;
import com.ejisto.modules.repository.WebApplicationRepository;
import com.ejisto.util.ContainerUtils;
import com.ejisto.util.IOUtils;
import lombok.extern.log4j.Log4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.cargo.container.LocalContainer;
import org.codehaus.cargo.container.State;
import org.codehaus.cargo.container.configuration.Configuration;
import org.codehaus.cargo.container.configuration.ConfigurationType;
import org.codehaus.cargo.container.configuration.LocalConfiguration;
import org.codehaus.cargo.container.deployable.Deployable;
import org.codehaus.cargo.container.deployable.DeployableType;
import org.codehaus.cargo.container.deployable.WAR;
import org.codehaus.cargo.container.deployer.Deployer;
import org.codehaus.cargo.container.deployer.URLDeployableMonitor;
import org.codehaus.cargo.container.property.GeneralPropertySet;
import org.codehaus.cargo.container.property.ServletPropertySet;
import org.codehaus.cargo.container.spi.AbstractInstalledLocalContainer;
import org.codehaus.cargo.container.tomcat.TomcatPropertySet;
import org.codehaus.cargo.generic.DefaultContainerFactory;
import org.codehaus.cargo.generic.configuration.DefaultConfigurationFactory;
import org.codehaus.cargo.generic.deployable.DefaultDeployableFactory;
import org.codehaus.cargo.generic.deployable.DeployableFactory;
import org.codehaus.cargo.generic.deployer.DefaultDeployerFactory;
import org.codehaus.cargo.generic.deployer.DeployerFactory;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import static com.ejisto.constants.StringConstants.DEFAULT_CONTAINER_ID;
import static com.ejisto.constants.StringConstants.HTTP_LISTEN_PORT;
import static com.ejisto.core.container.WebApplication.Status.STARTED;
import static com.ejisto.util.IOUtils.guessWebApplicationUri;

/**
 * Created by IntelliJ IDEA.
 * User: celestino
 * Date: 2/18/11
 * Time: 7:23 PM
 */
@Log4j
public class CargoManager implements ContainerManager {

    private final ContainersRepository containersRepository;
    private final SettingsRepository settingsRepository;
    private final WebApplicationRepository webApplicationRepository;
    private final EventManager eventManager;

    private final ConcurrentMap<String, AbstractInstalledLocalContainer> installedContainers = new ConcurrentHashMap<>();
    private final ReentrantLock lifeCycleOperationLock = new ReentrantLock();
    private final Set<String> runningContainers = new CopyOnWriteArraySet<>();

    public CargoManager(ContainersRepository containersRepository, SettingsRepository settingsRepository,
            WebApplicationRepository webApplicationRepository, EventManager eventManager) {
        this.containersRepository = containersRepository;
        this.settingsRepository = settingsRepository;
        this.webApplicationRepository = webApplicationRepository;
        this.eventManager = eventManager;
    }

    @Override
    public String downloadAndInstall(String urlToString, String folder, ContainerType containerType)
            throws IOException {
        URL url = new URL(urlToString.trim());
        ContainerInstaller installer = new ContainerInstaller(url, folder);
        installer.install();
        containersRepository.registerDefaultContainer(containerType, installer.getHome(), containerType.getName());
        return installer.getHome();
    }

    @Override
    public boolean isServerRunning() {
        return runningContainers.contains(DEFAULT_CONTAINER_ID.getValue());
    }

    @Override
    public boolean isRunning(String containerId) {
        return runningContainers.contains(containerId);
    }

    @Override
    public boolean startDefault() throws NotInstalledException {
        return start(containersRepository.loadDefault());
    }

    @Override
    public boolean stopDefault() throws NotInstalledException {
        return stop(containersRepository.loadDefault());
    }

    @Override
    public void stopAllRunningContainers() throws NotInstalledException {
        for (String runningContainer : runningContainers) {
            stop(containersRepository.loadContainer(runningContainer));
        }
    }

    @Override
    public boolean start(Container container) {
        return start(loadCargoContainer(container, true), container);
    }

    @Override
    public boolean stop(Container container) {
        if (!runningContainers.contains(container.getId())) {
            return true;
        }
        AbstractInstalledLocalContainer localContainer = loadCargoContainer(container, false);
        boolean owned = false;
        try {
            owned = lifeCycleOperationLock.tryLock(30, TimeUnit.SECONDS);
            if (owned) {
                if (!runningContainers.contains(container.getId())) {
                    return true;
                }
                DeployerFactory deployerFactory = new DefaultDeployerFactory();
                Deployer deployer = deployerFactory.createDeployer(localContainer);
                localContainer.getConfiguration().getDeployables().forEach(deployer::undeploy);
                localContainer.stop();
                runningContainers.remove(container.getId());
                return true;
            }
        } catch (InterruptedException e) {
            log.error("caught InterruptedException", e);
            Thread.currentThread().interrupt();
        } finally {
            if (owned) {
                lifeCycleOperationLock.unlock();
            }
        }
        return false;
    }

    @Override
    public boolean deployToDefaultContainer(WebApplicationDescriptor webApplicationDescriptor)
            throws NotInstalledException {
        return deploy(webApplicationDescriptor, containersRepository.loadDefault());
    }

    @Override
    public boolean deploy(WebApplicationDescriptor webApplicationDescriptor, Container container) {
        LocalContainer localContainer = loadCargoContainer(container, false);
        boolean started = runningContainers.contains(container.getId());
        if (started) {
            eventManager.publishEventAndWait(
                    new ChangeServerStatus(this, container.getId(), ChangeServerStatus.Command.SHUTDOWN));
        }
        String deployId = UUID.randomUUID().toString();
        eventManager.publishEventAndWait(new ApplicationScanRequired(this, deployId, webApplicationDescriptor));
        Deployable deployable = staticDeploy(webApplicationDescriptor, deployId, localContainer);
        if (deployable == null) {
            return false;
        }
        webApplicationRepository.registerWebApplication(container.getId(),
                new CargoWebApplication(webApplicationDescriptor.getContextPath(), container.getId(), deployable));
        if (started) {
            eventManager.publishEventAndWait(
                    new ChangeServerStatus(this, container.getId(), ChangeServerStatus.Command.STARTUP));
        }
        return true;
    }

    @Override
    public boolean undeploy(String containerId, String contextPath) throws NotInstalledException {
        WebApplication<?> webApplication = webApplicationRepository
                .getRegisteredWebApplication(containerId, contextPath).orElseThrow(IllegalArgumentException::new);
        Deployable deployable = (Deployable) webApplication.getContainerWebApplicationDescriptor();
        final AbstractInstalledLocalContainer container = loadCargoContainer(
                containersRepository.loadContainer(containerId), false);
        boolean success = true;
        if (container.getState() == State.STARTED) {
            success = undeploy(DEFAULT_CONTAINER_ID.getValue(), contextPath, deployable, container);
        }
        if (success) {
            webApplicationRepository.unregisterWebApplication(DEFAULT_CONTAINER_ID.getValue(), contextPath);
        }
        return true;
    }

    @Override
    public boolean undeployFromDefaultContainer(String contextPath) throws NotInstalledException {
        return undeploy(DEFAULT_CONTAINER_ID.getValue(), contextPath);
    }

    @Override
    public boolean startWebApplication(String containerId, String contextPath) throws NotInstalledException {
        return start(containerId, contextPath, getDeployableFromRepository(containerId, contextPath),
                loadCargoContainer(containersRepository.loadContainer(containerId), false));
    }

    @Override
    public boolean stopWebApplication(String containerId, String contextPath) throws NotInstalledException {
        return stop(DEFAULT_CONTAINER_ID.getValue(), contextPath,
                getDeployableFromRepository(DEFAULT_CONTAINER_ID.getValue(), contextPath),
                loadCargoContainer(containersRepository.loadContainer(containerId), false));
    }

    @Override
    public boolean startWebApplicationOnDefaultServer(String contextPath) throws NotInstalledException {
        return startWebApplication(DEFAULT_CONTAINER_ID.getValue(), contextPath);
    }

    @Override
    public boolean stopWebApplicationOnDefaultServer(String contextPath) throws NotInstalledException {
        return stopWebApplication(DEFAULT_CONTAINER_ID.getValue(), contextPath);
    }

    @Override
    public String getDefaultHome() throws NotInstalledException {
        return getHome(containersRepository.loadDefault());
    }

    @Override
    public String getHome(Container container) {
        return loadCargoContainer(container, false).getHome();
    }

    private Container buildNewStandaloneContainer() throws NotInstalledException {
        Container defaultContainer = containersRepository.loadDefault();
        Container container = new Container(true);
        container.setId(UUID.randomUUID().toString());
        container.setContainerType(defaultContainer.getContainerType());
        container.setDescription("Temporary Instance");
        container.setHomeDir(defaultContainer.getHomeDir());
        return container;
    }

    @Override
    public Container startStandaloneInstance(Map<String, String> additionalJavaSystemProperties,
            List<WebApplicationDescriptor> webApplications) throws NotInstalledException, IOException {
        Container container = buildNewStandaloneContainer();
        Path path = Files.createTempDirectory("standalone").toAbsolutePath();
        Configuration configuration = new DefaultConfigurationFactory().createConfiguration(
                container.getContainerType().getCargoID(), org.codehaus.cargo.container.ContainerType.INSTALLED,
                ConfigurationType.STANDALONE, path.toString());
        int serverPort = IOUtils.findFirstAvailablePort(9090);
        configuration.setProperty(ServletPropertySet.PORT, String.valueOf(serverPort));
        configuration.setProperty(TomcatPropertySet.AJP_PORT,
                String.valueOf(IOUtils.findFirstAvailablePort(10000)));
        String existingJvmArgs = configuration.getPropertyValue(GeneralPropertySet.JVMARGS);
        StringBuilder args = new StringBuilder();
        if (StringUtils.isNotBlank(existingJvmArgs)) {
            args.append(existingJvmArgs);
        }
        additionalJavaSystemProperties.entrySet().forEach(
                property -> args.append(" -D").append(property.getKey()).append("=").append(property.getValue()));
        configuration.setProperty(GeneralPropertySet.JVMARGS, args.toString());
        AbstractInstalledLocalContainer instance = createContainer(container.getHomeDir(),
                container.getContainerType().getCargoID(), container.getId(), configuration);
        String deployId = UUID.randomUUID().toString();
        for (WebApplicationDescriptor webApplication : webApplications) {
            eventManager.publishEventAndWait(new ApplicationScanRequired(this, deployId, webApplication));
            staticDeploy(webApplication, deployId, instance);
        }
        instance.start();
        container.setHomeDir(path.toString());
        container.setPort(serverPort);
        runningContainers.add(container.getId());
        return container;
    }

    private boolean start(LocalContainer localContainer, Container entity) {
        boolean owned = false;
        if (runningContainers.contains(entity.getId())) {
            return false;
        }
        try {
            owned = lifeCycleOperationLock.tryLock(30, TimeUnit.SECONDS);
            if (owned) {
                if (runningContainers.contains(entity.getId())) {
                    return false;
                }
                if (runningContainers.add(entity.getId())) {
                    localContainer.start();
                    return true;
                }
                return false;
            }
        } catch (InterruptedException e) {
            log.error("caught InterruptedException", e);
            Thread.currentThread().interrupt();
        } catch (RuntimeException e) {
            runningContainers.remove(entity.getId());
            throw e;
        } finally {
            if (owned) {
                lifeCycleOperationLock.unlock();
            }
        }
        return false;
    }

    private AbstractInstalledLocalContainer loadDefault(boolean addStartupOptions) throws NotInstalledException {
        Container container = containersRepository.loadDefault();
        return loadCargoContainer(container, addStartupOptions);
    }

    @SuppressWarnings("unchecked")
    private AbstractInstalledLocalContainer loadCargoContainer(Container installedContainer,
            boolean addStartupOptions) {
        String cargoId = installedContainer.getContainerType().getCargoID();
        boolean standalone = installedContainer.isStandalone();
        if (!standalone && installedContainers.containsKey(cargoId)) {
            return installedContainers.get(cargoId);
        }
        //container creation
        AbstractInstalledLocalContainer container = createContainer(installedContainer.getHomeDir(), cargoId,
                installedContainer.getId(), null);
        if (standalone) {
            return container;
        }
        AbstractInstalledLocalContainer existing = installedContainers.putIfAbsent(cargoId, container);
        return existing == null ? container : existing;
    }

    private AbstractInstalledLocalContainer createContainer(String homeDir, String cargoId, String containerId,
            Configuration configuration) {
        if (configuration == null) {
            File configurationDir = new File(homeDir);
            configuration = loadExistingConfiguration(cargoId, configurationDir);
        }
        String agentPath = ContainerUtils.extractAgentJar(System.getProperty("java.class.path"));
        StringBuilder jvmArgs = new StringBuilder("-javaagent:");
        jvmArgs.append(agentPath);
        jvmArgs.append(" -noverify -Djava.net.preferIPv4Stack=true");
        jvmArgs.append(" -Dejisto.http.port=").append(System.getProperty(HTTP_LISTEN_PORT.getValue()));
        jvmArgs.append(" -D").append(StringConstants.CLASS_DEBUG_PATH.getValue()).append("=")
                .append(FilenameUtils.normalize(System.getProperty("java.io.tmpdir") + "/"));
        jvmArgs.append(" -D").append(StringConstants.ACTIVATE_IN_MEMORY_RELOAD.getValue()).append("=false");

        String existingConfiguration = configuration.getPropertyValue(GeneralPropertySet.JVMARGS);
        if (StringUtils.isNotBlank(existingConfiguration)) {
            jvmArgs.append(" ").append(existingConfiguration);
        }
        configuration.setProperty(GeneralPropertySet.JVMARGS, jvmArgs.append(" ").toString());
        DefaultContainerFactory containerFactory = new DefaultContainerFactory();
        AbstractInstalledLocalContainer container = (AbstractInstalledLocalContainer) containerFactory
                .createContainer(cargoId, org.codehaus.cargo.container.ContainerType.INSTALLED, configuration);
        container.setHome(homeDir);
        container.setLogger(new ServerLogger(containerId));
        container.addExtraClasspath(agentPath);
        return container;
    }

    private Configuration loadExistingConfiguration(String containerId, File configurationDir) {
        log.debug("loading existing configuration for container " + containerId);
        return new DefaultConfigurationFactory().createConfiguration(containerId,
                org.codehaus.cargo.container.ContainerType.INSTALLED, ConfigurationType.EXISTING,
                configurationDir.getAbsolutePath());
    }

    @SuppressWarnings("unchecked")
    private Deployable staticDeploy(WebApplicationDescriptor webApplicationDescriptor, String deployId,
            LocalContainer container) {
        try {
            Deployable deployable = createDeployable(webApplicationDescriptor, deployId, container);
            replaceDeployable(deployable, container);
            return deployable;
        } catch (Exception e) {
            log.error("error during static deploy", e);
            return null;
        }
    }

    private boolean undeploy(String containerId, String contextPath, Deployable deployable,
            LocalContainer container) {
        try {
            URLDeployableMonitor monitor = new URLDeployableMonitor(
                    new URL(guessWebApplicationUri(contextPath, settingsRepository)));
            getDeployerFor(container).undeploy(deployable, monitor);
            return true;
        } catch (Exception ex) {
            log.error("error during undeploy", ex);
            return false;
        }
    }

    private boolean stop(String containerId, String contextPath, Deployable deployable, LocalContainer container) {
        try {
            getDeployerFor(container).stop(deployable);
            webApplicationRepository.getRegisteredWebApplication(containerId, contextPath)
                    .orElseThrow(IllegalStateException::new).setStatus(WebApplication.Status.STOPPED);
            return true;
        } catch (Exception ex) {
            log.error("error during web application stop", ex);
            return false;
        }
    }

    private boolean start(String containerId, String contextPath, Deployable deployable, LocalContainer container) {
        try {
            getDeployerFor(container).deploy(deployable);
            webApplicationRepository.getRegisteredWebApplication(containerId, contextPath)
                    .orElseThrow(IllegalStateException::new).setStatus(STARTED);
            return true;
        } catch (Exception ex) {
            log.error("error during web application start", ex);
            return false;
        }
    }

    private Deployer getDeployerFor(LocalContainer container) {
        DeployerFactory deployerFactory = new DefaultDeployerFactory();
        return deployerFactory.createDeployer(container);
    }

    private Deployable createDeployable(WebApplicationDescriptor webApplicationDescriptor, String deployId,
            LocalContainer container) {
        DeployableFactory deployableFactory = new DefaultDeployableFactory();
        Path contextPath = Paths.get(webApplicationDescriptor.getContextPath()).getFileName();
        Path temporaryDeployableDir = Paths.get(System.getProperty("java.io.tmpdir"), deployId)
                .resolve(contextPath);
        Deployable deployable = deployableFactory.createDeployable(container.getId(),
                temporaryDeployableDir.toFile().getAbsolutePath(), DeployableType.WAR);
        ((WAR) deployable).setContext(webApplicationDescriptor.getContextPath());
        return deployable;
    }

    private void replaceDeployable(Deployable replacement, LocalContainer container) {
        LocalConfiguration configuration = container.getConfiguration();
        findDeployable(replacement.getFile(), configuration)
                .ifPresent(d -> configuration.getDeployables().remove(d));
        configuration.addDeployable(replacement);
    }

    private Optional<Deployable> findDeployable(String fileName, LocalConfiguration configuration) {
        return configuration.getDeployables().stream().filter(d -> d.getFile().equals(fileName)).findFirst();
    }

    private Deployable getDeployableFromRepository(String containerId, String contextPath) {
        final WebApplication<?> application = webApplicationRepository
                .getRegisteredWebApplication(containerId, contextPath).orElseThrow(IllegalArgumentException::new);
        return (Deployable) application.getContainerWebApplicationDescriptor();
    }

}