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.Enumeration; 027 import java.io.IOException; 028 import java.io.PrintWriter; 029 030 import javax.servlet.ServletException; 031 import javax.servlet.http.HttpServlet; 032 import javax.servlet.http.HttpServletRequest; 033 import javax.servlet.http.HttpServletResponse; 034 035 import org.apache.hadoop.util.StringUtils; 036 037 /** 038 * A servlet for changing a node's configuration. 039 * 040 * Reloads the configuration file, verifies whether changes are 041 * possible and asks the admin to approve the change. 042 * 043 */ 044 public class ReconfigurationServlet extends HttpServlet { 045 046 private static final long serialVersionUID = 1L; 047 048 private static final Log LOG = 049 LogFactory.getLog(ReconfigurationServlet.class); 050 051 // the prefix used to fing the attribute holding the reconfigurable 052 // for a given request 053 // 054 // we get the attribute prefix + servlet path 055 public static final String CONF_SERVLET_RECONFIGURABLE_PREFIX = 056 "conf.servlet.reconfigurable."; 057 058 @Override 059 public void init() throws ServletException { 060 super.init(); 061 } 062 063 private Reconfigurable getReconfigurable(HttpServletRequest req) { 064 LOG.info("servlet path: " + req.getServletPath()); 065 LOG.info("getting attribute: " + CONF_SERVLET_RECONFIGURABLE_PREFIX + 066 req.getServletPath()); 067 return (Reconfigurable) 068 this.getServletContext().getAttribute(CONF_SERVLET_RECONFIGURABLE_PREFIX + 069 req.getServletPath()); 070 } 071 072 private void printHeader(PrintWriter out, String nodeName) { 073 out.print("<html><head>"); 074 out.printf("<title>%s Reconfiguration Utility</title>\n", 075 StringEscapeUtils.escapeHtml(nodeName)); 076 out.print("</head><body>\n"); 077 out.printf("<h1>%s Reconfiguration Utility</h1>\n", 078 StringEscapeUtils.escapeHtml(nodeName)); 079 } 080 081 private void printFooter(PrintWriter out) { 082 out.print("</body></html>\n"); 083 } 084 085 /** 086 * Print configuration options that can be changed. 087 */ 088 private void printConf(PrintWriter out, Reconfigurable reconf) { 089 Configuration oldConf = reconf.getConf(); 090 Configuration newConf = new Configuration(); 091 092 Collection<ReconfigurationUtil.PropertyChange> changes = 093 ReconfigurationUtil.getChangedProperties(newConf, 094 oldConf); 095 096 boolean changeOK = true; 097 098 out.println("<form action=\"\" method=\"post\">"); 099 out.println("<table border=\"1\">"); 100 out.println("<tr><th>Property</th><th>Old value</th>"); 101 out.println("<th>New value </th><th></th></tr>"); 102 for (ReconfigurationUtil.PropertyChange c: changes) { 103 out.print("<tr><td>"); 104 if (!reconf.isPropertyReconfigurable(c.prop)) { 105 out.print("<font color=\"red\">" + 106 StringEscapeUtils.escapeHtml(c.prop) + "</font>"); 107 changeOK = false; 108 } else { 109 out.print(StringEscapeUtils.escapeHtml(c.prop)); 110 out.print("<input type=\"hidden\" name=\"" + 111 StringEscapeUtils.escapeHtml(c.prop) + "\" value=\"" + 112 StringEscapeUtils.escapeHtml(c.newVal) + "\"/>"); 113 } 114 out.print("</td><td>" + 115 (c.oldVal == null ? "<it>default</it>" : 116 StringEscapeUtils.escapeHtml(c.oldVal)) + 117 "</td><td>" + 118 (c.newVal == null ? "<it>default</it>" : 119 StringEscapeUtils.escapeHtml(c.newVal)) + 120 "</td>"); 121 out.print("</tr>\n"); 122 } 123 out.println("</table>"); 124 if (!changeOK) { 125 out.println("<p><font color=\"red\">WARNING: properties marked red" + 126 " will not be changed until the next restart.</font></p>"); 127 } 128 out.println("<input type=\"submit\" value=\"Apply\" />"); 129 out.println("</form>"); 130 } 131 132 @SuppressWarnings("unchecked") 133 private Enumeration<String> getParams(HttpServletRequest req) { 134 return (Enumeration<String>) req.getParameterNames(); 135 } 136 137 /** 138 * Apply configuratio changes after admin has approved them. 139 */ 140 private void applyChanges(PrintWriter out, Reconfigurable reconf, 141 HttpServletRequest req) 142 throws IOException, ReconfigurationException { 143 Configuration oldConf = reconf.getConf(); 144 Configuration newConf = new Configuration(); 145 146 Enumeration<String> params = getParams(req); 147 148 synchronized(oldConf) { 149 while (params.hasMoreElements()) { 150 String rawParam = params.nextElement(); 151 String param = StringEscapeUtils.unescapeHtml(rawParam); 152 String value = 153 StringEscapeUtils.unescapeHtml(req.getParameter(rawParam)); 154 if (value != null) { 155 if (value.equals(newConf.getRaw(param)) || value.equals("default") || 156 value.equals("null") || value.equals("")) { 157 if ((value.equals("default") || value.equals("null") || 158 value.equals("")) && 159 oldConf.getRaw(param) != null) { 160 out.println("<p>Changed \"" + 161 StringEscapeUtils.escapeHtml(param) + "\" from \"" + 162 StringEscapeUtils.escapeHtml(oldConf.getRaw(param)) + 163 "\" to default</p>"); 164 reconf.reconfigureProperty(param, null); 165 } else if (!value.equals("default") && !value.equals("null") && 166 !value.equals("") && 167 (oldConf.getRaw(param) == null || 168 !oldConf.getRaw(param).equals(value))) { 169 // change from default or value to different value 170 if (oldConf.getRaw(param) == null) { 171 out.println("<p>Changed \"" + 172 StringEscapeUtils.escapeHtml(param) + 173 "\" from default to \"" + 174 StringEscapeUtils.escapeHtml(value) + "\"</p>"); 175 } else { 176 out.println("<p>Changed \"" + 177 StringEscapeUtils.escapeHtml(param) + "\" from \"" + 178 StringEscapeUtils.escapeHtml(oldConf. 179 getRaw(param)) + 180 "\" to \"" + 181 StringEscapeUtils.escapeHtml(value) + "\"</p>"); 182 } 183 reconf.reconfigureProperty(param, value); 184 } else { 185 LOG.info("property " + param + " unchanged"); 186 } 187 } else { 188 // parameter value != newConf value 189 out.println("<p>\"" + StringEscapeUtils.escapeHtml(param) + 190 "\" not changed because value has changed from \"" + 191 StringEscapeUtils.escapeHtml(value) + "\" to \"" + 192 StringEscapeUtils.escapeHtml(newConf.getRaw(param)) + 193 "\" since approval</p>"); 194 } 195 } 196 } 197 } 198 } 199 200 @Override 201 protected void doGet(HttpServletRequest req, HttpServletResponse resp) 202 throws ServletException, IOException { 203 LOG.info("GET"); 204 PrintWriter out = resp.getWriter(); 205 206 Reconfigurable reconf = getReconfigurable(req); 207 String nodeName = reconf.getClass().getCanonicalName(); 208 209 printHeader(out, nodeName); 210 printConf(out, reconf); 211 printFooter(out); 212 } 213 214 @Override 215 protected void doPost(HttpServletRequest req, HttpServletResponse resp) 216 throws ServletException, IOException { 217 LOG.info("POST"); 218 PrintWriter out = resp.getWriter(); 219 220 Reconfigurable reconf = getReconfigurable(req); 221 String nodeName = reconf.getClass().getCanonicalName(); 222 223 printHeader(out, nodeName); 224 225 try { 226 applyChanges(out, reconf, req); 227 } catch (ReconfigurationException e) { 228 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 229 StringUtils.stringifyException(e)); 230 return; 231 } 232 233 out.println("<p><a href=\"" + req.getServletPath() + "\">back</a></p>"); 234 printFooter(out); 235 } 236 237 }