All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.manager.host.HostManagerServlet Maven / Gradle / Ivy

There is a newer version: 11.0.2
Show newest version
/*
 * 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.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.StringTokenizer;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;

import jakarta.servlet.ServletException;
import jakarta.servlet.UnavailableException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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.Host;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.HostConfig;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.res.StringManager;

/**
 * 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.
 * 

* This servlet examines the value returned by getPathInfo() * and related query parameters to determine what action is being requested. * The following actions and parameters (starting after the servlet path) * are supported: *

    *
  • /add?name={host-name}&aliases={host-aliases}&manager={manager} - * Create and add a new virtual host. The 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).
  • *
  • /remove?name={host-name} - Remove a virtual host. * The host-name attribute indicates the name of the host. *
  • *
  • /list - List the virtual hosts installed on the server. * Each host will be listed with the following format * host-name#host-aliases.
  • *
  • /start?name={host-name} - Start the virtual host.
  • *
  • /stop?name={host-name} - Stop the virtual host.
  • *
*

* 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. *

* The following servlet initialization parameters are recognized: *

    *
  • debug - The debugging detail level that controls the amount * of information that is logged by this servlet. Default is zero. *
* * @author Craig R. McClanahan * @author Remy Maucherat */ public class HostManagerServlet extends HttpServlet implements ContainerServlet { private static final long serialVersionUID = 1L; // ----------------------------------------------------- Instance Variables /** * The Context container associated with our web application. */ protected transient Context context = null; /** * The debugging detail level for this servlet. */ protected int debug = 1; /** * The associated host. */ protected transient Host installedHost = null; /** * The associated engine. */ protected transient Engine engine = null; /** * The string manager for this package. */ protected static final StringManager sm = StringManager.getManager(Constants.Package); /** * The Wrapper container associated with this servlet. */ protected transient Wrapper wrapper = null; // ----------------------------------------------- ContainerServlet Methods /** * Return the Wrapper with which we are associated. */ @Override public Wrapper getWrapper() { return this.wrapper; } /** * Set the Wrapper with which we are associated. * * @param wrapper The new wrapper */ @Override public void setWrapper(Wrapper wrapper) { this.wrapper = wrapper; if (wrapper == null) { context = null; installedHost = null; engine = null; } else { context = (Context) wrapper.getParent(); installedHost = (Host) context.getParent(); engine = (Engine) installedHost.getParent(); } } // --------------------------------------------------------- Public Methods /** * Finalize this servlet. */ @Override 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 */ @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(); 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); // Stop older versions of IE thinking they know best. We set text/plain // in the line above for a reason. IE's behaviour is unwanted at best // and dangerous at worst. response.setHeader("X-Content-Type-Options", "nosniff"); PrintWriter writer = response.getWriter(); // Process the requested command if (command == null) { writer.println(smClient.getString("hostManagerServlet.noCommand")); } else if (command.equals("/add")) { add(request, writer, name, false, smClient); } else if (command.equals("/remove")) { remove(writer, name, smClient); } else if (command.equals("/list")) { list(writer, smClient); } else if (command.equals("/start")) { start(writer, name, smClient); } else if (command.equals("/stop")) { stop(writer, name, smClient); } else if (command.equals("/persist")) { persist(writer, smClient); } else { writer.println(smClient.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 * @param smClient StringManager for the client's locale */ protected void add(HttpServletRequest request, PrintWriter writer, String name, boolean htmlMode, StringManager smClient) { 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 copyXML = booleanParameter(request, "copyXML", false, htmlMode); add(writer, name, aliases, appBase, manager, autoDeploy, deployOnStartup, deployXML, unpackWARs, copyXML, smClient); } /** * Extract boolean value from checkbox with default. * @param request The Servlet request * @param parameter The parameter name * @param theDefault Default value * @param htmlMode Flag value * @return the boolean value for the parameter */ 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; } @Override 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")); } // Set our properties from the initialization parameters String value = null; try { value = getServletConfig().getInitParameter("debug"); debug = Integer.parseInt(value); } catch (Throwable t) { ExceptionUtils.handleThrowable(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 ? * @param autoDeploy Flag value * @param deployOnStartup Flag value * @param deployXML Flag value * @param unpackWARs Flag value * @param copyXML Flag value * @param smClient StringManager for the client's locale */ protected synchronized void add (PrintWriter writer, String name, String aliases, String appBase, boolean manager, boolean autoDeploy, boolean deployOnStartup, boolean deployXML, boolean unpackWARs, boolean copyXML, StringManager smClient) { if (debug >= 1) { log(sm.getString("hostManagerServlet.add", name)); } // Validate the requested host name if ((name == null) || name.length() == 0) { writer.println(smClient.getString( "hostManagerServlet.invalidHostName", name)); return; } // Check if host already exists if (engine.findChild(name) != null) { writer.println(smClient.getString( "hostManagerServlet.alreadyHost", name)); return; } // Validate and create appBase File appBaseFile = null; File file = null; String applicationBase = appBase; if (applicationBase == null || applicationBase.length() == 0) { applicationBase = name; } file = new File(applicationBase); if (!file.isAbsolute()) { file = new File(engine.getCatalinaBase(), file.getPath()); } try { appBaseFile = file.getCanonicalFile(); } catch (IOException e) { appBaseFile = file; } if (!appBaseFile.mkdirs() && !appBaseFile.isDirectory()) { writer.println(smClient.getString( "hostManagerServlet.appBaseCreateFail", appBaseFile.toString(), name)); return; } // Create base for config files File configBaseFile = getConfigBase(name); // Copy manager.xml if requested if (manager) { if (configBaseFile == null) { writer.println(smClient.getString( "hostManagerServlet.configBaseCreateFail", name)); return; } try (InputStream is = getServletContext().getResourceAsStream("/WEB-INF/manager.xml")) { Path dest = (new File(configBaseFile, "manager.xml")).toPath(); Files.copy(is, dest); } catch (IOException e) { writer.println(smClient.getString("hostManagerServlet.managerXml")); return; } } StandardHost host = new StandardHost(); host.setAppBase(applicationBase); host.setName(name); host.addLifecycleListener(new HostConfig()); // Add host aliases if ((aliases != null) && !aliases.isEmpty()) { 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.setCopyXML(copyXML); // Add new host try { engine.addChild(host); } catch (Exception e) { writer.println(smClient.getString("hostManagerServlet.exception", e.toString())); return; } host = (StandardHost) engine.findChild(name); if (host != null) { writer.println(smClient.getString("hostManagerServlet.addSuccess", name)); } else { // Something failed writer.println(smClient.getString( "hostManagerServlet.addFailed", name)); } } /** * Remove the specified host. * * @param writer Writer to render results to * @param name host name * @param smClient StringManager for the client's locale */ protected synchronized void remove(PrintWriter writer, String name, StringManager smClient) { if (debug >= 1) { log(sm.getString("hostManagerServlet.remove", name)); } // Validate the requested host name if ((name == null) || name.length() == 0) { writer.println(smClient.getString( "hostManagerServlet.invalidHostName", name)); return; } // Check if host exists if (engine.findChild(name) == null) { writer.println(smClient.getString( "hostManagerServlet.noHost", name)); return; } // Prevent removing our own host if (engine.findChild(name) == installedHost) { writer.println(smClient.getString( "hostManagerServlet.cannotRemoveOwnHost", name)); return; } // Remove host // Note that the host will not get physically removed try { Container child = engine.findChild(name); engine.removeChild(child); if ( child instanceof ContainerBase ) { child.destroy(); } } catch (Exception e) { writer.println(smClient.getString("hostManagerServlet.exception", e.toString())); return; } Host host = (StandardHost) engine.findChild(name); if (host == null) { writer.println(smClient.getString( "hostManagerServlet.removeSuccess", name)); } else { // Something failed writer.println(smClient.getString( "hostManagerServlet.removeFailed", name)); } } /** * Render a list of the currently active Contexts in our virtual host. * * @param writer Writer to render to * @param smClient StringManager for the client's locale */ protected void list(PrintWriter writer, StringManager smClient) { if (debug >= 1) { log(sm.getString("hostManagerServlet.list", engine.getName())); } writer.println(smClient.getString("hostManagerServlet.listed", engine.getName())); Container[] hosts = engine.findChildren(); for (Container container : hosts) { Host host = (Host) container; String name = host.getName(); String[] aliases = host.findAliases(); writer.println(String.format("[%s]:[%s]", name, StringUtils.join(aliases))); } } /** * Start the host with the specified name. * * @param writer Writer to render to * @param name Host name * @param smClient StringManager for the client's locale */ protected void start(PrintWriter writer, String name, StringManager smClient) { if (debug >= 1) { log(sm.getString("hostManagerServlet.start", name)); } // Validate the requested host name if ((name == null) || name.length() == 0) { writer.println(smClient.getString( "hostManagerServlet.invalidHostName", name)); return; } Container host = engine.findChild(name); // Check if host exists if (host == null) { writer.println(smClient.getString( "hostManagerServlet.noHost", name)); return; } // Prevent starting our own host if (host == installedHost) { writer.println(smClient.getString( "hostManagerServlet.cannotStartOwnHost", name)); return; } // Don't start host if already started if (host.getState().isAvailable()) { writer.println(smClient.getString( "hostManagerServlet.alreadyStarted", name)); return; } // Start host try { host.start(); writer.println(smClient.getString( "hostManagerServlet.started", name)); } catch (Exception e) { getServletContext().log (sm.getString("hostManagerServlet.startFailed", name), e); writer.println(smClient.getString( "hostManagerServlet.startFailed", name)); writer.println(smClient.getString( "hostManagerServlet.exception", e.toString())); } } /** * Stop the host with the specified name. * * @param writer Writer to render to * @param name Host name * @param smClient StringManager for the client's locale */ protected void stop(PrintWriter writer, String name, StringManager smClient) { if (debug >= 1) { log(sm.getString("hostManagerServlet.stop", name)); } // Validate the requested host name if ((name == null) || name.length() == 0) { writer.println(smClient.getString( "hostManagerServlet.invalidHostName", name)); return; } Container host = engine.findChild(name); // Check if host exists if (host == null) { writer.println(smClient.getString("hostManagerServlet.noHost", name)); return; } // Prevent stopping our own host if (host == installedHost) { writer.println(smClient.getString( "hostManagerServlet.cannotStopOwnHost", name)); return; } // Don't stop host if already stopped if (!host.getState().isAvailable()) { writer.println(smClient.getString( "hostManagerServlet.alreadyStopped", name)); return; } // Stop host try { host.stop(); writer.println(smClient.getString("hostManagerServlet.stopped", name)); } catch (Exception e) { getServletContext().log(sm.getString( "hostManagerServlet.stopFailed", name), e); writer.println(smClient.getString("hostManagerServlet.stopFailed", name)); writer.println(smClient.getString("hostManagerServlet.exception", e.toString())); } } /** * Persist the current configuration to server.xml. * * @param writer Writer to render to * @param smClient i18n resources localized for the client */ protected void persist(PrintWriter writer, StringManager smClient) { if (debug >= 1) { log(sm.getString("hostManagerServlet.persist")); } try { MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); ObjectName oname = new ObjectName(engine.getDomain() + ":type=StoreConfig"); platformMBeanServer.invoke(oname, "storeConfig", null, null); writer.println(smClient.getString("hostManagerServlet.persisted")); } catch (Exception e) { getServletContext().log(sm.getString("hostManagerServlet.persistFailed"), e); writer.println(smClient.getString("hostManagerServlet.persistFailed")); // catch InstanceNotFoundException when StoreConfig is not enabled instead of printing // the failure message if (e instanceof InstanceNotFoundException) { writer.println("Please enable StoreConfig to use this feature."); } else { writer.println(smClient.getString("hostManagerServlet.exception", e.toString())); } } } // -------------------------------------------------------- Support Methods /** * Get config base. * @param hostName The host name * @return the config base for the host */ protected File getConfigBase(String hostName) { File configBase = new File(context.getCatalinaBase(), "conf"); if (!configBase.exists()) { return null; } if (engine != null) { configBase = new File(configBase, engine.getName()); } if (installedHost != null) { configBase = new File(configBase, hostName); } if (!configBase.mkdirs() && !configBase.isDirectory()) { return null; } return configBase; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy