SLIDER-5 CLI minimal configs come back from parent; full from full path

git-svn-id: https://svn.apache.org/repos/asf/incubator/slider/trunk@1592568 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
index 054fc66..866ee88 100644
--- a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
+++ b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java
@@ -1958,6 +1958,11 @@
     } else if (registryArgs.listConf) {
       // list the configurations
       actionRegistryListConfigs(registryArgs);
+    } else if (SliderUtils.isSet(registryArgs.getConf)) {
+      // get a configuration
+      PublishedConfiguration publishedConfiguration =
+          actionRegistryGetConfig(registryArgs);
+      
     } else {
       exitCode = EXIT_FALSE;
     }
@@ -1986,7 +1991,7 @@
   }
 
   /**
-   * Registry operation
+   * list configs available for an instance
    *
    * @param registryArgs registry Arguments
    * @throws YarnException YARN problems
@@ -2013,6 +2018,44 @@
     }
   }
 
+  /**
+   * list configs available for an instance
+   *
+   * @param registryArgs registry Arguments
+   * @throws YarnException YARN problems
+   * @throws IOException Network or other problems
+   */
+  public PublishedConfiguration actionRegistryGetConfig(ActionRegistryArgs registryArgs)
+      throws YarnException, IOException {
+    ServiceInstanceData instance = lookupInstance(registryArgs);
+
+    RegistryRetriever retriever = new RegistryRetriever(instance);
+    boolean external = !registryArgs.internal;
+    PublishedConfigSet configurations =
+        retriever.getConfigurations(external);
+
+    PublishedConfiguration published =
+        retriever.retrieveConfiguration(registryArgs.getConf, external);
+    return published;
+  }
+  
+  private void outputConfig(PublishedConfiguration published,
+      ActionRegistryArgs registryArgs) {
+    // decide whether or not to print
+    boolean print = registryArgs.dest == null;
+    String entry = registryArgs.getConf;
+    String format = registryArgs.format;
+    File destFile;
+    if (!print) {
+      destFile = registryArgs.dest;
+      if (destFile.isDirectory()) {
+        // creating it under a directory
+        destFile = new File(destFile, entry + "." + format);
+      }
+      log.info("Destination path: {}", destFile);
+    }
+    
+  }
 
   /**
    * Look up an instance
@@ -2123,4 +2166,13 @@
       IOException {
     return maybeStartRegistry();
   }
+
+  /**
+   * Output to standard out/stderr (implementation specific detail)
+   * @param src source
+   */
+  @SuppressWarnings("UseOfSystemOutOrSystemErr")
+  private static void print(CharSequence src) {
+    System.out.append(src);
+  }
 }
diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormats.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormats.java
new file mode 100644
index 0000000..b18cebb
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormats.java
@@ -0,0 +1,38 @@
+/*
+ * 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.slider.core.registry.docstore;
+
+public enum ConfigFormats {
+
+  JSON("json"),
+  PROPERTIES("properties"),
+  XML("xml"),
+  YAML("yaml");
+
+  ConfigFormats(String suffix) {
+    this.suffix = suffix;
+  }
+
+  private final String suffix;
+
+  public String getSuffix() {
+    return suffix;
+  }
+  
+}
diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
index 3c7b521..00bdeb5 100644
--- a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
+++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
@@ -21,7 +21,6 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.slider.common.tools.ConfigHelper;
 import org.apache.slider.core.exceptions.BadConfigException;
-import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
@@ -43,9 +42,6 @@
 public class PublishedConfiguration {
 
   public String description;
-
-  public int size;
-  
   public long updated;
   
   public String updatedTime;
@@ -59,7 +55,7 @@
     return updated;
   }
 
-  private Map<String, String> values = new HashMap<String, String>();
+  public Map<String, String> entries = new HashMap<String, String>();
 
   /**
    * Is the configuration empty. This means either that it has not
@@ -68,7 +64,7 @@
    * @return
    */
   public boolean isEmpty() {
-    return values.isEmpty();
+    return entries.isEmpty();
   }
 
   /**
@@ -78,9 +74,9 @@
    * @param entries entries to put
    */
   public void putValues(Iterable<Map.Entry<String, String>> entries) {
-    values = new HashMap<String, String>();
+    this.entries = new HashMap<String, String>();
     for (Map.Entry<String, String> entry : entries) {
-      values.put(entry.getKey(), entry.getValue());
+      this.entries.put(entry.getKey(), entry.getValue());
     }
     
   }
@@ -92,7 +88,7 @@
   public Configuration asConfiguration() {
     Configuration conf = new Configuration(false);
     try {
-      ConfigHelper.addConfigMap(conf, values, "");
+      ConfigHelper.addConfigMap(conf, entries, "");
     } catch (BadConfigException e) {
       // triggered on a null value; switch to a runtime (and discard the stack)
       throw new RuntimeException(e.toString());
@@ -110,7 +106,7 @@
    */
   public Properties asProperties() {
     Properties props = new Properties();
-    props.putAll(values);
+    props.putAll(entries);
     return props;
   }
 
@@ -121,7 +117,7 @@
    */
   public String asJson() throws IOException {
     ObjectMapper mapper = new ObjectMapper();
-    String json = mapper.writeValueAsString(values);
+    String json = mapper.writeValueAsString(entries);
     return json;
   }
 
@@ -134,9 +130,18 @@
   public PublishedConfiguration shallowCopy() {
     PublishedConfiguration that = new PublishedConfiguration();
     that.description = this.description;
-    that.size = this.size;
     that.updated = this.updated;
     that.updatedTime = this.updatedTime;
     return that;
   }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb =
+        new StringBuilder("PublishedConfiguration{");
+    sb.append("description='").append(description).append('\'');
+    sb.append("entries = ").append(entries.size());
+    sb.append('}');
+    return sb.toString();
+  }
 }
diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
new file mode 100644
index 0000000..ba38967
--- /dev/null
+++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.slider.core.registry.docstore;
+
+import java.io.File;
+import java.io.IOException;
+
+public abstract class PublishedConfigurationOutputter {
+
+  public void save(File dest) throws IOException {
+    
+  }
+
+  public String asString() {
+    return "";
+  } 
+
+}
diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/info/ServiceInstanceData.java b/slider-core/src/main/java/org/apache/slider/core/registry/info/ServiceInstanceData.java
index 6c6918c..e123a12 100644
--- a/slider-core/src/main/java/org/apache/slider/core/registry/info/ServiceInstanceData.java
+++ b/slider-core/src/main/java/org/apache/slider/core/registry/info/ServiceInstanceData.java
@@ -50,7 +50,7 @@
     final StringBuilder sb =
         new StringBuilder("ServiceInstanceData{");
     sb.append(", id='").append(id).append('\'');
-    sb.append("serviceType='").append(serviceType).append('\'');
+    sb.append(", serviceType='").append(serviceType).append('\'');
     sb.append('}');
     return sb.toString();
   }
diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java b/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java
index 5d34ea8..e4cf428 100644
--- a/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java
+++ b/slider-core/src/main/java/org/apache/slider/core/registry/retrieve/RegistryRetriever.java
@@ -25,8 +25,10 @@
 import com.sun.jersey.api.client.config.ClientConfig;
 import com.sun.jersey.api.client.config.DefaultClientConfig;
 import com.sun.jersey.api.json.JSONConfiguration;
+import org.apache.slider.common.tools.SliderUtils;
 import org.apache.slider.core.exceptions.ExceptionConverter;
 import org.apache.slider.core.registry.docstore.PublishedConfigSet;
+import org.apache.slider.core.registry.docstore.PublishedConfiguration;
 import org.apache.slider.core.registry.info.RegistryView;
 import org.apache.slider.core.registry.info.ServiceInstanceData;
 import org.slf4j.Logger;
@@ -98,8 +100,7 @@
                                       + destination(external) + " view");
     }
     try {
-      WebResource webResource = jerseyClient.resource(confURL);
-      webResource.type(MediaType.APPLICATION_JSON);
+      WebResource webResource = jsonResource(confURL);
       log.debug("GET {}", confURL);
       PublishedConfigSet configSet = webResource.get(PublishedConfigSet.class);
       return configSet;
@@ -108,6 +109,38 @@
     }
   }
 
+  private WebResource resource(String url) {
+    WebResource resource = jerseyClient.resource(url);
+    return resource;
+  }
+
+  private WebResource jsonResource(String url) {
+    WebResource resource = resource(url);
+    resource.type(MediaType.APPLICATION_JSON);
+    return resource;
+  }
+
+  /**
+   * Get a complete configuration, with all values
+   * @param name
+   * @param external
+   * @return
+   * @throws IOException
+   */
+  public PublishedConfiguration retrieveConfiguration(String name,
+      boolean external) throws IOException {
+    String confURL = getRegistryView(external).configurationsURL;
+    confURL = SliderUtils.appendToURL(confURL, name);
+    try {
+      WebResource webResource = jsonResource(confURL);
+      log.debug("GET {}", confURL);
+      PublishedConfiguration configSet = webResource.get(PublishedConfiguration.class);
+      return configSet;
+    } catch (UniformInterfaceException e) {
+      throw ExceptionConverter.convertJerseyException(confURL, e);
+    }
+  }
+  
   @Override
   public String toString() {
     return super.toString() + " - " + instance;
diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java
index 318ca03..d18a08d 100644
--- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java
+++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/publisher/PublisherResource.java
@@ -75,7 +75,7 @@
 
   @GET
   @Path("/{config}")
-  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+  @Produces({MediaType.APPLICATION_JSON})
   public PublishedConfiguration getConfigurationInstance(
       @PathParam("config") String config,
       @Context UriInfo uriInfo,
diff --git a/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneRegistryAM.groovy b/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneRegistryAM.groovy
index 904f715..6de0a9f 100644
--- a/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneRegistryAM.groovy
+++ b/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneRegistryAM.groovy
@@ -22,6 +22,7 @@
 import groovy.util.logging.Slf4j
 import org.apache.hadoop.yarn.api.records.ApplicationReport
 import org.apache.hadoop.yarn.api.records.YarnApplicationState
+import org.apache.hadoop.yarn.conf.YarnConfiguration
 import org.apache.slider.agent.AgentMiniClusterTestBase
 import org.apache.slider.api.ClusterNode
 import org.apache.slider.client.SliderClient
@@ -30,6 +31,7 @@
 import org.apache.slider.core.main.ServiceLauncher
 import org.apache.slider.core.persist.JsonSerDeser
 import org.apache.slider.core.registry.docstore.PublishedConfigSet
+import org.apache.slider.core.registry.docstore.PublishedConfiguration
 import org.apache.slider.core.registry.info.CustomRegistryConstants
 import org.apache.slider.core.registry.info.ServiceInstanceData
 import org.apache.slider.core.registry.retrieve.RegistryRetriever
@@ -129,9 +131,17 @@
 
     def externalEndpoints = serviceInstanceData.externalView.endpoints
 
-    def endpoint = externalEndpoints.get(CustomRegistryConstants.PUBLISHER_REST_API)
-    assert endpoint != null
-    def publisherURL = endpoint.asURL()
+    // hit the registry web page
+
+    def registryEndpoint = externalEndpoints.get(
+        CustomRegistryConstants.REGISTRY_REST_API)
+    assert registryEndpoint != null
+    def registryURL = registryEndpoint.asURL()
+    describe("Registry WADL @ $registryURL")
+    
+    def publisherEndpoint = externalEndpoints.get(CustomRegistryConstants.PUBLISHER_REST_API)
+    assert publisherEndpoint != null
+    def publisherURL = publisherEndpoint.asURL()
     def publisher = publisherURL.toString()
     describe("Publisher")
 
@@ -142,24 +152,31 @@
     def configSet = serDeser.fromJson(publishedJSON)
     assert configSet.size() >= 1
     assert configSet.contains(YARN_SITE)
-    def publishedYarnSite = configSet.get(YARN_SITE)
+    PublishedConfiguration publishedYarnSite = configSet.get(YARN_SITE)
 
+    assert publishedYarnSite.empty
     
+    //get the full URL
     def yarnSitePublisher = appendToURL(publisher, YARN_SITE)
+
+    String confJSON = GET(yarnSitePublisher)
+    log.info(confJSON)
+    JsonSerDeser< PublishedConfiguration> confSerDeser =
+        new JsonSerDeser<PublishedConfiguration>(PublishedConfiguration)
+
+    publishedYarnSite = confSerDeser.fromJson(confJSON)
+    
+    assert !publishedYarnSite.empty
+
+
+    //get the XML
     def yarnSiteXML = appendToURL(yarnSitePublisher, "xml")
 
 
     String confXML = GET(yarnSiteXML)
     log.info("Conf XML at $yarnSiteXML = \n $confXML")
 
-    String confJSON = GET(yarnSitePublisher, "json")
 
-    // hit the registry web page
-
-    def registryEndpoint = externalEndpoints.get(CustomRegistryConstants.REGISTRY_REST_API)
-    assert registryEndpoint != null
-    def registryURL = registryEndpoint.asURL()
-    describe("Registry WADL @ $registryURL")
 
     describe("Registry List")
     log.info(GET(registryURL, RestPaths.REGISTRY_SERVICE ))
@@ -177,8 +194,16 @@
       def config = externalConf.get(key)
       log.info "$key -- ${config.description}"
     }
-    assert externalConf["yarn-site.xml"]
+    assert externalConf[YARN_SITE]
 
+
+    def yarnSite = retriever.retrieveConfiguration(YARN_SITE, true)
+    assert !yarnSite.empty
+    def siteXML = yarnSite.asConfiguration()
+    def rmAddr = siteXML.get(YarnConfiguration.RM_ADDRESS)
+    assert rmAddr
+    
+    
     describe "Internal configurations"
     assert !retriever.hasConfigurations(false)
     try {