SLIDER-719 direct tests for PUT/POST/DELETE/HEAD all working with new client lib.
Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/95d4acb6 Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/95d4acb6 Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/95d4acb6 Branch: refs/heads/develop Commit: 95d4acb672b7a75e8b227e4d792178f83c0a543d Parents: 372a5b7 Author: Steve Loughran <[email protected]> Authored: Thu Jan 8 15:45:03 2015 +0000 Committer: Steve Loughran <[email protected]> Committed: Thu Jan 8 15:45:03 2015 +0000 ---------------------------------------------------------------------- .../core/restclient/HttpOperationResponse.java | 29 +++++++ .../apache/slider/core/restclient/HttpVerb.java | 28 +++++-- .../restclient/UrlConnectionOperations.java | 65 +++++++++++++--- .../rest/application/ApplicationResource.java | 79 +++++++++++++++----- .../application/actions/RestActionPing.java | 10 ++- .../application/resources/PingResource.java | 5 ++ .../standalone/TestStandaloneAgentWeb.groovy | 51 +++++++++++-- .../apache/slider/test/SliderTestUtils.groovy | 25 ++++++- 8 files changed, 243 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/main/java/org/apache/slider/core/restclient/HttpOperationResponse.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/restclient/HttpOperationResponse.java b/slider-core/src/main/java/org/apache/slider/core/restclient/HttpOperationResponse.java new file mode 100644 index 0000000..a5357a2 --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/core/restclient/HttpOperationResponse.java @@ -0,0 +1,29 @@ +/* + * 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.restclient; + +/** + * A response for use as a return value from operations + */ +public class HttpOperationResponse { + + public int responseCode; + public String contentType; + public byte[] data; +} http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/main/java/org/apache/slider/core/restclient/HttpVerb.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/restclient/HttpVerb.java b/slider-core/src/main/java/org/apache/slider/core/restclient/HttpVerb.java index 6767951..c040345 100644 --- a/slider-core/src/main/java/org/apache/slider/core/restclient/HttpVerb.java +++ b/slider-core/src/main/java/org/apache/slider/core/restclient/HttpVerb.java @@ -18,26 +18,40 @@ package org.apache.slider.core.restclient; +/** + * Http verbs with details on what they support in terms of submit and + * response bodies. + * <p> + * Those verbs which do support bodies in the response MAY NOT return it; + * if the response code is 204 then the answer is "no body", but the operation + * is considered a success. + */ public enum HttpVerb { - GET("GET", false), - POST("POST", true), - PUT("POST", true), - DELETE("DELETE", false), - HEAD("HEAD", false); + GET("GET", false, true), + POST("POST", true, true), + PUT("PUT", true, true), + DELETE("DELETE", false, true), + HEAD("HEAD", false, false); private final String verb; private final boolean hasUploadBody; + private final boolean hasResponseBody; - HttpVerb(String verb, boolean hasUploadBody) { + HttpVerb(String verb, boolean hasUploadBody, boolean hasResponseBody) { this.verb = verb; this.hasUploadBody = hasUploadBody; + this.hasResponseBody = hasResponseBody; } public String getVerb() { return verb; } - public boolean isHasUploadBody() { + public boolean hasUploadBody() { return hasUploadBody; } + + public boolean hasResponseBody() { + return hasResponseBody; + } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/main/java/org/apache/slider/core/restclient/UrlConnectionOperations.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/restclient/UrlConnectionOperations.java b/slider-core/src/main/java/org/apache/slider/core/restclient/UrlConnectionOperations.java index eb5d4a7..4633414 100644 --- a/slider-core/src/main/java/org/apache/slider/core/restclient/UrlConnectionOperations.java +++ b/slider-core/src/main/java/org/apache/slider/core/restclient/UrlConnectionOperations.java @@ -30,8 +30,10 @@ import org.apache.hadoop.yarn.webapp.NotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.ws.rs.core.MediaType; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; @@ -45,10 +47,22 @@ public class UrlConnectionOperations extends Configured { private URLConnectionFactory connectionFactory; + private boolean useSpnego = false; + public UrlConnectionOperations(Configuration conf) { super(conf); connectionFactory = URLConnectionFactory - .newDefaultURLConnectionFactory(conf); } + .newDefaultURLConnectionFactory(conf); + } + + + public boolean isUseSpnego() { + return useSpnego; + } + + public void setUseSpnego(boolean useSpnego) { + this.useSpnego = useSpnego; + } /** * Opens a url with read and connect timeouts @@ -58,28 +72,59 @@ public class UrlConnectionOperations extends Configured { * @return URLConnection * @throws IOException */ - public HttpURLConnection openConnection(URL url, boolean spnego) throws + public HttpURLConnection openConnection(URL url) throws IOException, AuthenticationException { Preconditions.checkArgument(url.getPort() != 0, "no port"); HttpURLConnection conn = - (HttpURLConnection) connectionFactory.openConnection(url, spnego); + (HttpURLConnection) connectionFactory.openConnection(url, useSpnego); conn.setUseCaches(false); conn.setInstanceFollowRedirects(true); return conn; } - public byte[] execGet(URL url, boolean spnego) throws + public HttpOperationResponse execGet(URL url) throws IOException, AuthenticationException { + return execHttpOperation(HttpVerb.GET, url, null, ""); + } + + public HttpOperationResponse execHttpOperation(HttpVerb verb, + URL url, + byte[] payload, + String contentType) + throws IOException, AuthenticationException { HttpURLConnection conn = null; + HttpOperationResponse outcome = new HttpOperationResponse(); int resultCode; byte[] body = null; - log.debug("GET {} spnego={}", url, spnego); + log.debug("{} {} spnego={}", verb, url, useSpnego); + boolean doOutput = verb.hasUploadBody(); + if (doOutput) { + Preconditions.checkArgument(payload !=null, + "Null payload on a verb which expects one"); + } try { - conn = openConnection(url, spnego); + conn = openConnection(url); + conn.setRequestMethod(verb.getVerb()); + conn.setDoOutput(doOutput); + if (doOutput) { + conn.setRequestProperty("Content-Type", contentType); + } + + + // now do the connection + conn.connect(); + + if (doOutput) { + OutputStream output = conn.getOutputStream(); + IOUtils.write(payload, output); + output.close(); + } + resultCode = conn.getResponseCode(); + outcome.contentType = conn.getContentType(); InputStream stream = conn.getErrorStream(); if (stream == null) { stream = conn.getInputStream(); @@ -93,11 +138,11 @@ public class UrlConnectionOperations extends Configured { } } catch (IOException e) { - throw NetUtils.wrapException(url.toString(), + throw NetUtils.wrapException(url.toString(), url.getPort(), "localhost", 0, e); } catch (AuthenticationException e) { - throw new IOException("From " + url + ": " + e.toString(), e); + throw new IOException("From " + url + ": " + e, e); } finally { if (conn != null) { @@ -105,7 +150,9 @@ public class UrlConnectionOperations extends Configured { } } uprateFaults(url.toString(), resultCode, body); - return body; + outcome.responseCode = resultCode; + outcome.data = body; + return outcome; } /** http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/ApplicationResource.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/ApplicationResource.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/ApplicationResource.java index 1bdf109..af310b1 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/ApplicationResource.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/ApplicationResource.java @@ -46,12 +46,18 @@ import org.slf4j.LoggerFactory; import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; + +import static javax.ws.rs.core.MediaType.*; import javax.ws.rs.core.UriInfo; import java.util.ArrayList; import java.util.List; @@ -129,7 +135,7 @@ public class ApplicationResource extends AbstractSliderResource { @GET @Path("/") - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public List<String> getRoot() { return ROOT_ENTRIES; } @@ -140,70 +146,70 @@ public class ApplicationResource extends AbstractSliderResource { */ @GET @Path(MODEL) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public List<String> getModel() { return MODEL_ENTRIES; } @GET @Path(MODEL_DESIRED) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public AggregateConf getModelDesired() { return lookupAggregateConf(MODEL_DESIRED); } @GET @Path(MODEL_DESIRED_APPCONF) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public ConfTree getModelDesiredAppconf() { return lookupConfTree(MODEL_DESIRED_APPCONF); } @GET @Path(MODEL_DESIRED_RESOURCES) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public ConfTree getModelDesiredResources() { return lookupConfTree(MODEL_DESIRED_RESOURCES); } @GET @Path(MODEL_RESOLVED) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public AggregateConf getModelResolved() { return lookupAggregateConf(MODEL_RESOLVED); } @GET @Path(MODEL_RESOLVED_APPCONF) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public ConfTree getModelResolvedAppconf() { return lookupConfTree(MODEL_RESOLVED_APPCONF); } @GET @Path(MODEL_RESOLVED_RESOURCES) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public ConfTree getModelResolvedResources() { return lookupConfTree(MODEL_RESOLVED_RESOURCES); } @GET @Path(LIVE) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public List<String> getLive() { return LIVE_ENTRIES; } @GET @Path(LIVE_RESOURCES) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public Object getLiveResources() { return lookupConfTree(LIVE_RESOURCES); } @GET @Path(LIVE_CONTAINERS) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public Map<String, SerializedContainerInformation> getLiveContainers() { try { return (Map<String, SerializedContainerInformation>)cache.lookup( @@ -215,7 +221,7 @@ public class ApplicationResource extends AbstractSliderResource { @GET @Path(LIVE_CONTAINERS + "/{containerId}") - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public SerializedContainerInformation getLiveContainer( @PathParam("containerId") String containerId) { try { @@ -230,7 +236,7 @@ public class ApplicationResource extends AbstractSliderResource { @GET @Path(LIVE_COMPONENTS) - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public Map<String, SerializedComponentInformation> getLiveComponents() { try { return (Map<String, SerializedComponentInformation>) cache.lookup( @@ -242,7 +248,7 @@ public class ApplicationResource extends AbstractSliderResource { @GET @Path(LIVE_COMPONENTS + "/{component}") - @Produces({MediaType.APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) public SerializedComponentInformation getLiveComponent( @PathParam("component") String component) { try { @@ -297,9 +303,46 @@ public class ApplicationResource extends AbstractSliderResource { @GET @Path(ACTION_PING) - @Produces({MediaType.APPLICATION_JSON}) - public Object actionPing(@Context HttpServletRequest request, + @Produces({APPLICATION_JSON}) + public Object actionPingGet(@Context HttpServletRequest request, + @Context UriInfo uriInfo) { + return new RestActionPing().ping(request, uriInfo, ""); + } + + @POST + @Path(ACTION_PING) + @Produces({APPLICATION_JSON}) + public Object actionPingPost(@Context HttpServletRequest request, + @Context UriInfo uriInfo, + String body) { + return new RestActionPing().ping(request, uriInfo, body); + } + + @PUT + @Path(ACTION_PING) + @Consumes({TEXT_PLAIN}) + @Produces({APPLICATION_JSON}) + public Object actionPingPut(@Context HttpServletRequest request, + @Context UriInfo uriInfo, + String body) { + return new RestActionPing().ping(request, uriInfo, body); + } + + @DELETE + @Path(ACTION_PING) + @Consumes({APPLICATION_JSON}) + @Produces({APPLICATION_JSON}) + public Object actionPingDelete(@Context HttpServletRequest request, + @Context UriInfo uriInfo) { + return new RestActionPing().ping(request, uriInfo, ""); + } + + @HEAD + @Path(ACTION_PING) + @Produces({APPLICATION_JSON}) + public Object actionPingHead(@Context HttpServletRequest request, @Context UriInfo uriInfo) { - return new RestActionPing().ping(request, uriInfo); + return new RestActionPing().ping(request, uriInfo, ""); } + } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionPing.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionPing.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionPing.java index 6113e1e..65126ac 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionPing.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/actions/RestActionPing.java @@ -34,15 +34,17 @@ public class RestActionPing { public RestActionPing() { } - public Object ping(@Context HttpServletRequest request, - @Context UriInfo uriInfo) { - log.info("Ping {}", request.getMethod()); + public Object ping(HttpServletRequest request, UriInfo uriInfo, String body) { + String verb = request.getMethod(); + log.info("Ping {}", verb); PingResource pingResource = new PingResource(); pingResource.time = System.currentTimeMillis(); + pingResource.verb = verb; + pingResource.body = body; String text = String.format(Locale.ENGLISH, "Ping verb %s received at %tc", - request.getMethod(), pingResource.time); + verb, pingResource.time); pingResource.text = text; return pingResource; } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/resources/PingResource.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/resources/PingResource.java b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/resources/PingResource.java index 7e5396c..3f67c55 100644 --- a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/resources/PingResource.java +++ b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/application/resources/PingResource.java @@ -26,13 +26,18 @@ import org.codehaus.jackson.map.annotate.JsonSerialize; public class PingResource { public long time; public String text; + public String verb; + public String body; @Override public String toString() { + final StringBuilder sb = new StringBuilder("PingResource{"); sb.append("time=").append(time); + sb.append(", verb=").append(verb); sb.append(", text='").append(text).append('\''); + sb.append(", body='").append(body).append('\''); sb.append('}'); return sb.toString(); } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneAgentWeb.groovy ---------------------------------------------------------------------- diff --git a/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneAgentWeb.groovy b/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneAgentWeb.groovy index 1018a02..d5be646 100644 --- a/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneAgentWeb.groovy +++ b/slider-core/src/test/groovy/org/apache/slider/agent/standalone/TestStandaloneAgentWeb.groovy @@ -29,9 +29,13 @@ import org.apache.slider.api.types.SerializedContainerInformation import org.apache.slider.common.params.Arguments import org.apache.slider.core.conf.AggregateConf import org.apache.slider.core.conf.ConfTree +import org.apache.slider.core.restclient.HttpOperationResponse +import org.apache.slider.core.restclient.HttpVerb import org.apache.slider.server.appmaster.web.rest.application.ApplicationResource import org.apache.slider.server.appmaster.web.rest.application.resources.PingResource +import javax.ws.rs.core.MediaType + import static org.apache.slider.api.ResourceKeys.* import static org.apache.slider.api.StatusKeys.* import org.apache.slider.client.SliderClient @@ -51,6 +55,7 @@ class TestStandaloneAgentWeb extends AgentMiniClusterTestBase { public static final int WEB_STARTUP_TIME = 30000 public static final String TEST_GLOBAL_OPTION = "test.global.option" public static final String TEST_GLOBAL_OPTION_PRESENT = "present" + public static final byte[] NO_BYTES = new byte[0] @Test public void testStandaloneAgentWeb() throws Throwable { @@ -187,9 +192,11 @@ class TestStandaloneAgentWeb extends AgentMiniClusterTestBase { assert amFullInfo.containers[0] == amContainerId testRESTModel(appmaster) - testPing(appmaster) - + // PUT & POST &c must go direct for now + String wsroot = appendToURL(realappmaster, SLIDER_CONTEXT_ROOT) + testPing(realappmaster) + } public void testRESTModel(String appmaster) { @@ -200,14 +207,14 @@ class TestStandaloneAgentWeb extends AgentMiniClusterTestBase { ApplicationResource.MODEL_ENTRIES) def unresolvedConf = fetchType(AggregateConf, appmaster, MODEL_DESIRED) - log.info "Unresolved \n$unresolvedConf" +// log.info "Unresolved \n$unresolvedConf" def unresolvedAppConf = unresolvedConf.appConfOperations def sam = "slider-appmaster" assert unresolvedAppConf.getComponentOpt(sam, TEST_GLOBAL_OPTION, "") == "" def resolvedConf = fetchType(AggregateConf, appmaster, MODEL_RESOLVED) - log.info "Resolved \n$resolvedConf" +// log.info "Resolved \n$resolvedConf" assert resolvedConf.appConfOperations.getComponentOpt( sam, TEST_GLOBAL_OPTION, "") == TEST_GLOBAL_OPTION_PRESENT @@ -239,11 +246,41 @@ class TestStandaloneAgentWeb extends AgentMiniClusterTestBase { } public void testPing(String appmaster) { - describe "ping" - def pinged = fetchType(PingResource, appmaster, ACTION_PING) - log.info "Ping: $pinged" + // GET + String ping = appendToURL(appmaster, SLIDER_PATH_APPLICATION, ACTION_PING) + describe "ping to AM URL $appmaster, ping URL $ping" + def pinged = fetchType(PingResource, appmaster, ACTION_PING +"?body=hello") + log.info "Ping GET: $pinged" + // POST + URL pingUrl = new URL(ping) + + def message = "hello" + pingAction(HttpVerb.POST, pingUrl, message) + pingAction(HttpVerb.PUT, pingUrl, message) + pingAction(HttpVerb.DELETE, pingUrl, message) + pingAction(HttpVerb.HEAD, pingUrl, message) + + } + + public HttpOperationResponse pingAction(HttpVerb verb, URL pingUrl, String payload) { + def pinged + def outcome = connectionFactory.execHttpOperation( + verb, + pingUrl, + payload.bytes, + MediaType.TEXT_PLAIN) + byte[] bytes = outcome.data + if (verb.hasResponseBody()) { + assert bytes.length > 0, "0 bytes from ping $verb.verb" + pinged = deser(PingResource, bytes) + log.info "Ping $verb.verb: $pinged" + assert verb.verb == pinged.verb + } else { + assert bytes.length == 0, "${bytes.length} bytes of data from ping $verb.verb" + } + return outcome } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/95d4acb6/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy ---------------------------------------------------------------------- diff --git a/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy b/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy index b496d45..87e3206 100644 --- a/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy +++ b/slider-core/src/test/groovy/org/apache/slider/test/SliderTestUtils.groovy @@ -29,7 +29,6 @@ import org.apache.hadoop.conf.Configuration import org.apache.hadoop.fs.FileStatus import org.apache.hadoop.fs.FileSystem as HadoopFS import org.apache.hadoop.fs.Path -import org.apache.hadoop.hdfs.web.URLConnectionFactory import org.apache.hadoop.net.NetUtils import org.apache.hadoop.service.ServiceStateException import org.apache.hadoop.util.Shell @@ -596,8 +595,8 @@ class SliderTestUtils extends Assert { log.info("Fetching HTTP content at " + path); URL url = new URL(path) - def bytes = connectionFactory.execGet(url, false) - String body = new String(bytes) + def outcome = connectionFactory.execGet(url) + String body = new String(outcome.data) return body; } @@ -1144,16 +1143,34 @@ class SliderTestUtils extends Assert { } } + /** + * Get a web page and deserialize the supplied JSON into + * an instance of the specific class. + * @param clazz class to deserialize to + * @param appmaster URL to base AM + * @param subpath subpath under AM + * @return the parsed data type + */ public <T> T fetchType( Class<T> clazz, String appmaster, String subpath) { - JsonSerDeser serDeser = new JsonSerDeser(clazz) def json = getWebPage( appmaster, RestPaths.SLIDER_PATH_APPLICATION + subpath) + return (T) deser(clazz, json); + } + + public <T> T deser(Class<T> clazz, String json) { + JsonSerDeser serDeser = new JsonSerDeser(clazz) T ctree = (T) serDeser.fromJson(json) return ctree } + + public <T> T deser(Class<T> clazz, byte[] data) { + JsonSerDeser serDeser = new JsonSerDeser(clazz) + T ctree = (T) serDeser.fromBytes(data) + return ctree + } public ConfTreeOperations fetchConfigTree( YarnConfiguration conf, String appmaster, String subpath) {
