/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.deployment; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.Attributes; import java.util.jar.Manifest; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.Notification; import javax.management.ObjectName; import org.jboss.bootstrap.spi.ServerConfig; import org.jboss.deployers.client.spi.DeployerClient; import org.jboss.deployers.client.spi.Deployment; import org.jboss.deployers.structure.spi.DeploymentContext; import org.jboss.deployers.structure.spi.DeploymentUnit; import org.jboss.deployers.structure.spi.main.MainDeployerStructure; import org.jboss.deployers.vfs.spi.client.VFSDeployment; import org.jboss.deployers.vfs.spi.client.VFSDeploymentFactory; import org.jboss.kernel.spi.dependency.KernelController; import org.jboss.system.ServiceMBeanSupport; import org.jboss.system.server.ServerConfigLocator; import org.jboss.util.file.Files; import org.jboss.util.file.JarUtils; import org.jboss.util.stream.Streams; import org.jboss.virtual.VFS; import org.jboss.virtual.VirtualFile; /** * The legacy component for deployer management. This now simply delegates to the * Main * * @deprecated see org.jboss.deployers.spi.deployment.MainDeployer * * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a> * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a> * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a> * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a> * @author adrian@jboss.org * @author ales.justin@jboss.org * @version $Revision: 84628 $ */ public class MainDeployer extends ServiceMBeanSupport implements Deployer, MainDeployerMBean { /** The controller */ private KernelController controller; private DeployerClient delegate; private Map<URL, String> contextMap = Collections.synchronizedMap(new HashMap<URL, String>()); /** The deployment factory */ private VFSDeploymentFactory deploymentFactory = VFSDeploymentFactory.getInstance(); /** * The variable <code>serviceController</code> is used by the * checkIncompleteDeployments method to ask for lists of mbeans * with deployment problems. */ private ObjectName serviceController; /** Deployers **/ private final LinkedList deployers = new LinkedList(); /** A Map of URL -> DeploymentInfo */ private final Map deploymentMap = Collections.synchronizedMap(new HashMap()); /** A list of all deployments that have deployers. */ private final List deploymentList = new ArrayList(); /** A list of all deployments that do not have deployers. */ private final List waitingDeployments = new ArrayList(); /** A helper for sorting deployment URLs. */ private final DeploymentSorter sorter = new DeploymentSorter(); /** A helper for sorting deploymentInfos */ private final Comparator infoSorter = new DeploymentInfoComparator(sorter); /** Helper class handling the SuffixOrder and EnhancedSuffixOrder attributes */ private final SuffixOrderHelper suffixOrderHelper = new SuffixOrderHelper(sorter); /** Should local copies be made of resources on the local file system */ private boolean copyFiles = true; /** The temporary directory for deployments. */ private File tempDir; /** The string naming the tempDir **/ private String tempDirString; /** * Explict no-args contsructor for JMX. */ public MainDeployer() { // Is there a better place to obtain startup information? String localCopy = System.getProperty("jboss.deploy.localcopy"); if (localCopy != null && ( localCopy.equalsIgnoreCase("false") || localCopy.equalsIgnoreCase("no") || localCopy.equalsIgnoreCase("off"))) { log.debug("Disabling local copies of file: urls"); copyFiles = false; } } public DeployerClient getKernelMainDeployer() { return delegate; } public void setKernelMainDeployer(DeployerClient delegate) { this.delegate = delegate; } public KernelController getController() { return controller; } public void setController(KernelController controller) { this.controller = controller; } /** Get the flag indicating whether directory content will be deployed * * @return the file copy flag * @jmx.managed-attribute */ public boolean getCopyFiles() { return copyFiles; } /** Set the flag indicating whether directory content will be deployed. The * default value is taken from the jboss.deploy.localcopy system * property. * * @param copyFiles the local copy flag value * @jmx.managed-attribute */ public void setCopyFiles(boolean copyFiles) { this.copyFiles = copyFiles; } /** Get the temp directory * * @return the path to the local tmp directory * @jmx.managed-attribute */ public File getTempDir() { return tempDir; } /** Set the temp directory * * @param tempDir the path to the local tmp directory * @jmx.managed-attribute */ public void setTempDir(File tempDir) { this.tempDir = tempDir; } /** Get the temp directory * * @return the path to the local tmp directory * @jmx.managed-attribute */ public String getTempDirString() { return tempDirString; } /** Get the ordering of the deployment suffixes * * @return the ordering of the deployment suffixes * @jmx.managed-attribute */ public String[] getSuffixOrder() { return suffixOrderHelper.getSuffixOrder(); } /** Get the enhanced suffix order * * @return the enhanced suffix order * @jmx.managed-attribute */ public String[] getEnhancedSuffixOrder() { return suffixOrderHelper.getEnhancedSuffixes(); } /** Set the enhanced suffix order * * @param enhancedSuffixOrder the enhanced suffix order * @jmx.managed-attribute */ public void setEnhancedSuffixOrder(String[] enhancedSuffixOrder) { suffixOrderHelper.setEnhancedSuffixes(enhancedSuffixOrder); } /** * Describe <code>setServiceController</code> method here. * * @param serviceController an <code>ObjectName</code> value * @jmx.managed-attribute */ public void setServiceController(final ObjectName serviceController) { this.serviceController = serviceController; } /** * The <code>listDeployed</code> method returns a collection of DeploymemtInfo * objects for the currently deployed packages. * * @return a <code>Collection</code> value * @jmx.managed-operation */ public Collection listDeployed() { synchronized (deploymentList) { log.debug("deployment list string: " + deploymentList); return new ArrayList(deploymentList); } } /** * The <code>listDeployedModules</code> method returns a collection of * SerializableDeploymentInfo objects for the currently deployed packages. * * @return a <code>Collection</code> value * @jmx.managed-operation */ public Collection listDeployedModules() { log.debug("listDeployedModules"); HashMap map = new HashMap(); synchronized (deploymentList) { Collection col = new ArrayList(deploymentList); // create a map entry for each deployment for (Iterator it = col.iterator(); it.hasNext();) { DeploymentInfo info = (DeploymentInfo) it.next(); map.put(info.url, new SerializableDeploymentInfo(info)); // assign parent and sub deployments fillParentAndChildrenSDI(info, map); } } // map.values is not serializable, so we copy return new ArrayList(map.values()); } /** * Describe <code>listDeployedAsString</code> method here. * * @return a <code>String</code> value * @jmx.managed-operation */ public String listDeployedAsString() { return "<pre>" + listDeployed() + "</pre>"; } /** * The <code>listIncompletelyDeployed</code> method returns a list of packages that have * not deployed completely. The toString method will include any exception in the status * field. * * @return a <code>Collection</code> value * @jmx.managed-operation */ public Collection listIncompletelyDeployed() { List id = new ArrayList(); List copy; synchronized (deploymentList) { copy = new ArrayList(deploymentList); } for (Iterator i = copy.iterator(); i.hasNext();) { DeploymentInfo di = (DeploymentInfo)i.next(); if (!"Deployed".equals(di.status) && !"Starting".equals(di.status)) { id.add(di); } // end of if () } // end of for () return id; } /** * The <code>listWaitingForDeployer</code> method returns a collection * of the packages that currently have no identified deployer. * * @return a <code>Collection</code> value * @jmx.managed-operation */ public Collection listWaitingForDeployer() { synchronized (waitingDeployments) { return new ArrayList(waitingDeployments); } } /** * The <code>addDeployer</code> method registers a deployer with the main deployer. * Any waiting packages are tested to see if the new deployer will deploy them. * * @param deployer a <code>SubDeployer</code> value * @jmx.managed-operation */ public void addDeployer(final SubDeployer deployer) { log.debug("Adding deployer: " + deployer); ObjectName deployerName = deployer.getServiceName(); synchronized(deployers) { deployers.addFirst(deployer); try { String[] suffixes = (String[]) server.getAttribute(deployerName, "EnhancedSuffixes"); suffixOrderHelper.addEnhancedSuffixes(suffixes); } catch(Exception e) { log.debug(deployerName + " does not support EnhancedSuffixes"); suffixOrderHelper.addSuffixes(deployer.getSuffixes(), deployer.getRelativeOrder()); } } // Send a notification about the deployer addition Notification msg = new Notification(ADD_DEPLOYER, this, getNextNotificationSequenceNumber()); msg.setUserData(deployerName); sendNotification(msg); synchronized (waitingDeployments) { List copy = new ArrayList(waitingDeployments); waitingDeployments.clear(); for (Iterator i = copy.iterator(); i.hasNext();) { DeploymentInfo di = (DeploymentInfo)i.next(); log.debug("trying to deploy with new deployer: " + di.shortName); try { di.setServer(server); deploy(di); } catch (DeploymentException e) { log.error("DeploymentException while trying to deploy a package with a new deployer", e); } // end of try-catch } // end of for () } } /** * The <code>removeDeployer</code> method unregisters a deployer with the MainDeployer. * Deployed packages deployed with this deployer are undeployed. * * @param deployer a <code>SubDeployer</code> value * @jmx.managed-operation */ public void removeDeployer(final SubDeployer deployer) { log.debug("Removing deployer: " + deployer); ObjectName deployerName = deployer.getServiceName(); boolean removed = false; synchronized(deployers) { removed = deployers.remove(deployer); try { String[] suffixes = (String[]) server.getAttribute(deployerName, "EnhancedSuffixes"); suffixOrderHelper.removeEnhancedSuffixes(suffixes); } catch(Exception e) { log.debug(deployerName + " does not support EnhancedSuffixes"); suffixOrderHelper.removeSuffixes(deployer.getSuffixes(), deployer.getRelativeOrder()); } } // Send a notification about the deployer removal if (removed) { Notification msg = new Notification(REMOVE_DEPLOYER, this, getNextNotificationSequenceNumber()); msg.setUserData(deployerName); sendNotification(msg); } List copy; synchronized (deploymentList) { copy = new ArrayList(deploymentList); } for (Iterator i = copy.iterator(); i.hasNext(); ) { DeploymentInfo di = (DeploymentInfo)i.next(); if (di.deployer == deployer) { undeploy(di); di.deployer = null; synchronized (waitingDeployments) { waitingDeployments.add(di); } } } } /** * The <code>listDeployers</code> method returns a collection of ObjectNames of * deployers registered with the MainDeployer. * * @return a <code>Collection<ObjectName></code> value * @jmx.managed-operation */ public Collection listDeployers() { ArrayList deployerNames = new ArrayList(); synchronized(deployers) { for(int n = 0; n < deployers.size(); n ++) { SubDeployer deployer = (SubDeployer) deployers.get(n); ObjectName name = deployer.getServiceName(); deployerNames.add(name); } } return deployerNames; } // ServiceMBeanSupport overrides --------------------------------- protected ObjectName getObjectName(MBeanServer server, ObjectName name) throws MalformedObjectNameException { return name == null ? OBJECT_NAME : name; } /** * The <code>createService</code> method is one of the ServiceMBean lifecyle operations. * (no jmx tag needed from superinterface) * @exception Exception if an error occurs */ protected void createService() throws Exception { ServerConfig config = ServerConfigLocator.locate(); // Get the temp directory location File basedir = config.getServerTempDir(); // Set the local copy temp dir to tmp/deploy tempDir = new File(basedir, "deploy"); // Delete any existing content Files.delete(tempDir); // Make sure the directory exists tempDir.mkdirs(); // used in inLocalCopyDir tempDirString = tempDir.toURL().toString(); // handles SuffixOrder & RelativeSuffixOrder attributes suffixOrderHelper.initialize(); } /** * The <code>shutdown</code> method undeploys all deployed packages in * reverse order of their deployement. * * @jmx.managed-operation */ public void shutdown() { // if we shutdown in the middle of a scan, it still might be possible that we try to redeploy // things we are busy killing... int deployCounter = 0; // undeploy everything in sight List copy; synchronized (deploymentList) { copy = new ArrayList(deploymentList); } for (ListIterator i = copy.listIterator(copy.size()); i.hasPrevious(); ) { try { undeploy((DeploymentInfo)i.previous(), true); deployCounter++; } catch (Exception e) { log.info("exception trying to undeploy during shutdown", e); } } // Help GC this.deployers.clear(); this.deploymentMap.clear(); this.deploymentList.clear(); this.waitingDeployments.clear(); this.tempDir = null; log.debug("Undeployed " + deployCounter + " deployed packages"); } /** * Describe <code>redeploy</code> method here. * * @param urlspec a <code>String</code> value * @exception DeploymentException if an error occurs * @exception MalformedURLException if an error occurs * @jmx.managed-operation */ public void redeploy(String urlspec) throws DeploymentException, MalformedURLException { redeploy(new URL(urlspec)); } /** * Describe <code>redeploy</code> method here. * * @param url an <code>URL</code> value * @exception DeploymentException if an error occurs * @jmx.managed-operation */ public void redeploy(URL url) throws DeploymentException { String deploymentName = contextMap.get(url); if (deploymentName != null) { try { Deployment deployment = delegate.getDeployment(deploymentName); delegate.addDeployment(deployment); delegate.process(); delegate.checkComplete(deployment); } catch (org.jboss.deployers.spi.DeploymentException e) { throw new DeploymentException(e); } } else { deploy(url); } } /** * Describe <code>redeploy</code> method here. * * @param sdi a <code>DeploymentInfo</code> value * @exception DeploymentException if an error occurs * @jmx.managed-operation */ public void redeploy(DeploymentInfo sdi) throws DeploymentException { try { undeploy(sdi); } catch (Throwable t) { log.info("Throwable from undeployment attempt: ", t); } // end of try-catch sdi.setServer(server); deploy(sdi); } /** * The <code>undeploy</code> method undeploys a package identified by a string * representation of a URL. * * @param urlspec the stringfied url to undeploy * @jmx.managed-operation */ public void undeploy(String urlspec) throws DeploymentException, MalformedURLException { undeploy(new URL(urlspec)); } /** * The <code>undeploy</code> method undeploys a package identified by a URL * * @param url the url to undeploy * @jmx.managed-operation */ public void undeploy(URL url) throws DeploymentException { String deploymentName = contextMap.remove(url); if (deploymentName != null) { try { delegate.removeDeployment(deploymentName); delegate.process(); } catch(Exception e) { DeploymentException ex = new DeploymentException("Error during undeploy of: "+url, e); throw ex; } } else { log.warn("undeploy '" + url + "' : package not deployed"); } } /** * The <code>undeploy</code> method undeploys a package represented by a * DeploymentInfo object. * * @param di a <code>DeploymentInfo</code> value * @jmx.managed-operation */ public void undeploy(DeploymentInfo di) { undeploy(di, false); } protected void undeploy(DeploymentInfo di, boolean isShutdown) { log.debug("Undeploying "+di.url); stop(di); destroy(di); } /** * The <code>stop</code> method is the first internal step of undeployment * * @param di a <code>DeploymentInfo</code> value */ private void stop(DeploymentInfo di) { // Stop all sub-deployments ArrayList reverseSortedSubs = new ArrayList(di.subDeployments); Collections.sort(reverseSortedSubs, infoSorter); Collections.reverse(reverseSortedSubs); for (Iterator subs = reverseSortedSubs.iterator(); subs.hasNext();) { DeploymentInfo sub = (DeploymentInfo) subs.next(); log.debug("Stopping sub deployment: "+sub.url); stop(sub); } // Lastly stop this deployment itself try { // Tell the respective deployer to undeploy this one if (di.deployer != null) { di.deployer.stop(di); di.status="Stopped"; di.state = DeploymentState.STOPPED; } } catch (Throwable t) { log.error("Deployer stop failed for: " + di.url, t); } } /** * The <code>destroy</code> method is the second and final internal undeployment step. * * @param di a <code>DeploymentInfo</code> value */ private void destroy(DeploymentInfo di) { // Destroy all sub-deployments ArrayList reverseSortedSubs = new ArrayList(di.subDeployments); Collections.sort(reverseSortedSubs, infoSorter); Collections.reverse(reverseSortedSubs); for (Iterator subs = reverseSortedSubs.iterator(); subs.hasNext();) { DeploymentInfo sub = (DeploymentInfo) subs.next(); log.debug("Destroying sub deployment: "+sub.url); destroy(sub); } // Lastly destroy the deployment itself try { // Tell the respective deployer to undeploy this one if (di.deployer != null) { di.deployer.destroy(di); di.status="Destroyed"; di.state = DeploymentState.DESTROYED; } } catch (Throwable t) { log.error("Deployer destroy failed for: " + di.url, t); di.state = DeploymentState.FAILED; } try { // remove from local maps synchronized (deploymentList) { deploymentMap.remove(di.url); if (deploymentList.lastIndexOf(di) != -1) { deploymentList.remove(deploymentList.lastIndexOf(di)); } } synchronized (waitingDeployments) { waitingDeployments.remove(di); } // Nuke my stuff, this includes the class loader di.cleanup(); log.debug("Undeployed "+di.url); } catch (Throwable t) { log.error("Undeployment cleanup failed: " + di.url, t); } } /** * The <code>deploy</code> method deploys a package identified by a * string representation of a URL. * * @param urlspec a <code>String</code> value * @exception MalformedURLException if an error occurs * @jmx.managed-operation */ public void deploy(String urlspec) throws DeploymentException, MalformedURLException { if( server == null ) throw new DeploymentException("The MainDeployer has been unregistered"); URL url; try { url = new URL(urlspec); } catch (MalformedURLException e) { File file = new File(urlspec); url = file.toURL(); } deploy(url); } /** * The <code>deploy</code> method deploys a package identified by a URL * * @param url an <code>URL</code> value * @jmx.managed-operation */ public void deploy(URL url) throws DeploymentException { log.info("deploy, url="+url); String deploymentName = contextMap.get(url); // if it does not exist create a new deployment if (deploymentName == null) { try { VirtualFile file = VFS.createNewRoot(url); VFSDeployment deployment = deploymentFactory.createVFSDeployment(file); delegate.addDeployment(deployment); deploymentName = deployment.getName(); delegate.process(); // TODO: JBAS-4292 contextMap.put(url, deploymentName); delegate.checkComplete(deployment); } catch(Exception e) { log.warn("Failed to deploy: "+url, e); DeploymentException ex = new DeploymentException("Failed to deploy: "+url, e); throw ex; } } } /** * The <code>deploy</code> method deploys a package represented by a DeploymentInfo object. * * @param deployment a <code>DeploymentInfo</code> value * @exception DeploymentException if an error occurs * @jmx.managed-operation */ public void deploy(DeploymentInfo deployment) throws DeploymentException { // If we are already deployed return if (isDeployed(deployment.url)) { log.info("Package: " + deployment.url + " is already deployed"); return; } log.debug("Starting deployment of package: " + deployment.url); boolean inited = false; try { inited = init(deployment); } catch (Throwable t) { log.error("Could not initialise deployment: " + deployment.url, t); DeploymentException.rethrowAsDeploymentException("Could not initialise deployment: " + deployment.url, t); } if ( inited ) { create(deployment); start(deployment); log.debug("Deployed package: " + deployment.url); } // end of if () else { log.debug("Deployment of package: " + deployment.url + " is waiting for an appropriate deployer."); } // end of else } /** * The <code>init</code> method is the first internal deployment step. * The tasks are to copy the code if necessary, * set up classloaders, and identify the deployer for the package. * * @param deployment a <code>DeploymentInfo</code> value * @throws DeploymentException if an error occurs */ private boolean init(DeploymentInfo deployment) throws DeploymentException { // If we are already deployed return if (isDeployed(deployment.url)) { log.info("Package: " + deployment.url + " is already deployed"); return false; } log.debug("Starting deployment (init step) of package at: " + deployment.url); try { // Create a local copy of that File, the sdi keeps track of the copy directory if (deployment.localUrl == null) { makeLocalCopy(deployment); URL[] localCl = new URL[]{deployment.localUrl}; deployment.localCl = new URLClassLoader(localCl); } // What deployer is able to deploy this file findDeployer(deployment); if(deployment.deployer == null) { deployment.state = DeploymentState.INIT_WAITING_DEPLOYER; log.debug("deployment waiting for deployer: " + deployment.url); synchronized (waitingDeployments) { if (waitingDeployments.contains(deployment) == false) waitingDeployments.add(deployment); } return false; } deployment.state = DeploymentState.INIT_DEPLOYER; //we have the deployer, continue deployment. deployment.deployer.init(deployment); // initialize the unified classloaders for this deployment deployment.createClassLoaders(); deployment.state = DeploymentState.INITIALIZED; // Add the deployment to the map so we can detect circular deployments synchronized (deploymentList) { deploymentMap.put(deployment.url, deployment); } // create subdeployments as needed parseManifestLibraries(deployment); log.debug("found " + deployment.subDeployments.size() + " subpackages of " + deployment.url); // get sorted subDeployments ArrayList sortedSubs = new ArrayList(deployment.subDeployments); Collections.sort(sortedSubs, infoSorter); for (Iterator lt = sortedSubs.listIterator(); lt.hasNext();) { init((DeploymentInfo) lt.next()); } } catch (Exception e) { deployment.state = DeploymentState.FAILED; DeploymentException.rethrowAsDeploymentException("exception in init of " + deployment.url, e); } finally { // whether you do it or not, for the autodeployer try { URL url = deployment.localUrl == null ? deployment.url : deployment.localUrl; long lastModified = -1; if (url.getProtocol().equals("file")) lastModified = new File(url.getFile()).lastModified(); else lastModified = url.openConnection().getLastModified(); deployment.lastModified=lastModified; deployment.lastDeployed=System.currentTimeMillis(); } catch (IOException ignore) { deployment.lastModified=System.currentTimeMillis(); deployment.lastDeployed=System.currentTimeMillis(); } synchronized (deploymentList) { // Do we watch it? Watch only urls outside our copy directory. if (!inLocalCopyDir(deployment.url) && deploymentList.contains(deployment) == false) { deploymentList.add(deployment); log.debug("Watching new file: " + deployment.url); } } } return true; } /** * The <code>create</code> method is the second internal deployment step. * It should set up all information not * requiring other components. for instance, the ejb Container is created, * and the proxy bound into jndi. * * @param deployment a <code>DeploymentInfo</code> value * @throws DeploymentException if an error occurs */ private void create(DeploymentInfo deployment) throws DeploymentException { log.debug("create step for deployment " + deployment.url); try { ArrayList sortedSubs = new ArrayList(deployment.subDeployments); Collections.sort(sortedSubs, infoSorter); for (Iterator lt = sortedSubs.listIterator(); lt.hasNext();) { create((DeploymentInfo) lt.next()); } deployment.state = DeploymentState.CREATE_SUBDEPLOYMENTS; // Deploy this SDI, if it is a deployable type if (deployment.deployer != null) { try { deployment.state = DeploymentState.CREATE_DEPLOYER; deployment.deployer.create(deployment); // See if all mbeans are created... deployment.state = DeploymentState.CREATED; deployment.status="Created"; log.debug("Done with create step of deploying " + deployment.shortName); } catch (Throwable t) { log.error("Could not create deployment: " + deployment.url, t); throw t; } } else { log.debug("Still no deployer for package in create step: " + deployment.shortName); } // end of else } catch (Throwable t) { log.trace("could not create deployment: " + deployment.url, t); deployment.status = "Deployment FAILED reason: " + t.getMessage(); deployment.state = DeploymentState.FAILED; DeploymentException.rethrowAsDeploymentException("Could not create deployment: " + deployment.url, t); } } /** * The <code>start</code> method is the third and final internal deployment step. * The purpose is to set up relationships between components. * for instance, ejb links are set up here. * * @param deployment a <code>DeploymentInfo</code> value * @throws DeploymentException if an error occurs */ private void start(DeploymentInfo deployment) throws DeploymentException { deployment.status = "Starting"; log.debug("Begin deployment start " + deployment.url); try { ArrayList sortedSubs = new ArrayList(deployment.subDeployments); Collections.sort(sortedSubs, infoSorter); for (Iterator lt = sortedSubs.listIterator(); lt.hasNext();) { start((DeploymentInfo) lt.next()); } deployment.state = DeploymentState.START_SUBDEPLOYMENTS; // Deploy this SDI, if it is a deployable type if (deployment.deployer != null) { try { deployment.state = DeploymentState.START_DEPLOYER; deployment.deployer.start(deployment); // See if all mbeans are started... Object[] args = {deployment, DeploymentState.STARTED}; String[] sig = {"org.jboss.deployment.DeploymentInfo", "org.jboss.deployment.DeploymentState"}; server.invoke(serviceController, "validateDeploymentState",args, sig); deployment.status = "Deployed"; log.debug("End deployment start on package: "+ deployment.shortName); } catch (Throwable t) { log.error("Could not start deployment: " + deployment.url, t); throw t; } } else { log.debug("Still no deployer for package in start step: " + deployment.shortName); } // end of else } catch (Throwable t) { log.trace("could not start deployment: " + deployment.url, t); deployment.state = DeploymentState.FAILED; deployment.status = "Deployment FAILED reason: " + t.getMessage(); DeploymentException.rethrowAsDeploymentException("Could not create deployment: " + deployment.url, t); } } /** * The <code>findDeployer</code> method attempts to find a deployer for the DeploymentInfo * supplied as a parameter. * * @param sdi a <code>DeploymentInfo</code> value */ private void findDeployer(DeploymentInfo sdi) { // If there is already a deployer use it if( sdi.deployer != null ) { log.debug("using existing deployer "+sdi.deployer); return; } // // To deploy directories of beans one should just name the directory // mybean.ear/bla...bla, so that the directory gets picked up by the right deployer // synchronized(deployers) { for (Iterator iterator = deployers.iterator(); iterator.hasNext(); ) { SubDeployer deployer = (SubDeployer) iterator.next(); if (deployer.accepts(sdi)) { sdi.deployer = deployer; log.debug("using deployer "+deployer); return; } } } log.debug("No deployer found for url: " + sdi.url); } /** * The <code>parseManifestLibraries</code> method looks into the manifest for classpath * goo, and tries to deploy referenced packages. * * @param sdi a <code>DeploymentInfo</code> value */ private void parseManifestLibraries(DeploymentInfo sdi) { String classPath = null; Manifest mf = sdi.getManifest(); if( mf != null ) { Attributes mainAttributes = mf.getMainAttributes(); classPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH); } if (classPath != null) { StringTokenizer st = new StringTokenizer(classPath); log.debug("resolveLibraries: "+classPath); while (st.hasMoreTokens()) { URL lib = null; String tk = st.nextToken(); log.debug("new manifest entry for sdi at "+sdi.shortName+" entry is "+tk); try { if (sdi.isDirectory) { File parentDir = new File(sdi.url.getPath()).getParentFile(); lib = new File(parentDir, tk).toURL(); } else { lib = new URL(sdi.url, tk); } // Only deploy this if it is not already being deployed if ( deploymentMap.containsKey(lib) == false ) { /* Test that the only deployer for this is the JARDeployer. Any other type of deployment cannot be initiated through a manifest reference. */ DeploymentInfo mfRef = new DeploymentInfo(lib, null, getServer()); makeLocalCopy(mfRef); URL[] localURL = {mfRef.localUrl}; mfRef.localCl = new java.net.URLClassLoader(localURL); findDeployer(mfRef); SubDeployer deployer = mfRef.deployer; if(deployer != null && (deployer instanceof JARDeployer) == false) { // Its a non-jar deployment that must be deployed seperately log.warn("Found non-jar deployer for " + tk + ": " + deployer); } // add the library sdi.addLibraryJar(lib); } } catch (Exception ignore) { log.debug("The manifest entry in "+sdi.url+" references URL "+lib+ " which could not be opened, entry ignored", ignore); } } } } /** * Downloads the jar file or directory the src URL points to. * In case of directory it becomes packed to a jar file. */ private void makeLocalCopy(DeploymentInfo sdi) { try { if (sdi.url.getProtocol().equals("file") && (!copyFiles || sdi.isDirectory)) { // If local copies have been disabled, do nothing sdi.localUrl = sdi.url; return; } // Are we already in the localCopyDir? else if (inLocalCopyDir(sdi.url)) { sdi.localUrl = sdi.url; return; } else { String shortName = sdi.shortName; File localFile = File.createTempFile("tmp", shortName, tempDir); sdi.localUrl = localFile.toURL(); copy(sdi.url, localFile); } } catch (Exception e) { log.error("Could not make local copy for " + sdi.url, e); } } private boolean inLocalCopyDir(URL url) { int i = 0; String urlTest = url.toString(); if( urlTest.startsWith("jar:") ) i = 4; return urlTest.startsWith(tempDirString, i); } protected void copy(URL src, File dest) throws IOException { log.debug("Copying " + src + " -> " + dest); // Validate that the dest parent directory structure exists File dir = dest.getParentFile(); if (!dir.exists()) { boolean created = dir.mkdirs(); if( created == false ) throw new IOException("mkdirs failed for: "+dir.getAbsolutePath()); } // Remove any existing dest content if( dest.exists() == true ) { boolean deleted = Files.delete(dest); if( deleted == false ) throw new IOException("delete of previous content failed for: "+dest.getAbsolutePath()); } if (src.getProtocol().equals("file")) { File srcFile = new File(src.getFile()); if (srcFile.isDirectory()) { log.debug("Making zip copy of: " + srcFile); // make a jar archive of the directory OutputStream out = new BufferedOutputStream(new FileOutputStream(dest)); JarUtils.jar(out, srcFile.listFiles()); out.close(); return; } } InputStream in = new BufferedInputStream(src.openStream()); OutputStream out = new BufferedOutputStream(new FileOutputStream(dest)); Streams.copy(in, out); out.flush(); out.close(); in.close(); } /** * The <code>start</code> method starts a package identified by a URL * * @param urlspec an <code>URL</code> value * @jmx.managed-operation */ public void start(String urlspec) throws DeploymentException, MalformedURLException { throw new DeploymentException("Not supported"); } /** * The <code>stop</code> method stops a package identified by a URL * * @param urlspec an <code>URL</code> value * @jmx.managed-operation */ public void stop(String urlspec) throws DeploymentException, MalformedURLException { throw new DeploymentException("Not supported"); } /** * The <code>isDeployed</code> method tells you if a package identified by a string * representation of a URL is currently deployed. * * @param url a <code>String</code> value * @return a <code>boolean</code> value * @exception MalformedURLException if an error occurs * @jmx.managed-operation */ public boolean isDeployed(String url) throws MalformedURLException { return isDeployed(new URL(url)); } /** * The <code>isDeployed</code> method tells you if a packaged identified by * a URL is deployed. * @param url an <code>URL</code> value * @return a <code>boolean</code> value * @jmx.managed-operation */ public boolean isDeployed(URL url) { String name = contextMap.get(url); if (name == null) { if (log.isTraceEnabled()) log.trace("No such context: " + url); if (url == null) throw new IllegalArgumentException("Null url"); String urlString = url.toString(); // remove this once the JBoss-test is updated with VFS usage if (urlString.startsWith("vfs") == false) return checkDeployed("vfs" + urlString); else return checkDeployed(urlString); } return checkDeployed(name); } /** * Is deployed. * * @param name the name of the deployment * @return true if deployed, false otherwise */ protected boolean checkDeployed(String name) { org.jboss.deployers.spi.DeploymentState deploymentState = delegate.getDeploymentState(name); log.debug("isDeployed, url="+name+", state="+deploymentState); return deploymentState == org.jboss.deployers.spi.DeploymentState.DEPLOYED; } /** * The <code>getDeployment</code> method returns the Deployment * object for the URL supplied. * * @param url an <code>URL</code> value * @return a <code>Deployment</code> value * @jmx.managed-operation */ public Deployment getDeployment(URL url) { String name = contextMap.get(url); if (name == null) return null; Deployment dc = delegate.getDeployment(name); log.debug("getDeployment, url="+url+", dc="+dc); return dc; } /** * The <code>getDeploymentContext</code> method returns the DeploymentContext * object for the URL supplied. * * @param url an <code>URL</code> value * @return a <code>DeploymentContext</code> value * @jmx.managed-operation */ @Deprecated public DeploymentContext getDeploymentContext(URL url) { String name = contextMap.get(url); if (name == null) return null; MainDeployerStructure structure = (MainDeployerStructure) delegate; DeploymentContext dc = structure.getDeploymentContext(name); log.debug("getDeploymentContext, url="+url+", dc="+dc); return dc; } /** * The <code>getDeploymentUnit</code> method returns the DeploymentUnit * object for the URL supplied. * * @param url an <code>URL</code> value * @return a <code>DeploymentUnit</code> value * @jmx.managed-operation */ public DeploymentUnit getDeploymentUnit(URL url) { String name = contextMap.get(url); if (name == null) return null; MainDeployerStructure structure = (MainDeployerStructure) delegate; DeploymentUnit du = structure.getDeploymentUnit(name); log.debug("getDeploymentUnit, url="+url+", du="+du); return du; } /** * The <code>getWatchUrl</code> method returns the URL that, when modified, * indicates that a redeploy is needed. * * @param url an <code>URL</code> value * @return a <code>URL</code> value * @jmx.managed-operation */ public URL getWatchUrl(URL url) { return url; } /** Check the current deployment states and generate a IncompleteDeploymentException * if there are mbeans waiting for depedencies. * @exception IncompleteDeploymentException * @jmx.managed-operation */ public void checkIncompleteDeployments() throws DeploymentException { try { delegate.checkComplete(); } catch (Exception e) { throw new DeploymentException("Deployments are incomplete", e); } } /** * @param parent * @param map */ private void fillParentAndChildrenSDI(DeploymentInfo parent, Map map) { Set subDeployments = parent.subDeployments; Iterator it = subDeployments.iterator(); while (it.hasNext()) { DeploymentInfo child = (DeploymentInfo) it.next(); SerializableDeploymentInfo sdichild = returnSDI(child, map); sdichild.parent = returnSDI(parent, map); sdichild.parent.subDeployments.add(sdichild); fillParentAndChildrenSDI(child, map); } } private SerializableDeploymentInfo returnSDI(DeploymentInfo di, Map map) { SerializableDeploymentInfo sdi = (SerializableDeploymentInfo) map.get(di.url); if( sdi == null ) { sdi = new SerializableDeploymentInfo(di); map.put(di.url, sdi); } return sdi; } }