Return-Path: X-Original-To: apmail-hadoop-common-commits-archive@www.apache.org Delivered-To: apmail-hadoop-common-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2369DDCDD for ; Sat, 27 Oct 2012 00:39:35 +0000 (UTC) Received: (qmail 92396 invoked by uid 500); 27 Oct 2012 00:39:34 -0000 Delivered-To: apmail-hadoop-common-commits-archive@hadoop.apache.org Received: (qmail 92341 invoked by uid 500); 27 Oct 2012 00:39:34 -0000 Mailing-List: contact common-commits-help@hadoop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: common-dev@hadoop.apache.org Delivered-To: mailing list common-commits@hadoop.apache.org Received: (qmail 92332 invoked by uid 99); 27 Oct 2012 00:39:34 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 27 Oct 2012 00:39:34 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 27 Oct 2012 00:39:32 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 9746123888CD for ; Sat, 27 Oct 2012 00:38:49 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1402728 - in /hadoop/common/branches/branch-1: ./ src/core/org/apache/hadoop/conf/ src/core/org/apache/hadoop/http/ src/test/org/apache/hadoop/conf/ Date: Sat, 27 Oct 2012 00:38:49 -0000 To: common-commits@hadoop.apache.org From: suresh@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20121027003849.9746123888CD@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: suresh Date: Sat Oct 27 00:38:46 2012 New Revision: 1402728 URL: http://svn.apache.org/viewvc?rev=1402728&view=rev Log: HADOOP-8567. Port conf servlet to dump running configuration to branch 1.x. Contributed by Jing Zhao. Added: hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/ConfServlet.java hadoop/common/branches/branch-1/src/test/org/apache/hadoop/conf/TestConfServlet.java Modified: hadoop/common/branches/branch-1/CHANGES.txt hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/Configuration.java hadoop/common/branches/branch-1/src/core/org/apache/hadoop/http/HttpServer.java Modified: hadoop/common/branches/branch-1/CHANGES.txt URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/CHANGES.txt?rev=1402728&r1=1402727&r2=1402728&view=diff ============================================================================== --- hadoop/common/branches/branch-1/CHANGES.txt (original) +++ hadoop/common/branches/branch-1/CHANGES.txt Sat Oct 27 00:38:46 2012 @@ -104,6 +104,9 @@ Release 1.2.0 - unreleased HDFS-3062. Print logs outside the namesystem lock invalidateWorkForOneNode and computeReplicationWorkForBlock. (Jing Zhao via suresh) + HADOOP-8567. Port conf servlet to dump running configuration to branch 1.x. + (Jing Zhao via suresh) + OPTIMIZATIONS HDFS-2533. Backport: Remove needless synchronization on some FSDataSet Added: hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/ConfServlet.java URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/ConfServlet.java?rev=1402728&view=auto ============================================================================== --- hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/ConfServlet.java (added) +++ hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/ConfServlet.java Sat Oct 27 00:38:46 2012 @@ -0,0 +1,100 @@ +/** + * 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.hadoop.conf; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.http.HttpServer; + +/** + * A servlet to print out the running configuration data. + */ +@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"}) +@InterfaceStability.Unstable +public class ConfServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + private static final String FORMAT_JSON = "json"; + private static final String FORMAT_XML = "xml"; + private static final String FORMAT_PARAM = "format"; + + /** + * Return the Configuration of the daemon hosting this servlet. + * This is populated when the HttpServer starts. + */ + private Configuration getConfFromContext() { + Configuration conf = (Configuration)getServletContext().getAttribute( + HttpServer.CONF_CONTEXT_ATTRIBUTE); + assert conf != null; + return conf; + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String format = request.getParameter(FORMAT_PARAM); + if (null == format) { + format = FORMAT_XML; + } + + if (FORMAT_XML.equals(format)) { + response.setContentType("text/xml; charset=utf-8"); + } else if (FORMAT_JSON.equals(format)) { + response.setContentType("application/json; charset=utf-8"); + } + + Writer out = response.getWriter(); + try { + writeResponse(getConfFromContext(), out, format); + } catch (BadFormatException bfe) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, bfe.getMessage()); + } + out.close(); + } + + /** + * Guts of the servlet - extracted for easy testing. + */ + static void writeResponse(Configuration conf, Writer out, String format) + throws IOException, BadFormatException { + if (FORMAT_JSON.equals(format)) { + Configuration.dumpConfiguration(conf, out); + } else if (FORMAT_XML.equals(format)) { + conf.writeXml(out); + } else { + throw new BadFormatException("Bad format: " + format); + } + } + + public static class BadFormatException extends Exception { + private static final long serialVersionUID = 1L; + + public BadFormatException(String msg) { + super(msg); + } + } + +} Modified: hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/Configuration.java URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/Configuration.java?rev=1402728&r1=1402727&r2=1402728&view=diff ============================================================================== --- hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/Configuration.java (original) +++ hadoop/common/branches/branch-1/src/core/org/apache/hadoop/conf/Configuration.java Sat Oct 27 00:38:46 2012 @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.URL; @@ -51,6 +52,7 @@ import javax.xml.parsers.DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; @@ -65,6 +67,7 @@ import org.apache.hadoop.util.Reflection import org.apache.hadoop.util.StringUtils; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; +import org.w3c.dom.Comment; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -171,10 +174,10 @@ public class Configuration implements It new CopyOnWriteArrayList(); /** - * Flag to indicate if the storage of resource which updates a key needs - * to be stored for each key + * The value reported as the setting resource when a key is set + * by code rather than a file resource. */ - private boolean storeResource; + static final String UNKNOWN_RESOURCE = "Unknown"; /** * Stores the mapping of key to the resource which modifies or loads @@ -223,30 +226,13 @@ public class Configuration implements It */ public Configuration(boolean loadDefaults) { this.loadDefaults = loadDefaults; + updatingResource = new HashMap(); if (LOG.isDebugEnabled()) { LOG.debug(StringUtils.stringifyException(new IOException("config()"))); } synchronized(Configuration.class) { REGISTRY.put(this, null); } - this.storeResource = false; - } - - /** - * A new configuration with the same settings and additional facility for - * storage of resource to each key which loads or updates - * the key most recently - * @param other the configuration from which to clone settings - * @param storeResource flag to indicate if the storage of resource to - * each key is to be stored - */ - private Configuration(Configuration other, boolean storeResource) { - this(other); - this.loadDefaults = other.loadDefaults; - this.storeResource = storeResource; - if (storeResource) { - updatingResource = new HashMap(); - } } /** @@ -260,20 +246,23 @@ public class Configuration implements It LOG.debug(StringUtils.stringifyException (new IOException("config(config)"))); } - - this.resources = (ArrayList)other.resources.clone(); - synchronized(other) { - if (other.properties != null) { - this.properties = (Properties)other.properties.clone(); - } - - if (other.overlay!=null) { - this.overlay = (Properties)other.overlay.clone(); - } - } - + + this.resources = (ArrayList) other.resources.clone(); + synchronized (other) { + if (other.properties != null) { + this.properties = (Properties) other.properties.clone(); + } + + if (other.overlay != null) { + this.overlay = (Properties) other.overlay.clone(); + } + + this.updatingResource = new HashMap( + other.updatingResource); + } + this.finalParameters = new HashSet(other.finalParameters); - synchronized(Configuration.class) { + synchronized (Configuration.class) { REGISTRY.put(this, null); } } @@ -437,6 +426,7 @@ public class Configuration implements It public void set(String name, String value) { getOverlay().setProperty(name, value); getProps().setProperty(name, value); + this.updatingResource.put(name, UNKNOWN_RESOURCE); } /** @@ -1071,10 +1061,8 @@ public class Configuration implements It loadResources(properties, resources, quietmode); if (overlay!= null) { properties.putAll(overlay); - if (storeResource) { - for (Map.Entry item: overlay.entrySet()) { - updatingResource.put((String) item.getKey(), "Unknown"); - } + for (Map.Entry item: overlay.entrySet()) { + updatingResource.put((String) item.getKey(), UNKNOWN_RESOURCE); } } } @@ -1246,9 +1234,7 @@ public class Configuration implements It if (value != null) { if (!finalParameters.contains(attr)) { properties.setProperty(attr, value); - if (storeResource) { - updatingResource.put(attr, name.toString()); - } + updatingResource.put(attr, name.toString()); } else if (!value.equals(properties.getProperty(attr))) { LOG.warn(name+":a attempt to override final parameter: "+attr +"; Ignoring."); @@ -1276,12 +1262,22 @@ public class Configuration implements It } /** - * Write out the non-default properties in this configuration to the give + * Write out the non-default properties in this configuration to the given * {@link OutputStream}. * * @param out the output stream to write to. */ public void writeXml(OutputStream out) throws IOException { + writeXml(new OutputStreamWriter(out)); + } + + /** + * Write out the non-default properties in this configuration to the given + * {@link Writer}. + * + * @param out the writer to write to. + */ + public synchronized void writeXml(Writer out) throws IOException { Properties properties = getProps(); try { Document doc = @@ -1300,7 +1296,11 @@ public class Configuration implements It } Element propNode = doc.createElement("property"); conf.appendChild(propNode); - + if (updatingResource != null) { + Comment commentNode = doc.createComment("Loaded from " + + updatingResource.get(name)); + propNode.appendChild(commentNode); + } Element nameNode = doc.createElement("name"); nameNode.appendChild(doc.createTextNode(name)); propNode.appendChild(nameNode); @@ -1317,8 +1317,10 @@ public class Configuration implements It TransformerFactory transFactory = TransformerFactory.newInstance(); Transformer transformer = transFactory.newTransformer(); transformer.transform(source, result); - } catch (Exception e) { - throw new RuntimeException(e); + } catch (TransformerException te) { + throw new IOException(te); + } catch (ParserConfigurationException pe) { + throw new IOException(pe); } } @@ -1333,26 +1335,26 @@ public class Configuration implements It * @param out the Writer to write to * @throws IOException */ - public static void dumpConfiguration(Configuration conf, + public static void dumpConfiguration(Configuration config, Writer out) throws IOException { - Configuration config = new Configuration(conf,true); - config.reloadConfiguration(); JsonFactory dumpFactory = new JsonFactory(); JsonGenerator dumpGenerator = dumpFactory.createJsonGenerator(out); dumpGenerator.writeStartObject(); dumpGenerator.writeFieldName("properties"); dumpGenerator.writeStartArray(); dumpGenerator.flush(); - for (Map.Entry item: config.getProps().entrySet()) { - dumpGenerator.writeStartObject(); - dumpGenerator.writeStringField("key", (String) item.getKey()); - dumpGenerator.writeStringField("value", - config.get((String) item.getKey())); - dumpGenerator.writeBooleanField("isFinal", - config.finalParameters.contains(item.getKey())); - dumpGenerator.writeStringField("resource", - config.updatingResource.get(item.getKey())); - dumpGenerator.writeEndObject(); + synchronized (config) { + for (Map.Entry item: config.getProps().entrySet()) { + dumpGenerator.writeStartObject(); + dumpGenerator.writeStringField("key", (String) item.getKey()); + dumpGenerator.writeStringField("value", + config.get((String) item.getKey())); + dumpGenerator.writeBooleanField("isFinal", + config.finalParameters.contains(item.getKey())); + dumpGenerator.writeStringField("resource", + config.updatingResource.get(item.getKey())); + dumpGenerator.writeEndObject(); + } } dumpGenerator.writeEndArray(); dumpGenerator.writeEndObject(); Modified: hadoop/common/branches/branch-1/src/core/org/apache/hadoop/http/HttpServer.java URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/core/org/apache/hadoop/http/HttpServer.java?rev=1402728&r1=1402727&r2=1402728&view=diff ============================================================================== --- hadoop/common/branches/branch-1/src/core/org/apache/hadoop/http/HttpServer.java (original) +++ hadoop/common/branches/branch-1/src/core/org/apache/hadoop/http/HttpServer.java Sat Oct 27 00:38:46 2012 @@ -42,6 +42,7 @@ import javax.servlet.http.HttpServletRes import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.ConfServlet; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.jmx.JMXJsonServlet; @@ -85,10 +86,10 @@ public class HttpServer implements Filte static final String FILTER_INITIALIZER_PROPERTY = "hadoop.http.filter.initializers"; - + // The ServletContext attribute where the daemon Configuration // gets stored. - static final String CONF_CONTEXT_ATTRIBUTE = "hadoop.conf"; + public static final String CONF_CONTEXT_ATTRIBUTE = "hadoop.conf"; static final String ADMINS_ACL = "admins.acl"; public static final String SPNEGO_FILTER = "SpnegoFilter"; public static final String KRB5_FILTER = "krb5Filter"; @@ -273,6 +274,7 @@ public class HttpServer implements Filte addServlet("stacks", "/stacks", StackServlet.class); addServlet("logLevel", "/logLevel", LogLevel.Servlet.class); addServlet("jmx", "/jmx", JMXJsonServlet.class); + addServlet("conf", "/conf", ConfServlet.class); } public void addContext(Context ctxt, boolean isFiltered) Added: hadoop/common/branches/branch-1/src/test/org/apache/hadoop/conf/TestConfServlet.java URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-1/src/test/org/apache/hadoop/conf/TestConfServlet.java?rev=1402728&view=auto ============================================================================== --- hadoop/common/branches/branch-1/src/test/org/apache/hadoop/conf/TestConfServlet.java (added) +++ hadoop/common/branches/branch-1/src/test/org/apache/hadoop/conf/TestConfServlet.java Sat Oct 27 00:38:46 2012 @@ -0,0 +1,113 @@ +/** + * 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.hadoop.conf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.junit.Test; +import org.mortbay.util.ajax.JSON; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +/** + * Basic test case that the ConfServlet can write configuration + * to its output in XML and JSON format. + */ +public class TestConfServlet { + private static final String TEST_KEY = "testconfservlet.key"; + private static final String TEST_VAL = "testval"; + + private Configuration getTestConf() { + Configuration testConf = new Configuration(); + testConf.set(TEST_KEY, TEST_VAL); + return testConf; + } + + @Test + @SuppressWarnings("unchecked") + public void testWriteJson() throws Exception { + StringWriter sw = new StringWriter(); + ConfServlet.writeResponse(getTestConf(), sw, "json"); + String json = sw.toString(); + boolean foundSetting = false; + Object parsed = JSON.parse(json); + Object[] properties = ((Map)parsed).get("properties"); + for (Object o : properties) { + Map propertyInfo = (Map)o; + String key = (String)propertyInfo.get("key"); + String val = (String)propertyInfo.get("value"); + String resource = (String)propertyInfo.get("resource"); + System.err.println("k: " + key + " v: " + val + " r: " + resource); + if (TEST_KEY.equals(key) && TEST_VAL.equals(val) + && Configuration.UNKNOWN_RESOURCE.equals(resource)) { + foundSetting = true; + } + } + assertTrue(foundSetting); + } + + @Test + public void testWriteXml() throws Exception { + StringWriter sw = new StringWriter(); + ConfServlet.writeResponse(getTestConf(), sw, "xml"); + String xml = sw.toString(); + + DocumentBuilderFactory docBuilderFactory + = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = docBuilderFactory.newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(xml))); + NodeList nameNodes = doc.getElementsByTagName("name"); + boolean foundSetting = false; + for (int i = 0; i < nameNodes.getLength(); i++) { + Node nameNode = nameNodes.item(i); + String key = nameNode.getTextContent(); + System.err.println("xml key: " + key); + if (TEST_KEY.equals(key)) { + foundSetting = true; + Element propertyElem = (Element)nameNode.getParentNode(); + String val = propertyElem.getElementsByTagName("value").item(0).getTextContent(); + assertEquals(TEST_VAL, val); + } + } + assertTrue(foundSetting); + } + + @Test + public void testBadFormat() throws Exception { + StringWriter sw = new StringWriter(); + try { + ConfServlet.writeResponse(getTestConf(), sw, "not a format"); + fail("writeResponse with bad format didn't throw!"); + } catch (ConfServlet.BadFormatException bfe) { + // expected + } + assertEquals("", sw.toString()); + } +} \ No newline at end of file