/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.SortedSet;
import java.util.TreeSet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import org.apache.catalina.Container;
import org.apache.catalina.Host;
import org.apache.catalina.util.ServerInfo;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.security.Escape;

Servlet that enables remote management of the virtual hosts deployed 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.

The difference between the HostManagerServlet and this Servlet is that this Servlet prints out an HTML interface which makes it easier to administrate.

However if you use a software that parses the output of 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, Malcolm Edgar, Glenn L. Nielsen, Peter Rossbach
See Also:
/** * Servlet that enables remote management of the virtual hosts deployed * 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. * <p> * The difference between the <code>HostManagerServlet</code> and this * Servlet is that this Servlet prints out an HTML interface which * makes it easier to administrate. * <p> * However if you use a software that parses the output of * <code>HostManagerServlet</code> you won't be able to upgrade * to this Servlet since the output are not in the * same format as from <code>HostManagerServlet</code> * * @author Bip Thelin * @author Malcolm Edgar * @author Glenn L. Nielsen * @author Peter Rossbach * @see org.apache.catalina.manager.ManagerServlet */
public final class HTMLHostManagerServlet extends HostManagerServlet { private static final long serialVersionUID = 1L; // --------------------------------------------------------- Public Methods
Process a GET request for the specified resource.
Params:
  • request – The servlet request we are processing
  • response – The servlet response we are creating
Throws:
/** * 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 */
@Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { StringManager smClient = StringManager.getManager( Constants.Package, request.getLocales()); // Identify the request parameters that we need String command = request.getPathInfo(); // 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) { // No command == list } else if (command.equals("/list")) { // Nothing to do - always generate list } else if (command.equals("/add") || command.equals("/remove") || command.equals("/start") || command.equals("/stop") || command.equals("/persist")) { message = smClient.getString( "hostManagerServlet.postCommand", command); } else { message = smClient.getString( "hostManagerServlet.unknownCommand", command); } list(request, response, message, smClient); }
Process a POST request for the specified resource.
Params:
  • request – The servlet request we are processing
  • response – The servlet response we are creating
Throws:
/** * 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 */
@Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { StringManager smClient = StringManager.getManager( Constants.Package, request.getLocales()); // 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) { // No command == list } else if (command.equals("/add")) { message = add(request, name, smClient); } else if (command.equals("/remove")) { message = remove(name, smClient); } else if (command.equals("/start")) { message = start(name, smClient); } else if (command.equals("/stop")) { message = stop(name, smClient); } else if (command.equals("/persist")) { message = persist(smClient); } else { //Try GET doGet(request, response); } list(request, response, message, smClient); }
Add a host using the specified parameters.
Params:
  • request – The Servlet request
  • name – Host name
  • smClient – StringManager for the client's locale
Returns:output
/** * Add a host using the specified parameters. * * @param request The Servlet request * @param name Host name * @param smClient StringManager for the client's locale * @return output */
protected String add(HttpServletRequest request,String name, StringManager smClient) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); super.add(request,printWriter,name,true, smClient); return stringWriter.toString(); }
Remove the specified host.
Params:
  • name – Host name
  • smClient – StringManager for the client's locale
Returns:output
/** * Remove the specified host. * * @param name Host name * @param smClient StringManager for the client's locale * @return output */
protected String remove(String name, StringManager smClient) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); super.remove(printWriter, name, smClient); return stringWriter.toString(); }
Start the host with the specified name.
Params:
  • name – Host name
  • smClient – StringManager for the client's locale
Returns:output
/** * Start the host with the specified name. * * @param name Host name * @param smClient StringManager for the client's locale * @return output */
protected String start(String name, StringManager smClient) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); super.start(printWriter, name, smClient); return stringWriter.toString(); }
Stop the host with the specified name.
Params:
  • name – Host name
  • smClient – StringManager for the client's locale
Returns:output
/** * Stop the host with the specified name. * * @param name Host name * @param smClient StringManager for the client's locale * @return output */
protected String stop(String name, StringManager smClient) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); super.stop(printWriter, name, smClient); return stringWriter.toString(); }
Persist the current configuration to server.xml.
Params:
  • smClient – i18n resources localized for the client
Returns:output
/** * Persist the current configuration to server.xml. * * @param smClient i18n resources localized for the client * @return output */
protected String persist(StringManager smClient) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); super.persist(printWriter, smClient); return stringWriter.toString(); }
Render an HTML list of the currently active Contexts in our virtual host, and memory and server status information.
Params:
  • request – The request
  • response – The response
  • message – a message to display
  • smClient – StringManager for the client's locale
