001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.core;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.net.URL;
024:
025: import org.apache.catalina.Container;
026: import org.apache.catalina.Context;
027: import org.apache.catalina.Deployer;
028: import org.apache.catalina.Engine;
029: import org.apache.catalina.Globals;
030: import org.apache.catalina.Host;
031: import org.apache.catalina.Lifecycle;
032: import org.apache.catalina.LifecycleException;
033: import org.apache.catalina.LifecycleListener;
034: import org.apache.catalina.startup.ContextRuleSet;
035: import org.apache.catalina.startup.ExpandWar;
036: import org.apache.catalina.startup.NamingRuleSet;
037: import org.apache.catalina.util.CatalinaDigester;
038: import org.apache.catalina.util.StringManager;
039: import org.apache.commons.digester.Digester;
040:
041: /**
042: * <p>Implementation of <b>Deployer</b> that is delegated to by the
043: * <code>StandardHost</code> implementation class.</p>
044: *
045: * @author Craig R. McClanahan
046: * @version $Revision: 1.23 $ $Date: 2004/04/12 22:05:15 $
047: */
048:
049: public class StandardHostDeployer implements Deployer {
050:
051: private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
052: .getLog(StandardHostDeployer.class);
053:
054: // ----------------------------------------------------------- Constructors
055:
056: public StandardHostDeployer() {
057: }
058:
059: /**
060: * Create a new StandardHostDeployer associated with the specified
061: * StandardHost.
062: *
063: * @param host The StandardHost we are associated with
064: */
065: public StandardHostDeployer(StandardHost host) {
066:
067: super ();
068: this .host = host;
069:
070: }
071:
072: // ----------------------------------------------------- Instance Variables
073:
074: /**
075: * The <code>ContextRuleSet</code> associated with our
076: * <code>digester</code> instance.
077: */
078: private ContextRuleSet contextRuleSet = null;
079:
080: /**
081: * The <code>Digester</code> instance to use for deploying web applications
082: * to this <code>Host</code>. <strong>WARNING</strong> - Usage of this
083: * instance must be appropriately synchronized to prevent simultaneous
084: * access by multiple threads.
085: */
086: private Digester digester = null;
087:
088: /**
089: * The <code>StandardHost</code> instance we are associated with.
090: */
091: protected StandardHost host = null;
092:
093: /**
094: * The <code>NamingRuleSet</code> associated with our
095: * <code>digester</code> instance.
096: */
097: private NamingRuleSet namingRuleSet = null;
098:
099: /**
100: * The document base which should replace the value specified in the
101: * <code>Context</code> being added in the <code>addChild()</code> method,
102: * or <code>null</code> if the original value should remain untouched.
103: */
104: private String overrideDocBase = null;
105:
106: /**
107: * The config file which should replace the value set for the config file
108: * of the <code>Context</code>being added in the <code>addChild()</code>
109: * method, or <code>null</code> if the original value should remain
110: * untouched.
111: */
112: private String overrideConfigFile = null;
113:
114: /**
115: * The string manager for this package.
116: */
117: protected static StringManager sm = StringManager
118: .getManager(Constants.Package);
119:
120: // -------------------------------------------------------- Depoyer Methods
121:
122: public Host getHost() {
123: return host;
124: }
125:
126: public void setHost(Host host) {
127: this .host = (StandardHost) host;
128: }
129:
130: /**
131: * Return the name of the Container with which this Deployer is associated.
132: */
133: public String getName() {
134:
135: return (host.getName());
136:
137: }
138:
139: /**
140: * Install a new web application, whose web application archive is at the
141: * specified URL, into this container with the specified context path.
142: * A context path of "" (the empty string) should be used for the root
143: * application for this container. Otherwise, the context path must
144: * start with a slash.
145: * <p>
146: * If this application is successfully installed, a ContainerEvent of type
147: * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
148: * before the associated Context is started, and a ContainerEvent of type
149: * <code>INSTALL_EVENT</code> will be sent to all registered listeners
150: * after the associated Context is started, with the newly created
151: * <code>Context</code> as an argument.
152: *
153: * @param contextPath The context path to which this application should
154: * be installed (must be unique)
155: * @param war A URL of type "jar:" that points to a WAR file, or type
156: * "file:" that points to an unpacked directory structure containing
157: * the web application to be installed
158: *
159: * @exception IllegalArgumentException if the specified context path
160: * is malformed (it must be "" or start with a slash)
161: * @exception IllegalStateException if the specified context path
162: * is already attached to an existing web application
163: * @exception IOException if an input/output error was encountered
164: * during installation
165: */
166: public synchronized void install(String contextPath, URL war)
167: throws IOException {
168:
169: // Validate the format and state of our arguments
170: if (contextPath == null)
171: throw new IllegalArgumentException(sm
172: .getString("standardHost.pathRequired"));
173: if (!contextPath.equals("") && !contextPath.startsWith("/"))
174: throw new IllegalArgumentException(sm.getString(
175: "standardHost.pathFormat", contextPath));
176: if (findDeployedApp(contextPath) != null)
177: throw new IllegalStateException(sm.getString(
178: "standardHost.pathUsed", contextPath));
179: if (war == null)
180: throw new IllegalArgumentException(sm
181: .getString("standardHost.warRequired"));
182:
183: // Calculate the document base for the new web application
184: log.info(sm.getString("standardHost.installing", contextPath,
185: war.toString()));
186: String url = war.toString();
187: String docBase = null;
188: boolean isWAR = false;
189: if (url.startsWith("jar:")) {
190: url = url.substring(4, url.length() - 2);
191: if (!url.toLowerCase().endsWith(".war")) {
192: throw new IllegalArgumentException(sm.getString(
193: "standardHost.warURL", url));
194: }
195: isWAR = true;
196: }
197: if (url.startsWith("file://"))
198: docBase = url.substring(7);
199: else if (url.startsWith("file:"))
200: docBase = url.substring(5);
201: else
202: throw new IllegalArgumentException(sm.getString(
203: "standardHost.warURL", url));
204:
205: // Determine if directory/war to install is in the host appBase
206: boolean isAppBase = false;
207: File appBase = new File(host.getAppBase());
208: if (!appBase.isAbsolute())
209: appBase = new File(System.getProperty("catalina.base"),
210: host.getAppBase());
211: File contextFile = new File(docBase);
212: File baseDir = contextFile.getParentFile();
213: if (appBase.getCanonicalPath().equals(
214: baseDir.getCanonicalPath())) {
215: isAppBase = true;
216: }
217:
218: // For security, if deployXML is false only allow directories
219: // and war files from the hosts appBase
220: if (!host.isDeployXML() && !isAppBase) {
221: throw new IllegalArgumentException(sm.getString(
222: "standardHost.installBase", url));
223: }
224:
225: // Make sure contextPath and directory/war names match when
226: // installing from the host appBase
227: if (isAppBase && host.getAutoDeploy()) {
228: String filename = contextFile.getName();
229: if (isWAR) {
230: filename = filename.substring(0, filename.length() - 4);
231: }
232: if (contextPath.length() == 0) {
233: if (!filename.equals("ROOT")) {
234: throw new IllegalArgumentException(sm.getString(
235: "standardHost.pathMatch", "/", "ROOT"));
236: }
237: } else if (!filename.equals(contextPath.substring(1))) {
238: throw new IllegalArgumentException(sm
239: .getString("standardHost.pathMatch",
240: contextPath, filename));
241: }
242: }
243:
244: // Expand war file if host wants wars unpacked
245: if (isWAR && host.isUnpackWARs()) {
246: if (contextPath.equals("")) {
247: docBase = ExpandWar.expand(host, war, "/ROOT");
248: } else {
249: docBase = ExpandWar.expand(host, war, contextPath);
250: }
251: }
252:
253: // Install the new web application
254: try {
255: Class clazz = Class.forName(host.getContextClass());
256: Context context = (Context) clazz.newInstance();
257: context.setPath(contextPath);
258: context.setDocBase(docBase);
259: if (context instanceof Lifecycle) {
260: clazz = Class.forName(host.getConfigClass());
261: LifecycleListener listener = (LifecycleListener) clazz
262: .newInstance();
263: ((Lifecycle) context).addLifecycleListener(listener);
264: }
265: host.fireContainerEvent(PRE_INSTALL_EVENT, context);
266: host.addChild(context);
267: host.fireContainerEvent(INSTALL_EVENT, context);
268: } catch (ClassNotFoundException e) {
269: log.info("", e);
270: } catch (Exception e) {
271: log.info("Error installing", e);
272: throw new IOException(e.toString());
273: }
274:
275: }
276:
277: /**
278: * Install a new web application, whose web application archive is at the
279: * specified URL, into this container with the specified context path.
280: * A context path of "" (the empty string) should be used for the root
281: * application for this container. Otherwise, the context path must
282: * start with a slash.
283: * <p>
284: * If this application is successfully installed, a ContainerEvent of type
285: * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
286: * before the associated Context is started, and a ContainerEvent of type
287: * <code>INSTALL_EVENT</code> will be sent to all registered listeners
288: * after the associated Context is started, with the newly created
289: * <code>Context</code> as an argument.
290: *
291: * @param contextPath The context path to which this application should
292: * be installed (must be unique)
293: * @param war A URL of type "jar:" that points to a WAR file, or type
294: * "file:" that points to an unpacked directory structure containing
295: * the web application to be installed
296: * @param configFile The path to a file to save the Context information.
297: * If configFile is null, the Context information is saved in server.xml;
298: * if it is NOT null, the Context information is saved in configFile.
299: *
300: * @exception IllegalArgumentException if the specified context path
301: * is malformed (it must be "" or start with a slash)
302: * @exception IllegalStateException if the specified context path
303: * is already attached to an existing web application
304: * @exception IOException if an input/output error was encountered
305: * during installation
306: */
307: public synchronized void install(String contextPath, URL war,
308: String configFile) throws IOException {
309:
310: // Validate the format and state of our arguments
311: if (contextPath == null)
312: throw new IllegalArgumentException(sm
313: .getString("standardHost.pathRequired"));
314: if (!contextPath.equals("") && !contextPath.startsWith("/"))
315: throw new IllegalArgumentException(sm.getString(
316: "standardHost.pathFormat", contextPath));
317: if (findDeployedApp(contextPath) != null)
318: throw new IllegalStateException(sm.getString(
319: "standardHost.pathUsed", contextPath));
320: if (war == null)
321: throw new IllegalArgumentException(sm
322: .getString("standardHost.warRequired"));
323:
324: // Calculate the document base for the new web application
325: log.info(sm.getString("standardHost.installing", contextPath,
326: war.toString()));
327: String url = war.toString();
328: String docBase = null;
329: boolean isWAR = false;
330: if (url.startsWith("jar:")) {
331: url = url.substring(4, url.length() - 2);
332: if (!url.toLowerCase().endsWith(".war")) {
333: throw new IllegalArgumentException(sm.getString(
334: "standardHost.warURL", url));
335: }
336: isWAR = true;
337: }
338: if (url.startsWith("file://"))
339: docBase = url.substring(7);
340: else if (url.startsWith("file:"))
341: docBase = url.substring(5);
342: else
343: throw new IllegalArgumentException(sm.getString(
344: "standardHost.warURL", url));
345:
346: // Expand war file if host wants wars unpacked
347: if (isWAR && host.isUnpackWARs()) {
348: if (contextPath.equals("")) {
349: docBase = ExpandWar.expand(host, war, "/ROOT");
350: } else {
351: docBase = ExpandWar.expand(host, war, contextPath);
352: }
353: }
354:
355: // Install the new web application
356: try {
357: Class clazz = Class.forName(host.getContextClass());
358: Context context = (Context) clazz.newInstance();
359: context.setPath(contextPath);
360: context.setDocBase(docBase);
361: context.setConfigFile(configFile);
362: if (context instanceof Lifecycle) {
363: clazz = Class.forName(host.getConfigClass());
364: LifecycleListener listener = (LifecycleListener) clazz
365: .newInstance();
366: ((Lifecycle) context).addLifecycleListener(listener);
367: }
368: host.fireContainerEvent(PRE_INSTALL_EVENT, context);
369: host.addChild(context);
370: host.fireContainerEvent(INSTALL_EVENT, context);
371:
372: // save context info into configFile
373: Engine engine = (Engine) host.getParent();
374: StandardServer server = (StandardServer) engine
375: .getService().getServer();
376: //server.storeContext(context);
377: } catch (Exception e) {
378: log.error(sm.getString("standardHost.installError",
379: contextPath), e);
380: throw new IOException(e.toString());
381: }
382:
383: }
384:
385: /**
386: * <p>Install a new web application, whose context configuration file
387: * (consisting of a <code><Context></code> element) and (optional)
388: * web application archive are at the specified URLs.</p>
389: *
390: * If this application is successfully installed, a ContainerEvent of type
391: * <code>PRE_INSTALL_EVENT</code> will be sent to registered listeners
392: * before the associated Context is started, and a ContainerEvent of type
393: * <code>INSTALL_EVENT</code> will be sent to all registered listeners
394: * after the associated Context is started, with the newly created
395: * <code>Context</code> as an argument.
396: *
397: * @param config A URL that points to the context configuration descriptor
398: * to be used for configuring the new Context
399: * @param war A URL of type "jar:" that points to a WAR file, or type
400: * "file:" that points to an unpacked directory structure containing
401: * the web application to be installed, or <code>null</code> to use
402: * the <code>docBase</code> attribute from the configuration descriptor
403: *
404: * @exception IllegalArgumentException if one of the specified URLs is
405: * null
406: * @exception IllegalStateException if the context path specified in the
407: * context configuration file is already attached to an existing web
408: * application
409: * @exception IOException if an input/output error was encountered
410: * during installation
411: */
412: public synchronized void install(URL config, URL war)
413: throws IOException {
414:
415: // Validate the format and state of our arguments
416: if (config == null)
417: throw new IllegalArgumentException(sm
418: .getString("standardHost.configRequired"));
419:
420: if (!host.isDeployXML())
421: throw new IllegalArgumentException(sm
422: .getString("standardHost.configNotAllowed"));
423:
424: log.info(sm.getString("standardHost.installingXML", config));
425:
426: // Calculate the document base for the new web application (if needed)
427: String docBase = null; // Optional override for value in config file
428: boolean isWAR = false;
429: if (war != null) {
430: String url = war.toString();
431: log.info(sm.getString("standardHost.installingWAR", url));
432: // Calculate the WAR file absolute pathname
433: if (url.startsWith("jar:")) {
434: url = url.substring(4, url.length() - 2);
435: isWAR = true;
436: }
437: if (url.startsWith("file://"))
438: docBase = url.substring(7);
439: else if (url.startsWith("file:"))
440: docBase = url.substring(5);
441: else
442: throw new IllegalArgumentException(sm.getString(
443: "standardHost.warURL", url));
444:
445: }
446:
447: // Expand war file if host wants wars unpacked
448: if (isWAR && host.isUnpackWARs()) {
449: docBase = ExpandWar.expand(host, war);
450: }
451:
452: // Install the new web application
453: this .overrideDocBase = docBase;
454: String configPath = config.toString();
455: if (configPath.startsWith("file:")) {
456: if (configPath.startsWith("file://")) {
457: configPath = configPath.substring(7);
458: } else {
459: configPath = configPath.substring(5);
460: }
461: this .overrideConfigFile = (new File(configPath))
462: .getAbsolutePath();
463: } else {
464: configPath = null;
465: }
466:
467: InputStream stream = null;
468: try {
469: if (configPath == null) {
470: stream = config.openStream();
471: } else {
472: stream = new FileInputStream(configPath);
473: }
474: Digester digester = createDigester();
475: digester.setClassLoader(this .getClass().getClassLoader());
476: digester.clear();
477: digester.push(this );
478: digester.parse(stream);
479: stream.close();
480: stream = null;
481: } catch (Exception e) {
482: host.log(
483: sm.getString("standardHost.installError", docBase),
484: e);
485: throw new IOException(e.toString());
486: } finally {
487: if (stream != null) {
488: try {
489: stream.close();
490: } catch (Throwable t) {
491: ;
492: }
493: }
494: this .overrideDocBase = null;
495: this .overrideConfigFile = null;
496: }
497:
498: }
499:
500: /**
501: * Installs a new web application from the web application archive at the
502: * specified URL, which must contain a META-INF/context.xml context
503: * configuration file (consisting of a <code><Context></code>
504: * element).
505: *
506: * <p>The web application is installed at the path specified inside the
507: * embedded META-INF/context.xml. The docBase (if any) specified inside the
508: * embedded META-INF/context.xml is overridden with the web application's
509: * location.
510: *
511: * <p>If the installation succeeds, a ContainerEvent of type
512: * <code>INSTALL_EVENT</code> is sent to all registered listeners,
513: * with the newly created <code>Context</code> as its argument.
514: *
515: * @param war URL pointing to web application location (WAR-packaged or
516: * unpacked directory)
517: *
518: * @exception IllegalArgumentException if <code>war</code> is null, or if
519: * the deployment host does not support any context.xml
520: * configuration files
521: * @exception IllegalStateException if the context path specified in the
522: * context configuration file is already in use by an existing
523: * web application
524: * @exception IOException if an input/output error was encountered
525: * during installation
526: */
527: public synchronized void install(URL war) throws IOException {
528:
529: if (war == null) {
530: throw new IllegalArgumentException(sm
531: .getString("standardHost.warRequired"));
532: }
533:
534: if (!host.isDeployXML()) {
535: throw new IllegalArgumentException(sm
536: .getString("standardHost.configNotAllowed"));
537: }
538:
539: // Calculate the document base for the new web application (if needed)
540: String docBase = null; // Optional override for value in config file
541: boolean isWAR = false;
542: String url = war.toString();
543: log.info(sm.getString("standardHost.installingWAR", url));
544: // Calculate the WAR file absolute pathname
545: if (url.startsWith("jar:")) {
546: url = url.substring("jar:".length(), url.length() - 2);
547: isWAR = true;
548: }
549: if (url.startsWith("file://")) {
550: docBase = url.substring("file://".length());
551: } else if (url.startsWith("file:")) {
552: docBase = url.substring("file:".length());
553: } else {
554: throw new IllegalArgumentException(sm.getString(
555: "standardHost.warURL", url));
556: }
557:
558: // Expand war file if host wants wars unpacked
559: if (isWAR && host.isUnpackWARs()) {
560: docBase = ExpandWar.expand(host, war);
561: }
562:
563: // Install the new web application
564: this .overrideDocBase = docBase;
565:
566: InputStream stream = null;
567: try {
568: URL contextXml = new URL(war.toString()
569: + "META-INF/context.xml");
570: log.info(sm.getString("standardHost.installingXML",
571: contextXml));
572: stream = contextXml.openStream();
573: Digester digester = createDigester();
574: digester.setClassLoader(this .getClass().getClassLoader());
575: digester.clear();
576: digester.push(this );
577: digester.parse(stream);
578: stream.close();
579: stream = null;
580: } catch (Exception e) {
581: host.log(
582: sm.getString("standardHost.installError", docBase),
583: e);
584: throw new IOException(e.toString());
585: } finally {
586: if (stream != null) {
587: try {
588: stream.close();
589: } catch (Throwable t) {
590: ;
591: }
592: }
593: this .overrideDocBase = null;
594: }
595:
596: }
597:
598: /**
599: * Return the Context for the deployed application that is associated
600: * with the specified context path (if any); otherwise return
601: * <code>null</code>.
602: *
603: * @param contextPath The context path of the requested web application
604: */
605: public Context findDeployedApp(String contextPath) {
606:
607: return ((Context) host.findChild(contextPath));
608:
609: }
610:
611: /**
612: * Return the context paths of all deployed web applications in this
613: * Container. If there are no deployed applications, a zero-length
614: * array is returned.
615: */
616: public String[] findDeployedApps() {
617:
618: Container children[] = host.findChildren();
619: String results[] = new String[children.length];
620: for (int i = 0; i < children.length; i++)
621: results[i] = children[i].getName();
622: return (results);
623:
624: }
625:
626: /**
627: * Remove an existing web application, attached to the specified context
628: * path. If this application is successfully removed, a
629: * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
630: * registered listeners, with the removed <code>Context</code> as
631: * an argument.
632: *
633: * @param contextPath The context path of the application to be removed
634: *
635: * @exception IllegalArgumentException if the specified context path
636: * is malformed (it must be "" or start with a slash)
637: * @exception IllegalArgumentException if the specified context path does
638: * not identify a currently installed web application
639: * @exception IOException if an input/output error occurs during
640: * removal
641: */
642: public void remove(String contextPath) throws IOException {
643:
644: // Validate the format and state of our arguments
645: if (contextPath == null)
646: throw new IllegalArgumentException(sm
647: .getString("standardHost.pathRequired"));
648: if (!contextPath.equals("") && !contextPath.startsWith("/"))
649: throw new IllegalArgumentException(sm.getString(
650: "standardHost.pathFormat", contextPath));
651:
652: // Locate the context and associated work directory
653: Context context = findDeployedApp(contextPath);
654: if (context == null)
655: throw new IllegalArgumentException(sm.getString(
656: "standardHost.pathMissing", contextPath));
657:
658: // Remove this web application
659: log.info(sm.getString("standardHost.removing", contextPath));
660: try {
661: host.removeChild(context);
662: host.fireContainerEvent(REMOVE_EVENT, context);
663: } catch (Exception e) {
664: log.error(sm.getString("standardHost.removeError",
665: contextPath), e);
666: throw new IOException(e.toString());
667: }
668:
669: }
670:
671: /**
672: * Remove an existing web application, attached to the specified context
673: * path. If this application is successfully removed, a
674: * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
675: * registered listeners, with the removed <code>Context</code> as
676: * an argument. Deletes the web application war file and/or directory
677: * if they exist in the Host's appBase.
678: *
679: * @param contextPath The context path of the application to be removed
680: * @param undeploy boolean flag to remove web application from server
681: *
682: * @exception IllegalArgumentException if the specified context path
683: * is malformed (it must be "" or start with a slash)
684: * @exception IllegalArgumentException if the specified context path does
685: * not identify a currently installed web application
686: * @exception IOException if an input/output error occurs during
687: * removal
688: */
689: public void remove(String contextPath, boolean undeploy)
690: throws IOException {
691:
692: // Validate the format and state of our arguments
693: if (contextPath == null)
694: throw new IllegalArgumentException(sm
695: .getString("standardHost.pathRequired"));
696: if (!contextPath.equals("") && !contextPath.startsWith("/"))
697: throw new IllegalArgumentException(sm.getString(
698: "standardHost.pathFormat", contextPath));
699:
700: // Locate the context and associated work directory
701: Context context = findDeployedApp(contextPath);
702: if (context == null)
703: throw new IllegalArgumentException(sm.getString(
704: "standardHost.pathMissing", contextPath));
705:
706: // Remove this web application
707: host.log(sm.getString("standardHost.removing", contextPath));
708: try {
709: // Get the work directory for the Context
710: File workDir = (File) context.getServletContext()
711: .getAttribute(Globals.WORK_DIR_ATTR);
712: String configFile = context.getConfigFile();
713: host.removeChild(context);
714:
715: if (undeploy) {
716: // Remove the web application directory and/or war file if it
717: // exists in the Host's appBase directory.
718:
719: // Determine if directory/war to remove is in the host appBase
720: boolean isAppBase = false;
721: File appBase = new File(host.getAppBase());
722: if (!appBase.isAbsolute())
723: appBase = new File(System
724: .getProperty("catalina.base"), host
725: .getAppBase());
726: File contextFile = new File(context.getDocBase());
727: File baseDir = contextFile.getParentFile();
728: if ((baseDir == null)
729: || (appBase.getCanonicalPath().equals(baseDir
730: .getCanonicalPath()))) {
731: isAppBase = true;
732: }
733:
734: boolean isWAR = false;
735: if (contextFile.getName().toLowerCase()
736: .endsWith(".war")) {
737: isWAR = true;
738: }
739: // Only remove directory and/or war if they are located in the
740: // Host's appBase autoDeploy is true
741: if (isAppBase && host.getAutoDeploy()) {
742: String filename = contextFile.getName();
743: if (isWAR) {
744: filename = filename.substring(0, filename
745: .length() - 4);
746: }
747: if (contextPath.length() == 0
748: && filename.equals("ROOT")
749: || filename
750: .equals(contextPath.substring(1))) {
751: if (!isWAR) {
752: long contextLastModified = contextFile
753: .lastModified();
754: if (contextFile.isDirectory()) {
755: ExpandWar.deleteDir(contextFile);
756: }
757: if (host.isUnpackWARs()) {
758: File contextWAR = new File(context
759: .getDocBase()
760: + ".war");
761: if (contextWAR.exists()) {
762: if (contextLastModified > contextWAR
763: .lastModified()) {
764: contextWAR.delete();
765: }
766: }
767: }
768: } else {
769: contextFile.delete();
770: }
771: }
772: if (host.isDeployXML() && (configFile != null)) {
773: File docBaseXml = new File(configFile);
774: docBaseXml.delete();
775: }
776: }
777:
778: // Remove the work directory for the Context
779: if (workDir == null
780: && context instanceof StandardContext
781: && ((StandardContext) context).getWorkDir() != null) {
782: workDir = new File(((StandardContext) context)
783: .getWorkPath());
784: }
785: if (workDir != null && workDir.exists()) {
786: ExpandWar.deleteDir(workDir);
787: }
788: }
789:
790: host.fireContainerEvent(REMOVE_EVENT, context);
791: } catch (Exception e) {
792: host.log(sm.getString("standardHost.removeError",
793: contextPath), e);
794: throw new IOException(e.toString());
795: }
796:
797: }
798:
799: /**
800: * Start an existing web application, attached to the specified context
801: * path. Only starts a web application if it is not running.
802: *
803: * @param contextPath The context path of the application to be started
804: *
805: * @exception IllegalArgumentException if the specified context path
806: * is malformed (it must be "" or start with a slash)
807: * @exception IllegalArgumentException if the specified context path does
808: * not identify a currently installed web application
809: * @exception IOException if an input/output error occurs during
810: * startup
811: */
812: public void start(String contextPath) throws IOException {
813:
814: // Validate the format and state of our arguments
815: if (contextPath == null)
816: throw new IllegalArgumentException(sm
817: .getString("standardHost.pathRequired"));
818: if (!contextPath.equals("") && !contextPath.startsWith("/"))
819: throw new IllegalArgumentException(sm.getString(
820: "standardHost.pathFormat", contextPath));
821: Context context = findDeployedApp(contextPath);
822: if (context == null)
823: throw new IllegalArgumentException(sm.getString(
824: "standardHost.pathMissing", contextPath));
825: log.info("standardHost.start " + contextPath);
826: try {
827: ((Lifecycle) context).start();
828: } catch (LifecycleException e) {
829: log.info("standardHost.start " + contextPath + ": ", e);
830: throw new IllegalStateException("standardHost.start "
831: + contextPath + ": " + e);
832: }
833: }
834:
835: /**
836: * Stop an existing web application, attached to the specified context
837: * path. Only stops a web application if it is running.
838: *
839: * @param contextPath The context path of the application to be stopped
840: *
841: * @exception IllegalArgumentException if the specified context path
842: * is malformed (it must be "" or start with a slash)
843: * @exception IllegalArgumentException if the specified context path does
844: * not identify a currently installed web application
845: * @exception IOException if an input/output error occurs while stopping
846: * the web application
847: */
848: public void stop(String contextPath) throws IOException {
849:
850: // Validate the format and state of our arguments
851: if (contextPath == null)
852: throw new IllegalArgumentException(sm
853: .getString("standardHost.pathRequired"));
854: if (!contextPath.equals("") && !contextPath.startsWith("/"))
855: throw new IllegalArgumentException(sm.getString(
856: "standardHost.pathFormat", contextPath));
857: Context context = findDeployedApp(contextPath);
858: if (context == null)
859: throw new IllegalArgumentException(sm.getString(
860: "standardHost.pathMissing", contextPath));
861: log.info("standardHost.stop " + contextPath);
862: try {
863: ((Lifecycle) context).stop();
864: } catch (LifecycleException e) {
865: log.error("standardHost.stop " + contextPath + ": ", e);
866: throw new IllegalStateException("standardHost.stop "
867: + contextPath + ": " + e);
868: }
869:
870: }
871:
872: // ------------------------------------------------------ Delegated Methods
873:
874: /**
875: * Delegate a request to add a child Context to our associated Host.
876: *
877: * @param child The child Context to be added
878: */
879: public void addChild(Container child) {
880:
881: Context context = (Context) child;
882: String contextPath = context.getPath();
883: if (contextPath == null)
884: throw new IllegalArgumentException(sm
885: .getString("standardHost.pathRequired"));
886: else if (!contextPath.equals("")
887: && !contextPath.startsWith("/"))
888: throw new IllegalArgumentException(sm.getString(
889: "standardHost.pathFormat", contextPath));
890: if (host.findChild(contextPath) != null)
891: throw new IllegalStateException(sm.getString(
892: "standardHost.pathUsed", contextPath));
893: if (this .overrideDocBase != null)
894: context.setDocBase(this .overrideDocBase);
895: if (this .overrideConfigFile != null)
896: context.setConfigFile(this .overrideConfigFile);
897: host.fireContainerEvent(PRE_INSTALL_EVENT, context);
898: host.addChild(child);
899: host.fireContainerEvent(INSTALL_EVENT, context);
900:
901: }
902:
903: /**
904: * Delegate a request for the parent class loader to our associated Host.
905: */
906: public ClassLoader getParentClassLoader() {
907:
908: return (host.getParentClassLoader());
909:
910: }
911:
912: /**
913: * Returns true if context.xml config files are supported.
914: *
915: * @return true of context.xml config files are supported, false otherwise
916: */
917: public boolean isDeployXML() {
918: return (host.isDeployXML());
919: }
920:
921: // ------------------------------------------------------ Protected Methods
922:
923: /**
924: * Create (if necessary) and return a Digester configured to process the
925: * context configuration descriptor for an application.
926: */
927: protected Digester createDigester() {
928: if (digester == null) {
929: digester = new CatalinaDigester();
930: digester.setValidating(false);
931: contextRuleSet = new ContextRuleSet("");
932: digester.addRuleSet(contextRuleSet);
933: namingRuleSet = new NamingRuleSet("Context/");
934: digester.addRuleSet(namingRuleSet);
935: }
936: return (digester);
937:
938: }
939:
940: }
|