Java tutorial
/* * Copyright (C) 2007-2017 Crafter Software Corporation. * * This program 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. * * This program 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 org.craftercms.deployer.impl; import java.io.File; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.configuration2.Configuration; import org.craftercms.deployer.api.Deployment; import org.craftercms.deployer.api.DeploymentPipeline; import org.craftercms.deployer.api.Target; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.support.CronTrigger; /** * Default implementation of {@link Target}. * * @author avasquez */ public class TargetImpl implements Target { private static final Logger logger = LoggerFactory.getLogger(TargetImpl.class); public static final String TARGET_ID_FORMAT = "%s-%s"; protected String env; protected String siteName; protected DeploymentPipeline deploymentPipeline; protected File configurationFile; protected Configuration configuration; protected ConfigurableApplicationContext applicationContext; protected ZonedDateTime loadDate; protected ScheduledFuture<?> scheduledDeploymentFuture; protected ExecutorService deploymentExecutor; protected Queue<Deployment> pendingDeployments; protected volatile Deployment currentDeployment; public static String getId(String env, String siteName) { return String.format(TARGET_ID_FORMAT, siteName, env); } public TargetImpl(String env, String siteName, DeploymentPipeline deploymentPipeline, File configurationFile, Configuration configuration, ConfigurableApplicationContext applicationContext) { this.env = env; this.siteName = siteName; this.deploymentPipeline = deploymentPipeline; this.configurationFile = configurationFile; this.configuration = configuration; this.applicationContext = applicationContext; this.loadDate = ZonedDateTime.now(); this.deploymentExecutor = Executors.newSingleThreadExecutor(); this.pendingDeployments = new ConcurrentLinkedQueue<>(); } @Override public String getEnv() { return env; } @Override public String getSiteName() { return siteName; } @Override public String getId() { return getId(env, siteName); } @Override public ZonedDateTime getLoadDate() { return loadDate; } @Override public File getConfigurationFile() { return configurationFile; } @Override public Configuration getConfiguration() { return configuration; } @Override public Deployment deploy(boolean waitTillDone, Map<String, Object> params) { Deployment deployment = new Deployment(this, params); pendingDeployments.add(deployment); Future<?> future = deploymentExecutor.submit(new DeploymentTask()); if (waitTillDone) { logger.debug("Waiting for deployment completion..."); try { future.get(); } catch (InterruptedException | ExecutionException e) { logger.error("Unable to wait for deployment completion", e); } } return deployment; } @Override public void scheduleDeployment(TaskScheduler scheduler, String cronExpression) { scheduledDeploymentFuture = scheduler.schedule(new ScheduledDeploymentTask(), new CronTrigger(cronExpression)); } @Override public Collection<Deployment> getPendingDeployments() { return new ArrayList<>(pendingDeployments); } @Override public Deployment getCurrentDeployment() { return currentDeployment; } @Override public Collection<Deployment> getAllDeployments() { Collection<Deployment> deployments = new ArrayList<>(); Deployment currentDeployment = getCurrentDeployment(); Collection<Deployment> pendingDeployments = getPendingDeployments(); if (currentDeployment != null) { deployments.add(currentDeployment); } if (CollectionUtils.isNotEmpty(pendingDeployments)) { deployments.addAll(pendingDeployments); } return deployments; } @Override public void close() { MDC.put(DeploymentConstants.TARGET_ID_MDC_KEY, getId()); try { logger.info("Closing target '{}'...", getId()); if (scheduledDeploymentFuture != null) { scheduledDeploymentFuture.cancel(true); } deploymentExecutor.shutdownNow(); deploymentPipeline.destroy(); if (applicationContext != null) { applicationContext.close(); } } catch (Exception e) { logger.error("Failed to close '" + getId() + "'", e); } MDC.remove(DeploymentConstants.TARGET_ID_MDC_KEY); } protected class ScheduledDeploymentTask implements Runnable { protected volatile Future<?> future; @Override public void run() { if (future == null || future.isDone()) { pendingDeployments.add(new Deployment(TargetImpl.this)); future = deploymentExecutor.submit(new DeploymentTask()); } } } protected class DeploymentTask implements Runnable { @Override public void run() { currentDeployment = pendingDeployments.remove(); MDC.put(DeploymentConstants.TARGET_ID_MDC_KEY, getId()); try { logger.info("------------------------------------------------------------"); logger.info("Deployment for {} started", getId()); logger.info("------------------------------------------------------------"); deploymentPipeline.execute(currentDeployment); double durationInSecs = currentDeployment.getDuration() / 1000.0; logger.info("------------------------------------------------------------"); logger.info("Deployment for {} finished in {} secs", getId(), String.format("%.3f", durationInSecs)); logger.info("------------------------------------------------------------"); } finally { currentDeployment = null; MDC.remove(DeploymentConstants.TARGET_ID_MDC_KEY); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TargetImpl target = (TargetImpl) o; if (!env.equals(target.env)) { return false; } if (!siteName.equals(target.siteName)) { return false; } if (!configurationFile.equals(target.configurationFile)) { return false; } return loadDate.equals(target.loadDate); } @Override public int hashCode() { int result = env.hashCode(); result = 31 * result + siteName.hashCode(); result = 31 * result + configurationFile.hashCode(); result = 31 * result + loadDate.hashCode(); return result; } @Override public String toString() { return "TargetImpl{" + "env='" + env + '\'' + ", siteName='" + siteName + '\'' + ", configurationFile=" + configurationFile + '}'; } }