com.stratuscom.harvester.deployer.FolderBasedAppRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.stratuscom.harvester.deployer.FolderBasedAppRunner.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF 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 com.stratuscom.harvester.deployer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileType;
import com.stratuscom.harvester.ApplicationManager;
import com.stratuscom.harvester.ConfigurationException;
import com.stratuscom.harvester.Context;
import com.stratuscom.harvester.FileUtility;
import com.stratuscom.harvester.Init;
import com.stratuscom.harvester.Injected;
import com.stratuscom.harvester.InjectionStyle;
import com.stratuscom.harvester.MBeanRegistrar;
import com.stratuscom.harvester.MessageNames;
import com.stratuscom.harvester.Name;
import com.stratuscom.harvester.Utils;
import com.stratuscom.harvester.admin.api.ApplicationInfo;
import com.stratuscom.harvester.admin.api.ApplicationStatus;
import com.stratuscom.harvester.deployer.ParseException;

/**
 *
 * A task that deploys and runs all the applications in a given directory when
 * the container is started up.
 */
public class FolderBasedAppRunner implements ApplicationManager {

    private static final Logger log = Logger.getLogger(FolderBasedAppRunner.class.getName(),
            MessageNames.BUNDLE_NAME);

    private String deployDirectory = com.stratuscom.harvester.Strings.DEFAULT_DEPLOY_DIRECTORY;

    @Injected(style = InjectionStyle.BY_TYPE)
    private FileUtility fileUtility = null;

    @Injected(style = InjectionStyle.BY_TYPE)
    private Context context;

    @Injected(style = InjectionStyle.BY_TYPE)
    private StarterServiceDeployer deployer;

    @Injected(style = InjectionStyle.BY_TYPE)
    private MBeanRegistrar mbeanRegistrar;

    @Name
    private String myName = null;

    private Map<String, DeploymentRecord> deployedServices = new HashMap<String, DeploymentRecord>();

    private class DeploymentRecord {

        String name;
        long updateTime;
        FileObject fileObject;
        ServiceLifeCycle serviceLifeCycle;
    }

    private boolean autoDeploy = false;

    public boolean isAutoDeploy() {
        return autoDeploy;
    }

    public void setAutoDeploy(boolean autoDeploy) {
        this.autoDeploy = autoDeploy;
    }

    public int getScanInterval() {
        return scanInterval;
    }

    public void setScanInterval(int scanInterval) {
        this.scanInterval = scanInterval;
    }

    private int scanInterval = 5;

    public String getDeployDirectory() {
        return deployDirectory;
    }

    public void setDeployDirectory(String deployDirectory) {
        this.deployDirectory = deployDirectory;
    }

    FileObject deploymentDirectoryFile = null;

    private String deployerName = null;

    public String getDeployerName() {
        return deployerName;
    }

    public void setDeployerName(String deployerName) {
        this.deployerName = deployerName;
    }

    @Init
    public void init() {
        try {
            tryInitialize();
        } catch (Throwable ex) {
            log.log(Level.SEVERE, MessageNames.STARTUP_DEPLOYER_FAILED_INIT, ex);
            throw new ConfigurationException(ex, MessageNames.STARTUP_DEPLOYER_FAILED_INIT);
        }
    }

    private void tryInitialize() throws IOException, ParseException {
        log.log(Level.FINE, MessageNames.STARTER_SERVICE_DEPLOYER_STARTING, myName);
        /*
        If the deployerName is supplied, look it up and override the injected deployer.
        */
        if (deployerName != null) {
            deployer = (StarterServiceDeployer) context.get(deployerName);
        }
        /*
         Establish the deployment directory.
         */
        deploymentDirectoryFile = fileUtility.getProfileDirectory().resolveFile(deployDirectory);
        if (deploymentDirectoryFile == null || deploymentDirectoryFile.getType() != FileType.FOLDER) {
            log.log(Level.WARNING, MessageNames.NO_DEPLOYMENT_DIRECTORY,
                    new Object[] { deployDirectory, fileUtility.getProfileDirectory() });
        }
        /*
         Do the scan task once - this will launch all the services currently in 
         deploy dir.
         */
        new ScanTask().runOnce();

        if (autoDeploy) {
            /* Now schedule a scan in the required scan time. */
            deployer.workManager.schedule(null, new ScanTask(), getScanInterval(), TimeUnit.SECONDS);
        }
    }

