blob: 4e668854b4ffad6d66ea6ca478e6167a3a0043c6 [file] [log] [blame]
/*
* Copyright 1999,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.manager;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.Binding;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
import org.apache.catalina.ContainerServlet;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.Role;
import org.apache.catalina.Server;
import org.apache.catalina.ServerFactory;
import org.apache.catalina.Session;
import org.apache.catalina.UserDatabase;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.util.RequestUtil;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.util.StringManager;
import org.apache.commons.modeler.Registry;
/**
* Servlet that enables remote management of the web applications installed
* within the same virtual host as this web application is. Normally, this
* functionality will be protected by a security constraint in the web
* application deployment descriptor. However, this requirement can be
* relaxed during testing.
* <p>
* This servlet examines the value returned by <code>getPathInfo()</code>
* and related query parameters to determine what action is being requested.
* The following actions and parameters (starting after the servlet path)
* are supported:
* <ul>
* <li><b>/deploy?config={config-url}</b> - Install and start a new
* web application, based on the contents of the context configuration
* file found at the specified URL. The <code>docBase</code> attribute
* of the context configuration file is used to locate the actual
* WAR or directory containing the application.</li>
* <li><b>/deploy?config={config-url}&war={war-url}/</b> - Install and start
* a new web application, based on the contents of the context
* configuration file found at <code>{config-url}</code>, overriding the
* <code>docBase</code> attribute with the contents of the web
* application archive found at <code>{war-url}</code>.</li>
* <li><b>/deploy?path=/xxx&war={war-url}</b> - Install and start a new
* web application attached to context path <code>/xxx</code>, based
* on the contents of the web application archive found at the
* specified URL.</li>
* <li><b>/list</b> - List the context paths of all currently installed web
* applications for this virtual host. Each context will be listed with
* the following format <code>path:status:sessions</code>.
* Where path is the context path. Status is either running or stopped.
* Sessions is the number of active Sessions.</li>
* <li><b>/reload?path=/xxx</b> - Reload the Java classes and resources for
* the application at the specified path.</li>
* <li><b>/resources?type=xxxx</b> - Enumerate the available global JNDI
* resources, optionally limited to those of the specified type
* (fully qualified Java class name), if available.</li>
* <li><b>/roles</b> - Enumerate the available security role names and
* descriptions from the user database connected to the <code>users</code>
* resource reference.
* <li><b>/serverinfo</b> - Display system OS and JVM properties.
* <li><b>/sessions?path=/xxx</b> - List session information about the web
* application attached to context path <code>/xxx</code> for this
* virtual host.</li>
* <li><b>/start?path=/xxx</b> - Start the web application attached to
* context path <code>/xxx</code> for this virtual host.</li>
* <li><b>/stop?path=/xxx</b> - Stop the web application attached to
* context path <code>/xxx</code> for this virtual host.</li>
* <li><b>/undeploy?path=/xxx</b> - Shutdown and remove the web application
* attached to context path <code>/xxx</code> for this virtual host,
* and remove the underlying WAR file or document base directory.
* (<em>NOTE</em> - This is only allowed if the WAR file or document
* base is stored in the <code>appBase</code> directory of this host,
* typically as a result of being placed there via the <code>/deploy</code>
* command.</li>
* </ul>
* <p>Use <code>path=/</code> for the ROOT context.</p>
* <p>The syntax of the URL for a web application archive must conform to one
* of the following patterns to be successfully deployed:</p>
* <ul>
* <li><b>file:/absolute/path/to/a/directory</b> - You can specify the absolute
* path of a directory that contains the unpacked version of a web
* application. This directory will be attached to the context path you
* specify without any changes.</li>
* <li><b>jar:file:/absolute/path/to/a/warfile.war!/</b> - You can specify a
* URL to a local web application archive file. The syntax must conform to
* the rules specified by the <code>JarURLConnection</code> class for a
* reference to an entire JAR file.</li>
* <li><b>jar:http://hostname:port/path/to/a/warfile.war!/</b> - You can specify
* a URL to a remote (HTTP-accessible) web application archive file. The
* syntax must conform to the rules specified by the
* <code>JarURLConnection</code> class for a reference to an entire
* JAR file.</li>
* </ul>
* <p>
* <b>NOTE</b> - Attempting to reload or remove the application containing
* this servlet itself will not succeed. Therefore, this servlet should
* generally be deployed as a separate web application within the virtual host
* to be managed.
* <p>
* <b>NOTE</b> - For security reasons, this application will not operate
* when accessed via the invoker servlet. You must explicitly map this servlet
* with a servlet mapping, and you will always want to protect it with
* appropriate security constraints as well.
* <p>
* The following servlet initialization parameters are recognized:
* <ul>
* <li><b>debug</b> - The debugging detail level that controls the amount
* of information that is logged by this servlet. Default is zero.
* </ul>
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Revision$ $Date$
*/
public class ManagerServlet
extends HttpServlet implements ContainerServlet {
// ----------------------------------------------------- Instance Variables
/**
* Path where context descriptors should be deployed.
*/
protected File configBase = null;
/**
* The Context container associated with our web application.
*/
protected Context context = null;
/**
* The debugging detail level for this servlet.
*/
protected int debug = 1;
/**
* File object representing the directory into which the deploy() command
* will store the WAR and context configuration files that have been
* uploaded.
*/
protected File deployed = null;
/**
* Path used to store revisions of webapps.
*/
protected File versioned = null;
/**
* Path used to store context descriptors.
*/
protected File contextDescriptors = null;
/**
* The associated host.
*/
protected Host host = null;
/**
* The host appBase.
*/
protected File appBase = null;
/**
* MBean server.
*/
protected MBeanServer mBeanServer = null;
/**
* The associated deployer ObjectName.
*/
protected ObjectName oname = null;
/**
* The global JNDI <code>NamingContext</code> for this server,
* if available.
*/
protected javax.naming.Context global = null;
/**
* The string manager for this package.
*/
protected static StringManager sm =
StringManager.getManager(Constants.Package);
/**
* The Wrapper container associated with this servlet.
*/
protected Wrapper wrapper = null;
// ----------------------------------------------- ContainerServlet Methods
/**
* Return the Wrapper with which we are associated.
*/
public Wrapper getWrapper() {
return (this.wrapper);
}
/**
* Set the Wrapper with which we are associated.
*
* @param wrapper The new wrapper
*/
public void setWrapper(Wrapper wrapper) {
this.wrapper = wrapper;
if (wrapper == null) {
context = null;
host = null;
oname = null;
} else {
context = (Context) wrapper.getParent();
host = (Host) context.getParent();
Engine engine = (Engine) host.getParent();
try {
oname = new ObjectName(engine.getName()
+ ":type=Deployer,host=" + host.getName());
} catch (Exception e) {
// ?
}
}
// Retrieve the MBean server
mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
}
// --------------------------------------------------------- Public Methods
/**
* Finalize this servlet.
*/
public void destroy() {
; // No actions necessary
}
/**
* Process a GET request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Verify that we were not accessed using the invoker servlet
if (request.getAttribute(Globals.INVOKED_ATTR) != null)
throw new UnavailableException
(sm.getString("managerServlet.cannotInvoke"));
// Identify the request parameters that we need
String command = request.getPathInfo();
if (command == null)
command = request.getServletPath();
String config = request.getParameter("config");
String path = request.getParameter("path");
String type = request.getParameter("type");
String war = request.getParameter("war");
String tag = request.getParameter("tag");
boolean update = false;
if ((request.getParameter("update") != null)
&& (request.getParameter("update").equals("true"))) {
update = true;
}
// Prepare our output writer to generate the response message
response.setContentType("text/plain; charset=" + Constants.CHARSET);
PrintWriter writer = response.getWriter();
// Process the requested command (note - "/deploy" is not listed here)
if (command == null) {
writer.println(sm.getString("managerServlet.noCommand"));
} else if (command.equals("/deploy")) {
if (war != null || config != null) {
deploy(writer, config, path, war, update);
} else {
deploy(writer, path, tag);
}
} else if (command.equals("/install")) {
// Deprecated
deploy(writer, config, path, war, false);
} else if (command.equals("/list")) {
list(writer);
} else if (command.equals("/reload")) {
reload(writer, path);
} else if (command.equals("/remove")) {
// Deprecated
undeploy(writer, path);
} else if (command.equals("/resources")) {
resources(writer, type);
} else if (command.equals("/roles")) {
roles(writer);
} else if (command.equals("/save")) {
save(writer, path);
} else if (command.equals("/serverinfo")) {
serverinfo(writer);
} else if (command.equals("/sessions")) {
sessions(writer, path);
} else if (command.equals("/start")) {
start(writer, path);
} else if (command.equals("/stop")) {
stop(writer, path);
} else if (command.equals("/undeploy")) {
undeploy(writer, path);
} else {
writer.println(sm.getString("managerServlet.unknownCommand",
command));
}
// Finish up the response
writer.flush();
writer.close();
}
/**
* Process a PUT request for the specified resource.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet-specified error occurs
*/
public void doPut(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Verify that we were not accessed using the invoker servlet
if (request.getAttribute(Globals.INVOKED_ATTR) != null)
throw new UnavailableException
(sm.getString("managerServlet.cannotInvoke"));
// Identify the request parameters that we need
String command = request.getPathInfo();
if (command == null)
command = request.getServletPath();
String path = request.getParameter("path");
String tag = request.getParameter("tag");
boolean update = false;
if ((request.getParameter("update") != null)
&& (request.getParameter("update").equals("true"))) {
update = true;
}
// Prepare our output writer to generate the response message
response.setContentType("text/plain;charset="+Constants.CHARSET);
PrintWriter writer = response.getWriter();
// Process the requested command
if (command == null) {
writer.println(sm.getString("managerServlet.noCommand"));
} else if (command.equals("/deploy")) {
deploy(writer, path, tag, update, request);
} else {
writer.println(sm.getString("managerServlet.unknownCommand",
command));
}
// Finish up the response
writer.flush();
writer.close();
}
/**
* Initialize this servlet.
*/
public void init() throws ServletException {
// Ensure that our ContainerServlet properties have been set
if ((wrapper == null) || (context == null))
throw new UnavailableException
(sm.getString("managerServlet.noWrapper"));
// Verify that we were not accessed using the invoker servlet
String servletName = getServletConfig().getServletName();
if (servletName == null)
servletName = "";
if (servletName.startsWith("org.apache.catalina.INVOKER."))
throw new UnavailableException
(sm.getString("managerServlet.cannotInvoke"));
// Set our properties from the initialization parameters
String value = null;
try {
value = getServletConfig().getInitParameter("debug");
debug = Integer.parseInt(value);
} catch (Throwable t) {
;
}
// Acquire global JNDI resources if available
Server server = ServerFactory.getServer();
if ((server != null) && (server instanceof StandardServer)) {
global = ((StandardServer) server).getGlobalNamingContext();
}
// Calculate the directory into which we will be deploying applications
versioned = (File) getServletContext().getAttribute
("javax.servlet.context.tempdir");
// Identify the appBase of the owning Host of this Context
// (if any)
String appBase = ((Host) context.getParent()).getAppBase();
deployed = new File(appBase);
if (!deployed.isAbsolute()) {
deployed = new File(System.getProperty("catalina.base"),
appBase);
}
configBase = new File(System.getProperty("catalina.base"), "conf");
Container container = context;
Container host = null;
Container engine = null;
while (container != null) {
if (container instanceof Host)
host = container;
if (container instanceof Engine)
engine = container;
container = container.getParent();
}
if (engine != null) {
configBase = new File(configBase, engine.getName());
}
if (host != null) {
configBase = new File(configBase, host.getName());
}
// Note: The directory must exist for this to work.
// Log debugging messages as necessary
if (debug >= 1) {
log("init: Associated with Deployer '" +
oname + "'");
if (global != null) {
log("init: Global resources are available");
}
}
}
// -------------------------------------------------------- Private Methods
/**
* Store server configuration.
*
* @param path Optional context path to save
*/
protected synchronized void save(PrintWriter writer, String path) {
Server server = ServerFactory.getServer();
if (!(server instanceof StandardServer)) {
writer.println(sm.getString("managerServlet.saveFail", server));
return;
}
if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
try {
((StandardServer) server).storeConfig();
writer.println(sm.getString("managerServlet.saved"));
} catch (Exception e) {
log("managerServlet.storeConfig", e);
writer.println(sm.getString("managerServlet.exception",
e.toString()));
return;
}
} else {
String contextPath = path;
if (path.equals("/")) {
contextPath = "";
}
Context context = (Context) host.findChild(contextPath);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext", path));
return;
}
try {
((StandardServer) server).storeContext(context);
writer.println(sm.getString("managerServlet.savedContext",
path));
} catch (Exception e) {
log("managerServlet.save[" + path + "]", e);
writer.println(sm.getString("managerServlet.exception",
e.toString()));
return;
}
}
}
/**
* Deploy a web application archive (included in the current request)
* at the specified context path.
*
* @param writer Writer to render results to
* @param path Context path of the application to be installed
* @param tag Tag to be associated with the webapp
* @param request Servlet request we are processing
*/
protected synchronized void deploy
(PrintWriter writer, String path,
String tag, boolean update, HttpServletRequest request) {
if (debug >= 1) {
log("deploy: Deploying web application at '" + path + "'");
}
// Validate the requested context path
if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
writer.println(sm.getString("managerServlet.invalidPath", path));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
String basename = getDocBase(path);
// Check if app already exists, or undeploy it if updating
Context context = (Context) host.findChild(path);
if (update) {
if (context != null) {
undeploy(writer, displayPath);
}
context = (Context) host.findChild(path);
}
if (context != null) {
writer.println
(sm.getString("managerServlet.alreadyContext",
displayPath));
return;
}
// Calculate the base path
File deployedPath = deployed;
if (tag != null) {
deployedPath = new File(versioned, tag);
deployedPath.mkdirs();
}
// Upload the web application archive to a local WAR file
File localWar = new File(deployedPath, basename + ".war");
if (debug >= 2) {
log("Uploading WAR file to " + localWar);
}
// Copy WAR to appBase
try {
if (!isServiced(path)) {
addServiced(path);
try {
// Upload WAR
uploadWar(request, localWar);
// Copy WAR and XML to the host app base if needed
if (tag != null) {
deployedPath = deployed;
File localWarCopy = new File(deployedPath, basename + ".war");
copy(localWar, localWarCopy);
localWar = localWarCopy;
copy(localWar, new File(getAppBase(), basename + ".war"));
}
// Perform new deployment
check(path);
} finally {
removeServiced(path);
}
}
} catch (Exception e) {
log("managerServlet.check[" + displayPath + "]", e);
writer.println(sm.getString("managerServlet.exception",
e.toString()));
return;
}
context = (Context) host.findChild(path);
if (context != null && context.getConfigured()) {
writer.println(sm.getString("managerServlet.deployed", displayPath));
} else {
// Something failed
writer.println(sm.getString("managerServlet.deployFailed", displayPath));
}
}
/**
* Install an application for the specified path from the specified
* web application archive.
*
* @param writer Writer to render results to
* @param tag Revision tag to deploy from
* @param path Context path of the application to be installed
*/
protected void deploy(PrintWriter writer, String path, String tag) {
// Validate the requested context path
if ((path == null) || path.length() == 0 || !path.startsWith("/")) {
writer.println(sm.getString("managerServlet.invalidPath", path));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
// Calculate the base path
File deployedPath = versioned;
if (tag != null) {
deployedPath = new File(deployedPath, tag);
}
// Find the local WAR file
File localWar = new File(deployedPath, getDocBase(path) + ".war");
// Find the local context deployment file (if any)
File localXml = new File(configBase, getConfigFile(path) + ".xml");
// Check if app already exists, or undeploy it if updating
Context context = (Context) host.findChild(path);
if (context != null) {
undeploy(writer, displayPath);
}
// Copy WAR to appBase
try {
if (!isServiced(path)) {
addServiced(path);
try {
copy(localWar, new File(getAppBase(), getDocBase(path) + ".war"));
// Perform new deployment
check(path);
} finally {
removeServiced(path);
}
}
} catch (Exception e) {
log("managerServlet.check[" + displayPath + "]", e);
writer.println(sm.getString("managerServlet.exception",
e.toString()));
return;
}
context = (Context) host.findChild(path);
if (context != null && context.getConfigured()) {
writer.println(sm.getString("managerServlet.deployed", displayPath));
} else {
// Something failed
writer.println(sm.getString("managerServlet.deployFailed", displayPath));
}
}
/**
* Install an application for the specified path from the specified
* web application archive.
*
* @param writer Writer to render results to
* @param config URL of the context configuration file to be installed
* @param path Context path of the application to be installed
* @param war URL of the web application archive to be installed
* @param update true to override any existing webapp on the path
*/
protected void deploy(PrintWriter writer, String config,
String path, String war, boolean update) {
if (config != null && config.length() == 0) {
config = null;
}
if (war != null && war.length() == 0) {
war = null;
}
if (debug >= 1) {
if (config != null && config.length() > 0) {
if (war != null) {
log("install: Installing context configuration at '" +
config + "' from '" + war + "'");
} else {
log("install: Installing context configuration at '" +
config + "'");
}
} else {
if (path != null && path.length() > 0) {
log("install: Installing web application at '" + path +
"' from '" + war + "'");
} else {
log("install: Installing web application from '" + war + "'");
}
}
}
if (path == null || path.length() == 0 || !path.startsWith("/")) {
writer.println(sm.getString("managerServlet.invalidPath",
RequestUtil.filter(path)));
return;
}
String displayPath = path;
if("/".equals(path)) {
path = "";
}
// Check if app already exists, or undeploy it if updating
Context context = (Context) host.findChild(path);
if (update) {
if (context != null) {
undeploy(writer, displayPath);
}
context = (Context) host.findChild(path);
}
if (context != null) {
writer.println
(sm.getString("managerServlet.alreadyContext",
displayPath));
return;
}
if (config != null && (config.startsWith("file:"))) {
config = config.substring("file:".length());
}
if (war != null && (war.startsWith("file:"))) {
war = war.substring("file:".length());
}
try {
if (!isServiced(path)) {
addServiced(path);
try {
if (config != null) {
copy(new File(config),
new File(configBase, getConfigFile(path) + ".xml"));
}
if (war != null) {
if (war.endsWith(".war")) {
copy(new File(war),
new File(getAppBase(), getDocBase(path) + ".war"));
} else {
copy(new File(war),
new File(getAppBase(), getDocBase(path)));
}
}
// Perform new deployment
check(path);
} finally {
removeServiced(path);
}
}
context = (Context) host.findChild(path);
if (context != null && context.getConfigured()) {
writer.println(sm.getString("managerServlet.deployed", displayPath));
} else {
// Something failed
writer.println(sm.getString("managerServlet.deployFailed", displayPath));
}
} catch (Throwable t) {
log("ManagerServlet.install[" + displayPath + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Render a list of the currently active Contexts in our virtual host.
*
* @param writer Writer to render to
*/
protected void list(PrintWriter writer) {
if (debug >= 1)
log("list: Listing contexts for virtual host '" +
host.getName() + "'");
writer.println(sm.getString("managerServlet.listed",
host.getName()));
Container[] contexts = host.findChildren();
for (int i = 0; i < contexts.length; i++) {
Context context = (Context) contexts[i];
String displayPath = context.getPath();
if( displayPath.equals("") )
displayPath = "/";
if (context != null ) {
if (context.getAvailable()) {
writer.println(sm.getString("managerServlet.listitem",
displayPath,
"running",
"" + context.getManager().findSessions().length,
context.getDocBase()));
} else {
writer.println(sm.getString("managerServlet.listitem",
displayPath,
"stopped",
"0",
context.getDocBase()));
}
}
}
}
/**
* Reload the web application at the specified context path.
*
* @param writer Writer to render to
* @param path Context path of the application to be restarted
*/
protected void reload(PrintWriter writer, String path) {
if (debug >= 1)
log("restart: Reloading web application at '" + path + "'");
if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath",
RequestUtil.filter(path)));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
try {
Context context = (Context) host.findChild(path);
if (context == null) {
writer.println(sm.getString
("managerServlet.noContext",
RequestUtil.filter(displayPath)));
return;
}
// It isn't possible for the manager to reload itself
if (context.getPath().equals(this.context.getPath())) {
writer.println(sm.getString("managerServlet.noSelf"));
return;
}
context.reload();
writer.println
(sm.getString("managerServlet.reloaded", displayPath));
} catch (Throwable t) {
log("ManagerServlet.reload[" + displayPath + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Render a list of available global JNDI resources.
*
* @param type Fully qualified class name of the resource type of interest,
* or <code>null</code> to list resources of all types
*/
protected void resources(PrintWriter writer, String type) {
if (debug >= 1) {
if (type != null) {
log("resources: Listing resources of type " + type);
} else {
log("resources: Listing resources of all types");
}
}
// Is the global JNDI resources context available?
if (global == null) {
writer.println(sm.getString("managerServlet.noGlobal"));
return;
}
// Enumerate the global JNDI resources of the requested type
if (type != null) {
writer.println(sm.getString("managerServlet.resourcesType",
type));
} else {
writer.println(sm.getString("managerServlet.resourcesAll"));
}
Class clazz = null;
try {
if (type != null) {
clazz = Class.forName(type);
}
} catch (Throwable t) {
log("ManagerServlet.resources[" + type + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
return;
}
printResources(writer, "", global, type, clazz);
}
/**
* List the resources of the given context.
*/
protected void printResources(PrintWriter writer, String prefix,
javax.naming.Context namingContext,
String type, Class clazz) {
try {
NamingEnumeration items = namingContext.listBindings("");
while (items.hasMore()) {
Binding item = (Binding) items.next();
if (item.getObject() instanceof javax.naming.Context) {
printResources
(writer, prefix + item.getName() + "/",
(javax.naming.Context) item.getObject(), type, clazz);
} else {
if ((clazz != null) &&
(!(clazz.isInstance(item.getObject())))) {
continue;
}
writer.print(prefix + item.getName());
writer.print(':');
writer.print(item.getClassName());
// Do we want a description if available?
writer.println();
}
}
} catch (Throwable t) {
log("ManagerServlet.resources[" + type + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Render a list of security role names (and corresponding descriptions)
* from the <code>org.apache.catalina.UserDatabase</code> resource that is
* connected to the <code>users</code> resource reference. Typically, this
* will be the global user database, but can be adjusted if you have
* different user databases for different virtual hosts.
*
* @param writer Writer to render to
*/
protected void roles(PrintWriter writer) {
if (debug >= 1) {
log("roles: List security roles from user database");
}
// Look up the UserDatabase instance we should use
UserDatabase database = null;
try {
InitialContext ic = new InitialContext();
database = (UserDatabase) ic.lookup("java:comp/env/users");
} catch (NamingException e) {
writer.println(sm.getString("managerServlet.userDatabaseError"));
log("java:comp/env/users", e);
return;
}
if (database == null) {
writer.println(sm.getString("managerServlet.userDatabaseMissing"));
return;
}
// Enumerate the available roles
writer.println(sm.getString("managerServlet.rolesList"));
Iterator roles = database.getRoles();
if (roles != null) {
while (roles.hasNext()) {
Role role = (Role) roles.next();
writer.print(role.getRolename());
writer.print(':');
if (role.getDescription() != null) {
writer.print(role.getDescription());
}
writer.println();
}
}
}
/**
* Writes System OS and JVM properties.
* @param writer Writer to render to
*/
protected void serverinfo(PrintWriter writer) {
if (debug >= 1)
log("serverinfo");
try {
StringBuffer props = new StringBuffer();
props.append("OK - Server info");
props.append("\nTomcat Version: ");
props.append(ServerInfo.getServerInfo());
props.append("\nOS Name: ");
props.append(System.getProperty("os.name"));
props.append("\nOS Version: ");
props.append(System.getProperty("os.version"));
props.append("\nOS Architecture: ");
props.append(System.getProperty("os.arch"));
props.append("\nJVM Version: ");
props.append(System.getProperty("java.runtime.version"));
props.append("\nJVM Vendor: ");
props.append(System.getProperty("java.vm.vendor"));
writer.println(props.toString());
} catch (Throwable t) {
getServletContext().log("ManagerServlet.serverinfo",t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Session information for the web application at the specified context path.
* Displays a profile of session MaxInactiveInterval timeouts listing number
* of sessions for each 10 minute timeout interval up to 10 hours.
*
* @param writer Writer to render to
* @param path Context path of the application to list session information for
*/
protected void sessions(PrintWriter writer, String path) {
if (debug >= 1)
log("sessions: Session information for web application at '" + path + "'");
if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath",
RequestUtil.filter(path)));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
try {
Context context = (Context) host.findChild(path);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext",
RequestUtil.filter(displayPath)));
return;
}
writer.println(sm.getString("managerServlet.sessions", displayPath));
writer.println(sm.getString("managerServlet.sessiondefaultmax",
"" + context.getManager().getMaxInactiveInterval()/60));
Session [] sessions = context.getManager().findSessions();
int [] timeout = new int[60];
int notimeout = 0;
for (int i = 0; i < sessions.length; i++) {
int time = sessions[i].getMaxInactiveInterval()/(10*60);
if (time < 0)
notimeout++;
else if (time >= timeout.length)
timeout[timeout.length-1]++;
else
timeout[time]++;
}
if (timeout[0] > 0)
writer.println(sm.getString("managerServlet.sessiontimeout",
"<10", "" + timeout[0]));
for (int i = 1; i < timeout.length-1; i++) {
if (timeout[i] > 0)
writer.println(sm.getString("managerServlet.sessiontimeout",
"" + (i)*10 + " - <" + (i+1)*10,
"" + timeout[i]));
}
if (timeout[timeout.length-1] > 0)
writer.println(sm.getString("managerServlet.sessiontimeout",
">=" + timeout.length*10,
"" + timeout[timeout.length-1]));
if (notimeout > 0)
writer.println(sm.getString("managerServlet.sessiontimeout",
"unlimited","" + notimeout));
} catch (Throwable t) {
log("ManagerServlet.sessions[" + displayPath + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Start the web application at the specified context path.
*
* @param writer Writer to render to
* @param path Context path of the application to be started
*/
protected void start(PrintWriter writer, String path) {
if (debug >= 1)
log("start: Starting web application at '" + path + "'");
if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath",
RequestUtil.filter(path)));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
try {
Context context = (Context) host.findChild(path);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext",
RequestUtil.filter(displayPath)));
return;
}
((Lifecycle) context).start();
if (context.getAvailable())
writer.println
(sm.getString("managerServlet.started", displayPath));
else
writer.println
(sm.getString("managerServlet.startFailed", displayPath));
} catch (Throwable t) {
getServletContext().log
(sm.getString("managerServlet.startFailed", displayPath), t);
writer.println
(sm.getString("managerServlet.startFailed", displayPath));
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Stop the web application at the specified context path.
*
* @param writer Writer to render to
* @param path Context path of the application to be stopped
*/
protected void stop(PrintWriter writer, String path) {
if (debug >= 1)
log("stop: Stopping web application at '" + path + "'");
if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath",
RequestUtil.filter(path)));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
try {
Context context = (Context) host.findChild(path);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext",
RequestUtil.filter(displayPath)));
return;
}
// It isn't possible for the manager to stop itself
if (context.getPath().equals(this.context.getPath())) {
writer.println(sm.getString("managerServlet.noSelf"));
return;
}
((Lifecycle) context).stop();
writer.println(sm.getString("managerServlet.stopped", displayPath));
} catch (Throwable t) {
log("ManagerServlet.stop[" + displayPath + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
/**
* Undeploy the web application at the specified context path.
*
* @param writer Writer to render to
* @param path Context path of the application to be removed
*/
protected void undeploy(PrintWriter writer, String path) {
if (debug >= 1)
log("undeploy: Undeploying web application at '" + path + "'");
if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath",
RequestUtil.filter(path)));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";
try {
// Validate the Context of the specified application
Context context = (Context) host.findChild(path);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext",
RequestUtil.filter(displayPath)));
return;
}
// Identify the appBase of the owning Host of this Context (if any)
String appBase = null;
File appBaseDir = null;
if (context.getParent() instanceof Host) {
appBase = ((Host) context.getParent()).getAppBase();
appBaseDir = new File(appBase);
if (!appBaseDir.isAbsolute()) {
appBaseDir = new File(System.getProperty("catalina.base"),
appBase);
}
}
if (!isServiced(path)) {
addServiced(path);
try {
// Try to stop the context first to be nicer
((Lifecycle) context).stop();
} catch (Throwable t) {
// Ignore
}
try {
File war = new File(getAppBase(), getDocBase(path) + ".war");
File dir = new File(getAppBase(), getDocBase(path));
File xml = new File(configBase, getConfigFile(path) + ".xml");
if (war.exists()) {
war.delete();
} else if (dir.exists()) {
undeployDir(dir);
} else {
xml.delete();
}
// Perform new deployment
check(path);
} finally {
removeServiced(path);
}
}
writer.println(sm.getString("managerServlet.undeployed",
displayPath));
} catch (Throwable t) {
log("ManagerServlet.undeploy[" + displayPath + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}
}
// -------------------------------------------------------- Support Methods
/**
* Given a context path, get the config file name.
*/
protected String getConfigFile(String path) {
String basename = null;
if (path.equals("")) {
basename = "ROOT";
} else {
basename = path.substring(1).replace('/', '#');
}
return (basename);
}
/**
* Given a context path, get the config file name.
*/
protected String getDocBase(String path) {
String basename = null;
if (path.equals("")) {
basename = "ROOT";
} else {
basename = path.substring(1);
}
return (basename);
}
/**
* Return a File object representing the "application root" directory
* for our associated Host.
*/
protected File getAppBase() {
if (appBase != null) {
return appBase;
}
File file = new File(host.getAppBase());
if (!file.isAbsolute())
file = new File(System.getProperty("catalina.base"),
host.getAppBase());
try {
appBase = file.getCanonicalFile();
} catch (IOException e) {
appBase = file;
}
return (appBase);
}
/**
* Invoke the check method on the deployer.
*/
protected void check(String name)
throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
mBeanServer.invoke(oname, "check", params, signature);
}
/**
* Invoke the check method on the deployer.
*/
protected boolean isServiced(String name)
throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
Boolean result =
(Boolean) mBeanServer.invoke(oname, "isServiced", params, signature);
return result.booleanValue();
}
/**
* Invoke the check method on the deployer.
*/
protected void addServiced(String name)
throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
mBeanServer.invoke(oname, "addServiced", params, signature);
}
/**
* Invoke the check method on the deployer.
*/
protected void removeServiced(String name)
throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
mBeanServer.invoke(oname, "removeServiced", params, signature);
}
/**
* Delete the specified directory, including all of its contents and
* subdirectories recursively.
*
* @param dir File object representing the directory to be deleted
*/
protected void undeployDir(File dir) {
String files[] = dir.list();
if (files == null) {
files = new String[0];
}
for (int i = 0; i < files.length; i++) {
File file = new File(dir, files[i]);
if (file.isDirectory()) {
undeployDir(file);
} else {
file.delete();
}
}
dir.delete();
}
/**
* Upload the WAR file included in this request, and store it at the
* specified file location.
*
* @param request The servlet request we are processing
* @param war The file into which we should store the uploaded WAR
*
* @exception IOException if an I/O error occurs during processing
*/
protected void uploadWar(HttpServletRequest request, File war)
throws IOException {
war.delete();
ServletInputStream istream = null;
BufferedOutputStream ostream = null;
try {
istream = request.getInputStream();
ostream =
new BufferedOutputStream(new FileOutputStream(war), 1024);
byte buffer[] = new byte[1024];
while (true) {
int n = istream.read(buffer);
if (n < 0) {
break;
}
ostream.write(buffer, 0, n);
}
ostream.flush();
ostream.close();
ostream = null;
istream.close();
istream = null;
} catch (IOException e) {
war.delete();
throw e;
} finally {
if (ostream != null) {
try {
ostream.close();
} catch (Throwable t) {
;
}
ostream = null;
}
if (istream != null) {
try {
istream.close();
} catch (Throwable t) {
;
}
istream = null;
}
}
}
/**
* Copy the specified file or directory to the destination.
*
* @param src File object representing the source
* @param dest File object representing the destination
*/
public static boolean copy(File src, File dest) {
return copyInternal(src, dest, new byte[4096]);
}
/**
* Copy the specified file or directory to the destination.
*
* @param src File object representing the source
* @param dest File object representing the destination
*/
public static boolean copyInternal(File src, File dest, byte[] buf) {
boolean result = true;
String files[] = null;
if (src.isDirectory()) {
files = src.list();
result = dest.mkdir();
} else {
files = new String[1];
files[0] = "";
}
if (files == null) {
files = new String[0];
}
for (int i = 0; (i < files.length) && result; i++) {
File fileSrc = new File(src, files[i]);
File fileDest = new File(dest, files[i]);
if (fileSrc.isDirectory()) {
result = copyInternal(fileSrc, fileDest, buf);
} else {
FileInputStream is = null;
FileOutputStream os = null;
try {
is = new FileInputStream(fileSrc);
os = new FileOutputStream(fileDest);
int len = 0;
while (true) {
len = is.read(buf);
if (len == -1)
break;
os.write(buf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
result = false;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
}
}
}
}
}
return result;
}
}