http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
index 52af23f..7bc9449 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestCall.java
@@ -12,6 +12,8 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.rest.client;
 
+import static org.apache.juneau.internal.StringUtils.*;
+
 import java.io.*;
 import java.lang.reflect.*;
 import java.net.*;
@@ -23,6 +25,7 @@ import java.util.regex.*;
 import org.apache.http.*;
 import org.apache.http.client.*;
 import org.apache.http.client.config.*;
+import org.apache.http.client.entity.*;
 import org.apache.http.client.methods.*;
 import org.apache.http.client.utils.*;
 import org.apache.http.impl.client.*;
@@ -78,9 +81,11 @@ public final class RestCall {
        private boolean isClosed = false;
        private boolean isFailed = false;
        private Object input;
+       private boolean hasInput;  // input() was called, even if it's setting 
'null'.
        private Serializer serializer;
        private Parser parser;
        private URIBuilder uriBuilder;
+       private NameValuePairs formData;
 
        /**
         * Constructs a REST call with the specified method name.
@@ -164,57 +169,84 @@ public final class RestCall {
        }
 
        /**
-        * Adds a parameter to the URI query.
+        * Adds a query parameter to the URI query.
         *
         * @param name The parameter name.
+        * Can be null/blank if the value is a {@link Map} or {@link String}.
         * @param value The parameter value converted to a string using UON 
notation.
+        * Can also be a {@link Map} or {@link String} if the name is 
null/blank.
+        * If a {@link String} and the name is null/blank, then calls {@link 
URIBuilder#setCustomQuery(String)}.
+        * @param skipIfEmpty Don't add the pair if the value is empty.
         * @return This object (for method chaining).
-        * @throws RestCallException
         */
-       public RestCall param(String name, Object value) throws 
RestCallException {
-               uriBuilder.addParameter(name, 
client.getUrlEncodingSerializer().serializeUrlPart(value));
+       @SuppressWarnings("unchecked")
+       public RestCall query(String name, Object value, boolean skipIfEmpty) {
+               if (! isEmpty(name)) {
+                       if (! (isEmpty(value) && skipIfEmpty))
+                               uriBuilder.addParameter(name, 
client.getUrlEncodingSerializer().serializePart(value, false, null));
+               } else {
+                       if (value instanceof String) {
+                               String s = value.toString();
+                               if (! isEmpty(s))
+                                       uriBuilder.setCustomQuery(s);
+                       } else if (value instanceof Map) {
+                               for (Map.Entry<String,Object> p : 
((Map<String,Object>) value).entrySet())
+                                       query(p.getKey(), p.getValue(), 
skipIfEmpty);
+                       } else {
+                               throw new RuntimeException("Invalid name passed 
to query(name,value,skipIfEmpty).");
+                       }
+               }
                return this;
        }
 
        /**
-        * Adds parameters to the URI query.
+        * Adds a query parameter to the URI query.
+        *
+        * @param name The parameter name.
+        * @param value The parameter value converted to a string using UON 
notation.
+        * @return This object (for method chaining).
+        * @throws RestCallException
+        */
+       public RestCall query(String name, Object value) throws 
RestCallException {
+               return query(name, value, false);
+       }
+
+       /**
+        * Adds query parameters to the URI query.
         *
         * @param params The parameters.  Values are converted to a string 
using UON notation.
         * @return This object (for method chaining).
         * @throws RestCallException
         */
-       public RestCall params(Map<String,Object> params) throws 
RestCallException {
-               for (Map.Entry<String,Object> p : params.entrySet())
-                       uriBuilder.addParameter(p.getKey(), 
client.getUrlEncodingSerializer().serializeUrlPart(p.getValue()));
-               return this;
+       public RestCall query(Map<String,Object> params) throws 
RestCallException {
+               return query(null, params);
        }
 
        /**
-        * Adds a parameter to the URI query if the parameter value is not 
<jk>null</jk>.
+        * Adds a query parameter to the URI query if the parameter value is 
not <jk>null</jk> or an empty string.
+        * <p>
+        * NE = "not empty"
         *
         * @param name The parameter name.
         * @param value The parameter value converted to a string using UON 
notation.
         * @return This object (for method chaining).
         * @throws RestCallException
         */
-       public RestCall paramIfNN(String name, Object value) throws 
RestCallException {
-               if (value != null)
-                       uriBuilder.addParameter(name, 
client.getUrlEncodingSerializer().serializeUrlPart(value));
-               return this;
+       public RestCall queryIfNE(String name, Object value) throws 
RestCallException {
+               return query(name, value, true);
        }
 
        /**
-        * Adds a parameter to the URI query if the parameter value is not 
<jk>null</jk> or an empty string.
+        * Adds query parameters to the URI for any parameters that aren't 
null/empty.
+        * <p>
+        * NE = "not empty"
         *
-        * @param name The parameter name.
-        * @param value The parameter value converted to a string using UON 
notation.
+        * @param params The parameters.  Values are converted to a string 
using UON notation.
         * @return This object (for method chaining).
         * @throws RestCallException
         */
-       public RestCall paramIfNE(String name, Object value) throws 
RestCallException {
-               if (! StringUtils.isEmpty(value))
-                       uriBuilder.addParameter(name, 
client.getUrlEncodingSerializer().serializeUrlPart(value));
-               return this;
+       public RestCall queryIfNE(Map<String,Object> params) throws 
RestCallException {
+               return query(null, params, true);
        }
 
        /**
@@ -229,6 +261,99 @@ public final class RestCall {
        }
 
        /**
+        * Adds a form data pair to this request to perform a URL-encoded form 
post.
+        *
+        * @param name The parameter name.
+        * Can be null/blank if the value is a {@link Map} or {@link 
NameValuePairs}.
+        * @param value The parameter value converted to a string using UON 
notation.
+        * Can also be a {@link Map} or {@link NameValuePairs}.
+        * @param skipIfEmpty Don't add the pair if the value is empty.
+        * @return This object (for method chaining).
+        */
+       @SuppressWarnings("unchecked")
+       public RestCall formData(String name, Object value, boolean 
skipIfEmpty) {
+               if (formData == null)
+                       formData = new NameValuePairs();
+               if (! isEmpty(name)) {
+                       if (! (isEmpty(value) && skipIfEmpty))
+                               formData.add(new SerializedNameValuePair(name, 
value, client.getUrlEncodingSerializer()));
+               } else {
+                       if (value instanceof NameValuePairs) {
+                               formData.addAll((NameValuePairs)value);
+                       } else if (value instanceof Map) {
+                               for (Map.Entry<String,Object> p : 
((Map<String,Object>) value).entrySet())
+                                       formData(p.getKey(), p.getValue(), 
skipIfEmpty);
+                       } else {
+                               throw new RuntimeException("Invalid name passed 
to formData(name,value,skipIfEmpty).");
+                       }
+               }
+               return this;
+       }
+
+       /**
+        * Adds a form data pair to this request to perform a URL-encoded form 
post.
+        *
+        * @param name The parameter name.
+        * Can be null/blank if the value is a {@link Map} or {@link 
NameValuePairs}.
+        * @param value The parameter value converted to a string using UON 
notation.
+        * Can also be a {@link Map} or {@link NameValuePairs}.
+        * @return This object (for method chaining).
+        * @throws RestCallException If name was null/blank and value wasn't a 
{@link Map} or {@link NameValuePairs}.
+        */
+       public RestCall formData(String name, Object value) throws 
RestCallException {
+               return formData(name, value, false);
+       }
+
+       /**
+        * Adds form data pairs to this request to perform a URL-encoded form 
post.
+        *
+        * @param nameValuePairs The name-value pairs of the request.
+        * @return This object (for method chaining).
+        * @throws RestCallException
+        */
+       public RestCall formData(NameValuePairs nameValuePairs) throws 
RestCallException {
+               return formData(null, nameValuePairs);
+       }
+
+       /**
+        * Adds form data pairs to this request to perform a URL-encoded form 
post.
+        *
+        * @param params The parameters.  Values are converted to a string 
using UON notation.
+        * @return This object (for method chaining).
+        * @throws RestCallException If name was null/blank and value wasn't a 
{@link Map} or {@link NameValuePairs}.
+        */
+       public RestCall formData(Map<String,Object> params) throws 
RestCallException {
+               return formData(null, params);
+       }
+
+       /**
+        * Adds a form data pair to the request if the parameter value is not 
<jk>null</jk> or an empty string.
+        * <p>
+        * NE = "not empty"
+        *
+        * @param name The parameter name.
+        * @param value The parameter value converted to a string using UON 
notation.
+        * @return This object (for method chaining).
+        * @throws RestCallException
+        */
+       public RestCall formDataIfNE(String name, Object value) throws 
RestCallException {
+               return formData(name, value, true);
+       }
+
+       /**
+        * Adds form data parameters to the request for any parameters that 
aren't null/empty.
+        * <p>
+        * NE = "not empty"
+        *
+        * @param params The parameters.  Values are converted to a string 
using UON notation.
+        * @return This object (for method chaining).
+        * @throws RestCallException
+        */
+       public RestCall formDataIfNE(Map<String,Object> params) throws 
RestCallException {
+               return formData(null, params, true);
+       }
+
+       /**
         * Sets the URI user info.
         *
         * @param userInfo The new URI user info.
@@ -261,12 +386,14 @@ public final class RestCall {
         *      <li>{@link InputStream} - Raw contents of {@code InputStream} 
will be serialized to remote resource.
         *      <li>{@link Object} - POJO to be converted to text using the 
{@link Serializer} registered with the {@link RestClient}.
         *      <li>{@link HttpEntity} - Bypass Juneau serialization and pass 
HttpEntity directly to HttpClient.
+        *      <li>{@link NameValuePairs} - Converted to a URL-encoded FORM 
post.
         * </ul>
         * @return This object (for method chaining).
         * @throws RestCallException If a retry was attempted, but the entity 
was not repeatable.
         */
        public RestCall input(final Object input) throws RestCallException {
                this.input = input;
+               this.hasInput = true;
                return this;
        }
 
@@ -302,19 +429,79 @@ public final class RestCall {
        
//--------------------------------------------------------------------------------
 
        /**
-        * Convenience method for setting a header value on the request.
-        * <p>
-        * Equivalent to calling <code>restCall.getRequest().setHeader(name, 
value.toString())</code>.
+        * Sets a header on the request.
         *
         * @param name The header name.
+        * The name can be null/empty if the value is a {@link Map}.
         * @param value The header value.
+        * @param skipIfEmpty Don't add the header if the name is null/empty.
         * @return This object (for method chaining).
         */
-       public RestCall header(String name, Object value) {
-               request.setHeader(name, value.toString());
+       @SuppressWarnings("unchecked")
+       public RestCall header(String name, Object value, boolean skipIfEmpty) {
+               if (! isEmpty(name)) {
+                       if (! (isEmpty(value) && skipIfEmpty))
+                               request.setHeader(name, 
client.getUrlEncodingSerializer().serializePart(value, false, true));
+               } else {
+                       if (value instanceof Map) {
+                               for (Map.Entry<String,Object> p : 
((Map<String,Object>) value).entrySet())
+                                       header(p.getKey(), p.getValue(), 
skipIfEmpty);
+                       } else {
+                               throw new RuntimeException("Invalid name passed 
to formData(name,value,skipIfEmpty).");
+                       }
+               }
                return this;
        }
 
+
+       /**
+        * Sets a header on the request.
+        *
+        * @param name The header name.
+        * The name can be null/empty if the value is a {@link Map}.
+        * @param value The header value.
+        * @return This object (for method chaining).
+        */
+       public RestCall header(String name, Object value) {
+               return header(name, value, false);
+       }
+
+       /**
+        * Sets headers on the request.
+        *
+        * @param values The header values.
+        * @return This object (for method chaining).
+        */
+       public RestCall headers(Map<String,Object> values) {
+               return header(null, values, false);
+       }
+
+       /**
+        * Sets a header on the request if the value is not null/empty.
+        * <p>
+        * NE = "not empty"
+        *
+        * @param name The header name.
+        * The name can be null/empty if the value is a {@link Map}.
+        * @param value The header value.
+        * @return This object (for method chaining).
+        */
+       public RestCall headerIfNE(String name, Object value) {
+               return header(name, value, true);
+       }
+
+       /**
+        * Sets headers on the request if the values are not null/empty.
+        * <p>
+        * NE = "not empty"
+        *
+        * @param values The header values.
+        * @return This object (for method chaining).
+        */
+       public RestCall headersIfNE(Map<String,Object> values) {
+               return header(null, values, true);
+       }
+
        /**
         * Sets the value for the <code>Accept</code> request header.
         * <p>
@@ -1082,13 +1269,28 @@ public final class RestCall {
 
                        request.setURI(uriBuilder.build());
 
-                       if (input != null) {
+                       if (hasInput || formData != null) {
+
+                               if (hasInput && formData != null)
+                                       throw new RestCallException("Both input 
and form data found on same request.");
+
                                if (! (request instanceof 
HttpEntityEnclosingRequestBase))
                                        throw new RestCallException(0, "Method 
does not support content entity.", request.getMethod(), request.getURI(), null);
-                               HttpEntity entity = (input instanceof 
HttpEntity) ? (HttpEntity)input : new RestRequestEntity(input, getSerializer());
-                               
((HttpEntityEnclosingRequestBase)request).setEntity(entity);
+
+                               HttpEntity entity = null;
+                               if (formData != null)
+                                       entity = new 
UrlEncodedFormEntity(formData);
+                               else if (input instanceof NameValuePairs)
+                                       entity = new 
UrlEncodedFormEntity((NameValuePairs)input);
+                               else if (input instanceof HttpEntity)
+                                       entity = (HttpEntity)input;
+                               else
+                                       entity = new RestRequestEntity(input, 
getSerializer());
+
                                if (retries > 1 && ! entity.isRepeatable())
                                        throw new RestCallException("Rest call 
set to retryable, but entity is not repeatable.");
+
+                               
((HttpEntityEnclosingRequestBase)request).setEntity(entity);
                        }
 
                        int sc = 0;
@@ -1104,7 +1306,7 @@ public final class RestCall {
                                        if (response != null)
                                                
EntityUtils.consumeQuietly(response.getEntity());
                                }
-                               if (! retryOn.onCode(sc))
+                               if (! retryOn.onResponse(response))
                                        retries = 0;
                                if (retries > 0) {
                                        for (RestCallInterceptor rci : 
interceptors)
@@ -1360,6 +1562,16 @@ public final class RestCall {
         *      <jc>// Parse into a map of object keys/values.</jc>
         *      Map m = 
restClient.doGet(url).getResponse(TreeMap.<jk>class</jk>);
         * </p>
+        * <p>
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>You can also specify any of the following types:
+        *              <ul>
+        *                      <li>{@link HttpResponse} - Returns the raw 
<code>HttpResponse</code> returned by the inner <code>HttpClient</code>.
+        *                      <li>{@link Reader} - Returns access to the raw 
reader of the response.
+        *                      <li>{@link InputStream} - Returns access to the 
raw input stream of the response.
+        *              </ul>
+        * </ul>
         *
         * @param <T> The class type of the object being created.
         * See {@link #getResponse(Type, Type...)} for details.
@@ -1427,6 +1639,12 @@ public final class RestCall {
         * <h5 class='section'>Notes:</h5>
         * <ul>
         *      <li>Use the {@link #getResponse(Class)} method instead if you 
don't need a parameterized map/collection.
+        *      <li>You can also specify any of the following types:
+        *              <ul>
+        *                      <li>{@link HttpResponse} - Returns the raw 
<code>HttpResponse</code> returned by the inner <code>HttpClient</code>.
+        *                      <li>{@link Reader} - Returns access to the raw 
reader of the response.
+        *                      <li>{@link InputStream} - Returns access to the 
raw input stream of the response.
+        *              </ul>
         * </ul>
         *
         * @param <T> The class type of the object to create.
@@ -1500,8 +1718,15 @@ public final class RestCall {
                return getResponsePojoRest(ObjectMap.class);
        }
 
+       @SuppressWarnings("unchecked")
        <T> T getResponse(ClassMeta<T> type) throws IOException, ParseException 
{
                try {
+                       if (type.getInnerClass().equals(HttpResponse.class))
+                               return (T)response;
+                       if (type.getInnerClass().equals(Reader.class))
+                               return (T)getReader();
+                       if (type.getInnerClass().equals(InputStream.class))
+                               return (T)getInputStream();
                        Parser p = getParser();
                        T o = null;
                        if (! p.isReaderParser()) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index df91067..e5f9a32 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -12,6 +12,8 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.rest.client;
 
+import static org.apache.juneau.internal.StringUtils.*;
+
 import java.io.*;
 import java.lang.reflect.*;
 import java.lang.reflect.Proxy;
@@ -29,6 +31,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
+import org.apache.juneau.remoteable.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
@@ -55,7 +58,6 @@ public class RestClient extends CoreObject {
        private final CloseableHttpClient httpClient;
        private final boolean keepHttpClientOpen;
        private final UrlEncodingSerializer urlEncodingSerializer;  // Used for 
form posts only.
-       private final String remoteableServletUri;
        private final String rootUrl;
        private volatile boolean isClosed = false;
        private final StackTraceElement[] creationStack;
@@ -84,7 +86,6 @@ public class RestClient extends CoreObject {
                        UrlEncodingSerializer urlEncodingSerializer,
                        Map<String,String> headers,
                        List<RestCallInterceptor> interceptors,
-                       String remoteableServletUri,
                        String rootUri,
                        RetryOn retryOn,
                        int retries,
@@ -102,7 +103,6 @@ public class RestClient extends CoreObject {
                Map<String,String> h2 = new 
ConcurrentHashMap<String,String>(headers);
 
                this.headers = Collections.unmodifiableMap(h2);
-               this.remoteableServletUri = remoteableServletUri;
                this.rootUrl = rootUri;
                this.retryOn = retryOn;
                this.retries = retries;
@@ -220,6 +220,21 @@ public class RestClient extends CoreObject {
        }
 
        /**
+        * Same as {@link #doPost(Object, Object)} but don't specify the input 
yet.
+        * <p>
+        * You must call either {@link RestCall#input(Object)} or {@link 
RestCall#formData(String, Object)}
+        * to set the contents on the result object.
+        *
+        * @param url The URL of the remote REST resource.  Can be any of the 
following:  {@link String}, {@link URI}, {@link URL}.
+        * @return A {@link RestCall} object that can be further tailored 
before executing the request
+        *      and getting the response as a parsed object.
+        * @throws RestCallException
+        */
+       public RestCall doPost(Object url) throws RestCallException {
+               return doCall("POST", url, true);
+       }
+
+       /**
         * Perform a <code>DELETE</code> request against the specified URL.
         *
         * @param url The URL of the remote REST resource.  Can be any of the 
following:  {@link String}, {@link URI}, {@link URL}.
@@ -337,6 +352,7 @@ public class RestClient extends CoreObject {
         *      <li>{@link InputStream} - Raw contents of {@code InputStream} 
will be serialized to remote resource.
         *      <li>{@link Object} - POJO to be converted to text using the 
{@link Serializer} registered with the {@link RestClient}.
         *      <li>{@link HttpEntity} - Bypass Juneau serialization and pass 
HttpEntity directly to HttpClient.
+        *      <li>{@link NameValuePairs} - Converted to a URL-encoded FORM 
post.
         * </ul>
         * This parameter is IGNORED if {@link HttpMethod#hasContent()} is 
<jk>false</jk>.
         * @return A {@link RestCall} object that can be further tailored 
before executing the request
@@ -405,64 +421,144 @@ public class RestClient extends CoreObject {
        }
 
        /**
-        * Create a new proxy interface for the specified remoteable service 
interface.
+        * Create a new proxy interface against a REST interface.
+        * <p>
+        * The URL to the REST interface is based on the following values:
+        * <ul>
+        *      <li>The {@link Remoteable#path() @Remoteable.path()} annotation 
on the interface (<code>remoteable-path</code>).
+        *      <li>The {@link RestClientBuilder#rootUrl(Object) rootUrl} on 
the client (<code>root-url</code>).
+        *      <li>The fully-qualified class name of the interface 
(<code>class-name</code>).
+        * </ul>
+        * <p>
+        * The URL calculation is as follows:
+        * <ul>
+        *      <li><code>remoteable-path</code> - If remoteable path is 
absolute.
+        *      <li><code>root-url/remoteable-path</code> - If remoteable path 
is relative and root-url has been specified.
+        *      <li><code>root-url/class-name</code> - If remoteable path is 
not specified.
+        * </ul>
+        * <p>
+        * If the information is not available to resolve to an absolute URL, a 
{@link RemoteableMetadataException} is thrown.
+        * <p>
+        * Examples:
+        * <p class='bcode'>
+        *      <jk>package</jk> org.apache.foo;
+        *
+        *      
<ja>@Remoteable</ja>(path=<js>"http://hostname/resturl/myinterface1";</js>)
+        *      <jk>public interface</jk> MyInterface1 { ... }
+        *
+        *      <ja>@Remoteable</ja>(path=<js>"/myinterface2"</js>)
+        *      <jk>public interface</jk> MyInterface2 { ... }
+        *
+        *      <jk>public interface</jk> MyInterface3 { ... }
+        *
+        *      <jc>// Resolves to "http://localhost/resturl/myinterface1";</jc>
+        *      MyInterface1 i1 = <jk>new</jk> RestClientBuilder()
+        *              .build()
+        *              .getRemoteableProxy(MyInterface1.<jk>class</jk>);
+        *
+        *      <jc>// Resolves to "http://hostname/resturl/myinterface2";</jc>
+        *      MyInterface2 i2 = <jk>new</jk> RestClientBuilder()
+        *              .rootUrl(<js>"http://hostname/resturl";</js>)
+        *              .build()
+        *              .getRemoteableProxy(MyInterface2.<jk>class</jk>);
+        *
+        *      <jc>// Resolves to 
"http://hostname/resturl/org.apache.foo.MyInterface3";</jc>
+        *      MyInterface3 i3 = <jk>new</jk> RestClientBuilder()
+        *              .rootUrl(<js>"http://hostname/resturl";</js>)
+        *              .build()
+        *              .getRemoteableProxy(MyInterface3.<jk>class</jk>);
+        * </p>
         *
         * @param interfaceClass The interface to create a proxy for.
         * @return The new proxy interface.
-        * @throws RuntimeException If the Remotable service URI has not been 
specified on this
-        *      client by calling {@link 
RestClientBuilder#remoteableServletUri(String)}.
+        * @throws RemoteableMetadataException If the REST URI cannot be 
determined based on the information given.
         */
        public <T> T getRemoteableProxy(final Class<T> interfaceClass) {
-               if (remoteableServletUri == null)
-                       throw new RuntimeException("Remoteable service URI has 
not been specified.");
-               return getRemoteableProxy(interfaceClass, remoteableServletUri 
+ '/' + interfaceClass.getName());
+               return getRemoteableProxy(interfaceClass, null);
        }
 
        /**
-        * Create a new proxy interface for the specified REST PROXY interface.
+        * Same as {@link #getRemoteableProxy(Class)} except explicitly 
specifies the URL of the REST interface.
         *
         * @param interfaceClass The interface to create a proxy for.
-        * @param proxyUrl The URL of the REST method annotated with 
<code><ja>@RestMethod</ja>(name=<js>"PROXY"</js>)</code>.
+        * @param restUrl The URL of the REST interface.
         * @return The new proxy interface.
         */
-       public <T> T getRemoteableProxy(final Class<T> interfaceClass, final 
Object proxyUrl) {
-               return getRemoteableProxy(interfaceClass, proxyUrl, serializer, 
parser);
+       public <T> T getRemoteableProxy(final Class<T> interfaceClass, final 
Object restUrl) {
+               return getRemoteableProxy(interfaceClass, restUrl, serializer, 
parser);
        }
 
        /**
         * Same as {@link #getRemoteableProxy(Class, Object)} but allows you to 
override the serializer and parser used.
         *
         * @param interfaceClass The interface to create a proxy for.
-        * @param proxyUrl The URL of the REST method annotated with 
<code><ja>@RestMethod</ja>(name=<js>"PROXY"</js>)</code>.
+        * @param restUrl The URL of the REST interface.
         * @param serializer The serializer used to serialize POJOs to the body 
of the HTTP request.
         * @param parser The parser used to parse POJOs from the body of the 
HTTP response.
         * @return The new proxy interface.
         */
        @SuppressWarnings({ "unchecked", "hiding" })
-       public <T> T getRemoteableProxy(final Class<T> interfaceClass, final 
Object proxyUrl, final Serializer serializer, final Parser parser) {
+       public <T> T getRemoteableProxy(final Class<T> interfaceClass, Object 
restUrl, final Serializer serializer, final Parser parser) {
+
+               if (restUrl == null) {
+                       Remoteable r = 
ReflectionUtils.getAnnotation(Remoteable.class, interfaceClass);
+
+                       String path = r == null ? "" : trimSlashes(r.path());
+                       if (path.indexOf("://") == -1) {
+                               if (path.isEmpty())
+                                       path = interfaceClass.getName();
+                               if (rootUrl == null)
+                                       throw new 
RemoteableMetadataException(interfaceClass, "Root URI has not been specified.  
Cannot construct absolute path to remoteable proxy.");
+                               path = trimSlashes(rootUrl) + '/' + path;
+                       }
+                       restUrl = path;
+               }
+
+               final String restUrl2 = restUrl.toString();
+
                try {
                        return (T)Proxy.newProxyInstance(
                                interfaceClass.getClassLoader(),
                                new Class[] { interfaceClass },
                                new InvocationHandler() {
 
-                                       final Map<Method,String> uriCache = new 
ConcurrentHashMap<Method,String>();
-                                       final String uri = 
toURI(proxyUrl).toString();
+                                       final RemoteableMeta rm = new 
RemoteableMeta(interfaceClass, restUrl2);
 
                                        @Override /* InvocationHandler */
                                        public Object invoke(Object proxy, 
Method method, Object[] args) throws Throwable {
+                                               RemoteableMethodMeta rmm = 
rm.getMethodMeta(method);
 
-                                               // Constructing this string 
each time can be time consuming, so cache it.
-                                               String u = uriCache.get(method);
-                                               if (u == null) {
-                                                       try {
-                                                               u = uri + '/' + 
URLEncoder.encode(ClassUtils.getMethodSignature(method), "utf-8");
-                                                       } catch 
(UnsupportedEncodingException e) {}
-                                                       uriCache.put(method, u);
-                                               }
+                                               if (rmm == null)
+                                                       throw new 
RuntimeException("Method is not exposed as a remoteable method.");
 
                                                try {
-                                                       return doPost(u, 
args).serializer(serializer).parser(parser).getResponse(method.getGenericReturnType());
+                                                       String url = 
rmm.getUrl();
+                                                       String httpMethod = 
rmm.getHttpMethod();
+                                                       RestCall rc = 
(httpMethod.equals("POST") ? doPost(url) : doGet(url));
+                                                       
rc.serializer(serializer).parser(parser);
+
+                                                       for (RemoteMethodArg a 
: rmm.getQueryArgs())
+                                                               
rc.query(a.name, args[a.index], a.skipIfNE);
+
+                                                       for (RemoteMethodArg a 
: rmm.getFormDataArgs())
+                                                               
rc.formData(a.name, args[a.index], a.skipIfNE);
+
+                                                       for (RemoteMethodArg a 
: rmm.getHeaderArgs())
+                                                               
rc.header(a.name, args[a.index], a.skipIfNE);
+
+                                                       if (rmm.getBodyArg() != 
null)
+                                                               
rc.input(args[rmm.getBodyArg()]);
+
+                                                       if 
(rmm.getOtherArgs().length > 0) {
+                                                               Object[] 
otherArgs = new Object[rmm.getOtherArgs().length];
+                                                               int i = 0;
+                                                               for (Integer 
otherArg : rmm.getOtherArgs())
+                                                                       
otherArgs[i++] = args[otherArg];
+                                                               
rc.input(otherArgs);
+                                                       }
+
+                                                       return 
rc.getResponse(method.getGenericReturnType());
+
                                                } catch (RestCallException e) {
                                                        // Try to throw 
original exception if possible.
                                                        
e.throwServerException(interfaceClass.getClassLoader());

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
index faea9f8..e3ffcc8 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
@@ -67,7 +67,6 @@ public class RestClientBuilder extends CoreObjectBuilder {
 
        private List<RestCallInterceptor> interceptors = new 
ArrayList<RestCallInterceptor>();
 
-       private String remoteableServletUri;
        private String rootUrl;
        private SSLOpts sslOpts;
        private boolean pooled;
@@ -140,7 +139,7 @@ public class RestClientBuilder extends CoreObjectBuilder {
 
                        UrlEncodingSerializer us = new 
SerializerBuilder(propertyStore).build(UrlEncodingSerializer.class);
 
-                       return new RestClient(propertyStore, httpClient, 
keepHttpClientOpen, s, p, us, headers, interceptors, remoteableServletUri, 
rootUrl, retryOn, retries, retryInterval, debug, executorService, 
executorServiceShutdownOnClose);
+                       return new RestClient(propertyStore, httpClient, 
keepHttpClientOpen, s, p, us, headers, interceptors, rootUrl, retryOn, retries, 
retryInterval, debug, executorService, executorServiceShutdownOnClose);
                } catch (Exception e) {
                        throw new RuntimeException(e);
                }
@@ -231,18 +230,6 @@ public class RestClientBuilder extends CoreObjectBuilder {
        }
 
        /**
-        * Sets the URI of the remoteable services REST servlet for invoking 
remoteable services.
-        *
-        * @param remoteableServletUri The URI of the REST resource 
implementing a remoteable services servlet.
-        *      (typically an instance of <code>RemoteableServlet</code>).
-        * @return This object (for method chaining).
-        */
-       public RestClientBuilder remoteableServletUri(String 
remoteableServletUri) {
-               this.remoteableServletUri = remoteableServletUri;
-               return this;
-       }
-
-       /**
         * Set a root URL for this client.
         * <p>
         * When set, URL strings passed in through the various rest call 
methods (e.g. {@link RestClient#doGet(Object)}
@@ -1064,6 +1051,31 @@ public class RestClientBuilder extends CoreObjectBuilder 
{
                return header("No-Trace", true);
        }
 
+       /**
+        * Sets the {@link UrlEncodingSerializerContext#URLENC_paramFormat} 
property on the URL-encoding serializers in this group.
+        * <p>
+        * This overrides the behavior of the URL-encoding serializer to quote 
and escape characters
+        * in query names and values that may be confused for UON notation 
(e.g. <js>"'(foo=123)'"</js>, <js>"'@(1,2,3)'"</js>).
+        * <p>
+        *
+        * @param value The new value for this property.
+        * @return This object (for method chaining).
+        * @see UrlEncodingSerializerContext#URLENC_paramFormat
+        */
+       public RestClientBuilder paramFormat(String value) {
+               super.property(UrlEncodingSerializerContext.URLENC_paramFormat, 
value);
+               return this;
+       }
+
+       /**
+        * Shortcut for calling <code>paramFormat(<js>"PLAINTEXT"</js>)</code>.
+        *
+        * @return This object (for method chaining).
+        */
+       public RestClientBuilder plainTextParams() {
+               super.property(UrlEncodingSerializerContext.URLENC_paramFormat, 
"PLAINTEXT");
+               return this;
+       }
 
        @Override /* CoreObjectBuilder */
        public RestClientBuilder beansRequireDefaultConstructor(boolean value) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequestEntity.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequestEntity.java
 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequestEntity.java
index 712c043..96c10e6 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequestEntity.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequestEntity.java
@@ -54,11 +54,13 @@ public final class RestRequestEntity extends 
BasicHttpEntity {
                                } else if (! serializer.isWriterSerializer()) {
                                        OutputStreamSerializer s2 = 
(OutputStreamSerializer)serializer;
                                        s2.serialize(output, os);
+                                       os.flush();
                                        os.close();
                                } else {
                                        Writer w = new OutputStreamWriter(os, 
IOUtils.UTF8);
                                        WriterSerializer s2 = 
(WriterSerializer)serializer;
                                        s2.serialize(output, w);
+                                       w.flush();
                                        w.close();
                                }
                        } catch (SerializeException e) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RetryOn.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RetryOn.java 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RetryOn.java
index 6a1c542..6d225b0 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RetryOn.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RetryOn.java
@@ -12,10 +12,16 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.rest.client;
 
+import org.apache.http.*;
+
 /**
  * Used to determine whether a request should be retried based on the HTTP 
response code.
+ * <p>
+ * Subclasses should override either the {@link #onCode(int)} method (if you 
only care about
+ * the HTTP status code) or {@link #onResponse(HttpResponse)} (if you want 
full access to
+ * the HTTP response object.
  */
-public interface RetryOn {
+public abstract class RetryOn {
 
        /**
         * Default RetryOn that returns <jk>true</jk> of any HTTP response 
&gt;= 400 is received.
@@ -40,8 +46,20 @@ public interface RetryOn {
        /**
         * Subclasses should override this method to determine whether the HTTP 
response is retryable.
         *
-        * @param httpResponseCode The HTTP response code.
+        * @param response The HTTP response object.  May be <jk>null</jk> if a 
connection could not be made.
+        * @return <jk>true</jk> if the specified response code is retryable.
+        */
+       protected boolean onResponse(HttpResponse response) {
+               return onCode(response == null || response.getStatusLine() == 
null ? -1 : response.getStatusLine().getStatusCode());
+       }
+
+       /**
+        * Subclasses should override this method to determine whether the HTTP 
response is retryable.
+        *
+        * @param httpResponseCode The HTTP response code.  <code>-1</code> if 
a connection could not be made.
         * @return <jk>true</jk> if the specified response code is retryable.
         */
-       boolean onCode(int httpResponseCode);
+       protected boolean onCode(int httpResponseCode) {
+               return false;
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
index f90c393..11382a6 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/SerializedNameValuePair.java
@@ -12,13 +12,7 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.rest.client;
 
-import static org.apache.juneau.uon.UonSerializerContext.*;
-
-import java.io.*;
-
 import org.apache.http.*;
-import org.apache.juneau.*;
-import org.apache.juneau.uon.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -38,9 +32,6 @@ public final class SerializedNameValuePair implements 
NameValuePair {
        private Object value;
        private UrlEncodingSerializer serializer;
 
-       // We must be sure to disable character encoding since it's done in the 
http client layer.
-       private static final ObjectMap op = new 
ObjectMap().append(UON_encodeChars, false);
-
        /**
         * Constructor.
         *
@@ -56,29 +47,11 @@ public final class SerializedNameValuePair implements 
NameValuePair {
 
        @Override /* NameValuePair */
        public String getName() {
-               if (name != null && name.length() > 0) {
-                       char c = name.charAt(0);
-                       if (c == '$' || c == '(') {
-                               try {
-                                       UonSerializerSession s = 
serializer.createSession(new StringWriter(), op, null, null, null, 
MediaType.UON);
-                                       serializer.serialize(s, name);
-                                       return s.getWriter().toString();
-                               } catch (Exception e) {
-                                       throw new RuntimeException(e);
-                               }
-                       }
-               }
-               return name;
+               return serializer.serializePart(name, false, null);
        }
 
        @Override /* NameValuePair */
        public String getValue() {
-               try {
-                       UonSerializerSession s = serializer.createSession(new 
StringWriter(), op, null, null, null, MediaType.UON);
-                       serializer.serialize(s, value);
-                       return s.getWriter().toString();
-               } catch (Exception e) {
-                       throw new RuntimeException(e);
-               }
+               return serializer.serializePart(value, false, null);
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/package.html
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/package.html 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/package.html
index 5bc9368..0f685ef 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/package.html
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/package.html
@@ -821,8 +821,6 @@
                </p>
                <p>
                        Proxy interfaces are retrieved using the {@link 
org.apache.juneau.rest.client.RestClient#getRemoteableProxy(Class)} method.
-                       The {@link 
org.apache.juneau.rest.client.RestClientBuilder#remoteableServletUri(String)} 
method is used to specify the location
-                               of the remoteable services servlet running on 
the server.
                        The remoteable servlet is a specialized subclass of 
{@link org.apache.juneau.rest.RestServlet} that provides a full-blown
                                REST interface for calling interfaces remotely. 
                </p>
@@ -840,7 +838,7 @@
                <p class='bcode'>
        <jc>// Create a RestClient using JSON for serialization, and point to 
the server-side remoteable servlet.</jc>
        RestClient client = <jk>new</jk> RestClientBuilder()
-               
.remoteableServletUri(<js>"https://localhost:9080/juneau/sample/remoteable";</js>)
+               
.rootUrl(<js>"https://localhost:9080/juneau/sample/remoteable";</js>)
                .build();
        
        <jc>// Create a proxy interface.</jc>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
new file mode 100644
index 0000000..3bced3f
--- /dev/null
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/FormDataResource.java
@@ -0,0 +1,37 @@
+// 
***************************************************************************************************************************
+// * 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.rest.test;
+
+import java.io.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+
+/**
+ * JUnit automated testcase resource.
+ */
+@RestResource(
+       path="/testFormData"
+)
+public class FormDataResource extends RestServletDefault {
+       private static final long serialVersionUID = 1L;
+
+       
//====================================================================================================
+       // Basic tests
+       
//====================================================================================================
+       @RestMethod(name="POST", path="/*")
+       public Reader test(RestRequest req) throws IOException {
+               return new 
StringReader("Content-Type=["+req.getContentType()+"], 
contents=["+IOUtils.read(req.getReader())+"]");
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxy.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxy.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxy.java
index 9f8da38..b2af7a6 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxy.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxy.java
@@ -14,15 +14,13 @@ package org.apache.juneau.rest.test;
 
 import java.util.*;
 
-import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.transform.*;
+import org.apache.juneau.remoteable.*;
+import org.apache.juneau.rest.test.pojos.*;
 
 /**
  * Interface proxy exposed in InterfaceProxyResource and tested in 
InterfaceProxyTest.
  */
+@Remoteable
 public interface InterfaceProxy {
 
        public static final String SWAP = 
"swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/";
@@ -50,14 +48,14 @@ public interface InterfaceProxy {
        List<String> returnStringList();
 
        // Beans
-       Bean returnBean();
-       Bean[][][] returnBean3dArray();
-       List<Bean> returnBeanList();
-       List<Bean[][][]> returnBean1d3dList();
-       Map<String,Bean> returnBeanMap();
-       Map<String,List<Bean>> returnBeanListMap();
-       Map<String,List<Bean[][][]>> returnBean1d3dListMap();
-       Map<Integer,List<Bean>> returnBeanListMapIntegerKeys();
+       ABean returnBean();
+       ABean[][][] returnBean3dArray();
+       List<ABean> returnBeanList();
+       List<ABean[][][]> returnBean1d3dList();
+       Map<String,ABean> returnBeanMap();
+       Map<String,List<ABean>> returnBeanListMap();
+       Map<String,List<ABean[][][]>> returnBean1d3dListMap();
+       Map<Integer,List<ABean>> returnBeanListMapIntegerKeys();
 
        // Typed beans
        TypedBean returnTypedBean();
@@ -121,14 +119,14 @@ public interface InterfaceProxy {
        void setStringList(List<String> x);
 
        // Beans
-       void setBean(Bean x);
-       void setBean3dArray(Bean[][][] x);
-       void setBeanList(List<Bean> x);
-       void setBean1d3dList(List<Bean[][][]> x);
-       void setBeanMap(Map<String,Bean> x);
-       void setBeanListMap(Map<String,List<Bean>> x);
-       void setBean1d3dListMap(Map<String,List<Bean[][][]>> x);
-       void setBeanListMapIntegerKeys(Map<Integer,List<Bean>> x);
+       void setBean(ABean x);
+       void setBean3dArray(ABean[][][] x);
+       void setBeanList(List<ABean> x);
+       void setBean1d3dList(List<ABean[][][]> x);
+       void setBeanMap(Map<String,ABean> x);
+       void setBeanListMap(Map<String,List<ABean>> x);
+       void setBean1d3dListMap(Map<String,List<ABean[][][]>> x);
+       void setBeanListMapIntegerKeys(Map<Integer,List<ABean>> x);
 
        // Typed beans
        void setTypedBean(TypedBean x);
@@ -171,7 +169,7 @@ public interface InterfaceProxy {
        void setMultiParamsFloat(float x1, float[][][] x2, float[][][] x2n, 
List<float[][][]> x3, List<float[][][]> x3n);
        void setMultiParamsFloatObject(Float x1, Float x1n, Float[][][] x2, 
Float[][][] x2n, List<Float[][][]> x3, List<Float[][][]> x3n);
        void setMultiParamsString(String x1, String[][][] x2, String[][][] x2n, 
List<String[][][]> x3, List<String[][][]> x3n);
-       void setMultiParamsBean(Bean x1, Bean[][][] x2, Bean[][][] x2n, 
List<Bean[][][]> x3, List<Bean[][][]> x3n, Map<String,Bean> x4, 
Map<String,Bean> x4n, Map<String,List<Bean[][][]>> x5, 
Map<String,List<Bean[][][]>> x5n);
+       void setMultiParamsBean(ABean x1, ABean[][][] x2, ABean[][][] x2n, 
List<ABean[][][]> x3, List<ABean[][][]> x3n, Map<String,ABean> x4, 
Map<String,ABean> x4n, Map<String,List<ABean[][][]>> x5, 
Map<String,List<ABean[][][]>> x5n);
        void setMultiParamsSwappedPojo(SwappedPojo x1, SwappedPojo[][][] x2, 
SwappedPojo[][][] x2n, List<SwappedPojo[][][]> x3, List<SwappedPojo[][][]> x3n, 
Map<SwappedPojo,SwappedPojo> x4, Map<SwappedPojo,SwappedPojo> x4n, 
Map<SwappedPojo,List<SwappedPojo[][][]>> x5, 
Map<SwappedPojo,List<SwappedPojo[][][]>> x5n);
        void setMultiParamsImplicitSwappedPojo(ImplicitSwappedPojo x1, 
ImplicitSwappedPojo[][][] x2, ImplicitSwappedPojo[][][] x2n, 
List<ImplicitSwappedPojo[][][]> x3, List<ImplicitSwappedPojo[][][]> x3n, 
Map<ImplicitSwappedPojo,ImplicitSwappedPojo> x4, 
Map<ImplicitSwappedPojo,ImplicitSwappedPojo> x4n, 
Map<ImplicitSwappedPojo,List<ImplicitSwappedPojo[][][]>> x5, 
Map<ImplicitSwappedPojo,List<ImplicitSwappedPojo[][][]>> x5n);
        void setMultiParamsEnum(TestEnum x1, TestEnum[][][] x2, TestEnum[][][] 
x2n, List<TestEnum[][][]> x3, List<TestEnum[][][]> x3n, Map<TestEnum,TestEnum> 
x4, Map<TestEnum,TestEnum> x4n, Map<TestEnum,List<TestEnum[][][]>> x5, 
Map<TestEnum,List<TestEnum[][][]>> x5n);
@@ -180,17 +178,6 @@ public interface InterfaceProxy {
        // Helper classes
        
//--------------------------------------------------------------------------------
 
-       public static class Bean {
-               public int a;
-               public String b;
-
-               Bean init() {
-                       this.a = 1;
-                       this.b = "foo";
-                       return this;
-               }
-       }
-
        @SuppressWarnings("serial")
        public static class InterfaceProxyException1 extends Throwable {
                public InterfaceProxyException1(String msg) {
@@ -201,59 +188,4 @@ public interface InterfaceProxy {
        @SuppressWarnings("serial")
        public static class InterfaceProxyException2 extends Throwable {
        }
-
-       @Pojo(swap=SwappedPojoSwap.class)
-       public static class SwappedPojo {
-               public boolean wasUnswapped;
-       }
-
-       public static class SwappedPojoSwap extends 
PojoSwap<SwappedPojo,String> {
-               @Override
-               public String swap(BeanSession session, SwappedPojo c) throws 
SerializeException {
-                       return SWAP;
-               }
-
-               @Override
-               public SwappedPojo unswap(BeanSession session, String f, 
ClassMeta<?> hint) throws ParseException {
-                       SwappedPojo c = new SwappedPojo();
-                       if (f.equals(SWAP))
-                               c.wasUnswapped = true;
-                       return c;
-               }
-       }
-
-       @BeanIgnore
-       public static class ImplicitSwappedPojo {
-               public boolean wasUnswapped;
-               @Override
-               public String toString() {
-                       return SWAP;
-               }
-               public ImplicitSwappedPojo() {
-               }
-               public ImplicitSwappedPojo(String fromString) {
-                       if (fromString.equals(SWAP))
-                               wasUnswapped = true;
-               }
-       }
-
-       public static enum TestEnum {
-               ONE,TWO,THREE
-       }
-
-       @org.apache.juneau.annotation.Bean(beanDictionary={TypedBeanImpl.class})
-       public static interface TypedBean {
-       }
-
-       @org.apache.juneau.annotation.Bean(typeName="TypedBeanImpl", sort=true)
-       public static class TypedBeanImpl implements TypedBean {
-               public int a;
-               public String b;
-
-               public TypedBeanImpl init() {
-                       this.a = 1;
-                       this.b = "foo";
-                       return this;
-               }
-       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxyResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxyResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxyResource.java
index a5ade59..7b63d6f 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxyResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/InterfaceProxyResource.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest.test;
 
 import static java.util.Arrays.*;
+import static org.apache.juneau.rest.test.TestUtils.*;
 import static org.junit.Assert.*;
 
 import java.util.*;
@@ -20,6 +21,7 @@ import java.util.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.jena.*;
+import org.apache.juneau.rest.test.pojos.*;
 import org.apache.juneau.utils.*;
 import org.junit.*;
 
@@ -43,53 +45,66 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                        
//--------------------------------------------------------------------------------
 
                        // Various primitives
+
                        @Override
                        public void returnVoid() {
                        }
+
                        @Override
                        public Integer returnInteger() {
                                return 1;
                        }
+
                        @Override
                        public int returnInt() {
                                return 1;
                        }
+
                        @Override
                        public boolean returnBoolean() {
                                return true;
                        }
+
                        @Override
                        public float returnFloat() {
                                return 1f;
                        }
+
                        @Override
                        public Float returnFloatObject() {
                                return 1f;
                        }
+
                        @Override
                        public String returnString() {
                                return "foobar";
                        }
+
                        @Override
                        public String returnNullString() {
                                return null;
                        }
+
                        @Override
                        public int[][][] returnInt3dArray() {
                                return new int[][][]{{{1,2},null},null};
                        }
+
                        @Override
                        public Integer[][][] returnInteger3dArray() {
                                return new Integer[][][]{{{1,null},null},null};
                        }
+
                        @Override
                        public String[][][] returnString3dArray() {
                                return new 
String[][][]{{{"foo","bar",null},null},null};
                        }
+
                        @Override
                        public List<Integer> returnIntegerList() {
                                return asList(new Integer[]{1,null});
                        }
+
                        @Override
                        public List<List<List<Integer>>> returnInteger3dList() {
                                return new AList<List<List<Integer>>>()
@@ -102,136 +117,167 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                                )
                                .append(null);
                        }
+
                        @Override
                        public List<Integer[][][]> returnInteger1d3dList() {
                                return new AList<Integer[][][]>().append(new 
Integer[][][]{{{1,null},null},null}).append(null);
                        }
+
                        @Override
                        public List<int[][][]> returnInt1d3dList() {
                                return new AList<int[][][]>().append(new 
int[][][]{{{1,2},null},null}).append(null);
                        }
+
                        @Override
                        public List<String> returnStringList() {
                                return asList(new String[]{"foo","bar",null});
                        }
 
                        // Beans
+
                        @Override
-                       public Bean returnBean() {
-                               return new Bean().init();
+                       public ABean returnBean() {
+                               return new ABean().init();
                        }
+
                        @Override
-                       public Bean[][][] returnBean3dArray() {
-                               return new Bean[][][]{{{new 
Bean().init(),null},null},null};
+                       public ABean[][][] returnBean3dArray() {
+                               return new ABean[][][]{{{new 
ABean().init(),null},null},null};
                        }
+
                        @Override
-                       public List<Bean> returnBeanList() {
-                               return asList(new Bean().init());
+                       public List<ABean> returnBeanList() {
+                               return asList(new ABean().init());
                        }
+
                        @Override
-                       public List<Bean[][][]> returnBean1d3dList() {
-                               return new AList<Bean[][][]>().append(new 
Bean[][][]{{{new Bean().init(),null},null},null}).append(null);
+                       public List<ABean[][][]> returnBean1d3dList() {
+                               return new AList<ABean[][][]>().append(new 
ABean[][][]{{{new ABean().init(),null},null},null}).append(null);
                        }
+
                        @Override
-                       public Map<String,Bean> returnBeanMap() {
-                               return new AMap<String,Bean>().append("foo",new 
Bean().init());
+                       public Map<String,ABean> returnBeanMap() {
+                               return new 
AMap<String,ABean>().append("foo",new ABean().init());
                        }
+
                        @Override
-                       public Map<String,List<Bean>> returnBeanListMap() {
-                               return new 
AMap<String,List<Bean>>().append("foo",asList(new Bean().init()));
+                       public Map<String,List<ABean>> returnBeanListMap() {
+                               return new 
AMap<String,List<ABean>>().append("foo",asList(new ABean().init()));
                        }
+
                        @Override
-                       public Map<String,List<Bean[][][]>> 
returnBean1d3dListMap() {
-                               return new 
AMap<String,List<Bean[][][]>>().append("foo", new 
AList<Bean[][][]>().append(new Bean[][][]{{{new 
Bean().init(),null},null},null}).append(null));
+                       public Map<String,List<ABean[][][]>> 
returnBean1d3dListMap() {
+                               return new 
AMap<String,List<ABean[][][]>>().append("foo", new 
AList<ABean[][][]>().append(new ABean[][][]{{{new 
ABean().init(),null},null},null}).append(null));
                        }
+
                        @Override
-                       public Map<Integer,List<Bean>> 
returnBeanListMapIntegerKeys() {
-                               return new 
AMap<Integer,List<Bean>>().append(1,asList(new Bean().init()));
+                       public Map<Integer,List<ABean>> 
returnBeanListMapIntegerKeys() {
+                               return new 
AMap<Integer,List<ABean>>().append(1,asList(new ABean().init()));
                        }
 
                        // Typed beans
+
                        @Override
                        public TypedBean returnTypedBean() {
                                return new TypedBeanImpl().init();
                        }
+
                        @Override
                        public TypedBean[][][] returnTypedBean3dArray() {
                                return new TypedBean[][][]{{{new 
TypedBeanImpl().init(),null},null},null};
                        }
+
                        @Override
                        public List<TypedBean> returnTypedBeanList() {
                                return asList((TypedBean)new 
TypedBeanImpl().init());
                        }
+
                        @Override
                        public List<TypedBean[][][]> returnTypedBean1d3dList() {
                                return new AList<TypedBean[][][]>().append(new 
TypedBean[][][]{{{new TypedBeanImpl().init(),null},null},null}).append(null);
                        }
+
                        @Override
                        public Map<String,TypedBean> returnTypedBeanMap() {
                                return new 
AMap<String,TypedBean>().append("foo",new TypedBeanImpl().init());
                        }
+
                        @Override
                        public Map<String,List<TypedBean>> 
returnTypedBeanListMap() {
                                return new 
AMap<String,List<TypedBean>>().append("foo",asList((TypedBean)new 
TypedBeanImpl().init()));
                        }
+
                        @Override
                        public Map<String,List<TypedBean[][][]>> 
returnTypedBean1d3dListMap() {
                                return new 
AMap<String,List<TypedBean[][][]>>().append("foo", new 
AList<TypedBean[][][]>().append(new TypedBean[][][]{{{new 
TypedBeanImpl().init(),null},null},null}).append(null));
                        }
+
                        @Override
                        public Map<Integer,List<TypedBean>> 
returnTypedBeanListMapIntegerKeys() {
                                return new 
AMap<Integer,List<TypedBean>>().append(1,asList((TypedBean)new 
TypedBeanImpl().init()));
                        }
 
                        // Swapped POJOs
+
                        @Override
                        public SwappedPojo returnSwappedPojo() {
                                return new SwappedPojo();
                        }
+
                        @Override
                        public SwappedPojo[][][] returnSwappedPojo3dArray() {
                                return new SwappedPojo[][][]{{{new 
SwappedPojo(),null},null},null};
                        }
+
                        @Override
                        public Map<SwappedPojo,SwappedPojo> 
returnSwappedPojoMap() {
                                return new 
AMap<SwappedPojo,SwappedPojo>().append(new SwappedPojo(), new SwappedPojo());
                        }
+
                        @Override
                        public Map<SwappedPojo,SwappedPojo[][][]> 
returnSwappedPojo3dMap() {
                                return new 
AMap<SwappedPojo,SwappedPojo[][][]>().append(new SwappedPojo(), new 
SwappedPojo[][][]{{{new SwappedPojo(),null},null},null});
                        }
 
                        // Implicit swapped POJOs
+
                        @Override
                        public ImplicitSwappedPojo returnImplicitSwappedPojo() {
                                return new ImplicitSwappedPojo();
                        }
+
                        @Override
                        public ImplicitSwappedPojo[][][] 
returnImplicitSwappedPojo3dArray() {
                                return new ImplicitSwappedPojo[][][]{{{new 
ImplicitSwappedPojo(),null},null},null};
                        }
+
                        @Override
                        public Map<ImplicitSwappedPojo,ImplicitSwappedPojo> 
returnImplicitSwappedPojoMap() {
                                return new 
AMap<ImplicitSwappedPojo,ImplicitSwappedPojo>().append(new 
ImplicitSwappedPojo(), new ImplicitSwappedPojo());
                        }
+
                        @Override
                        public 
Map<ImplicitSwappedPojo,ImplicitSwappedPojo[][][]> 
returnImplicitSwappedPojo3dMap() {
                                return new 
AMap<ImplicitSwappedPojo,ImplicitSwappedPojo[][][]>().append(new 
ImplicitSwappedPojo(), new ImplicitSwappedPojo[][][]{{{new 
ImplicitSwappedPojo(),null},null},null});
                        }
 
                        // Enums
+
                        @Override
                        public TestEnum returnEnum() {
                                return TestEnum.TWO;
                        }
+
                        @Override
                        public TestEnum[][][] returnEnum3d() {
                                return new 
TestEnum[][][]{{{TestEnum.TWO,null},null},null};
                        }
+
                        @Override
                        public List<TestEnum> returnEnumList() {
                                return new 
AList<TestEnum>().append(TestEnum.TWO).append(null);
                        }
+
                        @Override
                        public List<List<List<TestEnum>>> returnEnum3dList() {
                                return new AList<List<List<TestEnum>>>()
@@ -244,18 +290,22 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                                .append(null)
                                );
                        }
+
                        @Override
                        public List<TestEnum[][][]> returnEnum1d3dList() {
                                return new AList<TestEnum[][][]>().append(new 
TestEnum[][][]{{{TestEnum.TWO,null},null},null}).append(null);
                        }
+
                        @Override
                        public Map<TestEnum,TestEnum> returnEnumMap() {
                                return new 
AMap<TestEnum,TestEnum>().append(TestEnum.ONE,TestEnum.TWO);
                        }
+
                        @Override
                        public Map<TestEnum,TestEnum[][][]> 
returnEnum3dArrayMap() {
                                return new 
AMap<TestEnum,TestEnum[][][]>().append(TestEnum.ONE, new 
TestEnum[][][]{{{TestEnum.TWO,null},null},null});
                        }
+
                        @Override
                        public Map<TestEnum,List<TestEnum[][][]>> 
returnEnum1d3dListMap() {
                                return new 
AMap<TestEnum,List<TestEnum[][][]>>().append(TestEnum.ONE, new 
AList<TestEnum[][][]>().append(new 
TestEnum[][][]{{{TestEnum.TWO,null},null},null}).append(null));
@@ -269,6 +319,7 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                        public void throwException1() throws 
InterfaceProxy.InterfaceProxyException1 {
                                throw new 
InterfaceProxy.InterfaceProxyException1("foo");
                        }
+
                        @Override
                        public void throwException2() throws 
InterfaceProxy.InterfaceProxyException2 {
                                throw new 
InterfaceProxy.InterfaceProxyException2();
@@ -279,162 +330,197 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                        
//--------------------------------------------------------------------------------
 
                        // Various primitives
+
                        @Override
                        public void setNothing() {
                        }
+
                        @Override
                        public void setInt(int x) {
                                assertEquals(1, x);
                        }
+
                        @Override
                        public void setInteger(Integer x) {
                                assertEquals((Integer)1, x);
                        }
+
                        @Override
                        public void setBoolean(boolean x) {
                                assertTrue(x);
                        }
+
                        @Override
                        public void setFloat(float x) {
                                assertTrue(1f == x);
                        }
+
                        @Override
                        public void setFloatObject(Float x) {
                                assertTrue(1f == x);
                        }
+
                        @Override
                        public void setString(String x) {
                                assertEquals("foo", x);
                        }
+
                        @Override
                        public void setNullString(String x) {
                                assertNull(x);
                        }
+
                        @Override
                        public void setInt3dArray(int[][][] x) {
                                assertObjectEquals("[[[1,2],null],null]", x);
                        }
+
                        @Override
                        public void setInteger3dArray(Integer[][][] x) {
                                assertObjectEquals("[[[1,null],null],null]", x);
                        }
+
                        @Override
                        public void setString3dArray(String[][][] x) {
                                
assertObjectEquals("[[['foo',null],null],null]", x);
                        }
+
                        @Override
                        public void setIntegerList(List<Integer> x) {
                                assertObjectEquals("[1,null]", x);
-                               assertEquals(Integer.class, 
x.get(0).getClass());
+                               assertClass(Integer.class, x.get(0));
                        }
+
                        @Override
                        public void setInteger3dList(List<List<List<Integer>>> 
x) {
                                assertObjectEquals("[[[1,null],null],null]", x);
-                               assertEquals(Integer.class, 
x.get(0).get(0).get(0).getClass());
+                               assertClass(Integer.class, 
x.get(0).get(0).get(0));
                        }
+
                        @Override
                        public void setInteger1d3dList(List<Integer[][][]> x) {
                                
assertObjectEquals("[[[[1,null],null],null],null]", x);
-                               assertEquals(Integer[][][].class, 
x.get(0).getClass());
-                               assertEquals(Integer.class, 
x.get(0)[0][0][0].getClass());
+                               assertClass(Integer[][][].class, x.get(0));
+                               assertClass(Integer.class, x.get(0)[0][0][0]);
                        }
+
                        @Override
                        public void setInt1d3dList(List<int[][][]> x) {
                                
assertObjectEquals("[[[[1,2],null],null],null]", x);
-                               assertEquals(int[][][].class, 
x.get(0).getClass());
+                               assertClass(int[][][].class, x.get(0));
                        }
+
                        @Override
                        public void setStringList(List<String> x) {
                                assertObjectEquals("['foo','bar',null]", x);
                        }
 
                        // Beans
+
                        @Override
-                       public void setBean(Bean x) {
+                       public void setBean(ABean x) {
                                assertObjectEquals("{a:1,b:'foo'}", x);
                        }
+
                        @Override
-                       public void setBean3dArray(Bean[][][] x) {
+                       public void setBean3dArray(ABean[][][] x) {
                                
assertObjectEquals("[[[{a:1,b:'foo'},null],null],null]", x);
                        }
+
                        @Override
-                       public void setBeanList(List<Bean> x) {
+                       public void setBeanList(List<ABean> x) {
                                assertObjectEquals("[{a:1,b:'foo'}]", x);
                        }
+
                        @Override
-                       public void setBean1d3dList(List<Bean[][][]> x) {
+                       public void setBean1d3dList(List<ABean[][][]> x) {
                                
assertObjectEquals("[[[[{a:1,b:'foo'},null],null],null],null]", x);
                        }
+
                        @Override
-                       public void setBeanMap(Map<String,Bean> x) {
+                       public void setBeanMap(Map<String,ABean> x) {
                                assertObjectEquals("{foo:{a:1,b:'foo'}}", x);
                        }
+
                        @Override
-                       public void setBeanListMap(Map<String,List<Bean>> x) {
+                       public void setBeanListMap(Map<String,List<ABean>> x) {
                                assertObjectEquals("{foo:[{a:1,b:'foo'}]}", x);
                        }
+
                        @Override
-                       public void 
setBean1d3dListMap(Map<String,List<Bean[][][]>> x) {
+                       public void 
setBean1d3dListMap(Map<String,List<ABean[][][]>> x) {
                                
assertObjectEquals("{foo:[[[[{a:1,b:'foo'},null],null],null],null]}", x);
                        }
+
                        @Override
-                       public void 
setBeanListMapIntegerKeys(Map<Integer,List<Bean>> x) {
+                       public void 
setBeanListMapIntegerKeys(Map<Integer,List<ABean>> x) {
                                assertObjectEquals("{'1':[{a:1,b:'foo'}]}", x); 
 // Note: JsonSerializer serializes key as string.
-                               assertEquals(Integer.class, 
x.keySet().iterator().next().getClass());
+                               assertClass(Integer.class, 
x.keySet().iterator().next());
                        }
 
                        // Typed beans
+
                        @Override
                        public void setTypedBean(TypedBean x) {
                                
assertObjectEquals("{_type:'TypedBeanImpl',a:1,b:'foo'}", x);
-                               assertEquals(TypedBeanImpl.class, x.getClass());
+                               assertClass(TypedBeanImpl.class, x);
                        }
+
                        @Override
                        public void setTypedBean3dArray(TypedBean[][][] x) {
                                
assertObjectEquals("[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null]", 
x);
-                               assertEquals(TypedBeanImpl.class, 
x[0][0][0].getClass());
+                               assertClass(TypedBeanImpl.class, x[0][0][0]);
                        }
+
                        @Override
                        public void setTypedBeanList(List<TypedBean> x) {
                                
assertObjectEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", x);
-                               assertEquals(TypedBeanImpl.class, 
x.get(0).getClass());
+                               assertClass(TypedBeanImpl.class, x.get(0));
                        }
+
                        @Override
                        public void setTypedBean1d3dList(List<TypedBean[][][]> 
x) {
                                
assertObjectEquals("[[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null],null]",
 x);
-                               assertEquals(TypedBeanImpl.class, 
x.get(0)[0][0][0].getClass());
+                               assertClass(TypedBeanImpl.class, 
x.get(0)[0][0][0]);
                        }
+
                        @Override
                        public void setTypedBeanMap(Map<String,TypedBean> x) {
                                
assertObjectEquals("{foo:{_type:'TypedBeanImpl',a:1,b:'foo'}}", x);
-                               assertEquals(TypedBeanImpl.class, 
x.get("foo").getClass());
+                               assertClass(TypedBeanImpl.class, x.get("foo"));
                        }
+
                        @Override
                        public void 
setTypedBeanListMap(Map<String,List<TypedBean>> x) {
                                
assertObjectEquals("{foo:[{_type:'TypedBeanImpl',a:1,b:'foo'}]}", x);
-                               assertEquals(TypedBeanImpl.class, 
x.get("foo").get(0).getClass());
+                               assertClass(TypedBeanImpl.class, 
x.get("foo").get(0));
                        }
+
                        @Override
                        public void 
setTypedBean1d3dListMap(Map<String,List<TypedBean[][][]>> x) {
                                
assertObjectEquals("{foo:[[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null],null]}",
 x);
-                               assertEquals(TypedBeanImpl.class, 
x.get("foo").get(0)[0][0][0].getClass());
+                               assertClass(TypedBeanImpl.class, 
x.get("foo").get(0)[0][0][0]);
                        }
+
                        @Override
                        public void 
setTypedBeanListMapIntegerKeys(Map<Integer,List<TypedBean>> x) {
                                
assertObjectEquals("{'1':[{_type:'TypedBeanImpl',a:1,b:'foo'}]}", x);  // Note: 
JsonSerializer serializes key as string.
-                               assertEquals(TypedBeanImpl.class, 
x.get(1).get(0).getClass());
+                               assertClass(TypedBeanImpl.class, 
x.get(1).get(0));
                        }
 
                        // Swapped POJOs
+
                        @Override
                        public void setSwappedPojo(SwappedPojo x) {
                                assertTrue(x.wasUnswapped);
                        }
+
                        @Override
                        public void setSwappedPojo3dArray(SwappedPojo[][][] x) {
                                
assertObjectEquals("[[['"+SWAP+"',null],null],null]", x);
                                assertTrue(x[0][0][0].wasUnswapped);
                        }
+
                        @Override
                        public void 
setSwappedPojoMap(Map<SwappedPojo,SwappedPojo> x) {
                                assertObjectEquals("{'"+SWAP+"':'"+SWAP+"'}", 
x);
@@ -442,6 +528,7 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                                assertTrue(e.getKey().wasUnswapped);
                                assertTrue(e.getValue().wasUnswapped);
                        }
+
                        @Override
                        public void 
setSwappedPojo3dMap(Map<SwappedPojo,SwappedPojo[][][]> x) {
                                
assertObjectEquals("{'"+SWAP+"':[[['"+SWAP+"',null],null],null]}", x);
@@ -451,15 +538,18 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                        }
 
                        // Implicit swapped POJOs
+
                        @Override
                        public void setImplicitSwappedPojo(ImplicitSwappedPojo 
x) {
                                assertTrue(x.wasUnswapped);
                        }
+
                        @Override
                        public void 
setImplicitSwappedPojo3dArray(ImplicitSwappedPojo[][][] x) {
                                
assertObjectEquals("[[['"+SWAP+"',null],null],null]", x);
                                assertTrue(x[0][0][0].wasUnswapped);
                        }
+
                        @Override
                        public void 
setImplicitSwappedPojoMap(Map<ImplicitSwappedPojo,ImplicitSwappedPojo> x) {
                                assertObjectEquals("{'"+SWAP+"':'"+SWAP+"'}", 
x);
@@ -467,6 +557,7 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                                assertTrue(e.getKey().wasUnswapped);
                                assertTrue(e.getValue().wasUnswapped);
                        }
+
                        @Override
                        public void 
setImplicitSwappedPojo3dMap(Map<ImplicitSwappedPojo,ImplicitSwappedPojo[][][]> 
x) {
                                
assertObjectEquals("{'"+SWAP+"':[[['"+SWAP+"',null],null],null]}", x);
@@ -476,49 +567,57 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                        }
 
                        // Enums
+
                        @Override
                        public void setEnum(TestEnum x) {
                                assertEquals(TestEnum.TWO, x);
                        }
+
                        @Override
                        public void setEnum3d(TestEnum[][][] x) {
                                
assertObjectEquals("[[['TWO',null],null],null]", x);
                        }
+
                        @Override
                        public void setEnumList(List<TestEnum> x) {
                                assertObjectEquals("['TWO',null]", x);
-                               assertEquals(TestEnum.class, 
x.get(0).getClass());
+                               assertClass(TestEnum.class, x.get(0));
                        }
+
                        @Override
                        public void setEnum3dList(List<List<List<TestEnum>>> x) 
{
                                
assertObjectEquals("[[['TWO',null],null,null]]", x);
-                               assertEquals(TestEnum.class, 
x.get(0).get(0).get(0).getClass());
+                               assertClass(TestEnum.class, 
x.get(0).get(0).get(0));
                        }
+
                        @Override
                        public void setEnum1d3dList(List<TestEnum[][][]> x) {
                                
assertObjectEquals("[[[['TWO',null],null],null],null]", x);
-                               assertEquals(TestEnum[][][].class, 
x.get(0).getClass());
+                               assertClass(TestEnum[][][].class, x.get(0));
                        }
+
                        @Override
                        public void setEnumMap(Map<TestEnum,TestEnum> x) {
                                assertObjectEquals("{ONE:'TWO'}", x);
                                Map.Entry<TestEnum,TestEnum> e = 
x.entrySet().iterator().next();
-                               assertEquals(TestEnum.class, 
e.getKey().getClass());
-                               assertEquals(TestEnum.class, 
e.getValue().getClass());
+                               assertClass(TestEnum.class, e.getKey());
+                               assertClass(TestEnum.class, e.getValue());
                        }
+
                        @Override
                        public void 
setEnum3dArrayMap(Map<TestEnum,TestEnum[][][]> x) {
                                
assertObjectEquals("{ONE:[[['TWO',null],null],null]}", x);
                                Map.Entry<TestEnum,TestEnum[][][]> e = 
x.entrySet().iterator().next();
-                               assertEquals(TestEnum.class, 
e.getKey().getClass());
-                               assertEquals(TestEnum[][][].class, 
e.getValue().getClass());
+                               assertClass(TestEnum.class, e.getKey());
+                               assertClass(TestEnum[][][].class, e.getValue());
                        }
+
                        @Override
                        public void 
setEnum1d3dListMap(Map<TestEnum,List<TestEnum[][][]>> x) {
                                
assertObjectEquals("{ONE:[[[['TWO',null],null],null],null]}", x);
                                Map.Entry<TestEnum,List<TestEnum[][][]>> e = 
x.entrySet().iterator().next();
-                               assertEquals(TestEnum.class, 
e.getKey().getClass());
-                               assertEquals(TestEnum[][][].class, 
e.getValue().get(0).getClass());
+                               assertClass(TestEnum.class, e.getKey());
+                               assertClass(TestEnum[][][].class, 
e.getValue().get(0));
                        }
 
                        
//--------------------------------------------------------------------------------
@@ -531,91 +630,99 @@ public class InterfaceProxyResource extends 
RestServletJenaDefault {
                                assertObjectEquals("[[[1,2],null],null]", x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[[1,2],null],null],null]", x3);
-                               assertEquals(int[][][].class, 
x3.get(0).getClass());
+                               assertClass(int[][][].class, x3.get(0));
                                assertNull(x3n);
                        }
+
                        @Override
                        public void setMultiParamsInteger(Integer x1, Integer 
x1n, Integer[][][] x2, Integer[][][] x2n, List<Integer[][][]> x3, 
List<Integer[][][]> x3n) {
                                assertObjectEquals("1", x1);
                                assertObjectEquals("[[[1,null],null],null]", 
x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[[1,null],null],null],null]", x3);
-                               assertEquals(Integer[][][].class, 
x3.get(0).getClass());
+                               assertClass(Integer[][][].class, x3.get(0));
                                assertNull(x3n);
                        }
+
                        @Override
                        public void setMultiParamsFloat(float x1, float[][][] 
x2, float[][][] x2n, List<float[][][]> x3, List<float[][][]> x3n) {
                                assertObjectEquals("1.0", x1);
                                assertObjectEquals("[[[1.0,2.0],null],null]", 
x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[[1.0,2.0],null],null],null]", x3);
-                               assertEquals(float[][][].class, 
x3.get(0).getClass());
+                               assertClass(float[][][].class, x3.get(0));
                                assertNull(x3n);
                        }
+
                        @Override
                        public void setMultiParamsFloatObject(Float x1, Float 
x1n, Float[][][] x2, Float[][][] x2n, List<Float[][][]> x3, List<Float[][][]> 
x3n) {
                                assertObjectEquals("1.0", x1);
                                assertObjectEquals("[[[1.0,null],null],null]", 
x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[[1.0,null],null],null],null]", x3);
-                               assertEquals(Float[][][].class, 
x3.get(0).getClass());
+                               assertClass(Float[][][].class, x3.get(0));
                                assertNull(x3n);
                        }
+
                        @Override
                        public void setMultiParamsString(String x1, 
String[][][] x2, String[][][] x2n, List<String[][][]> x3, List<String[][][]> 
x3n) {
                                assertObjectEquals("'foo'", x1);
                                
assertObjectEquals("[[['foo',null],null],null]", x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[['foo',null],null],null],null]", x3);
-                               assertEquals(String[][][].class, 
x3.get(0).getClass());
+                               assertClass(String[][][].class, x3.get(0));
                                assertNull(x3n);
                        }
+
                        @Override
-                       public void setMultiParamsBean(Bean x1, Bean[][][] x2, 
Bean[][][] x2n, List<Bean[][][]> x3, List<Bean[][][]> x3n, Map<String,Bean> x4, 
Map<String,Bean> x4n, Map<String,List<Bean[][][]>> x5, 
Map<String,List<Bean[][][]>> x5n) {
+                       public void setMultiParamsBean(ABean x1, ABean[][][] 
x2, ABean[][][] x2n, List<ABean[][][]> x3, List<ABean[][][]> x3n, 
Map<String,ABean> x4, Map<String,ABean> x4n, Map<String,List<ABean[][][]>> x5, 
Map<String,List<ABean[][][]>> x5n) {
                                assertObjectEquals("{a:1,b:'foo'}", x1);
                                
assertObjectEquals("[[[{a:1,b:'foo'},null],null],null]", x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[[{a:1,b:'foo'},null],null],null],null]", x3);
-                               assertEquals(Bean[][][].class, 
x3.get(0).getClass());
+                               assertClass(ABean[][][].class, x3.get(0));
                                assertNull(x3n);
                                assertObjectEquals("{foo:{a:1,b:'foo'}}", x4);
                                assertNull(x4n);
                                
assertObjectEquals("{foo:[[[[{a:1,b:'foo'},null],null],null],null]}", x5);
                                assertNull(x5n);
                        }
+
                        @Override
                        public void setMultiParamsSwappedPojo(SwappedPojo x1, 
SwappedPojo[][][] x2, SwappedPojo[][][] x2n, List<SwappedPojo[][][]> x3, 
List<SwappedPojo[][][]> x3n, Map<SwappedPojo,SwappedPojo> x4, 
Map<SwappedPojo,SwappedPojo> x4n, Map<SwappedPojo,List<SwappedPojo[][][]>> x5, 
Map<SwappedPojo,List<SwappedPojo[][][]>> x5n) {
                                assertObjectEquals("'"+SWAP+"'", x1);
                                
assertObjectEquals("[[['"+SWAP+"',null],null],null]", x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[['"+SWAP+"',null],null],null],null]", x3);
-                               assertEquals(SwappedPojo[][][].class, 
x3.get(0).getClass());
+                               assertClass(SwappedPojo[][][].class, x3.get(0));
                                assertNull(x3n);
                                assertObjectEquals("{'"+SWAP+"':'"+SWAP+"'}", 
x4);
                                assertNull(x4n);
                                
assertObjectEquals("{'"+SWAP+"':[[[['"+SWAP+"',null],null],null],null]}", x5);
                                assertNull(x5n);
                        }
+
                        @Override
                        public void 
setMultiParamsImplicitSwappedPojo(ImplicitSwappedPojo x1, 
ImplicitSwappedPojo[][][] x2, ImplicitSwappedPojo[][][] x2n, 
List<ImplicitSwappedPojo[][][]> x3, List<ImplicitSwappedPojo[][][]> x3n, 
Map<ImplicitSwappedPojo,ImplicitSwappedPojo> x4, 
Map<ImplicitSwappedPojo,ImplicitSwappedPojo> x4n, 
Map<ImplicitSwappedPojo,List<ImplicitSwappedPojo[][][]>> x5, 
Map<ImplicitSwappedPojo,List<ImplicitSwappedPojo[][][]>> x5n) {
                                assertObjectEquals("'"+SWAP+"'", x1);
                                
assertObjectEquals("[[['"+SWAP+"',null],null],null]", x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[['"+SWAP+"',null],null],null],null]", x3);
-                               assertEquals(ImplicitSwappedPojo[][][].class, 
x3.get(0).getClass());
+                               assertClass(ImplicitSwappedPojo[][][].class, 
x3.get(0));
                                assertNull(x3n);
                                assertObjectEquals("{'"+SWAP+"':'"+SWAP+"'}", 
x4);
                                assertNull(x4n);
                                
assertObjectEquals("{'"+SWAP+"':[[[['"+SWAP+"',null],null],null],null]}", x5);
                                assertNull(x5n);
                        }
+
                        @Override
                        public void setMultiParamsEnum(TestEnum x1, 
TestEnum[][][] x2, TestEnum[][][] x2n, List<TestEnum[][][]> x3, 
List<TestEnum[][][]> x3n, Map<TestEnum,TestEnum> x4, Map<TestEnum,TestEnum> 
x4n, Map<TestEnum,List<TestEnum[][][]>> x5, Map<TestEnum,List<TestEnum[][][]>> 
x5n) {
                                assertObjectEquals("'TWO'", x1);
                                
assertObjectEquals("[[['TWO',null],null],null]", x2);
                                assertNull(x2n);
                                
assertObjectEquals("[[[['TWO',null],null],null],null]", x3);
-                               assertEquals(TestEnum[][][].class, 
x3.get(0).getClass());
+                               assertClass(TestEnum[][][].class, x3.get(0));
                                assertNull(x3n);
                                assertObjectEquals("{ONE:'TWO'}", x4);
                                assertNull(x4n);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
index fabb545..34a18eb 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
@@ -31,6 +31,7 @@ import org.apache.juneau.rest.labels.*;
                DefaultContentTypesResource.class,
                ErrorConditionsResource.class,
                TransformsResource.class,
+               FormDataResource.class,
                GroupsResource.class,
                GzipResource.TestGzipOff.class,
                GzipResource.TestGzipOn.class,
@@ -60,6 +61,7 @@ import org.apache.juneau.rest.labels.*;
                RestClient2Resource.class,
                SerializersResource.class,
                StaticFilesResource.class,
+               ThirdPartyProxyResource.class,
                UrisResource.class,
                UrlContentResource.class,
                ShutdownResource.class

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7139635d/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/TestUtils.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/TestUtils.java 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/TestUtils.java
new file mode 100644
index 0000000..102c17e
--- /dev/null
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/TestUtils.java
@@ -0,0 +1,79 @@
+// 
***************************************************************************************************************************
+// * 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.rest.test;
+
+import java.text.*;
+
+import org.apache.juneau.json.*;
+import org.apache.juneau.rest.client.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transforms.*;
+import org.junit.Assert;
+
+import junit.framework.*;
+
+public class TestUtils {
+
+       private static JsonSerializer js2 = new JsonSerializerBuilder()
+               .simple()
+               .pojoSwaps(IteratorSwap.class, EnumerationSwap.class)
+               .build();
+
+       /**
+        * Assert that the object equals the specified string after running it 
through JsonSerializer.DEFAULT_LAX.toString().
+        */
+       public static void assertObjectEquals(String s, Object o) {
+               assertObjectEquals(s, o, js2);
+       }
+
+       /**
+        * Assert that the object is an instance of the specified class.
+        */
+       public static void assertClass(Class<?> c, Object o) {
+               Assert.assertEquals(c, o == null ? null : o.getClass());
+       }
+
+       /**
+        * Assert that the object equals the specified string after running it 
through ws.toString().
+        */
+       public static void assertObjectEquals(String s, Object o, 
WriterSerializer ws) {
+               Assert.assertEquals(s, ws.toString(o));
+       }
+
+       public static void checkErrorResponse(boolean debug, RestCallException 
e, int status, String...contains) throws AssertionFailedError {
+               String r = e.getResponseMessage();
+               if (debug) {
+                       System.err.println(r); // NOT DEBUG
+                       e.printStackTrace();
+               }
+               if (status != e.getResponseCode()) {
+                       dumpResponse(r, "Response status code was not correct.  
Expected: ''{0}''.  Actual: ''{1}''", status, e.getResponseCode());
+                       throw new 
AssertionFailedError(MessageFormat.format("Response status code was not 
correct.  Expected: ''{0}''.  Actual: ''{1}''", status, e.getResponseCode()));
+               }
+               for (String s : contains) {
+                       if (r == null || ! r.contains(s)) {
+                               if (! debug)
+                                       dumpResponse(r, "Response did not have 
the following expected text: ''{0}''", s);
+                               throw new 
AssertionFailedError(MessageFormat.format("Response did not have the following 
expected text: ''{0}''", s));
+                       }
+               }
+       }
+
+       private static void dumpResponse(String r, String msg, Object...args) {
+               System.err.println("*** Failure 
****************************************************************************************");
 // NOT DEBUG
+               System.err.println(MessageFormat.format(msg, args));
+               System.err.println("*** Response-Start 
*********************************************************************************");
 // NOT DEBUG
+               System.err.println(r); // NOT DEBUG
+               System.err.println("*** Response-End 
***********************************************************************************");
 // NOT DEBUG
+       }
+}

Reply via email to