View Javadoc

1   /*
2    * Copyright 1999,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.catalina.servlets;
19  
20  
21  import java.io.BufferedOutputStream;
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.InputStream;
25  import java.io.IOException;
26  import java.io.PrintWriter;
27  import java.net.URL;
28  import java.net.MalformedURLException;
29  import java.util.Iterator;
30  import java.util.Locale;
31  import java.util.jar.JarEntry;
32  import java.util.jar.JarFile;
33  import javax.naming.Binding;
34  import javax.naming.InitialContext;
35  import javax.naming.NamingEnumeration;
36  import javax.naming.NamingException;
37  import javax.naming.directory.DirContext;
38  import javax.servlet.ServletException;
39  import javax.servlet.ServletInputStream;
40  import javax.servlet.UnavailableException;
41  import javax.servlet.http.HttpServlet;
42  import javax.servlet.http.HttpServletRequest;
43  import javax.servlet.http.HttpServletResponse;
44  
45  import org.apache.catalina.Container;
46  import org.apache.catalina.ContainerServlet;
47  import org.apache.catalina.Engine;
48  import org.apache.catalina.Context;
49  import org.apache.catalina.Deployer;
50  import org.apache.catalina.Globals;
51  import org.apache.catalina.Host;
52  import org.apache.catalina.Lifecycle;
53  import org.apache.catalina.LifecycleListener;
54  import org.apache.catalina.Role;
55  import org.apache.catalina.Server;
56  import org.apache.catalina.ServerFactory;
57  import org.apache.catalina.Session;
58  import org.apache.catalina.UserDatabase;
59  import org.apache.catalina.Wrapper;
60  import org.apache.catalina.core.StandardServer;
61  import org.apache.catalina.startup.HostConfig;
62  import org.apache.catalina.util.RequestUtil;
63  import org.apache.catalina.util.ServerInfo;
64  import org.apache.catalina.util.StringManager;
65  import org.apache.naming.resources.ProxyDirContext;
66  import org.apache.naming.resources.WARDirContext;
67  
68  import org.talika.tms.Constants;
69  
70  /**
71   * Servlet that enables remote management of the web applications installed
72   * within the same virtual host as this web application is.  Normally, this
73   * functionality will be protected by a security constraint in the web
74   * application deployment descriptor.  However, this requirement can be
75   * relaxed during testing.
76   * <p>
77   * This servlet examines the value returned by <code>getPathInfo()</code>
78   * and related query parameters to determine what action is being requested.
79   * The following actions and parameters (starting after the servlet path)
80   * are supported:
81   * <ul>
82   * <li><b>/install?config={config-url}</b> - Install and start a new
83   *     web application, based on the contents of the context configuration
84   *     file found at the specified URL.  The <code>docBase</code> attribute
85   *     of the context configuration file is used to locate the actual
86   *     WAR or directory containing the application.</li>
87   * <li><b>/install?config={config-url}&war={war-url}/</b> - Install and start
88   *     a new web application, based on the contents of the context
89   *     configuration file found at <code>{config-url}</code>, overriding the
90   *     <code>docBase</code> attribute with the contents of the web
91   *     application archive found at <code>{war-url}</code>.</li>
92   * <li><b>/install?path=/xxx&war={war-url}</b> - Install and start a new
93   *     web application attached to context path <code>/xxx</code>, based
94   *     on the contents of the web application archive found at the
95   *     specified URL.</li>
96   * <li><b>/list</b> - List the context paths of all currently installed web
97   *     applications for this virtual host.  Each context will be listed with
98   *     the following format <code>path:status:sessions</code>.
99   *     Where path is the context path.  Status is either running or stopped.
100  *     Sessions is the number of active Sessions.</li>
101  * <li><b>/reload?path=/xxx</b> - Reload the Java classes and resources for
102  *     the application at the specified path, but do not reread the web.xml
103  *     configuration files.</li>
104  * <li><b>/remove?path=/xxx</b> - Shutdown and remove the web application
105  *     attached to context path <code>/xxx</code> for this virtual host.</li>
106  * <li><b>/resources?type=xxxx</b> - Enumerate the available global JNDI
107  *     resources, optionally limited to those of the specified type
108  *     (fully qualified Java class name), if available.</li>
109  * <li><b>/roles</b> - Enumerate the available security role names and
110  *     descriptions from the user database connected to the <code>users</code>
111  *     resource reference.
112  * <li><b>/serverinfo</b> - Display system OS and JVM properties.
113  * <li><b>/sessions?path=/xxx</b> - List session information about the web
114  *     application attached to context path <code>/xxx</code> for this
115  *     virtual host.</li>
116  * <li><b>/start?path=/xxx</b> - Start the web application attached to
117  *     context path <code>/xxx</code> for this virtual host.</li>
118  * <li><b>/stop?path=/xxx</b> - Stop the web application attached to
119  *     context path <code>/xxx</code> for this virtual host.</li>
120  * <li><b>/undeploy?path=/xxx</b> - Shutdown and remove the web application
121  *     attached to context path <code>/xxx</code> for this virtual host,
122  *     and remove the underlying WAR file or document base directory.
123  *     (<em>NOTE</em> - This is only allowed if the WAR file or document
124  *     base is stored in the <code>appBase</code> directory of this host,
125  *     typically as a result of being placed there via the <code>/deploy</code>
126  *     command.</li>
127  * </ul>
128  * <p>Use <code>path=/</code> for the ROOT context.</p>
129  * <p>The syntax of the URL for a web application archive must conform to one
130  * of the following patterns to be successfully deployed:</p>
131  * <ul>
132  * <li><b>file:/absolute/path/to/a/directory</b> - You can specify the absolute
133  *     path of a directory that contains the unpacked version of a web
134  *     application.  This directory will be attached to the context path you
135  *     specify without any changes.</li>
136  * <li><b>jar:file:/absolute/path/to/a/warfile.war!/</b> - You can specify a
137  *     URL to a local web application archive file.  The syntax must conform to
138  *     the rules specified by the <code>JarURLConnection</code> class for a
139  *     reference to an entire JAR file.</li>
140  * <li><b>jar:http://hostname:port/path/to/a/warfile.war!/</b> - You can specify
141  *     a URL to a remote (HTTP-accessible) web application archive file.  The
142  *     syntax must conform to the rules specified by the
143  *     <code>JarURLConnection</code> class for a reference to an entire
144  *     JAR file.</li>
145  * </ul>
146  * <p>
147  * <b>NOTE</b> - Attempting to reload or remove the application containing
148  * this servlet itself will not succeed.  Therefore, this servlet should
149  * generally be deployed as a separate web application within the virtual host
150  * to be managed.
151  * <p>
152  * <b>NOTE</b> - For security reasons, this application will not operate
153  * when accessed via the invoker servlet.  You must explicitly map this servlet
154  * with a servlet mapping, and you will always want to protect it with
155  * appropriate security constraints as well.
156  * <p>
157  * The following servlet initialization parameters are recognized:
158  * <ul>
159  * <li><b>debug</b> - The debugging detail level that controls the amount
160  *     of information that is logged by this servlet.  Default is zero.
161  * </ul>
162  *
163  * @author Craig R. McClanahan
164  * @author Jose M. Palomar
165  * @version $Revision: 319 $ $Date: 2006-09-10 13:57:10 +0200 (dom 10 de sep de 2006) $
166  * 
167  * @todo Test more deploy functions over multiple hosts.
168  */
169 
170 public class ManagerXServlet
171     extends HttpServlet implements ContainerServlet {
172 
173 
174     // ----------------------------------------------------- Instance Variables
175 
176 
177     /**
178      * The Context container associated with our web application.
179      */
180     protected Context context = null;
181 
182 
183     /**
184      * The debugging detail level for this servlet.
185      */
186     protected int debug = 1;
187 
188 
189     /**
190      * The Engine container of all our vhosts
191      */
192     protected Engine engine = null;
193 
194 
195     /**
196      * File object representing the directory into which the deploy() command
197      * will store the WAR and context configuration files that have been
198      * uploaded.
199      */
200     protected File deployed = null;
201 
202 
203     /**
204      * The global JNDI <code>NamingContext</code> for this server,
205      * if available.
206      */
207     protected javax.naming.Context global = null;
208 
209 
210     /**
211      * The string manager for this package.
212      */
213     protected static StringManager sm =
214         StringManager.getManager(Constants.Package);
215 
216 
217     /**
218      * The Wrapper container associated with this servlet.
219      */
220     protected Wrapper wrapper = null;
221 
222 
223     // ----------------------------------------------- ContainerServlet Methods
224 
225 
226     /**
227      * Return the Wrapper with which we are associated.
228      */
229     public Wrapper getWrapper() {
230 
231         return (this.wrapper);
232 
233     }
234 
235 
236     /**
237      * Set the Wrapper with which we are associated.
238      *
239      * @param wrapper The new wrapper
240      */
241     public void setWrapper(Wrapper wrapper) {
242 
243         this.wrapper = wrapper;
244         if (wrapper == null) {
245             context = null;
246             engine = null;
247         } else {
248             context = (Context) wrapper.getParent();
249             engine = (Engine) context.getParent().getParent();
250         }
251 
252     }
253 
254 
255     // --------------------------------------------------------- Public Methods
256 
257 
258     /**
259      * Finalize this servlet.
260      */
261     public void destroy() {
262 
263         ;       // No actions necessary
264 
265     }
266 
267 
268     /**
269      * Process a GET request for the specified resource.
270      *
271      * @param request The servlet request we are processing
272      * @param response The servlet response we are creating
273      *
274      * @exception IOException if an input/output error occurs
275      * @exception ServletException if a servlet-specified error occurs
276      */
277     public void doGet(HttpServletRequest request,
278                       HttpServletResponse response)
279         throws IOException, ServletException {
280 
281         // Verify that we were not accessed using the invoker servlet
282         if (request.getAttribute(Globals.INVOKED_ATTR) != null)
283             throw new UnavailableException
284                 (sm.getString("managerServlet.cannotInvoke"));
285 
286         // Identify the request parameters that we need
287         String command = request.getPathInfo();
288         if (command == null)
289             command = request.getServletPath();
290         String vhost = request.getParameter("vhost");
291         String config = request.getParameter("config");
292         String path = request.getParameter("path");
293         String type = request.getParameter("type");
294         String war = request.getParameter("war");
295 
296         // Prepare our output writer to generate the response message
297         Locale locale = Locale.getDefault();
298         String charset = context.getCharsetMapper().getCharset(locale);
299         response.setLocale(locale);
300         response.setContentType("text/plain; charset=" + charset);
301         PrintWriter writer = response.getWriter();
302 
303         // Process the requested command (note - "/deploy" is not listed here)
304         if (command == null) {
305             writer.println(sm.getString("managerServlet.noCommand"));
306         } else if (command.equals("/vhosts")) {
307             vhosts(writer);
308         } else if (command.equals("/install")) {
309             install(writer, vhost, config, path, war);
310         } else if (command.equals("/list")) {
311             list(writer, vhost);
312         } else if (command.equals("/reload")) {
313             reload(writer, vhost, path);
314         } else if (command.equals("/remove")) {
315             remove(writer, vhost, path);
316         } else if (command.equals("/resources")) {
317             resources(writer, type);
318         } else if (command.equals("/roles")) {
319             roles(writer);
320         } else if (command.equals("/serverinfo")) {
321             serverinfo(writer);
322         } else if (command.equals("/sessions")) {
323             sessions(writer, vhost, path);
324         } else if (command.equals("/start")) {
325             start(writer, vhost, path);
326         } else if (command.equals("/stop")) {
327             stop(writer, vhost, path);
328         } else if (command.equals("/undeploy")) {
329             undeploy(writer, vhost, path);
330         } else {
331             writer.println(sm.getString("managerServlet.unknownCommand",
332                                         command));
333         }
334 
335         // Finish up the response
336         writer.flush();
337         writer.close();
338 
339     }
340 
341 
342     /**
343      * Process a PUT request for the specified resource.
344      *
345      * @param request The servlet request we are processing
346      * @param response The servlet response we are creating
347      *
348      * @exception IOException if an input/output error occurs
349      * @exception ServletException if a servlet-specified error occurs
350      */
351     public void doPut(HttpServletRequest request,
352                       HttpServletResponse response)
353         throws IOException, ServletException {
354 
355         // Verify that we were not accessed using the invoker servlet
356         if (request.getAttribute(Globals.INVOKED_ATTR) != null)
357             throw new UnavailableException
358                 (sm.getString("managerServlet.cannotInvoke"));
359 
360         // Identify the request parameters that we need
361         String command = request.getPathInfo();
362         if (command == null)
363             command = request.getServletPath();
364         String vhost = request.getParameter("vhost");
365         String path = request.getParameter("path");
366 
367         // Prepare our output writer to generate the response message
368         response.setContentType("text/plain");
369         Locale locale = Locale.getDefault();
370         response.setLocale(locale);
371         PrintWriter writer = response.getWriter();
372 
373         // Process the requested command
374         if (command == null) {
375             writer.println(sm.getString("managerServlet.noCommand"));
376         } else if (command.equals("/deploy")) {
377             deploy(writer, vhost, path, request);
378         } else {
379             writer.println(sm.getString("managerServlet.unknownCommand",
380                                         command));
381         }
382 
383         // Saving configuration
384         Server server = ServerFactory.getServer();
385         if ((server != null) && (server instanceof StandardServer)) {
386             try {
387                 ((StandardServer) server).store();
388             } catch (Exception e) {
389                 writer.println(sm.getString("managerServlet.saveFail",
390                                             e.getMessage()));
391             }
392         }
393 
394         // Finish up the response
395         writer.flush();
396         writer.close();
397 
398     }
399 
400 
401     /**
402      * Initialize this servlet.
403      */
404     public void init() throws ServletException {
405 
406         // Ensure that our ContainerServlet properties have been set
407         if ((wrapper == null) || (context == null))
408             throw new UnavailableException
409                 (sm.getString("managerServlet.noWrapper"));
410 
411         // Verify that we were not accessed using the invoker servlet
412         String servletName = getServletConfig().getServletName();
413         if (servletName == null)
414             servletName = "";
415         if (servletName.startsWith("org.apache.catalina.INVOKER."))
416             throw new UnavailableException
417                 (sm.getString("managerServlet.cannotInvoke"));
418 
419         // Set our properties from the initialization parameters
420         String value = null;
421         try {
422             value = getServletConfig().getInitParameter("debug");
423             debug = Integer.parseInt(value);
424         } catch (Throwable t) {
425             ;
426         }
427 
428         // Acquire global JNDI resources if available
429         Server server = ServerFactory.getServer();
430         if ((server != null) && (server instanceof StandardServer)) {
431             global = ((StandardServer) server).getGlobalNamingContext();
432         }
433 
434         // Calculate the directory into which we will be deploying applications
435         deployed = (File) getServletContext().getAttribute
436             ("javax.servlet.context.tempdir");
437 
438         // Log debugging messages as necessary
439         if (debug >= 1) {
440             log("init: Associated with Engine '" +
441                 engine.getName() + "'");
442             if (global != null) {
443                 log("init: Global resources are available");
444             }
445         }
446 
447     }
448 
449 
450 
451     // -------------------------------------------------------- Private Methods
452 
453 
454     /**
455      * Render a list of the currently active vhosts.
456      *
457      * @param writer Writer to render to
458      */
459     protected void vhosts(PrintWriter writer) {
460 
461         if (debug >= 1)
462             log("list: Listing virtual hosts for engine '" +
463                 engine.getName() + "'");
464 
465         writer.println(sm.getString("managerServlet.vhosts",
466                                     engine.getName()));
467         
468         Container hosts[] = engine.findChildren();
469         for (int i = 0; i < hosts.length; i++) {            
470             writer.println(sm.getString("managerServlet.hostitem",
471                                         hosts[i].getName()));            
472         }
473         
474     }
475     
476     /**
477      * Deploy a web application archive (included in the current request)
478      * at the specified context path.
479      *
480      * @param writer Writer to render results to
481      * @param vhost Virtual host to be listed
482      * @param path Context path of the application to be installed
483      * @param request Servlet request we are processing
484      */
485     protected synchronized void deploy(PrintWriter writer, String vhost, String path,
486                                        HttpServletRequest request) {
487 
488         if (debug >= 1) {
489             log("deploy: Deploying web application at '" + vhost + ":" + path + "'");
490         }
491 
492         if(vhost == null || vhost.length() == 0) {
493             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
494             return;
495         }        
496 
497         Deployer deployer = (Deployer) engine.findChild(vhost);
498         if (deployer == null) {
499             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
500             return;                
501         }
502         
503         // Validate the requested context path
504         if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
505             writer.println(sm.getString("managerServlet.invalidPath", path));
506             return;
507         }
508         String displayPath = path;
509         if( path.equals("/") )
510             path = "";
511         String basename = null;
512         if (path.equals("")) {
513             basename = "_";
514         } else {
515             basename = path.substring(1);
516         }
517         if (deployer.findDeployedApp(path) != null) {
518             writer.println
519                 (sm.getString("managerServlet.alreadyContext", displayPath));
520             return;
521         }
522 
523         // Identify the appBase of the owning Host of this Context
524         // (if any)
525         String appBase = ((Host) context.getParent()).getAppBase();
526         File appBaseDir = new File(appBase);
527         if (!appBaseDir.isAbsolute()) {
528             appBaseDir = new File(System.getProperty("catalina.base"),
529                                   appBase);
530         }
531         File localWar = new File(appBaseDir, basename + ".war");
532         if (localWar.exists()) {
533             writer.println(sm.getString
534                         ("managerServlet.installUploadWarExists",localWar));
535             return;
536         }
537 
538         // Upload the web application archive to a local WAR file
539         File tempWar = new File(deployed, localWar.getName());
540         if (debug >= 2) {
541             log("Uploading WAR file to " + tempWar);
542         }
543         try {
544             uploadWar(request, tempWar);
545         } catch (IOException e) {
546             log("managerServlet.upload[" + displayPath + "]", e);
547             writer.println(sm.getString("managerServlet.exception",
548                                         e.toString()));
549             tempWar.delete();
550             return;
551         }
552 
553         // renameTo doc says it is supposed not to move the file in every platform
554         boolean moved = tempWar.renameTo(localWar);
555         if (!moved){
556             writer.println(sm.getString
557                         ("managerServlet.installUploadFail",localWar));
558             tempWar.delete();
559             return;
560         }
561 
562         // Extract the nested context deployment file (if any)
563         File localXml = new File(deployed, basename + ".xml");
564         if (debug >= 2) {
565             log("Extracting XML file to " + localXml);
566         }
567         try {
568             extractXml(localWar, localXml);
569         } catch (IOException e) {
570             log("managerServlet.extract[" + displayPath + "]", e);
571             writer.println(sm.getString("managerServlet.exception",
572                                         e.toString()));
573             return;
574         }
575 
576         // There is a possible race condition here. If liveDeploy is true it
577         // means the deployer could start to deploy the app before we do it.
578         synchronized(getLock()) {
579             // Deploy this web application
580             try {
581                 URL warURL =
582                     new URL("jar:file:" + localWar.getAbsolutePath() + "!/");
583                 URL xmlURL = null;
584                 if (localXml.exists()) {
585                     xmlURL = new URL("file:" + localXml.getAbsolutePath());
586                 }
587                 if (xmlURL != null) {
588                     deployer.install(xmlURL, warURL);
589                 } else {
590                     deployer.install(path, warURL);
591                 }
592             } catch (Throwable t) {
593                 log("ManagerServlet.deploy[" + displayPath + "]", t);
594                 writer.println(sm.getString("managerServlet.exception",
595                                             t.toString()));
596                 localWar.delete();
597                 localXml.delete();
598                 return;
599             }
600         }
601 
602         // Acknowledge successful completion of this deploy command
603         writer.println(sm.getString("managerServlet.installed",
604                                     displayPath));
605 
606     }
607 
608 
609     /**
610      * Install an application for the specified path from the specified
611      * web application archive.
612      *
613      * @param writer Writer to render results to
614      * @param vhost Virtual host of the application to be installed
615      * @param config URL of the context configuration file to be installed
616      * @param path Context path of the application to be installed
617      * @param war URL of the web application archive to be installed
618      */
619     protected void install(PrintWriter writer, String vhost, String config,
620                            String path, String war) {
621 
622         if(vhost == null || vhost.length() == 0) {
623             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
624             return;
625         }        
626 
627         Deployer deployer = (Deployer) engine.findChild(vhost);
628         if (deployer == null) {
629             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
630             return;                
631         }
632         
633         if (war != null && war.length() == 0) {
634             war = null;
635         }
636 
637         if (debug >= 1) {
638             if (config != null && config.length() > 0) {
639                 if (war != null) {
640                     log("install: Installing context configuration at '" +
641                         vhost + ":" + config + "' from '" + war + "'");
642                 } else {
643                     log("install: Installing context configuration at '" +
644                         vhost + ":" + config + "'");
645                 }
646             } else {
647                 log("install: Installing web application at '" + vhost + ":" + path +
648                     "' from '" + war + "'");
649             }
650         }
651 
652         // See if directory/war is relative to host appBase
653         if (war != null && war.indexOf('/') < 0 ) {
654             // Identify the appBase of the owning Host of this Context (if any)
655             String appBase = null;
656             File appBaseDir = null;
657             if (context.getParent() instanceof Host) {
658                 appBase = ((Host) context.getParent()).getAppBase();
659                 appBaseDir = new File(appBase);
660                 if (!appBaseDir.isAbsolute()) {
661                     appBaseDir = new File(System.getProperty("catalina.base"),
662                                           appBase);
663                 }
664                 File file = new File(appBaseDir,war);
665                 try {
666                     URL url = file.toURL();
667                     war = url.toString();
668                     if (war.toLowerCase().endsWith(".war")) {
669                         war = "jar:" + war + "!/";
670                     }
671                 } catch(MalformedURLException e) {
672                     ;
673                 }
674             }
675         }
676 
677         if (config != null && config.length() > 0) {
678 
679             if ((war != null) &&
680                 (!war.startsWith("file:") && !war.startsWith("jar:"))) {
681                 writer.println(sm.getString("managerServlet.invalidWar", war));
682                 return;
683             }
684 
685             try {
686                 if (war == null) {
687                     deployer.install(new URL(config), null);
688                 } else {
689                     deployer.install(new URL(config), new URL(war));
690                 }
691                 writer.println(sm.getString("managerServlet.configured",
692                                             config));
693             } catch (Throwable t) {
694                 log("ManagerServlet.configure[" + config + "]", t);
695                 writer.println(sm.getString("managerServlet.exception",
696                                             t.toString()));
697             }
698 
699         } else {
700 
701             if ((war == null) ||
702                 (!war.startsWith("file:") && !war.startsWith("jar:"))) {
703                 writer.println(sm.getString("managerServlet.invalidWar", war));
704                 return;
705             }
706 
707             if (path == null || path.length() == 0) {
708                 int end = war.length();
709                 String filename = war.toLowerCase();
710                 if (filename.endsWith("!/")) {
711                     filename = filename.substring(0,filename.length()-2);
712                     end -= 2;
713                 }
714                 if (filename.endsWith(".war")) {
715                     filename = filename.substring(0,filename.length()-4);
716                     end -= 4;
717                 }
718                 if (filename.endsWith("/")) {
719                     filename = filename.substring(0,filename.length()-1);
720                     end--;
721                 }
722                 int beg = filename.lastIndexOf('/') + 1;
723                 if (beg < 0 || end < 0 || beg >= end) {
724                     writer.println(sm.getString("managerServlet.invalidWar", war));
725                     return;
726                 }
727                 path = "/" + war.substring(beg,end);
728                 if (path.equals("/ROOT")) {
729                     path = "/";
730                 }
731             }
732 
733             if (path == null || path.length() == 0 || !path.startsWith("/")) {
734                 writer.println(sm.getString("managerServlet.invalidPath",
735                                             path));
736                 return;
737             }
738             String displayPath = path;
739             if("/".equals(path)) {
740                 path = "";
741             }
742 
743             try {
744                 Context context =  deployer.findDeployedApp(path);
745                 if (context != null) {
746                     writer.println
747                         (sm.getString("managerServlet.alreadyContext",
748                                       displayPath));
749                     return;
750                 }
751                 deployer.install(path, new URL(war));
752                 writer.println(sm.getString("managerServlet.installed",
753                                             displayPath));
754             } catch (Throwable t) {
755                 log("ManagerServlet.install[" + displayPath + "]", t);
756                 writer.println(sm.getString("managerServlet.exception",
757                                             t.toString()));
758             }
759 
760         }
761 
762     }
763 
764 
765     /**
766      * Render a list of the currently active Contexts in our virtual host.
767      *
768      * @param writer Writer to render to
769      * @param vhost Virtual host to be listed
770      */
771     protected void list(PrintWriter writer, String vhost) {
772 
773         if (debug >= 1)
774             log("list: Listing contexts for virtual host '" +
775                 vhost + "'");
776 
777         if(vhost == null || vhost.length() == 0) {
778             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
779             return;
780         }        
781 
782         Deployer deployer = (Deployer) engine.findChild(vhost);
783         if (deployer == null) {
784             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
785             return;                
786         }
787         
788         writer.println(sm.getString("managerServlet.listed",
789                                     deployer.getName()));
790         String contextPaths[] = deployer.findDeployedApps();
791         for (int i = 0; i < contextPaths.length; i++) {
792             Context context = deployer.findDeployedApp(contextPaths[i]);
793             String displayPath = contextPaths[i];
794             if( displayPath.equals("") )
795                 displayPath = "/";
796             if (context != null ) {
797                 if (context.getAvailable()) {
798                     writer.println(sm.getString("managerServlet.listitem",
799                                                 displayPath,
800                                                 "running",
801                                       "" + context.getManager().findSessions().length,
802                                                 context.getDocBase()));
803                 } else {
804                     writer.println(sm.getString("managerServlet.listitem",
805                                                 displayPath,
806                                                 "stopped",
807                                                 "0",
808                                                 context.getDocBase()));
809                 }
810             }
811         }
812     }
813 
814 
815     /**
816      * Reload the web application at the specified context path.
817      *
818      * @param writer Writer to render to
819      * @param vhost Virtual host to be listed
820      * @param path Context path of the application to be restarted
821      */
822     protected void reload(PrintWriter writer, String vhost, String path) {
823 
824         if (debug >= 1)
825             log("restart: Reloading web application at '" + vhost + ":" + path + "'");
826 
827         if(vhost == null || vhost.length() == 0) {
828             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
829             return;
830         }        
831 
832         Deployer deployer = (Deployer) engine.findChild(vhost);
833         if (deployer == null) {
834             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
835             return;                
836         }
837         
838         if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
839             writer.println(sm.getString("managerServlet.invalidPath", path));
840             return;
841         }
842         String displayPath = path;
843         if( path.equals("/") )
844             path = "";
845 
846         try {
847             Context context = deployer.findDeployedApp(path);
848             if (context == null) {
849                 writer.println(sm.getString("managerServlet.noContext", displayPath));
850             return;
851             }
852             DirContext resources = context.getResources();
853             if (resources instanceof ProxyDirContext) {
854                 resources = ((ProxyDirContext) resources).getDirContext();
855             }
856             if (resources instanceof WARDirContext) {
857                 writer.println(sm.getString("managerServlet.noReload", displayPath));
858                 return;
859             }
860             // It isn't possible for the manager to reload itself
861             if (context.getPath().equals(this.context.getPath())) {
862                 writer.println(sm.getString("managerServlet.noSelf"));
863                 return;
864             }
865             context.reload();
866             writer.println(sm.getString("managerServlet.reloaded", displayPath));
867         } catch (Throwable t) {
868             log("ManagerServlet.reload[" + displayPath + "]", t);
869             writer.println(sm.getString("managerServlet.exception",
870                                         t.toString()));
871         }
872 
873     }
874 
875 
876     /**
877      * Remove the web application at the specified context path.
878      *
879      * @param writer Writer to render to
880      * @param vhost Virtual host to be listed
881      * @param path Context path of the application to be removed
882      */
883     protected void remove(PrintWriter writer, String vhost, String path) {
884 
885         if (debug >= 1)
886             log("remove: Removing web application at '" + vhost + ":" + path + "'");
887 
888         if(vhost == null || vhost.length() == 0) {
889             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
890             return;
891         }        
892 
893         Deployer deployer = (Deployer) engine.findChild(vhost);
894         if (deployer == null) {
895             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
896             return;                
897         }
898         
899         if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
900             writer.println(sm.getString("managerServlet.invalidPath", path));
901             return;
902         }
903         String displayPath = path;
904         if( path.equals("/") )
905             path = "";
906 
907         try {
908             Context context = deployer.findDeployedApp(path);
909             if (context == null) {
910                 writer.println(sm.getString("managerServlet.noContext", displayPath));
911                 return;
912             }
913             // It isn't possible for the manager to remove itself
914             if (context.getPath().equals(this.context.getPath())) {
915                 writer.println(sm.getString("managerServlet.noSelf"));
916                 return;
917             }
918             deployer.remove(path,true);
919             writer.println(sm.getString("managerServlet.removed", displayPath));
920         } catch (Throwable t) {
921             log("ManagerServlet.remove[" + displayPath + "]", t);
922             writer.println(sm.getString("managerServlet.exception",
923                                         t.toString()));
924         }
925 
926     }
927 
928 
929     /**
930      * Render a list of available global JNDI resources.
931      *
932      * @param type Fully qualified class name of the resource type of interest,
933      *  or <code>null</code> to list resources of all types
934      */
935     protected void resources(PrintWriter writer, String type) {
936 
937         if (debug >= 1) {
938             if (type != null) {
939                 log("resources:  Listing resources of type " + type);
940             } else {
941                 log("resources:  Listing resources of all types");
942             }
943         }
944 
945         // Is the global JNDI resources context available?
946         if (global == null) {
947             writer.println(sm.getString("managerServlet.noGlobal"));
948             return;
949         }
950 
951         // Enumerate the global JNDI resources of the requested type
952         if (type != null) {
953             writer.println(sm.getString("managerServlet.resourcesType",
954                                         type));
955         } else {
956             writer.println(sm.getString("managerServlet.resourcesAll"));
957         }
958 
959         Class clazz = null;
960         try {
961             if (type != null) {
962                 clazz = Class.forName(type);
963             }
964         } catch (Throwable t) {
965             log("ManagerServlet.resources[" + type + "]", t);
966             writer.println(sm.getString("managerServlet.exception",
967                                         t.toString()));
968             return;
969         }
970 
971         printResources(writer, "", global, type, clazz);
972 
973     }
974 
975 
976     /**
977      * List the resources of the given context.
978      */
979     protected void printResources(PrintWriter writer, String prefix,
980                                   javax.naming.Context namingContext,
981                                   String type, Class clazz) {
982 
983         try {
984             NamingEnumeration items = namingContext.listBindings("");
985             while (items.hasMore()) {
986                 Binding item = (Binding) items.next();
987                 if (item.getObject() instanceof javax.naming.Context) {
988                     printResources
989                         (writer, prefix + item.getName() + "/",
990                          (javax.naming.Context) item.getObject(), type, clazz);
991                 } else {
992                     if ((clazz != null) &&
993                         (!(clazz.isInstance(item.getObject())))) {
994                         continue;
995                     }
996                     writer.print(prefix + item.getName());
997                     writer.print(':');
998                     writer.print(item.getClassName());
999                     // Do we want a description if available?
1000                     writer.println();
1001                 }
1002             }
1003         } catch (Throwable t) {
1004             log("ManagerServlet.resources[" + type + "]", t);
1005             writer.println(sm.getString("managerServlet.exception",
1006                                         t.toString()));
1007         }
1008 
1009     }
1010 
1011 
1012     /**
1013      * Render a list of security role names (and corresponding descriptions)
1014      * from the <code>org.apache.catalina.UserDatabase</code> resource that is
1015      * connected to the <code>users</code> resource reference.  Typically, this
1016      * will be the global user database, but can be adjusted if you have
1017      * different user databases for different virtual hosts.
1018      *
1019      * @param writer Writer to render to
1020      */
1021     protected void roles(PrintWriter writer) {
1022 
1023         if (debug >= 1) {
1024             log("roles:  List security roles from user database");
1025         }
1026 
1027         // Look up the UserDatabase instance we should use
1028         UserDatabase database = null;
1029         try {
1030             InitialContext ic = new InitialContext();
1031             database = (UserDatabase) ic.lookup("java:comp/env/users");
1032         } catch (NamingException e) {
1033             writer.println(sm.getString("managerServlet.userDatabaseError"));
1034             log("java:comp/env/users", e);
1035             return;
1036         }
1037         if (database == null) {
1038             writer.println(sm.getString("managerServlet.userDatabaseMissing"));
1039             return;
1040         }
1041 
1042         // Enumerate the available roles
1043         writer.println(sm.getString("managerServlet.rolesList"));
1044         Iterator roles = database.getRoles();
1045         if (roles != null) {
1046             while (roles.hasNext()) {
1047                 Role role = (Role) roles.next();
1048                 writer.print(role.getRolename());
1049                 writer.print(':');
1050                 if (role.getDescription() != null) {
1051                     writer.print(role.getDescription());
1052                 }
1053                 writer.println();
1054             }
1055         }
1056 
1057 
1058     }
1059 
1060 
1061     /**
1062      * Writes System OS and JVM properties.
1063      * @param writer Writer to render to
1064      */
1065     protected void serverinfo(PrintWriter writer) {
1066         if (debug >= 1)
1067             log("serverinfo");
1068         try {
1069             StringBuffer props = new StringBuffer();
1070             props.append("Tomcat Version: ");
1071             props.append(ServerInfo.getServerInfo());
1072             props.append("\nOS Name: ");
1073             props.append(System.getProperty("os.name"));
1074             props.append("\nOS Version: ");
1075             props.append(System.getProperty("os.version"));
1076             props.append("\nOS Architecture: ");
1077             props.append(System.getProperty("os.arch"));
1078             props.append("\nJVM Version: ");
1079             props.append(System.getProperty("java.runtime.version"));
1080             props.append("\nJVM Vendor: ");
1081             props.append(System.getProperty("java.vm.vendor"));
1082             writer.println(props.toString());
1083         } catch (Throwable t) {
1084             getServletContext().log("ManagerServlet.serverinfo",t);
1085             writer.println(sm.getString("managerServlet.exception",
1086                                         t.toString()));
1087         }
1088     }
1089 
1090     /**
1091      * Session information for the web application at the specified context path.
1092      * Displays a profile of session MaxInactiveInterval timeouts listing number
1093      * of sessions for each 10 minute timeout interval up to 10 hours.
1094      *
1095      * @param writer Writer to render to
1096      * @param vhost Virtual host to be listed
1097      * @param path Context path of the application to list session information for
1098      */
1099     protected void sessions(PrintWriter writer, String vhost, String path) {
1100 
1101         if (debug >= 1)
1102             log("sessions: Session information for web application at '" + vhost + ":" + path + "'");
1103 
1104         if(vhost == null || vhost.length() == 0) {
1105             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1106             return;
1107         }        
1108 
1109         Deployer deployer = (Deployer) engine.findChild(vhost);
1110         if (deployer == null) {
1111             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1112             return;                
1113         }
1114         
1115         if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1116             writer.println(sm.getString("managerServlet.invalidPath", path));
1117             return;
1118         }
1119         String displayPath = path;
1120         if( path.equals("/") )
1121             path = "";
1122         try {
1123             Context context = deployer.findDeployedApp(path);
1124             if (context == null) {
1125                 writer.println(sm.getString("managerServlet.noContext", displayPath));
1126                 return;
1127             }
1128             writer.println(sm.getString("managerServlet.sessions", displayPath));
1129             writer.println(sm.getString("managerServlet.sessiondefaultmax",
1130                                 "" + context.getManager().getMaxInactiveInterval()/60));
1131             Session [] sessions = context.getManager().findSessions();
1132             int [] timeout = new int[60];
1133             int notimeout = 0;
1134             for (int i = 0; i < sessions.length; i++) {
1135                 int time = sessions[i].getMaxInactiveInterval()/(10*60);
1136                 if (time < 0)
1137                     notimeout++;
1138                 else if (time >= timeout.length)
1139                     timeout[timeout.length-1]++;
1140                 else
1141                     timeout[time]++;
1142             }
1143             if (timeout[0] > 0)
1144                 writer.println(sm.getString("managerServlet.sessiontimeout",
1145                                             "<10",  "" + timeout[0]));
1146             for (int i = 1; i < timeout.length-1; i++) {
1147                 if (timeout[i] > 0)
1148                     writer.println(sm.getString("managerServlet.sessiontimeout",
1149                                      "" + (i)*10 + " - <" + (i+1)*10,
1150                                                 "" + timeout[i]));
1151             }
1152             if (timeout[timeout.length-1] > 0)
1153                 writer.println(sm.getString("managerServlet.sessiontimeout",
1154                                             ">=" + timeout.length*10,
1155                                             "" + timeout[timeout.length-1]));
1156             if (notimeout > 0)
1157                 writer.println(sm.getString("managerServlet.sessiontimeout",
1158                                             "unlimited","" + notimeout));
1159         } catch (Throwable t) {
1160             log("ManagerServlet.sessions[" + displayPath + "]", t);
1161             writer.println(sm.getString("managerServlet.exception",
1162                                         t.toString()));
1163         }
1164 
1165     }
1166 
1167     /**
1168      * Start the web application at the specified context path.
1169      *
1170      * @param writer Writer to render to
1171      * @param vhost Virtual host to be listed
1172      * @param path Context path of the application to be started
1173      */
1174     protected void start(PrintWriter writer, String vhost, String path) {
1175 
1176         if (debug >= 1)
1177             log("start: Starting web application at '" + vhost + ":" + path + "'");
1178 
1179         if(vhost == null || vhost.length() == 0) {
1180             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1181             return;
1182         }        
1183 
1184         Deployer deployer = (Deployer) engine.findChild(vhost);
1185         if (deployer == null) {
1186             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1187             return;                
1188         }
1189         
1190         if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1191             writer.println(sm.getString("managerServlet.invalidPath", path));
1192             return;
1193         }
1194         String displayPath = path;
1195         if( path.equals("/") )
1196             path = "";
1197 
1198         try {
1199             Context context = deployer.findDeployedApp(path);
1200             if (context == null) {
1201                 writer.println(sm.getString("managerServlet.noContext", displayPath));
1202                 return;
1203             }
1204             deployer.start(path);
1205             if (context.getAvailable())
1206                 writer.println
1207                     (sm.getString("managerServlet.started", displayPath));
1208             else
1209                 writer.println
1210                     (sm.getString("managerServlet.startFailed", displayPath));
1211         } catch (Throwable t) {
1212             getServletContext().log
1213                 (sm.getString("managerServlet.startFailed", displayPath), t);
1214             writer.println
1215                 (sm.getString("managerServlet.startFailed", displayPath));
1216             writer.println(sm.getString("managerServlet.exception",
1217                                         t.toString()));
1218         }
1219 
1220     }
1221 
1222 
1223     /**
1224      * Stop the web application at the specified context path.
1225      *
1226      * @param writer Writer to render to
1227      * @param vhost Virtual host to be listed
1228      * @param path Context path of the application to be stopped
1229      */
1230     protected void stop(PrintWriter writer, String vhost, String path) {
1231 
1232         if (debug >= 1)
1233             log("stop: Stopping web application at '" + vhost + ":" + path + "'");
1234 
1235         if(vhost == null || vhost.length() == 0) {
1236             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1237             return;
1238         }        
1239 
1240         Deployer deployer = (Deployer) engine.findChild(vhost);
1241         if (deployer == null) {
1242             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1243             return;                
1244         }
1245         
1246         if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1247             writer.println(sm.getString("managerServlet.invalidPath", path));
1248             return;
1249         }
1250         String displayPath = path;
1251         if( path.equals("/") )
1252             path = "";
1253 
1254         try {
1255             Context context = deployer.findDeployedApp(path);
1256             if (context == null) {
1257                 writer.println(sm.getString("managerServlet.noContext", displayPath));
1258                 return;
1259             }
1260             // It isn't possible for the manager to stop itself
1261             if (context.getPath().equals(this.context.getPath())) {
1262                 writer.println(sm.getString("managerServlet.noSelf"));
1263                 return;
1264             }
1265             deployer.stop(path);
1266             writer.println(sm.getString("managerServlet.stopped", displayPath));
1267         } catch (Throwable t) {
1268             log("ManagerServlet.stop[" + displayPath + "]", t);
1269             writer.println(sm.getString("managerServlet.exception",
1270                                         t.toString()));
1271         }
1272 
1273     }
1274 
1275 
1276     /**
1277      * Undeploy the web application at the specified context path.
1278      *
1279      * @param writer Writer to render to
1280      * @param vhost Virtual host to be listed
1281      * @param path Context path of the application to be removed
1282      */
1283     protected void undeploy(PrintWriter writer, String vhost, String path) {
1284 
1285         if (debug >= 1)
1286             log("undeploy: Undeploying web application at '" + vhost + ":" + path + "'");
1287 
1288         if(vhost == null || vhost.length() == 0) {
1289             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1290             return;
1291         }        
1292 
1293         Deployer deployer = (Deployer) engine.findChild(vhost);
1294         if (deployer == null) {
1295             writer.println(sm.getString("managerServlet.invalidVirtualHost", vhost));
1296             return;                
1297         }
1298         
1299         if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
1300             writer.println(sm.getString("managerServlet.invalidPath", path));
1301             return;
1302         }
1303         String displayPath = path;
1304         if( path.equals("/") )
1305             path = "";
1306 
1307         try {
1308 
1309             // Validate the Context of the specified application
1310             Context context = deployer.findDeployedApp(path);
1311             if (context == null) {
1312                 writer.println(sm.getString("managerServlet.noContext",
1313                                             displayPath));
1314                 return;
1315             }
1316 
1317             // Identify the appBase of the owning Host of this Context (if any)
1318             String appBase = null;
1319             File appBaseDir = null;
1320             if (context.getParent() instanceof Host) {
1321                 appBase = ((Host) context.getParent()).getAppBase();
1322                 appBaseDir = new File(appBase);
1323                 if (!appBaseDir.isAbsolute()) {
1324                     appBaseDir = new File(System.getProperty("catalina.base"),
1325                                           appBase);
1326                 }
1327             }
1328 
1329             // Validate the docBase path of this application
1330             String docBase = context.getDocBase();
1331             File docBaseDir = new File(docBase);
1332             if (!docBaseDir.isAbsolute()) {
1333                 docBaseDir = new File(appBaseDir, docBase);
1334             }
1335             String docBasePath = docBaseDir.getCanonicalPath();
1336             if (!docBasePath.startsWith(appBaseDir.getCanonicalPath())) {
1337                 writer.println(sm.getString("managerServlet.noDocBase",
1338                                             displayPath));
1339                 return;
1340             }
1341 
1342             // Remove this web application and its associated docBase
1343             if (debug >= 2) {
1344                 log("Undeploying document base " + docBasePath);
1345             }
1346             // It isn't possible for the manager to undeploy itself
1347             if (context.getPath().equals(this.context.getPath())) {
1348                 writer.println(sm.getString("managerServlet.noSelf"));
1349                 return;
1350             }
1351             deployer.remove(path);
1352             
1353             String docBaseWarPath = null;
1354             
1355             // Delete the directory if there is one
1356             if (docBaseDir.isDirectory()) {
1357                 undeployDir(docBaseDir);
1358                 docBaseWarPath = docBaseDir.getCanonicalPath() + ".war";
1359             } else {
1360                 docBaseWarPath = docBaseDir.getCanonicalPath();
1361             }
1362             
1363             // Delete the WAR file if there is one
1364             File docBaseWar = new File(docBaseWarPath);
1365             docBaseWar.delete();
1366             
1367             // Delete the context xml file if there is one
1368             String docBaseXmlPath =
1369                 docBaseWarPath.substring(0, docBaseWarPath.length() - 4) + ".xml";
1370             File docBaseXml = new File(docBaseXmlPath);
1371             docBaseXml.delete();
1372             
1373             writer.println(sm.getString("managerServlet.undeployed",
1374                                         displayPath));
1375 
1376         } catch (Throwable t) {
1377             log("ManagerServlet.undeploy[" + displayPath + "]", t);
1378             writer.println(sm.getString("managerServlet.exception",
1379                                         t.toString()));
1380         }
1381 
1382         // Saving configuration
1383         Server server = ServerFactory.getServer();
1384         if ((server != null) && (server instanceof StandardServer)) {
1385             try {
1386                 ((StandardServer) server).store();
1387             } catch (Exception e) {
1388                 writer.println(sm.getString("managerServlet.saveFail",
1389                                             e.getMessage()));
1390             }
1391         }
1392 
1393     }
1394 
1395 
1396     // -------------------------------------------------------- Support Methods
1397 
1398 
1399     /**
1400      * Extract the context configuration file from the specified WAR,
1401      * if it is present.  If it is not present, ensure that the corresponding
1402      * file does not exist.
1403      *
1404      * @param war File object representing the WAR
1405      * @param xml File object representing where to store the extracted
1406      *  context configuration file (if it exists)
1407      *
1408      * @exception IOException if an i/o error occurs
1409      */
1410     protected void extractXml(File war, File xml) throws IOException {
1411 
1412         xml.delete();
1413         JarFile jar = null;
1414         JarEntry entry = null;
1415         InputStream istream = null;
1416         BufferedOutputStream ostream = null;
1417         try {
1418             jar = new JarFile(war);
1419             entry = jar.getJarEntry("META-INF/context.xml");
1420             if (entry == null) {
1421                 return;
1422             }
1423             istream = jar.getInputStream(entry);
1424             ostream =
1425                 new BufferedOutputStream(new FileOutputStream(xml), 1024);
1426             byte buffer[] = new byte[1024];
1427             while (true) {
1428                 int n = istream.read(buffer);
1429                 if (n < 0) {
1430                     break;
1431                 }
1432                 ostream.write(buffer, 0, n);
1433             }
1434             ostream.flush();
1435             ostream.close();
1436             ostream = null;
1437             istream.close();
1438             istream = null;
1439             entry = null;
1440             jar.close();
1441             jar = null;
1442         } catch (IOException e) {
1443             xml.delete();
1444             throw e;
1445         } finally {
1446             if (ostream != null) {
1447                 try {
1448                     ostream.close();
1449                 } catch (Throwable t) {
1450                     ;
1451                 }
1452                 ostream = null;
1453             }
1454             if (istream != null) {
1455                 try {
1456                     istream.close();
1457                 } catch (Throwable t) {
1458                     ;
1459                 }
1460                 istream = null;
1461             }
1462             entry = null;
1463             if (jar != null) {
1464                 try {
1465                     jar.close();
1466                 } catch (Throwable t) {
1467                     ;
1468                 }
1469                 jar = null;
1470             }
1471         }
1472 
1473     }
1474 
1475 
1476     /**
1477      * Delete the specified directory, including all of its contents and
1478      * subdirectories recursively.
1479      *
1480      * @param dir File object representing the directory to be deleted
1481      */
1482     protected void undeployDir(File dir) {
1483 
1484         String files[] = dir.list();
1485         if (files == null) {
1486             files = new String[0];
1487         }
1488         for (int i = 0; i < files.length; i++) {
1489             File file = new File(dir, files[i]);
1490             if (file.isDirectory()) {
1491                 undeployDir(file);
1492             } else {
1493                 file.delete();
1494             }
1495         }
1496         dir.delete();
1497 
1498     }
1499 
1500 
1501     /**
1502      * Upload the WAR file included in this request, and store it at the
1503      * specified file location.
1504      *
1505      * @param request The servlet request we are processing
1506      * @param war The file into which we should store the uploaded WAR
1507      *
1508      * @exception IOException if an I/O error occurs during processing
1509      */
1510     protected void uploadWar(HttpServletRequest request, File war)
1511         throws IOException {
1512 
1513         war.delete();
1514         ServletInputStream istream = null;
1515         BufferedOutputStream ostream = null;
1516         try {
1517             istream = request.getInputStream();
1518             ostream =
1519                 new BufferedOutputStream(new FileOutputStream(war), 1024);
1520             byte buffer[] = new byte[1024];
1521             while (true) {
1522                 int n = istream.read(buffer);
1523                 if (n < 0) {
1524                     break;
1525                 }
1526                 ostream.write(buffer, 0, n);
1527             }
1528             ostream.flush();
1529             ostream.close();
1530             ostream = null;
1531             istream.close();
1532             istream = null;
1533         } catch (IOException e) {
1534             war.delete();
1535             throw e;
1536         } finally {
1537             if (ostream != null) {
1538                 try {
1539                     ostream.close();
1540                 } catch (Throwable t) {
1541                     ;
1542                 }
1543                 ostream = null;
1544             }
1545             if (istream != null) {
1546                 try {
1547                     istream.close();
1548                 } catch (Throwable t) {
1549                     ;
1550                 }
1551                 istream = null;
1552             }
1553         }
1554 
1555     }
1556 
1557     /**
1558      * Obtain an object to lock on to prevent this servlet and the HostConfig
1559      * thread both trying to deploy the same application.
1560      * 
1561      * @return An appropriate object to use for the lock
1562      */
1563     protected Object getLock() {
1564         Object lock = null;
1565 
1566         // Parent of context is host. Hosts have HostConfig lifecyclelisteners
1567         Container parent = context.getParent();
1568         if (parent instanceof Lifecycle) {
1569             LifecycleListener[] listeners =
1570                 ((Lifecycle) parent).findLifecycleListeners();
1571 
1572             for (int i=0; i<listeners.length; i++) {
1573                 if (listeners[i] instanceof HostConfig) {
1574                     lock = listeners[i];
1575                 }
1576             }
1577         }
1578         
1579         // In case no HostConfig is found, can't sync on null
1580         if (lock == null) {
1581             lock = new Object();
1582         }
1583         
1584         return lock;
1585     }
1586 
1587 }