Repository: incubator-juneau
Updated Branches:
  refs/heads/master 78dffd972 -> f2fafd3ef


PartSerializer support.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/f2fafd3e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/f2fafd3e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/f2fafd3e

Branch: refs/heads/master
Commit: f2fafd3ef89335d124f8a98c1c2fa7500be85619
Parents: 78dffd9
Author: JamesBognar <[email protected]>
Authored: Thu May 25 18:58:11 2017 -0400
Committer: JamesBognar <[email protected]>
Committed: Thu May 25 18:58:11 2017 -0400

----------------------------------------------------------------------
 .../main/java/org/apache/juneau/dto/Link.java   |  3 +-
 .../org/apache/juneau/internal/StringUtils.java |  1 +
 .../org/apache/juneau/remoteable/FormData.java  | 16 +++-
 .../apache/juneau/remoteable/FormDataIfNE.java  | 12 +++
 .../org/apache/juneau/remoteable/Header.java    | 16 +++-
 .../apache/juneau/remoteable/HeaderIfNE.java    | 12 +++
 .../java/org/apache/juneau/remoteable/Path.java | 16 +++-
 .../org/apache/juneau/remoteable/Query.java     | 16 +++-
 .../org/apache/juneau/remoteable/QueryIfNE.java | 12 +++
 .../juneau/remoteable/RemoteMethodArg.java      | 16 +++-
 .../juneau/remoteable/RemoteableMethodMeta.java | 14 +--
 .../juneau/serializer/PartSerializer.java       | 52 +++++++++++
 .../org/apache/juneau/serializer/PartType.java  | 31 +++++++
 .../urlencoding/UrlEncodingSerializer.java      | 15 ++-
 juneau-core/src/main/javadoc/overview.html      | 22 ++++-
 .../juneau/rest/client/NameValuePairs.java      | 10 +-
 .../org/apache/juneau/rest/client/RestCall.java | 98 ++++++++++++--------
 .../apache/juneau/rest/client/RestClient.java   | 47 +++++++---
 .../juneau/rest/client/RestClientBuilder.java   | 41 +++++++-
 .../rest/client/SerializedNameValuePair.java    |  9 +-
 .../rest/test/ThirdPartyProxyResource.java      | 34 +++++++
 .../juneau/rest/test/ThirdPartyProxyTest.java   | 34 +++++++
 .../java/org/apache/juneau/rest/Redirect.java   |  5 +-
 23 files changed, 441 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/dto/Link.java 
b/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
index 1b926aa..e1036c7 100644
--- a/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
+++ b/juneau-core/src/main/java/org/apache/juneau/dto/Link.java
@@ -15,6 +15,7 @@ package org.apache.juneau.dto;
 import java.text.*;
 
 import org.apache.juneau.html.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 import org.apache.juneau.utils.*;
 
