You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/08/15 20:14:04 UTC

[juneau] branch master updated: Improvements to ResponseBeanMeta.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 002a9b5  Improvements to ResponseBeanMeta.
002a9b5 is described below

commit 002a9b57fb853b3fddd3844fc55872aff7e6f99c
Author: JamesBognar <ja...@apache.org>
AuthorDate: Wed Aug 15 16:13:49 2018 -0400

    Improvements to ResponseBeanMeta.
---
 .../org/apache/juneau/utils/ClassUtilsTest.java    |  21 ++
 .../apache/juneau/http/annotation/Response.java    |  16 ++
 .../org/apache/juneau/httppart/HttpPartSchema.java |  12 +
 .../org/apache/juneau/httppart/HttpPartType.java   |  11 +-
 .../juneau/httppart/bean/RequestBeanMeta.java      | 103 ++++-----
 .../httppart/bean/RequestBeanPropertyMeta.java     |  40 ++--
 .../juneau/httppart/bean/ResponseBeanMeta.java     | 117 +++++-----
 .../httppart/bean/ResponseBeanPropertyMeta.java    |  51 ++--
 .../org/apache/juneau/httppart/bean/Utils.java     |  74 ++++++
 .../org/apache/juneau/internal/ClassUtils.java     |  33 ++-
 .../juneau/remoteable/RemoteMethodBeanArg.java     |   9 +-
 .../juneau/remoteable/RemoteMethodReturn.java      |   4 +-
 .../rest/test/client/RequestBeanProxyTest.java     |  46 ++--
 .../rest/test/client/ThirdPartyProxyTest.java      | 257 ---------------------
 .../org/apache/juneau/rest/client/RestCall.java    | 101 +++++++-
 .../org/apache/juneau/rest/client/RestClient.java  |  44 ++--
 .../java/org/apache/juneau/rest/RequestBody.java   |   3 +-
 .../org/apache/juneau/rest/RestJavaMethod.java     |   5 +-
 .../java/org/apache/juneau/rest/RestRequest.java   |  10 +-
 .../juneau/rest/reshandlers/DefaultHandler.java    |  16 +-
 20 files changed, 474 insertions(+), 499 deletions(-)

diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
index 39bd0cf..840a0b1 100755
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
@@ -448,4 +448,25 @@ public class ClassUtilsTest {
 		assertEquals("['0','1','2','3','4']", l.toString());
 	}
 
+
+	//====================================================================================================
+	// findMatchingMethods()
+	//====================================================================================================
+
+	public static interface I1 {
+		public int foo(int x);
+	}
+	public static class I2 {
+		public int foo(int x) { return 0; }
+	}
+	public static class I3 extends I2 implements I1 {
+		@Override
+		public int foo(int x) {return 0;}
+	}
+
+	@Test
+	public void findMatchingMethods() throws Exception {
+		assertObjectEquals("['public int org.apache.juneau.utils.ClassUtilsTest$I3.foo(int)','public int org.apache.juneau.utils.ClassUtilsTest$I2.foo(int)','public abstract int org.apache.juneau.utils.ClassUtilsTest$I1.foo(int)']", ClassUtils.findMatchingMethods(I3.class.getMethod("foo", int.class)));
+	}
+
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java
index 6cf9c9d..b20c158 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/http/annotation/Response.java
@@ -54,6 +54,22 @@ import org.apache.juneau.jsonschema.*;
 public @interface Response {
 
 	/**
+	 * Specifies the {@link HttpPartParser} class used for parsing strings to values.
+	 *
+	 * <p>
+	 * Overrides for this part the part parser defined on the REST resource which by default is {@link OpenApiPartParser}.
+	 */
+	Class<? extends HttpPartParser> partParser() default HttpPartParser.Null.class;
+
+	/**
+	 * Specifies whether a part parser should be used for parsing this value.
+	 *
+	 * <p>
+	 * If <jk>false</jk>, then it indicates that normal Juneau parsers (e.g. {@link JsonParser}) should be used for this part.
+	 */
+	public boolean usePartParser() default false;
+
+	/**
 	 * Specifies the {@link HttpPartSerializer} class used for serializing values to strings.
 	 *
 	 * <p>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
index 87cdd19..29f9016 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartSchema.java
@@ -211,6 +211,18 @@ public class HttpPartSchema {
 		return create().apply(a).build();
 	}
 
+	/**
+	 * Finds the schema information on the specified annotation.
+	 *
+	 * @param a
+	 * 	The annotation to find the schema information on..
+	 * @param defaultName The default part name if not specified on the annotation.
+	 * @return The schema information found on the annotation.
+	 */
+	public static HttpPartSchema create(Annotation a, String defaultName) {
+		return create().name(defaultName).apply(a).build();
+	}
+
 	HttpPartSchema(HttpPartSchemaBuilder b) {
 		this.name = b.name;
 		this.codes = copy(b.codes);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java
index a79fbda..3f60e90 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/HttpPartType.java
@@ -29,9 +29,18 @@ public enum HttpPartType {
 	/** A form-data parameter */
 	FORMDATA,
 
-	/** An HTTP header */
+	/** An HTTP request header */
 	HEADER,
 
+	/** An HTTP response header */
+	RESPONSE_HEADER,
+
+	/** An HTTP response body */
+	RESPONSE_BODY,
+
+	/** An HTTP response status code */
+	RESPONSE_STATUS,
+
 	/** A non-standard field */
 	OTHER;
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java
index efa22df..7d3ef9d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanMeta.java
@@ -13,8 +13,9 @@
 package org.apache.juneau.httppart.bean;
 
 import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.httppart.bean.Utils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
 
-import java.lang.annotation.*;
 import java.lang.reflect.*;
 import java.util.*;
 
@@ -64,7 +65,7 @@ public class RequestBeanMeta {
 	//-----------------------------------------------------------------------------------------------------------------
 
 	private final ClassMeta<?> cm;
-	private final Map<String,RequestBeanPropertyMeta> properties, propertiesByGetter;
+	private final Map<String,RequestBeanPropertyMeta> properties;
 	private final HttpPartSerializer serializer;
 	private final HttpPartParser parser;
 
@@ -72,15 +73,10 @@ public class RequestBeanMeta {
 		this.cm = b.cm;
 		this.serializer = ClassUtils.newInstance(HttpPartSerializer.class, b.serializer, true, b.ps);
 		this.parser = ClassUtils.newInstance(HttpPartParser.class, b.parser, true, b.ps);
-		Map<String,RequestBeanPropertyMeta> properties = new LinkedHashMap<>(), propertiesByGetter = new LinkedHashMap<>();
-		for (Map.Entry<String,RequestBeanPropertyMeta.Builder> e : b.properties.entrySet()) {
-			RequestBeanPropertyMeta pm = e.getValue().build(serializer, parser);
-			properties.put(e.getKey(), pm);
-			propertiesByGetter.put(pm.getGetter(), pm);
-
-		}
+		Map<String,RequestBeanPropertyMeta> properties = new LinkedHashMap<>();
+		for (Map.Entry<String,RequestBeanPropertyMeta.Builder> e : b.properties.entrySet())
+			properties.put(e.getKey(), e.getValue().build(serializer, parser));
 		this.properties = Collections.unmodifiableMap(properties);
-		this.propertiesByGetter = Collections.unmodifiableMap(propertiesByGetter);
 	}
 
 	static class Builder {
@@ -101,34 +97,31 @@ public class RequestBeanMeta {
 		Builder apply(Class<?> c) {
 			apply(getAnnotation(Request.class, c));
 			this.cm = BeanContext.DEFAULT.getClassMeta(c);
-			BeanMeta<?> bm = BeanContext.DEFAULT.getBeanMeta(c);
-			for (BeanPropertyMeta bp : bm.getPropertyMetas()) {
-				String n = bp.getName();
-				Annotation a = bp.getAnnotation(Path.class);
-				String g = bp.getGetter().getName();
-				if (a != null) {
-					HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
-					getProperty(n, HttpPartType.PATH).apply(s).getter(g);
-				}
-				a = bp.getAnnotation(Header.class);
-				if (a != null) {
-					HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
-					getProperty(n, HttpPartType.HEADER).apply(s).getter(g);
-				}
-				a = bp.getAnnotation(Query.class);
-				if (a != null) {
-					HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
-					getProperty(n, HttpPartType.QUERY).apply(s).getter(g);
-				}
-				a = bp.getAnnotation(FormData.class);
-				if (a != null) {
-					HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
-					getProperty(n, HttpPartType.FORMDATA).apply(s).getter(g);
-				}
-				a = bp.getAnnotation(Body.class);
-				if (a != null) {
-					HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
-					getProperty(n, HttpPartType.BODY).apply(s).getter(g);
+			for (Method m : ClassUtils.getAllMethods(c, false)) {
+				if (isPublic(m)) {
+					assertNoAnnotations(m, Request.class, ResponseHeader.class, ResponseBody.class, ResponseStatus.class);
+					String n = m.getName();
+					if (hasAnnotation(Body.class, m)) {
+						assertNoArgs(m, Body.class);
+						assertReturnNotVoid(m, Body.class);
+						properties.put(n, RequestBeanPropertyMeta.create(BODY, Body.class, m));
+					} else if (hasAnnotation(Header.class, m)) {
+						assertNoArgs(m, Header.class);
+						assertReturnNotVoid(m, Header.class);
+						properties.put(n, RequestBeanPropertyMeta.create(HEADER, Header.class, m));
+					} else if (hasAnnotation(Query.class, m)) {
+						assertNoArgs(m, Query.class);
+						assertReturnNotVoid(m, Query.class);
+						properties.put(n, RequestBeanPropertyMeta.create(QUERY, Query.class, m));
+					} else if (hasAnnotation(FormData.class, m)) {
+						assertNoArgs(m, FormData.class);
+						assertReturnNotVoid(m, FormData.class);
+						properties.put(n, RequestBeanPropertyMeta.create(FORMDATA, FormData.class, m));
+					} else if (hasAnnotation(Path.class, m)) {
+						assertNoArgs(m, Path.class);
+						assertReturnNotVoid(m, Path.class);
+						properties.put(n, RequestBeanPropertyMeta.create(PATH, Path.class, m));
+					}
 				}
 			}
 			return this;
@@ -147,43 +140,33 @@ public class RequestBeanMeta {
 		RequestBeanMeta build() {
 			return new RequestBeanMeta(this);
 		}
-
-		private RequestBeanPropertyMeta.Builder getProperty(String name, HttpPartType partType) {
-			RequestBeanPropertyMeta.Builder b = properties.get(name);
-			if (b == null) {
-				b = RequestBeanPropertyMeta.create().name(name).partType(partType);
-				properties.put(name, b);
-			}
-			return b;
-		}
 	}
 
 	/**
-	 * Returns metadata about the bean property with the specified property name.
+	 * Returns metadata about the class.
 	 *
-	 * @param name The bean property name.
-	 * @return Metadata about the bean property, or <jk>null</jk> if none found.
+	 * @return Metadata about the class.
 	 */
-	public RequestBeanPropertyMeta getProperty(String name) {
-		return properties.get(name);
+	public ClassMeta<?> getClassMeta() {
+		return cm;
 	}
 
 	/**
-	 * Returns metadata about the bean property with the specified method getter name.
+	 * Returns metadata about the bean property with the specified property name.
 	 *
-	 * @param name The bean method getter name.
+	 * @param name The bean property name.
 	 * @return Metadata about the bean property, or <jk>null</jk> if none found.
 	 */
-	public RequestBeanPropertyMeta getPropertyByGetter(String name) {
-		return propertiesByGetter.get(name);
+	public RequestBeanPropertyMeta getProperty(String name) {
+		return properties.get(name);
 	}
 
 	/**
-	 * Returns metadata about the class.
+	 * Returns all the annotated methods on this bean.
 	 *
-	 * @return Metadata about the class.
+	 * @return All the annotated methods on this bean.
 	 */
-	public ClassMeta<?> getClassMeta() {
-		return cm;
+	public Collection<RequestBeanPropertyMeta> getProperties() {
+		return properties.values();
 	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java
index 62953ce..ca0d2c5 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/RequestBeanPropertyMeta.java
@@ -12,6 +12,12 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.httppart.bean;
 
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.httppart.bean.Utils.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+
 import org.apache.juneau.*;
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.httppart.*;
@@ -22,15 +28,18 @@ import org.apache.juneau.internal.*;
  */
 public class RequestBeanPropertyMeta {
 
-	static RequestBeanPropertyMeta.Builder create() {
-		return new Builder();
+	static RequestBeanPropertyMeta.Builder create(HttpPartType partType, Class<? extends Annotation> c, Method m) {
+		HttpPartSchemaBuilder sb = HttpPartSchema.create().name(getPropertyName(m));
+		for (Annotation a : getAnnotationsParentFirst(c, m))
+			sb.apply(a);
+		return new Builder().partType(partType).schema(sb.build()).getter(m);
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
 	// Instance
 	//-----------------------------------------------------------------------------------------------------------------
 
-	private final String partName, getter;
+	private final Method getter;
 	private final HttpPartType partType;
 	private final HttpPartSerializer serializer;
 	private final HttpPartParser parser;
@@ -38,8 +47,7 @@ public class RequestBeanPropertyMeta {
 
 	RequestBeanPropertyMeta(Builder b, HttpPartSerializer serializer, HttpPartParser parser) {
 		this.partType = b.partType;
-		this.schema = b.schema.build();
-		this.partName = StringUtils.firstNonEmpty(schema.getName(), b.name);
+		this.schema = b.schema;
 		this.getter = b.getter;
 		this.serializer = schema.getSerializer() == null ? serializer : ClassUtils.newInstance(HttpPartSerializer.class, schema.getSerializer(), true, b.ps);
 		this.parser = schema.getParser() == null ? parser : ClassUtils.newInstance(HttpPartParser.class, schema.getParser(), true, b.ps);
@@ -47,16 +55,11 @@ public class RequestBeanPropertyMeta {
 
 	static class Builder {
 		HttpPartType partType;
-		HttpPartSchemaBuilder schema;
-		String name, getter;
+		HttpPartSchema schema;
+		Method getter;
 		PropertyStore ps = PropertyStore.DEFAULT;
 
-		Builder name(String value) {
-			name = value;
-			return this;
-		}
-
-		Builder getter(String value) {
+		Builder getter(Method value) {
 			getter = value;
 			return this;
 		}
@@ -66,16 +69,11 @@ public class RequestBeanPropertyMeta {
 			return this;
 		}
 
-		Builder schema(HttpPartSchemaBuilder value) {
+		Builder schema(HttpPartSchema value) {
 			schema = value;
 			return this;
 		}
 
-		Builder apply(HttpPartSchemaBuilder s) {
-			schema = s;
-			return this;
-		}
-
 		RequestBeanPropertyMeta build(HttpPartSerializer serializer, HttpPartParser parser) {
 			return new RequestBeanPropertyMeta(this, serializer, parser);
 		}
@@ -87,7 +85,7 @@ public class RequestBeanPropertyMeta {
 	 * @return The HTTP part name, or <jk>null</jk> if it doesn't have a part name.
 	 */
 	public String getPartName() {
-		return partName;
+		return schema == null ? null : schema.getName();
 	}
 
 	/**
@@ -97,7 +95,7 @@ public class RequestBeanPropertyMeta {
 	 * 	The name of the Java method getter that defines this property.
 	 * 	<br>Never <jk>null</jk>.
 	 */
-	public String getGetter() {
+	public Method getGetter() {
 		return getter;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
index 70cb830..59a0184 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanMeta.java
@@ -12,15 +12,15 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.httppart.bean;
 
-import static org.apache.juneau.internal.ClassFlags.*;
 import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.httppart.bean.Utils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
 
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.internal.*;
@@ -109,42 +109,56 @@ public class ResponseBeanMeta {
 	//-----------------------------------------------------------------------------------------------------------------
 
 	private final ClassMeta<?> cm;
+	private final Map<String,ResponseBeanPropertyMeta> properties;
 	private final int code;
 	private final Map<String,ResponseBeanPropertyMeta> headerMethods;
-	private final Method statusMethod, bodyMethod;
+	private final ResponseBeanPropertyMeta statusMethod, bodyMethod;
 	private final HttpPartSerializer partSerializer;
+	private final HttpPartParser partParser;
 	private final HttpPartSchema schema;
 	private final boolean usePartSerializer;
 
 	ResponseBeanMeta(Builder b) {
 		this.cm = b.cm;
 		this.code = b.code;
-		this.partSerializer = ClassUtils.newInstance(HttpPartSerializer.class, b.partSerializer, true, b.ps);
+		this.partSerializer = newInstance(HttpPartSerializer.class, b.partSerializer, true, b.ps);
+		this.partParser = newInstance(HttpPartParser.class, b.partParser, true, b.ps);
+		this.usePartSerializer = b.usePartSerializer;
 		this.schema = b.schema.build();
-		this.usePartSerializer = b.usePartSerializer || partSerializer != null;
+
+		Map<String,ResponseBeanPropertyMeta> properties = new LinkedHashMap<>();
 
 		Map<String,ResponseBeanPropertyMeta> hm = new LinkedHashMap<>();
 		for (Map.Entry<String,ResponseBeanPropertyMeta.Builder> e : b.headerMethods.entrySet()) {
-			ResponseBeanPropertyMeta pm = e.getValue().build(partSerializer);
+			ResponseBeanPropertyMeta pm = e.getValue().build(partSerializer, partParser);
 			hm.put(e.getKey(), pm);
-
+			properties.put(pm.getGetter().getName(), pm);
 		}
 		this.headerMethods = Collections.unmodifiableMap(hm);
-		this.bodyMethod = b.bodyMethod;
-		this.statusMethod = b.statusMethod;
+
+		this.bodyMethod = b.bodyMethod == null ? null : b.bodyMethod.schema(schema).build(partSerializer, partParser);
+		this.statusMethod = b.statusMethod == null ? null : b.statusMethod.build(null, null);
+
+		if (bodyMethod != null)
+			properties.put(bodyMethod.getGetter().getName(), bodyMethod);
+		if (statusMethod != null)
+			properties.put(statusMethod.getGetter().getName(), statusMethod);
+
+		this.properties = Collections.unmodifiableMap(properties);
 	}
 
 	static class Builder {
 		ClassMeta<?> cm;
 		int code;
 		PropertyStore ps;
-		boolean usePartSerializer;
 		Class<? extends HttpPartSerializer> partSerializer;
+		Class<? extends HttpPartParser> partParser;
 		HttpPartSchemaBuilder schema = HttpPartSchema.create();
+		boolean usePartSerializer;
 
 		Map<String,ResponseBeanPropertyMeta.Builder> headerMethods = new LinkedHashMap<>();
-		Method bodyMethod;
-		Method statusMethod;
+		ResponseBeanPropertyMeta.Builder bodyMethod;
+		ResponseBeanPropertyMeta.Builder statusMethod;
 
 		Builder(PropertyStore ps) {
 			this.ps = ps;
@@ -154,47 +168,25 @@ public class ResponseBeanMeta {
 			Class<?> c = ClassUtils.toClass(t);
 			this.cm = BeanContext.DEFAULT.getClassMeta(c);
 			for (Method m : ClassUtils.getAllMethods(c, false)) {
-				if (isAll(m, PUBLIC)) {
+				if (isPublic(m)) {
+					assertNoAnnotations(m, Response.class, Body.class, Header.class, Query.class, FormData.class, Path.class);
 					if (hasAnnotation(ResponseHeader.class, m)) {
-						if (m.getParameterTypes().length != 0)
-							throw new InvalidAnnotationException("@ResponseHeader annotation on method cannot have arguments.  Method=''{0}''", m);
-						Class<?> rt = m.getReturnType();
-						if (rt == void.class)
-							throw new InvalidAnnotationException("Invalid return type for @ResponseHeader annotation on method.  Method=''{0}''", m);
-						ResponseHeader a = getAnnotation(ResponseHeader.class, m);
-						String n = a.name();
-						if (n.isEmpty())
-							n = m.getName();
-						HttpPartSchemaBuilder s = HttpPartSchema.create().apply(a);
-						headerMethods.put(n, ResponseBeanPropertyMeta.create().name(n).partType(HttpPartType.HEADER).apply(s).getter(m));
-					}
-					if (hasAnnotation(ResponseStatus.class, m)) {
-						if (m.getParameterTypes().length != 0)
-							throw new InvalidAnnotationException("@ResponseStatus annotation on method cannot have arguments.  Method=''{0}''", m);
-						Class<?> rt = m.getReturnType();
-						if (rt != int.class || rt != Integer.class)
-							throw new InvalidAnnotationException("Invalid return type for @ResponseStatus annotation on method.  Method=''{0}''", m);
-						statusMethod = m;
-					}
-					if (hasAnnotation(ResponseBody.class, m)) {
+						assertNoArgs(m, ResponseHeader.class);
+						assertReturnNotVoid(m, ResponseHeader.class);
+						HttpPartSchema s = HttpPartSchema.create(getAnnotation(ResponseHeader.class, m), getPropertyName(m));
+						headerMethods.put(s.getName(), ResponseBeanPropertyMeta.create(RESPONSE_HEADER, s, m));
+					} else if (hasAnnotation(ResponseStatus.class, m)) {
+						assertNoArgs(m, ResponseHeader.class);
+						assertReturnType(m, ResponseHeader.class, int.class, Integer.class);
+						statusMethod = ResponseBeanPropertyMeta.create(RESPONSE_STATUS, m);
+					} else if (hasAnnotation(ResponseBody.class, m)) {
 						Class<?>[] pt = m.getParameterTypes();
-						if (pt.length == 0) {
-							Class<?> rt = m.getReturnType();
-							if (rt == void.class)
-								throw new InvalidAnnotationException("Invalid return type for @ResponseBody annotation on method.  Method=''{0}''", m);
-						} else if (pt.length == 1) {
-							Class<?> rt = pt[0];
-							if (rt != OutputStream.class && rt != Writer.class)
-								throw new InvalidAnnotationException("Invalid return type for @ResponseBody annotation on method.  Method=''{0}''", m);
-						} else {
-							throw new InvalidAnnotationException("@ResponseBody annotation on method cannot have arguments.  Method=''{0}''", m);
-						}
-						bodyMethod = m;
+						if (pt.length == 0)
+							assertReturnNotVoid(m, ResponseHeader.class);
+						else
+							assertArgType(m, ResponseHeader.class, OutputStream.class, Writer.class);
+						bodyMethod = ResponseBeanPropertyMeta.create(RESPONSE_BODY, m);
 					}
-					if (hasAnnotation(Body.class, m))
-						throw new InvalidAnnotationException("@Body annotation cannot be used in a @Response bean.  Use @ResponseBody instead.  Method=''{0}''", m);
-					if (hasAnnotation(Header.class, m))
-						throw new InvalidAnnotationException("@Header annotation cannot be used in a @Response bean.  Use @ResponseHeader instead.  Method=''{0}''", m);
 				}
 			}
 			return this;
@@ -204,6 +196,8 @@ public class ResponseBeanMeta {
 			if (a != null) {
 				if (a.partSerializer() != HttpPartSerializer.Null.class)
 					partSerializer = a.partSerializer();
+				if (a.partParser() != HttpPartParser.Null.class)
+					partParser = a.partParser();
 				if (a.value().length > 0)
 					code = a.value()[0];
 				if (a.code().length > 0)
@@ -251,7 +245,7 @@ public class ResponseBeanMeta {
 	 *
 	 * @return The <ja>@ResponseBody</ja>-annotated method, or <jk>null</jk> if it doesn't exist.
 	 */
-	public Method getBodyMethod() {
+	public ResponseBeanPropertyMeta getBodyMethod() {
 		return bodyMethod;
 	}
 
@@ -260,7 +254,7 @@ public class ResponseBeanMeta {
 	 *
 	 * @return The <ja>@ResponseStatus</ja>-annotated method, or <jk>null</jk> if it doesn't exist.
 	 */
-	public Method getStatusMethod() {
+	public ResponseBeanPropertyMeta getStatusMethod() {
 		return statusMethod;
 	}
 
@@ -290,4 +284,23 @@ public class ResponseBeanMeta {
 	public ClassMeta<?> getClassMeta() {
 		return cm;
 	}
+
+	/**
+	 * Returns metadata about the bean property with the specified method getter name.
+	 *
+	 * @param name The bean method getter name.
+	 * @return Metadata about the bean property, or <jk>null</jk> if none found.
+	 */
+	public ResponseBeanPropertyMeta getProperty(String name) {
+		return properties.get(name);
+	}
+
+	/**
+	 * Returns all the annotated methods on this bean.
+	 *
+	 * @return All the annotated methods on this bean.
+	 */
+	public Collection<ResponseBeanPropertyMeta> getProperties() {
+		return properties.values();
+	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java
index d4b6553..6789045 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/ResponseBeanPropertyMeta.java
@@ -12,43 +12,48 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.httppart.bean;
 
+import static org.apache.juneau.internal.ClassUtils.*;
+
 import java.lang.reflect.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.httppart.*;
-import org.apache.juneau.internal.*;
 
 /**
  * Represents the metadata gathered from a getter method of a class annotated with {@link Response}.
  */
 public class ResponseBeanPropertyMeta {
 
-	static ResponseBeanPropertyMeta.Builder create() {
-		return new Builder();
+	static ResponseBeanPropertyMeta.Builder create(HttpPartType partType, HttpPartSchema schema, Method m) {
+		return new Builder().partType(partType).schema(schema).getter(m);
+	}
+
+	static ResponseBeanPropertyMeta.Builder create(HttpPartType partType, Method m) {
+		return new Builder().partType(partType).getter(m);
 	}
 
 	//-----------------------------------------------------------------------------------------------------------------
 	// Instance
 	//-----------------------------------------------------------------------------------------------------------------
 
-	private final String partName;
 	private final Method getter;
 	private final HttpPartType partType;
 	private final HttpPartSerializer serializer;
+	private final HttpPartParser parser;
 	private final HttpPartSchema schema;
 
-	ResponseBeanPropertyMeta(Builder b, HttpPartSerializer serializer) {
+	ResponseBeanPropertyMeta(Builder b, HttpPartSerializer serializer, HttpPartParser parser) {
 		this.partType = b.partType;
-		this.schema = b.schema.build();
-		this.partName = StringUtils.firstNonEmpty(schema.getName(), b.name);
+		this.schema = b.schema;
 		this.getter = b.getter;
-		this.serializer = schema.getSerializer() == null ? serializer : ClassUtils.newInstance(HttpPartSerializer.class, schema.getSerializer(), true, b.ps);
+		this.serializer = schema.getSerializer() == null ? serializer : newInstance(HttpPartSerializer.class, schema.getSerializer(), true, b.ps);
+		this.parser = schema.getParser() == null ? parser : newInstance(HttpPartParser.class, schema.getParser(), true, b.ps);
 	}
 
 	static class Builder {
 		HttpPartType partType;
-		HttpPartSchemaBuilder schema;
+		HttpPartSchema schema;
 		String name;
 		Method getter;
 		PropertyStore ps = PropertyStore.DEFAULT;
@@ -68,18 +73,13 @@ public class ResponseBeanPropertyMeta {
 			return this;
 		}
 
-		Builder schema(HttpPartSchemaBuilder value) {
+		Builder schema(HttpPartSchema value) {
 			schema = value;
 			return this;
 		}
 
-		Builder apply(HttpPartSchemaBuilder s) {
-			schema = s;
-			return this;
-		}
-
-		ResponseBeanPropertyMeta build(HttpPartSerializer serializer) {
-			return new ResponseBeanPropertyMeta(this, serializer);
+		ResponseBeanPropertyMeta build(HttpPartSerializer serializer, HttpPartParser parser) {
+			return new ResponseBeanPropertyMeta(this, serializer, parser);
 		}
 	}
 
@@ -89,7 +89,7 @@ public class ResponseBeanPropertyMeta {
 	 * @return The HTTP part name, or <jk>null</jk> if it doesn't have a part name.
 	 */
 	public String getPartName() {
-		return partName;
+		return schema == null ? null : schema.getName();
 	}
 
 	/**
@@ -117,10 +117,21 @@ public class ResponseBeanPropertyMeta {
 	/**
 	 * Returns the serializer to use for serializing the bean property value.
 	 *
+	 * @param _default The default serializer to use if not defined on the annotation.
 	 * @return The serializer to use for serializing the bean property value.
 	 */
-	public HttpPartSerializer getSerializer() {
-		return serializer;
+	public HttpPartSerializer getSerializer(HttpPartSerializer _default) {
+		return serializer == null ? _default : serializer;
+	}
+
+	/**
+	 * Returns the parser to use for parsing the bean property value.
+	 *
+	 * @param _default The default parser to use if not defined on the annotation.
+	 * @return The parser to use for parsing the bean property value.
+	 */
+	public HttpPartParser getParser(HttpPartParser _default) {
+		return parser == null ? _default : parser;
 	}
 
 	/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java
new file mode 100644
index 0000000..f227465
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/httppart/bean/Utils.java
@@ -0,0 +1,74 @@
+// ***************************************************************************************************************************
+// * 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.juneau.httppart.bean;
+
+import static org.apache.juneau.internal.ClassUtils.*;
+
+import java.beans.*;
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+
+import org.apache.juneau.annotation.*;
+
+/**
+ * Utility methods.
+ */
+class Utils {
+
+	@SafeVarargs
+	static final void assertNoAnnotations(Method m, Class<? extends Annotation> a, Class<? extends Annotation>...c) throws InvalidAnnotationException {
+		for (Class<? extends Annotation> cc : c) {
+			if (hasAnnotation(cc, m))
+				throw new InvalidAnnotationException("@{0} annotation cannot be used in a @{1} bean.  Method=''{2}''", cc.getSimpleName(), a.getSimpleName(), m);
+		}
+	}
+
+	static void assertNoArgs(Method m, Class<?> a) throws InvalidAnnotationException {
+		if (m.getParameterTypes().length != 0)
+			throw new InvalidAnnotationException("Method with @{0} annotation cannot have arguments.  Method=''{1}''", a.getSimpleName(), m);
+	}
+
+	static void assertReturnNotVoid(Method m, Class<?> a) throws InvalidAnnotationException {
+		Class<?> rt = m.getReturnType();
+		if (rt == void.class)
+			throw new InvalidAnnotationException("Invalid return type for method with annotation @{0}.  Method=''{1}''", a.getSimpleName(), m);
+	}
+
+	static void assertReturnType(Method m, Class<? extends Annotation> a, Class<?>...c) throws InvalidAnnotationException {
+		Class<?> rt = m.getReturnType();
+		for (Class<?> cc : c)
+			if (rt == cc)
+				return;
+		throw new InvalidAnnotationException("Invalid return type for method with annotation @{0}.  Method=''{1}''", a.getSimpleName(), m);
+	}
+
+	static void assertArgType(Method m, Class<? extends Annotation> a, Class<?>...c) throws InvalidAnnotationException {
+		Class<?>[] ptt = m.getParameterTypes();
+		if (ptt.length != 1)
+			throw new InvalidAnnotationException("Only one parameter can be passed to method with @{0} annotation.  Method=''{0}''", a.getSimpleName(), m);
+		Class<?> rt = ptt[0];
+		for (Class<?> cc : c)
+			if (rt == cc)
+				return;
+		throw new InvalidAnnotationException("Invalid return type for method with annotation @{0}.  Method=''{1}''", a.getSimpleName(), m);
+	}
+
+	static String getPropertyName(Method m) {
+		String n = m.getName();
+		if (n.startsWith("get") && n.length() > 3)
+			return Introspector.decapitalize(n.substring(3));
+		if (n.startsWith("is") && n.length() > 2)
+			return Introspector.decapitalize(n.substring(2));
+		return n;
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 4ee46ca..9b4ed01 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -1180,6 +1180,32 @@ public final class ClassUtils {
 		return null;
 	}
 
+	/**
+	 * Given a specific method, finds all declared methods with the same name and arguments on all
+	 * superclasses and interfaces.
+	 *
+	 * @param m The method to find matches against.
+	 * @return All matching methods including the input method itself.
+	 */
+	public static List<Method> findMatchingMethods(Method m) {
+		return findMatchingMethods(new ArrayList<Method>(), m);
+	}
+
+	private static List<Method> findMatchingMethods(List<Method> l, Method m) {
+		l.add(m);
+		Class<?> c = m.getDeclaringClass();
+		Class<?> pc = c.getSuperclass();
+		if (pc != null)
+			for (Method m2 : pc.getDeclaredMethods())
+				if (isSameMethod(m, m2))
+					findMatchingMethods(l, m2);
+		for (Class<?> ic : c.getInterfaces())
+			for (Method m2 : ic.getDeclaredMethods())
+				if (isSameMethod(m, m2))
+					findMatchingMethods(l, m2);
+		return l;
+	}
+
 	private static boolean isSameMethod(Method m1, Method m2) {
 		return m1.getName().equals(m2.getName()) && Arrays.equals(m1.getParameterTypes(), m2.getParameterTypes());
 	}
@@ -2294,9 +2320,10 @@ public final class ClassUtils {
 	@SuppressWarnings("unchecked")
 	public static <T extends Annotation> List<T> getAnnotations(Class<T> a, Method m) {
 		List<T> l = new ArrayList<>();
-		for (Annotation a2 :  m.getAnnotations())
-			if (a.isInstance(a2))
-				l.add((T)a2);
+		for (Method m2 : findMatchingMethods(m))
+			for (Annotation a2 :  m2.getAnnotations())
+				if (a.isInstance(a2))
+					l.add((T)a2);
 		Type t = m.getGenericReturnType();
 		if (Value.isType(t))
 			appendAnnotations(a, Value.getParameterType(t), l);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java
index fcea234..6f765a8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodBeanArg.java
@@ -57,12 +57,11 @@ public final class RemoteMethodBeanArg {
 	}
 
 	/**
-	 * Returns metadata on the specified property of the request bean.
+	 * Returns metadata on the request bean.
 	 *
-	 * @param name The bean property name.
-	 * @return Metadata about the bean property, or <jk>null</jk> if not found.
+	 * @return Metadata about the bean.
 	 */
-	public RequestBeanPropertyMeta getProperty(String name) {
-		return meta.getProperty(name);
+	public RequestBeanMeta getMeta() {
+		return meta;
 	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodReturn.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodReturn.java
index eb68c1f..6df8345 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodReturn.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/remoteable/RemoteMethodReturn.java
@@ -37,8 +37,8 @@ public final class RemoteMethodReturn {
 
 	RemoteMethodReturn(Method m, ReturnValue returnValue) {
 		this.returnType = m.getGenericReturnType();
-		if (hasAnnotation(Body.class, m)) {
-			this.schema = HttpPartSchema.create(Body.class, m);
+		if (hasAnnotation(Response.class, m)) {
+			this.schema = HttpPartSchema.create(Response.class, m);
 			this.parser = newInstance(HttpPartParser.class, schema.getParser());
 		} else {
 			this.schema = null;
diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java
index e558714..2d5630b 100644
--- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java
+++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/RequestBeanProxyTest.java
@@ -20,7 +20,6 @@ import static org.junit.Assert.*;
 import java.io.*;
 import java.util.*;
 
-import org.apache.juneau.annotation.*;
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.remoteable.*;
@@ -68,7 +67,6 @@ public class RequestBeanProxyTest {
 		@Query String getA();
 		@Query("b") String getX1();
 		@Query(name="c") String getX2();
-		@Query @BeanProperty("d") String getX3();
 		@Query(name="e",allowEmptyValue=true) String getX4();
 		@Query("f") String getX5();
 		@Query("g") String getX6();
@@ -79,7 +77,6 @@ public class RequestBeanProxyTest {
 		@Override public String getA() { return "a1"; }
 		@Override public String getX1() { return "b1"; }
 		@Override public String getX2() { return "c1"; }
-		@Override public String getX3() { return "d1"; }
 		@Override public String getX4() { return ""; }
 		@Override public String getX5() { return null; }
 		@Override public String getX6() { return "true"; }
@@ -91,15 +88,15 @@ public class RequestBeanProxyTest {
 
 	@Test
 	public void a01a_query_simpleVals_plainText() throws Exception {
-		assertEquals("{a:'a1',b:'b1',c:'c1',d:'d1',e:'',g:'true',h:'123'}", a01a.normal(new A01_BeanImpl()));
+		assertEquals("{a:'a1',b:'b1',c:'c1',e:'',g:'true',h:'123'}", a01a.normal(new A01_BeanImpl()));
 	}
 	@Test
 	public void a01b_query_simpleVals_uon() throws Exception {
-		assertEquals("{a:'a1',b:'b1',c:'c1',d:'d1',e:'',g:'\\'true\\'',h:'\\'123\\''}", a01b.normal(new A01_BeanImpl()));
+		assertEquals("{a:'a1',b:'b1',c:'c1',e:'',g:'\\'true\\'',h:'\\'123\\''}", a01b.normal(new A01_BeanImpl()));
 	}
 	@Test
 	public void a01c_query_simpleVals_x() throws Exception {
-		assertEquals("{a:'xa1x',b:'xb1x',c:'xc1x',d:'xd1x',e:'xx',g:'xtruex',h:'x123x'}", a01b.serialized(new A01_BeanImpl()));
+		assertEquals("{a:'xa1x',b:'xb1x',c:'xc1x',e:'xx',g:'xtruex',h:'x123x'}", a01b.serialized(new A01_BeanImpl()));
 	}
 
 	//=================================================================================================================
@@ -368,11 +365,6 @@ public class RequestBeanProxyTest {
 		public String getX2() {
 			return "c1";
 		}
-		@FormData
-		@BeanProperty("d")
-		public String getX3() {
-			return "d1";
-		}
 		@FormData(name="e",allowEmptyValue=true)
 		public String getX4() {
 			return "";
@@ -397,17 +389,17 @@ public class RequestBeanProxyTest {
 	@Test
 	public void c01a_formData_simpleVals_plainText() throws Exception {
 		String r = c01a.normal(new C01_Bean());
-		assertEquals("{a:'a1',b:'b1',c:'c1',d:'d1',e:'',g:'true',h:'123'}", r);
+		assertEquals("{a:'a1',b:'b1',c:'c1',e:'',g:'true',h:'123'}", r);
 	}
 	@Test
 	public void c01b_formData_simpleVals_uon() throws Exception {
 		String r = c01b.normal(new C01_Bean());
-		assertEquals("{a:'a1',b:'b1',c:'c1',d:'d1',e:'',g:'\\'true\\'',h:'\\'123\\''}", r);
+		assertEquals("{a:'a1',b:'b1',c:'c1',e:'',g:'\\'true\\'',h:'\\'123\\''}", r);
 	}
 	@Test
 	public void c01c_formData_simpleVals_x() throws Exception {
 		String r = c01b.serialized(new C01_Bean());
-		assertEquals("{a:'xa1x',b:'xb1x',c:'xc1x',d:'xd1x',e:'xx',g:'xtruex',h:'x123x'}", r);
+		assertEquals("{a:'xa1x',b:'xb1x',c:'xc1x',e:'xx',g:'xtruex',h:'x123x'}", r);
 	}
 
 	//=================================================================================================================
@@ -677,11 +669,6 @@ public class RequestBeanProxyTest {
 		public String getX2() {
 			return "c1";
 		}
-		@Header
-		@BeanProperty("d")
-		public String getX3() {
-			return "d1";
-		}
 		@Header(name="e",allowEmptyValue=true)
 		public String getX4() {
 			return "";
@@ -706,17 +693,17 @@ public class RequestBeanProxyTest {
 	@Test
 	public void e01a_headerSimpleValsPlainText() throws Exception {
 		String r = e01a.normal(new E01_Bean());
-		assertEquals("{a:'a1',b:'b1',c:'c1',d:'d1',e:'',g:'true',h:'123'}", r);
+		assertEquals("{a:'a1',b:'b1',c:'c1',e:'',g:'true',h:'123'}", r);
 	}
 	@Test
 	public void e01b_headerSimpleValsUon() throws Exception {
 		String r = e01b.normal(new E01_Bean());
-		assertEquals("{a:'a1',b:'b1',c:'c1',d:'d1',e:'',g:'\\'true\\'',h:'\\'123\\''}", r);
+		assertEquals("{a:'a1',b:'b1',c:'c1',e:'',g:'\\'true\\'',h:'\\'123\\''}", r);
 	}
 	@Test
 	public void e01c_headerSimpleValsX() throws Exception {
 		String r = e01b.serialized(new E01_Bean());
-		assertEquals("{a:'xa1x',b:'xb1x',c:'xc1x',d:'xd1x',e:'xx',g:'xtruex',h:'x123x'}", r);
+		assertEquals("{a:'xa1x',b:'xb1x',c:'xc1x',e:'xx',g:'xtruex',h:'x123x'}", r);
 	}
 
 	//=================================================================================================================
@@ -915,10 +902,10 @@ public class RequestBeanProxyTest {
 	@Remoteable(path="/")
 	public static interface G01_Remoteable {
 
-		@RemoteMethod(httpMethod="GET", path="/echoPath/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}")
+		@RemoteMethod(httpMethod="GET", path="/echoPath/{a}/{b}/{c}/{e}/{f}/{g}/{h}")
 		String normal(@Request G01_Bean rb);
 
-		@RemoteMethod(httpMethod="GET", path="/echoPath/{a}/{b}/{c}/{d}/{e}/{f}/{g}/{h}")
+		@RemoteMethod(httpMethod="GET", path="/echoPath/{a}/{b}/{c}/{e}/{f}/{g}/{h}")
 		String serialized(@Request(partSerializer=XSerializer.class) G01_Bean rb);
 	}
 
@@ -935,11 +922,6 @@ public class RequestBeanProxyTest {
 		public String getX2() {
 			return "c1";
 		}
-		@Path
-		@BeanProperty("d")
-		public String getX3() {
-			return "d1";
-		}
 		@Path(name="e",allowEmptyValue=true)
 		public String getX4() {
 			return "";
@@ -964,17 +946,17 @@ public class RequestBeanProxyTest {
 	@Test
 	public void g01a_pathSimpleValsPlainText() throws Exception {
 		String r = g01a.normal(new G01_Bean());
-		assertEquals("echoPath/a1/b1/c1/d1//null/true/123", r);
+		assertEquals("echoPath/a1/b1/c1//null/true/123", r);
 	}
 	@Test
 	public void g01b_pathSimpleValsUon() throws Exception {
 		String r = g01b.normal(new G01_Bean());
-		assertEquals("echoPath/a1/b1/c1/d1//null/'true'/'123'", r);
+		assertEquals("echoPath/a1/b1/c1//null/'true'/'123'", r);
 	}
 	@Test
 	public void g01c_pathSimpleValsX() throws Exception {
 		String r = g01b.serialized(new G01_Bean());
-		assertEquals("echoPath/xa1x/xb1x/xc1x/xd1x/xx/NULL/xtruex/x123x", r);
+		assertEquals("echoPath/xa1x/xb1x/xc1x/xx/NULL/xtruex/x123x", r);
 	}
 
 	//=================================================================================================================
diff --git a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java
index d059748..fdc9d0d 100644
--- a/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java
+++ b/juneau-microservice/juneau-microservice-test/src/test/java/org/apache/juneau/rest/test/client/ThirdPartyProxyTest.java
@@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals;
 import java.util.*;
 import java.util.concurrent.atomic.*;
 
-import org.apache.juneau.annotation.*;
 import org.apache.juneau.html.*;
 import org.apache.juneau.http.annotation.*;
 import org.apache.juneau.http.annotation.Request;
@@ -1262,40 +1261,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 	}
 
 	@Test
-	public void ga04_reqBeanPath4() throws Exception {
-		String r = proxy.reqBeanPath4(
-			new ReqBeanPath4() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
-	public void ga05_reqBeanPath5() throws Exception {
-		String r = proxy.reqBeanPath5(
-			new ReqBeanPath5() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
 	public void ga06_reqBeanPath6() throws Exception {
 		String r = proxy.reqBeanPath6(
 			new ReqBeanPath6() {
@@ -1377,40 +1342,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 	}
 
 	@Test
-	public void gb04_reqBeanQuery4() throws Exception {
-		String r = proxy.reqBeanQuery4(
-			new ReqBeanQuery4() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
-	public void gb05_reqBeanQuery5() throws Exception {
-		String r = proxy.reqBeanQuery5(
-			new ReqBeanQuery5() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
 	public void gb06_reqBeanQuery6() throws Exception {
 		String r = proxy.reqBeanQuery6(
 			new ReqBeanQuery6() {
@@ -1492,40 +1423,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 	}
 
 	@Test
-	public void gd04_reqBeanFormData4() throws Exception {
-		String r = proxy.reqBeanFormData4(
-			new ReqBeanFormData4() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
-	public void gd05_reqBeanFormData5() throws Exception {
-		String r = proxy.reqBeanFormData5(
-			new ReqBeanFormData5() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
 	public void gd06_reqBeanFormData6() throws Exception {
 		String r = proxy.reqBeanFormData6(
 			new ReqBeanFormData6() {
@@ -1607,40 +1504,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 	}
 
 	@Test
-	public void gf04_reqBeanHeader4() throws Exception {
-		String r = proxy.reqBeanHeader4(
-			new ReqBeanHeader4() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
-	public void gf05_reqBeanHeader5() throws Exception {
-		String r = proxy.reqBeanHeader5(
-			new ReqBeanHeader5() {
-				@Override
-				public int getX() {
-					return 1;
-				}
-				@Override
-				public String getY() {
-					return "foo";
-				}
-			}
-		);
-		assertEquals("OK", r);
-	}
-
-	@Test
 	public void gf06_reqBeanHeader6() throws Exception {
 		String r = proxy.reqBeanHeader6(
 			new ReqBeanHeader6() {
@@ -2117,36 +1980,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		}
 
 		@RemoteMethod(httpMethod="POST", path="/reqBeanPath/{a}/{b}")
-		String reqBeanPath4(
-			@Request ReqBeanPath4 rb
-		);
-
-		public static interface ReqBeanPath4 {
-			@Path
-			@BeanProperty(name="a")
-			int getX();
-
-			@Path
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanPath/{a}/{b}")
-		String reqBeanPath5(
-			@Request ReqBeanPath5 rb
-		);
-
-		public static interface ReqBeanPath5 {
-			@Path
-			@BeanProperty(name="a")
-			int getX();
-
-			@Path
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanPath/{a}/{b}")
 		String reqBeanPath6(
 			@Request ReqBeanPath6 rb
 		);
@@ -2226,36 +2059,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		}
 
 		@RemoteMethod(httpMethod="POST", path="/reqBeanQuery")
-		String reqBeanQuery4(
-			@Request ReqBeanQuery4 rb
-		);
-
-		public static interface ReqBeanQuery4 {
-			@Query
-			@BeanProperty(name="a")
-			int getX();
-
-			@Query
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanQuery")
-		String reqBeanQuery5(
-			@Request ReqBeanQuery5 rb
-		);
-
-		public static interface ReqBeanQuery5 {
-			@Query
-			@BeanProperty(name="a")
-			int getX();
-
-			@Query
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanQuery")
 		String reqBeanQuery6(
 			@Request ReqBeanQuery6 rb
 		);
@@ -2335,36 +2138,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		}
 
 		@RemoteMethod(httpMethod="POST", path="/reqBeanFormData")
-		String reqBeanFormData4(
-			@Request ReqBeanFormData4 rb
-		);
-
-		public static interface ReqBeanFormData4 {
-			@FormData
-			@BeanProperty(name="a")
-			int getX();
-
-			@FormData
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanFormData")
-		String reqBeanFormData5(
-			@Request ReqBeanFormData5 rb
-		);
-
-		public static interface ReqBeanFormData5 {
-			@FormData
-			@BeanProperty(name="a")
-			int getX();
-
-			@FormData
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanFormData")
 		String reqBeanFormData6(
 			@Request ReqBeanFormData6 rb
 		);
@@ -2444,36 +2217,6 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		}
 
 		@RemoteMethod(httpMethod="POST", path="/reqBeanHeader")
-		String reqBeanHeader4(
-			@Request ReqBeanHeader4 rb
-		);
-
-		public static interface ReqBeanHeader4 {
-			@Header
-			@BeanProperty(name="a")
-			int getX();
-
-			@Header
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanHeader")
-		String reqBeanHeader5(
-			@Request ReqBeanHeader5 rb
-		);
-
-		public static interface ReqBeanHeader5 {
-			@Header
-			@BeanProperty(name="a")
-			int getX();
-
-			@Header
-			@BeanProperty(name="b")
-			String getY();
-		}
-
-		@RemoteMethod(httpMethod="POST", path="/reqBeanHeader")
 		String reqBeanHeader6(
 			@Request ReqBeanHeader6 rb
 		);
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
index 00a5ef0..d291b05 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
@@ -15,9 +15,11 @@ package org.apache.juneau.rest.client;
 import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.IOUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
 
 import java.io.*;
 import java.lang.reflect.*;
+import java.lang.reflect.Proxy;
 import java.net.*;
 import java.util.*;
 import java.util.concurrent.*;
@@ -37,6 +39,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.encoders.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.httppart.*;
+import org.apache.juneau.httppart.bean.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.parser.ParseException;
@@ -213,7 +216,7 @@ public final class RestCall extends BeanSession implements Closeable {
 		if (! isMulti) {
 			if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty))
 				try {
-					uriBuilder.addParameter(name, serializer.createSession(null).serialize(HttpPartType.QUERY, schema, value));
+					uriBuilder.addParameter(name, serializer.createSession(null).serialize(QUERY, schema, value));
 				} catch (SchemaValidationException e) {
 					throw new RestCallException(e, "Validation error on request query parameter ''{0}''=''{1}''", name, value);
 				} catch (SerializeException e) {
@@ -454,9 +457,9 @@ public final class RestCall extends BeanSession implements Closeable {
 			try {
 				String p = null;
 				if (name.equals("/*"))
-					p = path.replaceAll("\\/\\*$", serializer.createSession(null).serialize(HttpPartType.PATH, schema, value));
+					p = path.replaceAll("\\/\\*$", serializer.createSession(null).serialize(PATH, schema, value));
 				else
-					p = path.replace(var, serializer.createSession(null).serialize(HttpPartType.PATH, schema, value));
+					p = path.replace(var, serializer.createSession(null).serialize(PATH, schema, value));
 				uriBuilder.setPath(p);
 			} catch (SchemaValidationException e) {
 				throw new RestCallException(e, "Validation error on request path parameter ''{0}''=''{1}''", name, value);
@@ -557,7 +560,7 @@ public final class RestCall extends BeanSession implements Closeable {
 	public RestCall body(Object input, HttpPartSerializer partSerializer, HttpPartSchema schema) throws RestCallException {
 		try {
 			if (partSerializer != null)
-				body(new StringEntity(partSerializer.serialize(HttpPartType.BODY, schema, input)));
+				body(new StringEntity(partSerializer.serialize(BODY, schema, input)));
 			else
 				body(input);
 		} catch (SchemaValidationException e) {
@@ -626,7 +629,7 @@ public final class RestCall extends BeanSession implements Closeable {
 		if (! isMulti) {
 			if (value != null && ! (ObjectUtils.isEmpty(value) && skipIfEmpty))
 				try {
-					request.setHeader(name, serializer.createSession(null).serialize(HttpPartType.HEADER, schema, value));
+					request.setHeader(name, serializer.createSession(null).serialize(HEADER, schema, value));
 				} catch (SchemaValidationException e) {
 					throw new RestCallException(e, "Validation error on request header parameter ''{0}''=''{1}''", name, value);
 				} catch (SerializeException e) {
@@ -1828,6 +1831,51 @@ public final class RestCall extends BeanSession implements Closeable {
 	}
 
 	/**
+	 * Same as {@link #getResponseHeader(String)} except parses the header value using the specified part parser and schema.
+	 *
+	 * @param name The header name.
+	 * @param partParser The part parser to use for parsing the header.
+	 * @param schema The part schema.  Can be <jk>null</jk>.
+	 * @param c The type to convert the part into.
+	 * @return The parsed part.
+	 * @throws IOException
+	 * @throws ParseException
+	 */
+	public <T> T getResponseHeader(HttpPartParser partParser, HttpPartSchema schema, String name, Class<T> c) throws IOException, ParseException {
+		return getResponseHeader(partParser, schema, name, (Type)c);
+	}
+
+	/**
+	 * Same as {@link #getResponseHeader(String)} except parses the header value using the specified part parser and schema.
+	 *
+	 * @param name The header name.
+	 * @param partParser The part parser to use for parsing the header.
+	 * @param schema The part schema.  Can be <jk>null</jk>.
+	 * @param type The type to convert the part into.
+	 * @param args The type arguments to convert the part into.
+	 * @return The parsed part.
+	 * @throws IOException
+	 * @throws ParseException
+	 */
+	public <T> T getResponseHeader(HttpPartParser partParser, HttpPartSchema schema, String name, Type type, Type...args) throws IOException, ParseException {
+		try {
+			HttpResponse r = getResponse();
+			Header h = r.getFirstHeader(name);
+			if (h == null)
+				return null;
+			String hs = h.getValue();
+			if (partParser == null)
+				partParser = client.getPartParser();
+			return partParser.parse(schema, hs, type, args);
+		} catch (IOException e) {
+			isFailed = true;
+			throw e;
+		} finally {
+			close();
+		}
+	}
+
+	/**
 	 * Connects to the remote resource (if {@code connect()} hasn't already been called) and returns the HTTP response code.
 	 *
 	 * <p>
@@ -2030,7 +2078,7 @@ public final class RestCall extends BeanSession implements Closeable {
 	 * @throws IOException If a connection error occurred.
 	 * @see BeanSession#getClassMeta(Class) for argument syntax for maps and collections.
 	 */
-	public <T> T getResponse(HttpPartParser partParser, HttpPartSchema schema, Type type, Type...args) throws IOException, ParseException {
+	public <T> T getResponseBody(HttpPartParser partParser, HttpPartSchema schema, Type type, Type...args) throws IOException, ParseException {
 		BeanContext bc = parser;
 		if (bc == null)
 			bc = BeanContext.DEFAULT;
@@ -2120,7 +2168,7 @@ public final class RestCall extends BeanSession implements Closeable {
 				if (type.hasStringTransform())
 					return type.getStringTransform().transform(getResponseAsString());
 				if (partParser != null)
-					return partParser.createSession(null).parse(HttpPartType.BODY, schema, getResponseAsString(), type);
+					return partParser.createSession(null).parse(BODY, schema, getResponseAsString(), type);
 			}
 
 			if (parser != null) {
@@ -2159,6 +2207,45 @@ public final class RestCall extends BeanSession implements Closeable {
 	}
 
 	/**
+	 * Converts the response from this call into a response bean.
+	 *
+	 * @param rbm The metadata used to construct the response bean.
+	 * @return A new response bean.
+	 */
+	public <T> T getResponse(final ResponseBeanMeta rbm) {
+		try {
+			Class<T> c = (Class<T>)rbm.getClassMeta().getInnerClass();
+			final RestClient rc = this.client;
+			return (T)Proxy.newProxyInstance(
+				c.getClassLoader(),
+				new Class[] { c },
+				new InvocationHandler() {
+					@Override /* InvocationHandler */
+					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+						ResponseBeanPropertyMeta pm = rbm.getProperty(method.getName());
+						if (pm != null) {
+							HttpPartParser pp = pm.getParser(rc.getPartParser());
+							HttpPartSchema schema = pm.getSchema();
+							String name = pm.getPartName();
+							ClassMeta<?> type = rc.getClassMeta(method.getGenericReturnType());
+							HttpPartType pt = pm.getPartType();
+							if (pt == RESPONSE_BODY)
+								return getResponseBody(pp, schema, type);
+							if (pt == RESPONSE_HEADER)
+								return getResponseHeader(pp, schema, name, type);
+							if (pt == RESPONSE_STATUS)
+								return getResponseCode();
+						}
+						return null;
+					}
+
+			});
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
 	 * Returns access to the {@link HttpUriRequest} passed to {@link HttpClient#execute(HttpUriRequest)}.
 	 *
 	 * @return The {@link HttpUriRequest} object.
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index 2fc0c7a..d102e32 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -1073,30 +1073,26 @@ public class RestClient extends BeanContext implements Closeable {
 								rc.body(args[ba.getIndex()], ba.getSerializer(null), ba.getSchema());
 
 							if (rmm.getRequestArgs().length > 0) {
-								BeanSession bs = createBeanSession();
 								for (RemoteMethodBeanArg rmba : rmm.getRequestArgs()) {
-									BeanMap<?> bm = bs.toBeanMap(args[rmba.getIndex()]);
-
-									for (BeanPropertyValue bpv : bm.getValues(false)) {
-										BeanPropertyMeta pMeta = bpv.getMeta();
-										Object val = bpv.getValue();
-										RequestBeanPropertyMeta a = rmba.getProperty(pMeta.getName());
-										if (a != null) {
-											HttpPartType pt = a.getPartType();
-											HttpPartSchema schema = a.getSchema();
-											if (pt == PATH)
-												rc.path(a.getPartName(), val, a.getSerializer(s), schema);
-											if (val != null) {
-												if (pt == QUERY) {
-													rc.query(a.getPartName(), val, schema.isSkipIfEmpty(), a.getSerializer(s), schema);
-												} else if (pt == FORMDATA) {
-													rc.formData(a.getPartName(), val, schema.isSkipIfEmpty(), a.getSerializer(s), schema);
-												} else if (pt == HEADER) {
-													rc.header(a.getPartName(), val, schema.isSkipIfEmpty(), a.getSerializer(s), schema);
-												} else if (pt == HttpPartType.BODY) {
-													rc.body(val, a.getSerializer(s), schema);
-												}
-											}
+									RequestBeanMeta rbm = rmba.getMeta();
+									for (RequestBeanPropertyMeta p : rbm.getProperties()) {
+										Object val = p.getGetter().invoke(args[rmba.getIndex()]);
+										HttpPartType pt = p.getPartType();
+										HttpPartSerializer ps = p.getSerializer(s);
+										String pn = p.getPartName();
+										HttpPartSchema schema = p.getSchema();
+										boolean sie = schema.isSkipIfEmpty();
+										if (pt == PATH)
+											rc.path(pn, val, p.getSerializer(s), schema);
+										else if (val != null) {
+											if (pt == QUERY) 
+												rc.query(pn, val, sie, ps, schema);
+											else if (pt == FORMDATA)
+												rc.formData(pn, val, sie, ps, schema);
+											else if (pt == HEADER)
+												rc.header(pn, val, sie, ps, schema);
+											else if (pt == HttpPartType.BODY)
+												rc.body(val, p.getSerializer(s), schema);
 										}
 									}
 								}
@@ -1124,7 +1120,7 @@ public class RestClient extends BeanContext implements Closeable {
 									return returnCode < 400;
 								throw new RestCallException("Invalid return type on method annotated with @RemoteableMethod(returns=HTTP_STATUS).  Only integer and booleans types are valid.");
 							} else {
-								Object v = rc.getResponse(rmr.getParser(), rmr.getSchema(), method.getGenericReturnType());
+								Object v = rc.getResponseBody(rmr.getParser(), rmr.getSchema(), method.getGenericReturnType());
 								if (v == null && method.getReturnType().isPrimitive())
 									v = ClassUtils.getPrimitiveDefault(method.getReturnType());
 								return v;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
index a0df444..7a65ba2 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestBody.java
@@ -14,6 +14,7 @@ package org.apache.juneau.rest;
 
 import static org.apache.juneau.internal.IOUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
 
 import java.io.*;
 import java.lang.reflect.*;
@@ -473,7 +474,7 @@ public class RequestBody {
 
 		if (partParser != null) {
 			String in = asString();
-			return partParser.createSession(req.getParserSessionArgs()).parse(HttpPartType.BODY, schema, isEmpty(in) ? null : in, cm);
+			return partParser.createSession(req.getParserSessionArgs()).parse(BODY, schema, isEmpty(in) ? null : in, cm);
 		}
 
 		MediaType mt = getMediaType();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
index f7aa24f..297dcc6 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestJavaMethod.java
@@ -20,6 +20,7 @@ import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.internal.Utils.*;
 import static org.apache.juneau.rest.RestContext.*;
 import static org.apache.juneau.rest.util.RestUtils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
 
 import java.beans.*;
 import java.lang.annotation.*;
@@ -449,7 +450,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod>  {
 			if (a != null) {
 				HttpPartSchema schema = HttpPartSchema.create(a);
 				HttpPartSerializer serializer = createPartSerializer(schema.getSerializer(), serializers.getPropertyStore(), partSerializer);
-				pm = new ResponsePartMeta(HttpPartType.HEADER, schema, serializer);
+				pm = new ResponsePartMeta(HEADER, schema, serializer);
 			}
 			if (pm == null)
 				pm = ResponsePartMeta.NULL;
@@ -470,7 +471,7 @@ public class RestJavaMethod implements Comparable<RestJavaMethod>  {
 			if (a != null) {
 				HttpPartSchema schema = HttpPartSchema.create(a);
 				HttpPartSerializer serializer = schema.isUsePartSerializer() ? createPartSerializer(schema.getSerializer(), serializers.getPropertyStore(), partSerializer) : null;
-				pm = new ResponsePartMeta(HttpPartType.BODY, schema, serializer);
+				pm = new ResponsePartMeta(BODY, schema, serializer);
 			}
 			if (pm == null)
 				pm = ResponsePartMeta.NULL;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index abac3d3..dcaa249 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -1485,7 +1485,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
 				new InvocationHandler() {
 					@Override /* InvocationHandler */
 					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-						RequestBeanPropertyMeta pm = rbm.getPropertyByGetter(method.getName());
+						RequestBeanPropertyMeta pm = rbm.getProperty(method.getName());
 						if (pm != null) {
 							HttpPartParser pp = pm.getParser(getPartParser());
 							HttpPartSchema schema = pm.getSchema();
@@ -1494,13 +1494,13 @@ public final class RestRequest extends HttpServletRequestWrapper {
 							HttpPartType pt = pm.getPartType();
 							if (pt == HttpPartType.BODY)
 								return getBody().asType(pm.getParser(null), schema, type);
-							if (pt == HttpPartType.QUERY)
+							if (pt == QUERY)
 								return getQuery().get(pp, schema, name, type);
-							if (pt == HttpPartType.FORMDATA)
+							if (pt == FORMDATA)
 								return getFormData().get(pp, schema, name, type);
-							if (pt == HttpPartType.HEADER)
+							if (pt == HEADER)
 								return getHeaders().get(pp, schema, name, type);
-							if (pt == HttpPartType.PATH)
+							if (pt == PATH)
 								return getPathMatch().get(pp, schema, name, type);
 						}
 						return null;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
index 5e444c7..96941aa 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/reshandlers/DefaultHandler.java
@@ -14,6 +14,7 @@ package org.apache.juneau.rest.reshandlers;
 
 import static org.apache.juneau.internal.ObjectUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.httppart.HttpPartType.*;
 
 import java.io.*;
 import java.lang.reflect.*;
@@ -64,10 +65,10 @@ public class DefaultHandler implements ResponseHandler {
 
 		if (rm != null) {
 
-			Method stm = rm.getStatusMethod();
+			ResponseBeanPropertyMeta stm = rm.getStatusMethod();
 			if (stm != null) {
 				try {
-					res.setStatus((int)stm.invoke(o));
+					res.setStatus((int)stm.getGetter().invoke(o));
 				} catch (Exception e) {
 					throw new InternalServerError(e, "Could not get status.");
 				}
@@ -85,21 +86,22 @@ public class DefaultHandler implements ResponseHandler {
 							String k = asString(key);
 							Object v = m.get(key);
 							HttpPartSchema s = hm.getSchema().getProperty(k);
-							res.setHeader(new HttpPart(k, HttpPartType.HEADER, s, firstNonNull(hm.getSerializer(), req.getPartSerializer()), req.getSerializerSessionArgs(), v));
+							res.setHeader(new HttpPart(k, RESPONSE_HEADER, s, hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), v));
 						}
 					} else {
-						res.setHeader(new HttpPart(n, HttpPartType.HEADER, hm.getSchema(), firstNonNull(hm.getSerializer(), req.getPartSerializer()), req.getSerializerSessionArgs(), ho));
+						res.setHeader(new HttpPart(n, RESPONSE_HEADER, hm.getSchema(), hm.getSerializer(req.getPartSerializer()), req.getSerializerSessionArgs(), ho));
 					}
 				} catch (Exception e) {
 					throw new InternalServerError(e, "Could not set header ''{0}''", hm.getPartName());
 				}
 			}
 
-			Method m = rm.getBodyMethod();
+			ResponseBeanPropertyMeta bm = rm.getBodyMethod();
 			boolean usePartSerializer = rm.isUsePartSerializer();
 			HttpPartSchema schema = rm.getSchema();
 
-			if (m != null) {
+			if (bm != null) {
+				Method m = bm.getGetter();
 				try {
 					Class<?>[] pt = m.getParameterTypes();
 					if (pt.length == 1) {
@@ -124,7 +126,7 @@ public class DefaultHandler implements ResponseHandler {
 				HttpPartSerializer ps = firstNonNull(rm.getPartSerializer(), req.getPartSerializer());
 				if (ps != null) {
 					try (FinishablePrintWriter w = res.getNegotiatedWriter()) {
-						w.append(ps.serialize(HttpPartType.BODY, schema, o));
+						w.append(ps.serialize(BODY, schema, o));
 						w.flush();
 						w.finish();
 					} catch (SchemaValidationException | SerializeException e) {