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 }