0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jboss.deployment;
0023:
0024: import java.io.BufferedInputStream;
0025: import java.io.BufferedOutputStream;
0026: import java.io.File;
0027: import java.io.FileOutputStream;
0028: import java.io.IOException;
0029: import java.io.InputStream;
0030: import java.io.OutputStream;
0031: import java.net.MalformedURLException;
0032: import java.net.URL;
0033: import java.net.URLClassLoader;
0034: import java.util.ArrayList;
0035: import java.util.Collection;
0036: import java.util.Collections;
0037: import java.util.Comparator;
0038: import java.util.HashMap;
0039: import java.util.HashSet;
0040: import java.util.Iterator;
0041: import java.util.LinkedList;
0042: import java.util.List;
0043: import java.util.ListIterator;
0044: import java.util.Map;
0045: import java.util.Set;
0046: import java.util.StringTokenizer;
0047: import java.util.jar.Attributes;
0048: import java.util.jar.Manifest;
0049:
0050: import javax.management.JMException;
0051: import javax.management.MBeanServer;
0052: import javax.management.MalformedObjectNameException;
0053: import javax.management.Notification;
0054: import javax.management.ObjectName;
0055:
0056: import org.jboss.mx.util.JMXExceptionDecoder;
0057: import org.jboss.system.ServiceContext;
0058: import org.jboss.system.ServiceMBeanSupport;
0059: import org.jboss.system.server.ServerConfig;
0060: import org.jboss.system.server.ServerConfigLocator;
0061: import org.jboss.util.file.Files;
0062: import org.jboss.util.file.JarUtils;
0063: import org.jboss.util.stream.Streams;
0064:
0065: /**
0066: * The main/primary component for deployment.
0067: *
0068: * <p>MainDeployerMBean is generated by xdoclet.
0069: *
0070: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
0071: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
0072: * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
0073: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
0074: * @version $Revision: 62368 $
0075: */
0076: public class MainDeployer extends ServiceMBeanSupport implements
0077: Deployer, MainDeployerMBean {
0078: /**
0079: * The variable <code>serviceController</code> is used by the
0080: * checkIncompleteDeployments method to ask for lists of mbeans
0081: * with deployment problems.
0082: */
0083: private ObjectName serviceController;
0084:
0085: /** Deployers **/
0086: private final LinkedList deployers = new LinkedList();
0087:
0088: /** A Map of URL -> DeploymentInfo */
0089: private final Map deploymentMap = Collections
0090: .synchronizedMap(new HashMap());
0091:
0092: /** A list of all deployments that have deployers. */
0093: private final List deploymentList = new ArrayList();
0094:
0095: /** A list of all deployments that do not have deployers. */
0096: private final List waitingDeployments = new ArrayList();
0097:
0098: /** A helper for sorting deployment URLs. */
0099: private final DeploymentSorter sorter = new DeploymentSorter();
0100:
0101: /** A helper for sorting deploymentInfos */
0102: private final Comparator infoSorter = new DeploymentInfoComparator(
0103: sorter);
0104:
0105: /** Helper class handling the SuffixOrder and EnhancedSuffixOrder attributes */
0106: private final SuffixOrderHelper suffixOrderHelper = new SuffixOrderHelper(
0107: sorter);
0108:
0109: /** Should local copies be made of resources on the local file system */
0110: private boolean copyFiles = true;
0111:
0112: /** The temporary directory for deployments. */
0113: private File tempDir;
0114:
0115: /** The string naming the tempDir **/
0116: private String tempDirString;
0117:
0118: /**
0119: * Explict no-args contsructor for JMX.
0120: */
0121: public MainDeployer() {
0122: // Is there a better place to obtain startup information?
0123: String localCopy = System.getProperty("jboss.deploy.localcopy");
0124: if (localCopy != null
0125: && (localCopy.equalsIgnoreCase("false")
0126: || localCopy.equalsIgnoreCase("no") || localCopy
0127: .equalsIgnoreCase("off"))) {
0128: log.debug("Disabling local copies of file: urls");
0129: copyFiles = false;
0130: }
0131: }
0132:
0133: /** Get the flag indicating whether directory content will be deployed
0134: *
0135: * @return the file copy flag
0136: * @jmx.managed-attribute
0137: */
0138: public boolean getCopyFiles() {
0139: return copyFiles;
0140: }
0141:
0142: /** Set the flag indicating whether directory content will be deployed. The
0143: * default value is taken from the jboss.deploy.localcopy system
0144: * property.
0145: *
0146: * @param copyFiles the local copy flag value
0147: * @jmx.managed-attribute
0148: */
0149: public void setCopyFiles(boolean copyFiles) {
0150: this .copyFiles = copyFiles;
0151: }
0152:
0153: /** Get the temp directory
0154: *
0155: * @return the path to the local tmp directory
0156: * @jmx.managed-attribute
0157: */
0158: public File getTempDir() {
0159: return tempDir;
0160: }
0161:
0162: /** Set the temp directory
0163: *
0164: * @param tempDir the path to the local tmp directory
0165: * @jmx.managed-attribute
0166: */
0167: public void setTempDir(File tempDir) {
0168: this .tempDir = tempDir;
0169: }
0170:
0171: /** Get the temp directory
0172: *
0173: * @return the path to the local tmp directory
0174: * @jmx.managed-attribute
0175: */
0176: public String getTempDirString() {
0177: return tempDirString;
0178: }
0179:
0180: /** Get the ordering of the deployment suffixes
0181: *
0182: * @return the ordering of the deployment suffixes
0183: * @jmx.managed-attribute
0184: */
0185: public String[] getSuffixOrder() {
0186: return suffixOrderHelper.getSuffixOrder();
0187: }
0188:
0189: /** Get the enhanced suffix order
0190: *
0191: * @return the enhanced suffix order
0192: * @jmx.managed-attribute
0193: */
0194: public String[] getEnhancedSuffixOrder() {
0195: return suffixOrderHelper.getEnhancedSuffixes();
0196: }
0197:
0198: /** Set the enhanced suffix order
0199: *
0200: * @param enhancedSuffixOrder the enhanced suffix order
0201: * @jmx.managed-attribute
0202: */
0203: public void setEnhancedSuffixOrder(String[] enhancedSuffixOrder) {
0204: suffixOrderHelper.setEnhancedSuffixes(enhancedSuffixOrder);
0205: }
0206:
0207: /**
0208: * Describe <code>setServiceController</code> method here.
0209: *
0210: * @param serviceController an <code>ObjectName</code> value
0211: * @jmx.managed-attribute
0212: */
0213: public void setServiceController(final ObjectName serviceController) {
0214: this .serviceController = serviceController;
0215: }
0216:
0217: /**
0218: * The <code>listDeployed</code> method returns a collection of DeploymemtInfo
0219: * objects for the currently deployed packages.
0220: *
0221: * @return a <code>Collection</code> value
0222: * @jmx.managed-operation
0223: */
0224: public Collection listDeployed() {
0225: synchronized (deploymentList) {
0226: log.debug("deployment list string: " + deploymentList);
0227: return new ArrayList(deploymentList);
0228: }
0229: }
0230:
0231: /**
0232: * The <code>listDeployedModules</code> method returns a collection of
0233: * SerializableDeploymentInfo objects for the currently deployed packages.
0234: *
0235: * @return a <code>Collection</code> value
0236: * @jmx.managed-operation
0237: */
0238: public Collection listDeployedModules() {
0239: log.debug("listDeployedModules");
0240:
0241: HashMap map = new HashMap();
0242: synchronized (deploymentList) {
0243: Collection col = new ArrayList(deploymentList);
0244:
0245: // create a map entry for each deployment
0246: for (Iterator it = col.iterator(); it.hasNext();) {
0247: DeploymentInfo info = (DeploymentInfo) it.next();
0248: map.put(info.url, new SerializableDeploymentInfo(info));
0249: // assign parent and sub deployments
0250: fillParentAndChildrenSDI(info, map);
0251: }
0252: }
0253:
0254: // map.values is not serializable, so we copy
0255: return new ArrayList(map.values());
0256: }
0257:
0258: /**
0259: * Describe <code>listDeployedAsString</code> method here.
0260: *
0261: * @return a <code>String</code> value
0262: * @jmx.managed-operation
0263: */
0264: public String listDeployedAsString() {
0265: return "<pre>" + listDeployed() + "</pre>";
0266: }
0267:
0268: /**
0269: * The <code>listIncompletelyDeployed</code> method returns a list of packages that have
0270: * not deployed completely. The toString method will include any exception in the status
0271: * field.
0272: *
0273: * @return a <code>Collection</code> value
0274: * @jmx.managed-operation
0275: */
0276: public Collection listIncompletelyDeployed() {
0277: List id = new ArrayList();
0278: List copy;
0279: synchronized (deploymentList) {
0280: copy = new ArrayList(deploymentList);
0281: }
0282: for (Iterator i = copy.iterator(); i.hasNext();) {
0283: DeploymentInfo di = (DeploymentInfo) i.next();
0284: if (!"Deployed".equals(di.status)
0285: && !"Starting".equals(di.status)) {
0286: id.add(di);
0287: } // end of if ()
0288:
0289: } // end of for ()
0290: return id;
0291: }
0292:
0293: /**
0294: * The <code>listWaitingForDeployer</code> method returns a collection
0295: * of the packages that currently have no identified deployer.
0296: *
0297: * @return a <code>Collection</code> value
0298: * @jmx.managed-operation
0299: */
0300: public Collection listWaitingForDeployer() {
0301: synchronized (waitingDeployments) {
0302: return new ArrayList(waitingDeployments);
0303: }
0304: }
0305:
0306: /**
0307: * The <code>addDeployer</code> method registers a deployer with the main deployer.
0308: * Any waiting packages are tested to see if the new deployer will deploy them.
0309: *
0310: * @param deployer a <code>SubDeployer</code> value
0311: * @jmx.managed-operation
0312: */
0313: public void addDeployer(final SubDeployer deployer) {
0314: log.debug("Adding deployer: " + deployer);
0315: ObjectName deployerName = deployer.getServiceName();
0316:
0317: synchronized (deployers) {
0318: deployers.addFirst(deployer);
0319: try {
0320: String[] suffixes = (String[]) server.getAttribute(
0321: deployerName, "EnhancedSuffixes");
0322: suffixOrderHelper.addEnhancedSuffixes(suffixes);
0323: } catch (Exception e) {
0324: log.debug(deployerName
0325: + " does not support EnhancedSuffixes");
0326: suffixOrderHelper.addSuffixes(deployer.getSuffixes(),
0327: deployer.getRelativeOrder());
0328: }
0329: }
0330:
0331: // Send a notification about the deployer addition
0332: Notification msg = new Notification(ADD_DEPLOYER, this ,
0333: getNextNotificationSequenceNumber());
0334: msg.setUserData(deployerName);
0335: sendNotification(msg);
0336:
0337: synchronized (waitingDeployments) {
0338: List copy = new ArrayList(waitingDeployments);
0339: waitingDeployments.clear();
0340: for (Iterator i = copy.iterator(); i.hasNext();) {
0341: DeploymentInfo di = (DeploymentInfo) i.next();
0342: log.debug("trying to deploy with new deployer: "
0343: + di.shortName);
0344: try {
0345: di.setServer(server);
0346: deploy(di);
0347: } catch (DeploymentException e) {
0348: log
0349: .error(
0350: "DeploymentException while trying to deploy a package with a new deployer",
0351: e);
0352: } // end of try-catch
0353: } // end of for ()
0354: }
0355: }
0356:
0357: /**
0358: * The <code>removeDeployer</code> method unregisters a deployer with the MainDeployer.
0359: * Deployed packages deployed with this deployer are undeployed.
0360: *
0361: * @param deployer a <code>SubDeployer</code> value
0362: * @jmx.managed-operation
0363: */
0364: public void removeDeployer(final SubDeployer deployer) {
0365: log.debug("Removing deployer: " + deployer);
0366: ObjectName deployerName = deployer.getServiceName();
0367: boolean removed = false;
0368:
0369: synchronized (deployers) {
0370: removed = deployers.remove(deployer);
0371: try {
0372: String[] suffixes = (String[]) server.getAttribute(
0373: deployerName, "EnhancedSuffixes");
0374: suffixOrderHelper.removeEnhancedSuffixes(suffixes);
0375: } catch (Exception e) {
0376: log.debug(deployerName
0377: + " does not support EnhancedSuffixes");
0378: suffixOrderHelper.removeSuffixes(
0379: deployer.getSuffixes(), deployer
0380: .getRelativeOrder());
0381: }
0382: }
0383:
0384: // Send a notification about the deployer removal
0385: if (removed) {
0386: Notification msg = new Notification(REMOVE_DEPLOYER, this ,
0387: getNextNotificationSequenceNumber());
0388: msg.setUserData(deployerName);
0389: sendNotification(msg);
0390: }
0391:
0392: List copy;
0393: synchronized (deploymentList) {
0394: copy = new ArrayList(deploymentList);
0395: }
0396: for (Iterator i = copy.iterator(); i.hasNext();) {
0397: DeploymentInfo di = (DeploymentInfo) i.next();
0398: if (di.deployer == deployer) {
0399: undeploy(di);
0400: di.deployer = null;
0401: synchronized (waitingDeployments) {
0402: waitingDeployments.add(di);
0403: }
0404: }
0405: }
0406: }
0407:
0408: /**
0409: * The <code>listDeployers</code> method returns a collection of ObjectNames of
0410: * deployers registered with the MainDeployer.
0411: *
0412: * @return a <code>Collection<ObjectName></code> value
0413: * @jmx.managed-operation
0414: */
0415: public Collection listDeployers() {
0416: ArrayList deployerNames = new ArrayList();
0417: synchronized (deployers) {
0418: for (int n = 0; n < deployers.size(); n++) {
0419: SubDeployer deployer = (SubDeployer) deployers.get(n);
0420: ObjectName name = deployer.getServiceName();
0421: deployerNames.add(name);
0422: }
0423: }
0424: return deployerNames;
0425: }
0426:
0427: // ServiceMBeanSupport overrides ---------------------------------
0428:
0429: protected ObjectName getObjectName(MBeanServer server,
0430: ObjectName name) throws MalformedObjectNameException {
0431: return name == null ? OBJECT_NAME : name;
0432: }
0433:
0434: /**
0435: * The <code>createService</code> method is one of the ServiceMBean lifecyle operations.
0436: * (no jmx tag needed from superinterface)
0437: * @exception Exception if an error occurs
0438: */
0439: protected void createService() throws Exception {
0440: ServerConfig config = ServerConfigLocator.locate();
0441: // Get the temp directory location
0442: File basedir = config.getServerTempDir();
0443: // Set the local copy temp dir to tmp/deploy
0444: tempDir = new File(basedir, "deploy");
0445: // Delete any existing content
0446: Files.delete(tempDir);
0447: // Make sure the directory exists
0448: tempDir.mkdirs();
0449:
0450: // used in inLocalCopyDir
0451: tempDirString = tempDir.toURL().toString();
0452:
0453: // handles SuffixOrder & RelativeSuffixOrder attributes
0454: suffixOrderHelper.initialize();
0455: }
0456:
0457: /**
0458: * The <code>shutdown</code> method undeploys all deployed packages in
0459: * reverse order of their deployement.
0460: *
0461: * @jmx.managed-operation
0462: */
0463: public void shutdown() {
0464: // if we shutdown in the middle of a scan, it still might be possible that we try to redeploy
0465: // things we are busy killing...
0466: int deployCounter = 0;
0467:
0468: // undeploy everything in sight
0469: List copy;
0470: synchronized (deploymentList) {
0471: copy = new ArrayList(deploymentList);
0472: }
0473: for (ListIterator i = copy.listIterator(copy.size()); i
0474: .hasPrevious();) {
0475: try {
0476: DeploymentInfo di = (DeploymentInfo) i.previous();
0477:
0478: // A nested deployment may be already undeployed by the outer deployment
0479: // so check at this point to avoid undeploying twice, see JBAS-3585.
0480: if (getDeployment(di.url) != null) {
0481: undeploy(di, true);
0482: deployCounter++;
0483: }
0484: } catch (Exception e) {
0485: log.info(
0486: "exception trying to undeploy during shutdown",
0487: e);
0488: }
0489: }
0490: // Help GC
0491: this .deployers.clear();
0492: this .deploymentMap.clear();
0493: this .deploymentList.clear();
0494: this .waitingDeployments.clear();
0495: this .tempDir = null;
0496:
0497: log.debug("Undeployed " + deployCounter + " deployed packages");
0498: }
0499:
0500: /**
0501: * Describe <code>redeploy</code> method here.
0502: *
0503: * @param urlspec a <code>String</code> value
0504: * @exception DeploymentException if an error occurs
0505: * @exception MalformedURLException if an error occurs
0506: * @jmx.managed-operation
0507: */
0508: public void redeploy(String urlspec) throws DeploymentException,
0509: MalformedURLException {
0510: redeploy(new URL(urlspec));
0511: }
0512:
0513: /**
0514: * Describe <code>redeploy</code> method here.
0515: *
0516: * @param url an <code>URL</code> value
0517: * @exception DeploymentException if an error occurs
0518: * @jmx.managed-operation
0519: */
0520: public void redeploy(URL url) throws DeploymentException {
0521: DeploymentInfo sdi = getDeployment(url);
0522: if (sdi != null) {
0523: redeploy(sdi);
0524: } else {
0525: deploy(url);
0526: } // end of else
0527: }
0528:
0529: /**
0530: * Describe <code>redeploy</code> method here.
0531: *
0532: * @param sdi a <code>DeploymentInfo</code> value
0533: * @exception DeploymentException if an error occurs
0534: * @jmx.managed-operation
0535: */
0536: public void redeploy(DeploymentInfo sdi) throws DeploymentException {
0537: try {
0538: undeploy(sdi);
0539: } catch (Throwable t) {
0540: log.info("Throwable from undeployment attempt: ", t);
0541: } // end of try-catch
0542: sdi.setServer(server);
0543: deploy(sdi);
0544: }
0545:
0546: /**
0547: * The <code>undeploy</code> method undeploys a package identified by a string
0548: * representation of a URL.
0549: *
0550: * @param urlspec the stringfied url to undeploy
0551: * @jmx.managed-operation
0552: */
0553: public void undeploy(String urlspec) throws DeploymentException,
0554: MalformedURLException {
0555: undeploy(new URL(urlspec));
0556: }
0557:
0558: /**
0559: * The <code>undeploy</code> method undeploys a package identified by a URL
0560: *
0561: * @param url the url to undeploy
0562: * @jmx.managed-operation
0563: */
0564: public void undeploy(URL url) throws DeploymentException {
0565: DeploymentInfo sdi = getDeployment(url);
0566: if (sdi != null) {
0567: undeploy(sdi);
0568: } else {
0569: log.debug("undeploy '" + url + "' : package not deployed");
0570: }
0571: }
0572:
0573: /**
0574: * The <code>undeploy</code> method undeploys a package represented by a
0575: * DeploymentInfo object.
0576: *
0577: * @param di a <code>DeploymentInfo</code> value
0578: * @jmx.managed-operation
0579: */
0580: public void undeploy(DeploymentInfo di) {
0581: undeploy(di, false);
0582: }
0583:
0584: protected void undeploy(DeploymentInfo di, boolean isShutdown) {
0585: log.debug("Undeploying " + di.url + ", isShutdown="
0586: + isShutdown);
0587: stop(di);
0588: destroy(di);
0589: }
0590:
0591: /**
0592: * The <code>stop</code> method is the first internal step of undeployment
0593: *
0594: * @param di a <code>DeploymentInfo</code> value
0595: */
0596: private void stop(DeploymentInfo di) {
0597: // Stop all sub-deployments
0598: ArrayList reverseSortedSubs = new ArrayList(di.subDeployments);
0599: boolean isSorted = di.sortedSubDeployments;
0600: if (!isSorted)
0601: Collections.sort(reverseSortedSubs, infoSorter);
0602: Collections.reverse(reverseSortedSubs);
0603: for (Iterator subs = reverseSortedSubs.iterator(); subs
0604: .hasNext();) {
0605: DeploymentInfo sub = (DeploymentInfo) subs.next();
0606: log.debug("Stopping sub deployment: " + sub.url);
0607: stop(sub);
0608: }
0609: // Lastly stop this deployment itself
0610: try {
0611: // Tell the respective deployer to undeploy this one
0612: if (di.deployer != null) {
0613: di.deployer.stop(di);
0614: di.status = "Stopped";
0615: di.state = DeploymentState.STOPPED;
0616: }
0617: } catch (Throwable t) {
0618: log.error("Deployer stop failed for: " + di.url, t);
0619: }
0620:
0621: }
0622:
0623: /**
0624: * The <code>destroy</code> method is the second and final internal undeployment step.
0625: *
0626: * @param di a <code>DeploymentInfo</code> value
0627: */
0628: private void destroy(DeploymentInfo di) {
0629: // Destroy all sub-deployments
0630: ArrayList reverseSortedSubs = new ArrayList(di.subDeployments);
0631: Collections.sort(reverseSortedSubs, infoSorter);
0632: boolean isSorted = di.sortedSubDeployments;
0633: if (!isSorted)
0634: Collections.reverse(reverseSortedSubs);
0635: for (Iterator subs = reverseSortedSubs.iterator(); subs
0636: .hasNext();) {
0637: DeploymentInfo sub = (DeploymentInfo) subs.next();
0638: log.debug("Destroying sub deployment: " + sub.url);
0639: destroy(sub);
0640: }
0641: // Lastly destroy the deployment itself
0642: try {
0643: // Tell the respective deployer to undeploy this one
0644: if (di.deployer != null) {
0645: di.deployer.destroy(di);
0646: di.status = "Destroyed";
0647: di.state = DeploymentState.DESTROYED;
0648: }
0649: } catch (Throwable t) {
0650: log.error("Deployer destroy failed for: " + di.url, t);
0651: di.state = DeploymentState.FAILED;
0652: }
0653:
0654: try {
0655: // remove from local maps
0656: synchronized (deploymentList) {
0657: deploymentMap.remove(di.url);
0658: if (deploymentList.lastIndexOf(di) != -1) {
0659: deploymentList.remove(deploymentList
0660: .lastIndexOf(di));
0661: }
0662: }
0663: synchronized (waitingDeployments) {
0664: waitingDeployments.remove(di);
0665: }
0666: // Nuke my stuff, this includes the class loader
0667: di.cleanup();
0668:
0669: log.debug("Undeployed " + di.url);
0670: } catch (Throwable t) {
0671: log.error("Undeployment cleanup failed: " + di.url, t);
0672: }
0673: }
0674:
0675: /**
0676: * The <code>deploy</code> method deploys a package identified by a
0677: * string representation of a URL.
0678: *
0679: * @param urlspec a <code>String</code> value
0680: * @exception MalformedURLException if an error occurs
0681: * @jmx.managed-operation
0682: */
0683: public void deploy(String urlspec) throws DeploymentException,
0684: MalformedURLException {
0685: if (server == null)
0686: throw new DeploymentException(
0687: "The MainDeployer has been unregistered");
0688:
0689: URL url;
0690: try {
0691: url = new URL(urlspec);
0692: } catch (MalformedURLException e) {
0693: File file = new File(urlspec);
0694: url = file.toURL();
0695: }
0696:
0697: deploy(url);
0698: }
0699:
0700: /**
0701: * The <code>deploy</code> method deploys a package identified by a URL
0702: *
0703: * @param url an <code>URL</code> value
0704: * @jmx.managed-operation
0705: */
0706: public void deploy(URL url) throws DeploymentException {
0707: DeploymentInfo sdi = getDeployment(url);
0708: // if it does not exist create a new deployment
0709: if (sdi == null) {
0710: sdi = new DeploymentInfo(url, null, getServer());
0711: deploy(sdi);
0712: }
0713: if (sdi.state != DeploymentState.STARTED)
0714: checkIncompleteDeployments();
0715: }
0716:
0717: /**
0718: * The <code>deploy</code> method deploys a package represented by a DeploymentInfo object.
0719: *
0720: * @param deployment a <code>DeploymentInfo</code> value
0721: * @exception DeploymentException if an error occurs
0722: * @jmx.managed-operation
0723: */
0724: public void deploy(DeploymentInfo deployment)
0725: throws DeploymentException {
0726: // If we are already deployed return
0727: if (isDeployed(deployment.url)) {
0728: log.info("Package: " + deployment.url
0729: + " is already deployed");
0730: return;
0731: }
0732: log.debug("Starting deployment of package: " + deployment.url);
0733:
0734: boolean inited = false;
0735: try {
0736: inited = init(deployment);
0737: } catch (Throwable t) {
0738: log.error("Could not initialise deployment: "
0739: + deployment.url, t);
0740: DeploymentException.rethrowAsDeploymentException(
0741: "Could not initialise deployment: "
0742: + deployment.url, t);
0743: }
0744: if (inited) {
0745: create(deployment);
0746: start(deployment);
0747: log.debug("Deployed package: " + deployment.url);
0748: } // end of if ()
0749: else {
0750: log.debug("Deployment of package: " + deployment.url
0751: + " is waiting for an appropriate deployer.");
0752: } // end of else
0753: }
0754:
0755: /**
0756: * The <code>init</code> method is the first internal deployment step.
0757: * The tasks are to copy the code if necessary,
0758: * set up classloaders, and identify the deployer for the package.
0759: *
0760: * @param deployment a <code>DeploymentInfo</code> value
0761: * @throws DeploymentException if an error occurs
0762: */
0763: private boolean init(DeploymentInfo deployment)
0764: throws DeploymentException {
0765: // If we are already deployed return
0766: if (isDeployed(deployment.url)) {
0767: log.info("Package: " + deployment.url
0768: + " is already deployed");
0769: return false;
0770: }
0771: log.debug("Starting deployment (init step) of package at: "
0772: + deployment.url);
0773: try {
0774: // Create a local copy of that File, the sdi keeps track of the copy directory
0775: if (deployment.localUrl == null) {
0776: makeLocalCopy(deployment);
0777: URL[] localCl = new URL[] { deployment.localUrl };
0778: deployment.localCl = new URLClassLoader(localCl);
0779: }
0780:
0781: // What deployer is able to deploy this file
0782: findDeployer(deployment);
0783:
0784: if (deployment.deployer == null) {
0785: deployment.state = DeploymentState.INIT_WAITING_DEPLOYER;
0786: log.debug("deployment waiting for deployer: "
0787: + deployment.url);
0788: synchronized (waitingDeployments) {
0789: if (waitingDeployments.contains(deployment) == false)
0790: waitingDeployments.add(deployment);
0791: }
0792: return false;
0793: }
0794: deployment.state = DeploymentState.INIT_DEPLOYER;
0795: //we have the deployer, continue deployment.
0796: deployment.deployer.init(deployment);
0797: // initialize the unified classloaders for this deployment
0798: deployment.createClassLoaders();
0799: deployment.state = DeploymentState.INITIALIZED;
0800:
0801: // Add the deployment to the map so we can detect circular deployments
0802: synchronized (deploymentList) {
0803: deploymentMap.put(deployment.url, deployment);
0804: }
0805:
0806: // create subdeployments as needed
0807: parseManifestLibraries(deployment);
0808:
0809: log.debug("found " + deployment.subDeployments.size()
0810: + " subpackages of " + deployment.url);
0811: // get sorted subDeployments
0812: ArrayList sortedSubs = new ArrayList(
0813: deployment.subDeployments);
0814: boolean isSorted = deployment.sortedSubDeployments;
0815: if (!isSorted) {
0816: Collections.sort(sortedSubs, infoSorter);
0817: }
0818: for (Iterator lt = sortedSubs.listIterator(); lt.hasNext();) {
0819: init((DeploymentInfo) lt.next());
0820: }
0821: } catch (Exception e) {
0822: deployment.state = DeploymentState.FAILED;
0823: DeploymentException.rethrowAsDeploymentException(
0824: "exception in init of " + deployment.url, e);
0825: } finally {
0826: // whether you do it or not, for the autodeployer
0827: try {
0828: URL url = deployment.localUrl == null ? deployment.url
0829: : deployment.localUrl;
0830:
0831: long lastModified = -1;
0832:
0833: if (url.getProtocol().equals("file"))
0834: lastModified = new File(url.getFile())
0835: .lastModified();
0836: else
0837: lastModified = url.openConnection()
0838: .getLastModified();
0839:
0840: deployment.lastModified = lastModified;
0841: deployment.lastDeployed = System.currentTimeMillis();
0842: } catch (IOException ignore) {
0843: deployment.lastModified = System.currentTimeMillis();
0844: deployment.lastDeployed = System.currentTimeMillis();
0845: }
0846:
0847: synchronized (deploymentList) {
0848: // Do we watch it? Watch only urls outside our copy directory.
0849: if (!inLocalCopyDir(deployment.url)
0850: && deploymentList.contains(deployment) == false) {
0851: deploymentList.add(deployment);
0852: log.debug("Watching new file: " + deployment.url);
0853: }
0854: }
0855: }
0856: return true;
0857: }
0858:
0859: /**
0860: * The <code>create</code> method is the second internal deployment step.
0861: * It should set up all information not
0862: * requiring other components. for instance, the ejb Container is created,
0863: * and the proxy bound into jndi.
0864: *
0865: * @param deployment a <code>DeploymentInfo</code> value
0866: * @throws DeploymentException if an error occurs
0867: */
0868: private void create(DeploymentInfo deployment)
0869: throws DeploymentException {
0870: log.debug("create step for deployment " + deployment.url);
0871: try {
0872: ArrayList sortedSubs = new ArrayList(
0873: deployment.subDeployments);
0874: boolean isSorted = deployment.sortedSubDeployments;
0875: if (!isSorted)
0876: Collections.sort(sortedSubs, infoSorter);
0877: for (Iterator lt = sortedSubs.listIterator(); lt.hasNext();) {
0878: create((DeploymentInfo) lt.next());
0879: }
0880: deployment.state = DeploymentState.CREATE_SUBDEPLOYMENTS;
0881:
0882: // Deploy this SDI, if it is a deployable type
0883: if (deployment.deployer != null) {
0884: try {
0885: deployment.state = DeploymentState.CREATE_DEPLOYER;
0886: deployment.deployer.create(deployment);
0887: // See if all mbeans are created...
0888: deployment.state = DeploymentState.CREATED;
0889: deployment.status = "Created";
0890: log.debug("Done with create step of deploying "
0891: + deployment.shortName);
0892: } catch (Throwable t) {
0893: log.error("Could not create deployment: "
0894: + deployment.url, t);
0895: throw t;
0896: }
0897: } else {
0898: log
0899: .debug("Still no deployer for package in create step: "
0900: + deployment.shortName);
0901: } // end of else
0902: } catch (Throwable t) {
0903: log.trace("could not create deployment: " + deployment.url,
0904: t);
0905: deployment.status = "Deployment FAILED reason: "
0906: + t.getMessage();
0907: deployment.state = DeploymentState.FAILED;
0908: DeploymentException
0909: .rethrowAsDeploymentException(
0910: "Could not create deployment: "
0911: + deployment.url, t);
0912: }
0913: }
0914:
0915: /**
0916: * The <code>start</code> method is the third and final internal deployment step.
0917: * The purpose is to set up relationships between components.
0918: * for instance, ejb links are set up here.
0919: *
0920: * @param deployment a <code>DeploymentInfo</code> value
0921: * @throws DeploymentException if an error occurs
0922: */
0923: private void start(DeploymentInfo deployment)
0924: throws DeploymentException {
0925: deployment.status = "Starting";
0926: log.debug("Begin deployment start " + deployment.url);
0927: try {
0928: ArrayList sortedSubs = new ArrayList(
0929: deployment.subDeployments);
0930: boolean isSorted = deployment.sortedSubDeployments;
0931: if (!isSorted)
0932: Collections.sort(sortedSubs, infoSorter);
0933: for (Iterator lt = sortedSubs.listIterator(); lt.hasNext();) {
0934: start((DeploymentInfo) lt.next());
0935: }
0936: deployment.state = DeploymentState.START_SUBDEPLOYMENTS;
0937:
0938: // Deploy this SDI, if it is a deployable type
0939: if (deployment.deployer != null) {
0940: try {
0941: deployment.state = DeploymentState.START_DEPLOYER;
0942: deployment.deployer.start(deployment);
0943: // See if all mbeans are started...
0944: Object[] args = { deployment,
0945: DeploymentState.STARTED };
0946: String[] sig = {
0947: "org.jboss.deployment.DeploymentInfo",
0948: "org.jboss.deployment.DeploymentState" };
0949: server.invoke(serviceController,
0950: "validateDeploymentState", args, sig);
0951: deployment.status = "Deployed";
0952: log.debug("End deployment start on package: "
0953: + deployment.shortName);
0954: } catch (Throwable t) {
0955: log.error("Could not start deployment: "
0956: + deployment.url, t);
0957: throw t;
0958: }
0959: } else {
0960: log
0961: .debug("Still no deployer for package in start step: "
0962: + deployment.shortName);
0963: } // end of else
0964: } catch (Throwable t) {
0965: log.trace("could not start deployment: " + deployment.url,
0966: t);
0967: deployment.state = DeploymentState.FAILED;
0968: deployment.status = "Deployment FAILED reason: "
0969: + t.getMessage();
0970: DeploymentException
0971: .rethrowAsDeploymentException(
0972: "Could not create deployment: "
0973: + deployment.url, t);
0974: }
0975: }
0976:
0977: /**
0978: * The <code>findDeployer</code> method attempts to find a deployer for the DeploymentInfo
0979: * supplied as a parameter.
0980: *
0981: * @param sdi a <code>DeploymentInfo</code> value
0982: */
0983: private void findDeployer(DeploymentInfo sdi) {
0984: // If there is already a deployer use it
0985: if (sdi.deployer != null) {
0986: log.debug("using existing deployer " + sdi.deployer);
0987: return;
0988: }
0989:
0990: //
0991: // To deploy directories of beans one should just name the directory
0992: // mybean.ear/bla...bla, so that the directory gets picked up by the right deployer
0993: //
0994: synchronized (deployers) {
0995: for (Iterator iterator = deployers.iterator(); iterator
0996: .hasNext();) {
0997: SubDeployer deployer = (SubDeployer) iterator.next();
0998: if (deployer.accepts(sdi)) {
0999: sdi.deployer = deployer;
1000: log.debug("using deployer " + deployer);
1001: return;
1002: }
1003: }
1004: }
1005: log.debug("No deployer found for url: " + sdi.url);
1006: }
1007:
1008: /**
1009: * The <code>parseManifestLibraries</code> method looks into the manifest for classpath
1010: * goo, and tries to deploy referenced packages.
1011: *
1012: * @param sdi a <code>DeploymentInfo</code> value
1013: */
1014: private void parseManifestLibraries(DeploymentInfo sdi) {
1015: String classPath = null;
1016:
1017: Manifest mf = sdi.getManifest();
1018:
1019: if (mf != null) {
1020: Attributes mainAttributes = mf.getMainAttributes();
1021: classPath = mainAttributes
1022: .getValue(Attributes.Name.CLASS_PATH);
1023: }
1024:
1025: if (classPath != null) {
1026: StringTokenizer st = new StringTokenizer(classPath);
1027: log.debug("resolveLibraries: " + classPath);
1028:
1029: while (st.hasMoreTokens()) {
1030: URL lib = null;
1031: String tk = st.nextToken();
1032: log.debug("new manifest entry for sdi at "
1033: + sdi.shortName + " entry is " + tk);
1034:
1035: try {
1036: if (sdi.isDirectory) {
1037: File parentDir = new File(sdi.url.getPath())
1038: .getParentFile();
1039: lib = new File(parentDir, tk).toURL();
1040: } else {
1041: lib = new URL(sdi.url, tk);
1042: }
1043:
1044: // Only deploy this if it is not already being deployed
1045: if (deploymentMap.containsKey(lib) == false) {
1046: /* Test that the only deployer for this is the JARDeployer.
1047: Any other type of deployment cannot be initiated through
1048: a manifest reference.
1049: */
1050: DeploymentInfo mfRef = new DeploymentInfo(lib,
1051: null, getServer());
1052: makeLocalCopy(mfRef);
1053: URL[] localURL = { mfRef.localUrl };
1054: mfRef.localCl = new java.net.URLClassLoader(
1055: localURL);
1056: findDeployer(mfRef);
1057: SubDeployer deployer = mfRef.deployer;
1058: if (deployer != null
1059: && (deployer instanceof JARDeployer) == false) {
1060: // Its a non-jar deployment that must be deployed seperately
1061: log.warn("Found non-jar deployer for " + tk
1062: + ": " + deployer);
1063: }
1064:
1065: // add the library
1066: sdi.addLibraryJar(lib);
1067: }
1068: } catch (Exception ignore) {
1069: log
1070: .debug("The manifest entry in "
1071: + sdi.url
1072: + " references URL "
1073: + lib
1074: + " which could not be opened, entry ignored");
1075: }
1076: }
1077: }
1078: }
1079:
1080: /**
1081: * Downloads the jar file or directory the src URL points to.
1082: * In case of directory it becomes packed to a jar file.
1083: */
1084: private void makeLocalCopy(DeploymentInfo sdi) {
1085: try {
1086: if (sdi.url.getProtocol().equals("file")
1087: && (!copyFiles || sdi.isDirectory)) {
1088: // If local copies have been disabled, do nothing
1089: sdi.localUrl = sdi.url;
1090: return;
1091: }
1092: // Are we already in the localCopyDir?
1093: else if (inLocalCopyDir(sdi.url)) {
1094: sdi.localUrl = sdi.url;
1095: return;
1096: } else {
1097: String shortName = sdi.shortName;
1098: File localFile = File.createTempFile("tmp", shortName,
1099: tempDir);
1100: sdi.localUrl = localFile.toURL();
1101: copy(sdi.url, localFile);
1102: }
1103: } catch (Exception e) {
1104: log.error("Could not make local copy for " + sdi.url, e);
1105: }
1106: }
1107:
1108: private boolean inLocalCopyDir(URL url) {
1109: int i = 0;
1110: String urlTest = url.toString();
1111: if (urlTest.startsWith("jar:"))
1112: i = 4;
1113:
1114: return urlTest.startsWith(tempDirString, i);
1115: }
1116:
1117: protected void copy(URL src, File dest) throws IOException {
1118: log.debug("Copying " + src + " -> " + dest);
1119:
1120: // Validate that the dest parent directory structure exists
1121: File dir = dest.getParentFile();
1122: if (!dir.exists()) {
1123: boolean created = dir.mkdirs();
1124: if (created == false)
1125: throw new IOException("mkdirs failed for: "
1126: + dir.getAbsolutePath());
1127: }
1128:
1129: // Remove any existing dest content
1130: if (dest.exists() == true) {
1131: boolean deleted = Files.delete(dest);
1132: if (deleted == false)
1133: throw new IOException(
1134: "delete of previous content failed for: "
1135: + dest.getAbsolutePath());
1136: }
1137:
1138: if (src.getProtocol().equals("file")) {
1139: File srcFile = new File(src.getFile());
1140: if (srcFile.isDirectory()) {
1141: log.debug("Making zip copy of: " + srcFile);
1142: // make a jar archive of the directory
1143: OutputStream out = new BufferedOutputStream(
1144: new FileOutputStream(dest));
1145: JarUtils.jar(out, srcFile.listFiles());
1146: out.close();
1147: return;
1148: }
1149: }
1150:
1151: InputStream in = new BufferedInputStream(src.openStream());
1152: OutputStream out = new BufferedOutputStream(
1153: new FileOutputStream(dest));
1154: Streams.copy(in, out);
1155: out.flush();
1156: out.close();
1157: in.close();
1158: }
1159:
1160: /**
1161: * The <code>start</code> method starts a package identified by a URL
1162: *
1163: * @param urlspec an <code>URL</code> value
1164: * @jmx.managed-operation
1165: */
1166: public void start(String urlspec) throws DeploymentException,
1167: MalformedURLException {
1168: DeploymentInfo sdi = getDeployment(new URL(urlspec));
1169: if (sdi != null) {
1170: start(sdi);
1171: }
1172: }
1173:
1174: /**
1175: * The <code>stop</code> method stops a package identified by a URL
1176: *
1177: * @param urlspec an <code>URL</code> value
1178: * @jmx.managed-operation
1179: */
1180: public void stop(String urlspec) throws DeploymentException,
1181: MalformedURLException {
1182: DeploymentInfo sdi = getDeployment(new URL(urlspec));
1183: if (sdi != null) {
1184: stop(sdi);
1185: }
1186: }
1187:
1188: /**
1189: * The <code>isDeployed</code> method tells you if a package identified by a string
1190: * representation of a URL is currently deployed.
1191: *
1192: * @param url a <code>String</code> value
1193: * @return a <code>boolean</code> value
1194: * @exception MalformedURLException if an error occurs
1195: * @jmx.managed-operation
1196: */
1197: public boolean isDeployed(String url) throws MalformedURLException {
1198: return isDeployed(new URL(url));
1199: }
1200:
1201: /**
1202: * The <code>isDeployed</code> method tells you if a packaged identified by
1203: * a URL is deployed.
1204: * @param url an <code>URL</code> value
1205: * @return a <code>boolean</code> value
1206: * @jmx.managed-operation
1207: */
1208: public boolean isDeployed(URL url) {
1209: DeploymentInfo di = getDeployment(url);
1210: if (di == null) {
1211: return false;
1212: } // end of if ()
1213: return di.state == DeploymentState.STARTED;
1214: }
1215:
1216: /**
1217: * The <code>getDeployment</code> method returns the DeploymentInfo
1218: * object for the URL supplied.
1219: *
1220: * @param url an <code>URL</code> value
1221: * @return a <code>DeploymentInfo</code> value
1222: * @jmx.managed-operation
1223: */
1224: public DeploymentInfo getDeployment(URL url) {
1225: synchronized (deploymentList) {
1226: return (DeploymentInfo) deploymentMap.get(url);
1227: }
1228: }
1229:
1230: /**
1231: * The <code>getWatchUrl</code> method returns the URL that, when modified,
1232: * indicates that a redeploy is needed.
1233: *
1234: * @param url an <code>URL</code> value
1235: * @return a <code>URL</code> value
1236: * @jmx.managed-operation
1237: */
1238: public URL getWatchUrl(URL url) {
1239: DeploymentInfo info = getDeployment(url);
1240: return info == null ? null : info.watch;
1241: }
1242:
1243: /** Check the current deployment states and generate a IncompleteDeploymentException
1244: * if there are mbeans waiting for depedencies.
1245: * @exception IncompleteDeploymentException
1246: * @jmx.managed-operation
1247: */
1248: public void checkIncompleteDeployments() throws DeploymentException {
1249: try {
1250: Collection waitingForClasses = new HashSet();
1251: Collection waitingForDepends = (Collection) server.invoke(
1252: serviceController, "listIncompletelyDeployed",
1253: new Object[] {}, new String[] {});
1254: Collection allServices = (Collection) server.invoke(
1255: serviceController, "listDeployed", new Object[] {},
1256: new String[] {});
1257:
1258: // Weed services that are waiting for other deployments
1259: Collection rootCause = new HashSet(waitingForDepends);
1260: Collection missing = new HashSet();
1261: for (Iterator i = rootCause.iterator(); i.hasNext();) {
1262: ServiceContext ctx = (ServiceContext) i.next();
1263: for (Iterator j = ctx.iDependOn.iterator(); j.hasNext();) {
1264: ServiceContext dependee = (ServiceContext) j.next();
1265: if (dependee.state != ServiceContext.RUNNING) {
1266: // Add missing mbean
1267: if (allServices.contains(dependee) == false)
1268: missing.add(dependee);
1269: // We are not a root cause
1270: i.remove();
1271: break;
1272: }
1273: }
1274: }
1275: // Add missing mbeans to the root cause
1276: rootCause.addAll(missing);
1277:
1278: IncompleteDeploymentException ide = new IncompleteDeploymentException(
1279: waitingForClasses, waitingForDepends, rootCause,
1280: listIncompletelyDeployed(),
1281: listWaitingForDeployer());
1282: if (!ide.isEmpty()) {
1283: throw ide;
1284: } // end of if ()
1285: } catch (JMException jme) {
1286: throw new DeploymentException(JMXExceptionDecoder
1287: .decode(jme));
1288: } // end of try-catch
1289: }
1290:
1291: /**
1292: * @param parent
1293: * @param map
1294: */
1295: private void fillParentAndChildrenSDI(DeploymentInfo parent, Map map) {
1296: Set subDeployments = parent.subDeployments;
1297: Iterator it = subDeployments.iterator();
1298: while (it.hasNext()) {
1299: DeploymentInfo child = (DeploymentInfo) it.next();
1300: SerializableDeploymentInfo sdichild = returnSDI(child, map);
1301: sdichild.parent = returnSDI(parent, map);
1302: sdichild.parent.subDeployments.add(sdichild);
1303: fillParentAndChildrenSDI(child, map);
1304: }
1305: }
1306:
1307: private SerializableDeploymentInfo returnSDI(DeploymentInfo di,
1308: Map map) {
1309: SerializableDeploymentInfo sdi = (SerializableDeploymentInfo) map
1310: .get(di.url);
1311: if (sdi == null) {
1312: sdi = new SerializableDeploymentInfo(di);
1313: map.put(di.url, sdi);
1314: }
1315: return sdi;
1316: }
1317:
1318: }
|