You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/06/20 01:29:32 UTC

incubator-juneau git commit: Add @RemoteMethod(returns) annotation.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master 95ee1b536 -> 35cb8461b


Add @RemoteMethod(returns) annotation.

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

Branch: refs/heads/master
Commit: 35cb8461bf0b4dab5214cb74843f28636827d809
Parents: 95ee1b5
Author: JamesBognar <ja...@apache.org>
Authored: Mon Jun 19 21:29:23 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Mon Jun 19 21:29:23 2017 -0400

----------------------------------------------------------------------
 .../main/java/org/apache/juneau/ClassMeta.java  | 45 +---------
 .../org/apache/juneau/internal/ClassUtils.java  | 31 +++++++
 .../apache/juneau/remoteable/RemoteMethod.java  | 29 +++++-
 .../juneau/remoteable/RemoteableMethodMeta.java | 13 +++
 .../apache/juneau/remoteable/ReturnValue.java   | 25 ++++++
 .../java/org/apache/juneau/utils/PojoQuery.java |  1 +
 juneau-core/src/main/javadoc/overview.html      |  1 +
 .../apache/juneau/rest/client/RestClient.java   | 17 +++-
 .../rest/test/ThirdPartyProxyResource.java      | 14 +++
 .../juneau/rest/test/ThirdPartyProxyTest.java   | 75 ++++++++++++++++
 .../java/org/apache/juneau/rest/CallMethod.java |  2 +-
 .../apache/juneau/rest/widget/QueryWidget.html  | 92 ++++++++++++++------
 12 files changed, 271 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
index 786d60a..1545511 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java
@@ -111,15 +111,6 @@ public final class ClassMeta<T> implements Type {
 	private final BeanRegistry beanRegistry;                // The bean registry of this class meta (if it has one).
 	private final ClassMeta<?>[] args;                      // Arg types if this is an array of args.
 
-	private static final Boolean BOOLEAN_DEFAULT = false;
-	private static final Character CHARACTER_DEFAULT = (char)0;
-	private static final Short SHORT_DEFAULT = (short)0;
-	private static final Integer INTEGER_DEFAULT = 0;
-	private static final Long LONG_DEFAULT = 0l;
-	private static final Float FLOAT_DEFAULT = 0f;
-	private static final Double DOUBLE_DEFAULT = 0d;
-	private static final Byte BYTE_DEFAULT = (byte)0;
-
 	private ReadWriteLock lock = new ReentrantReadWriteLock(false);
 	private Lock rLock = lock.readLock(), wLock = lock.writeLock();
 
@@ -521,41 +512,7 @@ public final class ClassMeta<T> implements Type {
 				}
 			}
 
