package org.mortbay.ijetty.deployer; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.deploy.ConfigurationManager; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.util.AttributesMap; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.xml.XmlConfiguration; /** * AndroidContextDeployer * TODO this class should be able to extend ContextDeployer rather than * reimplement it entirely. * */ public class AndroidContextDeployer extends AbstractLifeCycle { /* ------------------------------------------------------------ */ protected class ScannerListener implements Scanner.DiscreteListener { /** * Handle a new deployment * * @see org.mortbay.util.Scanner.FileAddedListener#fileAdded(java.lang.String) */ public void fileAdded(String filename) throws Exception { deploy(filename); } /** * Handle a change to an existing deployment. Undeploy then redeploy. * * @see org.mortbay.util.Scanner.FileChangedListener#fileChanged(java.lang.String) */ public void fileChanged(String filename) throws Exception { redeploy(filename); } /** * Handle an undeploy. * * @see org.mortbay.util.Scanner.FileRemovedListener#fileRemoved(java.lang.String) */ public void fileRemoved(String filename) throws Exception { undeploy(filename); } @Override public String toString() { return "ContextDeployer$Scanner"; } } private AttributesMap _attributes = null; public final static String NAME = "ConfiguredDeployer"; private int _scanInterval = 10; private Scanner _scanner; private ScannerListener _scannerListener; private Resource _configurationDir; @SuppressWarnings("unchecked") private Map _currentDeployments = new HashMap(); private ContextHandlerCollection _contexts; private ConfigurationManager _configMgr; private boolean _recursive = false; public AndroidContextDeployer() throws Exception { super(); _scanner = new Scanner(); _attributes = new AttributesMap(); } /* ------------------------------------------------------------ */ /** * Create a WebAppContext for the webapp being hot deployed, then apply the * xml config file to it to configure it. * * @param filename * the config file found in the hot deploy directory * @return * @throws Exception */ @SuppressWarnings("unchecked") protected ContextHandler createContext(String filename) throws Exception { // The config file can call any method on WebAppContext to configure // the webapp being deployed. Resource resource = Resource.newResource(filename); if (!resource.exists()) { return null; } XmlConfiguration xmlConfiguration = new XmlConfiguration(resource.getURL()); HashMap properties = new HashMap(); properties.put("Server", _contexts.getServer()); if (_configMgr != null) { properties.putAll(_configMgr.getProperties()); } xmlConfiguration.setProperties(properties); ContextHandler context = (ContextHandler) xmlConfiguration.configure(); Enumeration<?> names = _attributes.getAttributeNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); context.setAttribute(name, _attributes.getAttribute(name)); } return context; } /* ------------------------------------------------------------ */ @SuppressWarnings("unchecked") public void deploy(String filename) throws Exception { ContextHandler context = createContext(filename); Log.info("Deploy " + filename + " -> " + context); _contexts.addHandler(context); _currentDeployments.put(filename, context); if (_contexts.isStarted()) { context.start(); } } /* ------------------------------------------------------------ */ /** * Start the hot deployer looking for webapps to deploy/undeploy * * @see org.mortbay.component.AbstractLifeCycle#doStart() */ @Override protected void doStart() throws Exception { if (_configurationDir == null) { Log.warn("No configuration dir specified"); throw new IllegalStateException("No configuration dir specified"); } if (_contexts == null) { throw new IllegalStateException("No context handler collection specified for deployer"); } _scanner.setScanDirs(Collections.singletonList(_configurationDir.getFile())); _scanner.setScanInterval(getScanInterval()); _scanner.setRecursive(_recursive); //only look in the top level for deployment files? // Accept changes only in files that could be a deployment descriptor _scanner.setFilenameFilter(new FilenameFilter() { public boolean accept(File dir, String name) { try { if (name.endsWith(".xml") && dir.equals(getConfigurationDir().getFile())) { return true; } return false; } catch (IOException e) { Log.warn(e); return false; } } }); _scannerListener = new ScannerListener(); _scanner.addListener(_scannerListener); _scanner.scan(); _scanner.start(); _contexts.getServer().getContainer().addBean(_scanner); } /* ------------------------------------------------------------ */ /** * Stop the hot deployer. * * @see org.mortbay.component.AbstractLifeCycle#doStop() */ @Override protected void doStop() throws Exception { _scanner.removeListener(_scannerListener); _scanner.stop(); } public Object getAttribute(String name) { return _attributes.getAttribute(name); } /* ------------------------------------------------------------ */ /** * @return */ public Resource getConfigurationDir() { return _configurationDir; } /* ------------------------------------------------------------ */ /** * @return */ public ConfigurationManager getConfigurationManager() { return _configMgr; } /* ------------------------------------------------------------ */ /** * @return the ContextHandlerColletion to which to deploy the contexts */ public ContextHandlerCollection getContexts() { return _contexts; } /* ------------------------------------------------------------ */ /** * @return */ public String getDirectory() { return getConfigurationDir().getName(); } public boolean getRecursive() { return _recursive; } /* ------------------------------------------------------------ */ public int getScanInterval() { return _scanInterval; } public boolean isRecursive() { return _recursive; } /* ------------------------------------------------------------ */ public void redeploy(String filename) throws Exception { undeploy(filename); deploy(filename); } public void setAttribute(String name, Object value) { _attributes.setAttribute(name, value); } /* ------------------------------------------------------------ */ /** * @param file * @throws Exception */ public void setConfigurationDir(File file) throws Exception { setConfigurationDir(Resource.newResource(file.toURL())); } /* ------------------------------------------------------------ */ /** * @param resource */ public void setConfigurationDir(Resource resource) { if (isStarted() || isStarting()) { throw new IllegalStateException("Cannot change hot deploy dir after deployer start"); } _configurationDir = resource; } /* ------------------------------------------------------------ */ /** * @param dir * @throws Exception */ public void setConfigurationDir(String dir) throws Exception { setConfigurationDir(Resource.newResource(dir)); } /* ------------------------------------------------------------ */ /** * @param configMgr */ public void setConfigurationManager(ConfigurationManager configMgr) { _configMgr = configMgr; } /* ------------------------------------------------------------ */ /** * Associate with a {@link ContextHandlerCollection}. * * @param contexts * the ContextHandlerColletion to which to deploy the contexts */ public void setContexts(ContextHandlerCollection contexts) { if (isStarted() || isStarting()) { throw new IllegalStateException("Cannot set Contexts after deployer start"); } _contexts = contexts; } /* ------------------------------------------------------------ */ /** * @param directory */ public void setDirectory(String directory) throws Exception { setConfigurationDir(directory); } public void setRecursive(boolean recursive) { _recursive = recursive; } /* ------------------------------------------------------------ */ /** * @param seconds * The period in second between scans for changed configuration * files. A zero or negative interval disables hot deployment */ public void setScanInterval(int seconds) { if (isStarted() || isStarting()) { throw new IllegalStateException("Cannot change scan interval after deployer start"); } _scanInterval = seconds; } /* ------------------------------------------------------------ */ public void undeploy(String filename) throws Exception { ContextHandler context = (ContextHandler) _currentDeployments.get(filename); Log.info("Undeploy " + filename + " -> " + context); if (context == null) { return; } context.stop(); _contexts.removeHandler(context); _currentDeployments.remove(filename); } }