0001: /*
0002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/servlets/ManagerServlet.java,v 1.24 2002/05/31 21:08:03 remm Exp $
0003: * $Revision: 1.24 $
0004: * $Date: 2002/05/31 21:08:03 $
0005: *
0006: * ====================================================================
0007: *
0008: * The Apache Software License, Version 1.1
0009: *
0010: * Copyright (c) 1999 The Apache Software Foundation. All rights
0011: * reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. Redistributions in binary form must reproduce the above copyright
0021: * notice, this list of conditions and the following disclaimer in
0022: * the documentation and/or other materials provided with the
0023: * distribution.
0024: *
0025: * 3. The end-user documentation included with the redistribution, if
0026: * any, must include the following acknowlegement:
0027: * "This product includes software developed by the
0028: * Apache Software Foundation (http://www.apache.org/)."
0029: * Alternately, this acknowlegement may appear in the software itself,
0030: * if and wherever such third-party acknowlegements normally appear.
0031: *
0032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0033: * Foundation" must not be used to endorse or promote products derived
0034: * from this software without prior written permission. For written
0035: * permission, please contact apache@apache.org.
0036: *
0037: * 5. Products derived from this software may not be called "Apache"
0038: * nor may "Apache" appear in their names without prior written
0039: * permission of the Apache Group.
0040: *
0041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0052: * SUCH DAMAGE.
0053: * ====================================================================
0054: *
0055: * This software consists of voluntary contributions made by many
0056: * individuals on behalf of the Apache Software Foundation. For more
0057: * information on the Apache Software Foundation, please see
0058: * <http://www.apache.org/>.
0059: *
0060: * [Additional notices, if required by prior licensing conditions]
0061: *
0062: */
0063:
0064: package org.apache.catalina.servlets;
0065:
0066: import java.io.BufferedOutputStream;
0067: import java.io.File;
0068: import java.io.FileOutputStream;
0069: import java.io.InputStream;
0070: import java.io.IOException;
0071: import java.io.PrintWriter;
0072: import java.net.URL;
0073: import java.util.Enumeration;
0074: import java.util.Iterator;
0075: import java.util.jar.JarEntry;
0076: import java.util.jar.JarFile;
0077: import javax.naming.Binding;
0078: import javax.naming.InitialContext;
0079: import javax.naming.NamingEnumeration;
0080: import javax.naming.NamingException;
0081: import javax.naming.directory.DirContext;
0082: import javax.servlet.ServletException;
0083: import javax.servlet.ServletInputStream;
0084: import javax.servlet.UnavailableException;
0085: import javax.servlet.http.HttpServlet;
0086: import javax.servlet.http.HttpServletRequest;
0087: import javax.servlet.http.HttpServletResponse;
0088: import org.apache.catalina.Container;
0089: import org.apache.catalina.ContainerServlet;
0090: import org.apache.catalina.Context;
0091: import org.apache.catalina.Deployer;
0092: import org.apache.catalina.Globals;
0093: import org.apache.catalina.Host;
0094: import org.apache.catalina.Role;
0095: import org.apache.catalina.Server;
0096: import org.apache.catalina.ServerFactory;
0097: import org.apache.catalina.Session;
0098: import org.apache.catalina.UserDatabase;
0099: import org.apache.catalina.Wrapper;
0100: import org.apache.catalina.core.StandardServer;
0101: import org.apache.catalina.util.StringManager;
0102: import org.apache.naming.resources.ProxyDirContext;
0103: import org.apache.naming.resources.WARDirContext;
0104:
0105: /**
0106: * Servlet that enables remote management of the web applications installed
0107: * within the same virtual host as this web application is. Normally, this
0108: * functionality will be protected by a security constraint in the web
0109: * application deployment descriptor. However, this requirement can be
0110: * relaxed during testing.
0111: * <p>
0112: * This servlet examines the value returned by <code>getPathInfo()</code>
0113: * and related query parameters to determine what action is being requested.
0114: * The following actions and parameters (starting after the servlet path)
0115: * are supported:
0116: * <ul>
0117: * <li><b>/install?config={config-url}</b> - Install and start a new
0118: * web application, based on the contents of the context configuration
0119: * file found at the specified URL. The <code>docBase</code> attribute
0120: * of the context configuration file is used to locate the actual
0121: * WAR or directory containing the application.</li>
0122: * <li><b>/install?config={config-url}&war={war-url}/</b> - Install and start
0123: * a new web application, based on the contents of the context
0124: * configuration file found at <code>{config-url}</code>, overriding the
0125: * <code>docBase</code> attribute with the contents of the web
0126: * application archive found at <code>{war-url}</code>.</li>
0127: * <li><b>/install?path=/xxx&war={war-url}</b> - Install and start a new
0128: * web application attached to context path <code>/xxx</code>, based
0129: * on the contents of the web application archive found at the
0130: * specified URL.</li>
0131: * <li><b>/list</b> - List the context paths of all currently installed web
0132: * applications for this virtual host. Each context will be listed with
0133: * the following format <code>path:status:sessions</code>.
0134: * Where path is the context path. Status is either running or stopped.
0135: * Sessions is the number of active Sessions.</li>
0136: * <li><b>/reload?path=/xxx</b> - Reload the Java classes and resources for
0137: * the application at the specified path, but do not reread the web.xml
0138: * configuration files.</li>
0139: * <li><b>/remove?path=/xxx</b> - Shutdown and remove the web application
0140: * attached to context path <code>/xxx</code> for this virtual host.</li>
0141: * <li><b>/resources?type=xxxx</b> - Enumerate the available global JNDI
0142: * resources, optionally limited to those of the specified type
0143: * (fully qualified Java class name), if available.</li>
0144: * <li><b>/roles</b> - Enumerate the available security role names and
0145: * descriptions from the user database connected to the <code>users</code>
0146: * resource reference.
0147: * <li><b>/sessions?path=/xxx</b> - List session information about the web
0148: * application attached to context path <code>/xxx</code> for this
0149: * virtual host.</li>
0150: * <li><b>/start?path=/xxx</b> - Start the web application attached to
0151: * context path <code>/xxx</code> for this virtual host.</li>
0152: * <li><b>/stop?path=/xxx</b> - Stop the web application attached to
0153: * context path <code>/xxx</code> for this virtual host.</li>
0154: * <li><b>/undeploy?path=/xxx</b> - Shutdown and remove the web application
0155: * attached to context path <code>/xxx</code> for this virtual host,
0156: * and remove the underlying WAR file or document base directory.
0157: * (<em>NOTE</em> - This is only allowed if the WAR file or document
0158: * base is stored in the <code>appBase</code> directory of this host,
0159: * typically as a result of being placed there via the <code>/deploy</code>
0160: * command.</li>
0161: * </ul>
0162: * <p>Use <code>path=/</code> for the ROOT context.</p>
0163: * <p>The syntax of the URL for a web application archive must conform to one
0164: * of the following patterns to be successfully deployed:</p>
0165: * <ul>
0166: * <li><b>file:/absolute/path/to/a/directory</b> - You can specify the absolute
0167: * path of a directory that contains the unpacked version of a web
0168: * application. This directory will be attached to the context path you
0169: * specify without any changes.</li>
0170: * <li><b>jar:file:/absolute/path/to/a/warfile.war!/</b> - You can specify a
0171: * URL to a local web application archive file. The syntax must conform to
0172: * the rules specified by the <code>JarURLConnection</code> class for a
0173: * reference to an entire JAR file.</li>
0174: * <li><b>jar:http://hostname:port/path/to/a/warfile.war!/</b> - You can specify
0175: * a URL to a remote (HTTP-accessible) web application archive file. The
0176: * syntax must conform to the rules specified by the
0177: * <code>JarURLConnection</code> class for a reference to an entire
0178: * JAR file.</li>
0179: * </ul>
0180: * <p>
0181: * <b>NOTE</b> - Attempting to reload or remove the application containing
0182: * this servlet itself will not succeed. Therefore, this servlet should
0183: * generally be deployed as a separate web application within the virtual host
0184: * to be managed.
0185: * <p>
0186: * <b>NOTE</b> - For security reasons, this application will not operate
0187: * when accessed via the invoker servlet. You must explicitly map this servlet
0188: * with a servlet mapping, and you will always want to protect it with
0189: * appropriate security constraints as well.
0190: * <p>
0191: * The following servlet initialization parameters are recognized:
0192: * <ul>
0193: * <li><b>debug</b> - The debugging detail level that controls the amount
0194: * of information that is logged by this servlet. Default is zero.
0195: * </ul>
0196: *
0197: * @author Craig R. McClanahan
0198: * @version $Revision: 1.24 $ $Date: 2002/05/31 21:08:03 $
0199: */
0200:
0201: public class ManagerServlet extends HttpServlet implements
0202: ContainerServlet {
0203:
0204: // ----------------------------------------------------- Instance Variables
0205:
0206: /**
0207: * The Context container associated with our web application.
0208: */
0209: protected Context context = null;
0210:
0211: /**
0212: * The debugging detail level for this servlet.
0213: */
0214: protected int debug = 1;
0215:
0216: /**
0217: * File object representing the directory into which the deploy() command
0218: * will store the WAR and context configuration files that have been
0219: * uploaded.
0220: */
0221: protected File deployed = null;
0222:
0223: /**
0224: * The Deployer container that contains our own web application's Context,
0225: * along with the associated Contexts for web applications that we
0226: * are managing.
0227: */
0228: protected Deployer deployer = null;
0229:
0230: /**
0231: * The global JNDI <code>NamingContext</code> for this server,
0232: * if available.
0233: */
0234: protected javax.naming.Context global = null;
0235:
0236: /**
0237: * The string manager for this package.
0238: */
0239: protected static StringManager sm = StringManager
0240: .getManager(Constants.Package);
0241:
0242: /**
0243: * The Wrapper container associated with this servlet.
0244: */
0245: protected Wrapper wrapper = null;
0246:
0247: // ----------------------------------------------- ContainerServlet Methods
0248:
0249: /**
0250: * Return the Wrapper with which we are associated.
0251: */
0252: public Wrapper getWrapper() {
0253:
0254: return (this .wrapper);
0255:
0256: }
0257:
0258: /**
0259: * Set the Wrapper with which we are associated.
0260: *
0261: * @param wrapper The new wrapper
0262: */
0263: public void setWrapper(Wrapper wrapper) {
0264:
0265: this .wrapper = wrapper;
0266: if (wrapper == null) {
0267: context = null;
0268: deployer = null;
0269: } else {
0270: context = (Context) wrapper.getParent();
0271: deployer = (Deployer) context.getParent();
0272: }
0273:
0274: }
0275:
0276: // --------------------------------------------------------- Public Methods
0277:
0278: /**
0279: * Finalize this servlet.
0280: */
0281: public void destroy() {
0282:
0283: ; // No actions necessary
0284:
0285: }
0286:
0287: /**
0288: * Process a GET request for the specified resource.
0289: *
0290: * @param request The servlet request we are processing
0291: * @param response The servlet response we are creating
0292: *
0293: * @exception IOException if an input/output error occurs
0294: * @exception ServletException if a servlet-specified error occurs
0295: */
0296: public void doGet(HttpServletRequest request,
0297: HttpServletResponse response) throws IOException,
0298: ServletException {
0299:
0300: // Verify that we were not accessed using the invoker servlet
0301: if (request.getAttribute(Globals.INVOKED_ATTR) != null)
0302: throw new UnavailableException(sm
0303: .getString("managerServlet.cannotInvoke"));
0304:
0305: // Identify the request parameters that we need
0306: String command = request.getPathInfo();
0307: if (command == null)
0308: command = request.getServletPath();
0309: String config = request.getParameter("config");
0310: String path = request.getParameter("path");
0311: String type = request.getParameter("type");
0312: String war = request.getParameter("war");
0313:
0314: // Prepare our output writer to generate the response message
0315: response.setContentType("text/plain");
0316: PrintWriter writer = response.getWriter();
0317:
0318: // Process the requested command (note - "/deploy" is not listed here)
0319: if (command == null) {
0320: writer.println(sm.getString("managerServlet.noCommand"));
0321: } else if (command.equals("/install")) {
0322: install(writer, config, path, war);
0323: } else if (command.equals("/list")) {
0324: list(writer);
0325: } else if (command.equals("/reload")) {
0326: reload(writer, path);
0327: } else if (command.equals("/remove")) {
0328: remove(writer, path);
0329: } else if (command.equals("/resources")) {
0330: resources(writer, type);
0331: } else if (command.equals("/roles")) {
0332: roles(writer);
0333: } else if (command.equals("/sessions")) {
0334: sessions(writer, path);
0335: } else if (command.equals("/start")) {
0336: start(writer, path);
0337: } else if (command.equals("/stop")) {
0338: stop(writer, path);
0339: } else if (command.equals("/undeploy")) {
0340: undeploy(writer, path);
0341: } else {
0342: writer.println(sm.getString(
0343: "managerServlet.unknownCommand", command));
0344: }
0345:
0346: // Finish up the response
0347: writer.flush();
0348: writer.close();
0349:
0350: }
0351:
0352: /**
0353: * Process a PUT request for the specified resource.
0354: *
0355: * @param request The servlet request we are processing
0356: * @param response The servlet response we are creating
0357: *
0358: * @exception IOException if an input/output error occurs
0359: * @exception ServletException if a servlet-specified error occurs
0360: */
0361: public void doPut(HttpServletRequest request,
0362: HttpServletResponse response) throws IOException,
0363: ServletException {
0364:
0365: // Verify that we were not accessed using the invoker servlet
0366: if (request.getAttribute(Globals.INVOKED_ATTR) != null)
0367: throw new UnavailableException(sm
0368: .getString("managerServlet.cannotInvoke"));
0369:
0370: // Identify the request parameters that we need
0371: String command = request.getPathInfo();
0372: if (command == null)
0373: command = request.getServletPath();
0374: String path = request.getParameter("path");
0375:
0376: // Prepare our output writer to generate the response message
0377: response.setContentType("text/plain");
0378: PrintWriter writer = response.getWriter();
0379:
0380: // Process the requested command
0381: if (command == null) {
0382: writer.println(sm.getString("managerServlet.noCommand"));
0383: } else if (command.equals("/deploy")) {
0384: deploy(writer, path, request);
0385: } else {
0386: writer.println(sm.getString(
0387: "managerServlet.unknownCommand", command));
0388: }
0389:
0390: // Saving configuration
0391: Server server = ServerFactory.getServer();
0392: if ((server != null) && (server instanceof StandardServer)) {
0393: try {
0394: ((StandardServer) server).store();
0395: } catch (Exception e) {
0396: writer.println(sm.getString("managerServlet.saveFail",
0397: e.getMessage()));
0398: }
0399: }
0400:
0401: // Finish up the response
0402: writer.flush();
0403: writer.close();
0404:
0405: }
0406:
0407: /**
0408: * Initialize this servlet.
0409: */
0410: public void init() throws ServletException {
0411:
0412: // Ensure that our ContainerServlet properties have been set
0413: if ((wrapper == null) || (context == null))
0414: throw new UnavailableException(sm
0415: .getString("managerServlet.noWrapper"));
0416:
0417: // Verify that we were not accessed using the invoker servlet
0418: String servletName = getServletConfig().getServletName();
0419: if (servletName == null)
0420: servletName = "";
0421: if (servletName.startsWith("org.apache.catalina.INVOKER."))
0422: throw new UnavailableException(sm
0423: .getString("managerServlet.cannotInvoke"));
0424:
0425: // Set our properties from the initialization parameters
0426: String value = null;
0427: try {
0428: value = getServletConfig().getInitParameter("debug");
0429: debug = Integer.parseInt(value);
0430: } catch (Throwable t) {
0431: ;
0432: }
0433:
0434: // Acquire global JNDI resources if available
0435: Server server = ServerFactory.getServer();
0436: if ((server != null) && (server instanceof StandardServer)) {
0437: global = ((StandardServer) server).getGlobalNamingContext();
0438: }
0439:
0440: // Calculate the directory into which we will be deploying applications
0441: deployed = (File) getServletContext().getAttribute(
0442: "javax.servlet.context.tempdir");
0443:
0444: // Log debugging messages as necessary
0445: if (debug >= 1) {
0446: log("init: Associated with Deployer '" + deployer.getName()
0447: + "'");
0448: if (global != null) {
0449: log("init: Global resources are available");
0450: }
0451: }
0452:
0453: }
0454:
0455: // -------------------------------------------------------- Private Methods
0456:
0457: /**
0458: * Deploy a web application archive (included in the current request)
0459: * at the specified context path.
0460: *
0461: * @param writer Writer to render results to
0462: * @param path Context path of the application to be installed
0463: * @param request Servlet request we are processing
0464: */
0465: protected synchronized void deploy(PrintWriter writer, String path,
0466: HttpServletRequest request) {
0467:
0468: if (debug >= 1) {
0469: log("deploy: Deploying web application at '" + path + "'");
0470: }
0471:
0472: // Validate the requested context path
0473: if ((path == null)
0474: || (!path.startsWith("/") && path.equals(""))) {
0475: writer.println(sm.getString("managerServlet.invalidPath",
0476: path));
0477: return;
0478: }
0479: String displayPath = path;
0480: if (displayPath.equals("")) {
0481: displayPath = "/";
0482: }
0483: String basename = null;
0484: if (path.equals("")) {
0485: basename = "_";
0486: } else {
0487: basename = path.substring(1);
0488: }
0489: if (deployer.findDeployedApp(path) != null) {
0490: writer.println(sm.getString(
0491: "managerServlet.alreadyContext", displayPath));
0492: return;
0493: }
0494:
0495: // Upload the web application archive to a local WAR file
0496: File localWar = new File(deployed, basename + ".war");
0497: if (debug >= 2) {
0498: log("Uploading WAR file to " + localWar);
0499: }
0500: try {
0501: uploadWar(request, localWar);
0502: } catch (IOException e) {
0503: log("managerServlet.upload[" + displayPath + "]", e);
0504: writer.println(sm.getString("managerServlet.exception", e
0505: .toString()));
0506: return;
0507: }
0508:
0509: // Extract the nested context deployment file (if any)
0510: File localXml = new File(deployed, basename + ".xml");
0511: if (debug >= 2) {
0512: log("Extracting XML file to " + localXml);
0513: }
0514: try {
0515: extractXml(localWar, localXml);
0516: } catch (IOException e) {
0517: log("managerServlet.extract[" + displayPath + "]", e);
0518: writer.println(sm.getString("managerServlet.exception", e
0519: .toString()));
0520: return;
0521: }
0522:
0523: // Deploy this web application
0524: try {
0525: URL warURL = new URL("jar:file:"
0526: + localWar.getAbsolutePath() + "!/");
0527: URL xmlURL = null;
0528: if (localXml.exists()) {
0529: xmlURL = new URL("file:" + localXml.getAbsolutePath());
0530: }
0531: if (xmlURL != null) {
0532: deployer.install(xmlURL, warURL);
0533: } else {
0534: deployer.install(path, warURL);
0535: }
0536: } catch (Throwable t) {
0537: log("ManagerServlet.deploy[" + displayPath + "]", t);
0538: writer.println(sm.getString("managerServlet.exception", t
0539: .toString()));
0540: localWar.delete();
0541: localXml.delete();
0542: return;
0543: }
0544:
0545: // Acknowledge successful completion of this deploy command
0546: writer.println(sm.getString("managerServlet.installed",
0547: displayPath));
0548:
0549: }
0550:
0551: /**
0552: * Install an application for the specified path from the specified
0553: * web application archive.
0554: *
0555: * @param writer Writer to render results to
0556: * @param config URL of the context configuration file to be installed
0557: * @param path Context path of the application to be installed
0558: * @param war URL of the web application archive to be installed
0559: */
0560: protected void install(PrintWriter writer, String config,
0561: String path, String war) {
0562:
0563: if (debug >= 1) {
0564: if (config != null) {
0565: if (war != null) {
0566: log("install: Installing context configuration at '"
0567: + config + "' from '" + war + "'");
0568: } else {
0569: log("install: Installing context configuration at '"
0570: + config + "'");
0571: }
0572: } else {
0573: log("install: Installing web application at '" + path
0574: + "' from '" + war + "'");
0575: }
0576: }
0577:
0578: if (config != null) {
0579:
0580: if ((war != null)
0581: && (!war.startsWith("file:") && !war
0582: .startsWith("jar:"))) {
0583: writer.println(sm.getString(
0584: "managerServlet.invalidWar", war));
0585: return;
0586: }
0587:
0588: try {
0589: if (war == null) {
0590: deployer.install(new URL(config), null);
0591: } else {
0592: deployer.install(new URL(config), new URL(war));
0593: }
0594: writer.println(sm.getString(
0595: "managerServlet.configured", config));
0596: } catch (Throwable t) {
0597: log("ManagerServlet.configure[" + config + "]", t);
0598: writer.println(sm.getString("managerServlet.exception",
0599: t.toString()));
0600: }
0601:
0602: } else {
0603:
0604: if ((path == null)
0605: || (!path.startsWith("/") && path.equals(""))) {
0606: writer.println(sm.getString(
0607: "managerServlet.invalidPath", path));
0608: return;
0609: }
0610: String displayPath = path;
0611: if ("/".equals(path)) {
0612: path = "";
0613: }
0614: if ((war == null)
0615: || (!war.startsWith("file:") && !war
0616: .startsWith("jar:"))) {
0617: writer.println(sm.getString(
0618: "managerServlet.invalidWar", war));
0619: return;
0620: }
0621:
0622: try {
0623: Context context = deployer.findDeployedApp(path);
0624: if (context != null) {
0625: writer.println(sm.getString(
0626: "managerServlet.alreadyContext",
0627: displayPath));
0628: return;
0629: }
0630: deployer.install(path, new URL(war));
0631: writer.println(sm.getString("managerServlet.installed",
0632: displayPath));
0633: } catch (Throwable t) {
0634: log("ManagerServlet.install[" + displayPath + "]", t);
0635: writer.println(sm.getString("managerServlet.exception",
0636: t.toString()));
0637: }
0638:
0639: }
0640:
0641: }
0642:
0643: /**
0644: * Render a list of the currently active Contexts in our virtual host.
0645: *
0646: * @param writer Writer to render to
0647: */
0648: protected void list(PrintWriter writer) {
0649:
0650: if (debug >= 1)
0651: log("list: Listing contexts for virtual host '"
0652: + deployer.getName() + "'");
0653:
0654: writer.println(sm.getString("managerServlet.listed", deployer
0655: .getName()));
0656: String contextPaths[] = deployer.findDeployedApps();
0657: for (int i = 0; i < contextPaths.length; i++) {
0658: Context context = deployer.findDeployedApp(contextPaths[i]);
0659: String displayPath = contextPaths[i];
0660: if (displayPath.equals(""))
0661: displayPath = "/";
0662: if (context != null) {
0663: if (context.getAvailable()) {
0664: writer.println(sm.getString(
0665: "managerServlet.listitem", displayPath,
0666: "running", ""
0667: + context.getManager()
0668: .findSessions().length,
0669: context.getDocBase()));
0670: } else {
0671: writer.println(sm.getString(
0672: "managerServlet.listitem", displayPath,
0673: "stopped", "0", context.getDocBase()));
0674: }
0675: }
0676: }
0677: }
0678:
0679: /**
0680: * Reload the web application at the specified context path.
0681: *
0682: * @param writer Writer to render to
0683: * @param path Context path of the application to be restarted
0684: */
0685: protected void reload(PrintWriter writer, String path) {
0686:
0687: if (debug >= 1)
0688: log("restart: Reloading web application at '" + path + "'");
0689:
0690: if ((path == null)
0691: || (!path.startsWith("/") && path.equals(""))) {
0692: writer.println(sm.getString("managerServlet.invalidPath",
0693: path));
0694: return;
0695: }
0696: String displayPath = path;
0697: if (path.equals("/"))
0698: path = "";
0699:
0700: try {
0701: Context context = deployer.findDeployedApp(path);
0702: if (context == null) {
0703: writer.println(sm.getString("managerServlet.noContext",
0704: displayPath));
0705: return;
0706: }
0707: DirContext resources = context.getResources();
0708: if (resources instanceof ProxyDirContext) {
0709: resources = ((ProxyDirContext) resources)
0710: .getDirContext();
0711: }
0712: if (resources instanceof WARDirContext) {
0713: writer.println(sm.getString("managerServlet.noReload",
0714: displayPath));
0715: return;
0716: }
0717: context.reload();
0718: writer.println(sm.getString("managerServlet.reloaded",
0719: displayPath));
0720: } catch (Throwable t) {
0721: log("ManagerServlet.reload[" + displayPath + "]", t);
0722: writer.println(sm.getString("managerServlet.exception", t
0723: .toString()));
0724: }
0725:
0726: }
0727:
0728: /**
0729: * Remove the web application at the specified context path.
0730: *
0731: * @param writer Writer to render to
0732: * @param path Context path of the application to be removed
0733: */
0734: protected void remove(PrintWriter writer, String path) {
0735:
0736: if (debug >= 1)
0737: log("remove: Removing web application at '" + path + "'");
0738:
0739: if ((path == null)
0740: || (!path.startsWith("/") && path.equals(""))) {
0741: writer.println(sm.getString("managerServlet.invalidPath",
0742: path));
0743: return;
0744: }
0745: String displayPath = path;
0746: if (path.equals("/"))
0747: path = "";
0748:
0749: try {
0750: Context context = deployer.findDeployedApp(path);
0751: if (context == null) {
0752: writer.println(sm.getString("managerServlet.noContext",
0753: displayPath));
0754: return;
0755: }
0756: deployer.remove(path);
0757: writer.println(sm.getString("managerServlet.removed",
0758: displayPath));
0759: } catch (Throwable t) {
0760: log("ManagerServlet.remove[" + displayPath + "]", t);
0761: writer.println(sm.getString("managerServlet.exception", t
0762: .toString()));
0763: }
0764:
0765: }
0766:
0767: /**
0768: * Render a list of available global JNDI resources.
0769: *
0770: * @param type Fully qualified class name of the resource type of interest,
0771: * or <code>null</code> to list resources of all types
0772: */
0773: protected void resources(PrintWriter writer, String type) {
0774:
0775: if (debug >= 1) {
0776: if (type != null) {
0777: log("resources: Listing resources of type " + type);
0778: } else {
0779: log("resources: Listing resources of all types");
0780: }
0781: }
0782:
0783: // Is the global JNDI resources context available?
0784: if (global == null) {
0785: writer.println(sm.getString("managerServlet.noGlobal"));
0786: return;
0787: }
0788:
0789: // Enumerate the global JNDI resources of the requested type
0790: if (type != null) {
0791: writer.println(sm.getString("managerServlet.resourcesType",
0792: type));
0793: } else {
0794: writer.println(sm.getString("managerServlet.resourcesAll"));
0795: }
0796:
0797: Class clazz = null;
0798: try {
0799: if (type != null) {
0800: clazz = Class.forName(type);
0801: }
0802: } catch (Throwable t) {
0803: log("ManagerServlet.resources[" + type + "]", t);
0804: writer.println(sm.getString("managerServlet.exception", t
0805: .toString()));
0806: return;
0807: }
0808:
0809: printResources(writer, "", global, type, clazz);
0810:
0811: }
0812:
0813: /**
0814: * List the resources of the given context.
0815: */
0816: protected void printResources(PrintWriter writer, String prefix,
0817: javax.naming.Context namingContext, String type, Class clazz) {
0818:
0819: try {
0820: NamingEnumeration items = namingContext.listBindings("");
0821: while (items.hasMore()) {
0822: Binding item = (Binding) items.next();
0823: if (item.getObject() instanceof javax.naming.Context) {
0824: printResources(writer, prefix + item.getName()
0825: + "/", (javax.naming.Context) item
0826: .getObject(), type, clazz);
0827: } else {
0828: if ((clazz != null)
0829: && (!(clazz.isInstance(item.getObject())))) {
0830: continue;
0831: }
0832: writer.print(prefix + item.getName());
0833: writer.print(':');
0834: writer.print(item.getClassName());
0835: // Do we want a description if available?
0836: writer.println();
0837: }
0838: }
0839: } catch (Throwable t) {
0840: log("ManagerServlet.resources[" + type + "]", t);
0841: writer.println(sm.getString("managerServlet.exception", t
0842: .toString()));
0843: }
0844:
0845: }
0846:
0847: /**
0848: * Render a list of security role names (and corresponding descriptions)
0849: * from the <code>org.apache.catalina.UserDatabase</code> resource that is
0850: * connected to the <code>users</code> resource reference. Typically, this
0851: * will be the global user database, but can be adjusted if you have
0852: * different user databases for different virtual hosts.
0853: *
0854: * @param writer Writer to render to
0855: */
0856: protected void roles(PrintWriter writer) {
0857:
0858: if (debug >= 1) {
0859: log("roles: List security roles from user database");
0860: }
0861:
0862: // Look up the UserDatabase instance we should use
0863: UserDatabase database = null;
0864: try {
0865: InitialContext ic = new InitialContext();
0866: database = (UserDatabase) ic.lookup("java:comp/env/users");
0867: } catch (NamingException e) {
0868: writer.println(sm
0869: .getString("managerServlet.userDatabaseError"));
0870: log("java:comp/env/users", e);
0871: return;
0872: }
0873: if (database == null) {
0874: writer.println(sm
0875: .getString("managerServlet.userDatabaseMissing"));
0876: return;
0877: }
0878:
0879: // Enumerate the available roles
0880: writer.println(sm.getString("managerServlet.rolesList"));
0881: Iterator roles = database.getRoles();
0882: if (roles != null) {
0883: while (roles.hasNext()) {
0884: Role role = (Role) roles.next();
0885: writer.print(role.getRolename());
0886: writer.print(':');
0887: if (role.getDescription() != null) {
0888: writer.print(role.getDescription());
0889: }
0890: writer.println();
0891: }
0892: }
0893:
0894: }
0895:
0896: /**
0897: * Session information for the web application at the specified context path.
0898: * Displays a profile of session MaxInactiveInterval timeouts listing number
0899: * of sessions for each 10 minute timeout interval up to 10 hours.
0900: *
0901: * @param writer Writer to render to
0902: * @param path Context path of the application to list session information for
0903: */
0904: protected void sessions(PrintWriter writer, String path) {
0905:
0906: if (debug >= 1)
0907: log("sessions: Session information for web application at '"
0908: + path + "'");
0909:
0910: if ((path == null)
0911: || (!path.startsWith("/") && path.equals(""))) {
0912: writer.println(sm.getString("managerServlet.invalidPath",
0913: path));
0914: return;
0915: }
0916: String displayPath = path;
0917: if (path.equals("/"))
0918: path = "";
0919: try {
0920: Context context = deployer.findDeployedApp(path);
0921: if (context == null) {
0922: writer.println(sm.getString("managerServlet.noContext",
0923: displayPath));
0924: return;
0925: }
0926: writer.println(sm.getString("managerServlet.sessions",
0927: displayPath));
0928: writer.println(sm.getString(
0929: "managerServlet.sessiondefaultmax", ""
0930: + context.getManager()
0931: .getMaxInactiveInterval() / 60));
0932: Session[] sessions = context.getManager().findSessions();
0933: int[] timeout = new int[60];
0934: int notimeout = 0;
0935: for (int i = 0; i < sessions.length; i++) {
0936: int time = sessions[i].getMaxInactiveInterval()
0937: / (10 * 60);
0938: if (time < 0)
0939: notimeout++;
0940: else if (time >= timeout.length)
0941: timeout[timeout.length - 1]++;
0942: else
0943: timeout[time]++;
0944: }
0945: if (timeout[0] > 0)
0946: writer.println(sm.getString(
0947: "managerServlet.sessiontimeout", "<10"
0948: + timeout[0]));
0949: for (int i = 1; i < timeout.length - 1; i++) {
0950: if (timeout[i] > 0)
0951: writer.println(sm.getString(
0952: "managerServlet.sessiontimeout", "" + (i)
0953: * 10 + " - <" + (i + 1) * 10, ""
0954: + timeout[i]));
0955: }
0956: if (timeout[timeout.length - 1] > 0)
0957: writer.println(sm.getString(
0958: "managerServlet.sessiontimeout", ">="
0959: + timeout.length * 10, ""
0960: + timeout[timeout.length - 1]));
0961: if (notimeout > 0)
0962: writer.println(sm.getString(
0963: "managerServlet.sessiontimeout", "unlimited",
0964: "" + notimeout));
0965: } catch (Throwable t) {
0966: log("ManagerServlet.sessions[" + displayPath + "]", t);
0967: writer.println(sm.getString("managerServlet.exception", t
0968: .toString()));
0969: }
0970:
0971: }
0972:
0973: /**
0974: * Start the web application at the specified context path.
0975: *
0976: * @param writer Writer to render to
0977: * @param path Context path of the application to be started
0978: */
0979: protected void start(PrintWriter writer, String path) {
0980:
0981: if (debug >= 1)
0982: log("start: Starting web application at '" + path + "'");
0983:
0984: if ((path == null)
0985: || (!path.startsWith("/") && path.equals(""))) {
0986: writer.println(sm.getString("managerServlet.invalidPath",
0987: path));
0988: return;
0989: }
0990: String displayPath = path;
0991: if (path.equals("/"))
0992: path = "";
0993:
0994: try {
0995: Context context = deployer.findDeployedApp(path);
0996: if (context == null) {
0997: writer.println(sm.getString("managerServlet.noContext",
0998: displayPath));
0999: return;
1000: }
1001: deployer.start(path);
1002: if (context.getAvailable())
1003: writer.println(sm.getString("managerServlet.started",
1004: displayPath));
1005: else
1006: writer.println(sm.getString(
1007: "managerServlet.startFailed", displayPath));
1008: } catch (Throwable t) {
1009: getServletContext().log(
1010: sm.getString("managerServlet.startFailed",
1011: displayPath), t);
1012: writer.println(sm.getString("managerServlet.startFailed",
1013: displayPath));
1014: writer.println(sm.getString("managerServlet.exception", t
1015: .toString()));
1016: }
1017:
1018: }
1019:
1020: /**
1021: * Stop the web application at the specified context path.
1022: *
1023: * @param writer Writer to render to
1024: * @param path Context path of the application to be stopped
1025: */
1026: protected void stop(PrintWriter writer, String path) {
1027:
1028: if (debug >= 1)
1029: log("stop: Stopping web application at '" + path + "'");
1030:
1031: if ((path == null)
1032: || (!path.startsWith("/") && path.equals(""))) {
1033: writer.println(sm.getString("managerServlet.invalidPath",
1034: path));
1035: return;
1036: }
1037: String displayPath = path;
1038: if (path.equals("/"))
1039: path = "";
1040:
1041: try {
1042: Context context = deployer.findDeployedApp(path);
1043: if (context == null) {
1044: writer.println(sm.getString("managerServlet.noContext",
1045: displayPath));
1046: return;
1047: }
1048: deployer.stop(path);
1049: writer.println(sm.getString("managerServlet.stopped",
1050: displayPath));
1051: } catch (Throwable t) {
1052: log("ManagerServlet.stop[" + displayPath + "]", t);
1053: writer.println(sm.getString("managerServlet.exception", t
1054: .toString()));
1055: }
1056:
1057: }
1058:
1059: /**
1060: * Undeploy the web application at the specified context path.
1061: *
1062: * @param writer Writer to render to
1063: * @param path Context path of the application to be removed
1064: */
1065: protected void undeploy(PrintWriter writer, String path) {
1066:
1067: if (debug >= 1)
1068: log("undeploy: Undeploying web application at '" + path
1069: + "'");
1070:
1071: if ((path == null)
1072: || (!path.startsWith("/") && path.equals(""))) {
1073: writer.println(sm.getString("managerServlet.invalidPath",
1074: path));
1075: return;
1076: }
1077: String displayPath = path;
1078: if (path.equals("/"))
1079: path = "";
1080:
1081: try {
1082:
1083: // Validate the Context of the specified application
1084: Context context = deployer.findDeployedApp(path);
1085: if (context == null) {
1086: writer.println(sm.getString("managerServlet.noContext",
1087: displayPath));
1088: return;
1089: }
1090:
1091: // Identify the appBase of the owning Host of this Context (if any)
1092: String appBase = null;
1093: File appBaseDir = null;
1094: String appBasePath = null;
1095: if (context.getParent() instanceof Host) {
1096: appBase = ((Host) context.getParent()).getAppBase();
1097: appBaseDir = new File(appBase);
1098: if (!appBaseDir.isAbsolute()) {
1099: appBaseDir = new File(System
1100: .getProperty("catalina.base"), appBase);
1101: }
1102: appBasePath = appBaseDir.getCanonicalPath();
1103: }
1104:
1105: // Validate the docBase path of this application
1106: String deployedPath = deployed.getCanonicalPath();
1107: String docBase = context.getDocBase();
1108: File docBaseDir = new File(docBase);
1109: if (!docBaseDir.isAbsolute()) {
1110: docBaseDir = new File(appBaseDir, docBase);
1111: }
1112: String docBasePath = docBaseDir.getCanonicalPath();
1113: if (!docBasePath.startsWith(deployedPath)) {
1114: writer.println(sm.getString("managerServlet.noDocBase",
1115: displayPath));
1116: return;
1117: }
1118:
1119: // Remove this web application and its associated docBase
1120: if (debug >= 2) {
1121: log("Undeploying document base " + docBasePath);
1122: }
1123: deployer.remove(path);
1124: if (docBaseDir.isDirectory()) {
1125: undeployDir(docBaseDir);
1126: } else {
1127: docBaseDir.delete(); // Delete the WAR file
1128: }
1129: String docBaseXmlPath = docBasePath.substring(0,
1130: docBasePath.length() - 4)
1131: + ".xml";
1132: File docBaseXml = new File(docBaseXmlPath);
1133: docBaseXml.delete();
1134: writer.println(sm.getString("managerServlet.undeployed",
1135: displayPath));
1136:
1137: } catch (Throwable t) {
1138: log("ManagerServlet.undeploy[" + displayPath + "]", t);
1139: writer.println(sm.getString("managerServlet.exception", t
1140: .toString()));
1141: }
1142:
1143: // Saving configuration
1144: Server server = ServerFactory.getServer();
1145: if ((server != null) && (server instanceof StandardServer)) {
1146: try {
1147: ((StandardServer) server).store();
1148: } catch (Exception e) {
1149: writer.println(sm.getString("managerServlet.saveFail",
1150: e.getMessage()));
1151: }
1152: }
1153:
1154: }
1155:
1156: // -------------------------------------------------------- Support Methods
1157:
1158: /**
1159: * Extract the context configuration file from the specified WAR,
1160: * if it is present. If it is not present, ensure that the corresponding
1161: * file does not exist.
1162: *
1163: * @param war File object representing the WAR
1164: * @param xml File object representing where to store the extracted
1165: * context configuration file (if it exists)
1166: *
1167: * @exception IOException if an i/o error occurs
1168: */
1169: protected void extractXml(File war, File xml) throws IOException {
1170:
1171: xml.delete();
1172: JarFile jar = null;
1173: JarEntry entry = null;
1174: InputStream istream = null;
1175: BufferedOutputStream ostream = null;
1176: try {
1177: jar = new JarFile(war);
1178: entry = jar.getJarEntry("META-INF/context.xml");
1179: if (entry == null) {
1180: return;
1181: }
1182: istream = jar.getInputStream(entry);
1183: ostream = new BufferedOutputStream(
1184: new FileOutputStream(xml), 1024);
1185: byte buffer[] = new byte[1024];
1186: while (true) {
1187: int n = istream.read(buffer);
1188: if (n < 0) {
1189: break;
1190: }
1191: ostream.write(buffer, 0, n);
1192: }
1193: ostream.flush();
1194: ostream.close();
1195: ostream = null;
1196: istream.close();
1197: istream = null;
1198: entry = null;
1199: jar.close();
1200: jar = null;
1201: } catch (IOException e) {
1202: xml.delete();
1203: throw e;
1204: } finally {
1205: if (ostream != null) {
1206: try {
1207: ostream.close();
1208: } catch (Throwable t) {
1209: ;
1210: }
1211: ostream = null;
1212: }
1213: if (istream != null) {
1214: try {
1215: istream.close();
1216: } catch (Throwable t) {
1217: ;
1218: }
1219: istream = null;
1220: }
1221: entry = null;
1222: if (jar != null) {
1223: try {
1224: jar.close();
1225: } catch (Throwable t) {
1226: ;
1227: }
1228: jar = null;
1229: }
1230: }
1231:
1232: }
1233:
1234: /**
1235: * Delete the specified directory, including all of its contents and
1236: * subdirectories recursively.
1237: *
1238: * @param dir File object representing the directory to be deleted
1239: */
1240: protected void undeployDir(File dir) {
1241:
1242: String files[] = dir.list();
1243: if (files == null) {
1244: files = new String[0];
1245: }
1246: for (int i = 0; i < files.length; i++) {
1247: File file = new File(dir, files[i]);
1248: if (file.isDirectory()) {
1249: undeployDir(file);
1250: } else {
1251: file.delete();
1252: }
1253: }
1254: dir.delete();
1255:
1256: }
1257:
1258: /**
1259: * Upload the WAR file included in this request, and store it at the
1260: * specified file location.
1261: *
1262: * @param request The servlet request we are processing
1263: * @param file The file into which we should store the uploaded WAR
1264: *
1265: * @exception IOException if an I/O error occurs during processing
1266: */
1267: protected void uploadWar(HttpServletRequest request, File war)
1268: throws IOException {
1269:
1270: war.delete();
1271: ServletInputStream istream = null;
1272: BufferedOutputStream ostream = null;
1273: try {
1274: istream = request.getInputStream();
1275: ostream = new BufferedOutputStream(
1276: new FileOutputStream(war), 1024);
1277: byte buffer[] = new byte[1024];
1278: while (true) {
1279: int n = istream.read(buffer);
1280: if (n < 0) {
1281: break;
1282: }
1283: ostream.write(buffer, 0, n);
1284: }
1285: ostream.flush();
1286: ostream.close();
1287: ostream = null;
1288: istream.close();
1289: istream = null;
1290: } catch (IOException e) {
1291: war.delete();
1292: throw e;
1293: } finally {
1294: if (ostream != null) {
1295: try {
1296: ostream.close();
1297: } catch (Throwable t) {
1298: ;
1299: }
1300: ostream = null;
1301: }
1302: if (istream != null) {
1303: try {
1304: istream.close();
1305: } catch (Throwable t) {
1306: ;
1307: }
1308: istream = null;
1309: }
1310: }
1311:
1312: }
1313:
1314: }
|