From: remm
+* The difference between the
+* However if you use a software that parses the output of
+*
+ * This servlet examines the value returned by Use The syntax of the URL for a web application archive must conform to one
+ * of the following patterns to be successfully deployed:
+ * NOTE - 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.
+ *
+ * NOTE - 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.
+ *
+ * The following servlet initialization parameters are recognized:
+ * ");
+ writer.print(" Physical memory: ");
+ writer.print(formatSize(new Long(result[0]), true));
+ writer.print(" Available memory: ");
+ writer.print(formatSize(new Long(result[1]), true));
+ writer.print(" Total page file: ");
+ writer.print(formatSize(new Long(result[2]), true));
+ writer.print(" Free page file: ");
+ writer.print(formatSize(new Long(result[3]), true));
+ writer.print(" Memory load: ");
+ writer.print(new Long(result[6]));
+ writer.print(" ");
+ writer.print(" Free memory: ");
+ writer.print(formatSize
+ (new Long(Runtime.getRuntime().freeMemory()), true));
+ writer.print(" Total memory: ");
+ writer.print(formatSize
+ (new Long(Runtime.getRuntime().totalMemory()), true));
+ writer.print(" Max memory: ");
+ writer.print(formatSize
+ (new Long(Runtime.getRuntime().maxMemory()), true));
+ writer.print(" ");
+ writer.print(" Max threads: ");
+ writer.print(mBeanServer.getAttribute(tpName, "maxThreads"));
+ writer.print(" Min spare threads: ");
+ writer.print(mBeanServer.getAttribute(tpName, "minSpareThreads"));
+ writer.print(" Max spare threads: ");
+ writer.print(mBeanServer.getAttribute(tpName, "maxSpareThreads"));
+ writer.print(" Current thread count: ");
+ writer.print(mBeanServer.getAttribute(tpName, "currentThreadCount"));
+ writer.print(" Current thread busy: ");
+ writer.print(mBeanServer.getAttribute(tpName, "currentThreadsBusy"));
+ try {
+ Object value = mBeanServer.getAttribute(tpName, "keepAliveCount");
+ writer.print(" Keeped alive sockets count: ");
+ writer.print(value);
+ } catch (Exception e) {
+ // Ignore
+ }
+
+ writer.print(" ");
+ writer.print("P: Parse and prepare request S: Service F: Finishing R: Ready K: Keepalive");
+ writer.print(" ");
+ int count = 0;
+ Iterator iterator = hostsON.iterator();
+ while (iterator.hasNext()) {
+ ObjectName contextON = (ObjectName) iterator.next();
+ String webModuleName = contextON.getKeyProperty("name");
+ if (webModuleName.startsWith("//")) {
+ webModuleName = webModuleName.substring(2);
+ }
+ int slash = webModuleName.indexOf("/");
+ if (slash == -1) {
+ count++;
+ continue;
+ }
+
+ writer.print("");
+ writer.print(webModuleName);
+ writer.print("");
+ if (iterator.hasNext()) {
+ writer.print(" ");
+ Object startTime = mBeanServer.getAttribute(objectName,
+ "startTime");
+ writer.print(" Start time: " +
+ new Date(((Long) startTime).longValue()));
+ writer.print(" Startup time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "startupTime"), false));
+ writer.print(" TLD scan time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "tldScanTime"), false));
+ if (managerON != null) {
+ writeManager(writer, managerON, mBeanServer, mode);
+ }
+ if (jspMonitorONs != null) {
+ writeJspMonitor(writer, jspMonitorONs, mBeanServer, mode);
+ }
+ writer.print(" ");
+ writer.print(" Processing time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "processingTime"), true));
+ writer.print(" Max time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "maxTime"), false));
+ writer.print(" Request count: ");
+ writer.print(mBeanServer.getAttribute(objectName, "requestCount"));
+ writer.print(" Error count: ");
+ writer.print(mBeanServer.getAttribute(objectName, "errorCount"));
+ writer.print(" Load time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "loadTime"), false));
+ writer.print(" Classloading time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "classLoadTime"), false));
+ writer.print("
+* The difference between the
+* However if you use a software that parses the output of
+*
+ * This servlet examines the value returned by
+ * NOTE - Attempting to stop or remove the host containing
+ * this servlet itself will not succeed. Therefore, this servlet should
+ * generally be deployed in a separate virtual host.
+ *
+ * NOTE - 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.
+ *
+ * The following servlet initialization parameters are recognized:
+ * The default implementation of the
+ * {@link org.apache.tomcat.util.http.fileupload.FileItem FileItem} interface.
+ *
+ * After retrieving an instance of this class from a {@link
+ * org.apache.tomcat.util.http.fileupload.DiskFileUpload DiskFileUpload} instance (see
+ * {@link org.apache.tomcat.util.http.fileupload.DiskFileUpload
+ * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may
+ * either request all contents of file at once using {@link #get()} or
+ * request an {@link java.io.InputStream InputStream} with
+ * {@link #getInputStream()} and process the file without attempting to load
+ * it into memory, which may come handy with large files.
+ *
+ * @author Rafal Krzewski
+ * @author Sean Legassick
+ * @author Jason van Zyl
+ * @author John McNally
+ * @author Martin Cooper
+ * @author Sean C. Sullivan
+ *
+ * @version $Id: DefaultFileItem.java,v 1.21 2003/06/24 05:45:15 martinc Exp $
+ */
+public class DefaultFileItem
+ implements FileItem
+{
+
+ // ----------------------------------------------------------- Data members
+
+
+ /**
+ * Counter used in unique identifier generation.
+ */
+ private static int counter = 0;
+
+
+ /**
+ * The name of the form field as provided by the browser.
+ */
+ private String fieldName;
+
+
+ /**
+ * The content type passed by the browser, or
+ * This implementation first attempts to rename the uploaded item to the
+ * specified destination file, if the item was originally written to disk.
+ * Otherwise, the data will be copied to the specified file.
+ *
+ * This method is only guaranteed to work once, the first time it
+ * is invoked for a particular item. This is because, in the event that the
+ * method renames a temporary file, that file will no longer be available
+ * to copy or rename again at a later time.
+ *
+ * @param file The The default {@link org.apache.tomcat.util.http.fileupload.FileItemFactory}
+ * implementation. This implementation creates
+ * {@link org.apache.tomcat.util.http.fileupload.FileItem} instances which keep their
+ * content either in memory, for smaller items, or in a temporary file on disk,
+ * for larger items. The size threshold, above which content will be stored on
+ * disk, is configurable, as is the directory in which temporary files will be
+ * created. If not otherwise configured, the default configuration values are as
+ * follows:
+ * \n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ "
\n" +
+ " \n" +
+ " \n" +
+ "
\n" +
+ " \n" +
+ "
\n" +
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ " {1}\n" +
+ " \n" +
+ "
\n" +
+ "\n";
+
+ public static final String MESSAGE_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "" +
+ "{0} \n" +
+ " \n" +
+ " {1}
\n" +
+ "\n";
+
+ public static final String MANAGER_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ " {0} \n" +
+ "\n" +
+ " \n" +
+ "{2} \n" +
+ " {4} \n" +
+ " {6} \n" +
+ " {8} \n" +
+ "
\n" +
+ "\n";
+
+ public static final String SERVER_HEADER_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n";
+
+ public static final String SERVER_ROW_SECTION =
+ "{1} \n" +
+ " {2} \n" +
+ " {3} \n" +
+ " {4} \n" +
+ " {5} \n" +
+ " {6} \n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ " {1} \n" +
+ " {2} \n" +
+ " {3} \n" +
+ " {4} \n" +
+ " {5} \n" +
+ "
\n" +
+ "\n";
+
+ public static final String HTML_TAIL_SECTION =
+ "
\n" +
+ "ManagerServlet and this
+* Servlet is that this Servlet prints out a HTML interface which
+* makes it easier to administrate.
+* ManagerServletManagerServlet
+*
+* @author Bip Thelin
+* @author Malcolm Edgar
+* @author Glenn L. Nielsen
+* @version $Revision: 326772 $, $Date: 2005-10-20 03:37:02 +0200 (jeu., 20 oct. 2005) $
+* @see ManagerServlet
+*/
+
+public final class HTMLManagerServlet extends ManagerServlet {
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * 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 {
+
+ // Identify the request parameters that we need
+ String command = request.getPathInfo();
+
+ String path = request.getParameter("path");
+ String deployPath = request.getParameter("deployPath");
+ String deployConfig = request.getParameter("deployConfig");
+ String deployWar = request.getParameter("deployWar");
+
+ // Prepare our output writer to generate the response message
+ response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+ String message = "";
+ // Process the requested command
+ if (command == null || command.equals("/")) {
+ } else if (command.equals("/deploy")) {
+ message = deployInternal(deployConfig, deployPath, deployWar);
+ } else if (command.equals("/list")) {
+ } else if (command.equals("/reload")) {
+ message = reload(path);
+ } else if (command.equals("/undeploy")) {
+ message = undeploy(path);
+ } else if (command.equals("/sessions")) {
+ message = sessions(path);
+ } else if (command.equals("/start")) {
+ message = start(path);
+ } else if (command.equals("/stop")) {
+ message = stop(path);
+ } else {
+ message =
+ sm.getString("managerServlet.unknownCommand",
+ RequestUtil.filter(command));
+ }
+
+ list(request, response, message);
+ }
+
+ /**
+ * Process a POST 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 doPost(HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException, ServletException {
+
+ // Identify the request parameters that we need
+ String command = request.getPathInfo();
+
+ if (command == null || !command.equals("/upload")) {
+ doGet(request,response);
+ return;
+ }
+
+ // Prepare our output writer to generate the response message
+ response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+ String message = "";
+
+ // Create a new file upload handler
+ DiskFileUpload upload = new DiskFileUpload();
+
+ // Get the tempdir
+ File tempdir = (File) getServletContext().getAttribute
+ ("javax.servlet.context.tempdir");
+ // Set upload parameters
+ upload.setSizeMax(-1);
+ upload.setRepositoryPath(tempdir.getCanonicalPath());
+
+ // Parse the request
+ String basename = null;
+ String war = null;
+ FileItem warUpload = null;
+ try {
+ List items = upload.parseRequest(request);
+
+ // Process the uploaded fields
+ Iterator iter = items.iterator();
+ while (iter.hasNext()) {
+ FileItem item = (FileItem) iter.next();
+
+ if (!item.isFormField()) {
+ if (item.getFieldName().equals("deployWar") &&
+ warUpload == null) {
+ warUpload = item;
+ } else {
+ item.delete();
+ }
+ }
+ }
+ while (true) {
+ if (warUpload == null) {
+ message = sm.getString
+ ("htmlManagerServlet.deployUploadNoFile");
+ break;
+ }
+ war = warUpload.getName();
+ if (!war.toLowerCase().endsWith(".war")) {
+ message = sm.getString
+ ("htmlManagerServlet.deployUploadNotWar",war);
+ break;
+ }
+ // Get the filename if uploaded name includes a path
+ if (war.lastIndexOf('\\') >= 0) {
+ war = war.substring(war.lastIndexOf('\\') + 1);
+ }
+ if (war.lastIndexOf('/') >= 0) {
+ war = war.substring(war.lastIndexOf('/') + 1);
+ }
+ // Identify the appBase of the owning Host of this Context
+ // (if any)
+ basename = war.substring(0, war.toLowerCase().indexOf(".war"));
+ File file = new File(getAppBase(), war);
+ if (file.exists()) {
+ message = sm.getString
+ ("htmlManagerServlet.deployUploadWarExists",war);
+ break;
+ }
+ String path = null;
+ if (basename.equals("ROOT")) {
+ path = "";
+ } else {
+ path = "/" + basename;
+ }
+
+ if (!isServiced(path)) {
+ addServiced(path);
+ try {
+ warUpload.write(file);
+ // Perform new deployment
+ check(path);
+ } finally {
+ removeServiced(path);
+ }
+ }
+ break;
+ }
+ } catch(Exception e) {
+ message = sm.getString
+ ("htmlManagerServlet.deployUploadFail", e.getMessage());
+ log(message, e);
+ } finally {
+ if (warUpload != null) {
+ warUpload.delete();
+ }
+ warUpload = null;
+ }
+
+ list(request, response, message);
+ }
+
+ /**
+ * Deploy an application for the specified path from the specified
+ * web application archive.
+ *
+ * @param config URL of the context configuration file to be deployed
+ * @param path Context path of the application to be deployed
+ * @param war URL of the web application archive to be deployed
+ * @return message String
+ */
+ protected String deployInternal(String config, String path, String war) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.deploy(printWriter, config, path, war, false);
+
+ return stringWriter.toString();
+ }
+
+ /**
+ * Render a HTML list of the currently active Contexts in our virtual host,
+ * and memory and server status information.
+ *
+ * @param request The request
+ * @param response The response
+ * @param message a message to display
+ */
+ public void list(HttpServletRequest request,
+ HttpServletResponse response,
+ String message) throws IOException {
+
+ if (debug >= 1)
+ log("list: Listing contexts for virtual host '" +
+ host.getName() + "'");
+
+ PrintWriter writer = response.getWriter();
+
+ // HTML Header Section
+ writer.print(Constants.HTML_HEADER_SECTION);
+
+ // Body Header Section
+ Object[] args = new Object[2];
+ args[0] = request.getContextPath();
+ args[1] = sm.getString("htmlManagerServlet.title");
+ writer.print(MessageFormat.format
+ (Constants.BODY_HEADER_SECTION, args));
+
+ // Message Section
+ args = new Object[3];
+ args[0] = sm.getString("htmlManagerServlet.messageLabel");
+ args[1] = (message == null || message.length() == 0) ? "OK" : message;
+ writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args));
+
+ // Manager Section
+ args = new Object[9];
+ args[0] = sm.getString("htmlManagerServlet.manager");
+ args[1] = response.encodeURL(request.getContextPath() + "/html/list");
+ args[2] = sm.getString("htmlManagerServlet.list");
+ args[3] = response.encodeURL
+ (request.getContextPath() + "/" +
+ sm.getString("htmlManagerServlet.helpHtmlManagerFile"));
+ args[4] = sm.getString("htmlManagerServlet.helpHtmlManager");
+ args[5] = response.encodeURL
+ (request.getContextPath() + "/" +
+ sm.getString("htmlManagerServlet.helpManagerFile"));
+ args[6] = sm.getString("htmlManagerServlet.helpManager");
+ args[7] = response.encodeURL
+ (request.getContextPath() + "/status");
+ args[8] = sm.getString("statusServlet.title");
+ writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
+
+ // Apps Header Section
+ args = new Object[6];
+ args[0] = sm.getString("htmlManagerServlet.appsTitle");
+ args[1] = sm.getString("htmlManagerServlet.appsPath");
+ args[2] = sm.getString("htmlManagerServlet.appsName");
+ args[3] = sm.getString("htmlManagerServlet.appsAvailable");
+ args[4] = sm.getString("htmlManagerServlet.appsSessions");
+ args[5] = sm.getString("htmlManagerServlet.appsTasks");
+ writer.print(MessageFormat.format(APPS_HEADER_SECTION, args));
+
+ // Apps Row Section
+ // Create sorted map of deployed applications context paths.
+ Container children[] = host.findChildren();
+ String contextPaths[] = new String[children.length];
+ for (int i = 0; i < children.length; i++)
+ contextPaths[i] = children[i].getName();
+
+ TreeMap sortedContextPathsMap = new TreeMap();
+
+ for (int i = 0; i < contextPaths.length; i++) {
+ String displayPath = contextPaths[i];
+ sortedContextPathsMap.put(displayPath, contextPaths[i]);
+ }
+
+ String appsStart = sm.getString("htmlManagerServlet.appsStart");
+ String appsStop = sm.getString("htmlManagerServlet.appsStop");
+ String appsReload = sm.getString("htmlManagerServlet.appsReload");
+ String appsUndeploy = sm.getString("htmlManagerServlet.appsUndeploy");
+
+ Iterator iterator = sortedContextPathsMap.entrySet().iterator();
+ boolean isHighlighted = true;
+ String highlightColor = null;
+
+ while (iterator.hasNext()) {
+ // Bugzilla 34818, alternating row colors
+ isHighlighted = !isHighlighted;
+ if(isHighlighted) {
+ highlightColor = "#C3F3C3";
+ } else {
+ highlightColor = "#FFFFFF";
+ }
+
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String displayPath = (String) entry.getKey();
+ String contextPath = (String) entry.getKey();
+ Context context = (Context) host.findChild(contextPath);
+ if (displayPath.equals("")) {
+ displayPath = "/";
+ }
+
+ if (context != null ) {
+ args = new Object[6];
+ args[0] = displayPath;
+ args[1] = context.getDisplayName();
+ if (args[1] == null) {
+ args[1] = " ";
+ }
+ args[2] = new Boolean(context.getAvailable());
+ args[3] = response.encodeURL
+ (request.getContextPath() +
+ "/html/sessions?path=" + displayPath);
+ if (context.getManager() != null) {
+ args[4] = new Integer
+ (context.getManager().getActiveSessions());
+ } else {
+ args[4] = new Integer(0);
+ }
+
+ args[5] = highlightColor;
+
+ writer.print
+ (MessageFormat.format(APPS_ROW_DETAILS_SECTION, args));
+
+ args = new Object[9];
+ args[0] = response.encodeURL
+ (request.getContextPath() +
+ "/html/start?path=" + displayPath);
+ args[1] = appsStart;
+ args[2] = response.encodeURL
+ (request.getContextPath() +
+ "/html/stop?path=" + displayPath);
+ args[3] = appsStop;
+ args[4] = response.encodeURL
+ (request.getContextPath() +
+ "/html/reload?path=" + displayPath);
+ args[5] = appsReload;
+ args[6] = response.encodeURL
+ (request.getContextPath() +
+ "/html/undeploy?path=" + displayPath);
+ args[7] = appsUndeploy;
+
+ args[8] = highlightColor;
+
+ if (context.getPath().equals(this.context.getPath())) {
+ writer.print(MessageFormat.format(
+ MANAGER_APP_ROW_BUTTON_SECTION, args));
+ } else if (context.getAvailable()) {
+ writer.print(MessageFormat.format(
+ STARTED_APPS_ROW_BUTTON_SECTION, args));
+ } else {
+ writer.print(MessageFormat.format(
+ STOPPED_APPS_ROW_BUTTON_SECTION, args));
+ }
+
+ }
+ }
+
+ // Deploy Section
+ args = new Object[7];
+ args[0] = sm.getString("htmlManagerServlet.deployTitle");
+ args[1] = sm.getString("htmlManagerServlet.deployServer");
+ args[2] = response.encodeURL(request.getContextPath() + "/html/deploy");
+ args[3] = sm.getString("htmlManagerServlet.deployPath");
+ args[4] = sm.getString("htmlManagerServlet.deployConfig");
+ args[5] = sm.getString("htmlManagerServlet.deployWar");
+ args[6] = sm.getString("htmlManagerServlet.deployButton");
+ writer.print(MessageFormat.format(DEPLOY_SECTION, args));
+
+ args = new Object[4];
+ args[0] = sm.getString("htmlManagerServlet.deployUpload");
+ args[1] = response.encodeURL(request.getContextPath() + "/html/upload");
+ args[2] = sm.getString("htmlManagerServlet.deployUploadFile");
+ args[3] = sm.getString("htmlManagerServlet.deployButton");
+ writer.print(MessageFormat.format(UPLOAD_SECTION, args));
+
+ // Server Header Section
+ args = new Object[7];
+ args[0] = sm.getString("htmlManagerServlet.serverTitle");
+ args[1] = sm.getString("htmlManagerServlet.serverVersion");
+ args[2] = sm.getString("htmlManagerServlet.serverJVMVersion");
+ args[3] = sm.getString("htmlManagerServlet.serverJVMVendor");
+ args[4] = sm.getString("htmlManagerServlet.serverOSName");
+ args[5] = sm.getString("htmlManagerServlet.serverOSVersion");
+ args[6] = sm.getString("htmlManagerServlet.serverOSArch");
+ writer.print(MessageFormat.format
+ (Constants.SERVER_HEADER_SECTION, args));
+
+ // Server Row Section
+ args = new Object[6];
+ args[0] = ServerInfo.getServerInfo();
+ args[1] = System.getProperty("java.runtime.version");
+ args[2] = System.getProperty("java.vm.vendor");
+ args[3] = System.getProperty("os.name");
+ args[4] = System.getProperty("os.version");
+ args[5] = System.getProperty("os.arch");
+ writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
+
+ // HTML Tail Section
+ writer.print(Constants.HTML_TAIL_SECTION);
+
+ // Finish up the response
+ writer.flush();
+ writer.close();
+ }
+
+ /**
+ * Reload the web application at the specified context path.
+ *
+ * @see ManagerServlet#reload(PrintWriter, String)
+ *
+ * @param path Context path of the application to be restarted
+ * @return message String
+ */
+ protected String reload(String path) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.reload(printWriter, path);
+
+ return stringWriter.toString();
+ }
+
+ /**
+ * Undeploy the web application at the specified context path.
+ *
+ * @see ManagerServlet#undeploy(PrintWriter, String)
+ *
+ * @param path Context path of the application to be undeployd
+ * @return message String
+ */
+ protected String undeploy(String path) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.undeploy(printWriter, path);
+
+ return stringWriter.toString();
+ }
+
+ /**
+ * Display session information and invoke list.
+ *
+ * @see ManagerServlet#sessions(PrintWriter, String)
+ *
+ * @param path Context path of the application to list session information
+ * @return message String
+ */
+ public String sessions(String path) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.sessions(printWriter, path);
+
+ return stringWriter.toString();
+ }
+
+ /**
+ * Start the web application at the specified context path.
+ *
+ * @see ManagerServlet#start(PrintWriter, String)
+ *
+ * @param path Context path of the application to be started
+ * @return message String
+ */
+ public String start(String path) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.start(printWriter, path);
+
+ return stringWriter.toString();
+ }
+
+ /**
+ * Stop the web application at the specified context path.
+ *
+ * @see ManagerServlet#stop(PrintWriter, String)
+ *
+ * @param path Context path of the application to be stopped
+ * @return message String
+ */
+ protected String stop(String path) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.stop(printWriter, path);
+
+ return stringWriter.toString();
+ }
+
+ // ------------------------------------------------------ Private Constants
+
+ // These HTML sections are broken in relatively small sections, because of
+ // limited number of subsitutions MessageFormat can process
+ // (maximium of 10).
+
+ private static final String APPS_HEADER_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n";
+
+ private static final String APPS_ROW_DETAILS_SECTION =
+ "{1} \n" +
+ " {2} \n" +
+ " {3} \n" +
+ " {4} \n" +
+ " {5} \n" +
+ "\n" +
+ " \n";
+
+ private static final String STARTED_APPS_ROW_BUTTON_SECTION =
+ " {0} \n" +
+ " {1} \n" +
+ " {2} \n" +
+ " {4} \n";
+
+ private static final String MANAGER_APP_ROW_BUTTON_SECTION =
+ " \n" +
+ " \n" +
+ " {1} \n" +
+ " {3} \n" +
+ " {5} \n" +
+ " {7} \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " {1} \n" +
+ " {3} \n" +
+ " {5} \n" +
+ " {7} \n" +
+ " \n" +
+ " \n" +
+ "\n";
+
+ private static final String STOPPED_APPS_ROW_BUTTON_SECTION =
+ " \n" +
+ " \n" +
+ " {1} \n" +
+ " {3} \n" +
+ " {5} \n" +
+ " {7} \n" +
+ " \n" +
+ " \n" +
+ "\n";
+
+ private static final String DEPLOY_SECTION =
+ "
\n" +
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n" +
+ "{1} \n" +
+ "\n" +
+ " \n";
+
+ private static final String UPLOAD_SECTION =
+ "\n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ "
\n" +
+ "\n";
+
+}
diff --git a/java/org/apache/catalina/manager/JMXProxyServlet.java b/java/org/apache/catalina/manager/JMXProxyServlet.java
new file mode 100644
index 000000000..7509ed85f
--- /dev/null
+++ b/java/org/apache/catalina/manager/JMXProxyServlet.java
@@ -0,0 +1,230 @@
+/*
+ * 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.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.MBeanInfo;
+import javax.management.MBeanAttributeInfo;
+import javax.management.Attribute;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tomcat.util.modeler.Registry;
+
+/**
+ * This servlet will dump JMX attributes in a simple format
+ * and implement proxy services for modeler.
+ *
+ * @author Costin Manolache
+ */
+public class JMXProxyServlet extends HttpServlet {
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * MBean server.
+ */
+ protected MBeanServer mBeanServer = null;
+ protected Registry registry;
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Initialize this servlet.
+ */
+ public void init() throws ServletException {
+ // Retrieve the MBean server
+ registry = Registry.getRegistry(null, null);
+ mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+ }
+
+
+ /**
+ * 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
+ {
+
+ response.setContentType("text/plain");
+
+ PrintWriter writer = response.getWriter();
+ String qryString= request.getQueryString();
+
+ if( mBeanServer==null ) {
+ writer.println("Error - No mbean server");
+ return;
+ }
+
+ String qry=request.getParameter("set");
+ if( qry!= null ) {
+ String name=request.getParameter("att");
+ String val=request.getParameter("val");
+
+ setAttribute( writer, qry, name, val );
+ return;
+ }
+ qry=request.getParameter("get");
+ if( qry!= null ) {
+ String name=request.getParameter("att");
+ getAttribute( writer, qry, name );
+ return;
+ }
+ qry=request.getParameter("qry");
+ if( qry == null ) {
+ qry = "*:*";
+ }
+
+ listBeans( writer, qry );
+
+ }
+
+ public void getAttribute(PrintWriter writer, String onameStr, String att) {
+ try {
+ ObjectName oname = new ObjectName(onameStr);
+ Object value = mBeanServer.getAttribute(oname, att);
+ writer.println("OK - Attribute get '" + onameStr + "' - " + att + "= " + value.toString() );
+ } catch (Exception ex) {
+ writer.println("Error - " + ex.toString());
+ }
+ }
+
+ public void setAttribute( PrintWriter writer,
+ String onameStr, String att, String val )
+ {
+ try {
+ ObjectName oname=new ObjectName( onameStr );
+ String type=registry.getType(oname, att);
+ Object valueObj=registry.convertValue(type, val );
+ mBeanServer.setAttribute( oname, new Attribute(att, valueObj));
+ writer.println("OK - Attribute set");
+ } catch( Exception ex ) {
+ writer.println("Error - " + ex.toString());
+ }
+ }
+
+ public void listBeans( PrintWriter writer, String qry )
+ {
+
+ Set names = null;
+ try {
+ names=mBeanServer.queryNames(new ObjectName(qry), null);
+ writer.println("OK - Number of results: " + names.size());
+ writer.println();
+ } catch (Exception e) {
+ writer.println("Error - " + e.toString());
+ return;
+ }
+
+ Iterator it=names.iterator();
+ while( it.hasNext()) {
+ ObjectName oname=(ObjectName)it.next();
+ writer.println( "Name: " + oname.toString());
+
+ try {
+ MBeanInfo minfo=mBeanServer.getMBeanInfo(oname);
+ // can't be null - I thinl
+ String code=minfo.getClassName();
+ if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
+ code=(String)mBeanServer.getAttribute(oname, "modelerType");
+ }
+ writer.println("modelerType: " + code);
+
+ MBeanAttributeInfo attrs[]=minfo.getAttributes();
+ Object value=null;
+
+ for( int i=0; i< attrs.length; i++ ) {
+ if( ! attrs[i].isReadable() ) continue;
+ if( ! isSupported( attrs[i].getType() )) continue;
+ String attName=attrs[i].getName();
+ if( attName.indexOf( "=") >=0 ||
+ attName.indexOf( ":") >=0 ||
+ attName.indexOf( " ") >=0 ) {
+ continue;
+ }
+
+ try {
+ value=mBeanServer.getAttribute(oname, attName);
+ } catch( Throwable t) {
+ System.out.println("Error getting attribute " + oname +
+ " " + attName + " " + t.toString());
+ continue;
+ }
+ if( value==null ) continue;
+ if( "modelerType".equals( attName)) continue;
+ String valueString=value.toString();
+ writer.println( attName + ": " + escape(valueString));
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ writer.println();
+ }
+
+ }
+
+ public String escape(String value) {
+ // The only invalid char is \n
+ // We also need to keep the string short and split it with \nSPACE
+ // XXX TODO
+ int idx=value.indexOf( "\n" );
+ if( idx < 0 ) return value;
+
+ int prev=0;
+ StringBuffer sb=new StringBuffer();
+ while( idx >= 0 ) {
+ appendHead(sb, value, prev, idx-1);
+
+ sb.append( "\\n\n ");
+ prev=idx+1;
+ if( idx==value.length() -1 ) break;
+ idx=value.indexOf('\n', idx+1);
+ }
+ if( prev < value.length() )
+ appendHead( sb, value, prev, value.length());
+ return sb.toString();
+ }
+
+ private void appendHead( StringBuffer sb, String value, int start, int end) {
+ int pos=start;
+ while( end-pos > 78 ) {
+ sb.append( value.substring(pos, pos+78));
+ sb.append( "\n ");
+ pos=pos+78;
+ }
+ sb.append( value.substring(pos,end));
+ }
+
+ public boolean isSupported( String type ) {
+ return true;
+ }
+}
diff --git a/java/org/apache/catalina/manager/LocalStrings.properties b/java/org/apache/catalina/manager/LocalStrings.properties
new file mode 100644
index 000000000..feeabb131
--- /dev/null
+++ b/java/org/apache/catalina/manager/LocalStrings.properties
@@ -0,0 +1,81 @@
+htmlManagerServlet.appsAvailable=Running
+htmlManagerServlet.appsName=Display Name
+htmlManagerServlet.appsPath=Path
+htmlManagerServlet.appsReload=Reload
+htmlManagerServlet.appsUndeploy=Undeploy
+htmlManagerServlet.appsSessions=Sessions
+htmlManagerServlet.appsStart=Start
+htmlManagerServlet.appsStop=Stop
+htmlManagerServlet.appsTasks=Commands
+htmlManagerServlet.appsTitle=Applications
+htmlManagerServlet.helpHtmlManager=HTML Manager Help
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=Manager Help
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=Deploy
+htmlManagerServlet.deployConfig=XML Configuration file URL:
+htmlManagerServlet.deployPath=Context Path (optional):
+htmlManagerServlet.deployServer=Deploy directory or WAR file located on server
+htmlManagerServlet.deployTitle=Deploy
+htmlManagerServlet.deployUpload=WAR file to deploy
+htmlManagerServlet.deployUploadFail=FAIL - Deploy Upload Failed, Exception: {0}
+htmlManagerServlet.deployUploadFile=Select WAR file to upload
+htmlManagerServlet.deployUploadNotWar=FAIL - File uploaded \"{0}\" must be a .war
+htmlManagerServlet.deployUploadNoFile=FAIL - File upload failed, no file
+htmlManagerServlet.deployUploadWarExists=FAIL - War file \"{0}\" already exists on server
+htmlManagerServlet.deployWar=WAR or Directory URL:
+htmlManagerServlet.list=List Applications
+htmlManagerServlet.manager=Manager
+htmlManagerServlet.messageLabel=Message:
+htmlManagerServlet.serverJVMVendor=JVM Vendor
+htmlManagerServlet.serverJVMVersion=JVM Version
+htmlManagerServlet.serverOSArch=OS Architecture
+htmlManagerServlet.serverOSName=OS Name
+htmlManagerServlet.serverOSVersion=OS Version
+htmlManagerServlet.serverTitle=Server Information
+htmlManagerServlet.serverVersion=Tomcat Version
+htmlManagerServlet.title=Tomcat Web Application Manager
+managerServlet.alreadyContext=FAIL - Application already exists at path {0}
+managerServlet.alreadyDocBase=FAIL - Directory {0} is already in use
+managerServlet.cannotInvoke=Cannot invoke manager servlet through invoker
+managerServlet.configured=OK - Deployed application from context file {0}
+managerServlet.deployed=OK - Deployed application at context path {0}
+managerServlet.deployFailed=FAIL - Failed to deploy application at context path {0}
+managerServlet.exception=FAIL - Encountered exception {0}
+managerServlet.deployed=OK - Deployed application at context path {0}
+managerServlet.invalidPath=FAIL - Invalid context path {0} was specified
+managerServlet.invalidWar=FAIL - Invalid application URL {0} was specified
+managerServlet.listed=OK - Listed applications for virtual host {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FAIL - Cannot identify application base for context path {0}
+managerServlet.noCommand=FAIL - No command was specified
+managerServlet.noContext=FAIL - No context exists for path {0}
+managerServlet.noDirectory=FAIL - Non-directory document base for path {0}
+managerServlet.noDocBase=FAIL - Cannot undeploy document base for path {0}
+managerServlet.noGlobal=FAIL - No global JNDI resources are available
+managerServlet.noReload=FAIL - Reload not supported on WAR deployed at path {0}
+managerServlet.noRename=FAIL - Cannot deploy uploaded WAR for path {0}
+managerServlet.noRole=FAIL - User does not possess role {0}
+managerServlet.noSelf=FAIL - The manager can not reload, undeploy, stop, or undeploy itself
+managerServlet.noWrapper=Container has not called setWrapper() for this servlet
+managerServlet.reloaded=OK - Reloaded application at context path {0}
+managerServlet.undeployd=OK - Undeployed application at context path {0}
+managerServlet.resourcesAll=OK - Listed global resources of all types
+managerServlet.resourcesType=OK - Listed global resources of type {0}
+managerServlet.rolesList=OK - Listed security roles
+managerServlet.saveFail=FAIL - Configuration save failed: {0}
+managerServlet.saved=OK - Server configuration saved
+managerServlet.savedContext=OK - Context {0} configuration saved
+managerServlet.sessiondefaultmax=Default maximum session inactive interval {0} minutes
+managerServlet.sessiontimeout={0} minutes:{1} sessions
+managerServlet.sessions=OK - Session information for application at context path {0}
+managerServlet.started=OK - Started application at context path {0}
+managerServlet.startFailed=FAIL - Application at context path {0} could not be started
+managerServlet.stopped=OK - Stopped application at context path {0}
+managerServlet.undeployed=OK - Undeployed application at context path {0}
+managerServlet.unknownCommand=FAIL - Unknown command {0}
+managerServlet.userDatabaseError=FAIL - Cannot resolve user database reference
+managerServlet.userDatabaseMissing=FAIL - No user database is available
+
+statusServlet.title=Server Status
+statusServlet.complete=Complete Server Status
diff --git a/java/org/apache/catalina/manager/LocalStrings_de.properties b/java/org/apache/catalina/manager/LocalStrings_de.properties
new file mode 100644
index 000000000..20ada40c2
--- /dev/null
+++ b/java/org/apache/catalina/manager/LocalStrings_de.properties
@@ -0,0 +1,78 @@
+htmlManagerServlet.appsAvailable=Verfügbar
+htmlManagerServlet.appsName=Anzeigename
+htmlManagerServlet.appsPath=Kontext Pfad
+htmlManagerServlet.appsReload=Neu laden
+htmlManagerServlet.appsUndeploy=Entfernen
+htmlManagerServlet.appsSessions=Sitzungen
+htmlManagerServlet.appsStart=Start
+htmlManagerServlet.appsStop=Stop
+htmlManagerServlet.appsTasks=Kommandos
+htmlManagerServlet.appsTitle=Anwendungen
+htmlManagerServlet.helpHtmlManager=Hilfeseite HTML Manager (englisch)
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=Hilfeseite Manager (englisch)
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=Installieren
+htmlManagerServlet.deployConfig=XML Konfigurationsdatei URL:
+htmlManagerServlet.deployPath=Kontext Pfad (optional):
+htmlManagerServlet.deployServer=Verzeichnis oder WAR Datei auf Server installieren
+htmlManagerServlet.deployTitle=Installieren
+htmlManagerServlet.deployUpload=Lokale WAR Datei zur Installation hochladen
+htmlManagerServlet.deployUploadFail=FEHLER - Hochladen zur Installation fehlgeschlagen, Ausnahme: {0}
+htmlManagerServlet.deployUploadFile=WAR Datei auswählen
+htmlManagerServlet.deployUploadNotWar=FEHLER - Hochgeladene Datei \"{0}\" muss ein .war sein
+htmlManagerServlet.deployUploadNoFile=FEHLER - Hochladen fehlgeschlagen, keine Datei vorhanden
+htmlManagerServlet.deployUploadWarExists=FEHLER - WAR Datei \"{0}\" existiert bereits auf Server
+htmlManagerServlet.deployWar=WAR oder Verzeichnis URL:
+htmlManagerServlet.list=Anwendungen auflisten
+htmlManagerServlet.manager=Manager
+htmlManagerServlet.messageLabel=Nachricht:
+htmlManagerServlet.serverJVMVendor=JVM Hersteller
+htmlManagerServlet.serverJVMVersion=JVM Version
+htmlManagerServlet.serverOSArch=OS Architektur
+htmlManagerServlet.serverOSName=OS Name
+htmlManagerServlet.serverOSVersion=OS Version
+htmlManagerServlet.serverTitle=Server Informationen
+htmlManagerServlet.serverVersion=Tomcat Version
+htmlManagerServlet.title=Tomcat Webanwendungs-Manager
+managerServlet.alreadyContext=FEHLER - Anwendung existiert bereits für Kontext Pfad {0}
+managerServlet.alreadyDocBase=FEHLER - Verzeichnis {0} bereits in Benutzung
+managerServlet.cannotInvoke=Kann Manager-Servlet nicht durch Invoker aufrufen
+managerServlet.configured=OK - Anwendung von Kontext-Datei {0} installiert
+managerServlet.deployed=OK - Anwendung mit Kontext Pfad {0} installiert
+managerServlet.exception=FEHLER - Ausnahme aufgetreten {0}
+managerServlet.deployed=OK - Anwendung mit Kontext Pfad {0} installiert
+managerServlet.invalidPath=FEHLER - Ungültiger Kontext Pfad {0} angegeben
+managerServlet.invalidWar=FEHLER - Ungültige URL {0} für Anwendung angegeben
+managerServlet.listed=OK - Auflistung der Webanwendungen für virtuellen Server {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FEHLER - Kann Verzeichnis für Kontext Pfad {0} nicht finden
+managerServlet.noCommand=FEHLER - Es wurde kein Kommando angegeben
+managerServlet.noContext=FEHLER - Es existiert kein Kontext für Pfad {0}
+managerServlet.noDirectory=FEHLER - Pfad {0} ist kein Verzeichnis
+managerServlet.noDocBase=FEHLER - Kann Webanwendungs-Verzeichnis nicht entfernen für Kontext Pfad {0}
+managerServlet.noGlobal=FEHLER - Keine globalen JNDI Ressourcen verfügbar
+managerServlet.noReload=FEHLER - Neu laden nicht unterstützt für WAR mit Pfad {0}
+managerServlet.noRename=FEHLER - Kann hochgeladenes WAR mit Pfad {0} nicht installieren
+managerServlet.noRole=FEHLER - Benutzer nicht in Rolle {0}
+managerServlet.noSelf=FEHLER - Manager-Kommandos können nicht auf die Manager-Anwendung selbst angewendet werden
+managerServlet.noWrapper=Container hat setWrapper() für dieses Servlet nicht aufgerufen
+managerServlet.reloaded=OK - Anwendung mit Kontext Pfad {0} neu geladen
+managerServlet.undeployd=OK - Anwendung mit Kontext Pfad {0} entfernt
+managerServlet.resourcesAll=OK - Auflistung globaler Ressourcen (alle Typen)
+managerServlet.resourcesType=OK - Auflistung globaler Ressourcen von Typ {0}
+managerServlet.rolesList=OK - Auflistung der Sicherheits-Rollen
+managerServlet.saveFail=FEHLER - Speichern der Konfiguration fehlgeschlagen: {0}
+managerServlet.sessiondefaultmax=Voreingestellter Sitzungsablauf nach maximal {0} Minuten Inaktivität
+managerServlet.sessiontimeout={0} Minuten: {1} Sitzungen
+managerServlet.sessions=OK - Sitzungs-Informationen für Anwendung mit Kontext Pfad {0}
+managerServlet.started=OK - Anwendung mit Kontext Pfad {0} gestartet
+managerServlet.startFailed=FEHLER - Anwendung mit Kontext Pfad {0} konnte nicht gestartet werden
+managerServlet.stopped=OK - Anwendung mit Kontext Pfad {0} gestoppt
+managerServlet.undeployed=OK - Anwendung mit Kontext Pfad {0} entfernt
+managerServlet.unknownCommand=FEHLER - Unbekanntes Kommando {0}
+managerServlet.userDatabaseError=FEHLER - Kann Referenz auf Benutzerdatendank nicht auflösen
+managerServlet.userDatabaseMissing=FEHLER - Keine Benutzerdatenbank vorhanden
+
+statusServlet.title=Server Status
+statusServlet.complete=Ausführlicher Server Status
diff --git a/java/org/apache/catalina/manager/LocalStrings_es.properties b/java/org/apache/catalina/manager/LocalStrings_es.properties
new file mode 100644
index 000000000..72a2c1b09
--- /dev/null
+++ b/java/org/apache/catalina/manager/LocalStrings_es.properties
@@ -0,0 +1,80 @@
+htmlManagerServlet.appsAvailable=Ejecutándose
+htmlManagerServlet.appsName=Nombre a Mostrar
+htmlManagerServlet.appsPath=Trayectoria
+htmlManagerServlet.appsReload=Recargar
+htmlManagerServlet.appsUndeploy=Replegar
+htmlManagerServlet.appsSessions=Sesiones
+htmlManagerServlet.appsStart=Arrancar
+htmlManagerServlet.appsStop=Parar
+htmlManagerServlet.appsTasks=Comandos
+htmlManagerServlet.appsTitle=Aplicaciones
+htmlManagerServlet.helpHtmlManager=Ayuda HTML de Gestor
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=Ayuda de Gestor
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=Desplegar
+htmlManagerServlet.deployConfig=URL de archivo de Configuración XML:
+htmlManagerServlet.deployPath=Trayectoria de Contexto (opcional):
+htmlManagerServlet.deployServer=Desplegar directorio o archivo WAR localizado en servidor
+htmlManagerServlet.deployTitle=Desplegar
+htmlManagerServlet.deployUpload=Archivo WAR a desplegar
+htmlManagerServlet.deployUploadFail=FALLO - Falló Carga de Despliegue, Excepción: {0}
+htmlManagerServlet.deployUploadFile=Seleccione archivo WAR a cargar
+htmlManagerServlet.deployUploadNotWar=FALLO - El archivo cargado \"{0}\" debe de ser un .war
+htmlManagerServlet.deployUploadNoFile=FALLO - Falló la carga de archivo, no hay archivo
+htmlManagerServlet.deployUploadWarExists=FALLO - El archivo war \"{0}\" ya existe en el servidor
+htmlManagerServlet.deployWar=URL de WAR o Directorio:
+htmlManagerServlet.list=Listar Aplicaciones
+htmlManagerServlet.manager=Gestor
+htmlManagerServlet.messageLabel=Mensaje:
+htmlManagerServlet.serverJVMVendor=Vendedor JVM
+htmlManagerServlet.serverJVMVersion=Versión JVM
+htmlManagerServlet.serverOSArch=Arquitectura de SO
+htmlManagerServlet.serverOSName=Nombre de SO
+htmlManagerServlet.serverOSVersion=Versión de SO
+htmlManagerServlet.serverTitle=Información de Servidor
+htmlManagerServlet.serverVersion=Versión de Tomcat
+htmlManagerServlet.title=Gestor de Aplicaciones Web de Tomcat
+managerServlet.alreadyContext=FALLO - Ya existe la aplicación en la trayectoria {0}
+managerServlet.alreadyDocBase=FALLO - Directorio {0} ya está siendo usado
+managerServlet.cannotInvoke=No puedo invocar servlet de gestor a través de invocador
+managerServlet.configured=OK - Desplegada aplicación desde archivo de contexto {0}
+managerServlet.deployed=OK - Desplegada aplicación en trayectoria de contexto {0}
+managerServlet.exception=FALLO - Encontrada excepción {0}
+managerServlet.deployed=OK - Desplegada aplicación en trayectoria de contexto {0}
+managerServlet.invalidPath=FALLO - Se ha especificado una trayectoria inválida de contexto {0}
+managerServlet.invalidWar=FALLO - Se ha especificado una URL de aplicación inválida {0}
+managerServlet.listed=OK - Aplicaciones listadas para máquinda virutal {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FALLO - No puedo identificar aplicación base para trayectoria de contexto {0}
+managerServlet.noCommand=FALLO - No se ha especificado comando
+managerServlet.noContext=FALLO - No existe contexto para trayectoria {0}
+managerServlet.noDirectory=FALLO - Documento base No-directorio para trayectoria {0}
+managerServlet.noDocBase=FALLO - No puedo replegar documento base para trayectoria {0}
+managerServlet.noGlobal=FALLO - No hay disponibles recursos globales JNDI
+managerServlet.noReload=FALLO - Recarga no soportada en WAR desplegado en trayectoria {0}
+managerServlet.noRename=FALLO - No pudeo desplegar WAR cargado para trayectoria {0}
+managerServlet.noRole=FALLO - El usuario no desempeña el papel de {0}
+managerServlet.noSelf=FALLO - El gestor no puede recargarse, replegarse, pararse o replegarse a sí mismo
+managerServlet.noWrapper=El Contenedor no ha llamado a setWrapper() para este servlet
+managerServlet.reloaded=OK - Recargada aplicación en trayectoria de contexto {0}
+managerServlet.undeployd=OK - Replegada aplicación en trayectoria de contexto {0}
+managerServlet.resourcesAll=OK - Listados recursos globales de todos los tipos
+managerServlet.resourcesType=OK - Listados recursos globales de tipo {0}
+managerServlet.rolesList=OK - Listados papeles de seguridad
+managerServlet.saveFail=FAIL - Fallo al guardar la configuración: {0}
+managerServlet.saved=OK - Configuración de Servidor guardada
+managerServlet.savedContext=OK - Configuración de Contexto {0} guardada
+managerServlet.sessiondefaultmax=Intervalo máximo por defecto de sesión inactiva {0} minutos
+managerServlet.sessiontimeout={0} minutos: {1} sesiones
+managerServlet.sessions=OK - Información de sesión para aplicación en trayectoria de contexto {0}
+managerServlet.started=OK - Arrancada aplicación en trayectoria de contexto {0}
+managerServlet.startFailed=FALLO - No se pudo arrancar la aplicación en trayectoria de contexto {0}
+managerServlet.stopped=OK - Parada aplicación en trayectoria de contexto {0}
+managerServlet.undeployed=OK - Replegada aplicacación en trayectoria de contexto {0}
+managerServlet.unknownCommand=FALLO - Comando desconocido {0}
+managerServlet.userDatabaseError=FALLO - No puedo resolver referencia de base de datos de usuario
+managerServlet.userDatabaseMissing=FALLO - No se encuentra disponible base de datos de usuario
+
+statusServlet.title=Estado de Servidor
+statusServlet.complete=Estado Completo de Servidor
diff --git a/java/org/apache/catalina/manager/LocalStrings_fr.properties b/java/org/apache/catalina/manager/LocalStrings_fr.properties
new file mode 100644
index 000000000..4a2e1f99a
--- /dev/null
+++ b/java/org/apache/catalina/manager/LocalStrings_fr.properties
@@ -0,0 +1,64 @@
+htmlManagerServlet.appsAvailable=Fonctionnant
+htmlManagerServlet.appsName=Nom d''affichage
+htmlManagerServlet.appsPath=Chemin
+htmlManagerServlet.appsReload=Recharger
+htmlManagerServlet.appsRemove=Retirer
+htmlManagerServlet.appsSessions=Sessions
+htmlManagerServlet.appsStart=Démarrer
+htmlManagerServlet.appsStop=Arréter
+htmlManagerServlet.appsTitle=Applications
+htmlManagerServlet.installButton=Installation
+htmlManagerServlet.installConfig=URL de configuration:
+htmlManagerServlet.installPath=Chemin:
+htmlManagerServlet.installTitle=Installation
+htmlManagerServlet.installWar=URL du WAR:
+htmlManagerServlet.messageLabel=Message:
+htmlManagerServlet.serverJVMVendor=Fournisseur de la JVM
+htmlManagerServlet.serverJVMVersion=Version de la JVM
+htmlManagerServlet.serverOSArch=Architecture d''OS
+htmlManagerServlet.serverOSName=Nom d''OS
+htmlManagerServlet.serverOSVersion=Version d''OS
+htmlManagerServlet.serverTitle=Serveur
+htmlManagerServlet.serverVersion=Version de serveur
+htmlManagerServlet.title=Gestionnaire d''applications WEB Tomcat
+managerServlet.alreadyContext=ECHEC - l''application existe déjà dans le chemin {0}
+managerServlet.alreadyDocBase=ECHEC - Le répertoire {0} est déjà utilisé
+managerServlet.cannotInvoke=Impossible d''utiliser le gestionnaire de servlet au travers du délégué (invoker)
+managerServlet.configured=OK - Application configurée depuis le fichier contexte {0}
+managerServlet.deployed=OK - Application déployée pour le chemin de contexte {0}
+managerServlet.exception=ECHEC - L''exception {0} a été rencontrée
+managerServlet.installed=OK - Application installée pour le chemin de contexte {0}
+managerServlet.invalidPath=ECHEC - Un chemin de contexte invalide {0} a été spécifié
+managerServlet.invalidWar=ECHEC - Une URL d''application invalide {0} a été spécifiée
+managerServlet.listed=OK - Applications listées pour l''hôte virtuel (virtual host) {0}
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=ECHEC - Impossible d''identifier la base de l''application base pour le chemin de context {0}
+managerServlet.noCommand=ECHEC - Aucune commande n''a été spécifiée
+managerServlet.noContext=ECHEC - Aucune contexte n''existe pour le chemin {0}
+managerServlet.noDirectory=ECHEC - La base de document n''est pas un répertoire pour le chemin {0}
+managerServlet.noDocBase=ECHEC - Impossible de retirer la base de document pour le chemin {0}
+managerServlet.noGlobal=ECHEC - Aucune ressource JNDI globale n''est disponible
+managerServlet.noReload=ECHEC - Rechargement non supporté par le WAR déployé au chemin {0}
+managerServlet.noRename=ECHEC - Impossible de déployer un WAR téléchargé pour le chemin {0}
+managerServlet.noRole=ECHEC - L''utilisateur ne possède pas le rôle {0}
+managerServlet.noSelf=ECHEC - Le gestionnaire ne peut recharger, retirer, arrêter, ou se déployer lui-même
+managerServlet.noWrapper=Le conteneur n''a pas appelé "setWrapper()" pour cette servlet
+managerServlet.reloaded=OK - Application rechargée au chemin de contexte {0}
+managerServlet.removed=OK - Application retirée au chemin de contexte {0}
+managerServlet.resourcesAll=OK - Liste des ressources globales de tout type
+managerServlet.resourcesType=OK - Liste des ressources globales de type {0}
+managerServlet.rolesList=OK - Liste de rôles de securité
+managerServlet.saveFail=ECHEC - La sauvegarde de la configuration a échoué: {0}
+managerServlet.sessiondefaultmax=Interval par défaut de maximum de session inactive {0} minutes
+managerServlet.sessiontimeout={0} minutes:{1} sessions
+managerServlet.sessions=OK - Information de session pour l''application au chemin de contexte {0}
+managerServlet.started=OK - Application démarrée pour le chemin de contexte {0}
+managerServlet.startFailed=ECHEC - L''application pour le chemin de contexte {0} n''a pas puêtredémarrée
+managerServlet.stopped=OK - Application arrétée pour le chemin de contexte {0}
+managerServlet.undeployed=OK - Application non-déployée pour le chemin de contexte {0}
+managerServlet.unknownCommand=ECHEC - Commande inconnue {0}
+managerServlet.userDatabaseError=ECHEC - Impossible de résoudre la base de données utilisateurs deréférence
+managerServlet.userDatabaseMissing=ECHEC - Aucune base de données utilisateurs n''est disponible
+
+statusServlet.title=Etat du serveur
+statusServlet.complete=Etat complet du serveur
diff --git a/java/org/apache/catalina/manager/LocalStrings_ja.properties b/java/org/apache/catalina/manager/LocalStrings_ja.properties
new file mode 100644
index 000000000..ac307de94
--- /dev/null
+++ b/java/org/apache/catalina/manager/LocalStrings_ja.properties
@@ -0,0 +1,79 @@
+htmlManagerServlet.appsAvailable=\u5b9f\u884c\u4e2d
+htmlManagerServlet.appsName=\u8868\u793a\u540d
+htmlManagerServlet.appsPath=\u30d1\u30b9
+htmlManagerServlet.appsReload=\u518d\u30ed\u30fc\u30c9
+htmlManagerServlet.appsUndeploy=\u914d\u5099\u89e3\u9664
+htmlManagerServlet.appsSessions=\u30bb\u30c3\u30b7\u30e7\u30f3
+htmlManagerServlet.appsStart=\u8d77\u52d5
+htmlManagerServlet.appsStop=\u505c\u6b62
+htmlManagerServlet.appsTasks=\u30b3\u30de\u30f3\u30c9
+htmlManagerServlet.appsTitle=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3
+htmlManagerServlet.helpHtmlManager=HTML\u30de\u30cd\u30fc\u30b8\u30e3\u30d8\u30eb\u30d7
+htmlManagerServlet.helpHtmlManagerFile=html-manager-howto.html
+htmlManagerServlet.helpManager=\u30de\u30cd\u30fc\u30b8\u30e3\u30d8\u30eb\u30d7
+htmlManagerServlet.helpManagerFile=manager-howto.html
+htmlManagerServlet.deployButton=\u914d\u5099
+htmlManagerServlet.deployConfig=XML\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306eURL:
+htmlManagerServlet.deployPath=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 (\u7701\u7565\u53ef):
+htmlManagerServlet.deployServer=\u30b5\u30fc\u30d0\u4e0a\u306eWAR\u30d5\u30a1\u30a4\u30eb\u53c8\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306e\u914d\u5099
+htmlManagerServlet.deployTitle=\u914d\u5099
+htmlManagerServlet.deployUpload=WAR\u30d5\u30a1\u30a4\u30eb\u306e\u914d\u5099
+htmlManagerServlet.deployUploadFail=FAIL - \u914d\u5099\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3001\u4f8b\u5916: {0}
+htmlManagerServlet.deployUploadFile=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308bWAR\u30d5\u30a1\u30a4\u30eb\u306e\u9078\u629e
+htmlManagerServlet.deployUploadNotWar=FAIL - \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3059\u308b\u30d5\u30a1\u30a4\u30eb \"{0}\" \u306fWAR\u30d5\u30a1\u30a4\u30eb\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+htmlManagerServlet.deployUploadNoFile=FAIL - \u30d5\u30a1\u30a4\u30eb\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3001\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+htmlManagerServlet.deployUploadWarExists=FAIL - WAR\u30d5\u30a1\u30a4\u30eb \"{0}\" \u306f\u65e2\u306b\u30b5\u30fc\u30d0\u4e0a\u306b\u5b58\u5728\u3057\u307e\u3059
+htmlManagerServlet.deployWar=WAR\u30d5\u30a1\u30a4\u30eb\u53c8\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u306eURL:
+htmlManagerServlet.list=\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4e00\u89a7
+htmlManagerServlet.manager=\u30de\u30cd\u30fc\u30b8\u30e3
+htmlManagerServlet.messageLabel=\u30e1\u30c3\u30bb\u30fc\u30b8
+htmlManagerServlet.serverJVMVendor=JVM\u30d9\u30f3\u30c0
+htmlManagerServlet.serverJVMVersion=JVM\u30d0\u30fc\u30b8\u30e7\u30f3
+htmlManagerServlet.serverOSArch=OS\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3
+htmlManagerServlet.serverOSName=OS\u540d
+htmlManagerServlet.serverOSVersion=OS\u30d0\u30fc\u30b8\u30e7\u30f3
+htmlManagerServlet.serverTitle=\u30b5\u30fc\u30d0\u60c5\u5831
+htmlManagerServlet.serverVersion=Tomcat\u30d0\u30fc\u30b8\u30e7\u30f3
+htmlManagerServlet.title=Tomcat Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30de\u30cd\u30fc\u30b8\u30e3
+managerServlet.alreadyContext=FAIL - \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u3001\u65e2\u306b\u30d1\u30b9 {0} \u306b\u5b58\u5728\u3057\u307e\u3059
+managerServlet.alreadyDocBase=FAIL - \u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u306f\u65e2\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u307e\u3059
+managerServlet.cannotInvoke=\u30a4\u30f3\u30dc\u30fc\u30ab\u3067\u30de\u30cd\u30fc\u30b8\u30e3\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u3092\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
+managerServlet.configured=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb {0} \u304b\u3089\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3057\u305f
+managerServlet.deployed=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u3057\u307e\u3057\u305f
+managerServlet.exception=FAIL - \u4f8b\u5916 {0} \u304c\u767a\u751f\u3057\u307e\u3057\u305f
+managerServlet.deployed=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306b\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u307e\u3057\u305f
+managerServlet.invalidPath=FAIL - \u7121\u52b9\u306a\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f
+managerServlet.invalidWar=FAIL - \u7121\u52b9\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306eURL {0} \u304c\u6307\u5b9a\u3055\u308c\u307e\u3057\u305f
+managerServlet.listed=OK - \u30d0\u30fc\u30c1\u30e3\u30eb\u30db\u30b9\u30c8 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u4e00\u89a7\u3067\u3059
+managerServlet.listitem={0}:{1}:{2}:{3}
+managerServlet.noAppBase=FAIL - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306b\u5bfe\u3057\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d9\u30fc\u30b9\u3092\u78ba\u8a8d\u3067\u304d\u307e\u305b\u3093
+managerServlet.noCommand=FAIL - \u30b3\u30de\u30f3\u30c9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+managerServlet.noContext=FAIL - \u30d1\u30b9 {0} \u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
+managerServlet.noDirectory=FAIL - \u30d1\u30b9 {0} \u306b\u5bfe\u3059\u308b\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093
+managerServlet.noDocBase=FAIL - \u30d1\u30b9 {0} \u306b\u5bfe\u3059\u308b\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093
+managerServlet.noGlobal=FAIL - \u30b0\u30ed\u30fc\u30d0\u30eb\u306aJNDI\u30ea\u30bd\u30fc\u30b9\u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+managerServlet.noReload=FAIL - \u30d1\u30b9 {0} \u306b\u914d\u5099\u3055\u308c\u305fWAR\u30d5\u30a1\u30a4\u30eb\u3067\u306f\u518d\u30ed\u30fc\u30c9\u304c\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+managerServlet.noRename=FAIL - \u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3055\u308c\u305fWAR\u30d5\u30a1\u30a4\u30eb\u3092\u30d1\u30b9 {0} \u306b\u914d\u5099\u3067\u304d\u307e\u305b\u3093
+managerServlet.noRole=FAIL - \u30e6\u30fc\u30b6\u306f\u30ed\u30fc\u30eb {0} \u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093
+managerServlet.noSelf=FAIL - \u30de\u30cd\u30fc\u30b8\u30e3\u81ea\u8eab\u3092\u518d\u30ed\u30fc\u30c9\u3001\u524a\u9664\u3001\u505c\u6b62\u3001\u53c8\u306f\u914d\u5099\u89e3\u9664\u3067\u304d\u307e\u305b\u3093
+managerServlet.noWrapper=\u30b3\u30f3\u30c6\u30ca\u306f\u3053\u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306b\u5bfe\u3057\u3066\u547c\u3073\u51fa\u3055\u308c\u305fsetWrapper()\u3092\u6301\u3063\u3066\u3044\u307e\u305b\u3093
+managerServlet.reloaded=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u30ed\u30fc\u30c9\u3057\u307e\u3057\u305f
+managerServlet.undeployd=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u304b\u3089\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u89e3\u9664\u3057\u307e\u3057\u305f
+managerServlet.resourcesAll=OK - \u3059\u3079\u3066\u306e\u30bf\u30a4\u30d7\u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u30ea\u30bd\u30fc\u30b9\u3092\u5217\u6319\u3057\u307e\u3057\u305f
+managerServlet.resourcesType=OK - \u30bf\u30a4\u30d7 {0} \u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u30ea\u30bd\u30fc\u30b9\u3092\u5217\u6319\u3057\u307e\u3057\u305f
+managerServlet.rolesList=OK - \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ed\u30fc\u30eb\u3092\u5217\u6319\u3057\u307e\u3057\u305f
+managerServlet.saveFail=FAIL - \u8a2d\u5b9a\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+managerServlet.saved=OK - \u30b5\u30fc\u30d0\u306e\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f
+managerServlet.savedContext=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8 {0} \u306e\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u307e\u3057\u305f
+managerServlet.sessiondefaultmax=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u6700\u5927\u30bb\u30c3\u30b7\u30e7\u30f3\u505c\u6b62\u9593\u9694\u306f{0}\u5206\u3067\u3059
+managerServlet.sessiontimeout={0}\u5206: {1}\u30bb\u30c3\u30b7\u30e7\u30f3
+managerServlet.sessions=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u60c5\u5831\u3067\u3059
+managerServlet.started=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u307e\u3057\u305f
+managerServlet.startFailed=FAIL - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u8d77\u52d5\u3067\u304d\u307e\u305b\u3093
+managerServlet.stopped=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u505c\u6b62\u3057\u307e\u3057\u305f
+managerServlet.undeployed=OK - \u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u30d1\u30b9 {0} \u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u914d\u5099\u89e3\u9664\u3057\u307e\u3057\u305f
+managerServlet.unknownCommand=FAIL - \u672a\u77e5\u306e\u30b3\u30de\u30f3\u30c9 {0} \u3067\u3059
+managerServlet.userDatabaseError=FAIL - \u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u53c2\u7167\u3092\u89e3\u6c7a\u3067\u304d\u307e\u305b\u3093
+managerServlet.userDatabaseMissing=FAIL - \u30e6\u30fc\u30b6\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+statusServlet.title=\u30b5\u30fc\u30d0\u306e\u72b6\u614b
+statusServlet.complete=\u30b5\u30fc\u30d0\u306e\u5168\u72b6\u614b
diff --git a/java/org/apache/catalina/manager/ManagerServlet.java b/java/org/apache/catalina/manager/ManagerServlet.java
new file mode 100644
index 000000000..d71ed0c2f
--- /dev/null
+++ b/java/org/apache/catalina/manager/ManagerServlet.java
@@ -0,0 +1,1582 @@
+/*
+ * 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.tomcat.util.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.
+ * getPathInfo()
+ * and related query parameters to determine what action is being requested.
+ * The following actions and parameters (starting after the servlet path)
+ * are supported:
+ *
+ *
+ * docBase attribute
+ * of the context configuration file is used to locate the actual
+ * WAR or directory containing the application.{config-url}, overriding the
+ * docBase attribute with the contents of the web
+ * application archive found at {war-url}./xxx, based
+ * on the contents of the web application archive found at the
+ * specified URL.path:status:sessions.
+ * Where path is the context path. Status is either running or stopped.
+ * Sessions is the number of active Sessions.users
+ * resource reference.
+ * /xxx for this
+ * virtual host./xxx for this virtual host./xxx for this virtual host./xxx for this virtual host,
+ * and remove the underlying WAR file or document base directory.
+ * (NOTE - This is only allowed if the WAR file or document
+ * base is stored in the appBase directory of this host,
+ * typically as a result of being placed there via the /deploy
+ * command.path=/ for the ROOT context.
+ *
+ * JarURLConnection class for a
+ * reference to an entire JAR file.JarURLConnection class for a reference to an entire
+ * JAR file.
+ *
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 393613 $ $Date: 2006-04-12 23:08:01 +0200 (mer., 12 avr. 2006) $
+ */
+
+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 NamingContext 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 null 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 org.apache.catalina.UserDatabase resource that is
+ * connected to the users 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) {
+ boolean result = false;
+ try {
+ if( src != null &&
+ !src.getCanonicalPath().equals(dest.getCanonicalPath()) ) {
+ result = copyInternal(src, dest, new byte[4096]);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+
+ /**
+ * 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;
+
+ }
+
+
+}
diff --git a/java/org/apache/catalina/manager/StatusManagerServlet.java b/java/org/apache/catalina/manager/StatusManagerServlet.java
new file mode 100644
index 000000000..abb9050fc
--- /dev/null
+++ b/java/org/apache/catalina/manager/StatusManagerServlet.java
@@ -0,0 +1,358 @@
+/*
+ * 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.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerNotification;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.modeler.Registry;
+
+/**
+ * This servlet will display a complete status of the HTTP/1.1 connector.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 303870 $ $Date: 2005-04-19 00:50:24 +0200 (mar., 19 avr. 2005) $
+ */
+
+public class StatusManagerServlet
+ extends HttpServlet implements NotificationListener {
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The debugging detail level for this servlet.
+ */
+ private int debug = 0;
+
+
+ /**
+ * MBean server.
+ */
+ protected MBeanServer mBeanServer = null;
+
+
+ /**
+ * Vector of protocol handlers object names.
+ */
+ protected Vector protocolHandlers = new Vector();
+
+
+ /**
+ * Vector of thread pools object names.
+ */
+ protected Vector threadPools = new Vector();
+
+
+ /**
+ * Vector of request processors object names.
+ */
+ protected Vector requestProcessors = new Vector();
+
+
+ /**
+ * Vector of global request processors object names.
+ */
+ protected Vector globalRequestProcessors = new Vector();
+
+
+ /**
+ * The string manager for this package.
+ */
+ protected static StringManager sm =
+ StringManager.getManager(Constants.Package);
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Initialize this servlet.
+ */
+ public void init() throws ServletException {
+
+ // Retrieve the MBean server
+ mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+
+ // Set our properties from the initialization parameters
+ String value = null;
+ try {
+ value = getServletConfig().getInitParameter("debug");
+ debug = Integer.parseInt(value);
+ } catch (Throwable t) {
+ ;
+ }
+
+ try {
+
+ // Query protocol handlers
+ String onStr = "*:type=ProtocolHandler,*";
+ ObjectName objectName = new ObjectName(onStr);
+ Set set = mBeanServer.queryMBeans(objectName, null);
+ Iterator iterator = set.iterator();
+ while (iterator.hasNext()) {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ protocolHandlers.addElement(oi.getObjectName());
+ }
+
+ // Query Thread Pools
+ onStr = "*:type=ThreadPool,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext()) {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ threadPools.addElement(oi.getObjectName());
+ }
+
+ // Query Global Request Processors
+ onStr = "*:type=GlobalRequestProcessor,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext()) {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ globalRequestProcessors.addElement(oi.getObjectName());
+ }
+
+ // Query Request Processors
+ onStr = "*:type=RequestProcessor,*";
+ objectName = new ObjectName(onStr);
+ set = mBeanServer.queryMBeans(objectName, null);
+ iterator = set.iterator();
+ while (iterator.hasNext()) {
+ ObjectInstance oi = (ObjectInstance) iterator.next();
+ requestProcessors.addElement(oi.getObjectName());
+ }
+
+ // Register with MBean server
+ onStr = "JMImplementation:type=MBeanServerDelegate";
+ objectName = new ObjectName(onStr);
+ mBeanServer.addNotificationListener(objectName, this, null, null);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+
+ /**
+ * 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 {
+
+ // mode is flag for HTML or XML output
+ int mode = 0;
+ // if ?XML=true, set the mode to XML
+ if (request.getParameter("XML") != null
+ && request.getParameter("XML").equals("true")) {
+ mode = 1;
+ }
+ StatusTransformer.setContentType(response, mode);
+
+ PrintWriter writer = response.getWriter();
+
+ boolean completeStatus = false;
+ if ((request.getPathInfo() != null)
+ && (request.getPathInfo().equals("/all"))) {
+ completeStatus = true;
+ }
+ // use StatusTransformer to output status
+ StatusTransformer.writeHeader(writer,mode);
+
+ // Body Header Section
+ Object[] args = new Object[2];
+ args[0] = request.getContextPath();
+ if (completeStatus) {
+ args[1] = sm.getString("statusServlet.complete");
+ } else {
+ args[1] = sm.getString("statusServlet.title");
+ }
+ // use StatusTransformer to output status
+ StatusTransformer.writeBody(writer,args,mode);
+
+ // Manager Section
+ args = new Object[9];
+ args[0] = sm.getString("htmlManagerServlet.manager");
+ args[1] = response.encodeURL(request.getContextPath() + "/html/list");
+ args[2] = sm.getString("htmlManagerServlet.list");
+ args[3] = response.encodeURL
+ (request.getContextPath() + "/" +
+ sm.getString("htmlManagerServlet.helpHtmlManagerFile"));
+ args[4] = sm.getString("htmlManagerServlet.helpHtmlManager");
+ args[5] = response.encodeURL
+ (request.getContextPath() + "/" +
+ sm.getString("htmlManagerServlet.helpManagerFile"));
+ args[6] = sm.getString("htmlManagerServlet.helpManager");
+ if (completeStatus) {
+ args[7] = response.encodeURL
+ (request.getContextPath() + "/status");
+ args[8] = sm.getString("statusServlet.title");
+ } else {
+ args[7] = response.encodeURL
+ (request.getContextPath() + "/status/all");
+ args[8] = sm.getString("statusServlet.complete");
+ }
+ // use StatusTransformer to output status
+ StatusTransformer.writeManager(writer,args,mode);
+
+ // Server Header Section
+ args = new Object[7];
+ args[0] = sm.getString("htmlManagerServlet.serverTitle");
+ args[1] = sm.getString("htmlManagerServlet.serverVersion");
+ args[2] = sm.getString("htmlManagerServlet.serverJVMVersion");
+ args[3] = sm.getString("htmlManagerServlet.serverJVMVendor");
+ args[4] = sm.getString("htmlManagerServlet.serverOSName");
+ args[5] = sm.getString("htmlManagerServlet.serverOSVersion");
+ args[6] = sm.getString("htmlManagerServlet.serverOSArch");
+ // use StatusTransformer to output status
+ StatusTransformer.writePageHeading(writer,args,mode);
+
+ // Server Row Section
+ args = new Object[6];
+ args[0] = ServerInfo.getServerInfo();
+ args[1] = System.getProperty("java.runtime.version");
+ args[2] = System.getProperty("java.vm.vendor");
+ args[3] = System.getProperty("os.name");
+ args[4] = System.getProperty("os.version");
+ args[5] = System.getProperty("os.arch");
+ // use StatusTransformer to output status
+ StatusTransformer.writeServerInfo(writer, args, mode);
+
+ try {
+
+ // Display operating system statistics using APR if available
+ StatusTransformer.writeOSState(writer,mode);
+
+ // Display virtual machine statistics
+ StatusTransformer.writeVMState(writer,mode);
+
+ Enumeration enumeration = threadPools.elements();
+ while (enumeration.hasMoreElements()) {
+ ObjectName objectName = (ObjectName) enumeration.nextElement();
+ String name = objectName.getKeyProperty("name");
+ // use StatusTransformer to output status
+ StatusTransformer.writeConnectorState
+ (writer, objectName,
+ name, mBeanServer, globalRequestProcessors,
+ requestProcessors, mode);
+ }
+
+ if ((request.getPathInfo() != null)
+ && (request.getPathInfo().equals("/all"))) {
+ // Note: Retrieving the full status is much slower
+ // use StatusTransformer to output status
+ StatusTransformer.writeDetailedState
+ (writer, mBeanServer, mode);
+ }
+
+ } catch (Exception e) {
+ throw new ServletException(e);
+ }
+
+ // use StatusTransformer to output status
+ StatusTransformer.writeFooter(writer, mode);
+
+ }
+
+ // ------------------------------------------- NotificationListener Methods
+
+
+ public void handleNotification(Notification notification,
+ java.lang.Object handback) {
+
+ if (notification instanceof MBeanServerNotification) {
+ ObjectName objectName =
+ ((MBeanServerNotification) notification).getMBeanName();
+ if (notification.getType().equals
+ (MBeanServerNotification.REGISTRATION_NOTIFICATION)) {
+ String type = objectName.getKeyProperty("type");
+ if (type != null) {
+ if (type.equals("ProtocolHandler")) {
+ protocolHandlers.addElement(objectName);
+ } else if (type.equals("ThreadPool")) {
+ threadPools.addElement(objectName);
+ } else if (type.equals("GlobalRequestProcessor")) {
+ globalRequestProcessors.addElement(objectName);
+ } else if (type.equals("RequestProcessor")) {
+ requestProcessors.addElement(objectName);
+ }
+ }
+ } else if (notification.getType().equals
+ (MBeanServerNotification.UNREGISTRATION_NOTIFICATION)) {
+ String type = objectName.getKeyProperty("type");
+ if (type != null) {
+ if (type.equals("ProtocolHandler")) {
+ protocolHandlers.removeElement(objectName);
+ } else if (type.equals("ThreadPool")) {
+ threadPools.removeElement(objectName);
+ } else if (type.equals("GlobalRequestProcessor")) {
+ globalRequestProcessors.removeElement(objectName);
+ } else if (type.equals("RequestProcessor")) {
+ requestProcessors.removeElement(objectName);
+ }
+ }
+ String j2eeType = objectName.getKeyProperty("j2eeType");
+ if (j2eeType != null) {
+
+ }
+ }
+ }
+
+ }
+
+
+}
diff --git a/java/org/apache/catalina/manager/StatusTransformer.java b/java/org/apache/catalina/manager/StatusTransformer.java
new file mode 100644
index 000000000..2bdf5baca
--- /dev/null
+++ b/java/org/apache/catalina/manager/StatusTransformer.java
@@ -0,0 +1,933 @@
+/*
+ * 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.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.util.RequestUtil;
+
+/**
+ * This is a refactoring of the servlet to externalize
+ * the output into a simple class. Although we could
+ * use XSLT, that is unnecessarily complex.
+ *
+ * @author Peter Lin
+ * @version $Revision: 303967 $ $Date: 2005-06-29 19:31:56 +0200 (mer., 29 juin 2005) $
+ */
+
+public class StatusTransformer {
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ public static void setContentType(HttpServletResponse response,
+ int mode) {
+ if (mode == 0){
+ response.setContentType("text/html;charset="+Constants.CHARSET);
+ } else if (mode == 1){
+ response.setContentType("text/xml;charset="+Constants.CHARSET);
+ }
+ }
+
+
+ /**
+ * 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 static void writeHeader(PrintWriter writer, int mode) {
+ if (mode == 0){
+ // HTML Header Section
+ writer.print(Constants.HTML_HEADER_SECTION);
+ } else if (mode == 1){
+ writer.write(Constants.XML_DECLARATION);
+ writer.write
+ (Constants.XML_STYLE);
+ writer.write("OS
");
+
+ writer.print("
");
+ writer.print(" Process kernel time: ");
+ writer.print(formatTime(new Long(result[11] / 1000), true));
+ writer.print(" Process user time: ");
+ writer.print(formatTime(new Long(result[12] / 1000), true));
+ writer.print("JVM
");
+
+ writer.print("");
+ writer.print(name);
+ writer.print("
");
+
+ writer.print("
");
+
+ ObjectName grpName = null;
+
+ Enumeration enumeration = globalRequestProcessors.elements();
+ while (enumeration.hasMoreElements()) {
+ ObjectName objectName = (ObjectName) enumeration.nextElement();
+ if (name.equals(objectName.getKeyProperty("name"))) {
+ grpName = objectName;
+ }
+ }
+
+ if (grpName == null) {
+ return;
+ }
+
+ writer.print(" Max processing time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (grpName, "maxTime"), false));
+ writer.print(" Processing time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (grpName, "processingTime"), true));
+ writer.print(" Request count: ");
+ writer.print(mBeanServer.getAttribute(grpName, "requestCount"));
+ writer.print(" Error count: ");
+ writer.print(mBeanServer.getAttribute(grpName, "errorCount"));
+ writer.print(" Bytes received: ");
+ writer.print(formatSize(mBeanServer.getAttribute
+ (grpName, "bytesReceived"), true));
+ writer.print(" Bytes sent: ");
+ writer.print(formatSize(mBeanServer.getAttribute
+ (grpName, "bytesSent"), true));
+ writer.print("
");
+
+ writer.print(" ");
+
+ enumeration = requestProcessors.elements();
+ while (enumeration.hasMoreElements()) {
+ ObjectName objectName = (ObjectName) enumeration.nextElement();
+ if (name.equals(objectName.getKeyProperty("worker"))) {
+ writer.print("Stage Time B Sent B Recv Client VHost Request ");
+ writeProcessorState(writer, objectName, mBeanServer, mode);
+ writer.print(" ");
+ }
+ }
+
+ writer.print("");
+ writer.write(stageStr);
+ writer.write(" ");
+
+ if (fullStatus) {
+ writer.write("");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (pName, "requestProcessingTime"), false));
+ writer.write(" ");
+ writer.write("");
+ if (showRequest) {
+ writer.print(formatSize(mBeanServer.getAttribute
+ (pName, "requestBytesSent"), false));
+ } else {
+ writer.write("?");
+ }
+ writer.write(" ");
+ writer.write("");
+ if (showRequest) {
+ writer.print(formatSize(mBeanServer.getAttribute
+ (pName, "requestBytesReceived"),
+ false));
+ } else {
+ writer.write("?");
+ }
+ writer.write(" ");
+ writer.write("");
+ writer.print(filter(mBeanServer.getAttribute
+ (pName, "remoteAddr")));
+ writer.write(" ");
+ writer.write("");
+ writer.write(filter(mBeanServer.getAttribute
+ (pName, "virtualHost")));
+ writer.write(" ");
+ writer.write("");
+ if (showRequest) {
+ writer.write(filter(mBeanServer.getAttribute
+ (pName, "method")));
+ writer.write(" ");
+ writer.write(filter(mBeanServer.getAttribute
+ (pName, "currentUri")));
+ String queryString = (String) mBeanServer.getAttribute
+ (pName, "currentQueryString");
+ if ((queryString != null) && (!queryString.equals(""))) {
+ writer.write("?");
+ writer.print(RequestUtil.filter(queryString));
+ }
+ writer.write(" ");
+ writer.write(filter(mBeanServer.getAttribute
+ (pName, "protocol")));
+ } else {
+ writer.write("?");
+ }
+ writer.write(" ");
+ } else {
+ writer.write("? ? ? ? ? ? ");
+ }
+ } else if (mode == 1){
+ writer.write("");
+ writer.print("Application list");
+ writer.print("
");
+
+ writer.print("
");
+ }
+
+ }
+ writer.print("");
+ writer.print(name);
+ writer.print("
");
+ writer.print("");
+
+ writer.print("
");
+ writer.print(" Active sessions: ");
+ writer.print(mBeanServer.getAttribute
+ (objectName, "activeSessions"));
+ writer.print(" Session count: ");
+ writer.print(mBeanServer.getAttribute
+ (objectName, "sessionCounter"));
+ writer.print(" Max active sessions: ");
+ writer.print(mBeanServer.getAttribute(objectName, "maxActive"));
+ writer.print(" Rejected session creations: ");
+ writer.print(mBeanServer.getAttribute
+ (objectName, "rejectedSessions"));
+ writer.print(" Expired sessions: ");
+ writer.print(mBeanServer.getAttribute
+ (objectName, "expiredSessions"));
+ writer.print(" Longest session alive time: ");
+ writer.print(formatSeconds(mBeanServer.getAttribute(
+ objectName,
+ "sessionMaxAliveTime")));
+ writer.print(" Average session alive time: ");
+ writer.print(formatSeconds(mBeanServer.getAttribute(
+ objectName,
+ "sessionAverageAliveTime")));
+ writer.print(" Processing time: ");
+ writer.print(formatTime(mBeanServer.getAttribute
+ (objectName, "processingTime"), false));
+ } else if (mode == 1) {
+ // for now we don't write out the wrapper details
+ }
+
+ }
+
+
+ /**
+ * Write JSP monitoring information.
+ */
+ public static void writeJspMonitor(PrintWriter writer,
+ Set jspMonitorONs,
+ MBeanServer mBeanServer,
+ int mode)
+ throws Exception {
+
+ int jspCount = 0;
+ int jspReloadCount = 0;
+
+ Iterator iter = jspMonitorONs.iterator();
+ while (iter.hasNext()) {
+ ObjectName jspMonitorON = (ObjectName) iter.next();
+ Object obj = mBeanServer.getAttribute(jspMonitorON, "jspCount");
+ jspCount += ((Integer) obj).intValue();
+ obj = mBeanServer.getAttribute(jspMonitorON, "jspReloadCount");
+ jspReloadCount += ((Integer) obj).intValue();
+ }
+
+ if (mode == 0) {
+ writer.print("
");
+ writer.print(" JSPs loaded: ");
+ writer.print(jspCount);
+ writer.print(" JSPs reloaded: ");
+ writer.print(jspReloadCount);
+ } else if (mode == 1) {
+ // for now we don't write out anything
+ }
+ }
+
+
+ /**
+ * Write detailed information about a wrapper.
+ */
+ public static void writeWrapper(PrintWriter writer, ObjectName objectName,
+ MBeanServer mBeanServer, int mode)
+ throws Exception {
+
+ if (mode == 0) {
+ String servletName = objectName.getKeyProperty("name");
+
+ String[] mappings = (String[])
+ mBeanServer.invoke(objectName, "findMappings", null, null);
+
+ writer.print("");
+ writer.print(servletName);
+ if ((mappings != null) && (mappings.length > 0)) {
+ writer.print(" [ ");
+ for (int i = 0; i < mappings.length; i++) {
+ writer.print(mappings[i]);
+ if (i < mappings.length - 1) {
+ writer.print(" , ");
+ }
+ }
+ writer.print(" ] ");
+ }
+ writer.print("
");
+
+ writer.print("\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ "
\n" +
+ " \n" +
+ " \n" +
+ "
\n" +
+ " \n" +
+ "
\n" +
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ " {1}\n" +
+ " \n" +
+ "
\n" +
+ "\n";
+
+ public static final String MESSAGE_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "" +
+ "{0} \n" +
+ " \n" +
+ " {1}
\n" +
+ "\n";
+
+ public static final String MANAGER_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ " {0} \n" +
+ "\n" +
+ " \n" +
+ "{2} \n" +
+ " {4} \n" +
+ " {6} \n" +
+ " {8} \n" +
+ "
\n" +
+ "\n";
+
+ public static final String SERVER_HEADER_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n";
+
+ public static final String SERVER_ROW_SECTION =
+ "{1} \n" +
+ " {2} \n" +
+ " {3} \n" +
+ " {4} \n" +
+ " {5} \n" +
+ " {6} \n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ " {1} \n" +
+ " {2} \n" +
+ " {3} \n" +
+ " {4} \n" +
+ " {5} \n" +
+ "
\n" +
+ "\n";
+
+ public static final String HTML_TAIL_SECTION =
+ "
\n" +
+ "HostManagerServlet and this
+* Servlet is that this Servlet prints out a HTML interface which
+* makes it easier to administrate.
+* HostManagerServlet you won't be able to upgrade
+* to this Servlet since the output are not in the
+* same format as from HostManagerServlet
+*
+* @author Bip Thelin
+* @author Malcolm Edgar
+* @author Glenn L. Nielsen
+* @author Peter Rossbach
+* @version $Revision: 384293 $, $Date: 2006-03-08 19:09:36 +0100 (mer., 08 mars 2006) $
+* @see ManagerServlet
+*/
+
+public final class HTMLHostManagerServlet extends HostManagerServlet {
+
+ // --------------------------------------------------------- Public Methods
+
+ /**
+ * 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 {
+
+ // Identify the request parameters that we need
+ String command = request.getPathInfo();
+
+ String name = request.getParameter("name");
+
+ // Prepare our output writer to generate the response message
+ response.setContentType("text/html; charset=" + Constants.CHARSET);
+
+ String message = "";
+ // Process the requested command
+ if (command == null) {
+ } else if (command.equals("/add")) {
+ message = add(request, name);
+ } else if (command.equals("/remove")) {
+ message = remove(name);
+ } else if (command.equals("/list")) {
+ } else if (command.equals("/start")) {
+ message = start(name);
+ } else if (command.equals("/stop")) {
+ message = stop(name);
+ } else {
+ message =
+ sm.getString("hostManagerServlet.unknownCommand", command);
+ }
+
+ list(request, response, message);
+ }
+
+
+ /**
+ * Add a host using the specified parameters.
+ *
+ * @param name host name
+ */
+ protected String add(HttpServletRequest request,String name) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.add(request,printWriter,name,true);
+
+ return stringWriter.toString();
+ }
+
+
+ /**
+ * Remove the specified host.
+ *
+ * @param writer Writer to render results to
+ * @param name host name
+ */
+ protected String remove(String name) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.remove(printWriter, name);
+
+ return stringWriter.toString();
+ }
+
+
+ /**
+ * Start the host with the specified name.
+ *
+ * @param name Host name
+ */
+ protected String start(String name) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.start(printWriter, name);
+
+ return stringWriter.toString();
+ }
+
+
+ /**
+ * Stop the host with the specified name.
+ *
+ * @param name Host name
+ */
+ protected String stop(String name) {
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ super.stop(printWriter, name);
+
+ return stringWriter.toString();
+ }
+
+
+ /**
+ * Render a HTML list of the currently active Contexts in our virtual host,
+ * and memory and server status information.
+ *
+ * @param request The request
+ * @param response The response
+ * @param message a message to display
+ */
+ public void list(HttpServletRequest request,
+ HttpServletResponse response,
+ String message) throws IOException {
+
+ PrintWriter writer = response.getWriter();
+
+ // HTML Header Section
+ writer.print(Constants.HTML_HEADER_SECTION);
+
+ // Body Header Section
+ Object[] args = new Object[2];
+ args[0] = request.getContextPath();
+ args[1] = sm.getString("htmlHostManagerServlet.title");
+ writer.print(MessageFormat.format
+ (Constants.BODY_HEADER_SECTION, args));
+
+ // Message Section
+ args = new Object[3];
+ args[0] = sm.getString("htmlHostManagerServlet.messageLabel");
+ args[1] = (message == null || message.length() == 0) ? "OK" : message;
+ writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args));
+
+ // Manager Section
+ args = new Object[9];
+ args[0] = sm.getString("htmlHostManagerServlet.manager");
+ args[1] = response.encodeURL(request.getContextPath() + "/html/list");
+ args[2] = sm.getString("htmlHostManagerServlet.list");
+ args[3] = response.encodeURL
+ (request.getContextPath() + "/" +
+ sm.getString("htmlHostManagerServlet.helpHtmlManagerFile"));
+ args[4] = sm.getString("htmlHostManagerServlet.helpHtmlManager");
+ args[5] = response.encodeURL
+ (request.getContextPath() + "/" +
+ sm.getString("htmlHostManagerServlet.helpManagerFile"));
+ args[6] = sm.getString("htmlHostManagerServlet.helpManager");
+ args[7] = response.encodeURL("/manager/status");
+ args[8] = sm.getString("statusServlet.title");
+ writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args));
+
+ // Hosts Header Section
+ args = new Object[3];
+ args[0] = sm.getString("htmlHostManagerServlet.hostName");
+ args[1] = sm.getString("htmlHostManagerServlet.hostAliases");
+ args[2] = sm.getString("htmlHostManagerServlet.hostTasks");
+ writer.print(MessageFormat.format(HOSTS_HEADER_SECTION, args));
+
+ // Hosts Row Section
+ // Create sorted map of host names.
+ Container[] children = engine.findChildren();
+ String hostNames[] = new String[children.length];
+ for (int i = 0; i < children.length; i++)
+ hostNames[i] = children[i].getName();
+
+ TreeMap sortedHostNamesMap = new TreeMap();
+
+ for (int i = 0; i < hostNames.length; i++) {
+ String displayPath = hostNames[i];
+ sortedHostNamesMap.put(displayPath, hostNames[i]);
+ }
+
+ String hostsStart = sm.getString("htmlHostManagerServlet.hostsStart");
+ String hostsStop = sm.getString("htmlHostManagerServlet.hostsStop");
+ String hostsRemove = sm.getString("htmlHostManagerServlet.hostsRemove");
+
+ Iterator iterator = sortedHostNamesMap.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ String hostName = (String) entry.getKey();
+ Host host = (Host) engine.findChild(hostName);
+
+ if (host != null ) {
+ args = new Object[2];
+ args[0] = hostName;
+ String[] aliases = host.findAliases();
+ StringBuffer buf = new StringBuffer();
+ if (aliases.length > 0) {
+ buf.append(aliases[0]);
+ for (int j = 1; j < aliases.length; j++) {
+ buf.append(", ").append(aliases[j]);
+ }
+ }
+
+ if (buf.length() == 0) {
+ buf.append(" ");
+ }
+
+ args[1] = buf.toString();
+ writer.print
+ (MessageFormat.format(HOSTS_ROW_DETAILS_SECTION, args));
+
+ args = new Object[7];
+ args[0] = response.encodeURL
+ (request.getContextPath() +
+ "/html/start?name=" + hostName);
+ args[1] = hostsStart;
+ args[2] = response.encodeURL
+ (request.getContextPath() +
+ "/html/stop?name=" + hostName);
+ args[3] = hostsStop;
+ args[4] = response.encodeURL
+ (request.getContextPath() +
+ "/html/remove?name=" + hostName);
+ args[5] = hostsRemove;
+ args[6] = hostName;
+ if (host == this.host) {
+ writer.print(MessageFormat.format(
+ MANAGER_HOST_ROW_BUTTON_SECTION, args));
+ } else {
+ writer.print(MessageFormat.format(
+ HOSTS_ROW_BUTTON_SECTION, args));
+ }
+
+ }
+ }
+
+ // Add Section
+ args = new Object[6];
+ args[0] = sm.getString("htmlHostManagerServlet.addTitle");
+ args[1] = sm.getString("htmlHostManagerServlet.addHost");
+ args[2] = response.encodeURL(request.getContextPath() + "/html/add");
+ args[3] = sm.getString("htmlHostManagerServlet.addName");
+ args[4] = sm.getString("htmlHostManagerServlet.addAliases");
+ args[5] = sm.getString("htmlHostManagerServlet.addAppBase");
+ writer.print(MessageFormat.format(ADD_SECTION_START, args));
+
+ args = new Object[3];
+ args[0] = sm.getString("htmlHostManagerServlet.addAutoDeploy");
+ args[1] = "autoDeploy";
+ args[2] = "checked";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+ args[0] = sm.getString("htmlHostManagerServlet.addDeployOnStartup");
+ args[1] = "deployOnStartup";
+ args[2] = "checked";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+ args[0] = sm.getString("htmlHostManagerServlet.addDeployXML");
+ args[1] = "deployXML";
+ args[2] = "checked";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+ args[0] = sm.getString("htmlHostManagerServlet.addUnpackWARs");
+ args[1] = "unpackWARs";
+ args[2] = "checked";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+ args[0] = sm.getString("htmlHostManagerServlet.addXmlNamespaceAware");
+ args[1] = "xmlNamespaceAware";
+ args[2] = "";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+ args[0] = sm.getString("htmlHostManagerServlet.addXmlValidation");
+ args[1] = "xmlValidation";
+ args[2] = "";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+
+ args[0] = sm.getString("htmlHostManagerServlet.addManager");
+ args[1] = "manager";
+ args[2] = "checked";
+ writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args));
+
+ args = new Object[1];
+ args[0] = sm.getString("htmlHostManagerServlet.addButton");
+ writer.print(MessageFormat.format(ADD_SECTION_END, args));
+
+ // Server Header Section
+ args = new Object[7];
+ args[0] = sm.getString("htmlHostManagerServlet.serverTitle");
+ args[1] = sm.getString("htmlHostManagerServlet.serverVersion");
+ args[2] = sm.getString("htmlHostManagerServlet.serverJVMVersion");
+ args[3] = sm.getString("htmlHostManagerServlet.serverJVMVendor");
+ args[4] = sm.getString("htmlHostManagerServlet.serverOSName");
+ args[5] = sm.getString("htmlHostManagerServlet.serverOSVersion");
+ args[6] = sm.getString("htmlHostManagerServlet.serverOSArch");
+ writer.print(MessageFormat.format
+ (Constants.SERVER_HEADER_SECTION, args));
+
+ // Server Row Section
+ args = new Object[6];
+ args[0] = ServerInfo.getServerInfo();
+ args[1] = System.getProperty("java.runtime.version");
+ args[2] = System.getProperty("java.vm.vendor");
+ args[3] = System.getProperty("os.name");
+ args[4] = System.getProperty("os.version");
+ args[5] = System.getProperty("os.arch");
+ writer.print(MessageFormat.format(Constants.SERVER_ROW_SECTION, args));
+
+ // HTML Tail Section
+ writer.print(Constants.HTML_TAIL_SECTION);
+
+ // Finish up the response
+ writer.flush();
+ writer.close();
+ }
+
+
+ // ------------------------------------------------------ Private Constants
+
+ // These HTML sections are broken in relatively small sections, because of
+ // limited number of subsitutions MessageFormat can process
+ // (maximium of 10).
+
+ private static final String HOSTS_HEADER_SECTION =
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n";
+
+ private static final String HOSTS_ROW_DETAILS_SECTION =
+ "{0} \n" +
+ " {1} \n" +
+ " {2} \n" +
+ "\n" +
+ " \n";
+
+ private static final String HOSTS_ROW_BUTTON_SECTION =
+ " {0}" +
+ " \n" +
+ " {1} \n";
+
+ private static final String MANAGER_HOST_ROW_BUTTON_SECTION =
+ " \n" +
+ " \n" +
+ " {1} \n" +
+ " {3} \n" +
+ " {5} \n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " {1} \n" +
+ " {3} \n" +
+ " {5} \n" +
+ " \n" +
+ " \n" +
+ "\n";
+
+ private static final String ADD_SECTION_START =
+ "
\n" +
+ "\n" +
+ "
\n" +
+ "\n" +
+ " \n" +
+ "{0} \n" +
+ "\n" +
+ " \n" +
+ "{1} \n" +
+ "\n" +
+ " \n" +
+ "\n" +
+ "\n" +
+ " \n" +
+ "
\n" +
+ "\n";
+
+}
diff --git a/java/org/apache/catalina/manager/host/HostManagerServlet.java b/java/org/apache/catalina/manager/host/HostManagerServlet.java
new file mode 100644
index 000000000..869814730
--- /dev/null
+++ b/java/org/apache/catalina/manager/host/HostManagerServlet.java
@@ -0,0 +1,684 @@
+/*
+ * Copyright 1999,2004-2006 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.host;
+
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.StringTokenizer;
+
+import javax.management.MBeanServer;
+import javax.servlet.ServletException;
+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.Wrapper;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.startup.HostConfig;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.modeler.Registry;
+
+
+/**
+ * Servlet that enables remote management of the virtual hosts installed
+ * on the server. Normally, this functionality will be protected by
+ * a security constraint in the web application deployment descriptor.
+ * However, this requirement can be relaxed during testing.
+ * getPathInfo()
+ * and related query parameters to determine what action is being requested.
+ * The following actions and parameters (starting after the servlet path)
+ * are supported:
+ *
+ *
+ * host-name attribute
+ * indicates the name of the new host. The host-aliases
+ * attribute is a comma separated list of the host alias names.
+ * The manager attribute is a boolean value indicating if the
+ * webapp manager will be installed in the newly created host (optional,
+ * false by default).host-name attribute indicates the name of the host.
+ * host-name#host-aliases.
+ *
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision: 384293 $ $Date: 2006-03-08 19:09:36 +0100 (mer., 08 mars 2006) $
+ */
+
+public class HostManagerServlet
+ 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;
+
+
+ /**
+ * The associated host.
+ */
+ protected Host host = null;
+
+
+ /**
+ * The associated engine.
+ */
+ protected Engine engine = null;
+
+
+ /**
+ * MBean server.
+ */
+ protected MBeanServer mBeanServer = 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;
+ engine = null;
+ } else {
+ context = (Context) wrapper.getParent();
+ host = (Host) context.getParent();
+ engine = (Engine) host.getParent();
+ }
+
+ // 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("hostManagerServlet.cannotInvoke"));
+
+ // Identify the request parameters that we need
+ String command = request.getPathInfo();
+ if (command == null)
+ command = request.getServletPath();
+ String name = request.getParameter("name");
+
+ // 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("hostManagerServlet.noCommand"));
+ } else if (command.equals("/add")) {
+ add(request, writer, name, false);
+ } else if (command.equals("/remove")) {
+ remove(writer, name);
+ } else if (command.equals("/list")) {
+ list(writer);
+ } else if (command.equals("/start")) {
+ start(writer, name);
+ } else if (command.equals("/stop")) {
+ stop(writer, name);
+ } else {
+ writer.println(sm.getString("hostManagerServlet.unknownCommand",
+ command));
+ }
+
+ // Finish up the response
+ writer.flush();
+ writer.close();
+
+ }
+
+
+ /**
+ * Add host with the given parameters.
+ *
+ * @param request The request
+ * @param writer The output writer
+ * @param name The host name
+ * @param htmlMode Flag value
+ */
+ protected void add(HttpServletRequest request, PrintWriter writer, String name, boolean htmlMode ) {
+ String aliases = request.getParameter("aliases");
+ String appBase = request.getParameter("appBase");
+ boolean manager = booleanParameter(request, "manager", false, htmlMode);
+ boolean autoDeploy = booleanParameter(request, "autoDeploy", true, htmlMode);
+ boolean deployOnStartup = booleanParameter(request, "deployOnStartup", true, htmlMode);
+ boolean deployXML = booleanParameter(request, "deployXML", true, htmlMode);
+ boolean unpackWARs = booleanParameter(request, "unpackWARs", true, htmlMode);
+ boolean xmlNamespaceAware = booleanParameter(request, "xmlNamespaceAware", false, htmlMode);
+ boolean xmlValidation = booleanParameter(request, "xmlValidation", false, htmlMode);
+ add(writer, name, aliases, appBase, manager,
+ autoDeploy,
+ deployOnStartup,
+ deployXML,
+ unpackWARs,
+ xmlNamespaceAware,
+ xmlValidation);
+ }
+
+
+ /**
+ * extract boolean value from checkbox with default
+ * @param request
+ * @param parameter
+ * @param theDefault
+ * @param htmlMode
+ * @return
+ */
+ protected boolean booleanParameter(HttpServletRequest request,
+ String parameter, boolean theDefault, boolean htmlMode) {
+ String value = request.getParameter(parameter);
+ boolean booleanValue = theDefault;
+ if (value != null) {
+ if (htmlMode) {
+ if (value.equals("on")) {
+ booleanValue = true;
+ }
+ } else if (theDefault) {
+ if (value.equals("false")) {
+ booleanValue = false;
+ }
+ } else if (value.equals("true")) {
+ booleanValue = true;
+ }
+ } else if (htmlMode)
+ booleanValue = false;
+ return booleanValue;
+ }
+
+
+ /**
+ * 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("hostManagerServlet.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("hostManagerServlet.cannotInvoke"));
+
+ // Set our properties from the initialization parameters
+ String value = null;
+ try {
+ value = getServletConfig().getInitParameter("debug");
+ debug = Integer.parseInt(value);
+ } catch (Throwable t) {
+ ;
+ }
+
+ }
+
+
+
+ // -------------------------------------------------------- Private Methods
+
+
+ /**
+ * Add a host using the specified parameters.
+ *
+ * @param writer Writer to render results to
+ * @param name host name
+ * @param aliases comma separated alias list
+ * @param appBase application base for the host
+ * @param manager should the manager webapp be deployed to the new host ?
+ */
+ protected synchronized void add
+ (PrintWriter writer, String name, String aliases, String appBase,
+ boolean manager,
+ boolean autoDeploy,
+ boolean deployOnStartup,
+ boolean deployXML,
+ boolean unpackWARs,
+ boolean xmlNamespaceAware,
+ boolean xmlValidation) {
+ if (debug >= 1) {
+ log("add: Adding host '" + name + "'");
+ }
+
+ // Validate the requested host name
+ if ((name == null) || name.length() == 0) {
+ writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+ return;
+ }
+
+ // Check if host already exists
+ if (engine.findChild(name) != null) {
+ writer.println
+ (sm.getString("hostManagerServlet.alreadyHost", name));
+ return;
+ }
+
+ // Validate and create appBase
+ File appBaseFile = null;
+ if (appBase == null || appBase.length() == 0) {
+ appBase = name;
+ }
+ File file = new File(appBase);
+ if (!file.isAbsolute())
+ file = new File(System.getProperty("catalina.base"), appBase);
+ try {
+ appBaseFile = file.getCanonicalFile();
+ } catch (IOException e) {
+ appBaseFile = file;
+ }
+ if (!appBaseFile.exists()) {
+ appBaseFile.mkdirs();
+ }
+
+ // Create base for config files
+ File configBaseFile = getConfigBase(name);
+
+ // Copy manager.xml if requested
+ if (manager) {
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ is = getServletContext().getResourceAsStream("/manager.xml");
+ os = new FileOutputStream(new File(configBaseFile, "manager.xml"));
+ byte buffer[] = new byte[512];
+ int len = buffer.length;
+ while (true) {
+ len = is.read(buffer);
+ if (len == -1)
+ break;
+ os.write(buffer, 0, len);
+ }
+ } catch (IOException e) {
+ writer.println
+ (sm.getString("hostManagerServlet.managerXml"));
+ return;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ StandardHost host = new StandardHost();
+ host.setAppBase(appBase);
+ host.setName(name);
+
+ host.addLifecycleListener(new HostConfig());
+
+ // Add host aliases
+ if ((aliases != null) && !("".equals(aliases))) {
+ StringTokenizer tok = new StringTokenizer(aliases, ",");
+ while (tok.hasMoreTokens()) {
+ host.addAlias(tok.nextToken());
+ }
+ }
+ host.setAutoDeploy(autoDeploy);
+ host.setDeployOnStartup(deployOnStartup);
+ host.setDeployXML(deployXML);
+ host.setUnpackWARs(unpackWARs);
+ host.setXmlNamespaceAware(xmlNamespaceAware);
+ host.setXmlValidation(xmlValidation);
+
+ // Add new host
+ try {
+ engine.addChild(host);
+ } catch (Exception e) {
+ writer.println(sm.getString("hostManagerServlet.exception",
+ e.toString()));
+ return;
+ }
+
+ host = (StandardHost) engine.findChild(name);
+ if (host != null) {
+ writer.println(sm.getString("hostManagerServlet.add", name));
+ } else {
+ // Something failed
+ writer.println(sm.getString("hostManagerServlet.addFailed", name));
+ }
+
+ }
+
+
+ /**
+ * Remove the specified host.
+ *
+ * @param writer Writer to render results to
+ * @param name host name
+ */
+ protected synchronized void remove(PrintWriter writer, String name) {
+
+ if (debug >= 1) {
+ log("remove: Removing host '" + name + "'");
+ }
+
+ // Validate the requested host name
+ if ((name == null) || name.length() == 0) {
+ writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+ return;
+ }
+
+ // Check if host exists
+ if (engine.findChild(name) == null) {
+ writer.println
+ (sm.getString("hostManagerServlet.noHost", name));
+ return;
+ }
+
+ // Prevent removing our own host
+ if (engine.findChild(name) == host) {
+ writer.println
+ (sm.getString("hostManagerServlet.cannotRemoveOwnHost", name));
+ return;
+ }
+
+ // Remove host
+ // Note that the host will not get physically removed
+ try {
+ engine.removeChild(engine.findChild(name));
+ } catch (Exception e) {
+ writer.println(sm.getString("hostManagerServlet.exception",
+ e.toString()));
+ return;
+ }
+
+ Host host = (StandardHost) engine.findChild(name);
+ if (host == null) {
+ writer.println(sm.getString("hostManagerServlet.remove", name));
+ } else {
+ // Something failed
+ writer.println(sm.getString("hostManagerServlet.removeFailed", name));
+ }
+
+ }
+
+
+ /**
+ * 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 hosts for engine '"
+ + engine.getName() + "'");
+
+ writer.println(sm.getString("hostManagerServlet.listed",
+ engine.getName()));
+ Container[] hosts = engine.findChildren();
+ for (int i = 0; i < hosts.length; i++) {
+ Host host = (Host) hosts[i];
+ String name = host.getName();
+ String[] aliases = host.findAliases();
+ StringBuffer buf = new StringBuffer();
+ if (aliases.length > 0) {
+ buf.append(aliases[0]);
+ for (int j = 1; j < aliases.length; j++) {
+ buf.append(',').append(aliases[j]);
+ }
+ }
+ writer.println(sm.getString("hostManagerServlet.listitem",
+ name, buf.toString()));
+ }
+ }
+
+
+ /**
+ * Start the host with the specified name.
+ *
+ * @param writer Writer to render to
+ * @param name Host name
+ */
+ protected void start(PrintWriter writer, String name) {
+
+ if (debug >= 1)
+ log("start: Starting host with name '" + name + "'");
+
+ // Validate the requested host name
+ if ((name == null) || name.length() == 0) {
+ writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+ return;
+ }
+
+ // Check if host exists
+ if (engine.findChild(name) == null) {
+ writer.println
+ (sm.getString("hostManagerServlet.noHost", name));
+ return;
+ }
+
+ // Prevent starting our own host
+ if (engine.findChild(name) == host) {
+ writer.println
+ (sm.getString("hostManagerServlet.cannotStartOwnHost", name));
+ return;
+ }
+
+ // Start host
+ try {
+ ((Lifecycle) engine.findChild(name)).start();
+ writer.println
+ (sm.getString("hostManagerServlet.started", name));
+ } catch (Throwable t) {
+ getServletContext().log
+ (sm.getString("hostManagerServlet.startFailed", name), t);
+ writer.println
+ (sm.getString("hostManagerServlet.startFailed", name));
+ writer.println(sm.getString("hostManagerServlet.exception",
+ t.toString()));
+ return;
+ }
+
+ }
+
+
+ /**
+ * Start the host with the specified name.
+ *
+ * @param writer Writer to render to
+ * @param name Host name
+ */
+ protected void stop(PrintWriter writer, String name) {
+
+ if (debug >= 1)
+ log("stop: Stopping host with name '" + name + "'");
+
+ // Validate the requested host name
+ if ((name == null) || name.length() == 0) {
+ writer.println(sm.getString("hostManagerServlet.invalidHostName", name));
+ return;
+ }
+
+ // Check if host exists
+ if (engine.findChild(name) == null) {
+ writer.println
+ (sm.getString("hostManagerServlet.noHost", name));
+ return;
+ }
+
+ // Prevent starting our own host
+ if (engine.findChild(name) == host) {
+ writer.println
+ (sm.getString("hostManagerServlet.cannotStopOwnHost", name));
+ return;
+ }
+
+ // Start host
+ try {
+ ((Lifecycle) engine.findChild(name)).stop();
+ writer.println
+ (sm.getString("hostManagerServlet.stopped", name));
+ } catch (Throwable t) {
+ getServletContext().log
+ (sm.getString("hostManagerServlet.stopFailed", name), t);
+ writer.println
+ (sm.getString("hostManagerServlet.stopFailed", name));
+ writer.println(sm.getString("hostManagerServlet.exception",
+ t.toString()));
+ return;
+ }
+
+ }
+
+
+ // -------------------------------------------------------- Support Methods
+
+
+ /**
+ * Get config base.
+ */
+ protected File getConfigBase(String hostName) {
+ File configBase =
+ new File(System.getProperty("catalina.base"), "conf");
+ if (!configBase.exists()) {
+ return null;
+ }
+ if (engine != null) {
+ configBase = new File(configBase, engine.getName());
+ }
+ if (host != null) {
+ configBase = new File(configBase, hostName);
+ }
+ configBase.mkdirs();
+ return configBase;
+ }
+
+
+}
diff --git a/java/org/apache/catalina/manager/host/LocalStrings.properties b/java/org/apache/catalina/manager/host/LocalStrings.properties
new file mode 100644
index 000000000..5a665c984
--- /dev/null
+++ b/java/org/apache/catalina/manager/host/LocalStrings.properties
@@ -0,0 +1,59 @@
+hostManagerServlet.cannotInvoke=Cannot invoke host manager servlet through invoker
+hostManagerServlet.noCommand=FAIL - No command was specified
+hostManagerServlet.unknownCommand=FAIL - Unknown command {0}
+hostManagerServlet.noWrapper=Container has not called setWrapper() for this servlet
+hostManagerServlet.invalidHostName=FAIL - Invalid host name {0} was specified
+hostManagerServlet.alreadyHost=FAIL - Host already exists with host name {0}
+hostManagerServlet.managerXml=FAIL - Couldn't install manager.xml
+hostManagerServlet.exception=FAIL - Encountered exception {0}
+hostManagerServlet.add=OK - Host {0} added
+hostManagerServlet.addFailed=FAIL - Failed to add host {0}
+hostManagerServlet.cannotRemoveOwnHost=FAIL - Cannot remove own host {0}
+hostManagerServlet.remove=OK - Removed host {0}
+hostManagerServlet.removeFailed=FAIL - Failed to remove host {0}
+hostManagerServlet.listed=OK - Listed hosts
+hostManagerServlet.listitem={0}:{1}
+hostManagerServlet.cannotStartOwnHost=FAIL - Cannot start own host {0}
+hostManagerServlet.started=OK - Host {0} started
+hostManagerServlet.startFailed=FAIL - Failed to start host {0}
+hostManagerServlet.cannotStopOwnHost=FAIL - Cannot stop own host {0}
+hostManagerServlet.stopped=OK - Host {0} stopped
+hostManagerServlet.stopFailed=FAIL - Failed to stop host {0}
+
+htmlHostManagerServlet.title=Tomcat Virtual Host Manager
+htmlHostManagerServlet.messageLabel=Message:
+htmlHostManagerServlet.manager=Host Manager
+htmlHostManagerServlet.list=List Virtual Hosts
+htmlHostManagerServlet.helpHtmlManagerFile=html-host-manager-howto.html
+htmlHostManagerServlet.helpHtmlManager=HTML Host Manager Help (Coming Soon!)
+htmlHostManagerServlet.helpManagerFile=host-manager-howto.html
+htmlHostManagerServlet.helpManager=Host Manager Help
+htmlHostManagerServlet.hostName=Host name
+htmlHostManagerServlet.hostAliases=Host aliases
+htmlHostManagerServlet.hostTasks=Commands
+htmlHostManagerServlet.hostsStart=Start
+htmlHostManagerServlet.hostsStop=Stop
+htmlHostManagerServlet.hostsRemove=Remove
+htmlHostManagerServlet.addTitle=Add Virtual Host
+htmlHostManagerServlet.addHost=Host
+htmlHostManagerServlet.addName=Name:
+htmlHostManagerServlet.addAliases=Aliases:
+htmlHostManagerServlet.addAppBase=App base:
+htmlHostManagerServlet.addManager=Manager App
+htmlHostManagerServlet.addAutoDeploy=AutoDeploy
+htmlHostManagerServlet.addDeployOnStartup=DeployOnStartup
+htmlHostManagerServlet.addDeployXML=DeployXML
+htmlHostManagerServlet.addUnpackWARs=UnpackWARs
+htmlHostManagerServlet.addXmlNamespaceAware=XmlNamespaceAware
+htmlHostManagerServlet.addXmlValidation=XmlValidation
+htmlHostManagerServlet.addButton=Add
+htmlHostManagerServlet.serverTitle=Server Information
+htmlHostManagerServlet.serverVersion=Tomcat Version
+htmlHostManagerServlet.serverJVMVersion=JVM Version
+htmlHostManagerServlet.serverJVMVendor=JVM Vendor
+htmlHostManagerServlet.serverOSName=OS Name
+htmlHostManagerServlet.serverOSVersion=OS Version
+htmlHostManagerServlet.serverOSArch=OS Architecture
+
+statusServlet.title=Server Status
+statusServlet.complete=Complete Server Status
diff --git a/java/org/apache/tomcat/util/http/fileupload/DefaultFileItem.java b/java/org/apache/tomcat/util/http/fileupload/DefaultFileItem.java
new file mode 100644
index 000000000..805b6d0a1
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/DefaultFileItem.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * null if
+ * not defined.
+ */
+ private String contentType;
+
+
+ /**
+ * Whether or not this item is a simple form field.
+ */
+ private boolean isFormField;
+
+
+ /**
+ * The original filename in the user's filesystem.
+ */
+ private String fileName;
+
+
+ /**
+ * The threshold above which uploads will be stored on disk.
+ */
+ private int sizeThreshold;
+
+
+ /**
+ * The directory in which uploaded files will be stored, if stored on disk.
+ */
+ private File repository;
+
+
+ /**
+ * Cached contents of the file.
+ */
+ private byte[] cachedContent;
+
+
+ /**
+ * Output stream for this item.
+ */
+ private DeferredFileOutputStream dfos;
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs a new DefaultFileItem instance.
+ *
+ * @param fieldName The name of the form field.
+ * @param contentType The content type passed by the browser or
+ * null if not specified.
+ * @param isFormField Whether or not this item is a plain form field, as
+ * opposed to a file upload.
+ * @param fileName The original filename in the user's filesystem, or
+ * null if not specified.
+ * @param sizeThreshold The threshold, in bytes, below which items will be
+ * retained in memory and above which they will be
+ * stored as a file.
+ * @param repository The data repository, which is the directory in
+ * which files will be created, should the item size
+ * exceed the threshold.
+ */
+ DefaultFileItem(String fieldName, String contentType, boolean isFormField,
+ String fileName, int sizeThreshold, File repository)
+ {
+ this.fieldName = fieldName;
+ this.contentType = contentType;
+ this.isFormField = isFormField;
+ this.fileName = fileName;
+ this.sizeThreshold = sizeThreshold;
+ this.repository = repository;
+ }
+
+
+ // ------------------------------- Methods from javax.activation.DataSource
+
+
+ /**
+ * Returns an {@link java.io.InputStream InputStream} that can be
+ * used to retrieve the contents of the file.
+ *
+ * @return An {@link java.io.InputStream InputStream} that can be
+ * used to retrieve the contents of the file.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public InputStream getInputStream()
+ throws IOException
+ {
+ if (!dfos.isInMemory())
+ {
+ return new FileInputStream(dfos.getFile());
+ }
+
+ if (cachedContent == null)
+ {
+ cachedContent = dfos.getData();
+ }
+ return new ByteArrayInputStream(cachedContent);
+ }
+
+
+ /**
+ * Returns the content type passed by the browser or null if
+ * not defined.
+ *
+ * @return The content type passed by the browser or null if
+ * not defined.
+ */
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+
+ /**
+ * Returns the original filename in the client's filesystem.
+ *
+ * @return The original filename in the client's filesystem.
+ */
+ public String getName()
+ {
+ return fileName;
+ }
+
+
+ // ------------------------------------------------------- FileItem methods
+
+
+ /**
+ * Provides a hint as to whether or not the file contents will be read
+ * from memory.
+ *
+ * @return true if the file contents will be read
+ * from memory; false otherwise.
+ */
+ public boolean isInMemory()
+ {
+ return (dfos.isInMemory());
+ }
+
+
+ /**
+ * Returns the size of the file.
+ *
+ * @return The size of the file, in bytes.
+ */
+ public long getSize()
+ {
+ if (cachedContent != null)
+ {
+ return cachedContent.length;
+ }
+ else if (dfos.isInMemory())
+ {
+ return dfos.getData().length;
+ }
+ else
+ {
+ return dfos.getFile().length();
+ }
+ }
+
+
+ /**
+ * Returns the contents of the file as an array of bytes. If the
+ * contents of the file were not yet cached in memory, they will be
+ * loaded from the disk storage and cached.
+ *
+ * @return The contents of the file as an array of bytes.
+ */
+ public byte[] get()
+ {
+ if (dfos.isInMemory())
+ {
+ if (cachedContent == null)
+ {
+ cachedContent = dfos.getData();
+ }
+ return cachedContent;
+ }
+
+ byte[] fileData = new byte[(int) getSize()];
+ FileInputStream fis = null;
+
+ try
+ {
+ fis = new FileInputStream(dfos.getFile());
+ fis.read(fileData);
+ }
+ catch (IOException e)
+ {
+ fileData = null;
+ }
+ finally
+ {
+ if (fis != null)
+ {
+ try
+ {
+ fis.close();
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ }
+
+ return fileData;
+ }
+
+
+ /**
+ * Returns the contents of the file as a String, using the specified
+ * encoding. This method uses {@link #get()} to retrieve the
+ * contents of the file.
+ *
+ * @param encoding The character encoding to use.
+ *
+ * @return The contents of the file, as a string.
+ *
+ * @exception UnsupportedEncodingException if the requested character
+ * encoding is not available.
+ */
+ public String getString(String encoding)
+ throws UnsupportedEncodingException
+ {
+ return new String(get(), encoding);
+ }
+
+
+ /**
+ * Returns the contents of the file as a String, using the default
+ * character encoding. This method uses {@link #get()} to retrieve the
+ * contents of the file.
+ *
+ * @return The contents of the file, as a string.
+ */
+ public String getString()
+ {
+ return new String(get());
+ }
+
+
+ /**
+ * A convenience method to write an uploaded item to disk. The client code
+ * is not concerned with whether or not the item is stored in memory, or on
+ * disk in a temporary location. They just want to write the uploaded item
+ * to a file.
+ * File into which the uploaded item should
+ * be stored.
+ *
+ * @exception Exception if an error occurs.
+ */
+ public void write(File file) throws Exception
+ {
+ if (isInMemory())
+ {
+ FileOutputStream fout = null;
+ try
+ {
+ fout = new FileOutputStream(file);
+ fout.write(get());
+ }
+ finally
+ {
+ if (fout != null)
+ {
+ fout.close();
+ }
+ }
+ }
+ else
+ {
+ File outputFile = getStoreLocation();
+ if (outputFile != null)
+ {
+ /*
+ * The uploaded file is being stored on disk
+ * in a temporary location so move it to the
+ * desired file.
+ */
+ if (!outputFile.renameTo(file))
+ {
+ BufferedInputStream in = null;
+ BufferedOutputStream out = null;
+ try
+ {
+ in = new BufferedInputStream(
+ new FileInputStream(outputFile));
+ out = new BufferedOutputStream(
+ new FileOutputStream(file));
+ byte[] bytes = new byte[2048];
+ int s = 0;
+ while ((s = in.read(bytes)) != -1)
+ {
+ out.write(bytes, 0, s);
+ }
+ }
+ finally
+ {
+ try
+ {
+ in.close();
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ try
+ {
+ out.close();
+ }
+ catch (IOException e)
+ {
+ // ignore
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * For whatever reason we cannot write the
+ * file to disk.
+ */
+ throw new FileUploadException(
+ "Cannot write uploaded file to disk!");
+ }
+ }
+ }
+
+
+ /**
+ * Deletes the underlying storage for a file item, including deleting any
+ * associated temporary disk file. Although this storage will be deleted
+ * automatically when the FileItem instance is garbage
+ * collected, this method can be used to ensure that this is done at an
+ * earlier time, thus preserving system resources.
+ */
+ public void delete()
+ {
+ cachedContent = null;
+ File outputFile = getStoreLocation();
+ if (outputFile != null && outputFile.exists())
+ {
+ outputFile.delete();
+ }
+ }
+
+
+ /**
+ * Returns the name of the field in the multipart form corresponding to
+ * this file item.
+ *
+ * @return The name of the form field.
+ *
+ * @see #setFieldName(java.lang.String)
+ *
+ */
+ public String getFieldName()
+ {
+ return fieldName;
+ }
+
+
+ /**
+ * Sets the field name used to reference this file item.
+ *
+ * @param fieldName The name of the form field.
+ *
+ * @see #getFieldName()
+ *
+ */
+ public void setFieldName(String fieldName)
+ {
+ this.fieldName = fieldName;
+ }
+
+
+ /**
+ * Determines whether or not a FileItem instance represents
+ * a simple form field.
+ *
+ * @return true if the instance represents a simple form
+ * field; false if it represents an uploaded file.
+ *
+ * @see #setFormField(boolean)
+ *
+ */
+ public boolean isFormField()
+ {
+ return isFormField;
+ }
+
+
+ /**
+ * Specifies whether or not a FileItem instance represents
+ * a simple form field.
+ *
+ * @param state true if the instance represents a simple form
+ * field; false if it represents an uploaded file.
+ *
+ * @see #isFormField()
+ *
+ */
+ public void setFormField(boolean state)
+ {
+ isFormField = state;
+ }
+
+
+ /**
+ * Returns an {@link java.io.OutputStream OutputStream} that can
+ * be used for storing the contents of the file.
+ *
+ * @return An {@link java.io.OutputStream OutputStream} that can be used
+ * for storing the contensts of the file.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public OutputStream getOutputStream()
+ throws IOException
+ {
+ if (dfos == null)
+ {
+ File outputFile = getTempFile();
+ dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
+ }
+ return dfos;
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Returns the {@link java.io.File} object for the FileItem's
+ * data's temporary location on the disk. Note that for
+ * FileItems that have their data stored in memory,
+ * this method will return null. When handling large
+ * files, you can use {@link java.io.File#renameTo(java.io.File)} to
+ * move the file to new location without copying the data, if the
+ * source and destination locations reside within the same logical
+ * volume.
+ *
+ * @return The data file, or null if the data is stored in
+ * memory.
+ */
+ public File getStoreLocation()
+ {
+ return dfos.getFile();
+ }
+
+
+ // ------------------------------------------------------ Protected methods
+
+
+ /**
+ * Removes the file contents from the temporary storage.
+ */
+ protected void finalize()
+ {
+ File outputFile = dfos.getFile();
+
+ if (outputFile != null && outputFile.exists())
+ {
+ outputFile.delete();
+ }
+ }
+
+
+ /**
+ * Creates and returns a {@link java.io.File File} representing a uniquely
+ * named temporary file in the configured repository path.
+ *
+ * @return The {@link java.io.File File} to be used for temporary storage.
+ */
+ protected File getTempFile()
+ {
+ File tempDir = repository;
+ if (tempDir == null)
+ {
+ tempDir = new File(System.getProperty("java.io.tmpdir"));
+ }
+
+ String fileName = "upload_" + getUniqueId() + ".tmp";
+
+ File f = new File(tempDir, fileName);
+ f.deleteOnExit();
+ return f;
+ }
+
+
+ // -------------------------------------------------------- Private methods
+
+
+ /**
+ * Returns an identifier that is unique within the class loader used to
+ * load this class, but does not have random-like apearance.
+ *
+ * @return A String with the non-random looking instance identifier.
+ */
+ private static String getUniqueId()
+ {
+ int current;
+ synchronized (DefaultFileItem.class)
+ {
+ current = counter++;
+ }
+ String id = Integer.toString(current);
+
+ // If you manage to get more than 100 million of ids, you'll
+ // start getting ids longer than 8 characters.
+ if (current < 100000000)
+ {
+ id = ("00000000" + id).substring(id.length());
+ }
+ return id;
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/DefaultFileItemFactory.java b/java/org/apache/tomcat/util/http/fileupload/DefaultFileItemFactory.java
new file mode 100644
index 000000000..b710a0a67
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/DefaultFileItemFactory.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+import java.io.File;
+
+
+/**
+ *
+ *
+ * System.getProperty("java.io.tmpdir").
true if this is a plain form field;
+ * false otherwise.
+ * @param fileName The name of the uploaded file, if any, as supplied
+ * by the browser or other client.
+ *
+ * @return The newly created file item.
+ */
+ public FileItem createItem(
+ String fieldName,
+ String contentType,
+ boolean isFormField,
+ String fileName
+ )
+ {
+ return new DefaultFileItem(fieldName, contentType,
+ isFormField, fileName, sizeThreshold, repository);
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java b/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java
new file mode 100644
index 000000000..79c1fe758
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/DeferredFileOutputStream.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An output stream which will retain data in memory until a specified + * threshold is reached, and only then commit it to disk. If the stream is + * closed before the threshold is reached, the data will not be written to + * disk at all.
+ * + * @author Martin Cooper + * + * @version $Id: DeferredFileOutputStream.java,v 1.2 2003/05/31 22:31:08 martinc Exp $ + */ +public class DeferredFileOutputStream + extends ThresholdingOutputStream +{ + + // ----------------------------------------------------------- Data members + + + /** + * The output stream to which data will be written prior to the theshold + * being reached. + */ + private ByteArrayOutputStream memoryOutputStream; + + + /** + * The output stream to which data will be written after the theshold is + * reached. + */ + private FileOutputStream diskOutputStream; + + + /** + * The output stream to which data will be written at any given time. This + * will always be one ofmemoryOutputStream or
+ * diskOutputStream.
+ */
+ private OutputStream currentOutputStream;
+
+
+ /**
+ * The file to which output will be directed if the threshold is exceeded.
+ */
+ private File outputFile;
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs an instance of this class which will trigger an event at the
+ * specified threshold, and save data to a file beyond that point.
+ *
+ * @param threshold The number of bytes at which to trigger an event.
+ * @param outputFile The file to which data is saved beyond the threshold.
+ */
+ public DeferredFileOutputStream(int threshold, File outputFile)
+ {
+ super(threshold);
+ this.outputFile = outputFile;
+
+ memoryOutputStream = new ByteArrayOutputStream(threshold);
+ currentOutputStream = memoryOutputStream;
+ }
+
+
+ // --------------------------------------- ThresholdingOutputStream methods
+
+
+ /**
+ * Returns the current output stream. This may be memory based or disk
+ * based, depending on the current state with respect to the threshold.
+ *
+ * @return The underlying output stream.
+ *
+ * @exception IOException if an error occurs.
+ */
+ protected OutputStream getStream() throws IOException
+ {
+ return currentOutputStream;
+ }
+
+
+ /**
+ * Switches the underlying output stream from a memory based stream to one
+ * that is backed by disk. This is the point at which we realise that too
+ * much data is being written to keep in memory, so we elect to switch to
+ * disk-based storage.
+ *
+ * @exception IOException if an error occurs.
+ */
+ protected void thresholdReached() throws IOException
+ {
+ byte[] data = memoryOutputStream.toByteArray();
+ FileOutputStream fos = new FileOutputStream(outputFile);
+ fos.write(data);
+ diskOutputStream = fos;
+ currentOutputStream = fos;
+ memoryOutputStream = null;
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Determines whether or not the data for this output stream has been
+ * retained in memory.
+ *
+ * @return true if the data is available in memory;
+ * false otherwise.
+ */
+ public boolean isInMemory()
+ {
+ return (!isThresholdExceeded());
+ }
+
+
+ /**
+ * Returns the data for this output stream as an array of bytes, assuming
+ * that the data has been retained in memory. If the data was written to
+ * disk, this method returns null.
+ *
+ * @return The data for this output stream, or null if no such
+ * data is available.
+ */
+ public byte[] getData()
+ {
+ if (memoryOutputStream != null)
+ {
+ return memoryOutputStream.toByteArray();
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns the data for this output stream as a File, assuming
+ * that the data was written to disk. If the data was retained in memory,
+ * this method returns null.
+ *
+ * @return The file for this output stream, or null if no such
+ * file exists.
+ */
+ public File getFile()
+ {
+ return outputFile;
+ }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/DiskFileUpload.java b/java/org/apache/tomcat/util/http/fileupload/DiskFileUpload.java
new file mode 100644
index 000000000..5b1bac9d8
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/DiskFileUpload.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+import java.io.File;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * High level API for processing file uploads.
+ * + *This class handles multiple files per single HTML widget, sent using
+ * multipart/mixed encoding type, as specified by
+ * RFC 1867. Use {@link
+ * #parseRequest(HttpServletRequest)} to acquire a list of {@link
+ * org.apache.tomcat.util.http.fileupload.FileItem}s associated with a given HTML
+ * widget.
Individual parts will be stored in temporary disk storage or in memory, + * depending on their size, and will be available as {@link + * org.apache.tomcat.util.http.fileupload.FileItem}s.
+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id: DiskFileUpload.java,v 1.3 2003/06/01 00:18:13 martinc Exp $ + */ +public class DiskFileUpload + extends FileUploadBase + { + + // ----------------------------------------------------------- Data members + + + /** + * The factory to use to create new form items. + */ + private DefaultFileItemFactory fileItemFactory; + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an instance of this class which uses the default factory to + * createFileItem instances.
+ *
+ * @see #DiskFileUpload(DefaultFileItemFactory fileItemFactory)
+ */
+ public DiskFileUpload()
+ {
+ super();
+ this.fileItemFactory = new DefaultFileItemFactory();
+ }
+
+
+ /**
+ * Constructs an instance of this class which uses the supplied factory to
+ * create FileItem instances.
+ *
+ * @see #DiskFileUpload()
+ */
+ public DiskFileUpload(DefaultFileItemFactory fileItemFactory)
+ {
+ super();
+ this.fileItemFactory = fileItemFactory;
+ }
+
+
+ // ----------------------------------------------------- Property accessors
+
+
+ /**
+ * Returns the factory class used when creating file items.
+ *
+ * @return The factory class for new file items.
+ */
+ public FileItemFactory getFileItemFactory()
+ {
+ return fileItemFactory;
+ }
+
+
+ /**
+ * Sets the factory class to use when creating file items. The factory must
+ * be an instance of DefaultFileItemFactory or a subclass
+ * thereof, or else a ClassCastException will be thrown.
+ *
+ * @param factory The factory class for new file items.
+ */
+ public void setFileItemFactory(FileItemFactory factory)
+ {
+ this.fileItemFactory = (DefaultFileItemFactory) factory;
+ }
+
+
+ /**
+ * Returns the size threshold beyond which files are written directly to
+ * disk.
+ *
+ * @return The size threshold, in bytes.
+ *
+ * @see #setSizeThreshold(int)
+ */
+ public int getSizeThreshold()
+ {
+ return fileItemFactory.getSizeThreshold();
+ }
+
+
+ /**
+ * Sets the size threshold beyond which files are written directly to disk.
+ *
+ * @param sizeThreshold The size threshold, in bytes.
+ *
+ * @see #getSizeThreshold()
+ */
+ public void setSizeThreshold(int sizeThreshold)
+ {
+ fileItemFactory.setSizeThreshold(sizeThreshold);
+ }
+
+
+ /**
+ * Returns the location used to temporarily store files that are larger
+ * than the configured size threshold.
+ *
+ * @return The path to the temporary file location.
+ *
+ * @see #setRepositoryPath(String)
+ */
+ public String getRepositoryPath()
+ {
+ return fileItemFactory.getRepository().getPath();
+ }
+
+
+ /**
+ * Sets the location used to temporarily store files that are larger
+ * than the configured size threshold.
+ *
+ * @param repositoryPath The path to the temporary file location.
+ *
+ * @see #getRepositoryPath()
+ */
+ public void setRepositoryPath(String repositoryPath)
+ {
+ fileItemFactory.setRepository(new File(repositoryPath));
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Processes an RFC 1867
+ * compliant multipart/form-data stream. If files are stored
+ * on disk, the path is given by getRepository().
+ *
+ * @param req The servlet request to be parsed. Must be non-null.
+ * @param sizeThreshold The max size in bytes to be stored in memory.
+ * @param sizeMax The maximum allowed upload size, in bytes.
+ * @param path The location where the files should be stored.
+ *
+ * @return A list of FileItem instances parsed from the
+ * request, in the order that they were transmitted.
+ *
+ * @exception FileUploadException if there are problems reading/parsing
+ * the request or storing files.
+ */
+ public List /* FileItem */ parseRequest(HttpServletRequest req,
+ int sizeThreshold,
+ long sizeMax, String path)
+ throws FileUploadException
+ {
+ setSizeThreshold(sizeThreshold);
+ setSizeMax(sizeMax);
+ setRepositoryPath(path);
+ return parseRequest(req);
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileItem.java b/java/org/apache/tomcat/util/http/fileupload/FileItem.java
new file mode 100644
index 000000000..5edecda93
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/FileItem.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ * This class represents a file or form item that was received within a
+ * multipart/form-data POST request.
+ *
+ *
After retrieving an instance of this class from a {@link + * org.apache.tomcat.util.http.fileupload.FileUpload FileUpload} instance (see + * {@link org.apache.tomcat.util.http.fileupload.FileUpload + * #parseRequest(javax.servlet.http.HttpServletRequest)}), you may + * either request all contents of the file at once using {@link #get()} or + * request an {@link java.io.InputStream InputStream} with + * {@link #getInputStream()} and process the file without attempting to load + * it into memory, which may come handy with large files. + * + *
While this interface does not extend
+ * javax.activation.DataSource per se (to avoid a seldom used
+ * dependency), several of the defined methods are specifically defined with
+ * the same signatures as methods in that interface. This allows an
+ * implementation of this interface to also implement
+ * javax.activation.DataSource with minimal additional work.
+ *
+ * @author Rafal Krzewski
+ * @author Sean Legassick
+ * @author Jason van Zyl
+ * @author Martin Cooper
+ *
+ * @version $Id: FileItem.java,v 1.15 2003/06/01 17:33:24 martinc Exp $
+ */
+public interface FileItem
+ extends Serializable
+{
+
+
+ // ------------------------------- Methods from javax.activation.DataSource
+
+
+ /**
+ * Returns an {@link java.io.InputStream InputStream} that can be
+ * used to retrieve the contents of the file.
+ *
+ * @return An {@link java.io.InputStream InputStream} that can be
+ * used to retrieve the contents of the file.
+ *
+ * @exception IOException if an error occurs.
+ */
+ InputStream getInputStream()
+ throws IOException;
+
+
+ /**
+ * Returns the content type passed by the browser or null if
+ * not defined.
+ *
+ * @return The content type passed by the browser or null if
+ * not defined.
+ */
+ String getContentType();
+
+
+ /**
+ * Returns the original filename in the client's filesystem, as provided by
+ * the browser (or other client software). In most cases, this will be the
+ * base file name, without path information. However, some clients, such as
+ * the Opera browser, do include path information.
+ *
+ * @return The original filename in the client's filesystem.
+ */
+ String getName();
+
+
+ // ------------------------------------------------------- FileItem methods
+
+
+ /**
+ * Provides a hint as to whether or not the file contents will be read
+ * from memory.
+ *
+ * @return true if the file contents will be read from memory;
+ * false otherwise.
+ */
+ boolean isInMemory();
+
+
+ /**
+ * Returns the size of the file item.
+ *
+ * @return The size of the file item, in bytes.
+ */
+ long getSize();
+
+
+ /**
+ * Returns the contents of the file item as an array of bytes.
+ *
+ * @return The contents of the file item as an array of bytes.
+ */
+ byte[] get();
+
+
+ /**
+ * Returns the contents of the file item as a String, using the specified
+ * encoding. This method uses {@link #get()} to retrieve the
+ * contents of the item.
+ *
+ * @param encoding The character encoding to use.
+ *
+ * @return The contents of the item, as a string.
+ *
+ * @exception UnsupportedEncodingException if the requested character
+ * encoding is not available.
+ */
+ String getString(String encoding)
+ throws UnsupportedEncodingException;
+
+
+ /**
+ * Returns the contents of the file item as a String, using the default
+ * character encoding. This method uses {@link #get()} to retrieve the
+ * contents of the item.
+ *
+ * @return The contents of the item, as a string.
+ */
+ String getString();
+
+
+ /**
+ * A convenience method to write an uploaded item to disk. The client code
+ * is not concerned with whether or not the item is stored in memory, or on
+ * disk in a temporary location. They just want to write the uploaded item
+ * to a file.
+ *
+ * This method is not guaranteed to succeed if called more than once for
+ * the same item. This allows a particular implementation to use, for
+ * example, file renaming, where possible, rather than copying all of the
+ * underlying data, thus gaining a significant performance benefit.
+ *
+ * @param file The File into which the uploaded item should
+ * be stored.
+ *
+ * @exception Exception if an error occurs.
+ */
+ void write(File file) throws Exception;
+
+
+ /**
+ * Deletes the underlying storage for a file item, including deleting any
+ * associated temporary disk file. Although this storage will be deleted
+ * automatically when the FileItem instance is garbage
+ * collected, this method can be used to ensure that this is done at an
+ * earlier time, thus preserving system resources.
+ */
+ void delete();
+
+
+ /**
+ * Returns the name of the field in the multipart form corresponding to
+ * this file item.
+ *
+ * @return The name of the form field.
+ */
+ String getFieldName();
+
+
+ /**
+ * Sets the field name used to reference this file item.
+ *
+ * @param name The name of the form field.
+ */
+ void setFieldName(String name);
+
+
+ /**
+ * Determines whether or not a FileItem instance represents
+ * a simple form field.
+ *
+ * @return true if the instance represents a simple form
+ * field; false if it represents an uploaded file.
+ */
+ boolean isFormField();
+
+
+ /**
+ * Specifies whether or not a FileItem instance represents
+ * a simple form field.
+ *
+ * @param state true if the instance represents a simple form
+ * field; false if it represents an uploaded file.
+ */
+ void setFormField(boolean state);
+
+
+ /**
+ * Returns an {@link java.io.OutputStream OutputStream} that can
+ * be used for storing the contents of the file.
+ *
+ * @return An {@link java.io.OutputStream OutputStream} that can be used
+ * for storing the contensts of the file.
+ *
+ * @exception IOException if an error occurs.
+ */
+ OutputStream getOutputStream() throws IOException;
+
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileItemFactory.java b/java/org/apache/tomcat/util/http/fileupload/FileItemFactory.java
new file mode 100644
index 000000000..b85dbe8bf
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/FileItemFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+/**
+ *
A factory interface for creating {@link FileItem} instances. Factories + * can provide their own custom configuration, over and above that provided + * by the default file upload implementation.
+ * + * @author Martin Cooper + * + * @version $Id: FileItemFactory.java,v 1.1 2003/04/27 17:30:06 martinc Exp $ + */ +public interface FileItemFactory +{ + + /** + * Create a new {@link FileItem} instance from the supplied parameters and + * any local factory configuration. + * + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormFieldtrue if this is a plain form field;
+ * false otherwise.
+ * @param fileName The name of the uploaded file, if any, as supplied
+ * by the browser or other client.
+ *
+ * @return The newly created file item.
+ */
+ FileItem createItem(
+ String fieldName,
+ String contentType,
+ boolean isFormField,
+ String fileName
+ );
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileUpload.java b/java/org/apache/tomcat/util/http/fileupload/FileUpload.java
new file mode 100644
index 000000000..af4dfd8b0
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/FileUpload.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+/**
+ * High level API for processing file uploads.
+ * + *This class handles multiple files per single HTML widget, sent using
+ * multipart/mixed encoding type, as specified by
+ * RFC 1867. Use {@link
+ * #parseRequest(HttpServletRequest)} to acquire a list of {@link
+ * org.apache.tomcat.util.http.fileupload.FileItem}s associated with a given HTML
+ * widget.
How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.
+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id: FileUpload.java,v 1.23 2003/06/24 05:45:43 martinc Exp $ + */ +public class FileUpload + extends FileUploadBase + { + + // ----------------------------------------------------------- Data members + + + /** + * The factory to use to create new form items. + */ + private FileItemFactory fileItemFactory; + + + // ----------------------------------------------------------- Constructors + + + /** + * Constructs an instance of this class which uses the default factory to + * createFileItem instances.
+ *
+ * @see #FileUpload(FileItemFactory)
+ */
+ public FileUpload()
+ {
+ super();
+ }
+
+
+ /**
+ * Constructs an instance of this class which uses the supplied factory to
+ * create FileItem instances.
+ *
+ * @see #FileUpload()
+ */
+ public FileUpload(FileItemFactory fileItemFactory)
+ {
+ super();
+ this.fileItemFactory = fileItemFactory;
+ }
+
+
+ // ----------------------------------------------------- Property accessors
+
+
+ /**
+ * Returns the factory class used when creating file items.
+ *
+ * @return The factory class for new file items.
+ */
+ public FileItemFactory getFileItemFactory()
+ {
+ return fileItemFactory;
+ }
+
+
+ /**
+ * Sets the factory class to use when creating file items.
+ *
+ * @param factory The factory class for new file items.
+ */
+ public void setFileItemFactory(FileItemFactory factory)
+ {
+ this.fileItemFactory = factory;
+ }
+
+
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
new file mode 100644
index 000000000..1bfa3780d
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * High level API for processing file uploads.
+ * + *This class handles multiple files per single HTML widget, sent using
+ * multipart/mixed encoding type, as specified by
+ * RFC 1867. Use {@link
+ * #parseRequest(HttpServletRequest)} to acquire a list of {@link
+ * org.apache.tomcat.util.http.fileupload.FileItem}s associated with a given HTML
+ * widget.
How the data for individual parts is stored is determined by the factory + * used to create them; a given part may be in memory, on disk, or somewhere + * else.
+ * + * @author Rafal Krzewski + * @author Daniel Rall + * @author Jason van Zyl + * @author John McNally + * @author Martin Cooper + * @author Sean C. Sullivan + * + * @version $Id: FileUploadBase.java,v 1.3 2003/06/01 00:18:13 martinc Exp $ + */ +public abstract class FileUploadBase +{ + + // ---------------------------------------------------------- Class methods + + + /** + * Utility method that determines whether the request contains multipart + * content. + * + * @param req The servlet request to be evaluated. Must be non-null. + * + * @returntrue if the request is multipart;
+ * false otherwise.
+ */
+ public static final boolean isMultipartContent(HttpServletRequest req)
+ {
+ String contentType = req.getHeader(CONTENT_TYPE);
+ if (contentType == null)
+ {
+ return false;
+ }
+ if (contentType.startsWith(MULTIPART))
+ {
+ return true;
+ }
+ return false;
+ }
+
+
+ // ----------------------------------------------------- Manifest constants
+
+
+ /**
+ * HTTP content type header name.
+ */
+ public static final String CONTENT_TYPE = "Content-type";
+
+
+ /**
+ * HTTP content disposition header name.
+ */
+ public static final String CONTENT_DISPOSITION = "Content-disposition";
+
+
+ /**
+ * Content-disposition value for form data.
+ */
+ public static final String FORM_DATA = "form-data";
+
+
+ /**
+ * Content-disposition value for file attachment.
+ */
+ public static final String ATTACHMENT = "attachment";
+
+
+ /**
+ * Part of HTTP content type header.
+ */
+ public static final String MULTIPART = "multipart/";
+
+
+ /**
+ * HTTP content type header for multipart forms.
+ */
+ public static final String MULTIPART_FORM_DATA = "multipart/form-data";
+
+
+ /**
+ * HTTP content type header for multiple uploads.
+ */
+ public static final String MULTIPART_MIXED = "multipart/mixed";
+
+
+ /**
+ * The maximum length of a single header line that will be parsed
+ * (1024 bytes).
+ */
+ public static final int MAX_HEADER_SIZE = 1024;
+
+
+ // ----------------------------------------------------------- Data members
+
+
+ /**
+ * The maximum size permitted for an uploaded file. A value of -1 indicates
+ * no maximum.
+ */
+ private long sizeMax = -1;
+
+
+ /**
+ * The content encoding to use when reading part headers.
+ */
+ private String headerEncoding;
+
+
+ // ----------------------------------------------------- Property accessors
+
+
+ /**
+ * Returns the factory class used when creating file items.
+ *
+ * @return The factory class for new file items.
+ */
+ public abstract FileItemFactory getFileItemFactory();
+
+
+ /**
+ * Sets the factory class to use when creating file items.
+ *
+ * @param factory The factory class for new file items.
+ */
+ public abstract void setFileItemFactory(FileItemFactory factory);
+
+
+ /**
+ * Returns the maximum allowed upload size.
+ *
+ * @return The maximum allowed size, in bytes.
+ *
+ * @see #setSizeMax(long)
+ *
+ */
+ public long getSizeMax()
+ {
+ return sizeMax;
+ }
+
+
+ /**
+ * Sets the maximum allowed upload size. If negative, there is no maximum.
+ *
+ * @param sizeMax The maximum allowed size, in bytes, or -1 for no maximum.
+ *
+ * @see #getSizeMax()
+ *
+ */
+ public void setSizeMax(long sizeMax)
+ {
+ this.sizeMax = sizeMax;
+ }
+
+
+ /**
+ * Retrieves the character encoding used when reading the headers of an
+ * individual part. When not specified, or null, the platform
+ * default encoding is used.
+ *
+ * @return The encoding used to read part headers.
+ */
+ public String getHeaderEncoding()
+ {
+ return headerEncoding;
+ }
+
+
+ /**
+ * Specifies the character encoding to be used when reading the headers of
+ * individual parts. When not specified, or null, the platform
+ * default encoding is used.
+ *
+ * @param encoding The encoding used to read part headers.
+ */
+ public void setHeaderEncoding(String encoding)
+ {
+ headerEncoding = encoding;
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Processes an RFC 1867
+ * compliant multipart/form-data stream. If files are stored
+ * on disk, the path is given by getRepository().
+ *
+ * @param req The servlet request to be parsed.
+ *
+ * @return A list of FileItem instances parsed from the
+ * request, in the order that they were transmitted.
+ *
+ * @exception FileUploadException if there are problems reading/parsing
+ * the request or storing files.
+ */
+ public List /* FileItem */ parseRequest(HttpServletRequest req)
+ throws FileUploadException
+ {
+ if (null == req)
+ {
+ throw new NullPointerException("req parameter");
+ }
+
+ ArrayList items = new ArrayList();
+ String contentType = req.getHeader(CONTENT_TYPE);
+
+ if ((null == contentType) || (!contentType.startsWith(MULTIPART)))
+ {
+ throw new InvalidContentTypeException(
+ "the request doesn't contain a "
+ + MULTIPART_FORM_DATA
+ + " or "
+ + MULTIPART_MIXED
+ + " stream, content type header is "
+ + contentType);
+ }
+ int requestSize = req.getContentLength();
+
+ if (requestSize == -1)
+ {
+ throw new UnknownSizeException(
+ "the request was rejected because it's size is unknown");
+ }
+
+ if (sizeMax >= 0 && requestSize > sizeMax)
+ {
+ throw new SizeLimitExceededException(
+ "the request was rejected because "
+ + "it's size exceeds allowed range");
+ }
+
+ try
+ {
+ int boundaryIndex = contentType.indexOf("boundary=");
+ if (boundaryIndex < 0)
+ {
+ throw new FileUploadException(
+ "the request was rejected because "
+ + "no multipart boundary was found");
+ }
+ byte[] boundary = contentType.substring(
+ boundaryIndex + 9).getBytes();
+
+ InputStream input = req.getInputStream();
+
+ MultipartStream multi = new MultipartStream(input, boundary);
+ multi.setHeaderEncoding(headerEncoding);
+
+ boolean nextPart = multi.skipPreamble();
+ while (nextPart)
+ {
+ Map headers = parseHeaders(multi.readHeaders());
+ String fieldName = getFieldName(headers);
+ if (fieldName != null)
+ {
+ String subContentType = getHeader(headers, CONTENT_TYPE);
+ if (subContentType != null && subContentType
+ .startsWith(MULTIPART_MIXED))
+ {
+ // Multiple files.
+ byte[] subBoundary =
+ subContentType.substring(
+ subContentType
+ .indexOf("boundary=") + 9).getBytes();
+ multi.setBoundary(subBoundary);
+ boolean nextSubPart = multi.skipPreamble();
+ while (nextSubPart)
+ {
+ headers = parseHeaders(multi.readHeaders());
+ if (getFileName(headers) != null)
+ {
+ FileItem item =
+ createItem(headers, false);
+ OutputStream os = item.getOutputStream();
+ try
+ {
+ multi.readBodyData(os);
+ }
+ finally
+ {
+ os.close();
+ }
+ items.add(item);
+ }
+ else
+ {
+ // Ignore anything but files inside
+ // multipart/mixed.
+ multi.discardBodyData();
+ }
+ nextSubPart = multi.readBoundary();
+ }
+ multi.setBoundary(boundary);
+ }
+ else
+ {
+ if (getFileName(headers) != null)
+ {
+ // A single file.
+ FileItem item = createItem(headers, false);
+ OutputStream os = item.getOutputStream();
+ try
+ {
+ multi.readBodyData(os);
+ }
+ finally
+ {
+ os.close();
+ }
+ items.add(item);
+ }
+ else
+ {
+ // A form field.
+ FileItem item = createItem(headers, true);
+ OutputStream os = item.getOutputStream();
+ try
+ {
+ multi.readBodyData(os);
+ }
+ finally
+ {
+ os.close();
+ }
+ items.add(item);
+ }
+ }
+ }
+ else
+ {
+ // Skip this part.
+ multi.discardBodyData();
+ }
+ nextPart = multi.readBoundary();
+ }
+ }
+ catch (IOException e)
+ {
+ throw new FileUploadException(
+ "Processing of " + MULTIPART_FORM_DATA
+ + " request failed. " + e.getMessage());
+ }
+
+ return items;
+ }
+
+
+ // ------------------------------------------------------ Protected methods
+
+
+ /**
+ * Retrieves the file name from the Content-disposition
+ * header.
+ *
+ * @param headers A Map containing the HTTP request headers.
+ *
+ * @return The file name for the current encapsulation.
+ */
+ protected String getFileName(Map /* String, String */ headers)
+ {
+ String fileName = null;
+ String cd = getHeader(headers, CONTENT_DISPOSITION);
+ if (cd.startsWith(FORM_DATA) || cd.startsWith(ATTACHMENT))
+ {
+ int start = cd.indexOf("filename=\"");
+ int end = cd.indexOf('"', start + 10);
+ if (start != -1 && end != -1)
+ {
+ fileName = cd.substring(start + 10, end).trim();
+ }
+ }
+ return fileName;
+ }
+
+
+ /**
+ * Retrieves the field name from the Content-disposition
+ * header.
+ *
+ * @param headers A Map containing the HTTP request headers.
+ *
+ * @return The field name for the current encapsulation.
+ */
+ protected String getFieldName(Map /* String, String */ headers)
+ {
+ String fieldName = null;
+ String cd = getHeader(headers, CONTENT_DISPOSITION);
+ if (cd != null && cd.startsWith(FORM_DATA))
+ {
+ int start = cd.indexOf("name=\"");
+ int end = cd.indexOf('"', start + 6);
+ if (start != -1 && end != -1)
+ {
+ fieldName = cd.substring(start + 6, end);
+ }
+ }
+ return fieldName;
+ }
+
+
+ /**
+ * Creates a new {@link FileItem} instance.
+ *
+ * @param headers A Map containing the HTTP request
+ * headers.
+ * @param isFormField Whether or not this item is a form field, as
+ * opposed to a file.
+ *
+ * @return A newly created FileItem instance.
+ *
+ * @exception FileUploadException if an error occurs.
+ */
+ protected FileItem createItem(Map /* String, String */ headers,
+ boolean isFormField)
+ throws FileUploadException
+ {
+ return getFileItemFactory().createItem(getFieldName(headers),
+ getHeader(headers, CONTENT_TYPE),
+ isFormField,
+ getFileName(headers));
+ }
+
+
+ /**
+ * Parses the header-part and returns as key/value
+ * pairs.
+ *
+ *
If there are multiple headers of the same names, the name
+ * will map to a comma-separated list containing the values.
+ *
+ * @param headerPart The header-part of the current
+ * encapsulation.
+ *
+ * @return A Map containing the parsed HTTP request headers.
+ */
+ protected Map /* String, String */ parseHeaders(String headerPart)
+ {
+ Map headers = new HashMap();
+ char buffer[] = new char[MAX_HEADER_SIZE];
+ boolean done = false;
+ int j = 0;
+ int i;
+ String header, headerName, headerValue;
+ try
+ {
+ while (!done)
+ {
+ i = 0;
+ // Copy a single line of characters into the buffer,
+ // omitting trailing CRLF.
+ while (i < 2 || buffer[i - 2] != '\r' || buffer[i - 1] != '\n')
+ {
+ buffer[i++] = headerPart.charAt(j++);
+ }
+ header = new String(buffer, 0, i - 2);
+ if (header.equals(""))
+ {
+ done = true;
+ }
+ else
+ {
+ if (header.indexOf(':') == -1)
+ {
+ // This header line is malformed, skip it.
+ continue;
+ }
+ headerName = header.substring(0, header.indexOf(':'))
+ .trim().toLowerCase();
+ headerValue =
+ header.substring(header.indexOf(':') + 1).trim();
+ if (getHeader(headers, headerName) != null)
+ {
+ // More that one heder of that name exists,
+ // append to the list.
+ headers.put(headerName,
+ getHeader(headers, headerName) + ','
+ + headerValue);
+ }
+ else
+ {
+ headers.put(headerName, headerValue);
+ }
+ }
+ }
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ // Headers were malformed. continue with all that was
+ // parsed.
+ }
+ return headers;
+ }
+
+
+ /**
+ * Returns the header with the specified name from the supplied map. The
+ * header lookup is case-insensitive.
+ *
+ * @param headers A Map containing the HTTP request headers.
+ * @param name The name of the header to return.
+ *
+ * @return The value of specified header, or a comma-separated list if
+ * there were multiple headers of that name.
+ */
+ protected final String getHeader(Map /* String, String */ headers,
+ String name)
+ {
+ return (String) headers.get(name.toLowerCase());
+ }
+
+
+ /**
+ * Thrown to indicate that the request is not a multipart request.
+ */
+ public static class InvalidContentTypeException
+ extends FileUploadException
+ {
+ /**
+ * Constructs a InvalidContentTypeException with no
+ * detail message.
+ */
+ public InvalidContentTypeException()
+ {
+ super();
+ }
+
+ /**
+ * Constructs an InvalidContentTypeException with
+ * the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public InvalidContentTypeException(String message)
+ {
+ super(message);
+ }
+ }
+
+
+ /**
+ * Thrown to indicate that the request size is not specified.
+ */
+ public static class UnknownSizeException
+ extends FileUploadException
+ {
+ /**
+ * Constructs a UnknownSizeException with no
+ * detail message.
+ */
+ public UnknownSizeException()
+ {
+ super();
+ }
+
+ /**
+ * Constructs an UnknownSizeException with
+ * the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public UnknownSizeException(String message)
+ {
+ super(message);
+ }
+ }
+
+
+ /**
+ * Thrown to indicate that the request size exceeds the configured maximum.
+ */
+ public static class SizeLimitExceededException
+ extends FileUploadException
+ {
+ /**
+ * Constructs a SizeExceededException with no
+ * detail message.
+ */
+ public SizeLimitExceededException()
+ {
+ super();
+ }
+
+ /**
+ * Constructs an SizeExceededException with
+ * the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public SizeLimitExceededException(String message)
+ {
+ super(message);
+ }
+ }
+
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java b/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java
new file mode 100644
index 000000000..bbb21db60
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+/**
+ * Exception for errors encountered while processing the request.
+ *
+ * @author John McNally
+ * @version $Id: FileUploadException.java,v 1.7 2003/04/27 17:30:06 martinc Exp $
+ */
+public class FileUploadException
+ extends Exception
+{
+
+ /**
+ * Constructs a new FileUploadException without message.
+ */
+ public FileUploadException()
+ {
+ }
+
+ /**
+ * Constructs a new FileUploadException with specified detail
+ * message.
+ *
+ * @param msg the error message.
+ */
+ public FileUploadException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
new file mode 100644
index 000000000..97a22d1e3
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
@@ -0,0 +1,891 @@
+/*
+ * Copyright 2001-2006 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.tomcat.util.http.fileupload;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+
+/**
+ *
Low level API for processing file uploads. + * + *
This class can be used to process data streams conforming to MIME + * 'multipart' format as defined in + * RFC 1867. Arbitrarily + * large amounts of data in the stream can be processed under constant + * memory usage. + * + *
The format of the stream is defined in the following way:
+ *
+ *
+ * multipart-body := preamble 1*encapsulation close-delimiter epilogue
+ *
+ *
+ * encapsulation := delimiter body CRLF
+ * delimiter := "--" boundary CRLF
+ * close-delimiter := "--" boudary "--"
+ * preamble := <ignore>
+ * epilogue := <ignore>
+ * body := header-part CRLF body-part
+ * header-part := 1*header CRLF
+ * header := header-name ":" header-value
+ * header-name := <printable ascii characters except ":">
+ * header-value := <any ascii characters except CR & LF>
+ * body-data := <arbitrary data>
+ *
Note that body-data can contain another mulipart entity. There + * is limited support for single pass processing of such nested + * streams. The nested stream is required to have a + * boundary token of the same length as the parent stream (see {@link + * #setBoundary(byte[])}). + * + *
Here is an exaple of usage of this class.
+ *
+ *
+ * try {
+ * MultipartStream multipartStream = new MultipartStream(input,
+ * boundary);
+ * boolean nextPart = malitPartStream.skipPreamble();
+ * OutputStream output;
+ * while(nextPart) {
+ * header = chunks.readHeader();
+ * // process headers
+ * // create some output stream
+ * multipartStream.readBodyPart(output);
+ * nextPart = multipartStream.readBoundary();
+ * }
+ * } catch(MultipartStream.MalformedStreamException e) {
+ * // the stream failed to follow required syntax
+ * } catch(IOException) {
+ * // a read or write error occurred
+ * }
+ *
+ *
+ *
+ * @author Rafal Krzewski
+ * @author Martin Cooper
+ * @author Sean C. Sullivan
+ *
+ * @version $Id: MultipartStream.java,v 1.12 2003/06/01 00:18:13 martinc Exp $
+ */
+public class MultipartStream
+{
+
+ // ----------------------------------------------------- Manifest constants
+
+
+ /**
+ * The maximum length of header-part that will be
+ * processed (10 kilobytes = 10240 bytes.).
+ */
+ public static final int HEADER_PART_SIZE_MAX = 10240;
+
+
+ /**
+ * The default length of the buffer used for processing a request.
+ */
+ protected static final int DEFAULT_BUFSIZE = 4096;
+
+
+ /**
+ * A byte sequence that marks the end of header-part
+ * (CRLFCRLF).
+ */
+ protected static final byte[] HEADER_SEPARATOR = {0x0D, 0x0A, 0x0D, 0x0A};
+
+
+ /**
+ * A byte sequence that that follows a delimiter that will be
+ * followed by an encapsulation (CRLF).
+ */
+ protected static final byte[] FIELD_SEPARATOR = { 0x0D, 0x0A };
+
+
+ /**
+ * A byte sequence that that follows a delimiter of the last
+ * encapsulation in the stream (--).
+ */
+ protected static final byte[] STREAM_TERMINATOR = { 0x2D, 0x2D };
+
+
+ // ----------------------------------------------------------- Data members
+
+
+ /**
+ * The input stream from which data is read.
+ */
+ private InputStream input;
+
+
+ /**
+ * The length of the boundary token plus the leading CRLF--.
+ */
+ private int boundaryLength;
+
+
+ /**
+ * The amount of data, in bytes, that must be kept in the buffer in order
+ * to detect delimiters reliably.
+ */
+ private int keepRegion;
+
+
+ /**
+ * The byte sequence that partitions the stream.
+ */
+ private byte[] boundary;
+
+
+ /**
+ * The length of the buffer used for processing the request.
+ */
+ private int bufSize;
+
+
+ /**
+ * The buffer used for processing the request.
+ */
+ private byte[] buffer;
+
+
+ /**
+ * The index of first valid character in the buffer.
+ * Constructs a MultipartStream with a custom size buffer.
+ *
+ *
Note that the buffer must be at least big enough to contain the
+ * boundary string, plus 4 characters for CR/LF and double dash, plus at
+ * least one byte of data. Too small a buffer size setting will degrade
+ * performance.
+ *
+ * @param input The InputStream to serve as a data source.
+ * @param boundary The token used for dividing the stream into
+ * encapsulations.
+ * @param bufSize The size of the buffer to be used, in bytes.
+ *
+ *
+ * @see #MultipartStream()
+ * @see #MultipartStream(InputStream, byte[])
+ *
+ */
+ public MultipartStream(InputStream input,
+ byte[] boundary,
+ int bufSize)
+ {
+ this.input = input;
+ this.bufSize = bufSize;
+ this.buffer = new byte[bufSize];
+
+ // We prepend CR/LF to the boundary to chop trailng CR/LF from
+ // body-data tokens.
+ this.boundary = new byte[boundary.length + 4];
+ this.boundaryLength = boundary.length + 4;
+ this.keepRegion = boundary.length + 3;
+ this.boundary[0] = 0x0D;
+ this.boundary[1] = 0x0A;
+ this.boundary[2] = 0x2D;
+ this.boundary[3] = 0x2D;
+ System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
+
+ head = 0;
+ tail = 0;
+ }
+
+
+ /**
+ *
Constructs a MultipartStream with a default size buffer.
+ *
+ * @param input The InputStream to serve as a data source.
+ * @param boundary The token used for dividing the stream into
+ * encapsulations.
+ *
+ * @exception IOException when an error occurs.
+ *
+ * @see #MultipartStream()
+ * @see #MultipartStream(InputStream, byte[], int)
+ *
+ */
+ public MultipartStream(InputStream input,
+ byte[] boundary)
+ throws IOException
+ {
+ this(input, boundary, DEFAULT_BUFSIZE);
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Retrieves the character encoding used when reading the headers of an
+ * individual part. When not specified, or null, the platform
+ * default encoding is used.
+
+ *
+ * @return The encoding used to read part headers.
+ */
+ public String getHeaderEncoding()
+ {
+ return headerEncoding;
+ }
+
+
+ /**
+ * Specifies the character encoding to be used when reading the headers of
+ * individual parts. When not specified, or null, the platform
+ * default encoding is used.
+ *
+ * @param encoding The encoding used to read part headers.
+ */
+ public void setHeaderEncoding(String encoding)
+ {
+ headerEncoding = encoding;
+ }
+
+
+ /**
+ * Reads a byte from the buffer, and refills it as
+ * necessary.
+ *
+ * @return The next byte from the input stream.
+ *
+ * @exception IOException if there is no more data available.
+ */
+ public byte readByte()
+ throws IOException
+ {
+ // Buffer depleted ?
+ if (head == tail)
+ {
+ head = 0;
+ // Refill.
+ tail = input.read(buffer, head, bufSize);
+ if (tail == -1)
+ {
+ // No more data available.
+ throw new IOException("No more data is available");
+ }
+ }
+ return buffer[head++];
+ }
+
+
+ /**
+ * Skips a boundary token, and checks whether more
+ * encapsulations are contained in the stream.
+ *
+ * @return true if there are more encapsulations in
+ * this stream; false otherwise.
+ *
+ * @exception MalformedStreamException if the stream ends unexpecetedly or
+ * fails to follow required syntax.
+ */
+ public boolean readBoundary()
+ throws MalformedStreamException
+ {
+ byte[] marker = new byte[2];
+ boolean nextChunk = false;
+
+ head += boundaryLength;
+ try
+ {
+ marker[0] = readByte();
+ marker[1] = readByte();
+ if (arrayequals(marker, STREAM_TERMINATOR, 2))
+ {
+ nextChunk = false;
+ }
+ else if (arrayequals(marker, FIELD_SEPARATOR, 2))
+ {
+ nextChunk = true;
+ }
+ else
+ {
+ throw new MalformedStreamException(
+ "Unexpected characters follow a boundary");
+ }
+ }
+ catch (IOException e)
+ {
+ throw new MalformedStreamException("Stream ended unexpectedly");
+ }
+ return nextChunk;
+ }
+
+
+ /**
+ *
Changes the boundary token used for partitioning the stream. + * + *
This method allows single pass processing of nested multipart + * streams. + * + *
The boundary token of the nested stream is required
+ * to be of the same length as the boundary token in parent stream.
+ *
+ *
Restoring the parent stream boundary token after processing of a
+ * nested stream is left to the application.
+ *
+ * @param boundary The boundary to be used for parsing of the nested
+ * stream.
+ *
+ * @exception IllegalBoundaryException if the boundary
+ * has a different length than the one
+ * being currently parsed.
+ */
+ public void setBoundary(byte[] boundary)
+ throws IllegalBoundaryException
+ {
+ if (boundary.length != boundaryLength - 4)
+ {
+ throw new IllegalBoundaryException(
+ "The length of a boundary token can not be changed");
+ }
+ System.arraycopy(boundary, 0, this.boundary, 4, boundary.length);
+ }
+
+
+ /**
+ *
Reads the header-part of the current
+ * encapsulation.
+ *
+ *
Headers are returned verbatim to the input stream, including the
+ * trailing CRLF marker. Parsing is left to the
+ * application.
+ *
+ *
TODO allow limiting maximum header size to
+ * protect against abuse.
+ *
+ * @return The header-part of the current encapsulation.
+ *
+ * @exception MalformedStreamException if the stream ends unexpecetedly.
+ */
+ public String readHeaders()
+ throws MalformedStreamException
+ {
+ int i = 0;
+ byte b[] = new byte[1];
+ // to support multi-byte characters
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int sizeMax = HEADER_PART_SIZE_MAX;
+ int size = 0;
+ while (i < 4)
+ {
+ try
+ {
+ b[0] = readByte();
+ }
+ catch (IOException e)
+ {
+ throw new MalformedStreamException("Stream ended unexpectedly");
+ }
+ size++;
+ if (b[0] == HEADER_SEPARATOR[i])
+ {
+ i++;
+ }
+ else
+ {
+ i = 0;
+ }
+ if (size <= sizeMax)
+ {
+ baos.write(b[0]);
+ }
+ }
+
+ String headers = null;
+ if (headerEncoding != null)
+ {
+ try
+ {
+ headers = baos.toString(headerEncoding);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ // Fall back to platform default if specified encoding is not
+ // supported.
+ headers = baos.toString();
+ }
+ }
+ else
+ {
+ headers = baos.toString();
+ }
+
+ return headers;
+ }
+
+
+ /**
+ *
Reads body-data from the current
+ * encapsulation and writes its contents into the
+ * output Stream.
+ *
+ *
Arbitrary large amounts of data can be processed by this
+ * method using a constant size buffer. (see {@link
+ * #MultipartStream(InputStream,byte[],int) constructor}).
+ *
+ * @param output The Stream to write data into.
+ *
+ * @return the amount of data written.
+ *
+ * @exception MalformedStreamException if the stream ends unexpectedly.
+ * @exception IOException if an i/o error occurs.
+ */
+ public int readBodyData(OutputStream output)
+ throws MalformedStreamException,
+ IOException
+ {
+ boolean done = false;
+ int pad;
+ int pos;
+ int bytesRead;
+ int total = 0;
+ while (!done)
+ {
+ // Is boundary token present somewere in the buffer?
+ pos = findSeparator();
+ if (pos != -1)
+ {
+ // Write the rest of the data before the boundary.
+ output.write(buffer, head, pos - head);
+ total += pos - head;
+ head = pos;
+ done = true;
+ }
+ else
+ {
+ // Determine how much data should be kept in the
+ // buffer.
+ if (tail - head > keepRegion)
+ {
+ pad = keepRegion;
+ }
+ else
+ {
+ pad = tail - head;
+ }
+ // Write out the data belonging to the body-data.
+ output.write(buffer, head, tail - head - pad);
+
+ // Move the data to the beging of the buffer.
+ total += tail - head - pad;
+ System.arraycopy(buffer, tail - pad, buffer, 0, pad);
+
+ // Refill buffer with new data.
+ head = 0;
+ bytesRead = input.read(buffer, pad, bufSize - pad);
+
+ // [pprrrrrrr]
+ if (bytesRead != -1)
+ {
+ tail = pad + bytesRead;
+ }
+ else
+ {
+ // The last pad amount is left in the buffer.
+ // Boundary can't be in there so write out the
+ // data you have and signal an error condition.
+ output.write(buffer, 0, pad);
+ output.flush();
+ total += pad;
+ throw new MalformedStreamException(
+ "Stream ended unexpectedly");
+ }
+ }
+ }
+ output.flush();
+ return total;
+ }
+
+
+ /**
+ *
Reads body-data from the current
+ * encapsulation and discards it.
+ *
+ *
Use this method to skip encapsulations you don't need or don't
+ * understand.
+ *
+ * @return The amount of data discarded.
+ *
+ * @exception MalformedStreamException if the stream ends unexpectedly.
+ * @exception IOException if an i/o error occurs.
+ */
+ public int discardBodyData()
+ throws MalformedStreamException,
+ IOException
+ {
+ boolean done = false;
+ int pad;
+ int pos;
+ int bytesRead;
+ int total = 0;
+ while (!done)
+ {
+ // Is boundary token present somewere in the buffer?
+ pos = findSeparator();
+ if (pos != -1)
+ {
+ // Write the rest of the data before the boundary.
+ total += pos - head;
+ head = pos;
+ done = true;
+ }
+ else
+ {
+ // Determine how much data should be kept in the
+ // buffer.
+ if (tail - head > keepRegion)
+ {
+ pad = keepRegion;
+ }
+ else
+ {
+ pad = tail - head;
+ }
+ total += tail - head - pad;
+
+ // Move the data to the beging of the buffer.
+ System.arraycopy(buffer, tail - pad, buffer, 0, pad);
+
+ // Refill buffer with new data.
+ head = 0;
+ bytesRead = input.read(buffer, pad, bufSize - pad);
+
+ // [pprrrrrrr]
+ if (bytesRead != -1)
+ {
+ tail = pad + bytesRead;
+ }
+ else
+ {
+ // The last pad amount is left in the buffer.
+ // Boundary can't be in there so signal an error
+ // condition.
+ total += pad;
+ throw new MalformedStreamException(
+ "Stream ended unexpectedly");
+ }
+ }
+ }
+ return total;
+ }
+
+
+ /**
+ * Finds the beginning of the first
+ * NOTE: This implementation may trigger the event before the threshold
+ * is actually reached, since it triggers when a pending write operation would
+ * cause the threshold to be exceeded.
+ *
+ * @author Martin Cooper
+ *
+ * @version $Id: ThresholdingOutputStream.java,v 1.3 2003/05/31 22:31:08 martinc Exp $
+ */
+public abstract class ThresholdingOutputStream
+ extends OutputStream
+{
+
+ // ----------------------------------------------------------- Data members
+
+
+ /**
+ * The threshold at which the event will be triggered.
+ */
+ private int threshold;
+
+
+ /**
+ * The number of bytes written to the output stream.
+ */
+ private long written;
+
+
+ /**
+ * Whether or not the configured threshold has been exceeded.
+ */
+ private boolean thresholdExceeded;
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs an instance of this class which will trigger an event at the
+ * specified threshold.
+ *
+ * @param threshold The number of bytes at which to trigger an event.
+ */
+ public ThresholdingOutputStream(int threshold)
+ {
+ this.threshold = threshold;
+ }
+
+
+ // --------------------------------------------------- OutputStream methods
+
+
+ /**
+ * Writes the specified byte to this output stream.
+ *
+ * @param b The byte to be written.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public void write(int b) throws IOException
+ {
+ checkThreshold(1);
+ getStream().write(b);
+ written++;
+ }
+
+
+ /**
+ * Writes
+ Component for handling html file uploads as given by rfc 1867
+ RFC 1867.
+
+ Normal usage of the package involves
+ {@link org.apache.commons.fileupload.DiskFileUpload DiskFileUpload}
+ parsing the HttpServletRequest and returning a list of
+ {@link org.apache.commons.fileupload.FileItem FileItem}'s.
+ These
+ Normal usage example:
+
+ In the example above the first file is loaded into memory as a
+ encapsulation.
+ *
+ * @return true if an encapsulation was found in
+ * the stream.
+ *
+ * @exception IOException if an i/o error occurs.
+ */
+ public boolean skipPreamble()
+ throws IOException
+ {
+ // First delimiter may be not preceeded with a CRLF.
+ System.arraycopy(boundary, 2, boundary, 0, boundary.length - 2);
+ boundaryLength = boundary.length - 2;
+ try
+ {
+ // Discard all data up to the delimiter.
+ discardBodyData();
+
+ // Read boundary - if succeded, the stream contains an
+ // encapsulation.
+ return readBoundary();
+ }
+ catch (MalformedStreamException e)
+ {
+ return false;
+ }
+ finally
+ {
+ // Restore delimiter.
+ System.arraycopy(boundary, 0, boundary, 2, boundary.length - 2);
+ boundaryLength = boundary.length;
+ boundary[0] = 0x0D;
+ boundary[1] = 0x0A;
+ }
+ }
+
+
+ /**
+ * Compares count first bytes in the arrays
+ * a and b.
+ *
+ * @param a The first array to compare.
+ * @param b The second array to compare.
+ * @param count How many bytes should be compared.
+ *
+ * @return true if count first bytes in arrays
+ * a and b are equal.
+ */
+ public static boolean arrayequals(byte[] a,
+ byte[] b,
+ int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Searches for a byte of specified value in the buffer,
+ * starting at the specified position.
+ *
+ * @param value The value to find.
+ * @param pos The starting position for searching.
+ *
+ * @return The position of byte found, counting from beginning of the
+ * buffer, or -1 if not found.
+ */
+ protected int findByte(byte value,
+ int pos)
+ {
+ for (int i = pos; i < tail; i++)
+ {
+ if (buffer[i] == value)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+ /**
+ * Searches for the boundary in the buffer
+ * region delimited by head and tail.
+ *
+ * @return The position of the boundary found, counting from the
+ * beginning of the buffer, or -1 if
+ * not found.
+ */
+ protected int findSeparator()
+ {
+ int first;
+ int match = 0;
+ int maxpos = tail - boundaryLength;
+ for (first = head;
+ (first <= maxpos) && (match != boundaryLength);
+ first++)
+ {
+ first = findByte(boundary[0], first);
+ if (first == -1 || (first > maxpos))
+ {
+ return -1;
+ }
+ for (match = 1; match < boundaryLength; match++)
+ {
+ if (buffer[first + match] != boundary[match])
+ {
+ break;
+ }
+ }
+ }
+ if (match == boundaryLength)
+ {
+ return first - 1;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns a string representation of this object.
+ *
+ * @return The string representation of this object.
+ */
+ public String toString()
+ {
+ StringBuffer sbTemp = new StringBuffer();
+ sbTemp.append("boundary='");
+ sbTemp.append(String.valueOf(boundary));
+ sbTemp.append("'\nbufSize=");
+ sbTemp.append(bufSize);
+ return sbTemp.toString();
+ }
+
+ /**
+ * Thrown to indicate that the input stream fails to follow the
+ * required syntax.
+ */
+ public class MalformedStreamException
+ extends IOException
+ {
+ /**
+ * Constructs a MalformedStreamException with no
+ * detail message.
+ */
+ public MalformedStreamException()
+ {
+ super();
+ }
+
+ /**
+ * Constructs an MalformedStreamException with
+ * the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public MalformedStreamException(String message)
+ {
+ super(message);
+ }
+ }
+
+
+ /**
+ * Thrown upon attempt of setting an invalid boundary token.
+ */
+ public class IllegalBoundaryException
+ extends IOException
+ {
+ /**
+ * Constructs an IllegalBoundaryException with no
+ * detail message.
+ */
+ public IllegalBoundaryException()
+ {
+ super();
+ }
+
+ /**
+ * Constructs an IllegalBoundaryException with
+ * the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public IllegalBoundaryException(String message)
+ {
+ super(message);
+ }
+ }
+
+
+ // ------------------------------------------------------ Debugging methods
+
+
+ // These are the methods that were used to debug this stuff.
+ /*
+
+ // Dump data.
+ protected void dump()
+ {
+ System.out.println("01234567890");
+ byte[] temp = new byte[buffer.length];
+ for(int i=0; iOutputStream methods. However, these
+ * overrides ultimately call the corresponding methods in the underlying output
+ * stream implementation.
+ * b.length bytes from the specified byte array to this
+ * output stream.
+ *
+ * @param b The array of bytes to be written.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public void write(byte b[]) throws IOException
+ {
+ checkThreshold(b.length);
+ getStream().write(b);
+ written += b.length;
+ }
+
+
+ /**
+ * Writes len bytes from the specified byte array starting at
+ * offset off to this output stream.
+ *
+ * @param b The byte array from which the data will be written.
+ * @param off The start offset in the byte array.
+ * @param len The number of bytes to write.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public void write(byte b[], int off, int len) throws IOException
+ {
+ checkThreshold(len);
+ getStream().write(b, off, len);
+ written += len;
+ }
+
+
+ /**
+ * Flushes this output stream and forces any buffered output bytes to be
+ * written out.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public void flush() throws IOException
+ {
+ getStream().flush();
+ }
+
+
+ /**
+ * Closes this output stream and releases any system resources associated
+ * with this stream.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public void close() throws IOException
+ {
+ try
+ {
+ flush();
+ }
+ catch (IOException ignored)
+ {
+ // ignore
+ }
+ getStream().close();
+ }
+
+
+ // --------------------------------------------------------- Public methods
+
+
+ /**
+ * Returns the threshold, in bytes, at which an event will be triggered.
+ *
+ * @return The threshold point, in bytes.
+ */
+ public int getThreshold()
+ {
+ return threshold;
+ }
+
+
+ /**
+ * Returns the number of bytes that have been written to this output stream.
+ *
+ * @return The number of bytes written.
+ */
+ public long getByteCount()
+ {
+ return written;
+ }
+
+
+ /**
+ * Determines whether or not the configured threshold has been exceeded for
+ * this output stream.
+ *
+ * @return true if the threshold has been reached;
+ * false otherwise.
+ */
+ public boolean isThresholdExceeded()
+ {
+ return (written > threshold);
+ }
+
+
+ // ------------------------------------------------------ Protected methods
+
+
+ /**
+ * Checks to see if writing the specified number of bytes would cause the
+ * configured threshold to be exceeded. If so, triggers an event to allow
+ * a concrete implementation to take action on this.
+ *
+ * @param count The number of bytes about to be written to the underlying
+ * output stream.
+ *
+ * @exception IOException if an error occurs.
+ */
+ protected void checkThreshold(int count) throws IOException
+ {
+ if (!thresholdExceeded && (written + count > threshold))
+ {
+ thresholdReached();
+ thresholdExceeded = true;
+ }
+ }
+
+
+ // ------------------------------------------------------- Abstract methods
+
+
+ /**
+ * Returns the underlying output stream, to which the corresponding
+ * OutputStream methods in this class will ultimately delegate.
+ *
+ * @return The underlying output stream.
+ *
+ * @exception IOException if an error occurs.
+ */
+ protected abstract OutputStream getStream() throws IOException;
+
+
+ /**
+ * Indicates that the configured threshold has been reached, and that a
+ * subclass should take whatever action necessary on this event. This may
+ * include changing the underlying output stream.
+ *
+ * @exception IOException if an error occurs.
+ */
+ protected abstract void thresholdReached() throws IOException;
+}
diff --git a/java/org/apache/tomcat/util/http/fileupload/package.html b/java/org/apache/tomcat/util/http/fileupload/package.html
new file mode 100644
index 000000000..853871f15
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/fileupload/package.html
@@ -0,0 +1,66 @@
+
+
+ FileItem's provide easy access to the data
+ given in the upload. There is also a low level api for
+ manipulating the upload data encapsulated in the
+ {@link org.apache.commons.fileupload.MultipartStream MultipartStream}
+ class.
+
+
+ public void doPost(HttpServletRequest req, HttpServletResponse res)
+ {
+ DiskFileUpload fu = new DiskFileUpload();
+ // maximum size before a FileUploadException will be thrown
+ fu.setSizeMax(1000000);
+ // maximum size that will be stored in memory
+ fu.setSizeThreshold(4096);
+ // the location for saving data that is larger than getSizeThreshold()
+ fu.setRepositoryPath("/tmp");
+
+ List fileItems = fu.parseRequest(req);
+ // assume we know there are two files. The first file is a small
+ // text file, the second is unknown and is written to a file on
+ // the server
+ Iterator i = fileItems.iterator();
+ String comment = ((FileItem)i.next()).getString();
+ FileItem fi = (FileItem)i.next();
+ // filename on the client
+ String fileName = fi.getName();
+ // save comment and filename to database
+ ...
+ // write the file
+ fi.write("/www/uploads/" + fileName);
+ }
+
+ String. Before calling the getString method, the data
+ may have been in memory or on disk depending on its size. The second
+ file we assume it will be large and therefore never explicitly load
+ it into memory, though if it is less than 4096 bytes it will be
+ in memory before it is written to its final location. When writing to
+ the final location, if the data is larger than the
+ threshold, an attempt is made to rename the temporary file to
+ the given location. If it cannot be renamed, it is streamed to the
+ new location.
+