001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.hadoop.conf; 020 021 import org.apache.commons.logging.*; 022 023 import org.apache.commons.lang.StringEscapeUtils; 024 025 import java.util.Collection; 026 import java.util.Map; 027 import java.util.Enumeration; 028 import java.io.IOException; 029 import java.io.PrintWriter; 030 031 import javax.servlet.ServletContext; 032 import javax.servlet.ServletException; 033 import javax.servlet.http.HttpServlet; 034 import javax.servlet.http.HttpServletRequest; 035 import javax.servlet.http.HttpServletResponse; 036 037 import org.apache.hadoop.util.StringUtils; 038 039 /** 040 * A servlet for changing a node's configuration. 041 * 042 * Reloads the configuration file, verifies whether changes are 043 * possible and asks the admin to approve the change. 044 * 045 */ 046 public class ReconfigurationServlet extends HttpServlet { 047 048 private static final long serialVersionUID = 1L; 049 050 private static final Log LOG = 051 LogFactory.getLog(ReconfigurationServlet.class); 052 053 // the prefix used to fing the attribute holding the reconfigurable 054 // for a given request 055 // 056 // we get the attribute prefix + servlet path 057 public static final String CONF_SERVLET_RECONFIGURABLE_PREFIX = 058 "conf.servlet.reconfigurable."; 059 060 /** 061 * {@inheritDoc} 062 */ 063 @Override 064 public void init() throws ServletException { 065 super.init(); 066 } 067 068 private Reconfigurable getReconfigurable(HttpServletRequest req) { 069 LOG.info("servlet path: " + req.getServletPath()); 070 LOG.info("getting attribute: " + CONF_SERVLET_RECONFIGURABLE_PREFIX + 071 req.getServletPath()); 072 return (Reconfigurable) 073 this.getServletContext().getAttribute(CONF_SERVLET_RECONFIGURABLE_PREFIX + 074 req.getServletPath()); 075 } 076 077 private void printHeader(PrintWriter out, String nodeName) { 078 out.print("<html><head>"); 079 out.printf("<title>%s Reconfiguration Utility</title>\n", 080 StringEscapeUtils.escapeHtml(nodeName)); 081 out.print("</head><body>\n"); 082 out.printf("<h1>%s Reconfiguration Utility</h1>\n", 083 StringEscapeUtils.escapeHtml(nodeName)); 084 } 085 086 private void printFooter(PrintWriter out) { 087 out.print("</body></html>\n"); 088 } 089 090 /** 091 * Print configuration options that can be changed. 092 */ 093 private void printConf(PrintWriter out, Reconfigurable reconf) { 094 Configuration oldConf = reconf.getConf(); 095 Configuration newConf = new Configuration(); 096 097 Collection<ReconfigurationUtil.PropertyChange> changes = 098 ReconfigurationUtil.getChangedProperties(newConf, 099 oldConf); 100 101 boolean changeOK = true; 102 103 out.println("<form action=\"\" method=\"post\">"); 104 out.println("<table border=\"1\">"); 105 out.println("<tr><th>Property</th><th>Old value</th>"); 106 out.println("<th>New value </th><th></th></tr>"); 107 for (ReconfigurationUtil.PropertyChange c: changes) { 108 out.print("<tr><td>"); 109 if (!reconf.isPropertyReconfigurable(c.prop)) { 110 out.print("<font color=\"red\">" + 111 StringEscapeUtils.escapeHtml(c.prop) + "</font>"); 112 changeOK = false; 113 } else { 114 out.print(StringEscapeUtils.escapeHtml(c.prop)); 115 out.print("<input type=\"hidden\" name=\"" + 116 StringEscapeUtils.escapeHtml(c.prop) + "\" value=\"" + 117 StringEscapeUtils.escapeHtml(c.newVal) + "\"/>"); 118 } 119 out.print("</td><td>" + 120 (c.oldVal == null ? "<it>default</it>" : 121 StringEscapeUtils.escapeHtml(c.oldVal)) + 122 "</td><td>" + 123 (c.newVal == null ? "<it>default</it>" : 124 StringEscapeUtils.escapeHtml(c.newVal)) + 125 "</td>"); 126 out.print("</tr>\n"); 127 } 128 out.println("</table>"); 129 if (!changeOK) { 130 out.println("<p><font color=\"red\">WARNING: properties marked red" + 131 " will not be changed until the next restart.</font></p>"); 132 } 133 out.println("<input type=\"submit\" value=\"Apply\" />"); 134 out.println("</form>"); 135 } 136 137 @SuppressWarnings("unchecked") 138 private Enumeration<String> getParams(HttpServletRequest req) { 139 return (Enumeration<String>) req.getParameterNames(); 140 } 141 142 /** 143 * Apply configuratio changes after admin has approved them. 144 */ 145 private void applyChanges(PrintWriter out, Reconfigurable reconf, 146 HttpServletRequest req) 147 throws IOException, ReconfigurationException { 148 Configuration oldConf = reconf.getConf(); 149 Configuration newConf = new Configuration(); 150 151 Enumeration<String> params = getParams(req); 152 153 synchronized(oldConf) { 154 while (params.hasMoreElements()) { 155 String rawParam = params.nextElement(); 156 String param = StringEscapeUtils.unescapeHtml(rawParam); 157 String value = 158 StringEscapeUtils.unescapeHtml(req.getParameter(rawParam)); 159 if (value != null) { 160 if (value.equals(newConf.getRaw(param)) || value.equals("default") || 161 value.equals("null") || value.equals("")) { 162 if ((value.equals("default") || value.equals("null") || 163 value.equals("")) && 164 oldConf.getRaw(param) != null) { 165 out.println("<p>Changed \"" + 166 StringEscapeUtils.escapeHtml(param) + "\" from \"" + 167 StringEscapeUtils.escapeHtml(oldConf.getRaw(param)) + 168 "\" to default</p>"); 169 reconf.reconfigureProperty(param, null); 170 } else if (!value.equals("default") && !value.equals("null") && 171 !value.equals("") && 172 (oldConf.getRaw(param) == null || 173 !oldConf.getRaw(param).equals(value))) { 174 // change from default or value to different value 175 if (oldConf.getRaw(param) == null) { 176 out.println("<p>Changed \"" + 177 StringEscapeUtils.escapeHtml(param) + 178 "\" from default to \"" + 179 StringEscapeUtils.escapeHtml(value) + "\"</p>"); 180 } else { 181 out.println("<p>Changed \"" + 182 StringEscapeUtils.escapeHtml(param) + "\" from \"" + 183 StringEscapeUtils.escapeHtml(oldConf. 184 getRaw(param)) + 185 "\" to \"" + 186 StringEscapeUtils.escapeHtml(value) + "\"</p>"); 187 } 188 reconf.reconfigureProperty(param, value); 189 } else { 190 LOG.info("property " + param + " unchanged"); 191 } 192 } else { 193 // parameter value != newConf value 194 out.println("<p>\"" + StringEscapeUtils.escapeHtml(param) + 195 "\" not changed because value has changed from \"" + 196 StringEscapeUtils.escapeHtml(value) + "\" to \"" + 197 StringEscapeUtils.escapeHtml(newConf.getRaw(param)) + 198 "\" since approval</p>"); 199 } 200 } 201 } 202 } 203 } 204 205 /** 206 * {@inheritDoc} 207 */ 208 @Override 209 protected void doGet(HttpServletRequest req, HttpServletResponse resp) 210 throws ServletException, IOException { 211 LOG.info("GET"); 212 PrintWriter out = resp.getWriter(); 213 214 Reconfigurable reconf = getReconfigurable(req); 215 String nodeName = reconf.getClass().getCanonicalName(); 216 217 printHeader(out, nodeName); 218 printConf(out, reconf); 219 printFooter(out); 220 } 221 222 /** 223 * {@inheritDoc} 224 */ 225 @Override 226 protected void doPost(HttpServletRequest req, HttpServletResponse resp) 227 throws ServletException, IOException { 228 LOG.info("POST"); 229 PrintWriter out = resp.getWriter(); 230 231 Reconfigurable reconf = getReconfigurable(req); 232 String nodeName = reconf.getClass().getCanonicalName(); 233 234 printHeader(out, nodeName); 235 236 try { 237 applyChanges(out, reconf, req); 238 } catch (ReconfigurationException e) { 239 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 240 StringUtils.stringifyException(e)); 241 return; 242 } 243 244 out.println("<p><a href=\"" + req.getServletPath() + "\">back</a></p>"); 245 printFooter(out); 246 } 247 248 }