-			if (c.isPrimitive()) {
-				if (c == Boolean.TYPE)
-					primitiveDefault = BOOLEAN_DEFAULT;
-				else if (c == Character.TYPE)
-					primitiveDefault = CHARACTER_DEFAULT;
-				else if (c == Short.TYPE)
-					primitiveDefault = SHORT_DEFAULT;
-				else if (c == Integer.TYPE)
-					primitiveDefault = INTEGER_DEFAULT;
-				else if (c == Long.TYPE)
-					primitiveDefault = LONG_DEFAULT;
-				else if (c == Float.TYPE)
-					primitiveDefault = FLOAT_DEFAULT;
-				else if (c == Double.TYPE)
-					primitiveDefault = DOUBLE_DEFAULT;
-				else if (c == Byte.TYPE)
-					primitiveDefault = BYTE_DEFAULT;
-			} else {
-				if (c == Boolean.class)
-					primitiveDefault = BOOLEAN_DEFAULT;
-				else if (c == Character.class)
-					primitiveDefault = CHARACTER_DEFAULT;
-				else if (c == Short.class)
-					primitiveDefault = SHORT_DEFAULT;
-				else if (c == Integer.class)
-					primitiveDefault = INTEGER_DEFAULT;
-				else if (c == Long.class)
-					primitiveDefault = LONG_DEFAULT;
-				else if (c == Float.class)
-					primitiveDefault = FLOAT_DEFAULT;
-				else if (c == Double.class)
-					primitiveDefault = DOUBLE_DEFAULT;
-				else if (c == Byte.class)
-					primitiveDefault = BYTE_DEFAULT;
-			}
+			primitiveDefault = ClassUtils.getPrimitiveDefault(c);
 
 			for (Method m : c.getMethods())
 				if (isPublic(m) && isNotDeprecated(m))

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 0f52ed5..6a3e812 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -18,6 +18,7 @@ import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.utils.*;
 
 /**
  * Class-related utility methods.
@@ -923,4 +924,34 @@ public final class ClassUtils {
 		}
 		return l;
 	}
+
+	/**
+	 * Returns the default value for the specified primitive class.
+	 *
+	 * @param primitiveClass The primitive class to get the default value for.
+	 * @return The default value, or <jk>null</jk> if the specified class is not a primitive class.
+	 */
+	public static Object getPrimitiveDefault(Class<?> primitiveClass) {
+		return primitiveDefaultMap.get(primitiveClass);
+	}
+
+	private static final Map<Class<?>,Object> primitiveDefaultMap = Collections.unmodifiableMap(
+		new AMap<Class<?>,Object>()
+			.append(Boolean.TYPE, false)
+			.append(Character.TYPE, (char)0)
+			.append(Short.TYPE, (short)0)
+			.append(Integer.TYPE, 0)
+			.append(Long.TYPE, 0l)
+			.append(Float.TYPE, 0f)
+			.append(Double.TYPE, 0d)
+			.append(Byte.TYPE, (byte)0)
+			.append(Boolean.class, false)
+			.append(Character.class, (char)0)
+			.append(Short.class, (short)0)
+			.append(Integer.class, 0)
+			.append(Long.class, 0l)
+			.append(Float.class, 0f)
+			.append(Double.class, 0d)
+			.append(Byte.class, (byte)0)
+	);
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
index 97cd3be..0169033 100644
--- a/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/RemoteMethod.java
@@ -55,7 +55,7 @@ public @interface RemoteMethod {
 	String path() default "";
 
 	/**
-	 * Defines whether to use <code>GET</code> or <code>POST</code> for REST calls.
+	 * Defines the HTTP method to use for REST calls.
 	 * <p>
 	 * Possible values:
 	 * <ul>
@@ -66,4 +66,31 @@ public @interface RemoteMethod {
 	 * The default value is <js>"POST"</js>.
 	 */
 	String httpMethod() default "POST";
+
+	/**
+	 * The value the remoteable method returns.
+	 * <p>
+	 * Possible values:
+	 * <ul>
+	 * 	<li>{@link ReturnValue#BODY} (default) - The body of the HTTP response converted to a POJO.
+	 * 		<br>The return type on the Java method can be any of the following:
+	 * 		<ul>
+	 * 			<li><jk>void</jk> - Don't parse any response.  Note that the method will still throw an exception if an
+	 * 					error HTTP status is returned.
+	 * 			<li>Any parsable POJO - The body of the response will be converted to the POJO using the parser defined
+	 * 					on the <code>RestClient</code>.
+	 * 			<li><code>HttpResponse</code> - Returns the raw <code>HttpResponse</code> returned by the inner
+	 * 					<code>HttpClient</code>.
+	 * 			<li>{@link Reader} - Returns access to the raw reader of the response.
+	 * 			<li>{@link InputStream} - Returns access to the raw input stream of the response.
+	 * 		</ul>
+	 * 	<li>{@link ReturnValue#HTTP_STATUS} - The HTTP status code on the response.
+	 * 		<br>The return type on the Java method can be any of the following:
+	 * 		<ul>
+	 * 			<li><jk>int</jk>/<code>Integer</code> - The HTTP response code.
+	 * 			<li><jk>boolean</jk>/<code>Boolean</code> - <jk>true</jk> if the response code is <code>&lt;400</code>
+	 * 		</ul>
+	 * </ul>
+	 */
+	ReturnValue returns() default ReturnValue.BODY;
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/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 8430ae8..2db986e 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
@@ -37,6 +37,7 @@ public class RemoteableMethodMeta {
 	private final RemoteMethodArg[] pathArgs, queryArgs, headerArgs, formDataArgs, requestBeanArgs;
 	private final Integer[] otherArgs;
 	private final Integer bodyArg;
+	private final ReturnValue returnValue;
 
 	/**
 	 * Constructor.
@@ -55,6 +56,7 @@ public class RemoteableMethodMeta {
 		this.requestBeanArgs = b.requestBeanArgs.toArray(new RemoteMethodArg[b.requestBeanArgs.size()]);
 		this.otherArgs = b.otherArgs.toArray(new Integer[b.otherArgs.size()]);
 		this.bodyArg = b.bodyArg;
+		this.returnValue = b.returnValue;
 	}
 
 	private static class Builder {
@@ -68,6 +70,7 @@ public class RemoteableMethodMeta {
 		private List<Integer>
 			otherArgs = new LinkedList<Integer>();
 		private Integer bodyArg;
+		private ReturnValue returnValue;
 
 		private Builder(String restUrl, Method m) {
 			Remoteable r = m.getDeclaringClass().getAnnotation(Remoteable.class);
@@ -83,6 +86,8 @@ public class RemoteableMethodMeta {
 			if (! isOneOf(methodPaths, "NAME", "SIGNATURE"))
 				throw new RemoteableMetadataException(m, "Invalid value specified for @Remoteable.methodPaths() annotation.  Valid values are [NAME,SIGNATURE].");
 
+			returnValue = rm == null ? ReturnValue.BODY : rm.returns();
+
 			url =
 				trimSlashes(restUrl)
 				+ '/'
@@ -206,4 +211,12 @@ public class RemoteableMethodMeta {
 	public Integer getBodyArg() {
 		return bodyArg;
 	}
+
+	/**
+	 * Returns whether the method returns the HTTP response body or status code.
+	 * @return Whether the method returns the HTTP response body or status code.
+	 */
+	public ReturnValue getReturns() {
+		return returnValue;
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-core/src/main/java/org/apache/juneau/remoteable/ReturnValue.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/remoteable/ReturnValue.java b/juneau-core/src/main/java/org/apache/juneau/remoteable/ReturnValue.java
new file mode 100644
index 0000000..a6ebc3e
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/remoteable/ReturnValue.java
@@ -0,0 +1,25 @@
+// ***************************************************************************************************************************
+// * 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.remoteable;
+
+/**
+ * Possible values for the {@link RemoteMethod#returns()} annotation.
+ */
+public enum ReturnValue {
+
+	/** HTTP response body */
+	BODY, 
+	
+	/** HTTP status code */
+	HTTP_STATUS;
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java b/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java
index 737fb32..17292ef 100644
--- a/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java
+++ b/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java
@@ -238,6 +238,7 @@ public final class PojoQuery {
 		int limit = args.getLimit();
 		if (pos != 0 || limit != 0) {
 			int end = (limit == 0 || limit+pos >= l.size()) ? l.size() : limit + pos;
+			pos = Math.min(pos, l.size());
 			ObjectList l2 = new DelegateList(((DelegateList)l).getClassMeta());
 			l2.addAll(l.subList(pos, end));
 			l = l2;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/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 12d6c29..15aa1eb 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6183,6 +6183,7 @@
 			<li>{@link org.apache.juneau.utils.PojoQuery} improvements.
 				<br>Search columns containing lists and maps.
 				<br>Sort columns containing lists and maps.
+			<li>New {@link org.apache.juneau.remoteable.RemoteMethod#returns()} annotation.
 		</ul>
 
 		<h6 class='topic'>org.apache.juneau.rest</h6>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/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 484d934..6d06825 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
@@ -30,6 +30,7 @@ import org.apache.http.client.utils.*;
 import org.apache.http.entity.*;
 import org.apache.http.impl.client.*;
 import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.remoteable.*;
@@ -642,7 +643,21 @@ public class RestClient extends CoreObject {
 								rc.input(otherArgs);
 							}
 
-							return rc.getResponse(method.getGenericReturnType());
+							if (rmm.getReturns() == ReturnValue.HTTP_STATUS) {
+								rc.ignoreErrors();
+								int returnCode = rc.run();
+								Class<?> rt = method.getReturnType();
+								if (rt == Integer.class || rt == int.class)
+									return returnCode;
+								if (rt == Boolean.class || rt == boolean.class)
+									return returnCode < 400;
+								throw new RestCallException("Invalid return type on method annotated with @RemoteableMethod(returns=HTTP_STATUS).  Only integer and booleans types are valid.");
+							}
+
+							Object v = rc.getResponse(method.getGenericReturnType());
+							if (v == null && method.getReturnType().isPrimitive())
+								v = ClassUtils.getPrimitiveDefault(method.getReturnType());
+							return v;
 
 						} catch (RestCallException e) {
 							// Try to throw original exception if possible.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/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 d613422..157e20d 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
@@ -20,6 +20,7 @@ import static org.junit.Assert.*;
 import java.util.*;
 
 import org.apache.juneau.microservice.*;
+import org.apache.juneau.rest.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.test.pojos.*;
 import org.apache.juneau.utils.*;
@@ -1720,4 +1721,17 @@ public class ThirdPartyProxyResource extends ResourceJena {
 		return "OK";
 	}
 
+	//--------------------------------------------------------------------------------
+	// @RemoteableMethod(returns=HTTP_STATUS)
+	//--------------------------------------------------------------------------------
+
+	@RestMethod(name="GET", path="/httpStatusReturn200")
+	public void httpStatusReturn200(RestResponse res) {
+		res.setStatus(200);
+	}
+
+	@RestMethod(name="GET", path="/httpStatusReturn404")
+	public void httpStatusReturn404(RestResponse res) {
+		res.setStatus(404);
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/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 fe02c60..b6c0e5b 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
@@ -2147,6 +2147,55 @@ public class ThirdPartyProxyTest extends RestTestcase {
 		assertEquals("OK", r);
 	}
 
+	//--------------------------------------------------------------------------------
+	// @RemoteableMethod(returns=HTTP_STATUS)
+	//--------------------------------------------------------------------------------
+	@Test
+	public void i01a() throws Exception {
+		int r = proxy.httpStatusReturnInt200();
+		assertEquals(200, r);
+	}
+
+	@Test
+	public void i01b() throws Exception {
+		Integer r = proxy.httpStatusReturnInteger200();
+		assertEquals(200, r.intValue());
+	}
+
+	@Test
+	public void i01c() throws Exception {
+		int r = proxy.httpStatusReturnInt404();
+		assertEquals(404, r);
+	}
+
+	@Test
+	public void i01d() throws Exception {
+		Integer r = proxy.httpStatusReturnInteger404();
+		assertEquals(404, r.intValue());
+	}
+
+	@Test
+	public void i02a() throws Exception {
+		boolean r = proxy.httpStatusReturnBool200();
+		assertEquals(true, r);
+	}
+
+	@Test
+	public void i02b() throws Exception {
+		Boolean r = proxy.httpStatusReturnBoolean200();
+		assertEquals(true, r);
+	}
+
+	@Test
+	public void i02c() throws Exception {
+		boolean r = proxy.httpStatusReturnBool404();
+		assertEquals(false, r);
+	}
+
+	public void i02d() throws Exception {
+		Boolean r = proxy.httpStatusReturnBoolean404();
+		assertEquals(false, r);
+	}
 
 	//--------------------------------------------------------------------------------
 	// Proxy class
@@ -3716,6 +3765,32 @@ public class ThirdPartyProxyTest extends RestTestcase {
 
 		@RemoteMethod(httpMethod="POST", path="/setEnum1d3dListMap")
 		void setEnum1d3dListMap(@Body Map<TestEnum,List<TestEnum[][][]>> x);
+
+		// Method returns status code
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn200", returns=ReturnValue.HTTP_STATUS)
+		int httpStatusReturnInt200();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn200", returns=ReturnValue.HTTP_STATUS)
+		Integer httpStatusReturnInteger200();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn404", returns=ReturnValue.HTTP_STATUS)
+		int httpStatusReturnInt404();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn404", returns=ReturnValue.HTTP_STATUS)
+		Integer httpStatusReturnInteger404();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn200", returns=ReturnValue.HTTP_STATUS)
+		boolean httpStatusReturnBool200();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn200", returns=ReturnValue.HTTP_STATUS)
+		Boolean httpStatusReturnBoolean200();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn404", returns=ReturnValue.HTTP_STATUS)
+		boolean httpStatusReturnBool404();
+
+		@RemoteMethod(httpMethod="GET", path="/httpStatusReturn404", returns=ReturnValue.HTTP_STATUS)
+		Boolean httpStatusReturnBoolean404();
 	}
 
 	// Bean for testing NE annotations.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
index 2563442..207e312 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
@@ -811,7 +811,7 @@ class CallMethod implements Comparable<CallMethod>  {
 			throw new RestException(SC_BAD_REQUEST,
 				"Invalid argument type passed to the following method: ''{0}''.\n\tArgument types: {1}",
 				method.toString(), getReadableClassNames(args)
-			);
+			).initCause(e);
 		} catch (InvocationTargetException e) {
 			Throwable e2 = e.getTargetException();		// Get the throwable thrown from the doX() method.
 			if (e2 instanceof RestException)

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/35cb8461/juneau-rest/src/main/resources/org/apache/juneau/rest/widget/QueryWidget.html
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/resources/org/apache/juneau/rest/widget/QueryWidget.html b/juneau-rest/src/main/resources/org/apache/juneau/rest/widget/QueryWidget.html
index 02c4adf..d89516f 100644
--- a/juneau-rest/src/main/resources/org/apache/juneau/rest/widget/QueryWidget.html
+++ b/juneau-rest/src/main/resources/org/apache/juneau/rest/widget/QueryWidget.html
@@ -63,71 +63,109 @@
 	.tooltiptext {
 		white-space: nowrap;
 		float: left;
+		border: 1px solid black;
 	}
 </style>
 <form id='queryForm' style='display:inline'>
 	<table id='query' class='queryInput hidden'>
 		<tr>
-			<th>
+			<th>Search:</th>
+			<td>
+				<input name="s" size="50" value='$R{query.s}'>
+			</td>
+			<td>
 				<div class="tooltip">
-					Search <small>(?)</small>:
+					<small>(?)</small>
 					<span class="tooltiptext">
 						Comma-delimited list of key/value pair search terms.
-						<br>Keys are column names.
-						<br>Values are search terms.
-						<br>Refer to the <code>Queryable</code> javadocs for a full explanation of possible search terms.
-						<br>Example: <code>column1=foo*, column2&lt;100, column3=2013-2016.06.30</code>
+						<br>
+						<br>Keys are column names.  Values are search terms.
+						<br>
+						<br><b>Example:</b>&nbsp;&nbsp;<code>[column1=foo*, column2&lt;100, column3=2013-2016.06.30]</code>
+						<br>
+						<br><b>String fields:</b>
+						<br> - <code>'*'</code> represents any character
+						<br> - <code>'?'</code> represents one character
+						<br> - Use single or double quotes for phrases
+						<br>&nbsp;&nbsp;&nbsp;e.g. <code>[column='foo bar']</code> - The term 'foo bar'
+						<br> - Multiple search terms are ORed 
+						<br>&nbsp;&nbsp;&nbsp;e.g. <code>[column=foo bar]</code> - 'foo' OR 'bar'
+						<br> - Prepend <code>'+'</code> on tokens that must match
+						<br>&nbsp;&nbsp;&nbsp;e.g. <code>[column=+foo* +*bar]</code> - Start with 'foo' AND end with 'bar'.
+						<br> - Prepend <code>'-'</code> on tokens that must not match 
+						<br>&nbsp;&nbsp;&nbsp;e.g. <code>[column=+foo* -*bar]</code> - Start with 'foo' AND does not end with 'bar'.
+						<br>
+						<br><b>Numeric fields:</b>
+						<br><code>[column=123]</code> - A single number
+						<br><code>[column=1 2 3]</code>	- Multiple numbers
+						<br><code>[column=1-100]</code> - Between two numbers
+						<br><code>[column=1-100 200-300]</code> - Two ranges of numbers
+						<br><code>[column&gt;100]</code> - Greater than a number
+						<br><code>[column&gt;=100]</code> - Greater than or equal to a number
+						<br><code>[column=!123]</code> - Not a specific number
+						<br>
+						<br><b>Date/Calendar fields:</b>
+						<br><code>[column=2001]</code> - A specific year
+						<br><code>[column=2001.01.01.10.50]</code> - A specific time
+						<br><code>[column=&gt;2001]</code> - After a specific year
+						<br><code>[column=&gt;=2001]</code> - During or after a specific year
+						<br><code>[column=2001-2003.06.30]</code> - A date range
+						<br><code>[column=2001 2003 2005]</code> - Multiple ORed dates
 					</span>
 				</div>
-			</th>
-			<td>
-				<input name="s" size="50" value='$R{query.s}'>
 			</td>
 		</tr>
 		<tr>
-			<th>
+			<th>View:</th>
+			<td>
+				<input name="v" size="50" value='$R{query.v}'>
+			</td>
+			<td>
 				<div class="tooltip">
-					View <small>(?)</small>:
+					<small>(?)</small>
 					<span class="tooltiptext">
 						Comma-delimited list of columns to display.
-						<br>Example: <code>column1, column2</code>
+						<br>
+						<br><b>Example:</b>&nbsp;&nbsp;<code>[column1, column2]</code>
 					</span>
 				</div>
-			</th>
-			<td>
-				<input name="v" size="50" value='$R{query.v}'>
 			</td>
 		</tr>
 		<tr>
-			<th>
+			<th>Sort:</th>
+			<td>
+				<input name="o" size="50" value='$R{query.o}'>
+			</td>
+			<td>
 				<div class="tooltip">
-					Sort <small>(?)</small>:
+					<small>(?)</small>
 					<span class="tooltiptext">
 						Comma-delimited list of columns to sort by.
 						<br>Columns can be suffixed with '-' to indicate descending order.
-						<br>Example: <code>column1, column2-</code>
+						<br>
+						<br><b>Example:</b>&nbsp;&nbsp;<code>[column1, column2-]</code>
+						<br>
+						<br><b>Notes:</b>
+						<br> - Columns containing collections/arrays/lists are sorted by size.
 					</span>
 				</div>
-			</th>
-			<td>
-				<input name="o" size="50" value='$R{query.o}'>
 			</td>
 		</tr>
 		<tr>
-			<th>
-				Page:
-			</th>
+			<th>Page:</th>
 			<td>
 				Position: <input name='p' type='number' style='width:50px' step=20 min=0 value='$R{query.p}'>
 				Limit: <input name='l' type='number' style='width:50px' step=20 min=0 value='$R{query.l}'>
-				Ignore-case: <input name='i' type='checkbox' value='true'>
+				<span style='float:right'>Ignore-case: <input name='i' type='checkbox' value='true'></span>
 			</td>
-		</tr>
+			<td>
+			</td>
+		</tr> 
 		<tr>
 			<th>
 				&nbsp;
 			</th>
-			<td style='float:right'>
+			<td colspan='2' style='text-align:right'>
 				<input type='reset' value='Reset'>
 				<input type='button' value='Cancel' onclick='getElementById("query").classList.add("hidden")'>
 				<input type="submit" value='Submit'>