Throws:
/** * Render an 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 * @param smClient StringManager for the client's locale * @throws IOException An IO error occurred */
public void list(HttpServletRequest request, HttpServletResponse response, String message, StringManager smClient) throws IOException { if (debug >= 1) { log(sm.getString("hostManagerServlet.list", engine.getName())); } PrintWriter writer = response.getWriter(); Object[] args = new Object[2]; args[0] = request.getContextPath(); args[1] = smClient.getString("htmlHostManagerServlet.title"); // HTML Header Section writer.print(MessageFormat.format( org.apache.catalina.manager.Constants.HTML_HEADER_SECTION, args )); // Body Header Section writer.print(MessageFormat.format( org.apache.catalina.manager.Constants.BODY_HEADER_SECTION, args)); // Message Section args = new Object[3]; args[0] = smClient.getString("htmlHostManagerServlet.messageLabel"); if (message == null || message.length() == 0) { args[1] = "OK"; } else { args[1] = Escape.htmlElementContent(message); } writer.print(MessageFormat.format(Constants.MESSAGE_SECTION, args)); // Manager Section args = new Object[9]; args[0] = smClient.getString("htmlHostManagerServlet.manager"); args[1] = response.encodeURL(request.getContextPath() + "/html/list"); args[2] = smClient.getString("htmlHostManagerServlet.list"); args[3] = // External link (request.getContextPath() + "/" + smClient.getString("htmlHostManagerServlet.helpHtmlManagerFile")); args[4] = smClient.getString("htmlHostManagerServlet.helpHtmlManager"); args[5] = // External link (request.getContextPath() + "/" + smClient.getString("htmlHostManagerServlet.helpManagerFile")); args[6] = smClient.getString("htmlHostManagerServlet.helpManager"); args[7] = response.encodeURL("/manager/status"); args[8] = smClient.getString("statusServlet.title"); writer.print(MessageFormat.format(Constants.MANAGER_SECTION, args)); // Hosts Header Section args = new Object[3]; args[0] = smClient.getString("htmlHostManagerServlet.hostName"); args[1] = smClient.getString("htmlHostManagerServlet.hostAliases"); args[2] = smClient.getString("htmlHostManagerServlet.hostTasks"); writer.print(MessageFormat.format(HOSTS_HEADER_SECTION, args)); // Hosts Row Section // Create sorted set 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(); } SortedSet<String> sortedHostNames = new TreeSet<>(); sortedHostNames.addAll(Arrays.asList(hostNames)); String hostsStart = smClient.getString("htmlHostManagerServlet.hostsStart"); String hostsStop = smClient.getString("htmlHostManagerServlet.hostsStop"); String hostsRemove = smClient.getString("htmlHostManagerServlet.hostsRemove"); String hostThis = smClient.getString("htmlHostManagerServlet.hostThis"); for (String hostName : sortedHostNames) { Host host = (Host) engine.findChild(hostName); if (host != null ) { args = new Object[2]; args[0] = // External link Escape.htmlElementContent(hostName); String[] aliases = host.findAliases(); StringBuilder buf = new StringBuilder(); 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("&nbsp;"); args[1] = buf.toString(); } else { args[1] = Escape.htmlElementContent(buf.toString()); } writer.print (MessageFormat.format(HOSTS_ROW_DETAILS_SECTION, args)); args = new Object[5]; if (host.getState().isAvailable()) { args[0] = response.encodeURL (request.getContextPath() + "/html/stop?name=" + URLEncoder.encode(hostName, "UTF-8")); args[1] = hostsStop; } else { args[0] = response.encodeURL (request.getContextPath() + "/html/start?name=" + URLEncoder.encode(hostName, "UTF-8")); args[1] = hostsStart; } args[2] = response.encodeURL (request.getContextPath() + "/html/remove?name=" + URLEncoder.encode(hostName, "UTF-8")); args[3] = hostsRemove; args[4] = hostThis; if (host == this.installedHost) { 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] = smClient.getString("htmlHostManagerServlet.addTitle"); args[1] = smClient.getString("htmlHostManagerServlet.addHost"); args[2] = response.encodeURL(request.getContextPath() + "/html/add"); args[3] = smClient.getString("htmlHostManagerServlet.addName"); args[4] = smClient.getString("htmlHostManagerServlet.addAliases"); args[5] = smClient.getString("htmlHostManagerServlet.addAppBase"); writer.print(MessageFormat.format(ADD_SECTION_START, args)); args = new Object[3]; args[0] = smClient.getString("htmlHostManagerServlet.addAutoDeploy"); args[1] = "autoDeploy"; args[2] = "checked"; writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args)); args[0] = smClient.getString( "htmlHostManagerServlet.addDeployOnStartup"); args[1] = "deployOnStartup"; args[2] = "checked"; writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args)); args[0] = smClient.getString("htmlHostManagerServlet.addDeployXML"); args[1] = "deployXML"; args[2] = "checked"; writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args)); args[0] = smClient.getString("htmlHostManagerServlet.addUnpackWARs"); args[1] = "unpackWARs"; args[2] = "checked"; writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args)); args[0] = smClient.getString("htmlHostManagerServlet.addManager"); args[1] = "manager"; args[2] = "checked"; writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args)); args[0] = smClient.getString("htmlHostManagerServlet.addCopyXML"); args[1] = "copyXML"; args[2] = ""; writer.print(MessageFormat.format(ADD_SECTION_BOOLEAN, args)); args = new Object[1]; args[0] = smClient.getString("htmlHostManagerServlet.addButton"); writer.print(MessageFormat.format(ADD_SECTION_END, args)); // Persist Configuration Section args = new Object[4]; args[0] = smClient.getString("htmlHostManagerServlet.persistTitle"); args[1] = response.encodeURL(request.getContextPath() + "/html/persist"); args[2] = smClient.getString("htmlHostManagerServlet.persistAllButton"); args[3] = smClient.getString("htmlHostManagerServlet.persistAll"); writer.print(MessageFormat.format(PERSIST_SECTION, args)); // Server Header Section args = new Object[7]; args[0] = smClient.getString("htmlHostManagerServlet.serverTitle"); args[1] = smClient.getString("htmlHostManagerServlet.serverVersion"); args[2] = smClient.getString("htmlHostManagerServlet.serverJVMVersion"); args[3] = smClient.getString("htmlHostManagerServlet.serverJVMVendor"); args[4] = smClient.getString("htmlHostManagerServlet.serverOSName"); args[5] = smClient.getString("htmlHostManagerServlet.serverOSVersion"); args[6] = smClient.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 substitutions MessageFormat can process // (maximum of 10). private static final String HOSTS_HEADER_SECTION = "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" + "<tr>\n" + " <td colspan=\"5\" class=\"title\">{0}</td>\n" + "</tr>\n" + "<tr>\n" + " <td class=\"header-left\"><small>{0}</small></td>\n" + " <td class=\"header-center\"><small>{1}</small></td>\n" + " <td class=\"header-center\"><small>{2}</small></td>\n" + "</tr>\n"; private static final String HOSTS_ROW_DETAILS_SECTION = "<tr>\n" + " <td class=\"row-left\"><small><a href=\"http://{0}\" " + Constants.REL_EXTERNAL + ">{0}</a>" + "</small></td>\n" + " <td class=\"row-center\"><small>{1}</small></td>\n"; private static final String MANAGER_HOST_ROW_BUTTON_SECTION = " <td class=\"row-left\">\n" + " <small>{4}</small>\n" + " </td>\n" + "</tr>\n"; private static final String HOSTS_ROW_BUTTON_SECTION = " <td class=\"row-left\" NOWRAP>\n" + " <form class=\"inline\" method=\"POST\" action=\"{0}\">" + " <small><input type=\"submit\" value=\"{1}\"></small>" + " </form>\n" + " <form class=\"inline\" method=\"POST\" action=\"{2}\">" + " <small><input type=\"submit\" value=\"{3}\"></small>" + " </form>\n" + " </td>\n" + "</tr>\n"; private static final String ADD_SECTION_START = "</table>\n" + "<br>\n" + "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" + "<tr>\n" + " <td colspan=\"2\" class=\"title\">{0}</td>\n" + "</tr>\n" + "<tr>\n" + " <td colspan=\"2\" class=\"header-left\"><small>{1}</small></td>\n" + "</tr>\n" + "<tr>\n" + " <td colspan=\"2\">\n" + "<form method=\"post\" action=\"{2}\">\n" + "<table cellspacing=\"0\" cellpadding=\"3\">\n" + "<tr>\n" + " <td class=\"row-right\">\n" + " <small>{3}</small>\n" + " </td>\n" + " <td class=\"row-left\">\n" + " <input type=\"text\" name=\"name\" size=\"20\">\n" + " </td>\n" + "</tr>\n" + "<tr>\n" + " <td class=\"row-right\">\n" + " <small>{4}</small>\n" + " </td>\n" + " <td class=\"row-left\">\n" + " <input type=\"text\" name=\"aliases\" size=\"64\">\n" + " </td>\n" + "</tr>\n" + "<tr>\n" + " <td class=\"row-right\">\n" + " <small>{5}</small>\n" + " </td>\n" + " <td class=\"row-left\">\n" + " <input type=\"text\" name=\"appBase\" size=\"64\">\n" + " </td>\n" + "</tr>\n" ; private static final String ADD_SECTION_BOOLEAN = "<tr>\n" + " <td class=\"row-right\">\n" + " <small>{0}</small>\n" + " </td>\n" + " <td class=\"row-left\">\n" + " <input type=\"checkbox\" name=\"{1}\" {2}>\n" + " </td>\n" + "</tr>\n" ; private static final String ADD_SECTION_END = "<tr>\n" + " <td class=\"row-right\">\n" + " &nbsp;\n" + " </td>\n" + " <td class=\"row-left\">\n" + " <input type=\"submit\" value=\"{0}\">\n" + " </td>\n" + "</tr>\n" + "</table>\n" + "</form>\n" + "</td>\n" + "</tr>\n" + "</table>\n" + "<br>\n" + "\n"; private static final String PERSIST_SECTION = "<table border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n" + "<tr>\n" + " <td class=\"title\">{0}</td>\n" + "</tr>\n" + "<tr>\n" + " <td class=\"row-left\">\n" + " <form class=\"inline\" method=\"POST\" action=\"{1}\">" + " <small><input type=\"submit\" value=\"{2}\"></small>" + " </form> {3}\n" + " </td>\n" + "</tr>\n" + "</table>\n" + "<br>\n" + "\n"; }