001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.web;
023:
024: import java.io.ByteArrayInputStream;
025: import java.io.ByteArrayOutputStream;
026: import java.io.File;
027: import java.io.FileInputStream;
028: import java.io.InputStream;
029: import java.lang.reflect.Method;
030: import java.net.URL;
031: import java.net.URLClassLoader;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.HashMap;
035: import java.util.HashSet;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Set;
039: import java.util.jar.JarInputStream;
040: import java.util.zip.ZipEntry;
041:
042: import javax.management.ObjectName;
043:
044: import org.jboss.deployment.DeploymentException;
045: import org.jboss.deployment.DeploymentInfo;
046: import org.jboss.deployment.SubDeployerSupport;
047: import org.jboss.metadata.WebMetaData;
048: import org.jboss.metadata.XmlFileLoader;
049: import org.jboss.mx.loading.LoaderRepositoryFactory;
050: import org.jboss.mx.loading.LoaderRepositoryFactory.LoaderRepositoryConfig;
051: import org.jboss.mx.util.MBeanProxyExt;
052: import org.jboss.mx.util.ObjectNameFactory;
053: import org.jboss.system.ServiceControllerMBean;
054: import org.jboss.util.file.FilenameSuffixFilter;
055: import org.jboss.util.file.JarUtils;
056: import org.w3c.dom.Document;
057: import org.w3c.dom.Element;
058:
059: /** A template pattern class for web container integration into JBoss. This class
060: should be subclasses by web container providers wishing to integrate their
061: container into a JBoss server.
062:
063: @see org.jboss.web.AbstractWebDeployer
064:
065: @jmx:mbean extends="org.jboss.deployment.SubDeployerMBean"
066:
067: @author Scott.Stark@jboss.org
068: @author Christoph.Jung@infor.de
069: @author Thomas.Diesler@arcor.de
070: @version $Revision: 57209 $
071: */
072: public abstract class AbstractWebContainer extends SubDeployerSupport
073: implements AbstractWebContainerMBean {
074: public static final String DEPLOYER = "org.jboss.web.AbstractWebContainer.deployer";
075: public static final String WEB_APP = "org.jboss.web.AbstractWebContainer.webApp";
076: public static final String WEB_MODULE = "org.jboss.web.AbstractWebContainer.webModule";
077: public static final String ERROR = "org.jboss.web.AbstractWebContainer.error";
078:
079: public static interface WebDescriptorParser {
080: /** This method is called as part of subclass performDeploy() method implementations
081: to parse the web-app.xml and jboss-web.xml deployment descriptors from a
082: war deployment. The method creates the ENC(java:comp/env) env-entry,
083: resource-ref, ejb-ref, etc element values. The creation of the env-entry
084: values does not require a jboss-web.xml descriptor. The creation of the
085: resource-ref and ejb-ref elements does require a jboss-web.xml descriptor
086: for the JNDI name of the deployed resources/EJBs.
087:
088: Because the ENC context is private to the web application, the web
089: application class loader is used to identify the ENC. The class loader
090: is used because each war typically requires a unique class loader to
091: isolate the web application classes/resources. This means that the
092: ClassLoader passed to this method must be the thread context ClassLoader
093: seen by the server/jsp pages during init/destroy/service/etc. method
094: invocations if these methods interace with the JNDI ENC context.
095:
096: @param loader the ClassLoader for the web application. May not be null.
097: @param metaData the WebMetaData from the WebApplication object passed to
098: the performDeploy method.
099: */
100: public void parseWebAppDescriptors(ClassLoader loader,
101: WebMetaData metaData) throws Exception;
102:
103: /** Get the DeploymentInfo for the war the triggered the deployment process.
104: * The returned reference may be updated to affect the state of the
105: * JBoss DeploymentInfo object. This can be used to assign ObjectNames
106: * of MBeans created by the container.
107: * @return The DeploymentInfo for the war being deployed.
108: */
109: public DeploymentInfo getDeploymentInfo();
110: }
111:
112: /** The suffixes we accept, along with their relative order */
113: private static final String[] DEFAULT_ENHANCED_SUFFIXES = new String[] { "500:.war" };
114:
115: /** A mapping of deployed warUrl strings to the WebApplication object */
116: protected HashMap deploymentMap = new HashMap();
117: /** The parent class loader first model flag */
118: protected boolean java2ClassLoadingCompliance = false;
119: /** A flag indicating if war archives should be unpacked */
120: protected boolean unpackWars = true;
121: /** A flag indicating if local dirs with WEB-INF/web.xml should be treated as wars
122: */
123: protected boolean acceptNonWarDirs = false;
124:
125: /** If true, ejb-links that don't resolve don't cause an error (fallback to jndi-name) */
126: protected boolean lenientEjbLink = false;
127:
128: /** The default security-domain name to use */
129: protected String defaultSecurityDomain = "java:/jaas/other";
130: /** The request attribute name under which the JAAS Subject is store */
131: private String subjectAttributeName = null;
132: /** The ServiceController used to control web app startup dependencies */
133: protected ServiceControllerMBean serviceController;
134:
135: public AbstractWebContainer() {
136: setEnhancedSuffixes(DEFAULT_ENHANCED_SUFFIXES);
137: }
138:
139: /** Get the flag indicating if the normal Java2 parent first class loading
140: * model should be used over the servlet 2.3 web container first model.
141: * @return true for parent first, false for the servlet 2.3 model
142: * @jmx:managed-attribute
143: */
144: public boolean getJava2ClassLoadingCompliance() {
145: return java2ClassLoadingCompliance;
146: }
147:
148: /** Set the flag indicating if the normal Java2 parent first class loading
149: * model should be used over the servlet 2.3 web container first model.
150: * @param flag true for parent first, false for the servlet 2.3 model
151: * @jmx:managed-attribute
152: */
153: public void setJava2ClassLoadingCompliance(boolean flag) {
154: java2ClassLoadingCompliance = flag;
155: }
156:
157: /** Set the flag indicating if war archives should be unpacked. This may
158: * need to be set to false as long extraction paths under deploy can
159: * show up as deployment failures on some platforms.
160: *
161: * @jmx:managed-attribute
162: * @return true is war archives should be unpacked
163: */
164: public boolean getUnpackWars() {
165: return unpackWars;
166: }
167:
168: /** Get the flag indicating if war archives should be unpacked. This may
169: * need to be set to false as long extraction paths under deploy can
170: * show up as deployment failures on some platforms.
171: *
172: * @jmx:managed-attribute
173: * @param flag , true is war archives should be unpacked
174: */
175: public void setUnpackWars(boolean flag) {
176: this .unpackWars = flag;
177: }
178:
179: /**
180: * Get the flag indicating if local dirs with WEB-INF/web.xml should be
181: * treated as wars
182: * @return true if local dirs with WEB-INF/web.xml should be treated as wars
183: * @jmx.managed-attribute
184: */
185: public boolean getAcceptNonWarDirs() {
186: return acceptNonWarDirs;
187: }
188:
189: /**
190: * Set the flag indicating if local dirs with WEB-INF/web.xml should be
191: * treated as wars
192: * @param flag - true if local dirs with WEB-INF/web.xml should be treated as wars
193: * @jmx.managed-attribute
194: */
195: public void setAcceptNonWarDirs(boolean flag) {
196: this .acceptNonWarDirs = flag;
197: }
198:
199: /**
200: * Get the flag indicating if ejb-link errors should be ignored
201: * in favour of trying the jndi-name in jboss-web.xml
202: * @return the LenientEjbLink flag
203: *
204: * @jmx:managed-attribute
205: */
206: public boolean getLenientEjbLink() {
207: return lenientEjbLink;
208: }
209:
210: /**
211: * Set the flag indicating if ejb-link errors should be ignored
212: * in favour of trying the jndi-name in jboss-web.xml
213: *
214: * @jmx:managed-attribute
215: */
216: public void setLenientEjbLink(boolean flag) {
217: lenientEjbLink = flag;
218: }
219:
220: /** Get the default security domain implementation to use if a war
221: * does not declare a security-domain.
222: *
223: * @return jndi name of the security domain binding to use.
224: * @jmx:managed-attribute
225: */
226: public String getDefaultSecurityDomain() {
227: return defaultSecurityDomain;
228: }
229:
230: /** Set the default security domain implementation to use if a war
231: * does not declare a security-domain.
232: *
233: * @param defaultSecurityDomain - jndi name of the security domain binding
234: * to use.
235: * @jmx:managed-attribute
236: */
237: public void setDefaultSecurityDomain(String defaultSecurityDomain) {
238: this .defaultSecurityDomain = defaultSecurityDomain;
239: }
240:
241: /** Get the session attribute number under which the caller Subject is stored
242: * @jmx:managed-attribute
243: */
244: public String getSubjectAttributeName() {
245: return subjectAttributeName;
246: }
247:
248: /** Set the session attribute number under which the caller Subject is stored
249: * @jmx:managed-attribute
250: */
251: public void setSubjectAttributeName(String subjectAttributeName) {
252: this .subjectAttributeName = subjectAttributeName;
253: }
254:
255: public abstract AbstractWebDeployer getDeployer(DeploymentInfo di)
256: throws Exception;
257:
258: public boolean accepts(DeploymentInfo sdi) {
259: // Should be checking for .war and .war/
260: boolean accepts = super .accepts(sdi);
261:
262: if (accepts == false && acceptNonWarDirs == true) {
263: // Check for a local unpacked directory with a /WEB-INF/web.xml
264: if (sdi.url.getProtocol().equalsIgnoreCase("file")) {
265: File webXml = new File(sdi.url.getFile(),
266: "WEB-INF/web.xml");
267: accepts = webXml.exists();
268: }
269: }
270: return accepts;
271: }
272:
273: public synchronized void init(DeploymentInfo di)
274: throws DeploymentException {
275: log.debug("Begin init");
276: this .server = di.getServer();
277: try {
278: if (di.url.getPath().endsWith("/")) {
279: // the URL is a unpacked collection, watch the deployment descriptor
280: di.watch = new URL(di.url, "WEB-INF/web.xml");
281: } else {
282: // just watch the original URL
283: di.watch = di.url;
284: }
285:
286: // We need to unpack the WAR if it has webservices, because we need
287: // to manipulate th web.xml before deploying to the web container
288: boolean unpackWebservice = di.localCl
289: .findResource("WEB-INF/webservices.xml") != null;
290: // With JSR-181 annotated JSE endpoints we need to do it as well even if there is no webservices.xml
291: unpackWebservice |= server.isRegistered(ObjectNameFactory
292: .create("jboss.ws:service=ServiceEndpointManager"));
293:
294: // Make sure the war is unpacked if unpackWars is true
295: File warFile = new File(di.localUrl.getFile());
296: if (warFile.isDirectory() == false
297: && (unpackWars || unpackWebservice)) {
298: // After findResource we cannot rename the WAR anymore, because
299: // some systems keep an open reference to the file :(
300: String prefix = warFile.getCanonicalPath();
301: prefix = prefix
302: .substring(0, prefix.lastIndexOf(".war"));
303: File expWarFile = new File(prefix + "-exp.war");
304: if (expWarFile.mkdir() == false)
305: throw new DeploymentException(
306: "Was unable to mkdir: " + expWarFile);
307: log.debug("Unpacking war to: " + expWarFile);
308: FileInputStream fis = new FileInputStream(warFile);
309: JarUtils.unjar(fis, expWarFile);
310: fis.close();
311: log.debug("Replaced war with unpacked contents");
312: if (warFile.delete() == false)
313: log.debug("Was unable to delete war file");
314: else
315: log.debug("Deleted war archive");
316: // Reset the localUrl to end in a '/'
317: di.localUrl = expWarFile.toURL();
318: // Reset the localCl to point to the file
319: URL[] localCl = new URL[] { di.localUrl };
320: di.localCl = new URLClassLoader(localCl);
321: }
322:
323: WebMetaData metaData = new WebMetaData();
324: metaData.setResourceClassLoader(di.localCl);
325: metaData
326: .setJava2ClassLoadingCompliance(this .java2ClassLoadingCompliance);
327: di.metaData = metaData;
328:
329: String webContext = di.webContext;
330: if (webContext != null) {
331: if (webContext.length() > 0
332: && webContext.charAt(0) != '/')
333: webContext = "/" + webContext;
334: }
335: // Get the war URL
336: URL warURL = di.localUrl != null ? di.localUrl : di.url;
337: log.debug("webContext: " + webContext);
338: log.debug("warURL: " + warURL);
339:
340: // Parse the web.xml and jboss-web.xml descriptors
341: parseMetaData(webContext, warURL, di.shortName, metaData);
342:
343: // Check for a loader-repository
344: LoaderRepositoryConfig config = metaData.getLoaderConfig();
345: if (config != null)
346: di.setRepositoryInfo(config);
347:
348: // Generate an event for the initialization
349: super .init(di);
350: } catch (DeploymentException e) {
351: log.debug("Problem in init ", e);
352: throw e;
353: } catch (Exception e) {
354: log.error("Problem in init ", e);
355: throw new DeploymentException(e);
356: }
357:
358: log.debug("End init");
359: }
360:
361: /** Create a WebModule service, register it under the name
362: "jboss.web.deployment:war="+di.shortName
363: and invoke the ServiceController.create(jmxname, depends) using the depends
364: found in the WebMetaData.
365:
366: @param di - The deployment info for the war
367: @throws DeploymentException
368: */
369: public void create(DeploymentInfo di) throws DeploymentException {
370: log.debug("create, " + di.shortName);
371: try {
372: // initialize the annotations loader
373: URL loaderURL = (di.localUrl != null ? di.localUrl : di.url);
374: File warFile = new File(di.localUrl.getFile());
375: if (warFile.isDirectory()) {
376: List urlList = new ArrayList();
377: urlList.add(new URL(loaderURL + "WEB-INF/classes/"));
378:
379: File libDir = new File(warFile, "WEB-INF/lib/");
380: String[] jarArr = libDir.list(new FilenameSuffixFilter(
381: ".jar"));
382: for (int i = 0; jarArr != null && i < jarArr.length; i++) {
383: String urlStr = loaderURL + "WEB-INF/lib/"
384: + jarArr[i];
385: urlList.add(new URL(urlStr));
386: }
387: URL[] urlArr = new URL[urlList.size()];
388: urlList.toArray(urlArr);
389: di.annotationsCl = new URLClassLoader(urlArr, di.ucl);
390: } else {
391: List urlList = new ArrayList();
392: urlList.add(new URL(warFile + "!WEB-INF/classes"));
393:
394: FileInputStream fis = new FileInputStream(warFile);
395: JarInputStream jin = new JarInputStream(fis);
396: ZipEntry entry = jin.getNextEntry();
397: while (entry != null) {
398: String entryName = entry.getName();
399: if (entryName.startsWith("WEB-INF/lib")) {
400: urlList.add(new URL(warFile + "!" + entryName));
401: }
402: entry = jin.getNextEntry();
403: }
404: jin.close();
405:
406: URL[] urlArr = new URL[urlList.size()];
407: urlList.toArray(urlArr);
408: di.annotationsCl = new URLClassLoader(urlArr, di.ucl);
409: }
410:
411: AbstractWebDeployer deployer = getDeployer(di);
412: di.context.put(DEPLOYER, deployer);
413: WebMetaData metaData = (WebMetaData) di.metaData;
414: Collection depends = metaData.getDepends();
415: WebModule module = new WebModule(di, this , deployer);
416: ObjectName jmxName = new ObjectName(
417: "jboss.web.deployment:war=" + di.shortName + ",id="
418: + di.hashCode());
419: server.registerMBean(module, jmxName);
420: di.context.put(WEB_MODULE, jmxName);
421: serviceController.create(jmxName, depends);
422: // Generate an event for the create
423: super .create(di);
424: } catch (Exception e) {
425: throw new DeploymentException(
426: "Failed to create web module", e);
427: }
428: }
429:
430: /** Invokes the ServiceController.start(jmxName) to start the WebModule
431: after its dependencies are satisfied.
432:
433: @param di - The deployment info for the war
434: @throws DeploymentException
435: */
436: public synchronized void start(DeploymentInfo di)
437: throws DeploymentException {
438: ObjectName jmxName = (ObjectName) di.context.get(WEB_MODULE);
439: try {
440: serviceController.start(jmxName);
441: } catch (DeploymentException e) {
442: throw e;
443: } catch (Exception e) {
444: throw new DeploymentException("Unable to start web module",
445: e);
446: }
447: // Check for a deployment error
448: DeploymentException e = (DeploymentException) di.context
449: .get(ERROR);
450: if (e != null)
451: throw e;
452:
453: // Generate an event for the startup
454: super .start(di);
455: }
456:
457: /** Invokes the ServiceController.start(jmxName) to stop the WebModule
458: and its dependents.
459:
460: @param di - The deployment info for the war
461: @throws DeploymentException
462: */
463: public synchronized void stop(DeploymentInfo di)
464: throws DeploymentException {
465: ObjectName jmxName = (ObjectName) di.context.get(WEB_MODULE);
466: try {
467: if (jmxName != null)
468: serviceController.stop(jmxName);
469: } catch (DeploymentException e) {
470: throw e;
471: } catch (Exception e) {
472: throw new DeploymentException("Unable to stop web module",
473: e);
474: }
475: // Generate an event for the shutdown
476: super .stop(di);
477: }
478:
479: /** Invokes the ServiceController.destroy(jmxName) to destroy the WebModule
480: and its dependents.
481:
482: @param di - The deployment info for the war
483: @throws DeploymentException
484: */
485: public synchronized void destroy(DeploymentInfo di)
486: throws DeploymentException {
487: ObjectName jmxName = (ObjectName) di.context.get(WEB_MODULE);
488: try {
489: if (jmxName != null) {
490: try {
491: serviceController.destroy(jmxName);
492: } finally {
493: serviceController.remove(jmxName);
494: }
495: }
496: } catch (DeploymentException e) {
497: throw e;
498: } catch (Exception e) {
499: throw new DeploymentException("Unable to stop web module",
500: e);
501: }
502: // Generate an event for the shutdown
503: super .destroy(di);
504: }
505:
506: /** See if a war is deployed.
507: @jmx:managed-operation
508: */
509: public boolean isDeployed(String warUrl) {
510: return deploymentMap.containsKey(warUrl);
511: }
512:
513: public void addDeployedApp(URL warURL, WebApplication webApp) {
514: deploymentMap.put(warURL, webApp);
515: }
516:
517: /** Get the WebApplication object for a deployed war.
518: @param warUrl the war url string as originally passed to deploy().
519: @return The WebApplication created during the deploy step if the
520: warUrl is valid, null if no such deployment exists.
521: */
522: public WebApplication getDeployedApp(String warUrl) {
523: WebApplication appInfo = (WebApplication) deploymentMap
524: .get(warUrl);
525: return appInfo;
526: }
527:
528: public WebApplication removeDeployedApp(URL warURL) {
529: WebApplication appInfo = (WebApplication) deploymentMap
530: .remove(warURL);
531: return appInfo;
532: }
533:
534: /** Returns the applications deployed by the web container subclasses.
535: @jmx:managed-attribute
536: @return An Iterator of WebApplication objects for the deployed wars.
537: */
538: public Iterator getDeployedApplications() {
539: return deploymentMap.values().iterator();
540: }
541:
542: /** An accessor for any configuration element set via setConfig. This
543: method always returns null and must be overriden by subclasses to
544: return a valid value.
545: @jmx:managed-attribute
546: */
547: public Element getConfig() {
548: return null;
549: }
550:
551: /** This method is invoked to import an arbitrary XML configuration tree.
552: Subclasses should override this method if they support such a configuration
553: capability. This implementation does nothing.
554: @jmx:managed-attribute
555: */
556: public void setConfig(Element config) {
557: }
558:
559: /** Use reflection to access a URL[] getURLs method so that non-URLClassLoader
560: *class loaders that support this method can provide info.
561: */
562: public static URL[] getClassLoaderURLs(ClassLoader cl) {
563: URL[] urls = {};
564: try {
565: Class returnType = urls.getClass();
566: Class[] parameterTypes = {};
567: Method getURLs = cl.getClass().getMethod("getURLs",
568: parameterTypes);
569: if (returnType.isAssignableFrom(getURLs.getReturnType())) {
570: Object[] args = {};
571: urls = (URL[]) getURLs.invoke(cl, args);
572: }
573: if (urls == null || urls.length == 0) {
574: getURLs = cl.getClass().getMethod("getAllURLs",
575: parameterTypes);
576: if (returnType
577: .isAssignableFrom(getURLs.getReturnType())) {
578: Object[] args = {};
579: urls = (URL[]) getURLs.invoke(cl, args);
580: }
581: }
582: } catch (Exception ignore) {
583: }
584: return urls;
585: }
586:
587: /** A utility method that walks up the ClassLoader chain starting at
588: the given loader and queries each ClassLoader for a 'URL[] getURLs()'
589: method from which a complete classpath of URL strings is built.
590: */
591: public String[] getCompileClasspath(ClassLoader loader) {
592: HashSet tmp = new HashSet();
593: ClassLoader cl = loader;
594: while (cl != null) {
595: URL[] urls = getClassLoaderURLs(cl);
596: addURLs(tmp, urls);
597: cl = cl.getParent();
598: }
599: try {
600: URL[] globalUrls = (URL[]) server.getAttribute(
601: LoaderRepositoryFactory.DEFAULT_LOADER_REPOSITORY,
602: "URLs");
603: addURLs(tmp, globalUrls);
604: } catch (Exception e) {
605: log
606: .warn("Could not get global URL[] from default loader repository!");
607: } // end of try-catch
608: log.trace("JSP CompileClasspath: " + tmp);
609: String[] cp = new String[tmp.size()];
610: tmp.toArray(cp);
611: return cp;
612: }
613:
614: /** WARs do not have nested deployments
615: * @param di
616: */
617: protected void processNestedDeployments(DeploymentInfo di)
618: throws DeploymentException {
619: }
620:
621: protected void startService() throws Exception {
622: serviceController = (ServiceControllerMBean) MBeanProxyExt
623: .create(ServiceControllerMBean.class,
624: ServiceControllerMBean.OBJECT_NAME, server);
625: super .startService();
626: }
627:
628: /** This method creates a context-root string from either the
629: WEB-INF/jboss-web.xml context-root element is one exists, or the
630: filename portion of the warURL. It is called if the DeploymentInfo
631: webContext value is null which indicates a standalone war deployment.
632: A war name of ROOT.war is handled as a special case of a war that
633: should be installed as the default web context.
634: */
635: protected void parseMetaData(String ctxPath, URL warURL,
636: String warName, WebMetaData metaData)
637: throws DeploymentException {
638: InputStream jbossWebIS = null;
639: InputStream webIS = null;
640:
641: // Parse the war deployment descriptors, web.xml and jboss-web.xml
642: try {
643: // See if the warUrl is a directory
644: File warDir = new File(warURL.getFile());
645: if (warURL.getProtocol().equals("file")
646: && warDir.isDirectory() == true) {
647: File webDD = new File(warDir, "WEB-INF/web.xml");
648: if (webDD.exists() == true)
649: webIS = new FileInputStream(webDD);
650: File jbossWebDD = new File(warDir,
651: "WEB-INF/jboss-web.xml");
652: if (jbossWebDD.exists() == true)
653: jbossWebIS = new FileInputStream(jbossWebDD);
654: } else {
655: // First check for a WEB-INF/web.xml and a WEB-INF/jboss-web.xml
656: InputStream warIS = warURL.openStream();
657: java.util.zip.ZipInputStream zipIS = new java.util.zip.ZipInputStream(
658: warIS);
659: java.util.zip.ZipEntry entry;
660: byte[] buffer = new byte[512];
661: int bytes;
662: while ((entry = zipIS.getNextEntry()) != null) {
663: if (entry.getName().equals("WEB-INF/web.xml")) {
664: ByteArrayOutputStream baos = new ByteArrayOutputStream();
665: while ((bytes = zipIS.read(buffer)) > 0) {
666: baos.write(buffer, 0, bytes);
667: }
668: webIS = new ByteArrayInputStream(baos
669: .toByteArray());
670: } else if (entry.getName().equals(
671: "WEB-INF/jboss-web.xml")) {
672: ByteArrayOutputStream baos = new ByteArrayOutputStream();
673: while ((bytes = zipIS.read(buffer)) > 0) {
674: baos.write(buffer, 0, bytes);
675: }
676: jbossWebIS = new ByteArrayInputStream(baos
677: .toByteArray());
678: }
679: }
680: zipIS.close();
681: }
682:
683: XmlFileLoader xmlLoader = new XmlFileLoader();
684: String warURI = warURL.toExternalForm();
685: try {
686: if (webIS != null) {
687: Document webDoc = xmlLoader.getDocument(webIS,
688: warURI + "/WEB-INF/web.xml");
689: Element web = webDoc.getDocumentElement();
690: metaData.importXml(web);
691: }
692: } catch (Exception e) {
693: throw new DeploymentException(
694: "Failed to parse WEB-INF/web.xml", e);
695: }
696: try {
697: if (jbossWebIS != null) {
698: Document jbossWebDoc = xmlLoader.getDocument(
699: jbossWebIS, warURI
700: + "/WEB-INF/jboss-web.xml");
701: Element jbossWeb = jbossWebDoc.getDocumentElement();
702: metaData.importXml(jbossWeb);
703: }
704: } catch (Exception e) {
705: throw new DeploymentException(
706: "Failed to parse WEB-INF/jboss-web.xml", e);
707: }
708:
709: } catch (DeploymentException e) {
710: throw e;
711: } catch (Exception e) {
712: log.warn("Failed to parse descriptors for war(" + warURL
713: + ")", e);
714: }
715:
716: // Build a war root context from the war name if one was not specified
717: String webContext = ctxPath;
718: if (webContext == null)
719: webContext = metaData.getContextRoot();
720: if (webContext == null) {
721: // Build the context from the war name, strip the .war suffix
722: webContext = warName;
723: webContext = webContext.replace('\\', '/');
724: if (webContext.endsWith("/"))
725: webContext = webContext.substring(0, webContext
726: .length() - 1);
727: int prefix = webContext.lastIndexOf('/');
728: if (prefix > 0)
729: webContext = webContext.substring(prefix + 1);
730: int suffix = webContext.lastIndexOf(".war");
731: if (suffix > 0)
732: webContext = webContext.substring(0, suffix);
733: // Strip any '<int-value>.' prefix
734: int index = 0;
735: for (; index < webContext.length(); index++) {
736: char c = webContext.charAt(index);
737: if (Character.isDigit(c) == false && c != '.')
738: break;
739: }
740: webContext = webContext.substring(index);
741: }
742:
743: // Servlet containers are anal about the web context starting with '/'
744: if (webContext.length() > 0 && webContext.charAt(0) != '/')
745: webContext = "/" + webContext;
746: // And also the default root context must be an empty string, not '/'
747: else if (webContext.equals("/"))
748: webContext = "";
749: metaData.setContextRoot(webContext);
750: }
751:
752: private void addURLs(Set urlSet, URL[] urls) {
753: for (int u = 0; u < urls.length; u++) {
754: URL url = urls[u];
755: urlSet.add(url.toExternalForm());
756: }
757: }
758: }
|