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 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) throws ReconfigurationException {
142 Configuration oldConf = reconf.getConf();
143 Configuration newConf = new Configuration();
144
145 Enumeration<String> params = getParams(req);
146
147 synchronized(oldConf) {
148 while (params.hasMoreElements()) {
149 String rawParam = params.nextElement();
150 String param = StringEscapeUtils.unescapeHtml(rawParam);
151 String value =
152 StringEscapeUtils.unescapeHtml(req.getParameter(rawParam));
153 if (value != null) {
154 if (value.equals(newConf.getRaw(param)) || value.equals("default") ||
155 value.equals("null") || value.isEmpty()) {
156 if ((value.equals("default") || value.equals("null") ||
157 value.isEmpty()) &&
158 oldConf.getRaw(param) != null) {
159 out.println("<p>Changed \"" +
160 StringEscapeUtils.escapeHtml(param) + "\" from \"" +
161 StringEscapeUtils.escapeHtml(oldConf.getRaw(param)) +
162 "\" to default</p>");
163 reconf.reconfigureProperty(param, null);
164 } else if (!value.equals("default") && !value.equals("null") &&
165 !value.isEmpty() &&
166 (oldConf.getRaw(param) == null ||
167 !oldConf.getRaw(param).equals(value))) {
168 // change from default or value to different value
169 if (oldConf.getRaw(param) == null) {
170 out.println("<p>Changed \"" +
171 StringEscapeUtils.escapeHtml(param) +
172 "\" from default to \"" +
173 StringEscapeUtils.escapeHtml(value) + "\"</p>");
174 } else {
175 out.println("<p>Changed \"" +
176 StringEscapeUtils.escapeHtml(param) + "\" from \"" +
177 StringEscapeUtils.escapeHtml(oldConf.
178 getRaw(param)) +
179 "\" to \"" +
180 StringEscapeUtils.escapeHtml(value) + "\"</p>");
181 }
182 reconf.reconfigureProperty(param, value);
183 } else {
184 LOG.info("property " + param + " unchanged");
185 }
186 } else {
187 // parameter value != newConf value
188 out.println("<p>\"" + StringEscapeUtils.escapeHtml(param) +
189 "\" not changed because value has changed from \"" +
190 StringEscapeUtils.escapeHtml(value) + "\" to \"" +
191 StringEscapeUtils.escapeHtml(newConf.getRaw(param)) +
192 "\" since approval</p>");
193 }
194 }
195 }
196 }
197 }
198
199 @Override
200 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
201 throws ServletException, IOException {
202 LOG.info("GET");
203 resp.setContentType("text/html");
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 resp.setContentType("text/html");
219 PrintWriter out = resp.getWriter();
220
221 Reconfigurable reconf = getReconfigurable(req);
222 String nodeName = reconf.getClass().getCanonicalName();
223
224 printHeader(out, nodeName);
225
226 try {
227 applyChanges(out, reconf, req);
228 } catch (ReconfigurationException e) {
229 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
230 StringUtils.stringifyException(e));
231 return;
232 }
233
234 out.println("<p><a href=\"" + req.getServletPath() + "\">back</a></p>");
235 printFooter(out);
236 }
237
238 }