/* * Mobicents, Communications Middleware * * Copyright (c) 2008, Red Hat Middleware LLC or third-party * contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * 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 Lesser General Public License * for more details. * * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * * Boston, MA 02110-1301 USA */ package org.mobicents.diameter.server.bootstrap; import java.io.File; import java.io.FileFilter; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.jboss.kernel.Kernel; import org.jboss.kernel.plugins.deployment.xml.BasicXMLDeployer; /** * Simplified deployement framework designed for hot deployement of endpoints and media components. * * Deployement is represented by tree of folders. Each folder may contains one or more deployement descriptors. The most * top deployment directory is referenced as root. Maindeployer creates recursively HDScanner for root and each nested * directoty. The HDScanner corresponding to the root directory is triggered periodicaly by local timer and in it order * starts nested scanners recursively. * * @author kulikov * @author amit bhayani */ public class MainDeployer implements ContainerOperations{ /** JBoss microconatiner kernel */ private Kernel kernel; /** Basic deployer */ private BasicXMLDeployer kernelDeployer; /** Interval for scanning root deployment directory */ private int scanPeriod; /** Filter for selecting descriptors */ private FileFilter fileFilter; /** Root deployment directory as string */ private Set<String> path; /** Root deployment directory as file object */ private File[] root; /** Scanner trigger */ private ScheduledExecutorService executor = null; /** Trigger's controller */ private ScheduledFuture activeScan; /** Logger instance */ private Logger logger = Logger.getLogger(MainDeployer.class); /** * Creates new instance of deployer. */ public MainDeployer() { executor = Executors.newSingleThreadScheduledExecutor(new ScannerThreadFactory()); } /** * Gets the current value of the period used for scanning deployement directory. * * @return the value of the period in milliseconds. */ public int getScanPeriod() { return scanPeriod; } /** * Modifies value of the period used to scan deployement directory. * * @param scanPeriod * the value of the period in milliseconds. */ public void setScanPeriod(int scanPeriod) { this.scanPeriod = scanPeriod; } /** * Gets the path to the to the root deployment directory. * * @return path to deployment directory. */ public Set<String> getPath() { return path; } /** * Modify the path to the root deployment directory * * @param path */ public void setPath(Set<String> path) { this.path = path; root = new File[path.size()]; int count = 0; for (String s : path) { root[count++] = new File(s); } } /** * Gets the filter used by Deployer to select files for deployement. * * @return the file filter object. */ public FileFilter getFileFilter() { return fileFilter; } /** * Assigns file filter used for selection files for deploy. * * @param fileFilter * the file filter object. */ public void setFileFilter(FileFilter fileFilter) { this.fileFilter = fileFilter; } /** * Starts main deployer. * * @param kernel * the jboss microntainer kernel instance. * @param kernelDeployer * the jboss basic deployer. */ public void start(Kernel kernel, BasicXMLDeployer kernelDeployer) { this.kernel = kernel; this.kernelDeployer = kernelDeployer; // If scanPeriod is set to -ve, reset to 0 if (scanPeriod < 0) { this.scanPeriod = 0; } // If scanPeriod is set to less than 1 sec but greater than 0 millisec, re-set to 1 sec if (scanPeriod < 1000 && scanPeriod > 0) { this.scanPeriod = 1000; } for (File f : root) { // create deployement scanner and do first deployement HDScanner scanner = new HDScanner(f); scanner.run(); // hot-deployment disabled for scan period set to 0 if (!(scanPeriod == 0)) { activeScan = executor.scheduleAtFixedRate(scanner, scanPeriod, scanPeriod, TimeUnit.MILLISECONDS); } else { if (logger.isDebugEnabled()) { logger.debug("scanPeriod is set to 0. Hot deployment disabled"); } } } logger.info("[[[[[[[[[ Started " + "]]]]]]]]]"); } /** * Shuts down deployer. */ public void stop() { if (activeScan != null) { activeScan.cancel(true); } logger.info("Stopped"); } /** * Deploys components using specified deployement descriptor. * * @param file * the reference to the deployment descriptor * @throws java.lang.Throwable */ private void deploy(File file) { logger.info("Deploying " + file); String filePath = file.getPath(); if (filePath.endsWith(".xml")) { try { kernelDeployer.deploy(file.toURI().toURL()); kernelDeployer.validate(); logger.info("Deployed " + file); } catch (Throwable t) { logger.info("Could not deploy " + file, t); } } } /** * Undeploys components specified in deployment descriptor. * * @param file * the reference to the deployment descriptor. */ private void undeploy(File file) { logger.info("Undeploying " + file); String filePath = file.getPath(); if (filePath.endsWith(".xml")) { try { kernelDeployer.undeploy(file.toURI().toURL()); } catch (MalformedURLException e) { } logger.info("Undeployed " + file); } } /** * Redeploys components specified in deployment descriptor. * * This method subsequently performs undeployment and deployment procedures. * * @param file * the reference to the deployment descriptor. */ private void redeploy(File file) { undeploy(file); deploy(file); } /** * Deployment scanner. * * The scanner relates to a directory in the deployement structure. It is responsible for processing all descriptors * in this directotry and for triggering nested scanners. */ private class HDScanner implements Runnable { /** directory to which scanner relates */ private File dir; /** nested scanners */ private HashMap<File, HDScanner> scanners = new HashMap(); /** map between descriptor and last deployed time */ private HashMap<File, Deployment> deployments = new HashMap(); /** * Creates new instance of the scanner. * * @param dir * the directory releated to this scanner. */ public HDScanner(File dir) { // we do not any check for file (is it directory) because we know that // always directory this.dir = dir; } /** * This methods is called by local timer. * * It shoudl find changes in the deployement structure and execute corresponding method: * * -deploy for new descriptors; -undeploy for removed descriptors; -redeploy for updated descriptors; -create * nested scanner for new nested directories; -remove nested scanner for deleted nested directories; -run nested * scanners recursively. */ public void run() { // get the fresh list of nested files File[] files = dir.listFiles(); // select list of new files and process this list Collection<File> list = getNew(files); if (!list.isEmpty()) { for (File file : list) { // again, for directories we are creating nested scanners // and deploying desciptors if (file.isDirectory()) { HDScanner childScanner = new HDScanner(file); scanners.put(file, childScanner); // keep reference to nested directory because we need to track // the case when directory will be deleted } else if (file.isFile() && fileFilter.accept(file)) { deploy(file); } deployments.put(file, new Deployment(file)); } } // now same for removed. // determine list of removed files list = getRemoved(files); for (File file : list) { Deployment d = deployments.remove(file); if (d == null) { continue; } if (d.isDirectory()) { HDScanner scanner = scanners.remove(file); if (scanner != null) { scanner.undeployAll(); } } else { undeploy(file); } } // redeploying list = getUpdates(files); for (File file : list) { // ignore directories if (file.isFile() && fileFilter.accept(file)) { redeploy(file); } } // Processing nested deployments Collection<HDScanner> nested = scanners.values(); for (HDScanner scanner : nested) { scanner.run(); } } private void undeployAll() { // undeploy descriptors Set<File> list = deployments.keySet(); for (File file : list) { if (!deployments.get(file).isDirectory()) { undeploy(file); } } deployments.clear(); // recursively undeploy nested directories Collection<HDScanner> nested = scanners.values(); for (HDScanner scanner : nested) { scanner.undeployAll(); } } /** * Collects added descriptors from the specified list. * * @param files * the list of descriptors. * @return the list of new new descriptors. */ private Collection<File> getNew(File[] files) { ArrayList<File> list = new ArrayList(); for (File f : files) { if (!deployments.containsKey(f)) { list.add(f); } } return list; } /** * Collects descriptors that were deleted. * * @param files * the list of currently available descriptors * @return the list of deleted descriptors. */ private Collection<File> getRemoved(File[] files) { List<File> removed = new ArrayList(); Set<File> list = deployments.keySet(); for (File descriptor : list) { boolean found = false; for (File file : files) { if (descriptor.equals(file)) { found = true; break; } } if (!found) { removed.add(descriptor); } } return removed; } /** * Collects descriptors that were updates. * * @param files * the fresh list of descriptors. * @return the list of updated descriptors. */ private Collection<File> getUpdates(File[] files) { ArrayList<File> list = new ArrayList(); for (File f : files) { if (deployments.containsKey(f)) { long lastModified = deployments.get(f).lastModified(); if (lastModified < f.lastModified()) { deployments.get(f).update(f.lastModified()); list.add(f); } } } return list; } } /** * Scanner thread factory. */ private class ScannerThreadFactory implements ThreadFactory { /** * Creates new thread. * * @param r * main deployer. * @return thread object. */ public Thread newThread(Runnable r) { return new Thread(r, "MainDeployer"); } } //hook jmx method to terminate container public void terminateContainer() { //this will trigger shoot down hook, which nicely kills container. System.exit(0); } }