    private void registerApplication(ServiceLifeCycle deployedApp) throws MalformedObjectNameException,
            InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
        Hashtable<String, String> props = new Hashtable<String, String>();
        props.put(com.stratuscom.harvester.Strings.NAME, deployedApp.getName());
        ObjectName oName = new ObjectName(com.stratuscom.harvester.Strings.CONTAINER_JMX_DOMAIN, props);
        mbeanRegistrar.getMbeanServer().registerMBean(deployedApp, oName);
    }

    private void unregisterApplication(ServiceLifeCycle deployedApp) {
        try {
            Hashtable<String, String> props = new Hashtable<String, String>();
            props.put(com.stratuscom.harvester.Strings.NAME, deployedApp.getName());
            ObjectName oName = new ObjectName(com.stratuscom.harvester.Strings.CONTAINER_JMX_DOMAIN, props);
            mbeanRegistrar.getMbeanServer().unregisterMBean(oName);
        } catch (Exception e) {
            log.log(Level.SEVERE, MessageNames.FAILED_TO_REMOVE_MBEAN, new Object[] { deployedApp.getName() });
        }
    }

    private Map<String, DeploymentRecord> scanDeploymentArchives() throws FileSystemException {
        /*
         Go through the deployment directory looking for services to deploy.
         */
        Map<String, DeploymentRecord> deployDirListing = new HashMap<String, DeploymentRecord>();
        deploymentDirectoryFile.refresh();
        List<FileObject> serviceArchives = Utils.findChildrenWithSuffix(deploymentDirectoryFile,
                com.stratuscom.harvester.Strings.JAR);
        if (serviceArchives != null) {
            log.log(Level.FINER, MessageNames.FOUND_SERVICE_ARCHIVES,
                    new Object[] { serviceArchives.size(), deployDirectory });
            for (FileObject serviceArchive : serviceArchives) {
                DeploymentRecord rec = new DeploymentRecord();
                rec.fileObject = serviceArchive;
                rec.name = serviceArchive.getName().getBaseName();
                rec.updateTime = serviceArchive.getContent().getLastModifiedTime();
                deployDirListing.put(rec.name, rec);
            }
        }
        return deployDirListing;
    }

    private class ScanTask implements Runnable {

        public void runOnce() {
            try {
                log.log(Level.FINER, MessageNames.SCANNING_DEPLOYMENT_DIRECTORY, new Object[] { deployDirectory });
                Map<String, DeploymentRecord> deployDirListing = scanDeploymentArchives();
                // DeployDirListing will become the deployedServices collection
                synchDeployedServices(deployedServices, deployDirListing);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }

        public void run() {
            runOnce();
            deployer.workManager.schedule(null, this, scanInterval, TimeUnit.SECONDS);
        }
    }

    private synchronized void synchDeployedServices(Map<String, DeploymentRecord> currentList,
            Map<String, DeploymentRecord> newList) {
        // For each entry
        for (DeploymentRecord rec : newList.values()) {
            // If it isn't already in deployedServices, start it
            DeploymentRecord current = currentList.get(rec.name);
            if (current == null) {
                log.log(Level.FINE, MessageNames.STARTING_SERVICE, new Object[] { rec.name });
                currentList.put(rec.name, rec);
                deployAndStart(rec);
            } else if (current.updateTime != rec.updateTime) {
                // If it's in deployedServices but now newer, stop and restart
                log.log(Level.FINE, MessageNames.UPDATING_SERVICE, new Object[] { rec.name });
                currentList.remove(current.name);
                stopAndRemove(current);
                currentList.put(rec.name, rec);
                deployAndStart(rec);
            }
        }
        // If there are any services left in deployedServices, stop them
        List<DeploymentRecord> removals = new ArrayList<DeploymentRecord>();
        for (DeploymentRecord current : currentList.values()) {
            if (!newList.containsKey(current.name)) {
                removals.add(current);
            }
        }
        for (DeploymentRecord current : removals) {
            log.log(Level.FINE, MessageNames.STOPPING_SERVICE, new Object[] { current.name });
            currentList.remove(current.name);
            stopAndRemove(current);
        }

    }

    private void deployAndStart(DeploymentRecord dr) {
        try {
            /* Try the archive in all the deployers to see if someone can 
             * handle it. For now there's only one.
             */

            /*
             * Create the ApplicationEnvironment for the archive.
             */
            dr.serviceLifeCycle = deployer.deployServiceArchive(myName, dr.fileObject);
            // Register it as an MBean
            registerApplication(dr.serviceLifeCycle);
            dr.serviceLifeCycle.start();
        } catch (Throwable t) {
            log.log(Level.WARNING, MessageNames.FAILED_DEPLOY_SERVICE, dr.name);
            log.log(Level.WARNING, MessageNames.EXCEPTION_THROWN, Utils.stackTrace(t));
        }

    }

    private void stopAndRemove(DeploymentRecord dr) {
        dr.serviceLifeCycle.stop();
        unregisterApplication(dr.serviceLifeCycle);
    }

    public synchronized List<ApplicationInfo> getApplicationInfo() {
        List<ApplicationInfo> info = new ArrayList<ApplicationInfo>(deployedServices.size());
        for (DeploymentRecord rec : deployedServices.values()) {
            ApplicationInfo item = new ApplicationInfo(myName, rec.name, toApplicationStatus(rec.serviceLifeCycle));
            info.add(item);
        }
        return info;
    }

    ApplicationStatus toApplicationStatus(ServiceLifeCycle lc) {
        String status = lc.getStatus();
        if (Strings.RUNNING.equals(status)) {
            return ApplicationStatus.RUNNING;
        }
        if (Strings.IDLE.equals(status)) {
            return ApplicationStatus.STOPPED;
        }
        if (Strings.FAILED.equals(status)) {
            return ApplicationStatus.FAILED;
        }
        return ApplicationStatus.UNKNOWN;
    }
}