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<String,Object></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<String,Object></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<String,Object></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<String,Object></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 > 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 > 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;