@@ -104,7 +105,7 @@ public class Link implements Comparable<Link> {
         */
        public Link setHref(String href, Object...args) {
                for (int i = 0; i < args.length; i++)
-                       args[i] = 
UrlEncodingSerializer.DEFAULT.serializePart(args[i], null, null);
+                       args[i] = 
UrlEncodingSerializer.DEFAULT.serialize(PartType.PATH, args[i]);
                this.href = (args.length > 0 ? MessageFormat.format(href, args) 
: href);
                return this;
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java 
b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
index c4bf1db..d17bc58 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -26,6 +26,7 @@ import java.util.regex.*;
 import javax.xml.bind.*;
 
 import org.apache.juneau.parser.*;
+import org.apache.juneau.parser.ParseException;
 
 /**
  * Reusable string utility methods.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
index 23983c3..83389b8 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormData.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -41,12 +42,12 @@ import org.apache.juneau.urlencoding.*;
  * <p>
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
- *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li><code>NameValuePairs</code> - Individual name-value pairs.
  *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li>A bean - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  * </ul>
  * <p>
  * The annotation can also be applied to a bean property field or getter when 
the argument is annotated with
@@ -93,4 +94,13 @@ public @interface FormData {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
index d043217..2d0b3ab 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/FormDataIfNE.java
@@ -17,6 +17,9 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Identical to {@link FormData @FormData} except skips values if they're 
null/blank.
  */
@@ -38,4 +41,13 @@ public @interface FormDataIfNE {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
index 3fae979..6eaf870 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Header.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -39,11 +40,11 @@ import org.apache.juneau.urlencoding.*;
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
  *     <li><code>NameValuePairs</code> - Individual name-value pairs.
- *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li>A bean - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  * </ul>
  * <p>
  * The annotation can also be applied to a bean property field or getter when 
the argument is annotated with
@@ -90,4 +91,13 @@ public @interface Header {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
index 2aaf711..4839ff0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/HeaderIfNE.java
@@ -17,6 +17,9 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Identical to {@link Header @Header} except skips values if they're 
null/blank.
  */
@@ -38,4 +41,13 @@ public @interface HeaderIfNE {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
index 47c6eea..919615e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Path.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -36,11 +37,11 @@ import org.apache.juneau.urlencoding.*;
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
  *     <li><code>NameValuePairs</code> - Individual name-value pairs.
- *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li>A bean - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  * </ul>
  * <p>
  * The annotation can also be applied to a bean property field or getter when 
the argument is annotated with
@@ -84,4 +85,13 @@ public @interface Path {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
index 5729412..7738468 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/Query.java
@@ -18,6 +18,7 @@ import static java.lang.annotation.RetentionPolicy.*;
 import java.lang.annotation.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -42,11 +43,11 @@ import org.apache.juneau.urlencoding.*;
  * The argument can be any of the following types:
  * <ul class='spaced-list'>
  *     <li><code>NameValuePairs</code> - Individual name-value pairs.
- *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *     <li>Any serializable POJO - Converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li><code>Map&lt;String,Object&gt;</code> - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li>A bean - Individual name-value pairs.
- *             Values are converted to text using {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)}.
+ *             Values are converted to text using {@link 
UrlEncodingSerializer#serialize(PartType,Object)}.
  *     <li>{@link String} - Treated as a query string.
  * </ul>
  * <p>
@@ -95,4 +96,13 @@ public @interface Query {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
index c2cc2fd..8d38510 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/QueryIfNE.java
@@ -17,6 +17,9 @@ import static java.lang.annotation.RetentionPolicy.*;
 
 import java.lang.annotation.*;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Identical to {@link Query @Query} except skips values if they're null/blank.
  */
@@ -39,4 +42,13 @@ public @interface QueryIfNE {
         * </ul>
         */
        String value() default "*";
+
+       /**
+        * Specifies the {@link PartSerializer} class used for serializing 
values to strings.
+        * <p>
+        * The default serializer converters values to UON notation.
+        * <p>
+        * This annotation is provided to allow values to be custom serialized.
+        */
+       Class<? extends PartSerializer> serializer() default 
UrlEncodingSerializer.class;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
index 86d2eac..ab9a44c 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethodArg.java
@@ -12,6 +12,9 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.remoteable;
 
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+
 /**
  * Represents the metadata about an annotated argument of a method on a remote 
proxy interface.
  */
@@ -26,16 +29,27 @@ public class RemoteMethodArg {
        /** The value is skipped if it's null/empty. */
        public final boolean skipIfNE;
 
+       /** The serializer used for converting objects to strings. */
+       public final PartSerializer serializer;
+
        /**
         * Constructor.
         *
         * @param name The argument name.  Can be blank.
         * @param index The zero-based index of the argument on the Java method.
         * @param skipIfNE The value is skipped if it's null/empty.
+        * @param serializer The class to use for serializing headers, query 
paramters, form-data parameters, and
+        *      path variables.
+        *      If {@link UrlEncodingSerializer}, then the url-encoding 
serializer defined on the client will be used.
         */
-       protected RemoteMethodArg(String name, int index, boolean skipIfNE) {
+       protected RemoteMethodArg(String name, int index, boolean skipIfNE, 
Class<? extends PartSerializer> serializer) {
                this.name = name;
                this.index = index;
                this.skipIfNE = skipIfNE;
+               try {
+                       this.serializer = (serializer == 
UrlEncodingSerializer.class ? null : serializer.newInstance());
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
index bad4686..13b96b4 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteableMethodMeta.java
@@ -90,25 +90,25 @@ public class RemoteableMethodMeta {
                                        Class<?> ca = a.annotationType();
                                        if (ca == Path.class) {
                                                Path p = (Path)a;
-                                               annotated = pathArgs.add(new 
RemoteMethodArg(p.value(), index, false));
+                                               annotated = pathArgs.add(new 
RemoteMethodArg(p.value(), index, false, p.serializer()));
                                        } else if (ca == Query.class) {
                                                Query q = (Query)a;
-                                               annotated = queryArgs.add(new 
RemoteMethodArg(q.value(), index, false));
+                                               annotated = queryArgs.add(new 
RemoteMethodArg(q.value(), index, false, q.serializer()));
                                        } else if (ca == QueryIfNE.class) {
                                                QueryIfNE q = (QueryIfNE)a;
-                                               annotated = queryArgs.add(new 
RemoteMethodArg(q.value(), index, true));
+                                               annotated = queryArgs.add(new 
RemoteMethodArg(q.value(), index, true, q.serializer()));
                                        } else if (ca == FormData.class) {
                                                FormData f = (FormData)a;
-                                               annotated = 
formDataArgs.add(new RemoteMethodArg(f.value(), index, false));
+                                               annotated = 
formDataArgs.add(new RemoteMethodArg(f.value(), index, false, f.serializer()));
                                        } else if (ca == FormDataIfNE.class) {
                                                FormDataIfNE f = 
(FormDataIfNE)a;
-                                               annotated = 
formDataArgs.add(new RemoteMethodArg(f.value(), index, true));
+                                               annotated = 
formDataArgs.add(new RemoteMethodArg(f.value(), index, true, f.serializer()));
                                        } else if (ca == Header.class) {
                                                Header h = (Header)a;
-                                               annotated = headerArgs.add(new 
RemoteMethodArg(h.value(), index, false));
+                                               annotated = headerArgs.add(new 
RemoteMethodArg(h.value(), index, false, h.serializer()));
                                        } else if (ca == HeaderIfNE.class) {
                                                HeaderIfNE h = (HeaderIfNE)a;
-                                               annotated = headerArgs.add(new 
RemoteMethodArg(h.value(), index, true));
+                                               annotated = headerArgs.add(new 
RemoteMethodArg(h.value(), index, true, h.serializer()));
                                        } else if (ca == RequestBean.class) {
                                                annotated = true;
                                                requestBeanArgs.add(index);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java
new file mode 100644
index 0000000..0063b9e
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/PartSerializer.java
@@ -0,0 +1,52 @@
+// 
***************************************************************************************************************************
+// * 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.serializer;
+
+import org.apache.juneau.remoteable.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Interface used to convert POJOs to simple strings in HTTP headers, query 
parameters, form-data parameters, and URI
+ * path variables.
+ * <p>
+ * By default, the {@link UrlEncodingSerializer} class implements this 
interface so that it can be used to serialize
+ * these HTTP parts.
+ * However, the interface is provided to allow custom serialization of these 
objects by providing your own implementation
+ * class and using it in any of the following locations:
+ * <ul>
+ *     <li>{@link FormData#serializer()}
+ *     <li>{@link FormDataIfNE#serializer()}
+ *     <li>{@link Query#serializer()}
+ *     <li>{@link QueryIfNE#serializer()}
+ *     <li>{@link Header#serializer()}
+ *     <li>{@link HeaderIfNE#serializer()}
+ *     <li>{@link Path#serializer()}
+ *     <li><code>RestClientBuilder.partSerializer(Class)</code>
+ * </ul>
+ * <p>
+ * Implementations must include a no-arg constructor.
+ */
+public interface PartSerializer {
+
+       /**
+        * Converts the specified value to a string that can be used as an HTTP 
header value, query parameter value,
+        * form-data parameter, or URI path variable.
+        * <p>
+        * Returned values should NOT be URL-encoded.  This will happen 
automatically.
+        *
+        * @param type The category of value being serialized.
+        * @param value The value being serialized.
+        * @return The serialized value.
+        */
+       public String serialize(PartType type, Object value);
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java
new file mode 100644
index 0000000..498c49f
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/PartType.java
@@ -0,0 +1,31 @@
+// 
***************************************************************************************************************************
+// * 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.serializer;
+
+/**
+ * Represents possible enum values that can be passed to the {@link 
PartSerializer#serialize(PartType, Object)} method.
+ */
+public enum PartType {
+
+       /** A URI path variable */
+       PATH,
+
+       /** A URI query parameter */
+       QUERY,
+
+       /** A form-data parameter */
+       FORM_DATA,
+
+       /** An HTTP header */
+       HEADER
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
 
b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
index 1814833..2ab697a 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializer.java
@@ -132,7 +132,7 @@ import org.apache.juneau.uon.*;
  */
 @Produces("application/x-www-form-urlencoded")
 @SuppressWarnings("hiding")
-public class UrlEncodingSerializer extends UonSerializer {
+public class UrlEncodingSerializer extends UonSerializer implements 
PartSerializer {
 
        /** Reusable instance of {@link UrlEncodingSerializer}, all default 
settings. */
        public static final UrlEncodingSerializer DEFAULT = new 
UrlEncodingSerializer(PropertyStore.create());
@@ -396,7 +396,7 @@ public class UrlEncodingSerializer extends UonSerializer {
         * If <jk>null</jk>, then uses the value from the {@link 
UrlEncodingSerializerContext#URLENC_paramFormat} setting.
         * @return The serialized object.
         */
-       public String serializePart(Object o, Boolean urlEncode, Boolean 
plainTextParams) {
+       private String serializePart(Object o, Boolean urlEncode, Boolean 
plainTextParams) {
                try {
                        // Shortcut for simple types.
                        ClassMeta<?> cm = 
getBeanContext().getClassMetaForObject(o);
@@ -435,4 +435,15 @@ public class UrlEncodingSerializer extends UonSerializer {
                UrlEncodingSerializerSession s = 
(UrlEncodingSerializerSession)session;
                serializeAnything(s, s.getWriter(), o);
        }
+
+       @Override /* PartSerializer */
+       public String serialize(PartType type, Object value) {
+               switch(type) {
+                       case HEADER: return serializePart(value, false, true);
+                       case FORM_DATA: return serializePart(value, false, 
null);
+                       case PATH: return serializePart(value, false, null);
+                       case QUERY: return serializePart(value, false, null);
+                       default: return StringUtils.toString(value);
+               }
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html 
b/juneau-core/src/main/javadoc/overview.html
index f5b4f86..fe24402 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6136,6 +6136,20 @@
                                        <li>{@link 
org.apache.juneau.Session#getProperty(Class,String)}
                                        <li>{@link 
org.apache.juneau.Session#getProperty(Class,String,Object)}
                                </ul>   
+                       <li>New {@link 
org.apache.juneau.serializer.PartSerializer} interface particularly tailored to 
HTTP
+                               headers, query parameters, form-data 
parameters, and path variables.  
+                               <br>Allows easy user-defined serialization of 
these objects.
+                               <br>The interface can be used in the following 
locations:
+                               <ul>
+                                       <li>{@link 
org.apache.juneau.rest.client.RestClientBuilder#partSerializer(Class)}
+                                       <li>{@link 
org.apache.juneau.remoteable.Path#serializer} 
+                                       <li>{@link 
org.apache.juneau.remoteable.Query#serializer} 
+                                       <li>{@link 
org.apache.juneau.remoteable.QueryIfNE#serializer} 
+                                       <li>{@link 
org.apache.juneau.remoteable.FormData#serializer} 
+                                       <li>{@link 
org.apache.juneau.remoteable.FormDataIfNE#serializer} 
+                                       <li>{@link 
org.apache.juneau.remoteable.Header#serializer} 
+                                       <li>{@link 
org.apache.juneau.remoteable.HeaderIfNE#serializer} 
+                               </ul>
                </ul>
 
                <h6 class='topic'>org.apache.juneau.rest</h6>
@@ -6504,18 +6518,18 @@
                        <li>New methods on {@link 
org.apache.juneau.rest.client.RestCall}:
                                <ul>
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#uri(Object) uri(Object)}
-                                       <li>{@link 
org.apache.juneau.rest.client.RestCall#query(String,Object,boolean) 
query(String,Object,boolean)}
+                                       <li>{@link 
org.apache.juneau.rest.client.RestCall#query(String,Object,boolean,PartSerializer)
 query(String,Object,boolean,PartSerializer)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#query(String,Object) 
query(String,Object)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#queryIfNE(String,Object) 
queryIfNE(String,Object)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#query(Map) query(Map)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#queryIfNE(Map) queryIfNE(Map)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#query(String) query(String)}
-                                       <li>{@link 
org.apache.juneau.rest.client.RestCall#formData(String,Object,boolean) 
formData(String,Object,boolean)}
+                                       <li>{@link 
org.apache.juneau.rest.client.RestCall#formData(String,Object,boolean,PartSerializer)
 formData(String,Object,boolean,PartSerializer)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#formData(String,Object) 
formData(String,Object)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#formDataIfNE(String,Object) 
formDataIfNE(String,Object)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#formData(Map) formData(Map)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#formDataIfNE(Map) formDataIfNE(Map)}
-                                       <li>{@link 
org.apache.juneau.rest.client.RestCall#header(String,Object,boolean) 
header(String,Object,boolean)}
+                                       <li>{@link 
org.apache.juneau.rest.client.RestCall#header(String,Object,boolean,PartSerializer)
 header(String,Object,boolean,PartSerializer)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#header(String,Object) 
header(String,Object)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#headerIfNE(String,Object) 
headerIfNE(String,Object)}
                                        <li>{@link 
org.apache.juneau.rest.client.RestCall#headers(Map) headers(Map)}
@@ -6555,7 +6569,7 @@
                        <li>New methods added to {@link 
org.apache.juneau.rest.client.NameValuePairs}:
                                <ul>
                                        <li>{@link 
org.apache.juneau.rest.client.NameValuePairs#append(String,Object) 
append(String,Object)}
-                                       <li>{@link 
org.apache.juneau.rest.client.NameValuePairs#append(String,Object,UrlEncodingSerializer)
 append(String,Object,UrlEncodingSerializer)}
+                                       <li>{@link 
org.apache.juneau.rest.client.NameValuePairs#append(String,Object,PartSerializer)
 append(String,Object,PartSerializer)}
                                </ul>
                        <li>{@link org.apache.juneau.rest.client.RetryOn} is 
now an abstract class with an additional method:
                                <ul>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
index 845f520..25908de 100644
--- 
a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
+++ 
b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/NameValuePairs.java
@@ -18,6 +18,7 @@ import org.apache.http.*;
 import org.apache.http.client.entity.*;
 import org.apache.http.message.*;
 import org.apache.juneau.internal.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -67,16 +68,15 @@ public final class NameValuePairs extends 
LinkedList<NameValuePair> {
        /**
         * Appends the specified name/value pair to the end of this list.
         * <p>
-        * The value is converted to UON notation using the {@link 
UrlEncodingSerializer#serializePart(Object, Boolean, Boolean)} method
-        * of the specified serializer.
+        * The value is converted to UON notation using the {@link 
UrlEncodingSerializer} defined on the client.
         *
         * @param name The pair name.
         * @param value The pair value.
-        * @param serializer The serializer to use to convert the value to a 
string.
+        * @param partSerializer The serializer to use for converting values to 
simple strings.
         * @return This object (for method chaining).
         */
-       public NameValuePairs append(String name, Object value, 
UrlEncodingSerializer serializer) {
-               super.add(new SerializedNameValuePair(name, value, serializer));
+       public NameValuePairs append(String name, Object value, PartSerializer 
partSerializer) {
+               super.add(new SerializedNameValuePair(name, value, 
partSerializer));
                return this;
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/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 f06a172..ca1f541 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
@@ -58,7 +58,7 @@ import org.apache.juneau.utils.*;
  *     <li><a class="doclink" 
href="package-summary.html#RestClient">org.apache.juneau.rest.client &gt; REST 
client API</a> for more information and code examples.
  * </ul>
  */
-@SuppressWarnings("hiding")
+@SuppressWarnings({ "hiding", "unchecked" })
 public final class RestCall {
 
        private final RestClient client;                       // The client 
that created this call.
@@ -177,28 +177,31 @@ public final class RestCall {
         *      Can also be {@link Map}, {@link String}, {@link 
NameValuePairs}, or bean 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.
+        * @param partSerializer The part serializer to use to convert the 
value to a string.
+        *      If <jk>null</jk>, then the URL-encoding serializer defined on 
the client is used.
         * @return This object (for method chaining).
         * @throws RestCallException
         */
-       @SuppressWarnings("unchecked")
-       public RestCall query(String name, Object value, boolean skipIfEmpty) 
throws RestCallException {
+       public RestCall query(String name, Object value, boolean skipIfEmpty, 
PartSerializer partSerializer) throws RestCallException {
+               if (partSerializer == null)
+                       partSerializer = client.getPartSerializer();
                if (! ("*".equals(name) || isEmpty(name))) {
                        if (value != null && ! (isEmpty(value) && skipIfEmpty))
-                               uriBuilder.addParameter(name, 
client.getUrlEncodingSerializer().serializePart(value, false, null));
+                               uriBuilder.addParameter(name, 
partSerializer.serialize(PartType.QUERY, value));
                } else if (value instanceof NameValuePairs) {
                        for (NameValuePair p : (NameValuePairs)value)
-                               query(p.getName(), p.getValue(), skipIfEmpty);
+                               query(p.getName(), p.getValue(), skipIfEmpty, 
partSerializer);
                } 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);
+                               query(p.getKey(), p.getValue(), skipIfEmpty, 
partSerializer);
                } else if (isBean(value)){
-                       return query(name, toBeanMap(value), skipIfEmpty);
+                       return query(name, toBeanMap(value), skipIfEmpty, 
partSerializer);
                } else {
-                       throw new RuntimeException("Invalid name passed to 
query(name,value,skipIfEmpty): ("+name+","+value+","+skipIfEmpty+")");
+                       throw new FormattedRuntimeException("Invalid name 
''{0}'' passed to query(name,value,skipIfEmpty) for data type ''{1}''", name, 
ClassUtils.getReadableClassNameForObject(value));
                }
                return this;
        }
@@ -212,7 +215,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall query(String name, Object value) throws 
RestCallException {
-               return query(name, value, false);
+               return query(name, value, false, null);
        }
 
        /**
@@ -237,7 +240,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall queryIfNE(String name, Object value) throws 
RestCallException {
-               return query(name, value, true);
+               return query(name, value, true, null);
        }
 
        /**
@@ -250,7 +253,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall queryIfNE(Map<String,Object> params) throws 
RestCallException {
-               return query(null, params, true);
+               return query(null, params, true, null);
        }
 
        /**
@@ -272,27 +275,30 @@ public final class RestCall {
         * @param value The parameter value converted to a string using UON 
notation.
         *      Can also be {@link Map}, {@link NameValuePairs}, or bean if the 
name is null/blank/*.
         * @param skipIfEmpty Don't add the pair if the value is empty.
+        * @param partSerializer The part serializer to use to convert the 
value to a string.
+        *      If <jk>null</jk>, then the URL-encoding serializer defined on 
the client is used.
         * @return This object (for method chaining).
         * @throws RestCallException
         */
-       @SuppressWarnings("unchecked")
-       public RestCall formData(String name, Object value, boolean 
skipIfEmpty) throws RestCallException {
+       public RestCall formData(String name, Object value, boolean 
skipIfEmpty, PartSerializer partSerializer) throws RestCallException {
                if (formData == null)
                        formData = new NameValuePairs();
+               if (partSerializer == null)
+                       partSerializer = client.getPartSerializer();
                if (! ("*".equals(name) || isEmpty(name))) {
                        if (value != null && ! (isEmpty(value) && skipIfEmpty))
-                               formData.add(new SerializedNameValuePair(name, 
value, client.getUrlEncodingSerializer()));
+                               formData.add(new SerializedNameValuePair(name, 
value, partSerializer));
                } else if (value instanceof NameValuePairs) {
                        for (NameValuePair p : (NameValuePairs)value)
                                if (! (isEmpty(p.getValue()) && skipIfEmpty))
                                        formData.add(p);
                } else if (value instanceof Map) {
                        for (Map.Entry<String,Object> p : ((Map<String,Object>) 
value).entrySet())
-                               formData(p.getKey(), p.getValue(), skipIfEmpty);
+                               formData(p.getKey(), p.getValue(), skipIfEmpty, 
partSerializer);
                } else if (isBean(value)) {
-                       return formData(name, toBeanMap(value), skipIfEmpty);
+                       return formData(name, toBeanMap(value), skipIfEmpty, 
partSerializer);
                } else {
-                       throw new RuntimeException("Invalid name passed to 
formData(name,value,skipIfEmpty).");
+                       throw new FormattedRuntimeException("Invalid name 
''{0}'' passed to formData(name,value,skipIfEmpty) for data type ''{1}''", 
name, ClassUtils.getReadableClassNameForObject(value));
                }
                return this;
        }
@@ -308,7 +314,7 @@ public final class RestCall {
         * @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);
+               return formData(name, value, false, null);
        }
 
        /**
@@ -344,7 +350,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall formDataIfNE(String name, Object value) throws 
RestCallException {
-               return formData(name, value, true);
+               return formData(name, value, true, null);
        }
 
        /**
@@ -357,25 +363,28 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall formDataIfNE(Map<String,Object> params) throws 
RestCallException {
-               return formData(null, params, true);
+               return formData(null, params, true, null);
        }
 
        /**
         * Replaces a variable of the form <js>"{name}"</js> in the URL path 
with the specified value.
+        *
         * @param name The path variable name.
         * @param value The replacement value.
-        *
+        * @param partSerializer The part serializer to use to convert the 
value to a string.
+        *      If <jk>null</jk>, then the URL-encoding serializer defined on 
the client is used.
         * @return This object (for method chaining).
         * @throws RestCallException If variable could not be found in path.
         */
-       @SuppressWarnings("unchecked")
-       public RestCall path(String name, Object value) throws 
RestCallException {
+       public RestCall path(String name, Object value, PartSerializer 
partSerializer) throws RestCallException {
                String path = uriBuilder.getPath();
+               if (partSerializer == null)
+                       partSerializer = client.getPartSerializer();
                if (! ("*".equals(name) || isEmpty(name))) {
                        String var = "{" + name + "}";
                        if (path.indexOf(var) == -1)
                                throw new RestCallException("Path variable 
{"+name+"} was not found in path.");
-                       String newPath = path.replace(var, 
client.getUrlEncodingSerializer().serializePart(value, false, null));
+                       String newPath = path.replace(var, 
partSerializer.serialize(PartType.PATH, value));
                        uriBuilder.setPath(newPath);
                } else if (value instanceof NameValuePairs) {
                        for (NameValuePair p : (NameValuePairs)value)
@@ -386,12 +395,24 @@ public final class RestCall {
                } else if (isBean(value)) {
                        return path(name, toBeanMap(value));
                } else {
-                       throw new RuntimeException("Invalid name passed to 
path(name,value).");
+                       throw new FormattedRuntimeException("Invalid name 
''{0}'' passed to path(name,value) for data type ''{1}''", name, 
ClassUtils.getReadableClassNameForObject(value));
                }
                return this;
        }
 
        /**
+        * Replaces a variable of the form <js>"{name}"</js> in the URL path 
with the specified value.
+        *
+        * @param name The path variable name.
+        * @param value The replacement value.
+        * @return This object (for method chaining).
+        * @throws RestCallException If variable could not be found in path.
+        */
+       public RestCall path(String name, Object value) throws 
RestCallException {
+               return path(name, value, null);
+       }
+
+       /**
         * Sets the URI user info.
         *
         * @param userInfo The new URI user info.
@@ -473,24 +494,27 @@ public final class RestCall {
         * 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.
+        * @param partSerializer The part serializer to use to convert the 
value to a string.
+        *      If <jk>null</jk>, then the URL-encoding serializer defined on 
the client is used.
         * @return This object (for method chaining).
         * @throws RestCallException
         */
-       @SuppressWarnings("unchecked")
-       public RestCall header(String name, Object value, boolean skipIfEmpty) 
throws RestCallException {
+       public RestCall header(String name, Object value, boolean skipIfEmpty, 
PartSerializer partSerializer) throws RestCallException {
+               if (partSerializer == null)
+                       partSerializer = client.getPartSerializer();
                if (! ("*".equals(name) || isEmpty(name))) {
                        if (value != null && ! (isEmpty(value) && skipIfEmpty))
-                               request.setHeader(name, 
client.getUrlEncodingSerializer().serializePart(value, false, true));
+                               request.setHeader(name, 
partSerializer.serialize(PartType.HEADER, value));
                } else if (value instanceof NameValuePairs) {
                        for (NameValuePair p : (NameValuePairs)value)
-                               header(p.getName(), p.getValue(), skipIfEmpty);
+                               header(p.getName(), p.getValue(), skipIfEmpty, 
partSerializer);
                } else if (value instanceof Map) {
                        for (Map.Entry<String,Object> p : ((Map<String,Object>) 
value).entrySet())
-                               header(p.getKey(), p.getValue(), skipIfEmpty);
+                               header(p.getKey(), p.getValue(), skipIfEmpty, 
partSerializer);
                } else if (isBean(value)) {
-                       return header(name, toBeanMap(value), skipIfEmpty);
+                       return header(name, toBeanMap(value), skipIfEmpty, 
partSerializer);
                } else {
-                       throw new RuntimeException("Invalid name passed to 
header(name,value,skipIfEmpty).");
+                       throw new FormattedRuntimeException("Invalid name 
''{0}'' passed to header(name,value,skipIfEmpty) for data type ''{1}''", name, 
ClassUtils.getReadableClassNameForObject(value));
                }
                return this;
        }
@@ -506,7 +530,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall header(String name, Object value) throws 
RestCallException {
-               return header(name, value, false);
+               return header(name, value, false, null);
        }
 
        /**
@@ -517,7 +541,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall headers(Map<String,Object> values) throws 
RestCallException {
-               return header(null, values, false);
+               return header(null, values, false, null);
        }
 
        /**
@@ -532,7 +556,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall headerIfNE(String name, Object value) throws 
RestCallException {
-               return header(name, value, true);
+               return header(name, value, true, null);
        }
 
        /**
@@ -545,7 +569,7 @@ public final class RestCall {
         * @throws RestCallException
         */
        public RestCall headersIfNE(Map<String,Object> values) throws 
RestCallException {
-               return header(null, values, true);
+               return header(null, values, true, null);
        }
 
        /**
@@ -1735,7 +1759,6 @@ public final class RestCall {
         * @throws IOException If a connection error occurred.
         * @see BeanSession#getClassMeta(Class) for argument syntax for maps 
and collections.
         */
-       @SuppressWarnings("unchecked")
        public <T> T getResponse(Type type, Type...args) throws IOException, 
ParseException {
                BeanContext bc = getParser().getBeanContext();
                if (bc == null)
@@ -1795,7 +1818,6 @@ 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))

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/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 dbcc294..f863487 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
@@ -52,12 +52,16 @@ import org.apache.juneau.urlencoding.*;
  *     <li><a class="doclink" 
href="package-summary.html#RestClient">org.apache.juneau.rest.client &gt; REST 
client API</a> for more information and code examples.
  * </ul>
  */
+@SuppressWarnings("rawtypes")
 public class RestClient extends CoreObject {
 
+       private static final ConcurrentHashMap<Class,PartSerializer> 
partSerializerCache = new ConcurrentHashMap<Class,PartSerializer>();
+
        private final Map<String,String> headers;
        private final CloseableHttpClient httpClient;
        private final boolean keepHttpClientOpen;
        private final UrlEncodingSerializer urlEncodingSerializer;  // Used for 
form posts only.
+       private final PartSerializer partSerializer;
        private final String rootUrl;
        private volatile boolean isClosed = false;
        private final StackTraceElement[] creationStack;
@@ -84,6 +88,7 @@ public class RestClient extends CoreObject {
                        Serializer serializer,
                        Parser parser,
                        UrlEncodingSerializer urlEncodingSerializer,
+                       PartSerializer partSerializer,
                        Map<String,String> headers,
                        List<RestCallInterceptor> interceptors,
                        String rootUri,
@@ -99,6 +104,7 @@ public class RestClient extends CoreObject {
                this.serializer = serializer;
                this.parser = parser;
                this.urlEncodingSerializer = urlEncodingSerializer;
+               this.partSerializer = partSerializer;
 
                Map<String,String> h2 = new 
ConcurrentHashMap<String,String>(headers);
 
@@ -538,16 +544,16 @@ public class RestClient extends CoreObject {
                                                        
rc.serializer(serializer).parser(parser);
 
                                                        for (RemoteMethodArg a 
: rmm.getPathArgs())
-                                                               rc.path(a.name, 
args[a.index]);
+                                                               rc.path(a.name, 
args[a.index], a.serializer);
 
                                                        for (RemoteMethodArg a 
: rmm.getQueryArgs())
-                                                               
rc.query(a.name, args[a.index], a.skipIfNE);
+                                                               
rc.query(a.name, args[a.index], a.skipIfNE, a.serializer);
 
                                                        for (RemoteMethodArg a 
: rmm.getFormDataArgs())
-                                                               
rc.formData(a.name, args[a.index], a.skipIfNE);
+                                                               
rc.formData(a.name, args[a.index], a.skipIfNE, a.serializer);
 
                                                        for (RemoteMethodArg a 
: rmm.getHeaderArgs())
-                                                               
rc.header(a.name, args[a.index], a.skipIfNE);
+                                                               
rc.header(a.name, args[a.index], a.skipIfNE, a.serializer);
 
                                                        if (rmm.getBodyArg() != 
null)
                                                                
rc.input(args[rmm.getBodyArg()]);
@@ -563,31 +569,31 @@ public class RestClient extends CoreObject {
 
                                                                                
Path p = pMeta.getAnnotation(Path.class);
                                                                                
if (p != null)
-                                                                               
        rc.path(getName(p.value(), pMeta), val);
+                                                                               
        rc.path(getName(p.value(), pMeta), val, 
getPartSerializer(p.serializer()));
 
                                                                                
Query q1 = pMeta.getAnnotation(Query.class);
                                                                                
if (q1 != null)
-                                                                               
        rc.query(getName(q1.value(), pMeta), val, false);
+                                                                               
        rc.query(getName(q1.value(), pMeta), val, false, 
getPartSerializer(q1.serializer()));
 
                                                                                
QueryIfNE q2 = pMeta.getAnnotation(QueryIfNE.class);
                                                                                
if (q2 != null)
-                                                                               
        rc.query(getName(q2.value(), pMeta), val, true);
+                                                                               
        rc.query(getName(q2.value(), pMeta), val, true, 
getPartSerializer(q2.serializer()));
 
                                                                                
FormData f1 = pMeta.getAnnotation(FormData.class);
                                                                                
if (f1 != null)
-                                                                               
        rc.formData(getName(f1.value(), pMeta), val, false);
+                                                                               
        rc.formData(getName(f1.value(), pMeta), val, false, 
getPartSerializer(f1.serializer()));
 
                                                                                
FormDataIfNE f2 = pMeta.getAnnotation(FormDataIfNE.class);
                                                                                
if (f2 != null)
-                                                                               
        rc.formData(getName(f2.value(), pMeta), val, true);
+                                                                               
        rc.formData(getName(f2.value(), pMeta), val, true, 
getPartSerializer(f2.serializer()));
 
                                                                                
org.apache.juneau.remoteable.Header h1 = 
pMeta.getAnnotation(org.apache.juneau.remoteable.Header.class);
                                                                                
if (h1 != null)
-                                                                               
        rc.header(getName(h1.value(), pMeta), val, false);
+                                                                               
        rc.header(getName(h1.value(), pMeta), val, false, 
getPartSerializer(h1.serializer()));
 
                                                                                
HeaderIfNE h2 = pMeta.getAnnotation(HeaderIfNE.class);
                                                                                
if (h2 != null)
-                                                                               
        rc.header(getName(h2.value(), pMeta), val, true);
+                                                                               
        rc.header(getName(h2.value(), pMeta), val, true, 
getPartSerializer(h2.serializer()));
                                                                        }
                                                                }
                                                        }
@@ -622,10 +628,25 @@ public class RestClient extends CoreObject {
                return name;
        }
 
+       private static PartSerializer getPartSerializer(Class c) {
+               if (c == UrlEncodingSerializer.class)
+                       return null;
+               PartSerializer pf = partSerializerCache.get(c);
+               if (pf == null) {
+                       try {
+                               partSerializerCache.putIfAbsent(c, 
(PartSerializer)c.newInstance());
+                       } catch (Exception e) {
+                               throw new RuntimeException(e);
+                       }
+                       pf = partSerializerCache.get(c);
+               }
+               return pf;
+       }
+
        private Pattern absUrlPattern = Pattern.compile("^\\w+\\:\\/\\/.*");
 
-       UrlEncodingSerializer getUrlEncodingSerializer() {
-               return urlEncodingSerializer;
+       PartSerializer getPartSerializer() {
+               return partSerializer;
        }
 
        URI toURI(Object url) throws URISyntaxException {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/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 83961bc..76bec4f 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
@@ -51,7 +51,7 @@ import org.apache.juneau.urlencoding.*;
 /**
  * Builder class for the {@link RestClient} class.
  */
-@SuppressWarnings("hiding")
+@SuppressWarnings({"hiding"})
 public class RestClientBuilder extends CoreObjectBuilder {
 
        private HttpClientConnectionManager httpClientConnectionManager;
@@ -61,8 +61,10 @@ public class RestClientBuilder extends CoreObjectBuilder {
 
        private Class<? extends Serializer> serializerClass = 
JsonSerializer.class;
        private Class<? extends Parser> parserClass = JsonParser.class;
+       private Class<? extends PartSerializer> partSerializerClass = 
UrlEncodingSerializer.class;
        private Serializer serializer;
        private Parser parser;
+       private PartSerializer partSerializer;
 
        private Map<String,String> headers = new 
TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
 
@@ -140,7 +142,17 @@ public class RestClientBuilder extends CoreObjectBuilder {
 
                        UrlEncodingSerializer us = new 
SerializerBuilder(propertyStore).build(UrlEncodingSerializer.class);
 
-                       return new RestClient(propertyStore, httpClient, 
keepHttpClientOpen, s, p, us, headers, interceptors, rootUrl, retryOn, retries, 
retryInterval, debug, executorService, executorServiceShutdownOnClose);
+                       PartSerializer pf = null;
+                       if (partSerializer != null)
+                               pf = partSerializer;
+                       else if (partSerializerClass != null) {
+                               if (partSerializerClass == 
UrlEncodingSerializer.class)
+                                       pf = us;
+                               else
+                                       pf = partSerializerClass.newInstance();
+                       }
+
+                       return new RestClient(propertyStore, httpClient, 
keepHttpClientOpen, s, p, us, pf, headers, interceptors, rootUrl, retryOn, 
retries, retryInterval, debug, executorService, executorServiceShutdownOnClose);
                } catch (Exception e) {
                        throw new RuntimeException(e);
                }
@@ -383,6 +395,31 @@ public class RestClientBuilder extends CoreObjectBuilder {
        }
 
        /**
+        * Sets the part serializer to use for converting POJOs to headers, 
query parameters, form-data parameters, and
+        * path variables.
+        *
+        * @param partSerializer The part serializer instance.
+        * @return This object (for method chaining).
+        */
+       public RestClientBuilder partSerializer(PartSerializer partSerializer) {
+               this.partSerializer = partSerializer;
+               return this;
+       }
+
+       /**
+        * Sets the part formatter to use for converting POJOs to headers, 
query parameters, form-data parameters, and
+        * path variables.
+        *
+        * @param partSerializerClass The part serializer class.
+        *      The class must have a no-arg constructor.
+        * @return This object (for method chaining).
+        */
+       public RestClientBuilder partSerializer(Class<? extends PartSerializer> 
partSerializerClass) {
+               this.partSerializerClass = partSerializerClass;
+               return this;
+       }
+
+       /**
         * Set up this client to use BASIC auth.
         *
         * @param host The auth scope hostname.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/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 11382a6..0e6be73 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
@@ -13,6 +13,7 @@
 package org.apache.juneau.rest.client;
 
 import org.apache.http.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -30,7 +31,7 @@ import org.apache.juneau.urlencoding.*;
 public final class SerializedNameValuePair implements NameValuePair {
        private String name;
        private Object value;
-       private UrlEncodingSerializer serializer;
+       private PartSerializer serializer;
 
        /**
         * Constructor.
@@ -39,7 +40,7 @@ public final class SerializedNameValuePair implements 
NameValuePair {
         * @param value The POJO to serialize to the parameter value.
         * @param serializer The serializer to use to convert the value to a 
string.
         */
-       public SerializedNameValuePair(String name, Object value, 
UrlEncodingSerializer serializer) {
+       public SerializedNameValuePair(String name, Object value, 
PartSerializer serializer) {
                this.name = name;
                this.value = value;
                this.serializer = serializer;
@@ -47,11 +48,11 @@ public final class SerializedNameValuePair implements 
NameValuePair {
 
        @Override /* NameValuePair */
        public String getName() {
-               return serializer.serializePart(name, false, null);
+               return name;
        }
 
        @Override /* NameValuePair */
        public String getValue() {
-               return serializer.serializePart(value, false, null);
+               return serializer.serialize(PartType.FORM_DATA, value);
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
index 532f292..d613422 100644
--- 
a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
+++ 
b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ThirdPartyProxyResource.java
@@ -1686,4 +1686,38 @@ public class ThirdPartyProxyResource extends 
ResourceJena {
                assertClass(TestEnum.class, e.getKey());
                assertClass(TestEnum[][][].class, e.getValue().get(0));
        }
+
+       
//--------------------------------------------------------------------------------
+       // PartFormatter tests
+       
//--------------------------------------------------------------------------------
+
+       @RestMethod(name="POST", path="/partFormatters/{p1}")
+       public String partFormatter(
+               @Path("p1") String p1,
+               @Header("h1") String h1,
+               @Header("h2") String h2,
+               @Header("h3") String h3,
+               @Query("q1") String q1,
+               @Query("q2") String q2,
+               @Query("q3") String q3,
+               @FormData("f1") String f1,
+               @FormData("f2") String f2,
+               @FormData("f3") String f3
+       ) throws Exception {
+
+               assertEquals("dummy-1", p1);
+
+               assertEquals("dummy-2", h1);
+               assertEquals("dummy-3", h2);
+               assertNull(h3);
+               assertEquals("dummy-4", q1);
+               assertEquals("dummy-5", q2);
+               assertNull(q3);
+               assertEquals("dummy-6", f1);
+               assertEquals("dummy-7", f2);
+               assertNull(f3);
+
+               return "OK";
+       }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
index 59868e6..6cc497d 100644
--- 
a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
+++ 
b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ThirdPartyProxyTest.java
@@ -2137,6 +2137,15 @@ public class ThirdPartyProxyTest extends RestTestcase {
                assertEquals("OK", r);
        }
 
+       
//--------------------------------------------------------------------------------
+       // PartFormatters
+       
//--------------------------------------------------------------------------------
+       @Test
+       public void h01() throws Exception {
+               String r = proxy.partFormatters("1", "2", "3", "", "4", "5", 
"", "6", "7", "");
+               assertEquals("OK", r);
+       }
+
 
        
//--------------------------------------------------------------------------------
        // Proxy class
@@ -3373,6 +3382,24 @@ public class ThirdPartyProxyTest extends RestTestcase {
                }
 
                
//--------------------------------------------------------------------------------
+               // PartFormatters
+               
//--------------------------------------------------------------------------------
+
+               @RemoteMethod(httpMethod="POST", path="/partFormatters/{p1}")
+               String partFormatters(
+                       @Path(value="p1", serializer=DummyPartSerializer.class) 
String p1,
+                       @Header(value="h1", 
serializer=DummyPartSerializer.class) String h1,
+                       @HeaderIfNE(value="h2", 
serializer=DummyPartSerializer.class) String h2,
+                       @HeaderIfNE(value="h3", 
serializer=DummyPartSerializer.class) String h3,
+                       @Query(value="q1", 
serializer=DummyPartSerializer.class) String q1,
+                       @QueryIfNE(value="q2", 
serializer=DummyPartSerializer.class) String q2,
+                       @QueryIfNE(value="q3", 
serializer=DummyPartSerializer.class) String q3,
+                       @FormData(value="f1", 
serializer=DummyPartSerializer.class) String f1,
+                       @FormDataIfNE(value="f2", 
serializer=DummyPartSerializer.class) String f2,
+                       @FormDataIfNE(value="f3", 
serializer=DummyPartSerializer.class) String f3
+               );
+
+               
//--------------------------------------------------------------------------------
                // Test return types.
                
//--------------------------------------------------------------------------------
 
@@ -3701,4 +3728,11 @@ public class ThirdPartyProxyTest extends RestTestcase {
                        return this;
                }
        }
+
+       public static class DummyPartSerializer implements PartSerializer {
+               @Override
+               public String serialize(PartType type, Object value) {
+                       return "dummy-"+value;
+               }
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/f2fafd3e/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java 
b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
index 973452a..dfff199 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
@@ -15,6 +15,7 @@ package org.apache.juneau.rest;
 import java.net.*;
 import java.text.*;
 
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.urlencoding.*;
 
 /**
@@ -132,13 +133,13 @@ public final class Redirect {
        /**
         * Calculates the URL to redirect to.
         *
-        * @param s Use this serializer to encode arguments using the {@link 
UrlEncodingSerializer#serializePart(Object,Boolean,Boolean)} method.
+        * @param s Use this serializer to encode arguments using the {@link 
UrlEncodingSerializer#serialize(PartType,Object)} method.
         * @return The URL to redirect to.
         */
        public String toUrl(UrlEncodingSerializer s) {
                if (url != null && args != null && args.length > 0) {
                        for (int i = 0; i < args.length; i++)
-                               args[i] = s.serializePart(args[i], null, true);
+                               args[i] = s.serialize(PartType.PATH, args[i]);
                        return MessageFormat.format(url, args);
                }
                return url;

Reply via email to