http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/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 a4e6885..ef06af5 100644 --- a/juneau-core/src/main/javadoc/overview.html +++ b/juneau-core/src/main/javadoc/overview.html @@ -6231,7 +6231,9 @@ <ja>@BeanProperty</ja>(<js>"foo"</js>) </p> <li>Fixed a race condition in ClassMeta. - + <li><jsf>URLENC_paramFormat</jsf> has been moved to {@link org.apache.juneau.uon.UonSerializerContext#UON_paramFormat}, + and the UON/URL-Encoding serializers will now always serialize all values as plain text. + <br>This means that arrays and maps are converted to simple comma-delimited lists. </ul> <h6 class='topic'>org.apache.juneau.rest</h6> @@ -6463,11 +6465,11 @@ <br><ja>@Remoteable</ja> annotation has been moved to this package. <li>Updated doc: <a class='doclink' href='#Remoteable'>6 - Remoteable Services</a> <li>New doc: <a class='doclink' href='#Remoteable.3rdParty'>6.1 - Interface proxies against 3rd-party REST interfaces</a> - <li>New URL-encoding serializer setting: {@link org.apache.juneau.urlencoding.UrlEncodingSerializerContext#URLENC_paramFormat} + <li>New URL-encoding serializer setting: <code><del>UrlEncodingSerializerContext.URLENC_paramFormat</del></code>. <li>New methods on {@link org.apache.juneau.urlencoding.UrlEncodingSerializerBuilder}: <ul> <li>{@link org.apache.juneau.urlencoding.UrlEncodingSerializerBuilder#paramFormat(String) paramFormat(String)} - <li>{@link org.apache.juneau.urlencoding.UrlEncodingSerializerBuilder#plainTextParams() plainTextParams()} + <li><code><del>UrlEncodingSerializerBuilder.plainTextParams()</del></code> </ul> </ul> @@ -6626,7 +6628,7 @@ <ul> <li>{@link org.apache.juneau.rest.client.RestClientBuilder#executorService(ExecutorService,boolean) executorService(ExecutorService,boolean)} <li>{@link org.apache.juneau.rest.client.RestClientBuilder#paramFormat(String) paramFormat(ExecutorService,boolean)} - <li>{@link org.apache.juneau.rest.client.RestClientBuilder#plainTextParams() plainTextParams()} + <li><code><del>RestClientBuilder.plainTextParams()</del></code> <li>{@link org.apache.juneau.rest.client.RestClientBuilder#noTrace() noTrace()} - Adds a <code>No-Trace: true</code> header on all requests to prevent the servlet from logging errors. <br>Useful for testing scenarios when you don't want the console to end up showing errors done on purpose.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/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 769e5eb..9be6df6 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 @@ -28,6 +28,7 @@ import org.apache.http.client.config.*; import org.apache.http.client.entity.*; import org.apache.http.client.methods.*; import org.apache.http.client.utils.*; +import org.apache.http.entity.*; import org.apache.http.impl.client.*; import org.apache.http.util.*; import org.apache.juneau.*; @@ -37,6 +38,7 @@ import org.apache.juneau.internal.ObjectUtils; import org.apache.juneau.parser.*; import org.apache.juneau.parser.ParseException; import org.apache.juneau.serializer.*; +import org.apache.juneau.urlencoding.*; import org.apache.juneau.utils.*; /** @@ -191,16 +193,22 @@ public final class RestCall { uriBuilder.addParameter(name, partSerializer.serialize(PartType.QUERY, value)); } else if (value instanceof NameValuePairs) { for (NameValuePair p : (NameValuePairs)value) - query(p.getName(), p.getValue(), skipIfEmpty, partSerializer); - } else if (value instanceof String) { - String s = value.toString(); - if (! isEmpty(s)) - uriBuilder.setCustomQuery(s); + query(p.getName(), p.getValue(), skipIfEmpty, UrlEncodingSerializer.DEFAULT_PLAINTEXT); } else if (value instanceof Map) { for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) query(p.getKey(), p.getValue(), skipIfEmpty, partSerializer); - } else if (isBean(value)){ + } else if (isBean(value)) { return query(name, toBeanMap(value), skipIfEmpty, partSerializer); + } else if (value instanceof Reader) { + try { + uriBuilder.setCustomQuery(IOUtils.read(value)); + } catch (IOException e) { + throw new RestCallException(e); + } + } else if (value instanceof CharSequence) { + String s = value.toString(); + if (! isEmpty(s)) + uriBuilder.setCustomQuery(s); } else { throw new FormattedRuntimeException("Invalid name ''{0}'' passed to query(name,value,skipIfEmpty) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value)); } @@ -291,13 +299,21 @@ public final class RestCall { formData.add(new SerializedNameValuePair(name, value, partSerializer)); } else if (value instanceof NameValuePairs) { for (NameValuePair p : (NameValuePairs)value) - if (! (isEmpty(p.getValue()) && skipIfEmpty)) + if (p.getValue() != null && ! (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, partSerializer); } else if (isBean(value)) { return formData(name, toBeanMap(value), skipIfEmpty, partSerializer); + } else if (value instanceof Reader) { + contentType("application/x-www-form-urlencoded"); + input(value); + } else if (value instanceof CharSequence) { + try { + contentType("application/x-www-form-urlencoded"); + input(new StringEntity(value.toString())); + } catch (UnsupportedEncodingException e) {} } else { throw new FormattedRuntimeException("Invalid name ''{0}'' passed to formData(name,value,skipIfEmpty) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value)); } @@ -389,13 +405,13 @@ public final class RestCall { uriBuilder.setPath(newPath); } else if (value instanceof NameValuePairs) { for (NameValuePair p : (NameValuePairs)value) - path(p.getName(), p.getValue()); + path(p.getName(), p.getValue(), partSerializer); } else if (value instanceof Map) { for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) - path(p.getKey(), p.getValue()); + path(p.getKey(), p.getValue(), partSerializer); } else if (isBean(value)) { - return path(name, toBeanMap(value)); - } else { + return path(name, toBeanMap(value), partSerializer); + } else if (value != null) { throw new FormattedRuntimeException("Invalid name ''{0}'' passed to path(name,value) for data type ''{1}''", name, ClassUtils.getReadableClassNameForObject(value)); } return this; @@ -454,6 +470,7 @@ public final class RestCall { public RestCall input(final Object input) throws RestCallException { this.input = input; this.hasInput = true; + this.formData = null; return this; } @@ -508,7 +525,7 @@ public final class RestCall { request.setHeader(name, partSerializer.serialize(PartType.HEADER, value)); } else if (value instanceof NameValuePairs) { for (NameValuePair p : (NameValuePairs)value) - header(p.getName(), p.getValue(), skipIfEmpty, partSerializer); + header(p.getName(), p.getValue(), skipIfEmpty, UrlEncodingSerializer.DEFAULT_PLAINTEXT); } else if (value instanceof Map) { for (Map.Entry<String,Object> p : ((Map<String,Object>) value).entrySet()) header(p.getKey(), p.getValue(), skipIfEmpty, partSerializer); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/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 8f26768..910a9fb 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 @@ -567,39 +567,42 @@ public class RestClient extends CoreObject { if (rmm.getRequestBeanArgs().length > 0) { BeanSession bs = getBeanContext().createSession(); - for (Integer i : rmm.getRequestBeanArgs()) { - BeanMap<?> bm = bs.toBeanMap(args[i]); - for (BeanPropertyValue bpv : bm.getValues(true)) { + for (RemoteMethodArg rma : rmm.getRequestBeanArgs()) { + BeanMap<?> bm = bs.toBeanMap(args[rma.index]); + + for (BeanPropertyValue bpv : bm.getValues(false)) { BeanPropertyMeta pMeta = bpv.getMeta(); Object val = bpv.getValue(); Path p = pMeta.getAnnotation(Path.class); if (p != null) - rc.path(getName(p.value(), pMeta), val, getPartSerializer(p.serializer())); + rc.path(getName(p.name(), p.value(), pMeta), val, getPartSerializer(p.serializer(), rma.serializer)); - Query q1 = pMeta.getAnnotation(Query.class); - if (q1 != null) - rc.query(getName(q1.value(), pMeta), val, false, getPartSerializer(q1.serializer())); + if (val != null) { + Query q1 = pMeta.getAnnotation(Query.class); + if (q1 != null) + rc.query(getName(q1.name(), q1.value(), pMeta), val, q1.skipIfEmpty(), getPartSerializer(q1.serializer(), rma.serializer)); - QueryIfNE q2 = pMeta.getAnnotation(QueryIfNE.class); - if (q2 != null) - rc.query(getName(q2.value(), pMeta), val, true, getPartSerializer(q2.serializer())); + QueryIfNE q2 = pMeta.getAnnotation(QueryIfNE.class); + if (q2 != null) + rc.query(getName(q2.name(), q2.value(), pMeta), val, true, getPartSerializer(q2.serializer(), rma.serializer)); - FormData f1 = pMeta.getAnnotation(FormData.class); - if (f1 != null) - rc.formData(getName(f1.value(), pMeta), val, false, getPartSerializer(f1.serializer())); + FormData f1 = pMeta.getAnnotation(FormData.class); + if (f1 != null) + rc.formData(getName(f1.name(), f1.value(), pMeta), val, f1.skipIfEmpty(), getPartSerializer(f1.serializer(), rma.serializer)); - FormDataIfNE f2 = pMeta.getAnnotation(FormDataIfNE.class); - if (f2 != null) - rc.formData(getName(f2.value(), pMeta), val, true, getPartSerializer(f2.serializer())); + FormDataIfNE f2 = pMeta.getAnnotation(FormDataIfNE.class); + if (f2 != null) + rc.formData(getName(f2.name(), f2.value(), pMeta), val, true, getPartSerializer(f2.serializer(), rma.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, getPartSerializer(h1.serializer())); + org.apache.juneau.remoteable.Header h1 = pMeta.getAnnotation(org.apache.juneau.remoteable.Header.class); + if (h1 != null) + rc.header(getName(h1.name(), h1.value(), pMeta), val, h1.skipIfEmpty(), getPartSerializer(h1.serializer(), rma.serializer)); - HeaderIfNE h2 = pMeta.getAnnotation(HeaderIfNE.class); - if (h2 != null) - rc.header(getName(h2.value(), pMeta), val, true, getPartSerializer(h2.serializer())); + HeaderIfNE h2 = pMeta.getAnnotation(HeaderIfNE.class); + if (h2 != null) + rc.header(getName(h2.name(), h2.value(), pMeta), val, true, getPartSerializer(h2.serializer(), rma.serializer)); + } } } } @@ -628,14 +631,20 @@ public class RestClient extends CoreObject { } } - private static String getName(String name, BeanPropertyMeta pMeta) { - if ("*".equals(name) && ! pMeta.getClassMeta().isMapOrBean()) - name = pMeta.getName(); - return name; + private static String getName(String name1, String name2, BeanPropertyMeta pMeta) { + String n = name1.isEmpty() ? name2 : name1; + ClassMeta<?> cm = pMeta.getClassMeta(); + if (n.isEmpty() && (cm.isMapOrBean() || cm.isReader() || cm.isInstanceOf(NameValuePairs.class))) + n = "*"; + if (n.isEmpty()) + n = pMeta.getName(); + return n; } - private static PartSerializer getPartSerializer(Class c) { - if (c == UrlEncodingSerializer.class) + private static PartSerializer getPartSerializer(Class c, PartSerializer c2) { + if (c2 != null) + return c2; + if (c == PartSerializer.class) return null; PartSerializer pf = partSerializerCache.get(c); if (pf == null) { http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/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 463920a..65b4dc8 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 @@ -46,6 +46,7 @@ import org.apache.juneau.internal.*; import org.apache.juneau.json.*; import org.apache.juneau.parser.*; import org.apache.juneau.serializer.*; +import org.apache.juneau.uon.*; import org.apache.juneau.urlencoding.*; /** @@ -1101,7 +1102,7 @@ public class RestClientBuilder extends CoreObjectBuilder { } /** - * Sets the {@link UrlEncodingSerializerContext#URLENC_paramFormat} property on the URL-encoding serializers in this group. + * Sets the {@link UonSerializerContext#UON_paramFormat} property on the URL-encoding serializers in this group. * <p> * This overrides the behavior of the URL-encoding serializer to quote and escape characters * in query names and values that may be confused for UON notation (e.g. <js>"'(foo=123)'"</js>, <js>"'@(1,2,3)'"</js>). @@ -1109,20 +1110,34 @@ public class RestClientBuilder extends CoreObjectBuilder { * * @param value The new value for this property. * @return This object (for method chaining). - * @see UrlEncodingSerializerContext#URLENC_paramFormat + * @see UonSerializerContext#UON_paramFormat */ public RestClientBuilder paramFormat(String value) { - super.property(UrlEncodingSerializerContext.URLENC_paramFormat, value); + super.property(UonSerializerContext.UON_paramFormat, value); return this; } /** * Shortcut for calling <code>paramFormat(<js>"PLAINTEXT"</js>)</code>. + * <p> + * The default behavior is to serialize part values (query parameters, form data, headers, path variables) in UON notation. + * Calling this method forces plain-text to be used instead. + * <p> + * Specifially, UON notation has the following effects: + * <ul> + * <li>Boolean strings (<js>"true"</js>/<js>"false"</js>) and numeric values (<js>"123"</js>) will be + * quoted (<js>"'true'"</js>, <js>"'false'"</js>, <js>"'123'"</js>. + * <br>This allows them to be differentiated from actual boolean and numeric values. + * <li>String such as <js>"(foo='bar')"</js> that mimic UON structures will be quoted and escaped to + * <js>"'(foo=bar~'baz~')'"</js>. + * </ul> + * <p> + * The downside to using plain text part serialization is that you cannot serialize arbitrary POJOs. * * @return This object (for method chaining). */ - public RestClientBuilder plainTextParams() { - super.property(UrlEncodingSerializerContext.URLENC_paramFormat, "PLAINTEXT"); + public RestClientBuilder plainTextParts() { + super.property(UonSerializerContext.UON_paramFormat, "PLAINTEXT"); return this; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RequestBeanProxyResource.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RequestBeanProxyResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RequestBeanProxyResource.java new file mode 100644 index 0000000..eed0679 --- /dev/null +++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RequestBeanProxyResource.java @@ -0,0 +1,50 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.rest.test; + + +import java.io.*; + +import org.apache.juneau.microservice.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.rest.annotation.*; + +/** + * Validates the functionality of <ja>@RequestBeans</ja>. + */ +@RestResource( + path="/testRequestBeanProxy" +) +@SuppressWarnings("serial") +public class RequestBeanProxyResource extends ResourceJena { + + @RestMethod(name="GET", path="/echoQuery") + public Reader echoQuery(RestRequest req) throws Exception { + return new StringReader(req.getQuery().toString(true)); + } + + @RestMethod(name="POST", path="/echoFormData") + public Reader echoFormData(RestRequest req) throws Exception { + return new StringReader(req.getFormData().toString(true)); + } + + @RestMethod(name="GET", path="/echoHeaders") + public Reader echoHeaders(RestRequest req) throws Exception { + return new StringReader(req.getHeaders().subset("a,b,c,d,e,f,g,h,i,a1,a2,a3,a4,b1,b2,b3,b4,c1,c2,c3,c4").toString(true)); + } + + @RestMethod(name="GET", path="/echoPath/*") + public Reader echoPath(RestRequest req) throws Exception { + return new StringReader(req.getPathMatch().getRemainder()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java index f04181b..3bee867 100644 --- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java +++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java @@ -60,6 +60,7 @@ import org.apache.juneau.rest.labels.*; PathVariablesResource.class, PropertiesResource.class, QueryResource.class, + RequestBeanProxyResource.class, RestClient2Resource.class, SerializersResource.class, StaticFilesResource.class, http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/93aadae1/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/FormDataTest.java ---------------------------------------------------------------------- diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/FormDataTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/FormDataTest.java index 9aeff98..d0f1874 100644 --- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/FormDataTest.java +++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/FormDataTest.java @@ -68,7 +68,7 @@ public class FormDataTest extends RestTestcase { //==================================================================================================== @Test public void testPlainTextParams() throws Exception { - RestClient c = TestMicroservice.client(UrlEncodingSerializer.class, UrlEncodingParser.class).plainTextParams().build(); + RestClient c = TestMicroservice.client(UrlEncodingSerializer.class, UrlEncodingParser.class).plainTextParts().build(); String r; Map<String,Object> m = new AMap<String,Object>()
