Skip to content

Commit 00160f7

Browse files
committed
HADOOP-13628. Support to retrieve specific property from configuration via REST API. Contributed by Weiwei Yang
1 parent d65b957 commit 00160f7

File tree

4 files changed

+491
-74
lines changed

4 files changed

+491
-74
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ConfServlet.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,14 @@ public void doGet(HttpServletRequest request, HttpServletResponse response)
7070
response.setContentType("application/json; charset=utf-8");
7171
}
7272

73+
String name = request.getParameter("name");
7374
Writer out = response.getWriter();
7475
try {
75-
writeResponse(getConfFromContext(), out, format);
76+
writeResponse(getConfFromContext(), out, format, name);
7677
} catch (BadFormatException bfe) {
7778
response.sendError(HttpServletResponse.SC_BAD_REQUEST, bfe.getMessage());
79+
} catch (IllegalArgumentException iae) {
80+
response.sendError(HttpServletResponse.SC_NOT_FOUND, iae.getMessage());
7881
}
7982
out.close();
8083
}
@@ -89,17 +92,23 @@ static String parseAccecptHeader(HttpServletRequest request) {
8992
/**
9093
* Guts of the servlet - extracted for easy testing.
9194
*/
92-
static void writeResponse(Configuration conf, Writer out, String format)
93-
throws IOException, BadFormatException {
95+
static void writeResponse(Configuration conf,
96+
Writer out, String format, String propertyName)
97+
throws IOException, IllegalArgumentException, BadFormatException {
9498
if (FORMAT_JSON.equals(format)) {
95-
Configuration.dumpConfiguration(conf, out);
99+
Configuration.dumpConfiguration(conf, propertyName, out);
96100
} else if (FORMAT_XML.equals(format)) {
97-
conf.writeXml(out);
101+
conf.writeXml(propertyName, out);
98102
} else {
99103
throw new BadFormatException("Bad format: " + format);
100104
}
101105
}
102106

107+
static void writeResponse(Configuration conf, Writer out, String format)
108+
throws IOException, BadFormatException {
109+
writeResponse(conf, out, format, null);
110+
}
111+
103112
public static class BadFormatException extends Exception {
104113
private static final long serialVersionUID = 1L;
105114

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/Configuration.java

Lines changed: 222 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@
103103
import org.xml.sax.SAXException;
104104

105105
import com.google.common.base.Preconditions;
106+
import com.google.common.base.Strings;
106107

107-
/**
108+
/**
108109
* Provides access to configuration parameters.
109110
*
110111
* <h4 id="Resources">Resources</h4>
@@ -2834,14 +2835,37 @@ public void writeXml(OutputStream out) throws IOException {
28342835
writeXml(new OutputStreamWriter(out, "UTF-8"));
28352836
}
28362837

2837-
/**
2838-
* Write out the non-default properties in this configuration to the given
2839-
* {@link Writer}.
2840-
*
2838+
public void writeXml(Writer out) throws IOException {
2839+
writeXml(null, out);
2840+
}
2841+
2842+
/**
2843+
* Write out the non-default properties in this configuration to the
2844+
* given {@link Writer}.
2845+
*
2846+
* <li>
2847+
* When property name is not empty and the property exists in the
2848+
* configuration, this method writes the property and its attributes
2849+
* to the {@link Writer}.
2850+
* </li>
2851+
* <p>
2852+
*
2853+
* <li>
2854+
* When property name is null or empty, this method writes all the
2855+
* configuration properties and their attributes to the {@link Writer}.
2856+
* </li>
2857+
* <p>
2858+
*
2859+
* <li>
2860+
* When property name is not empty but the property doesn't exist in
2861+
* the configuration, this method throws an {@link IllegalArgumentException}.
2862+
* </li>
2863+
* <p>
28412864
* @param out the writer to write to.
28422865
*/
2843-
public void writeXml(Writer out) throws IOException {
2844-
Document doc = asXmlDocument();
2866+
public void writeXml(String propertyName, Writer out)
2867+
throws IOException, IllegalArgumentException {
2868+
Document doc = asXmlDocument(propertyName);
28452869

28462870
try {
28472871
DOMSource source = new DOMSource(doc);
@@ -2861,62 +2885,180 @@ public void writeXml(Writer out) throws IOException {
28612885
/**
28622886
* Return the XML DOM corresponding to this Configuration.
28632887
*/
2864-
private synchronized Document asXmlDocument() throws IOException {
2888+
private synchronized Document asXmlDocument(String propertyName)
2889+
throws IOException, IllegalArgumentException {
28652890
Document doc;
28662891
try {
2867-
doc =
2868-
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
2892+
doc = DocumentBuilderFactory
2893+
.newInstance()
2894+
.newDocumentBuilder()
2895+
.newDocument();
28692896
} catch (ParserConfigurationException pe) {
28702897
throw new IOException(pe);
28712898
}
2899+
28722900
Element conf = doc.createElement("configuration");
28732901
doc.appendChild(conf);
28742902
conf.appendChild(doc.createTextNode("\n"));
28752903
handleDeprecation(); //ensure properties is set and deprecation is handled
2876-
for (Enumeration<Object> e = properties.keys(); e.hasMoreElements();) {
2877-
String name = (String)e.nextElement();
2878-
Object object = properties.get(name);
2879-
String value = null;
2880-
if (object instanceof String) {
2881-
value = (String) object;
2882-
}else {
2883-
continue;
2884-
}
2885-
Element propNode = doc.createElement("property");
2886-
conf.appendChild(propNode);
2887-
2888-
Element nameNode = doc.createElement("name");
2889-
nameNode.appendChild(doc.createTextNode(name));
2890-
propNode.appendChild(nameNode);
2891-
2892-
Element valueNode = doc.createElement("value");
2893-
valueNode.appendChild(doc.createTextNode(value));
2894-
propNode.appendChild(valueNode);
2895-
2896-
if (updatingResource != null) {
2897-
String[] sources = updatingResource.get(name);
2898-
if(sources != null) {
2899-
for(String s : sources) {
2900-
Element sourceNode = doc.createElement("source");
2901-
sourceNode.appendChild(doc.createTextNode(s));
2902-
propNode.appendChild(sourceNode);
2904+
2905+
if(!Strings.isNullOrEmpty(propertyName)) {
2906+
if (!properties.containsKey(propertyName)) {
2907+
// given property not found, illegal argument
2908+
throw new IllegalArgumentException("Property " +
2909+
propertyName + " not found");
2910+
} else {
2911+
// given property is found, write single property
2912+
appendXMLProperty(doc, conf, propertyName);
2913+
conf.appendChild(doc.createTextNode("\n"));
2914+
}
2915+
} else {
2916+
// append all elements
2917+
for (Enumeration<Object> e = properties.keys(); e.hasMoreElements();) {
2918+
appendXMLProperty(doc, conf, (String)e.nextElement());
2919+
conf.appendChild(doc.createTextNode("\n"));
2920+
}
2921+
}
2922+
return doc;
2923+
}
2924+
2925+
/**
2926+
* Append a property with its attributes to a given {#link Document}
2927+
* if the property is found in configuration.
2928+
*
2929+
* @param doc
2930+
* @param conf
2931+
* @param propertyName
2932+
*/
2933+
private synchronized void appendXMLProperty(Document doc, Element conf,
2934+
String propertyName) {
2935+
// skip writing if given property name is empty or null
2936+
if (!Strings.isNullOrEmpty(propertyName)) {
2937+
String value = properties.getProperty(propertyName);
2938+
if (value != null) {
2939+
Element propNode = doc.createElement("property");
2940+
conf.appendChild(propNode);
2941+
2942+
Element nameNode = doc.createElement("name");
2943+
nameNode.appendChild(doc.createTextNode(propertyName));
2944+
propNode.appendChild(nameNode);
2945+
2946+
Element valueNode = doc.createElement("value");
2947+
valueNode.appendChild(doc.createTextNode(
2948+
properties.getProperty(propertyName)));
2949+
propNode.appendChild(valueNode);
2950+
2951+
Element finalNode = doc.createElement("final");
2952+
finalNode.appendChild(doc.createTextNode(
2953+
String.valueOf(finalParameters.contains(propertyName))));
2954+
propNode.appendChild(finalNode);
2955+
2956+
if (updatingResource != null) {
2957+
String[] sources = updatingResource.get(propertyName);
2958+
if(sources != null) {
2959+
for(String s : sources) {
2960+
Element sourceNode = doc.createElement("source");
2961+
sourceNode.appendChild(doc.createTextNode(s));
2962+
propNode.appendChild(sourceNode);
2963+
}
29032964
}
29042965
}
29052966
}
2906-
2907-
conf.appendChild(doc.createTextNode("\n"));
29082967
}
2909-
return doc;
29102968
}
29112969

29122970
/**
2913-
* Writes out all the parameters and their properties (final and resource) to
2914-
* the given {@link Writer}
2915-
* The format of the output would be
2916-
* { "properties" : [ {key1,value1,key1.isFinal,key1.resource}, {key2,value2,
2917-
* key2.isFinal,key2.resource}... ] }
2918-
* It does not output the parameters of the configuration object which is
2919-
* loaded from an input stream.
2971+
* Writes properties and their attributes (final and resource)
2972+
* to the given {@link Writer}.
2973+
*
2974+
* <li>
2975+
* When propertyName is not empty, and the property exists
2976+
* in the configuration, the format of the output would be,
2977+
* <pre>
2978+
* {
2979+
* "property": {
2980+
* "key" : "key1",
2981+
* "value" : "value1",
2982+
* "isFinal" : "key1.isFinal",
2983+
* "resource" : "key1.resource"
2984+
* }
2985+
* }
2986+
* </pre>
2987+
* </li>
2988+
*
2989+
* <li>
2990+
* When propertyName is null or empty, it behaves same as
2991+
* {@link #dumpConfiguration(Configuration, Writer)}, the
2992+
* output would be,
2993+
* <pre>
2994+
* { "properties" :
2995+
* [ { key : "key1",
2996+
* value : "value1",
2997+
* isFinal : "key1.isFinal",
2998+
* resource : "key1.resource" },
2999+
* { key : "key2",
3000+
* value : "value2",
3001+
* isFinal : "ke2.isFinal",
3002+
* resource : "key2.resource" }
3003+
* ]
3004+
* }
3005+
* </pre>
3006+
* </li>
3007+
*
3008+
* <li>
3009+
* When propertyName is not empty, and the property is not
3010+
* found in the configuration, this method will throw an
3011+
* {@link IllegalArgumentException}.
3012+
* </li>
3013+
* <p>
3014+
* @param config the configuration
3015+
* @param propertyName property name
3016+
* @param out the Writer to write to
3017+
* @throws IOException
3018+
* @throws IllegalArgumentException when property name is not
3019+
* empty and the property is not found in configuration
3020+
**/
3021+
public static void dumpConfiguration(Configuration config,
3022+
String propertyName, Writer out) throws IOException {
3023+
if(Strings.isNullOrEmpty(propertyName)) {
3024+
dumpConfiguration(config, out);
3025+
} else if (Strings.isNullOrEmpty(config.get(propertyName))) {
3026+
throw new IllegalArgumentException("Property " +
3027+
propertyName + " not found");
3028+
} else {
3029+
JsonFactory dumpFactory = new JsonFactory();
3030+
JsonGenerator dumpGenerator = dumpFactory.createJsonGenerator(out);
3031+
dumpGenerator.writeStartObject();
3032+
dumpGenerator.writeFieldName("property");
3033+
appendJSONProperty(dumpGenerator, config, propertyName);
3034+
dumpGenerator.writeEndObject();
3035+
dumpGenerator.flush();
3036+
}
3037+
}
3038+
3039+
/**
3040+
* Writes out all properties and their attributes (final and resource) to
3041+
* the given {@link Writer}, the format of the output would be,
3042+
*
3043+
* <pre>
3044+
* { "properties" :
3045+
* [ { key : "key1",
3046+
* value : "value1",
3047+
* isFinal : "key1.isFinal",
3048+
* resource : "key1.resource" },
3049+
* { key : "key2",
3050+
* value : "value2",
3051+
* isFinal : "ke2.isFinal",
3052+
* resource : "key2.resource" }
3053+
* ]
3054+
* }
3055+
* </pre>
3056+
*
3057+
* It does not output the properties of the configuration object which
3058+
* is loaded from an input stream.
3059+
* <p>
3060+
*
3061+
* @param config the configuration
29203062
* @param out the Writer to write to
29213063
* @throws IOException
29223064
*/
@@ -2930,29 +3072,47 @@ public static void dumpConfiguration(Configuration config,
29303072
dumpGenerator.flush();
29313073
synchronized (config) {
29323074
for (Map.Entry<Object,Object> item: config.getProps().entrySet()) {
2933-
dumpGenerator.writeStartObject();
2934-
dumpGenerator.writeStringField("key", (String) item.getKey());
2935-
dumpGenerator.writeStringField("value",
2936-
config.get((String) item.getKey()));
2937-
dumpGenerator.writeBooleanField("isFinal",
2938-
config.finalParameters.contains(item.getKey()));
2939-
String[] resources = config.updatingResource.get(item.getKey());
2940-
String resource = UNKNOWN_RESOURCE;
2941-
if(resources != null && resources.length > 0) {
2942-
resource = resources[0];
2943-
}
2944-
dumpGenerator.writeStringField("resource", resource);
2945-
dumpGenerator.writeEndObject();
3075+
appendJSONProperty(dumpGenerator,
3076+
config,
3077+
item.getKey().toString());
29463078
}
29473079
}
29483080
dumpGenerator.writeEndArray();
29493081
dumpGenerator.writeEndObject();
29503082
dumpGenerator.flush();
29513083
}
2952-
3084+
3085+
/**
3086+
* Write property and its attributes as json format to given
3087+
* {@link JsonGenerator}.
3088+
*
3089+
* @param jsonGen json writer
3090+
* @param config configuration
3091+
* @param name property name
3092+
* @throws IOException
3093+
*/
3094+
private static void appendJSONProperty(JsonGenerator jsonGen,
3095+
Configuration config, String name) throws IOException {
3096+
// skip writing if given property name is empty or null
3097+
if(!Strings.isNullOrEmpty(name) && jsonGen != null) {
3098+
jsonGen.writeStartObject();
3099+
jsonGen.writeStringField("key", name);
3100+
jsonGen.writeStringField("value", config.get(name));
3101+
jsonGen.writeBooleanField("isFinal",
3102+
config.finalParameters.contains(name));
3103+
String[] resources = config.updatingResource.get(name);
3104+
String resource = UNKNOWN_RESOURCE;
3105+
if(resources != null && resources.length > 0) {
3106+
resource = resources[0];
3107+
}
3108+
jsonGen.writeStringField("resource", resource);
3109+
jsonGen.writeEndObject();
3110+
}
3111+
}
3112+
29533113
/**
29543114
* Get the {@link ClassLoader} for this job.
2955-
*
3115+
*
29563116
* @return the correct class loader.
29573117
*/
29583118
public ClassLoader getClassLoader() {

0 commit comments

Comments
 (0)