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/05/07 20:16:13 UTC
[1/3] incubator-juneau git commit: Break up RestRequest into
different functional classes.
Repository: incubator-juneau
Updated Branches:
refs/heads/master fa4736b6d -> 321f6bdee
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
index c35e0bd..dfced43 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -31,12 +31,10 @@ import javax.servlet.http.*;
import org.apache.juneau.*;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.encoders.*;
-import org.apache.juneau.encoders.Encoder;
import org.apache.juneau.http.*;
import org.apache.juneau.ini.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
-import org.apache.juneau.parser.ParseException;
import org.apache.juneau.serializer.*;
import org.apache.juneau.svl.*;
import org.apache.juneau.uon.*;
@@ -70,26 +68,22 @@ public final class RestRequest extends HttpServletRequestWrapper {
private final String method;
private String pathRemainder;
- private byte[] body;
+ private RequestBody body;
private Method javaMethod;
private ObjectMap properties;
private SerializerGroup serializerGroup;
private ParserGroup parserGroup;
- private EncoderGroup encoders;
- private Encoder encoder;
- private int contentLength;
private final boolean debug;
private UrlEncodingParser urlEncodingParser; // The parser used to parse URL attributes and parameters (beanContext also used to parse headers)
private BeanSession beanSession;
private VarResolverSession varSession;
- private final Map<String,String[]> queryParams;
- private Map<String,String> formData;
- private final Map<String,String> defaultServletHeaders;
- private Map<String,String> defaultMethodHeaders, overriddenHeaders, overriddenQueryParams, overriddenFormDataParams, pathParameters;
+ private final RequestQuery queryParams;
+ private RequestFormData formData;
+ private RequestPathParams pathParams;
private boolean isPost;
private String servletURI, relativeServletURI;
private String charset, defaultCharset;
- private ObjectMap headers;
+ private RequestHeaders headers;
private ConfigFile cf;
private Swagger swagger, fileSwagger;
private String pageTitle, pageText, pageLinks;
@@ -106,33 +100,45 @@ public final class RestRequest extends HttpServletRequestWrapper {
// If this is a POST, we want to parse the query parameters ourselves to prevent
// the servlet code from processing the HTTP body as URL-Encoded parameters.
+ queryParams = new RequestQuery();
if (isPost)
- queryParams = context.getUrlEncodingParser().parseIntoSimpleMap(getQueryString());
- else {
- queryParams = req.getParameterMap();
- }
+ context.getUrlEncodingParser().parseIntoSimpleMap(getQueryString(), queryParams);
+ else
+ queryParams.putAll(req.getParameterMap());
+
// Get the HTTP method.
// Can be overridden through a "method" GET attribute.
String _method = super.getMethod();
- String m = getQueryParameter("method");
+ String m = getQuery("method");
if (context.allowMethodParam(m))
_method = m;
method = _method;
+ headers = new RequestHeaders();
+ for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) {
+ String name = e.nextElement();
+ headers.put(name, super.getHeaders(name));
+ }
+
+ body = new RequestBody(this);
+
if (context.isAllowBodyParam()) {
- String b = getQueryParameter("body");
+ String b = getQuery("body");
if (b != null) {
- setHeader("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
- this.body = b.getBytes(IOUtils.UTF8);
+ headers.put("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
+ body.load(b.getBytes(IOUtils.UTF8));
}
}
- defaultServletHeaders = context.getDefaultRequestHeaders();
+ if (context.isAllowHeaderParams())
+ headers.setQueryParams(queryParams);
- debug = "true".equals(getQueryParameter("debug", "false")) || "true".equals(getHeader("Debug", "false"));
+ debug = "true".equals(getQuery("debug", "false")) || "true".equals(getHeader("Debug", "false"));
+
+ this.pathParams = new RequestPathParams();
} catch (RestException e) {
throw e;
@@ -149,13 +155,28 @@ public final class RestRequest extends HttpServletRequestWrapper {
this.javaMethod = javaMethod;
this.pathRemainder = pathRemainder;
this.properties = properties;
- this.defaultMethodHeaders = mDefaultRequestHeaders;
- this.serializerGroup = mSerializers;
- this.parserGroup = mParsers;
this.urlEncodingParser = mUrlEncodingParser;
this.beanSession = urlEncodingParser.getBeanContext().createSession();
+ this.pathParams
+ .setParser(urlEncodingParser)
+ .setBeanSession(beanSession);
+ this.queryParams
+ .setParser(urlEncodingParser)
+ .setBeanSession(beanSession);
+ this.headers
+ .addDefault(mDefaultRequestHeaders)
+ .addDefault(context.getDefaultRequestHeaders())
+ .setParser(urlEncodingParser)
+ .setBeanSession(beanSession);
+ this.body
+ .setEncoders(encoders)
+ .setParsers(mParsers)
+ .setHeaders(headers)
+ .setBeanSession(beanSession)
+ .setUrlEncodingParser(mUrlEncodingParser);
+ this.serializerGroup = mSerializers;
+ this.parserGroup = mParsers;
this.defaultCharset = defaultCharset;
- this.encoders = encoders;
this.pageTitle = pageTitle;
this.pageText = pageText;
this.pageLinks = pageLinks;
@@ -209,188 +230,49 @@ public final class RestRequest extends HttpServletRequestWrapper {
//--------------------------------------------------------------------------------
/**
- * Sets a request header value.
+ * Returns the headers on this request.
+ *
+ * @return The headers on this request. Never <jk>null</jk>.
+ */
+ public RequestHeaders getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Convenience method for calling <code>getHeaders().put(name, value);</code>
*
* @param name The header name.
* @param value The header value.
*/
public void setHeader(String name, Object value) {
- if (overriddenHeaders == null)
- overriddenHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
- overriddenHeaders.put(name, StringUtils.toString(value));
+ getHeaders().put(name, value);
}
-
/**
- * Returns the specified header value, or <jk>null</jk> if the header doesn't exist.
- * <p>
- * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * Convenience method for calling <code>getHeaders().getFirst(name);</code>
*/
@Override /* ServletRequest */
public String getHeader(String name) {
- return getHeader(name, (String)null);
+ return getHeaders().getFirst(name);
}
/**
- * Returns the specified header value, or a default value if the header doesn't exist.
- * <p>
- * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * Convenience method for calling <code>getHeaders().getFirst(name, def);</code>
*
* @param name The HTTP header name.
* @param def The default value to return if the header value isn't found.
* @return The header value, or the default value if the header isn't present.
*/
public String getHeader(String name, String def) {
- String h = getOverriddenHeader(name);
- if (h != null)
- return h;
- h = super.getHeader(name);
- if (h != null && ! h.isEmpty())
- return h;
- if (defaultMethodHeaders != null) {
- h = defaultMethodHeaders.get(name);
- if (h != null)
- return h;
- }
- h = defaultServletHeaders.get(name);
- if (h != null)
- return h;
- return def;
- }
-
- /**
- * Returns the specified header value converted to a POJO.
- * <p>
- * The type can be any POJO type convertable from a <code>String</code>
- * (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into an integer.</jc>
- * <jk>int</jk> myheader = req.getHeader(<js>"My-Header"</js>, <jk>int</jk>.<jk>class</jk>);
- *
- * <jc>// Parse a UUID.</jc>
- * UUID myheader = req.getHeader(<js>"My-Header"</js>, UUID.<jk>class</jk>);
- * </p>
- *
- * @param name The HTTP header name.
- * @param type The class type to convert the header value to.
- * @param <T> The class type to convert the header value to.
- * @return The parameter value converted to the specified class type.
- */
- public <T> T getHeader(String name, Class<T> type) {
- String h = getHeader(name);
- return beanSession.convertToType(h, type);
- }
-
- /**
- * Same as {@link #getHeader(String, Class)} but returns a default value if not found.
- *
- * @param name The HTTP header name.
- * @param def The default value if the header was not specified or is <jk>null</jk>.
- * @param type The class type to convert the header value to.
- * @param <T> The class type to convert the header value to.
- * @return The parameter value converted to the specified class type.
- */
- public <T> T getHeader(String name, T def, Class<T> type) {
- String h = getHeader(name);
- if (h == null)
- return def;
- return beanSession.convertToType(h, type);
- }
-
- /**
- * Returns the specified header value converted to a POJO.
- * <p>
- * The type can be any POJO type convertable from a <code>String</code>
- * (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into a linked-list of strings.</jc>
- * List<String> myheader = req.getHeader(<js>"My-Header"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- * </p>
- *
- * @param name The HTTP header name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @param <T> The class type to convert the header value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException If the header could not be converted to the specified type.
- */
- public <T> T getHeader(String name, Type type, Type...args) throws ParseException {
- String h = getHeader(name);
- return urlEncodingParser.parsePart(h, type, args);
- }
-
- /**
- * Returns all the request headers as an {@link ObjectMap}.
- * <p>
- * Altering entries in this map does not alter headers in the underlying request.
- *
- * @return The request headers. Never <jk>null</jk>.
- */
- public ObjectMap getHeaders() {
- if (headers == null) {
- headers = new ObjectMap();
- for (Enumeration<String> e = getHeaderNames(); e.hasMoreElements();) {
- String key = e.nextElement();
- headers.put(key, getHeader(key));
- }
- }
- return headers;
+ return getHeaders().getFirst(name, def);
}
@Override /* ServletRequest */
public Enumeration<String> getHeaders(String name) {
- String h = getOverriddenHeader(name);
- if (h != null)
- return enumeration(singleton(h));
- return super.getHeaders(name);
- }
-
- /**
- * Returns the <code>Accept</code> header on the request.
- *
- * @return The parsed <code>Accept</code> header on the request, or <jk>null</jk> if not found.
- */
- public Accept getAcceptHeader() {
- return getHeader("Accept", Accept.class);
- }
-
- /**
- * Returns the <code>Content-Type</code> header on the request.
- *
- * @return The parsed <code>Content-Type</code> header on the request, or <jk>null</jk> if not found.
- */
- public ContentType getContentTypeHeader() {
- return getHeader("Content-Type", ContentType.class);
- }
-
- /**
- * Returns the <code>Accept-Encoding</code> header on the request.
- *
- * @return The parsed <code>Accept-Encoding</code> header on the request, or <jk>null</jk> if not found.
- */
- public AcceptEncoding getAcceptEncodingHeader() {
- return getHeader("Accept-Encoding", AcceptEncoding.class);
- }
-
- /**
- * Returns the <code>Time-Zone</code> header value on the request if there is one.
- * <p>
- * Example: <js>"GMT"</js>.
- *
- * @return The <code>Time-Zone</code> header value on the request, or <jk>null</jk> if not present.
- */
- public TimeZone getTimeZone() {
- String tz = getHeader("Time-Zone");
- if (tz != null)
- return TimeZone.getTimeZone(tz);
- return null;
+ String[] v = headers.get(name);
+ if (v == null || v.length == 0)
+ return Collections.enumeration(Collections.EMPTY_LIST);
+ return Collections.enumeration(Arrays.asList(v));
}
/**
@@ -435,7 +317,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
@Override /* ServletRequest */
public Locale getLocale() {
- String h = getOverriddenHeader("Accept-Language");
+ String h = headers.getFirst("Accept-Language");
if (h != null) {
MediaTypeRange[] mr = MediaTypeRange.parse(h);
if (mr.length > 0)
@@ -446,7 +328,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
@Override /* ServletRequest */
public Enumeration<Locale> getLocales() {
- String h = getOverriddenHeader("Accept-Language");
+ String h = headers.getFirst("Accept-Language");
if (h != null) {
MediaTypeRange[] mr = MediaTypeRange.parse(h);
if (mr.length > 0) {
@@ -465,672 +347,142 @@ public final class RestRequest extends HttpServletRequestWrapper {
//--------------------------------------------------------------------------------
/**
- * Sets a request query parameter value.
- *
- * @param name The parameter name.
- * @param value The parameter value.
- */
- public void setQueryParameter(String name, Object value) {
- if (overriddenQueryParams == null)
- overriddenQueryParams = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
- overriddenQueryParams.put(name, value == null ? null : value.toString());
- }
-
- /**
- * Returns a query parameter value.
- * <p>
- * Same as {@link #getParameter(String)} except only looks in the URL string, not parameters from URL-Encoded FORM posts.
- * <p>
- * This method can be used to retrieve a parameter without triggering the underlying servlet API to load and parse the request body.
- *
- * @param name The URL parameter name.
- * @return The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&foo"</js>.
- */
- public String getQueryParameter(String name) {
- String s = null;
- if (overriddenQueryParams != null)
- s = overriddenQueryParams.get(name);
- if (s != null)
- return s;
- String[] v = queryParams.get(name);
- if (v == null || v.length == 0)
- return null;
- if (v.length == 1 && v[0] != null && v[0].isEmpty()) {
- // Fix for behavior difference between Tomcat and WAS.
- // getParameter("foo") on "&foo" in Tomcat returns "".
- // getParameter("foo") on "&foo" in WAS returns null.
- if (queryParams.containsKey(name))
- return null;
- }
- return v[0];
- }
-
- /**
- * Same as {@link #getQueryParameter(String)} but returns the specified default value if the query parameter was not specified.
- *
- * @param name The URL parameter name.
- * @param def The default value.
- * @return The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&foo"</js>.
- */
- public String getQueryParameter(String name, String def) {
- String s = getQueryParameter(name);
- return s == null ? def : s;
- }
-
- /**
- * Returns the specified query parameter value converted to a POJO.
- * <p>
- * This method can be used to retrieve a parameter without triggering the underlying servlet API to load and parse the request body.
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into an integer.</jc>
- * <jk>int</jk> myparam = req.getQueryParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
- *
- * <jc>// Parse into an int array.</jc>
- * <jk>int</jk>[] myparam = req.getQueryParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
-
- * <jc>// Parse into a bean.</jc>
- * MyBean myparam = req.getQueryParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of objects.</jc>
- * List myparam = req.getQueryParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
- *
- * <jc>// Parse into a map of object keys/values.</jc>
- * Map myparam = req.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
- * </p>
- *
- * @param name The parameter name.
- * @param type The class type to convert the parameter value to.
- * @param <T> The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getQueryParameter(String name, Class<T> type) throws ParseException {
- return getQueryParameter(name, beanSession.getClassMeta(type));
- }
-
- /**
- * Same as {@link #getQueryParameter(String, Class)} except returns a default value if not found.
- *
- * @param name The parameter name.
- * @param def The default value if the parameter was not specified or is <jk>null</jk>.
- * @param type The class type to convert the parameter value to.
- * @param <T> The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getQueryParameter(String name, T def, Class<T> type) throws ParseException {
- return getQueryParameter(name, def, beanSession.getClassMeta(type));
- }
-
- /**
- * Returns the specified query parameter value converted to a POJO.
- * <p>
- * This method can be used to retrieve a parameter without triggering the underlying servlet API to load and parse the request body.
+ * Equivalent to {@link #getParameterMap()}, but only looks for query parameters in the URL, not form posts.
* <p>
- * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
+ * This method can be used to retrieve query parameters without triggering the underlying servlet API to load and parse the request body.
* <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into a linked-list of strings.</jc>
- * Listt<String> myparam = req.getQueryParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
- * Listt<List<String>> myparam = req.getQueryParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map of string keys/values.</jc>
- * Map<String,String> myparam = req.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
- * Map<String,List<MyBean>> myparam = req.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
- * </p>
+ * This object is modifiable.
*
- * @param name The parameter name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @param <T> The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
+ * @return The query parameters as a modifiable map.
*/
- public <T> T getQueryParameter(String name, Type type, Type...args) throws ParseException {
- return (T)getQueryParameter(name, beanSession.getClassMeta(type, args));
+ public RequestQuery getQuery() {
+ return queryParams;
}
/**
- * Same as {@link #getQueryParameter(String, Class)} except returns a default value if not found.
+ * Convenience method for calling <code>getQueryParams().put(name, value);</code>.
*
* @param name The parameter name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @param def The default value if the parameter was not specified or is <jk>null</jk>.
- * @param <T> The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getQueryParameter(String name, Object def, Type type, Type...args) throws ParseException {
- return (T)getQueryParameter(name, def, getBeanSession().getClassMeta(type, args));
- }
-
- /**
- * Same as {@link #getQueryParameter(String, Class)} except for use on multi-part parameters
- * (e.g. <js>"&key=1&key=2&key=3"</js> instead of <js>"&key=(1,2,3)"</js>).
- * <p>
- * This method must only be called when parsing into classes of type Collection or array.
- *
- * @param name The query parameter name.
- * @param c The class type to convert the parameter value to.
- * @param <T> The class type to convert the parameter value to.
- * @return The query parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getQueryParameters(String name, Class<T> c) throws ParseException {
- return getQueryParameters(name, beanSession.getClassMeta(c));
- }
-
- /**
- * Same as {@link #getQueryParameter(String, Type, Type...)} except for use on multi-part parameters
- * (e.g. <js>"&key=1&key=2&key=3"</js> instead of <js>"&key=(1,2,3)"</js>).
- * <p>
- * This method must only be called when parsing into classes of type Collection or array.
- *
- * @param name The query parameter name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @param <T> The class type to convert the parameter value to.
- * @return The query parameter value converted to the specified class type.
- * @throws ParseException
+ * @param value The parameter value.
*/
- public <T> T getQueryParameters(String name, Type type, Type...args) throws ParseException {
- return (T)getQueryParameters(name, getBeanSession().getClassMeta(type, args));
+ public void setQuery(String name, Object value) {
+ queryParams.put(name, value);
}
/**
- * Returns the list of all query parameters with the specified name.
- * <p>
- * Same as {@link #getParameterValues(String)} except only looks in the URL string, not parameters from URL-Encoded FORM posts.
- * <p>
- * This method can be used to retrieve parameters without triggering the underlying servlet API to load and parse the request body.
+ * Convenience method for calling <code>getQueryParams().getFirst(name);</code>
*
- * @param name
- * @return the list of query parameters, or <jk>null</jk> if the parameter does not exist.
+ * @param name The URL parameter name.
+ * @return The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&foo"</js>.
*/
- public String[] getQueryParameters(String name) {
- return queryParams.get(name);
+ public String getQuery(String name) {
+ return getQuery().getFirst(name);
}
/**
- * Returns <jk>true</jk> if the query parameters on this request contains the specified entry.
- * <p>
- * Note that this returns <jk>true</jk> even if the value is set to null (e.g. <js>"?key"</js>).
- * <p>
- * This method can be used to check the existence of a parameter without triggering the underlying
- * servlet API to load and parse the request body.
+ * Convenience method for calling <code>getQueryParams().getFirst(name, def);</code>
*
* @param name The URL parameter name.
- * @return <jk>true</jk> if the URL parameters on this request contains the specified entry.
+ * @param def The default value.
+ * @return The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&foo"</js>.
*/
- public boolean hasQueryParameter(String name) {
- return queryParams.containsKey(name);
+ public String getQuery(String name, String def) {
+ return getQuery().getFirst(name, def);
}
- /**
- * Returns <jk>true</jk> if the request contains any of the specified query parameters.
- *
- * @param params The list of parameters to check for.
- * @return <jk>true</jk> if the request contains any of the specified query parameters.
- */
- public boolean hasAnyQueryParameters(String...params) {
- for (String p : params)
- if (hasQueryParameter(p))
- return true;
- return false;
- }
- /**
- * Equivalent to {@link #getParameterMap()}, but only looks for query parameters in the URL, not form posts.
- * <p>
- * This method can be used to retrieve query parameters without triggering the underlying servlet API to load and parse the request body.
- * <p>
- * This object is modifiable.
- *
- * @return The query parameters as a modifiable map.
- */
- public Map<String,String[]> getQueryParameterMap() {
- return queryParams;
- }
+ //--------------------------------------------------------------------------------
+ // Form data parameters
+ //--------------------------------------------------------------------------------
/**
- * Equivalent to {@link #getParameterNames()}, but only looks for query parameters in the URL, not form posts.
- * <p>
- * This method can be used to retrieve query parameters without triggering the underlying servlet API to load and parse the request body.
- * <p>
- * This object is modifiable.
+ * Retrieves the URL-encoded form data from the request if the body has already been cached locally.
*
- * @return An iterator of query parameter names.
+ * @return The URL-encoded form data from the request.
*/
- public Iterator<String> getQueryParameterNames() {
- return queryParams.keySet().iterator();
- }
-
- /* Workhorse method */
- <T> T getQueryParameter(String name, T def, ClassMeta<T> cm) throws ParseException {
- String val = getQueryParameter(name);
- if (val == null)
- return def;
- return parseParameter(val, cm);
- }
-
- /* Workhorse method */
- <T> T getQueryParameter(String name, ClassMeta<T> cm) throws ParseException {
- String val = getQueryParameter(name);
- if (cm.isPrimitive() && (val == null || val.isEmpty()))
- return cm.getPrimitiveDefault();
- return parseParameter(val, cm);
- }
-
- /* Workhorse method */
- @SuppressWarnings("rawtypes")
- <T> T getQueryParameters(String name, ClassMeta<T> cm) throws ParseException {
- String[] p = getQueryParameters(name);
- if (p == null)
- return null;
- if (cm.isArray()) {
- List c = new ArrayList();
- for (int i = 0; i < p.length; i++)
- c.add(parseParameter(p[i], cm.getElementType()));
- return (T)ArrayUtils.toArray(c, cm.getElementType().getInnerClass());
- } else if (cm.isCollection()) {
- try {
- Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
- for (int i = 0; i < p.length; i++)
- c.add(parseParameter(p[i], cm.getElementType()));
- return (T)c;
- } catch (ParseException e) {
- throw e;
- } catch (Exception e) {
- // Typically an instantiation exception.
- throw new ParseException(e);
+ public RequestFormData getFormData() {
+ try {
+ if (formData == null) {
+ formData = new RequestFormData();
+ formData.setParser(urlEncodingParser).setBeanSession(beanSession);
+ if (! body.isLoaded()) {
+ formData.putAll(getParameterMap());
+ } else {
+ Map<String,String> m = urlEncodingParser.parse(body.getReader(), Map.class, String.class, String.class);
+ for (Map.Entry<String,String> e : m.entrySet()) {
+ formData.put(e.getKey(), e.getValue());
+ }
+ }
}
+ return formData;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
}
- throw new ParseException("Invalid call to getQueryParameters(String, ClassMeta). Class type must be a Collection or array.");
}
-
- //--------------------------------------------------------------------------------
- // Form data parameters
- //--------------------------------------------------------------------------------
-
/**
- * Sets a request form data parameter value.
+ * Convenience method for calling <code>getFormData().put(name, value);</code>.
*
* @param name The parameter name.
* @param value The parameter value.
*/
- public void setFormDataParameter(String name, Object value) {
- if (overriddenFormDataParams == null)
- overriddenFormDataParams = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
- overriddenFormDataParams.put(name, value == null ? null : value.toString());
+ public void setFormData(String name, Object value) {
+ getFormData().put(name, value);
}
/**
- * Returns a form data parameter value.
- * <p>
- * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
- * <p>
- * <h5 class='section'>Notes:</h5>
- * <ul>
- * <li>Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the underlying servlet API.
- * <li>This method returns the raw unparsed value, and differs from calling <code>getFormDataParameter(name, String.<jk>class</js>)</code>
- * which will convert the value from UON notation:
- * <ul>
- * <li><js>"null"</js> => <jk>null</jk>
- * <li><js>"'null'"</js> => <js>"null"</js>
- * <li><js>"'foo bar'"</js> => <js>"foo bar"</js>
- * <li><js>"foo~~bar"</js> => <js>"foo~bar"</js>
- * </ul>
- * </ul>
+ * Convenience method for calling <code>getFormData().getFirst(name);</code>.
*
* @param name The form data parameter name.
* @return The parameter value, or <jk>null</jk> if parameter does not exist.
*/
- public String getFormDataParameter(String name) {
- String s = null;
- if (overriddenFormDataParams != null)
- s = overriddenFormDataParams.get(name);
- if (s != null)
- return s;
-
- return getFormParameterInner(name);
+ public String getFormData(String name) {
+ return getFormData().getFirst(name);
}
/**
- * Same as {@link #getFormDataParameter(String)} except returns a default value if <jk>null</jk> or empty.
+ * Convenience method for calling <code>getFormData().getFirst(name, def);</code>.
*
* @param name The form data parameter name.
* @param def The default value.
* @return The parameter value, or the default value if <jk>null</jk> or empty.
*/
- public String getFormDataParameter(String name, String def) {
- String val = getFormParameterInner(name);
- if (val == null || val.isEmpty())
- return def;
- return val;
- }
-
- /**
- * Returns the specified form data parameter value converted to a POJO using the
- * {@link UrlEncodingParser} registered with this servlet.
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into an integer.</jc>
- * <jk>int</jk> myparam = req.getFormDataParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
- *
- * <jc>// Parse into an int array.</jc>
- * <jk>int</jk>[] myparam = req.getFormDataParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
-
- * <jc>// Parse into a bean.</jc>
- * MyBean myparam = req.getFormDataParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of objects.</jc>
- * List myparam = req.getFormDataParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
- *
- * <jc>// Parse into a map of object keys/values.</jc>
- * Map myparam = req.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
- * </p>
- * <p>
- * <h5 class='section'>Notes:</h5>
- * <ul>
- * <li>Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the underlying servlet API.
- * </ul>
- *
- * @param name The parameter name.
- * @param type The class type to convert the parameter value to.
- * @param <T> The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getFormDataParameter(String name, Class<T> type) throws ParseException {
- return getFormDataParameter(name, beanSession.getClassMeta(type));
+ public String getFormData(String name, String def) {
+ return getFormData().getFirst(name, def);
}
- /**
- * Same as {@link #getFormDataParameter(String, Class)} except returns a default value if not specified.
- *
- * @param name The parameter name.
- * @param def The default value if the parameter was not specified or is <jk>null</jk>.
- * @param type The class type to convert the parameter value to.
- * @param <T> The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getFormDataParameter(String name, T def, Class<T> type) throws ParseException {
- return getFormDataParameter(name, def, beanSession.getClassMeta(type));
- }
- /**
- * Same as {@link #getFormDataParameter(String, Class)} except for use on multi-part parameters
- * (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=(1,2,3)"</js>)
- * <p>
- * This method must only be called when parsing into classes of type Collection or array.
- *
- * @param name The parameter name.
- * @param type The class type to convert the parameter value to.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getFormDataParameters(String name, Class<T> type) throws ParseException {
- return getFormDataParameters(name, beanSession.getClassMeta(type));
- }
+ //--------------------------------------------------------------------------------
+ // Path parameters
+ //--------------------------------------------------------------------------------
/**
- * Returns the specified form data parameter value converted to a POJO using the
- * {@link UrlEncodingParser} registered with this servlet.
- * <p>
- * <h5 class='section'>Notes:</h5>
- * <ul>
- * <li>Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the underlying servlet API.
- * <li>Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
- * </ul>
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into a linked-list of strings.</jc>
- * List<String> myparam = req.getFormDataParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
- * List<List<String>> myparam = req.getFormDataParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map of string keys/values.</jc>
- * Map<String,String> myparam = req.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
- * Map<String,List<MyBean>> myparam = req.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
- * </p>
+ * Retrieves the URL-encoded form data from the request if the body has already been cached locally.
*
- * @param name The parameter name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
+ * @return The URL-encoded form data from the request.
*/
- public <T> T getFormDataParameter(String name, Type type, Type...args) throws ParseException {
- return (T)getFormDataParameter(name, beanSession.getClassMeta(type, args));
+ public RequestPathParams getPathParams() {
+ return pathParams;
}
/**
- * Same as {@link #getFormDataParameter(String, Type, Type...)} except for use on multi-part parameters
- * (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=(1,2,3)"</js>)
- * <p>
- * This method must only be called when parsing into classes of type Collection or array.
- *
- * @param name The parameter name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @return The parameter value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getFormDataParameters(String name, Type type, Type...args) throws ParseException {
- return (T)getFormDataParameters(name, beanSession.getClassMeta(type, args));
- }
-
- /**
- * Returns <jk>true</jk> if the form data parameters on this request contains the specified entry.
- * <p>
- * Note that this returns <jk>true</jk> even if the value is set to null (e.g. <js>"?key"</js>).
- *
- * @param name The URL parameter name.
- * @return <jk>true</jk> if the URL parameters on this request contains the specified entry.
- */
- public boolean hasFormDataParameter(String name) {
- return getParameterMap().containsKey(name);
- }
-
- /**
- * Retrieves the URL-encoded from data from the request if the body has already been cached locally.
- */
- private Map<String,String> getFormData() {
- try {
- if (formData == null)
- formData = urlEncodingParser.parse(body, Map.class, String.class, String.class);
- return formData;
- } catch (ParseException e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
-
- /* Workhorse method */
- <T> T getFormDataParameter(String name, T def, ClassMeta<T> cm) throws ParseException {
- String val = getFormParameterInner(name);
- if (val == null)
- return def;
- return parseParameter(val, cm);
- }
-
- /* Workhorse method */
- <T> T getFormDataParameter(String name, ClassMeta<T> cm) throws ParseException {
- String val = getFormParameterInner(name);
- if (cm.isPrimitive() && (val == null || val.isEmpty()))
- return cm.getPrimitiveDefault();
- return parseParameter(val, cm);
- }
-
- private String getFormParameterInner(String name) {
- return (body == null ? super.getParameter(name) : getFormData().get(name));
- }
-
- /* Workhorse method */
- @SuppressWarnings("rawtypes")
- <T> T getFormDataParameters(String name, ClassMeta<T> cm) throws ParseException {
- String[] p = getParameterValues(name);
- if (p == null)
- return null;
- if (cm.isArray()) {
- List c = new ArrayList();
- for (int i = 0; i < p.length; i++)
- c.add(parseParameter(p[i], cm.getElementType()));
- return (T)ArrayUtils.toArray(c, cm.getElementType().getInnerClass());
- } else if (cm.isCollection()) {
- try {
- Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
- for (int i = 0; i < p.length; i++)
- c.add(parseParameter(p[i], cm.getElementType()));
- return (T)c;
- } catch (ParseException e) {
- throw e;
- } catch (Exception e) {
- // Typically an instantiation exception.
- throw new ParseException(e);
- }
- }
- throw new ParseException("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array.");
- }
-
-
- //--------------------------------------------------------------------------------
- // Path parameters
- //--------------------------------------------------------------------------------
-
- /**
- * Sets a path parameter value.
- * <p>
- * A path parameter is a variable in the path pattern such as <js>"/{foo}"</js>
+ * Convenience method for calling <code>getPathParams().put(name, value);</code>.
*
* @param name The parameter name.
* @param value The parameter value.
*/
public void setPathParameter(String name, String value) {
- if (pathParameters == null)
- pathParameters = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
- pathParameters.put(name, value == null ? null : value.toString());
+ pathParams.put(name, StringUtils.toString(value));
}
/**
- * Returns a path parameter value.
- * <p>
- * A path parameter is a variable in the path pattern such as <js>"/{foo}"</js>
+ * Convenience method for calling <code>getPathParams().get(name);</code>.
*
* @param name The parameter name.
- * @return The paramter value, or <jk>null</jk> if path parameter not specified.
+ * @return The parameter value, or <jk>null</jk> if path parameter not specified.
*/
public String getPathParameter(String name) {
- return (pathParameters == null ? null : pathParameters.get(name));
- }
-
- /**
- * Returns the specified path parameter converted to a POJO.
- * <p>
- * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into an integer.</jc>
- * <jk>int</jk> myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
- *
- * <jc>// Parse into an int array.</jc>
- * <jk>int</jk>[] myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
-
- * <jc>// Parse into a bean.</jc>
- * MyBean myparam = req.getPathParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of objects.</jc>
- * List myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
- *
- * <jc>// Parse into a map of object keys/values.</jc>
- * Map myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
- * </p>
- *
- * @param name The attribute name.
- * @param type The class type to convert the attribute value to.
- * @param <T> The class type to convert the attribute value to.
- * @return The attribute value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getPathParameter(String name, Class<T> type) throws ParseException {
- return getPathParameter(name, beanSession.getClassMeta(type));
- }
-
- /**
- * Returns the specified path parameter converted to a POJO.
- * <p>
- * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
- * <p>
- * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into a linked-list of strings.</jc>
- * List<String> myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
- * List<List<String>> myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map of string keys/values.</jc>
- * Map<String,String> myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
- * Map<String,List<MyBean>> myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
- * </p>
- *
- * @param name The attribute name.
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @param <T> The class type to convert the attribute value to.
- * @return The attribute value converted to the specified class type.
- * @throws ParseException
- */
- public <T> T getPathParameter(String name, Type type, Type...args) throws ParseException {
- return (T)getPathParameter(name, beanSession.getClassMeta(type, args));
- }
-
- /* Workhorse method */
- <T> T getPathParameter(String name, ClassMeta<T> cm) throws ParseException {
- Object attr = getPathParameter(name);
- T t = null;
- if (attr != null)
- t = urlEncodingParser.parsePart(attr.toString(), cm);
- if (t == null && cm.isPrimitive())
- return cm.getPrimitiveDefault();
- return t;
+ return pathParams.get(name);
}
@@ -1139,123 +491,12 @@ public final class RestRequest extends HttpServletRequestWrapper {
//--------------------------------------------------------------------------------
/**
- * Reads the input from the HTTP request as JSON, XML, or HTML and converts the input to a POJO.
- * <p>
- * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &body=xxx} in the URL query string.
- * <p>
- * If type is <jk>null</jk> or <code>Object.<jk>class</jk></code>, then the actual type will be determined automatically based on the following input:
- * <table class='styled'>
- * <tr><th>Type</th><th>JSON input</th><th>XML input</th><th>Return type</th></tr>
- * <tr>
- * <td>object</td>
- * <td><js>"{...}"</js></td>
- * <td><code><xt><object></xt>...<xt></object></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'object'</xs><xt>></xt>...<xt></x></xt></code></td>
- * <td>{@link ObjectMap}</td>
- * </tr>
- * <tr>
- * <td>array</td>
- * <td><js>"[...]"</js></td>
- * <td><code><xt><array></xt>...<xt></array></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'array'</xs><xt>></xt>...<xt></x></xt></code></td>
- * <td>{@link ObjectList}</td>
- * </tr>
- * <tr>
- * <td>string</td>
- * <td><js>"'...'"</js></td>
- * <td><code><xt><string></xt>...<xt></string></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'string'</xs><xt>></xt>...<xt></x></xt></code></td>
- * <td>{@link String}</td>
- * </tr>
- * <tr>
- * <td>number</td>
- * <td><code>123</code></td>
- * <td><code><xt><number></xt>123<xt></number></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'number'</xs><xt>></xt>...<xt></x></xt></code></td>
- * <td>{@link Number}</td>
- * </tr>
- * <tr>
- * <td>boolean</td>
- * <td><jk>true</jk></td>
- * <td><code><xt><boolean></xt>true<xt></boolean></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'boolean'</xs><xt>></xt>...<xt></x></xt></code></td>
- * <td>{@link Boolean}</td>
- * </tr>
- * <tr>
- * <td>null</td>
- * <td><jk>null</jk> or blank</td>
- * <td><code><xt><null/></xt></code> or blank<br><code><xt><x</xt> <xa>type</xa>=<xs>'null'</xs><xt>/></xt></code></td>
- * <td><jk>null</jk></td>
- * </tr>
- * </table>
- * <p>
- * Refer to <a class="doclink" href="../../../../overview-summary.html#Core.PojoCategories">POJO Categories</a> for a complete definition of supported POJOs.
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into an integer.</jc>
- * <jk>int</jk> body = req.getBody(<jk>int</jk>.<jk>class</jk>);
- *
- * <jc>// Parse into an int array.</jc>
- * <jk>int</jk>[] body = req.getBody(<jk>int</jk>[].<jk>class</jk>);
-
- * <jc>// Parse into a bean.</jc>
- * MyBean body = req.getBody(MyBean.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of objects.</jc>
- * List body = req.getBody(LinkedList.<jk>class</jk>);
- *
- * <jc>// Parse into a map of object keys/values.</jc>
- * Map body = req.getBody(TreeMap.<jk>class</jk>);
- * </p>
- *
- * @param type The class type to instantiate.
- * @param <T> The class type to instantiate.
- * @return The input parsed to a POJO.
- * @throws IOException If a problem occurred trying to read from the reader.
- * @throws ParseException If the input contains a syntax error or is malformed for the requested {@code Accept} header or is not valid for the specified type.
- */
- public <T> T getBody(Class<T> type) throws IOException, ParseException {
- return getBody(beanSession.getClassMeta(type));
- }
-
- /**
- * Reads the input from the HTTP request as JSON, XML, or HTML and converts the input to a POJO.
- * <p>
- * <h5 class='section'>Examples:</h5>
- * <p class='bcode'>
- * <jc>// Parse into a linked-list of strings.</jc>
- * List<String> body = req.getBody(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
- * List<List<String>> body = req.getBody(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map of string keys/values.</jc>
- * Map<String,String> body = req.getBody(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
- *
- * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
- * Map<String,List<MyBean>> body = req.getBody(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
- * </p>
- *
- * @param type The type of object to create.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * @param args The type arguments of the class if it's a collection or map.
- * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
- * <br>Ignored if the main type is not a map or collection.
- * @param <T> The class type to instantiate.
- * @return The input parsed to a POJO.
- */
- public <T> T getBody(Type type, Type...args) {
- return (T)getBody(beanSession.getClassMeta(type, args));
- }
-
- /**
- * Returns the HTTP body content as a plain string.
- * <p>
- * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query string.
+ * Returns the body of this HTTP request.
*
- * @return The incoming input from the connection as a plain string.
- * @throws IOException If a problem occurred trying to read from the reader.
+ * @return The body of this HTTP request.
*/
- public String getBodyAsString() throws IOException {
- if (body == null)
- body = IOUtils.readBytes(getInputStream(), 1024);
- return new String(body, IOUtils.UTF8);
+ public RequestBody getBody() {
+ return body;
}
/**
@@ -1267,24 +508,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
@Override /* ServletRequest */
public BufferedReader getReader() throws IOException {
- Reader r = getUnbufferedReader();
- if (r instanceof BufferedReader)
- return (BufferedReader)r;
- int len = getContentLength();
- int buffSize = len <= 0 ? 8192 : Math.max(len, 8192);
- return new BufferedReader(r, buffSize);
- }
-
- /**
- * Same as {@link #getReader()}, but doesn't encapsulate the result in a {@link BufferedReader};
- *
- * @return An unbuffered reader.
- * @throws IOException
- */
- protected Reader getUnbufferedReader() throws IOException {
- if (body != null)
- return new CharSequenceReader(new String(body, IOUtils.UTF8));
- return new InputStreamReader(getInputStream(), getCharacterEncoding());
+ return getBody().getReader();
}
/**
@@ -1298,66 +522,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
@Override /* ServletRequest */
public ServletInputStream getInputStream() throws IOException {
-
- if (body != null)
- return new ServletInputStream2(body);
-
- Encoder enc = getEncoder();
-
- ServletInputStream is = super.getInputStream();
- if (enc != null) {
- final InputStream is2 = enc.getInputStream(is);
- return new ServletInputStream2(is2);
- }
- return is;
+ return getBody().getInputStream();
}
- /* Workhorse method */
- <T> T getBody(ClassMeta<T> cm) throws RestException {
-
- try {
- if (cm.isReader())
- return (T)getReader();
-
- if (cm.isInputStream())
- return (T)getInputStream();
-
- TimeZone timeZone = getTimeZone();
- Locale locale = getLocale();
- ParserMatch pm = getParserMatch();
-
- if (pm != null) {
- Parser p = pm.getParser();
- MediaType mediaType = pm.getMediaType();
- try {
- properties.append("mediaType", mediaType).append("characterEncoding", getCharacterEncoding());
- if (! p.isReaderParser()) {
- InputStreamParser p2 = (InputStreamParser)p;
- ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType);
- return p2.parseSession(session, cm);
- }
- ReaderParser p2 = (ReaderParser)p;
- ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType);
- return p2.parseSession(session, cm);
- } catch (ParseException e) {
- throw new RestException(SC_BAD_REQUEST,
- "Could not convert request body content to class type ''{0}'' using parser ''{1}''.",
- cm, p.getClass().getName()
- ).initCause(e);
- }
- }
-
- throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
- "Unsupported media-type in request header ''Content-Type'': ''{0}''\n\tSupported media-types: {1}",
- getHeader("Content-Type"), parserGroup.getSupportedMediaTypes()
- );
-
- } catch (IOException e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR,
- "I/O exception occurred while attempting to handle request ''{0}''.",
- getDescription()
- ).initCause(e);
- }
+ ServletInputStream getRawInputStream() throws IOException {
+ return super.getInputStream();
}
@@ -1724,53 +893,6 @@ public final class RestRequest extends HttpServletRequestWrapper {
return parserGroup;
}
- /**
- * Returns the parser and media type matching the request <code>Content-Type</code> header.
- *
- * @return The parser matching the request <code>Content-Type</code> header, or <jk>null</jk>
- * if no matching parser was found.
- * Includes the matching media type.
- */
- public ParserMatch getParserMatch() {
- MediaType mediaType = getContentTypeHeader();
- if (mediaType == null) {
- if (body != null)
- mediaType = MediaType.UON;
- else
- mediaType = MediaType.JSON;
- }
- ParserMatch pm = parserGroup.getParserMatch(mediaType);
-
- // If no patching parser for URL-encoding, use the one defined on the servlet.
- if (pm == null && mediaType.equals(MediaType.URLENCODING))
- pm = new ParserMatch(MediaType.URLENCODING, urlEncodingParser);
-
- return pm;
- }
-
- /**
- * Returns the parser matching the request <code>Content-Type</code> header.
- *
- * @return The parser matching the request <code>Content-Type</code> header, or <jk>null</jk>
- * if no matching parser was found.
- */
- public Parser getParser() {
- ParserMatch pm = getParserMatch();
- return (pm == null ? null : pm.getParser());
- }
-
- /**
- * Returns the reader parser matching the request <code>Content-Type</code> header.
- *
- * @return The reader parser matching the request <code>Content-Type</code> header, or <jk>null</jk>
- * if no matching reader parser was found, or the matching parser was an input stream parser.
- */
- public ReaderParser getReaderParser() {
- Parser p = getParser();
- if (p != null && p.isReaderParser())
- return (ReaderParser)p;
- return null;
- }
/**
* Returns the method of this request.
@@ -1782,10 +904,24 @@ public final class RestRequest extends HttpServletRequestWrapper {
return method;
}
+ /**
+ * Returns the HTTP 1.1 method name of the request as an enum.
+ * <p>
+ * Note that non-RFC2616 method names resolve as {@link HttpMethod#OTHER}.
+ *
+ * @return The HTTP method.
+ */
+ public HttpMethod getHttpMethod() {
+ return HttpMethod.forString(method);
+ }
@Override /* ServletRequest */
public int getContentLength() {
- return contentLength == 0 ? super.getContentLength() : contentLength;
+ return getBody().getContentLength();
+ }
+
+ int getRawContentLength() {
+ return super.getContentLength();
}
/**
@@ -1799,7 +935,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
* @return <jk>true</jk> if {@code &plainText=true} was specified as a URL parameter
*/
public boolean isPlainText() {
- return "true".equals(getQueryParameter("plainText", "false"));
+ return "true".equals(getQuery("plainText", "false"));
}
/**
@@ -1976,18 +1112,17 @@ public final class RestRequest extends HttpServletRequestWrapper {
sb.append("\t").append(h).append(": ").append(getHeader(h)).append("\n");
}
sb.append("---Default Servlet Headers---\n");
- for (Map.Entry<String,String> e : defaultServletHeaders.entrySet()) {
+ for (Map.Entry<String,String> e : context.getDefaultRequestHeaders().entrySet()) {
sb.append("\t").append(e.getKey()).append(": ").append(e.getValue()).append("\n");
}
if (javaMethod == null) {
sb.append("***init() not called yet!***\n");
} else if (method.equals("PUT") || method.equals("POST")) {
try {
- body = IOUtils.readBytes(getInputStream(), 1024);
sb.append("---Body UTF-8---\n");
- sb.append(new String(body, "UTF-8")).append("\n");
+ sb.append(body.asString()).append("\n");
sb.append("---Body Hex---\n");
- sb.append(StringUtils.toHex(body)).append("\n");
+ sb.append(body.asHex()).append("\n");
} catch (Exception e1) {
sb.append(e1.getLocalizedMessage());
context.getLogger().log(WARNING, e1, "Error occurred while trying to read debug input.");
@@ -2001,10 +1136,6 @@ public final class RestRequest extends HttpServletRequestWrapper {
// Utility methods
//--------------------------------------------------------------------------------
- private <T> T parseParameter(String val, ClassMeta<T> c) throws ParseException {
- return urlEncodingParser.parsePart(val, c);
- }
-
/*
* Converts an Accept-Language value entry to a Locale.
*/
@@ -2018,74 +1149,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
return new Locale(lang, country);
}
- private Encoder getEncoder() {
- if (encoder == null) {
- String ce = getHeader("content-encoding");
- if (! (ce == null || ce.isEmpty())) {
- ce = ce.trim();
- encoder = encoders.getEncoder(ce);
- if (encoder == null)
- throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
- "Unsupported encoding in request header ''Content-Encoding'': ''{0}''\n\tSupported codings: {1}",
- getHeader("content-encoding"), encoders.getSupportedEncodings()
- );
- }
-
- if (encoder != null)
- contentLength = -1;
- }
- // Note that if this is the identity encoder, we want to return null
- // so that we don't needlessly wrap the input stream.
- if (encoder == IdentityEncoder.INSTANCE)
- return null;
- return encoder;
- }
-
- /*
- * Returns header value from URL-parameters or set via setHeader() meant
- * to override actual header values on the request.
- */
- private String getOverriddenHeader(String name) {
- String h = null;
- if (context.isAllowHeaderParams())
- h = getQueryParameter(name);
- if (h != null)
- return h;
- if (overriddenHeaders != null) {
- h = overriddenHeaders.get(name);
- if (h != null)
- return h;
- }
- return h;
- }
void setJavaMethod(Method method) {
this.javaMethod = method;
}
-
- /**
- * ServletInputStream wrapper around a normal input stream.
- */
- private static class ServletInputStream2 extends ServletInputStream {
-
- private final InputStream is;
-
- private ServletInputStream2(InputStream is) {
- this.is = is;
- }
-
- private ServletInputStream2(byte[] b) {
- this(new ByteArrayInputStream(b));
- }
-
- @Override /* InputStream */
- public final int read() throws IOException {
- return is.read();
- }
-
- @Override /* InputStream */
- public final void close() throws IOException {
- is.close();
- }
- }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
index 9a16812..bf40391 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
@@ -46,7 +46,7 @@ import org.apache.juneau.rest.*;
*
* <h6 class='topic'>Important note concerning FORM posts</h6>
* <p>
- * This annotation should not be combined with the {@link Body @Body} annotation or {@link RestRequest#getBody(Class)} method
+ * This annotation should not be combined with the {@link Body @Body} annotation or {@link RestRequest#getBody()} method
* for <code>application/x-www-form-urlencoded POST</code> posts, since it will trigger the underlying servlet
* API to parse the body content as key-value pairs resulting in empty content.
* <p>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasFormData.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasFormData.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasFormData.java
index 79d9ba0..085d7e9 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasFormData.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasFormData.java
@@ -74,7 +74,7 @@ import org.apache.juneau.rest.*;
*
* <h6 class='topic'>Important note concerning FORM posts</h6>
* <p>
- * This annotation should not be combined with the {@link Body @Body} annotation or {@link RestRequest#getBody(Class)} method
+ * This annotation should not be combined with the {@link Body @Body} annotation or {@link RestRequest#getBody()} method
* for <code>application/x-www-form-urlencoded POST</code> posts, since it will trigger the underlying servlet API to parse the body
* content as key-value pairs, resulting in empty content.
* <p>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasQuery.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasQuery.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasQuery.java
index 880b1b7..19f4469 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasQuery.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/HasQuery.java
@@ -26,7 +26,7 @@ import org.apache.juneau.rest.*;
* Unlike {@link HasFormData @HasFormData}, using this annotation does not result in the servlet reading the contents
* of URL-encoded form posts.
* Therefore, this annotation can be used in conjunction with the {@link Body @Body} annotation
- * or {@link RestRequest#getBody(Class)} method for <code>application/x-www-form-urlencoded POST</code> calls.
+ * or {@link RestRequest#getBody()} method for <code>application/x-www-form-urlencoded POST</code> calls.
*
* <h5 class='section'>Example:</h5>
* <p class='bcode'>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
index 8e411d3..1f00fdc 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
@@ -26,7 +26,7 @@ import org.apache.juneau.rest.*;
* Unlike {@link FormData @FormData}, using this annotation does not result in the servlet reading the contents
* of URL-encoded form posts.
* Therefore, this annotation can be used in conjunction with the {@link Body @Body} annotation
- * or {@link RestRequest#getBody(Class)} method for <code>application/x-www-form-urlencoded POST</code> calls.
+ * or {@link RestRequest#getBody()} method for <code>application/x-www-form-urlencoded POST</code> calls.
*
* <h5 class='section'>Example:</h5>
* <p class='bcode'>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
index b4bb393..cf5f16d 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Introspectable.java
@@ -40,8 +40,8 @@ public final class Introspectable implements RestConverter {
@Override /* RestConverter */
@SuppressWarnings({"unchecked", "rawtypes"})
public Object convert(RestRequest req, Object o, ClassMeta cm) throws RestException {
- String method = req.getQueryParameter("invokeMethod");
- String args = req.getQueryParameter("invokeArgs");
+ String method = req.getQuery("invokeMethod");
+ String args = req.getQuery("invokeArgs");
if (method == null)
return o;
try {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Queryable.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Queryable.java b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Queryable.java
index fee4939..a563863 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Queryable.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Queryable.java
@@ -58,10 +58,11 @@ public final class Queryable implements RestConverter {
return null;
try {
+ RequestQuery q = req.getQuery();
// If no actual filtering parameters have been passed in, and there is no map augmenter specified,
// then just pass the original object back.
- if (req.hasAnyQueryParameters("q","v","s","g","i","p","l")) {
+ if (q.containsAnyKeys("q","v","s","g","i","p","l")) {
BeanSession session = req.getBeanSession();
if (cm.getPojoSwap() != null)
@@ -70,16 +71,16 @@ public final class Queryable implements RestConverter {
PojoQuery f = new PojoQuery(o, session);
if (o instanceof Collection || o.getClass().isArray()) {
- ObjectMap query = req.getQueryParameter("q", ObjectMap.class);
- List<String> view = req.getQueryParameter("v", List.class, String.class);
- List sort = req.getQueryParameter("s", List.class, String.class);
- boolean ignoreCase = req.getQueryParameter("i", false, Boolean.class);
- int pos = req.getQueryParameter("p", 0, Integer.class);
- int limit = req.getQueryParameter("l", 0, Integer.class);
+ ObjectMap query = q.get("q", ObjectMap.class);
+ List<String> view = q.get("v", List.class, String.class);
+ List sort = q.get("s", List.class, String.class);
+ boolean ignoreCase = q.get("i", false, Boolean.class);
+ int pos = q.get("p", 0, Integer.class);
+ int limit = q.get("l", 0, Integer.class);
o = f.filterCollection(query, view, sort, pos, limit, ignoreCase);
} else {
- List<String> view = req.getQueryParameter("v", List.class, String.class);
+ List<String> view = q.get("v", List.class, String.class);
o = f.filterMap(view);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
index f56bf15..4816453 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/converters/Traversable.java
@@ -53,7 +53,7 @@ public final class Traversable implements RestConverter {
try {
if (cm.getPojoSwap() != null)
o = cm.getPojoSwap().swap(req.getBeanSession(), o);
- PojoRest p = new PojoRest(o, req.getReaderParser());
+ PojoRest p = new PojoRest(o, req.getBody().getReaderParser());
o = p.get(req.getPathRemainder());
} catch (SerializeException e) {
throw new RestException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
index 795f521..2d173f5 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
@@ -99,9 +99,9 @@ public abstract class RemoteableServlet extends RestServletDefault {
public Object invoke(RestRequest req, @Path String javaInterface, @Path String javaMethod) throws Exception {
// Find the parser.
- ReaderParser p = req.getReaderParser();
+ ReaderParser p = req.getBody().getReaderParser();
if (p == null)
- throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Could not find parser for media type ''{0}''", req.getContentTypeHeader()); //$NON-NLS-1$
+ throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Could not find parser for media type ''{0}''", req.getHeaders().getContentType()); //$NON-NLS-1$
Class<?> c = getInterfaceClass(javaInterface);
// Find the service.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
index 190ea44..b06479b 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
@@ -60,13 +60,13 @@ public class DefaultHandler implements ResponseHandler {
if (! s.isWriterSerializer()) {
OutputStreamSerializer s2 = (OutputStreamSerializer)s;
OutputStream os = res.getNegotiatedOutputStream();
- SerializerSession session = s.createSession(os, p, req.getJavaMethod(), req.getLocale(), req.getTimeZone(), mediaType);
+ SerializerSession session = s.createSession(os, p, req.getJavaMethod(), req.getLocale(), req.getHeaders().getTimeZone(), mediaType);
s2.serialize(session, output);
os.close();
} else {
WriterSerializer s2 = (WriterSerializer)s;
Writer w = res.getNegotiatedWriter();
- SerializerSession session = s.createSession(w, p, req.getJavaMethod(), req.getLocale(), req.getTimeZone(), mediaType);
+ SerializerSession session = s.createSession(w, p, req.getJavaMethod(), req.getLocale(), req.getHeaders().getTimeZone(), mediaType);
s2.serialize(session, output);
w.close();
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
index 455a375..f02c2ab 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java
@@ -72,9 +72,9 @@ public class RequestVar extends SimpleVar {
if ("path".equals(prefix))
return req.getPathParameter(remainder);
if ("query".equals(prefix))
- return req.getQueryParameter(remainder);
+ return req.getQuery(remainder);
if ("formData".equals(prefix))
- return req.getFormDataParameter(remainder);
+ return req.getFormData(remainder);
if ("header".equals(prefix))
return req.getHeader(remainder);
if ("attribute".equals(prefix))
[2/3] incubator-juneau git commit: Break up RestRequest into
different functional classes.
Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
new file mode 100644
index 0000000..b0617bd
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestHeaders.java
@@ -0,0 +1,675 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.Date;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents the headers in an HTTP request.
+ * <p>
+ * Entries are stored in a case-insensitive map.
+ */
+public class RequestHeaders extends TreeMap<String,String[]> {
+ private static final long serialVersionUID = 1L;
+
+ private UrlEncodingParser parser;
+ private BeanSession beanSession;
+ private RequestQuery queryParams;
+
+ RequestHeaders() {
+ super(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ RequestHeaders setParser(UrlEncodingParser parser) {
+ this.parser = parser;
+ return this;
+ }
+
+ RequestHeaders setBeanSession(BeanSession beanSession) {
+ this.beanSession = beanSession;
+ return this;
+ }
+
+ RequestHeaders setQueryParams(RequestQuery queryParams) {
+ this.queryParams = queryParams;
+ return this;
+ }
+
+ /**
+ * Adds default entries to these headers.
+ * <p>
+ * This includes the default headers defined on the servlet and method levels.
+ *
+ * @param defaultEntries The default entries. Can be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestHeaders addDefault(Map<String,String> defaultEntries) {
+ if (defaultEntries != null) {
+ for (Map.Entry<String,String> e : defaultEntries.entrySet()) {
+ String key = e.getKey(), value = e.getValue();
+ String[] v = get(key);
+ if (v == null)
+ put(key, new String[]{value});
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Adds a set of header values to this object.
+ *
+ * @param name The header name.
+ * @param values The header values.
+ * @return This object (for method chaining).
+ */
+ public RequestHeaders put(String name, Enumeration<String> values) {
+ // Optimized for enumerations of one entry, the most-common case.
+ if (values.hasMoreElements()) {
+ String v = values.nextElement();
+ if (! v.isEmpty()) {
+ String[] s = new String[]{v};
+ while (values.hasMoreElements())
+ s = ArrayUtils.append(s, values.nextElement());
+ put(name, s);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns the specified header value, or <jk>null</jk> if the header doesn't exist.
+ * <p>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ * <p>
+ * @param name The header name.
+ * @return The header value, or <jk>null</jk> if it doesn't exist.
+ */
+ public String getFirst(String name) {
+ return getFirst(name, null);
+ }
+
+ /**
+ * Returns the specified header value, or a default value if the header doesn't exist.
+ * <p>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &HeaderName=x} in the URL query string.
+ *
+ * @param name The HTTP header name.
+ * @param def The default value to return if the header value isn't found.
+ * @return The header value, or the default value if the header isn't present.
+ */
+ public String getFirst(String name, String def) {
+ String[] v = null;
+ if (queryParams != null)
+ v = queryParams.get(name);
+ if (v == null || v.length == 0)
+ v = get(name);
+ if (v == null || v.length == 0)
+ return def;
+ return v[0];
+ }
+
+ /**
+ * Sets a request header value.
+ *
+ * @param name The header name.
+ * @param value The header value.
+ */
+ public void put(String name, Object value) {
+ super.put(name, new String[]{StringUtils.toString(value)});
+ }
+
+ /**
+ * Returns the specified header value converted to a POJO.
+ * <p>
+ * The type can be any POJO type convertable from a <code>String</code>
+ * (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> myheader = req.getHeader(<js>"My-Header"</js>, <jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse a UUID.</jc>
+ * UUID myheader = req.getHeader(<js>"My-Header"</js>, UUID.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The HTTP header name.
+ * @param type The class type to convert the header value to.
+ * @param <T> The class type to convert the header value to.
+ * @return The parameter value converted to the specified class type.
+ */
+ public <T> T get(String name, Class<T> type) {
+ String h = getFirst(name);
+ return beanSession.convertToType(h, type);
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} but returns a default value if not found.
+ *
+ * @param name The HTTP header name.
+ * @param def The default value if the header was not specified or is <jk>null</jk>.
+ * @param type The class type to convert the header value to.
+ * @param <T> The class type to convert the header value to.
+ * @return The parameter value converted to the specified class type.
+ */
+ public <T> T get(String name, T def, Class<T> type) {
+ String h = getFirst(name);
+ if (h == null)
+ return def;
+ return beanSession.convertToType(h, type);
+ }
+
+ /**
+ * Returns the specified header value converted to a POJO.
+ * <p>
+ * The type can be any POJO type convertable from a <code>String</code>
+ * (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into a linked-list of strings.</jc>
+ * List<String> myheader = req.getHeader(<js>"My-Header"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The HTTP header name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param <T> The class type to convert the header value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException If the header could not be converted to the specified type.
+ */
+ public <T> T get(String name, Type type, Type...args) throws ParseException {
+ String h = getFirst(name);
+ return parser.parsePart(h, type, args);
+ }
+
+ /**
+ * Returns the <code>Accept</code> header on the request.
+ * <p>
+ * Content-Types that are acceptable for the response.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Accept: text/plain
+ * </p>
+ *
+ * @return The parsed <code>Accept</code> header on the request, or <jk>null</jk> if not found.
+ *
+ */
+ public Accept getAccept() {
+ return Accept.forString(getFirst("Accept"));
+ }
+
+ /**
+ * Returns the <code>Accept-Charset</code> header on the request.
+ * <p>
+ * Character sets that are acceptable.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Accept-Charset: utf-8
+ * </p>
+ *
+ * @return The parsed <code>Accept-Charset</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public AcceptCharset getAcceptCharset() {
+ return AcceptCharset.forString(getFirst("Accept-Charset"));
+ }
+
+ /**
+ * Returns the <code>Accept-Encoding</code> header on the request.
+ * <p>
+ * List of acceptable encodings.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Accept-Encoding: gzip, deflate
+ * </p>
+ *
+ * @return The parsed <code>Accept-Encoding</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public AcceptEncoding getAcceptEncoding() {
+ return AcceptEncoding.forString(getFirst("Accept-Encoding"));
+ }
+
+ /**
+ * Returns the <code>Accept-Language</code> header on the request.
+ * <p>
+ * List of acceptable human languages for response.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Accept-Language: en-US
+ * </p>
+ *
+ * @return The parsed <code>Accept-Language</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public AcceptLanguage getAcceptLanguage() {
+ return AcceptLanguage.forString(getFirst("Accept-Language"));
+ }
+
+ /**
+ * Returns the <code>Authorization</code> header on the request.
+ * <p>
+ * Authentication credentials for HTTP authentication.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+ * </p>
+ *
+ * @return The parsed <code>Authorization</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Authorization getAuthorization() {
+ return Authorization.forString(getFirst("Authorization"));
+ }
+
+ /**
+ * Returns the <code>Cache-Control</code> header on the request.
+ * <p>
+ * Used to specify directives that must be obeyed by all caching mechanisms along the request-response chain.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Cache-Control: no-cache
+ * </p>
+ *
+ * @return The parsed <code>Cache-Control</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public CacheControl getCacheControl() {
+ return CacheControl.forString(getFirst("Cache-Control"));
+ }
+
+ /**
+ * Returns the <code>Connection</code> header on the request.
+ * <p>
+ * Control options for the current connection and list of hop-by-hop request fields.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Connection: keep-alive
+ * Connection: Upgrade
+ * </p>
+ *
+ * @return The parsed <code></code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Connection getConnection() {
+ return Connection.forString(getFirst("Connection"));
+ }
+
+ /**
+ * Returns the <code>Content-Length</code> header on the request.
+ * <p>
+ * The length of the request body in octets (8-bit bytes).
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Content-Length: 348
+ * </p>
+ *
+ * @return The parsed <code>Content-Length</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public ContentLength getContentLength() {
+ return ContentLength.forString(getFirst("Content-Length"));
+ }
+
+ /**
+ * Returns the <code>Content-Type</code> header on the request.
+ * <p>
+ * The MIME type of the body of the request (used with POST and PUT requests).
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Content-Type: application/x-www-form-urlencoded
+ * </p>
+ *
+ * @return The parsed <code>Content-Type</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public ContentType getContentType() {
+ return ContentType.forString(getFirst("Content-Type"));
+ }
+
+ /**
+ * Returns the <code>Date</code> header on the request.
+ * <p>
+ * The date and time that the message was originated (in "HTTP-date" format as defined by RFC 7231 Date/Time Formats).
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Date: Tue, 15 Nov 1994 08:12:31 GMT
+ * </p>
+ *
+ * @return The parsed <code>Date</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Date getDate() {
+ return Date.forString(getFirst("Date"));
+ }
+
+ /**
+ * Returns the <code>Expect</code> header on the request.
+ * <p>
+ * Indicates that particular server behaviors are required by the client.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Expect: 100-continue
+ * </p>
+ *
+ * @return The parsed <code>Expect</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Expect getExpect() {
+ return Expect.forString(getFirst("Expect"));
+ }
+
+ /**
+ * Returns the <code>From</code> header on the request.
+ * <p>
+ * The email address of the user making the request.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * From: user@example.com
+ * </p>
+ *
+ * @return The parsed <code>From</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public From getFrom() {
+ return From.forString(getFirst("From"));
+ }
+
+ /**
+ * Returns the <code>Host</code> header on the request.
+ * <p>
+ * The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening.
+ * The port number may be omitted if the port is the standard port for the service requested.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Host: en.wikipedia.org:8080
+ * Host: en.wikipedia.org
+ * </p>
+ *
+ * @return The parsed <code>Host</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Host getHost() {
+ return Host.forString(getFirst("Host"));
+ }
+
+ /**
+ * Returns the <code>If-Match</code> header on the request.
+ * <p>
+ * Only perform the action if the client supplied entity matches the same entity on the server.
+ * This is mainly for methods like PUT to only update a resource if it has not been modified since the user last updated it.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * If-Match: "737060cd8c284d8af7ad3082f209582d"
+ * </p>
+ *
+ * @return The parsed <code>If-Match</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public IfMatch getIfMatch() {
+ return IfMatch.forString(getFirst("If-Match"));
+ }
+
+ /**
+ * Returns the <code>If-Modified-Since</code> header on the request.
+ * <p>
+ * Allows a 304 Not Modified to be returned if content is unchanged.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+ * </p>
+ *
+ * @return The parsed <code>If-Modified-Since</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public IfModifiedSince getIfModifiedSince() {
+ return IfModifiedSince.forString(getFirst("If-Modified-Since"));
+ }
+
+ /**
+ * Returns the <code>If-None-Match</code> header on the request.
+ * <p>
+ * Allows a 304 Not Modified to be returned if content is unchanged, see HTTP ETag.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * If-None-Match: "737060cd8c284d8af7ad3082f209582d"
+ * </p>
+ *
+ * @return The parsed <code>If-None-Match</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public IfNoneMatch getIfNoneMatch() {
+ return IfNoneMatch.forString(getFirst("If-None-Match"));
+ }
+
+ /**
+ * Returns the <code>If-Range</code> header on the request.
+ * <p>
+ * If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire new entity.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * If-Range: "737060cd8c284d8af7ad3082f209582d"
+ * </p>
+ *
+ * @return The parsed <code>If-Range</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public IfRange getIfRange() {
+ return IfRange.forString(getFirst("If-Range"));
+ }
+
+ /**
+ * Returns the <code>If-Unmodified-Since</code> header on the request.
+ * <p>
+ * Only send the response if the entity has not been modified since a specific time.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
+ * </p>
+ *
+ * @return The parsed <code>If-Unmodified-Since</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public IfUnmodifiedSince getIfUnmodifiedSince() {
+ return IfUnmodifiedSince.forString(getFirst("If-Unmodified-Since"));
+ }
+
+ /**
+ * Returns the <code>Max-Forwards</code> header on the request.
+ * <p>
+ * Limit the number of times the message can be forwarded through proxies or gateways.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Max-Forwards: 10
+ * </p>
+ *
+ * @return The parsed <code>Max-Forwards</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public MaxForwards getMaxForwards() {
+ return MaxForwards.forString(getFirst("Max-Forwards"));
+ }
+
+ /**
+ * Returns the <code>Pragma</code> header on the request.
+ * <p>
+ * Implementation-specific fields that may have various effects anywhere along the request-response chain.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Pragma: no-cache
+ * </p>
+ *
+ * @return The parsed <code>Pragma</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Pragma getPragma() {
+ return Pragma.forString(getFirst("Pragma"));
+ }
+
+ /**
+ * Returns the <code>Proxy-Authorization</code> header on the request.
+ * <p>
+ * Authorization credentials for connecting to a proxy.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
+ * </p>
+ *
+ * @return The parsed <code>Proxy-Authorization</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public ProxyAuthorization getProxyAuthorization() {
+ return ProxyAuthorization.forString(getFirst("Proxy-Authorization"));
+ }
+
+ /**
+ * Returns the <code>Range</code> header on the request.
+ * <p>
+ * Request only part of an entity. Bytes are numbered from 0.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Range: bytes=500-999
+ * </p>
+ *
+ * @return The parsed <code>Range</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Range getRange() {
+ return Range.forString(getFirst("Range"));
+ }
+
+ /**
+ * Returns the <code>Referer</code> header on the request.
+ * <p>
+ * This is the address of the previous web page from which a link to the currently requested page was followed.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Referer: http://en.wikipedia.org/wiki/Main_Page
+ * </p>
+ *
+ * @return The parsed <code>Referer</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Referer getReferer() {
+ return Referer.forString(getFirst("Referer"));
+ }
+
+ /**
+ * Returns the <code>TE</code> header on the request.
+ * <p>
+ * The transfer encodings the user agent is willing to accept: the same values as for the response header field
+ * Transfer-Encoding can be used, plus the "trailers" value (related to the "chunked" transfer method) to notify the
+ * server it expects to receive additional fields in the trailer after the last, zero-sized, chunk.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * TE: trailers, deflate
+ * </p>
+ *
+ * @return The parsed <code>TE</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public TE getTE() {
+ return TE.forString(getFirst("TE"));
+ }
+
+ /**
+ * Returns the <code>Time-Zone</code> header value on the request if there is one.
+ * <p>
+ * Example: <js>"GMT"</js>.
+ *
+ * @return The <code>Time-Zone</code> header value on the request, or <jk>null</jk> if not present.
+ */
+ public TimeZone getTimeZone() {
+ String tz = getFirst("Time-Zone");
+ if (tz != null)
+ return TimeZone.getTimeZone(tz);
+ return null;
+ }
+
+ /**
+ * Returns the <code>User-Agent</code> header on the request.
+ * <p>
+ * The user agent string of the user agent.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0
+ * </p>
+ *
+ * @return The parsed <code>User-Agent</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public UserAgent getUserAgent() {
+ return UserAgent.forString(getFirst("User-Agent"));
+ }
+
+ /**
+ * Returns the <code>Upgrade</code> header on the request.
+ * <p>
+ * Ask the server to upgrade to another protocol.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Upgrade: HTTP/2.0, HTTPS/1.3, IRC/6.9, RTA/x11, websocket
+ * </p>
+ *
+ * @return The parsed <code>Upgrade</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Upgrade getUpgrade() {
+ return Upgrade.forString(getFirst("Upgrade"));
+ }
+
+ /**
+ * Returns the <code>Via</code> header on the request.
+ * <p>
+ * Informs the server of proxies through which the request was sent.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Via: 1.0 fred, 1.1 example.com (Apache/1.1)
+ * </p>
+ *
+ * @return The parsed <code>Via</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Via getVia() {
+ return Via.forString(getFirst("Via"));
+ }
+
+ /**
+ * Returns the <code>Warning</code> header on the request.
+ * <p>
+ * A general warning about possible problems with the entity body.
+ *
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * Warning: 199 Miscellaneous warning
+ * </p>
+ *
+ * @return The parsed <code>Warning</code> header on the request, or <jk>null</jk> if not found.
+ */
+ public Warning getWarning() {
+ return Warning.forString(getFirst("Warning"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java
new file mode 100644
index 0000000..8a12a0e
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestPathParams.java
@@ -0,0 +1,137 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents the path parameters on an HTTP request.
+ */
+@SuppressWarnings("unchecked")
+public class RequestPathParams extends TreeMap<String,String> {
+ private static final long serialVersionUID = 1L;
+
+ private UrlEncodingParser parser;
+ private BeanSession beanSession;
+
+ RequestPathParams() {
+ super(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ RequestPathParams setParser(UrlEncodingParser parser) {
+ this.parser = parser;
+ return this;
+ }
+
+ RequestPathParams setBeanSession(BeanSession beanSession) {
+ this.beanSession = beanSession;
+ return this;
+ }
+
+ /**
+ * Sets a request query parameter value.
+ *
+ * @param name The parameter name.
+ * @param value The parameter value.
+ */
+ public void put(String name, Object value) {
+ put(name, value);
+ }
+
+ /**
+ * Returns the specified path parameter converted to a POJO.
+ * <p>
+ * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink"
+ * href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse into an int array.</jc>
+ * <jk>int</jk>[] myparam = req.getPathParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+
+ * <jc>// Parse into a bean.</jc>
+ * MyBean myparam = req.getPathParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of objects.</jc>
+ * List myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of object keys/values.</jc>
+ * Map myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The attribute name.
+ * @param type The class type to convert the attribute value to.
+ * @param <T> The class type to convert the attribute value to.
+ * @return The attribute value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Class<T> type) throws ParseException {
+ return parse(name, beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified path parameter converted to a POJO.
+ * <p>
+ * The type can be any POJO type convertable from a <code>String</code> (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertable From Strings</a>).
+ * <p>
+ * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into a linked-list of strings.</jc>
+ * List<String> myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
+ * List<List<String>> myparam = req.getPathParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of string keys/values.</jc>
+ * Map<String,String> myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
+ * Map<String,List<MyBean>> myparam = req.getPathParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The attribute name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param <T> The class type to convert the attribute value to.
+ * @return The attribute value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Type type, Type...args) throws ParseException {
+ return (T)parse(name, beanSession.getClassMeta(type, args));
+ }
+
+ /* Workhorse method */
+ <T> T parse(String name, ClassMeta<T> cm) throws ParseException {
+ Object attr = get(name);
+ T t = null;
+ if (attr != null)
+ t = parser.parsePart(attr.toString(), cm);
+ if (t == null && cm.isPrimitive())
+ return cm.getPrimitiveDefault();
+ return t;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
new file mode 100644
index 0000000..6a9a564
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestQuery.java
@@ -0,0 +1,287 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents the query parameters in an HTTP request.
+ */
+@SuppressWarnings("unchecked")
+public final class RequestQuery extends LinkedHashMap<String,String[]> {
+ private static final long serialVersionUID = 1L;
+
+ private UrlEncodingParser parser;
+ private BeanSession beanSession;
+
+ RequestQuery setParser(UrlEncodingParser parser) {
+ this.parser = parser;
+ return this;
+ }
+
+ RequestQuery setBeanSession(BeanSession beanSession) {
+ this.beanSession = beanSession;
+ return this;
+ }
+
+ /**
+ * Sets a request query parameter value.
+ *
+ * @param name The parameter name.
+ * @param value The parameter value.
+ */
+ public void put(String name, Object value) {
+ put(name, new String[]{StringUtils.toString(value)});
+ }
+
+ /**
+ * Returns a query parameter value.
+ * <p>
+ * Same as {@link HttpServletRequest#getParameter(String)} except only looks in the URL string, not parameters from URL-Encoded FORM posts.
+ * <p>
+ * This method can be used to retrieve a parameter without triggering the underlying servlet API to load and parse the request body.
+ *
+ * @param name The URL parameter name.
+ * @return The parameter value, or <jk>null</jk> if parameter not specified or has no value (e.g. <js>"&foo"</js>.
+ */
+ public String getFirst(String name) {
+ String[] v = get(name);
+ if (v == null || v.length == 0)
+ return null;
+ if (v.length == 1 && v[0] != null && v[0].isEmpty()) {
+ // Fix for behavior difference between Tomcat and WAS.
+ // getParameter("foo") on "&foo" in Tomcat returns "".
+ // getParameter("foo") on "&foo" in WAS returns null.
+ if (containsKey(name))
+ return null;
+ }
+ return v[0];
+ }
+
+ /**
+ * Same as {@link #getFirst(String)} but returns the specified default value if the query parameter was not specified.
+ *
+ * @param name The URL parameter name.
+ * @param def The default value.
+ * @return The parameter value, or the default value if parameter not specified or has no value (e.g. <js>"&foo"</js>.
+ */
+ public String getFirst(String name, String def) {
+ String s = getFirst(name);
+ return s == null ? def : s;
+ }
+
+ /**
+ * Returns the specified query parameter value converted to a POJO.
+ * <p>
+ * This method can be used to retrieve a parameter without triggering the underlying servlet API to load and parse the request body.
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> myparam = req.getQueryParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse into an int array.</jc>
+ * <jk>int</jk>[] myparam = req.getQueryParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+
+ * <jc>// Parse into a bean.</jc>
+ * MyBean myparam = req.getQueryParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of objects.</jc>
+ * List myparam = req.getQueryParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of object keys/values.</jc>
+ * Map myparam = req.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Class<T> type) throws ParseException {
+ return get(name, beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} except returns a default value if not found.
+ *
+ * @param name The parameter name.
+ * @param def The default value if the parameter was not specified or is <jk>null</jk>.
+ * @param type The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, T def, Class<T> type) throws ParseException {
+ return get(name, def, beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified query parameter value converted to a POJO.
+ * <p>
+ * This method can be used to retrieve a parameter without triggering the underlying servlet API to load and parse the request body.
+ * <p>
+ * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into a linked-list of strings.</jc>
+ * Listt<String> myparam = req.getQueryParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
+ * Listt<List<String>> myparam = req.getQueryParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of string keys/values.</jc>
+ * Map<String,String> myparam = req.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
+ * Map<String,List<MyBean>> myparam = req.getQueryParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The parameter name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Type type, Type...args) throws ParseException {
+ return (T)parse(name, beanSession.getClassMeta(type, args));
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} except returns a default value if not found.
+ *
+ * @param name The parameter name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param def The default value if the parameter was not specified or is <jk>null</jk>.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Object def, Type type, Type...args) throws ParseException {
+ return (T)parse(name, def, beanSession.getClassMeta(type, args));
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} except for use on multi-part parameters
+ * (e.g. <js>"&key=1&key=2&key=3"</js> instead of <js>"&key=(1,2,3)"</js>).
+ * <p>
+ * This method must only be called when parsing into classes of type Collection or array.
+ *
+ * @param name The query parameter name.
+ * @param c The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The query parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T getAll(String name, Class<T> c) throws ParseException {
+ return getAll(name, beanSession.getClassMeta(c));
+ }
+
+ /**
+ * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
+ * (e.g. <js>"&key=1&key=2&key=3"</js> instead of <js>"&key=(1,2,3)"</js>).
+ * <p>
+ * This method must only be called when parsing into classes of type Collection or array.
+ *
+ * @param name The query parameter name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The query parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T getAll(String name, Type type, Type...args) throws ParseException {
+ return (T)parseAll(name, beanSession.getClassMeta(type, args));
+ }
+
+ /**
+ * Returns <jk>true</jk> if the request contains any of the specified query parameters.
+ *
+ * @param params The list of parameters to check for.
+ * @return <jk>true</jk> if the request contains any of the specified query parameters.
+ */
+ public boolean containsAnyKeys(String...params) {
+ for (String p : params)
+ if (containsKey(p))
+ return true;
+ return false;
+ }
+
+ /* Workhorse method */
+ private <T> T parse(String name, T def, ClassMeta<T> cm) throws ParseException {
+ String val = getFirst(name);
+ if (val == null)
+ return def;
+ return parseValue(val, cm);
+ }
+
+ /* Workhorse method */
+ private <T> T parse(String name, ClassMeta<T> cm) throws ParseException {
+ String val = getFirst(name);
+ if (cm.isPrimitive() && (val == null || val.isEmpty()))
+ return cm.getPrimitiveDefault();
+ return parseValue(val, cm);
+ }
+
+ /* Workhorse method */
+ @SuppressWarnings("rawtypes")
+ private <T> T parseAll(String name, ClassMeta<T> cm) throws ParseException {
+ String[] p = get(name);
+ if (p == null)
+ return null;
+ if (cm.isArray()) {
+ List c = new ArrayList();
+ for (int i = 0; i < p.length; i++)
+ c.add(parseValue(p[i], cm.getElementType()));
+ return (T)ArrayUtils.toArray(c, cm.getElementType().getInnerClass());
+ } else if (cm.isCollection()) {
+ try {
+ Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
+ for (int i = 0; i < p.length; i++)
+ c.add(parseValue(p[i], cm.getElementType()));
+ return (T)c;
+ } catch (ParseException e) {
+ throw e;
+ } catch (Exception e) {
+ // Typically an instantiation exception.
+ throw new ParseException(e);
+ }
+ }
+ throw new ParseException("Invalid call to getQueryParameters(String, ClassMeta). Class type must be a Collection or array.");
+ }
+
+ private <T> T parseValue(String val, ClassMeta<T> c) throws ParseException {
+ return parser.parsePart(val, c);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
index 97ab2d4..54a3fa0 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -444,7 +444,7 @@ public final class RestContext extends Context {
if (m != null) {
try {
// Parse the args and invoke the method.
- Parser p = req.getParser();
+ Parser p = req.getBody().getParser();
Object input = p.isReaderParser() ? req.getReader() : req.getInputStream();
Object output = m.invoke(o, p.parseArgs(input, m.getGenericParameterTypes()));
res.setOutput(output);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RestParam.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestParam.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParam.java
new file mode 100644
index 0000000..a900c14
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParam.java
@@ -0,0 +1,978 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import static org.apache.juneau.rest.RestParamType.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.logging.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.http.Date;
+import org.apache.juneau.ini.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * REST java method parameter resolver.
+ */
+public abstract class RestParam {
+
+ /**
+ * Standard set of method parameter resolvers.
+ */
+ public static final Map<Class<?>,RestParam> STANDARD_RESOLVERS;
+
+ static {
+ Map<Class<?>,RestParam> m = new HashMap<Class<?>,RestParam>();
+
+ @SuppressWarnings("rawtypes")
+ Class[] r = new Class[] {
+
+ // Standard top-level objects
+ HttpServletRequestObject.class,
+ RestRequestObject.class,
+ HttpServletResponseObject.class,
+ RestResponseObject.class,
+
+ // Headers
+ AcceptHeader.class,
+ AcceptCharsetHeader.class,
+ AcceptEncodingHeader.class,
+ AcceptLanguageHeader.class,
+ AuthorizationHeader.class,
+ CacheControlHeader.class,
+ ConnectionHeader.class,
+ ContentLengthHeader.class,
+ ContentTypeHeader.class,
+ DateHeader.class,
+ ExpectHeader.class,
+ FromHeader.class,
+ HostHeader.class,
+ IfMatchHeader.class,
+ IfModifiedSinceHeader.class,
+ IfNoneMatchHeader.class,
+ IfRangeHeader.class,
+ IfUnmodifiedSinceHeader.class,
+ MaxForwardsHeader.class,
+ PragmaHeader.class,
+ ProxyAuthorizationHeader.class,
+ RangeHeader.class,
+ RefererHeader.class,
+ TEHeader.class,
+ UserAgentHeader.class,
+ UpgradeHeader.class,
+ ViaHeader.class,
+ WarningHeader.class,
+ TimeZoneHeader.class,
+
+ // Other objects
+ ResourceBundleObject.class,
+ MessageBundleObject.class,
+ InputStreamObject.class,
+ ServletInputStreamObject.class,
+ ReaderObject.class,
+ OutputStreamObject.class,
+ ServletOutputStreamObject.class,
+ WriterObject.class,
+ RequestHeadersObject.class,
+ RequestQueryParamsObject.class,
+ RequestFormDataObject.class,
+ HttpMethodObject.class,
+ LoggerObject.class,
+ JuneauLoggerObject.class,
+ RestContextObject.class,
+ ParserObject.class,
+ LocaleObject.class,
+ SwaggerObject.class,
+ RequestPathParamsObject.class,
+ RequestBodyObject.class,
+ ConfigFileObject.class,
+ };
+
+ for (Class<?> c : r) {
+ try {
+ RestParam mpr = (RestParam)c.newInstance();
+ m.put(mpr.forClass(), mpr);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ STANDARD_RESOLVERS = Collections.unmodifiableMap(m);
+ }
+
+ final RestParamType paramType;
+ final String name;
+ final Type type;
+
+ /**
+ * Constructor.
+ *
+ * @param paramType The Swagger parameter type.
+ * @param name The parameter name.
+ * Can be <jk>null</jk> if parameter doesn't have a name (e.g. the request body).
+ * @param type The object type to convert the parameter to.
+ */
+ protected RestParam(RestParamType paramType, String name, Type type) {
+ this.paramType = paramType;
+ this.name = name;
+ this.type = type;
+ }
+
+ /**
+ * Resolves the parameter object.
+ *
+ * @param req The rest request.
+ * @param res The rest response.
+ * @return The resolved object.
+ * @throws Exception
+ */
+ public abstract Object resolve(RestRequest req, RestResponse res) throws Exception;
+
+ /**
+ * Returns the parameter class type that this parameter resolver is meant for.
+ * @return The parameter class type, or <jk>null</jk> if the type passed in isn't an instance of {@link Class}.
+ */
+ protected Class<?> forClass() {
+ if (type instanceof Class)
+ return (Class<?>)type;
+ return null;
+ }
+
+ /**
+ * Returns the swagger parameter type for this parameter as shown in the Swagger doc.
+ * @return the swagger parameter type for this parameter.
+ */
+ protected RestParamType getParamType() {
+ return paramType;
+ }
+
+ /**
+ * Returns the parameter name for this parameter as shown in the Swagger doc.
+ * @return the parameter name for this parameter.
+ */
+ protected String getName() {
+ return name;
+ }
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Request / Response retrievers
+ //-------------------------------------------------------------------------------------------------------------------
+
+ static final class HttpServletRequestObject extends RestParam {
+
+ protected HttpServletRequestObject() {
+ super(OTHER, null, HttpServletRequest.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) {
+ return req;
+ }
+ }
+
+ static final class HttpServletResponseObject extends RestParam {
+
+ protected HttpServletResponseObject() {
+ super(OTHER, null, HttpServletResponse.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) {
+ return res;
+ }
+ }
+
+ static final class RestRequestObject extends RestParam {
+
+ protected RestRequestObject() {
+ super(OTHER, null, RestRequest.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) {
+ return req;
+ }
+ }
+
+ static final class RestResponseObject extends RestParam {
+
+ protected RestResponseObject() {
+ super(OTHER, null, RestResponse.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) {
+ return res;
+ }
+ }
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Header retrievers
+ //-------------------------------------------------------------------------------------------------------------------
+
+ static final class AcceptHeader extends RestParam {
+
+ protected AcceptHeader() {
+ super(HEADER, "Accept-Header", AcceptHeader.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getAccept();
+ }
+ }
+
+ static final class AcceptCharsetHeader extends RestParam {
+
+ protected AcceptCharsetHeader() {
+ super(HEADER, "Accept-Charset", AcceptCharset.class);
+ }
+
+ @Override /* RestParam */
+ public AcceptCharset resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getAcceptCharset();
+ }
+ }
+
+ static final class AcceptEncodingHeader extends RestParam {
+
+ protected AcceptEncodingHeader() {
+ super(HEADER, "Accept-Encoding", AcceptEncoding.class);
+ }
+
+ @Override
+ public AcceptEncoding resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getAcceptEncoding();
+ }
+ }
+
+ static final class AcceptLanguageHeader extends RestParam {
+
+ protected AcceptLanguageHeader() {
+ super(HEADER, "Accept-Language", AcceptLanguage.class);
+ }
+
+ @Override
+ public AcceptLanguage resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getAcceptLanguage();
+ }
+ }
+
+ static final class AuthorizationHeader extends RestParam {
+
+ protected AuthorizationHeader() {
+ super(HEADER, "Authorization", Authorization.class);
+ }
+
+ @Override
+ public Authorization resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getAuthorization();
+ }
+ }
+
+ static final class CacheControlHeader extends RestParam {
+
+ protected CacheControlHeader() {
+ super(HEADER, "Cache-Control", CacheControl.class);
+ }
+
+ @Override
+ public CacheControl resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getCacheControl();
+ }
+ }
+
+ static final class ConnectionHeader extends RestParam {
+
+ protected ConnectionHeader() {
+ super(HEADER, "Connection", Connection.class);
+ }
+
+ @Override
+ public Connection resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getConnection();
+ }
+ }
+
+ static final class ContentLengthHeader extends RestParam {
+
+ protected ContentLengthHeader() {
+ super(HEADER, "Content-Length", ContentLength.class);
+ }
+
+ @Override
+ public ContentLength resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getContentLength();
+ }
+ }
+
+ static final class ContentTypeHeader extends RestParam {
+
+ protected ContentTypeHeader() {
+ super(HEADER, "Content-Type", ContentType.class);
+ }
+
+ @Override
+ public ContentType resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getContentType();
+ }
+ }
+
+ static final class DateHeader extends RestParam {
+
+ protected DateHeader() {
+ super(HEADER, "Date", Date.class);
+ }
+
+ @Override
+ public Date resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getDate();
+ }
+ }
+
+ static final class ExpectHeader extends RestParam {
+
+ protected ExpectHeader() {
+ super(HEADER, "Expect", Expect.class);
+ }
+
+ @Override
+ public Expect resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getExpect();
+ }
+ }
+
+ static final class FromHeader extends RestParam {
+
+ protected FromHeader() {
+ super(HEADER, "From", From.class);
+ }
+
+ @Override
+ public From resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getFrom();
+ }
+ }
+
+ static final class HostHeader extends RestParam {
+
+ protected HostHeader() {
+ super(HEADER, "Host", Host.class);
+ }
+
+ @Override
+ public Host resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getHost();
+ }
+ }
+
+ static final class IfMatchHeader extends RestParam {
+
+ protected IfMatchHeader() {
+ super(HEADER, "If-Match", IfMatch.class);
+ }
+
+ @Override
+ public IfMatch resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getIfMatch();
+ }
+ }
+
+ static final class IfModifiedSinceHeader extends RestParam {
+
+ protected IfModifiedSinceHeader() {
+ super(HEADER, "If-Modified-Since", IfModifiedSince.class);
+ }
+
+ @Override
+ public IfModifiedSince resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getIfModifiedSince();
+ }
+ }
+
+ static final class IfNoneMatchHeader extends RestParam {
+
+ protected IfNoneMatchHeader() {
+ super(HEADER, "If-None-Match", IfNoneMatch.class);
+ }
+
+ @Override
+ public IfNoneMatch resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getIfNoneMatch();
+ }
+ }
+
+ static final class IfRangeHeader extends RestParam {
+
+ protected IfRangeHeader() {
+ super(HEADER, "If-Range", IfRange.class);
+ }
+
+ @Override
+ public IfRange resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getIfRange();
+ }
+ }
+
+ static final class IfUnmodifiedSinceHeader extends RestParam {
+
+ protected IfUnmodifiedSinceHeader() {
+ super(HEADER, "If-Unmodified-Since", IfUnmodifiedSince.class);
+ }
+
+ @Override
+ public IfUnmodifiedSince resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getIfUnmodifiedSince();
+ }
+ }
+
+ static final class MaxForwardsHeader extends RestParam {
+
+ protected MaxForwardsHeader() {
+ super(HEADER, "Max-Forwards", MaxForwards.class);
+ }
+
+ @Override
+ public MaxForwards resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getMaxForwards();
+ }
+ }
+
+ static final class PragmaHeader extends RestParam {
+
+ protected PragmaHeader() {
+ super(HEADER, "Pragma", Pragma.class);
+ }
+
+ @Override
+ public Pragma resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getPragma();
+ }
+ }
+
+ static final class ProxyAuthorizationHeader extends RestParam {
+
+ protected ProxyAuthorizationHeader() {
+ super(HEADER, "Proxy-Authorization", ProxyAuthorization.class);
+ }
+
+ @Override
+ public ProxyAuthorization resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getProxyAuthorization();
+ }
+ }
+
+ static final class RangeHeader extends RestParam {
+
+ protected RangeHeader() {
+ super(HEADER, "Range", Range.class);
+ }
+
+ @Override
+ public Range resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getRange();
+ }
+ }
+
+ static final class RefererHeader extends RestParam {
+
+ protected RefererHeader() {
+ super(HEADER, "Referer", Referer.class);
+ }
+
+ @Override
+ public Referer resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getReferer();
+ }
+ }
+
+ static final class TEHeader extends RestParam {
+
+ protected TEHeader() {
+ super(HEADER, "TE", TE.class);
+ }
+
+ @Override
+ public TE resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getTE();
+ }
+ }
+
+ static final class UserAgentHeader extends RestParam {
+
+ protected UserAgentHeader() {
+ super(HEADER, "User-Agent", UserAgent.class);
+ }
+
+ @Override
+ public UserAgent resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getUserAgent();
+ }
+ }
+
+ static final class UpgradeHeader extends RestParam {
+
+ protected UpgradeHeader() {
+ super(HEADER, "Upgrade", Upgrade.class);
+ }
+
+ @Override
+ public Upgrade resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getUpgrade();
+ }
+ }
+
+ static final class ViaHeader extends RestParam {
+
+ protected ViaHeader() {
+ super(HEADER, "Via", Via.class);
+ }
+
+ @Override
+ public Via resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getVia();
+ }
+ }
+
+ static final class WarningHeader extends RestParam {
+
+ protected WarningHeader() {
+ super(HEADER, "Warning", Warning.class);
+ }
+
+ @Override
+ public Warning resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getWarning();
+ }
+ }
+
+ static final class TimeZoneHeader extends RestParam {
+
+ protected TimeZoneHeader() {
+ super(HEADER, "Time-Zone", TimeZone.class);
+ }
+
+ @Override
+ public TimeZone resolve(RestRequest req, RestResponse res) {
+ return req.getHeaders().getTimeZone();
+ }
+ }
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Annotated retrievers
+ //-------------------------------------------------------------------------------------------------------------------
+
+ static final class PathParameterObject extends RestParam {
+
+ protected PathParameterObject(String name, Type type) {
+ super(PATH, name, type);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getPathParams().get(name, type);
+ }
+ }
+
+ static final class BodyObject extends RestParam {
+
+ protected BodyObject(Type type) {
+ super(BODY, null, type);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getBody().asType(type);
+ }
+ }
+
+ static final class HeaderObject extends RestParam {
+
+ protected HeaderObject(String name, Type type) {
+ super(HEADER, name, type);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getHeaders().get(name, type);
+ }
+ }
+
+ static final class MethodObject extends RestParam {
+
+ protected MethodObject() {
+ super(OTHER, null, null);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getMethod();
+ }
+ }
+
+ static final class FormDataObject extends RestParam {
+ private final boolean multiPart, plainParams;
+
+ protected FormDataObject(String name, Type type, boolean multiPart, boolean plainParams) {
+ super(FORMDATA, name, type);
+ this.multiPart = multiPart;
+ this.plainParams = plainParams;
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ BeanSession bs = req.getBeanSession();
+ if (multiPart)
+ return req.getFormData().getAll(name, type);
+ if (plainParams)
+ return bs.convertToType(req.getFormData(name), bs.getClassMeta(type));
+ return req.getFormData().get(name, type);
+ }
+ }
+
+ static final class QueryObject extends RestParam {
+ private final boolean multiPart, plainParams;
+
+ protected QueryObject(String name, Type type, boolean multiPart, boolean plainParams) {
+ super(QUERY, name, type);
+ this.multiPart = multiPart;
+ this.plainParams = plainParams;
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ BeanSession bs = req.getBeanSession();
+ if (multiPart)
+ return req.getQuery().getAll(name, type);
+ if (plainParams)
+ return bs.convertToType(req.getQuery(name), bs.getClassMeta(type));
+ return req.getQuery().get(name, type);
+ }
+ }
+
+ static final class HasFormDataObject extends RestParam {
+
+ protected HasFormDataObject(String name, Type type) {
+ super(FORMDATA, name, type);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ BeanSession bs = req.getBeanSession();
+ return bs.convertToType(req.getFormData().containsKey(name), bs.getClassMeta(type));
+ }
+ }
+
+ static final class HasQueryObject extends RestParam {
+
+ protected HasQueryObject(String name, Type type) {
+ super(QUERY, name, type);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ BeanSession bs = req.getBeanSession();
+ return bs.convertToType(req.getQuery().containsKey(name), bs.getClassMeta(type));
+ }
+ }
+
+ static final class PathRemainderObject extends RestParam {
+
+ protected PathRemainderObject() {
+ super(OTHER, null, null);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getPathRemainder();
+ }
+ }
+
+ static final class PropsObject extends RestParam {
+
+ protected PropsObject() {
+ super(OTHER, null, null);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getProperties();
+ }
+ }
+
+ //-------------------------------------------------------------------------------------------------------------------
+ // Other retrievers
+ //-------------------------------------------------------------------------------------------------------------------
+
+ static final class ResourceBundleObject extends RestParam {
+
+ protected ResourceBundleObject() {
+ super(OTHER, null, ResourceBundle.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getResourceBundle();
+ }
+ }
+
+ static final class MessageBundleObject extends RestParam {
+
+ protected MessageBundleObject() {
+ super(OTHER, null, MessageBundle.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getResourceBundle();
+ }
+ }
+
+ static final class InputStreamObject extends RestParam {
+
+ protected InputStreamObject() {
+ super(OTHER, null, InputStream.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getInputStream();
+ }
+ }
+
+ static final class ServletInputStreamObject extends RestParam {
+
+ protected ServletInputStreamObject() {
+ super(OTHER, null, ServletInputStream.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getInputStream();
+ }
+ }
+
+ static final class ReaderObject extends RestParam {
+
+ protected ReaderObject() {
+ super(OTHER, null, Reader.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getReader();
+ }
+ }
+
+ static final class OutputStreamObject extends RestParam {
+
+ protected OutputStreamObject() {
+ super(OTHER, null, OutputStream.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return res.getOutputStream();
+ }
+ }
+
+ static final class ServletOutputStreamObject extends RestParam {
+
+ protected ServletOutputStreamObject() {
+ super(OTHER, null, ServletOutputStream.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return res.getOutputStream();
+ }
+ }
+
+ static final class WriterObject extends RestParam {
+
+ protected WriterObject() {
+ super(OTHER, null, Writer.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return res.getWriter();
+ }
+ }
+
+ static final class RequestHeadersObject extends RestParam {
+
+ protected RequestHeadersObject() {
+ super(OTHER, null, RequestHeaders.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getHeaders();
+ }
+ }
+
+ static final class RequestQueryParamsObject extends RestParam {
+
+ protected RequestQueryParamsObject() {
+ super(OTHER, null, RequestQuery.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getQuery();
+ }
+ }
+
+ static final class RequestFormDataObject extends RestParam {
+
+ protected RequestFormDataObject() {
+ super(OTHER, null, RequestFormData.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getFormData();
+ }
+ }
+
+ static final class HttpMethodObject extends RestParam {
+
+ protected HttpMethodObject() {
+ super(OTHER, null, HttpMethod.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getHttpMethod();
+ }
+ }
+
+ static final class LoggerObject extends RestParam {
+
+ protected LoggerObject() {
+ super(OTHER, null, Logger.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getContext().getLogger().getLogger();
+ }
+ }
+
+ static final class JuneauLoggerObject extends RestParam {
+
+ protected JuneauLoggerObject() {
+ super(OTHER, null, JuneauLogger.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getContext().getLogger().getLogger();
+ }
+ }
+
+ static final class RestContextObject extends RestParam {
+
+ protected RestContextObject() {
+ super(OTHER, null, RestContext.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getContext();
+ }
+ }
+
+ static final class ParserObject extends RestParam {
+
+ protected ParserObject() {
+ super(OTHER, null, Parser.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getBody().getParser();
+ }
+ }
+
+ static final class LocaleObject extends RestParam {
+
+ protected LocaleObject() {
+ super(OTHER, null, Locale.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getLocale();
+ }
+ }
+
+ static final class SwaggerObject extends RestParam {
+
+ protected SwaggerObject() {
+ super(OTHER, null, Swagger.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getSwagger();
+ }
+ }
+
+ static final class RequestPathParamsObject extends RestParam {
+
+ protected RequestPathParamsObject() {
+ super(OTHER, null, RequestPathParams.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getPathParams();
+ }
+ }
+
+ static final class RequestBodyObject extends RestParam {
+
+ protected RequestBodyObject() {
+ super(BODY, null, RequestBody.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getBody();
+ }
+ }
+
+ static final class ConfigFileObject extends RestParam {
+
+ protected ConfigFileObject() {
+ super(OTHER, null, ConfigFile.class);
+ }
+
+ @Override /* RestParam */
+ public Object resolve(RestRequest req, RestResponse res) throws Exception {
+ return req.getConfigFile();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamType.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamType.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamType.java
new file mode 100644
index 0000000..70fe2eb
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestParamType.java
@@ -0,0 +1,48 @@
+// ***************************************************************************************************************************
+// * 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;
+
+/**
+ * Represents the possible parameter types as defined by the Swagger 2.0 specification.
+ */
+public enum RestParamType {
+
+ /** Path variable */
+ PATH("path"),
+
+ /** Header value */
+ HEADER("header"),
+
+ /** Form data entry */
+ FORMDATA("formData"),
+
+ /** Query parameter */
+ QUERY("query"),
+
+ /** Request body */
+ BODY("body"),
+
+ /** Not a standard Swagger-defined field */
+ OTHER("other");
+
+ private final String value;
+
+ private RestParamType(String value) {
+ this.value = value;
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return value;
+ }
+}
[3/3] incubator-juneau git commit: Break up RestRequest into
different functional classes.
Posted by ja...@apache.org.
Break up RestRequest into different functional classes.
Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/321f6bde
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/321f6bde
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/321f6bde
Branch: refs/heads/master
Commit: 321f6bdeef10b2d5cabb3a51c14dc92349aa7cb3
Parents: fa4736b
Author: JamesBognar <ja...@apache.org>
Authored: Sun May 7 16:16:08 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Sun May 7 16:16:08 2017 -0400
----------------------------------------------------------------------
.../urlencoding/UrlEncodingParserTest.java | 8 +-
.../java/org/apache/juneau/http/HttpMethod.java | 73 +
.../org/apache/juneau/internal/WrappedMap.java | 94 ++
.../juneau/urlencoding/UrlEncodingParser.java | 5 +-
juneau-core/src/main/javadoc/overview.html | 34 +-
.../examples/rest/MethodExampleResource.java | 16 +-
.../rest/addressbook/AddressBookResource.java | 4 +-
.../rest/test/CallbackStringsResource.java | 8 +-
.../juneau/rest/test/OnPreCallResource.java | 2 +-
.../rest/test/OverlappingMethodsResource.java | 8 +-
.../apache/juneau/rest/test/ParamsResource.java | 36 +-
.../juneau/rest/test/RestClient2Resource.java | 2 +-
.../java/org/apache/juneau/rest/CallMethod.java | 32 +-
.../org/apache/juneau/rest/RequestBody.java | 431 ++++++
.../org/apache/juneau/rest/RequestFormData.java | 271 ++++
.../org/apache/juneau/rest/RequestHeaders.java | 675 ++++++++++
.../apache/juneau/rest/RequestPathParams.java | 137 ++
.../org/apache/juneau/rest/RequestQuery.java | 287 ++++
.../org/apache/juneau/rest/RestContext.java | 2 +-
.../java/org/apache/juneau/rest/RestParam.java | 978 ++++++++++++++
.../org/apache/juneau/rest/RestParamType.java | 48 +
.../org/apache/juneau/rest/RestRequest.java | 1247 +++---------------
.../apache/juneau/rest/annotation/FormData.java | 2 +-
.../juneau/rest/annotation/HasFormData.java | 2 +-
.../apache/juneau/rest/annotation/HasQuery.java | 2 +-
.../apache/juneau/rest/annotation/Query.java | 2 +-
.../juneau/rest/converters/Introspectable.java | 4 +-
.../juneau/rest/converters/Queryable.java | 17 +-
.../juneau/rest/converters/Traversable.java | 2 +-
.../rest/remoteable/RemoteableServlet.java | 4 +-
.../juneau/rest/response/DefaultHandler.java | 4 +-
.../org/apache/juneau/rest/vars/RequestVar.java | 4 +-
32 files changed, 3258 insertions(+), 1183 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
index d23eca3..3ae3679 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/urlencoding/UrlEncodingParserTest.java
@@ -606,7 +606,7 @@ public class UrlEncodingParserTest {
Map<String,String[]> m;
String s = "?f1=,()=&f2a=$b(true)&f2b=true&f3a=$n(123)&f3b=123&f4=$s(foo)";
- m = p.parseIntoSimpleMap(s);
+ m = p.parseIntoSimpleMap(s, null);
assertEquals(",()=", m.get("f1")[0]);
assertEquals("$b(true)", m.get("f2a")[0]);
assertEquals("true", m.get("f2b")[0]);
@@ -615,12 +615,12 @@ public class UrlEncodingParserTest {
assertEquals("$s(foo)", m.get("f4")[0]);
s = "f1=v1&=";
- m = p.parseIntoSimpleMap(s);
+ m = p.parseIntoSimpleMap(s, null);
assertEquals("v1", m.get("f1")[0]);
assertEquals("", m.get("")[0]);
s = "f1=v1&f2&f3";
- m = p.parseIntoSimpleMap(s);
+ m = p.parseIntoSimpleMap(s, null);
assertEquals("v1", m.get("f1")[0]);
assertTrue(m.containsKey("f2"));
assertTrue(m.containsKey("f3"));
@@ -637,7 +637,7 @@ public class UrlEncodingParserTest {
Map<String,String[]> m;
String s = "?f1&f1&f2&f2=abc&f2=def&f2";
- m = p.parseIntoSimpleMap(s);
+ m = p.parseIntoSimpleMap(s, null);
assertObjectEquals("{f1:null,f2:['abc','def']}", m);
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-core/src/main/java/org/apache/juneau/http/HttpMethod.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/http/HttpMethod.java b/juneau-core/src/main/java/org/apache/juneau/http/HttpMethod.java
new file mode 100644
index 0000000..b6ccf3b
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/http/HttpMethod.java
@@ -0,0 +1,73 @@
+// ***************************************************************************************************************************
+// * 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.http;
+
+import java.util.*;
+
+/**
+ * Represents valid HTTP 1.1 method names per the <a class='doclink' href='https://www.ietf.org/rfc/rfc2616.txt'>RFC 2616</a> spec.
+ */
+public enum HttpMethod {
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2'>OPTIONS</a> */
+ OPTIONS,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3'>GET</a> */
+ GET,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4'>HEAD</a> */
+ HEAD,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5'>POST</a> */
+ POST,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6'>PUT</a> */
+ PUT,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7'>DELETE</a> */
+ DELETE,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8'>TRACE</a> */
+ TRACE,
+
+ /** <a class='doclink' href='https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.9'>CONNECT</a> */
+ CONNECT,
+
+ /** A non-standard value. */
+ OTHER;
+
+ private static final Map<String,HttpMethod> cache = new TreeMap<String,HttpMethod>(String.CASE_INSENSITIVE_ORDER);
+ static {
+ cache.put("OPTIONS", OPTIONS);
+ cache.put("GET", GET);
+ cache.put("HEAD", HEAD);
+ cache.put("POST", POST);
+ cache.put("PUT", PUT);
+ cache.put("DELETE", DELETE);
+ cache.put("TRACE", TRACE);
+ cache.put("CONNECT", CONNECT);
+ }
+
+ /**
+ * Returns the enum for the specified key.
+ * <p>
+ * Case is ignored.
+ *
+ * @param key The HTTP method name.
+ * @return The HttpMethod enum, or {@link #OTHER} if it's not a standard method name.
+ */
+ public static HttpMethod forString(String key) {
+ HttpMethod m = cache.get(key);
+ return m == null ? OTHER : m;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-core/src/main/java/org/apache/juneau/internal/WrappedMap.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/WrappedMap.java b/juneau-core/src/main/java/org/apache/juneau/internal/WrappedMap.java
new file mode 100644
index 0000000..d1f0adf
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/WrappedMap.java
@@ -0,0 +1,94 @@
+// ***************************************************************************************************************************
+// * 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.internal;
+
+import java.util.*;
+
+/**
+ * Wraps an existing map inside an extensible interface so that convenience methods can be added to the subclass.
+ * @param <K> The key type.
+ * @param <V> The value type.
+ */
+public class WrappedMap<K,V> implements Map<K,V> {
+
+ private final Map<K,V> inner;
+
+ /**
+ * Constructor.
+ *
+ * @param inner The inner map.
+ */
+ protected WrappedMap(Map<K,V> inner) {
+ this.inner = inner;
+ }
+
+ @Override /* Map */
+ public void clear() {
+ inner.clear();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return inner.containsKey(key);
+ }
+
+ @Override /* Map */
+ public boolean containsValue(Object value) {
+ return inner.containsValue(value);
+ }
+
+ @Override /* Map */
+ public Set<java.util.Map.Entry<K,V>> entrySet() {
+ return inner.entrySet();
+ }
+
+ @Override /* Map */
+ public V get(Object key) {
+ return inner.get(key);
+ }
+
+ @Override /* Map */
+ public boolean isEmpty() {
+ return inner.isEmpty();
+ }
+
+ @Override /* Map */
+ public Set<K> keySet() {
+ return inner.keySet();
+ }
+
+ @Override /* Map */
+ public V put(K key, V value) {
+ return inner.put(key, value);
+ }
+
+ @Override /* Map */
+ public void putAll(Map<? extends K,? extends V> m) {
+ inner.putAll(m);
+ }
+
+ @Override /* Map */
+ public V remove(Object key) {
+ return inner.remove(key);
+ }
+
+ @Override /* Map */
+ public int size() {
+ return inner.size();
+ }
+
+ @Override /* Map */
+ public Collection<V> values() {
+ return inner.values();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
index af4881e..cb9f19c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java
@@ -333,12 +333,13 @@ public class UrlEncodingParser extends UonParser {
* Parse a URL query string into a simple map of key/value pairs.
*
* @param qs The query string to parse.
+ * @param map The map to parse into. If <jk>null</jk>, then a new {@link TreeMap} will be used.
* @return A sorted {@link TreeMap} of query string entries.
* @throws Exception
*/
- public Map<String,String[]> parseIntoSimpleMap(String qs) throws Exception {
+ public Map<String,String[]> parseIntoSimpleMap(String qs, Map<String,String[]> map) throws Exception {
- Map<String,String[]> m = new TreeMap<String,String[]>();
+ Map<String,String[]> m = map == null ? new TreeMap<String,String[]>() : map;
if (StringUtils.isEmpty(qs))
return m;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/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 566c169..44ef436 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -5911,29 +5911,29 @@
<h6 class='topic'>org.apache.juneau</h6>
<ul class='spaced-list'>
<li>New package: {@link org.apache.juneau.http}.
- <ul>
- <li>{@link org.apache.juneau.http.Accept}
- <li>{@link org.apache.juneau.http.AcceptEncoding}
- <li>{@link org.apache.juneau.http.ContentType}
- </ul>
<li>Support for dynamic beans. See {@link org.apache.juneau.annotation.BeanProperty#name() @BeanProperty.name()}.
<li>New doc: <a class='doclink' href='#Core.JacksonComparison'>2.12 - Comparison with Jackson</a>
</ul>
<h6 class='topic'>org.apache.juneau.rest</h6>
<ul class='spaced-list'>
- <li>The following object types can now be specified as unannotated arguments on REST Java methods:
- <ul>
- <li>{@link org.apache.juneau.http.Accept}
- <li>{@link org.apache.juneau.http.AcceptEncoding}
- <li>{@link org.apache.juneau.http.ContentType}
- </ul>
- <li>New methods on {@link org.apache.juneau.rest.RestRequest}:
- <ul>
- <li>{@link org.apache.juneau.rest.RestRequest#getAcceptHeader() getAcceptHeader()}
- <li>{@link org.apache.juneau.rest.RestRequest#getAcceptEncodingHeader() getAcceptEncodingHeader()}
- <li>{@link org.apache.juneau.rest.RestRequest#getContentTypeHeader() getContentTypeHeader()}
- </ul>
+ <li>The {@link org.apache.juneau.rest.RestRequest} class functionality has been broken up into the following
+ functional pieces to reduce its complexity:
+ <ul>
+ <li>{@link org.apache.juneau.rest.RestRequest#getBody()} - The request body.
+ <li>{@link org.apache.juneau.rest.RestRequest#getHeaders()} - The request headers.
+ <li>{@link org.apache.juneau.rest.RestRequest#getQuery()} - The request query parameters.
+ <li>{@link org.apache.juneau.rest.RestRequest#getFormData()} - The request form data parameters.
+ <li>{@link org.apache.juneau.rest.RestRequest#getPathParams()} - The path variables.
+ </ul>
+ The following classes have been introduced:
+ <ul>
+ <li>{@link org.apache.juneau.rest.RequestBody}
+ <li>{@link org.apache.juneau.rest.RequestHeaders}
+ <li>{@link org.apache.juneau.rest.RequestQuery}
+ <li>{@link org.apache.juneau.rest.RequestFormData}
+ <li>{@link org.apache.juneau.rest.RequestPathParams}
+ </ul>
</ul>
<h6 class='topic'>org.apache.juneau.rest.client</h6>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
index 1d06a5a..ba6ce35 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/MethodExampleResource.java
@@ -62,21 +62,23 @@ public class MethodExampleResource extends Resource {
String method = req.getMethod();
// Attributes (from URL pattern variables)
- String a1 = req.getPathParameter("a1", String.class);
- int a2 = req.getPathParameter("a2", int.class);
- UUID a3 = req.getPathParameter("a3", UUID.class);
+ RequestPathParams path = req.getPathParams();
+ String a1 = path.get("a1", String.class);
+ int a2 = path.get("a2", int.class);
+ UUID a3 = path.get("a3", UUID.class);
// Optional GET parameters
- int p1 = req.getQueryParameter("p1", 0, int.class);
- String p2 = req.getQueryParameter("p2", String.class);
- UUID p3 = req.getQueryParameter("p3", UUID.class);
+ RequestQuery query = req.getQuery();
+ int p1 = query.get("p1", 0, int.class);
+ String p2 = query.get("p2", String.class);
+ UUID p3 = query.get("p3", UUID.class);
// URL pattern post-match
String remainder = req.getPathRemainder();
// Headers
String lang = req.getHeader("Accept-Language");
- int doNotTrack = req.getHeader("DNT", int.class);
+ int doNotTrack = req.getHeaders().get("DNT", int.class);
// Send back a simple String response
String output = String.format(
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
index ab9ef57..122d949 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
@@ -209,7 +209,7 @@ public class AddressBookResource extends ResourceJena {
String pathRemainder = req.getPathRemainder();
PojoRest r = new PojoRest(p);
ClassMeta<?> cm = r.getClassMeta(pathRemainder);
- Object in = req.getBody(cm);
+ Object in = req.getBody().asType(cm);
r.put(pathRemainder, in);
return "PUT successful";
} catch (Exception e) {
@@ -230,7 +230,7 @@ public class AddressBookResource extends ResourceJena {
String pathInfo = req.getPathInfo();
PojoRest r = new PojoRest(a);
ClassMeta<?> cm = r.getClassMeta(pathInfo);
- Object in = req.getBody(cm);
+ Object in = req.getBody().asType(cm);
r.put(pathInfo, in);
return "PUT successful";
} catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/CallbackStringsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/CallbackStringsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/CallbackStringsResource.java
index 8b873af..74038b3 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/CallbackStringsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/CallbackStringsResource.java
@@ -32,7 +32,7 @@ public class CallbackStringsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/")
public ObjectMap test1(RestRequest req) throws Exception {
- return new ObjectMap().append("method","GET").append("headers", getFooHeaders(req)).append("content", req.getBodyAsString());
+ return new ObjectMap().append("method","GET").append("headers", getFooHeaders(req)).append("content", req.getBody().asString());
}
//====================================================================================================
@@ -40,14 +40,14 @@ public class CallbackStringsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="PUT", path="/")
public ObjectMap testCharsetOnResponse(RestRequest req) throws Exception {
- return new ObjectMap().append("method","PUT").append("headers", getFooHeaders(req)).append("content", req.getBodyAsString());
+ return new ObjectMap().append("method","PUT").append("headers", getFooHeaders(req)).append("content", req.getBody().asString());
}
private Map<String,Object> getFooHeaders(RestRequest req) {
Map<String,Object> m = new TreeMap<String,Object>();
- for (Map.Entry<String,Object> e : req.getHeaders().entrySet())
+ for (Map.Entry<String,String[]> e : req.getHeaders().entrySet())
if (e.getKey().startsWith("Foo-"))
- m.put(e.getKey(), e.getValue());
+ m.put(e.getKey(), e.getValue()[0]);
return m;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
index 46e20a6..9479ab4 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OnPreCallResource.java
@@ -85,6 +85,6 @@ public class OnPreCallResource extends RestServlet {
public String testPropertiesOverriddenProgrammatically(RestRequest req, @Properties ObjectMap properties) throws Exception {
properties.put("p3", "pp3");
properties.put("p4", "pp4");
- return req.getBody(String.class);
+ return req.getBody().asType(String.class);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
index 6bfef72..c5dfce1 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/OverlappingMethodsResource.java
@@ -45,14 +45,14 @@ public class OverlappingMethodsResource extends RestServletDefault {
public static class Test1Guard extends RestGuard {
@Override /* RestGuard */
public boolean isRequestAllowed(RestRequest req) {
- return req.getQueryParameter("t1","").equals("1");
+ return req.getQuery("t1","").equals("1");
}
}
public static class Test2Guard extends RestGuard {
@Override /* RestGuard */
public boolean isRequestAllowed(RestRequest req) {
- return req.getQueryParameter("t2","").equals("2");
+ return req.getQuery("t2","").equals("2");
}
}
@@ -77,14 +77,14 @@ public class OverlappingMethodsResource extends RestServletDefault {
public static class Test3aMatcher extends RestMatcher {
@Override /* RestMatcher */
public boolean matches(RestRequest req) {
- return req.getQueryParameter("t1","").equals("1");
+ return req.getQuery("t1","").equals("1");
}
}
public static class Test3bMatcher extends RestMatcher {
@Override /* RestMatcher */
public boolean matches(RestRequest req) {
- return req.getQueryParameter("t2","").equals("2");
+ return req.getQuery("t2","").equals("2");
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index d9424e7..0c29593 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -112,7 +112,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/testParamGet/*")
public String testParamGet(RestRequest req, @Query("p1") String p1, @Query("p2") int p2) throws Exception {
- return "p1=["+p1+","+req.getQueryParameter("p1")+","+req.getQueryParameter("p1", String.class)+"],p2=["+p2+","+req.getQueryParameter("p2")+","+req.getQueryParameter("p2", int.class)+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
}
//====================================================================================================
@@ -120,7 +121,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/testParamPost/*")
public String testParamPost(RestRequest req, @FormData("p1") String p1, @FormData("p2") int p2) throws Exception {
- return "p1=["+p1+","+req.getFormDataParameter("p1")+","+req.getFormDataParameter("p1", String.class)+"],p2=["+p2+","+req.getFormDataParameter("p2")+","+req.getFormDataParameter("p2", int.class)+"]";
+ RequestFormData f = req.getFormData();
+ return "p1=["+p1+","+req.getFormData("p1")+","+f.get("p1", String.class)+"],p2=["+p2+","+req.getFormData("p2")+","+f.get("p2", int.class)+"]";
}
//====================================================================================================
@@ -128,7 +130,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/testQParamGet/*")
public String testQParamGet(RestRequest req, @Query("p1") String p1, @Query("p2") int p2) throws Exception {
- return "p1=["+p1+","+req.getQueryParameter("p1")+","+req.getQueryParameter("p1", String.class)+"],p2=["+p2+","+req.getQueryParameter("p2")+","+req.getQueryParameter("p2", int.class)+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
}
//====================================================================================================
@@ -136,7 +139,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/testQParamPost/*")
public String testQParamPost(RestRequest req, @Query("p1") String p1, @Query("p2") int p2) throws Exception {
- return "p1=["+p1+","+req.getQueryParameter("p1")+","+req.getQueryParameter("p1", String.class)+"],p2=["+p2+","+req.getQueryParameter("p2")+","+req.getQueryParameter("p2", int.class)+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"],p2=["+p2+","+q.getFirst("p2")+","+q.get("p2", int.class)+"]";
}
//====================================================================================================
@@ -144,7 +148,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/testPlainParamGet/*")
public String testPlainParamGet(RestRequest req, @Query(value="p1",format="PLAIN") String p1) throws Exception {
- return "p1=["+p1+","+req.getQueryParameter("p1")+","+req.getQueryParameter("p1", String.class)+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"]";
}
//====================================================================================================
@@ -152,7 +157,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/testPlainParamPost/*")
public String testPlainParamPost(RestRequest req, @FormData(value="p1",format="PLAIN") String p1) throws Exception {
- return "p1=["+p1+","+req.getFormDataParameter("p1")+","+req.getFormDataParameter("p1", String.class)+"]";
+ RequestFormData f = req.getFormData();
+ return "p1=["+p1+","+req.getFormData("p1")+","+f.get("p1", String.class)+"]";
}
//====================================================================================================
@@ -160,7 +166,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/testPlainQParamGet/*")
public String testPlainQParamGet(RestRequest req, @Query(value="p1",format="PLAIN") String p1) throws Exception {
- return "p1=["+p1+","+req.getQueryParameter("p1")+","+req.getQueryParameter("p1", String.class)+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"]";
}
//====================================================================================================
@@ -168,7 +175,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/testPlainQParamPost/*")
public String testPlainQParamPost(RestRequest req, @Query(value="p1",format="PLAIN") String p1) throws Exception {
- return "p1=["+p1+","+req.getQueryParameter("p1")+","+req.getQueryParameter("p1", String.class)+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+req.getQuery("p1")+","+q.get("p1", String.class)+"]";
}
//====================================================================================================
@@ -176,7 +184,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/testHasParamGet/*")
public String testHasParamGet(RestRequest req, @HasQuery("p1") boolean p1, @HasQuery("p2") Boolean p2) throws Exception {
- return "p1=["+p1+","+req.hasQueryParameter("p1")+"],p2=["+p2+","+req.hasQueryParameter("p2")+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+q.containsKey("p1")+"],p2=["+p2+","+q.containsKey("p2")+"]";
}
//====================================================================================================
@@ -184,7 +193,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/testHasParamPost/*")
public String testHasParamPost(RestRequest req, @HasFormData("p1") boolean p1, @HasFormData("p2") Boolean p2) throws Exception {
- return "p1=["+p1+","+req.hasFormDataParameter("p1")+"],p2=["+p2+","+req.hasFormDataParameter("p2")+"]";
+ RequestFormData f = req.getFormData();
+ return "p1=["+p1+","+f.containsKey("p1")+"],p2=["+p2+","+f.containsKey("p2")+"]";
}
//====================================================================================================
@@ -192,7 +202,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/testHasQParamGet/*")
public String testHasQParamGet(RestRequest req, @HasQuery("p1") boolean p1, @HasQuery("p2") Boolean p2) throws Exception {
- return "p1=["+p1+","+req.hasQueryParameter("p1")+"],p2=["+p2+","+req.hasQueryParameter("p2")+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+q.containsKey("p1")+"],p2=["+p2+","+q.containsKey("p2")+"]";
}
//====================================================================================================
@@ -200,7 +211,8 @@ public class ParamsResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/testHasQParamPost/*")
public String testHasQParamPost_post(RestRequest req, @HasQuery("p1") boolean p1, @HasQuery("p2") Boolean p2) throws Exception {
- return "p1=["+p1+","+req.hasQueryParameter("p1")+"],p2=["+p2+","+req.hasQueryParameter("p2")+"]";
+ RequestQuery q = req.getQuery();
+ return "p1=["+p1+","+q.containsKey("p1")+"],p2=["+p2+","+q.containsKey("p2")+"]";
}
//====================================================================================================
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RestClient2Resource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RestClient2Resource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RestClient2Resource.java
index 671f12c..5aa04b9 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RestClient2Resource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/RestClient2Resource.java
@@ -31,6 +31,6 @@ public class RestClient2Resource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="POST", path="/")
public Reader test1(RestRequest req) throws Exception {
- return new StringReader(req.getBodyAsString());
+ return new StringReader(req.getBody().asString());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/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 6fb9a08..df39005 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
@@ -425,32 +425,32 @@ class CallMethod implements Comparable<CallMethod> {
switch(paramType) {
case REQ: return req;
case RES: return res;
- case PATH: return req.getPathParameter(name, type);
- case BODY: return req.getBody(type);
- case HEADER: return req.getHeader(name, type);
+ case PATH: return req.getPathParams().get(name, type);
+ case BODY: return req.getBody().asType(type);
+ case HEADER: return req.getHeaders().get(name, type);
case METHOD: return req.getMethod();
case FORMDATA: {
if (multiPart)
- return req.getFormDataParameters(name, type);
+ return req.getFormData().getAll(name, type);
if (plainParams)
- return session.convertToType(req.getFormDataParameter(name), session.getClassMeta(type));
- return req.getFormDataParameter(name, type);
+ return session.convertToType(req.getFormData(name), session.getClassMeta(type));
+ return req.getFormData().get(name, type);
}
case QUERY: {
if (multiPart)
- return req.getQueryParameters(name, type);
+ return req.getQuery().getAll(name, type);
if (plainParams)
- return session.convertToType(req.getQueryParameter(name), session.getClassMeta(type));
- return req.getQueryParameter(name, type);
+ return session.convertToType(req.getQuery(name), session.getClassMeta(type));
+ return req.getQuery().get(name, type);
}
- case HASFORMDATA: return session.convertToType(req.hasFormDataParameter(name), session.getClassMeta(type));
- case HASQUERY: return session.convertToType(req.hasQueryParameter(name), session.getClassMeta(type));
+ case HASFORMDATA: return session.convertToType(req.getFormData().containsKey(name), session.getClassMeta(type));
+ case HASQUERY: return session.convertToType(req.getQuery().containsKey(name), session.getClassMeta(type));
case PATHREMAINDER: return req.getPathRemainder();
case PROPS: return res.getProperties();
case MESSAGES: return req.getResourceBundle();
- case ACCEPT: return req.getAcceptHeader();
- case ACCEPTENCODING:return req.getAcceptEncodingHeader();
- case CONTENTTYPE: return req.getContentTypeHeader();
+ case ACCEPT: return req.getHeaders().getAccept();
+ case ACCEPTENCODING:return req.getHeaders().getAcceptEncoding();
+ case CONTENTTYPE: return req.getHeaders().getContentType();
default: return null;
}
}
@@ -937,9 +937,9 @@ class CallMethod implements Comparable<CallMethod> {
if ("path".equals(prefix))
return req.getPathParameter(remainder);
if ("query".equals(prefix))
- return req.getQueryParameter(remainder);
+ return req.getQuery(remainder);
if ("formData".equals(prefix))
- return req.getFormDataParameter(remainder);
+ return req.getFormData(remainder);
if ("header".equals(prefix))
return req.getHeader(remainder);
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
new file mode 100644
index 0000000..1b509b4
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestBody.java
@@ -0,0 +1,431 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import javax.servlet.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Contains the body of the HTTP request.
+ */
+@SuppressWarnings("unchecked")
+public class RequestBody {
+
+ private byte[] body;
+ private final RestRequest req;
+ private EncoderGroup encoders;
+ private Encoder encoder;
+ private ParserGroup parsers;
+ private UrlEncodingParser urlEncodingParser;
+ private RequestHeaders headers;
+ private BeanSession beanSession;
+ private int contentLength = 0;
+
+ RequestBody(RestRequest req) {
+ this.req = req;
+ }
+
+ RequestBody setEncoders(EncoderGroup encoders) {
+ this.encoders = encoders;
+ return this;
+ }
+
+ RequestBody setParsers(ParserGroup parsers) {
+ this.parsers = parsers;
+ return this;
+ }
+
+ RequestBody setHeaders(RequestHeaders headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ RequestBody setUrlEncodingParser(UrlEncodingParser urlEncodingParser) {
+ this.urlEncodingParser = urlEncodingParser;
+ return this;
+ }
+
+ RequestBody setBeanSession(BeanSession beanSession) {
+ this.beanSession = beanSession;
+ return this;
+ }
+
+ @SuppressWarnings("hiding")
+ RequestBody load(byte[] body) {
+ this.body = body;
+ return this;
+ }
+
+ boolean isLoaded() {
+ return body != null;
+ }
+
+ /**
+ * Reads the input from the HTTP request as JSON, XML, or HTML and converts the input to a POJO.
+ * <p>
+ * If {@code allowHeaderParams} init parameter is <jk>true</jk>, then first looks for {@code &body=xxx} in the URL
+ * query string.
+ * <p>
+ * If type is <jk>null</jk> or <code>Object.<jk>class</jk></code>, then the actual type will be determined
+ * automatically based on the following input:
+ * <table class='styled'>
+ * <tr><th>Type</th><th>JSON input</th><th>XML input</th><th>Return type</th></tr>
+ * <tr>
+ * <td>object</td>
+ * <td><js>"{...}"</js></td>
+ * <td><code><xt><object></xt>...<xt></object></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'object'</xs><xt>></xt>...<xt></x></xt></code></td>
+ * <td>{@link ObjectMap}</td>
+ * </tr>
+ * <tr>
+ * <td>array</td>
+ * <td><js>"[...]"</js></td>
+ * <td><code><xt><array></xt>...<xt></array></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'array'</xs><xt>></xt>...<xt></x></xt></code></td>
+ * <td>{@link ObjectList}</td>
+ * </tr>
+ * <tr>
+ * <td>string</td>
+ * <td><js>"'...'"</js></td>
+ * <td><code><xt><string></xt>...<xt></string></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'string'</xs><xt>></xt>...<xt></x></xt></code></td>
+ * <td>{@link String}</td>
+ * </tr>
+ * <tr>
+ * <td>number</td>
+ * <td><code>123</code></td>
+ * <td><code><xt><number></xt>123<xt></number></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'number'</xs><xt>></xt>...<xt></x></xt></code></td>
+ * <td>{@link Number}</td>
+ * </tr>
+ * <tr>
+ * <td>boolean</td>
+ * <td><jk>true</jk></td>
+ * <td><code><xt><boolean></xt>true<xt></boolean></xt></code><br><code><xt><x</xt> <xa>type</xa>=<xs>'boolean'</xs><xt>></xt>...<xt></x></xt></code></td>
+ * <td>{@link Boolean}</td>
+ * </tr>
+ * <tr>
+ * <td>null</td>
+ * <td><jk>null</jk> or blank</td>
+ * <td><code><xt><null/></xt></code> or blank<br><code><xt><x</xt> <xa>type</xa>=<xs>'null'</xs><xt>/></xt></code></td>
+ * <td><jk>null</jk></td>
+ * </tr>
+ * </table>
+ * <p>
+ * Refer to <a class="doclink" href="../../../../overview-summary.html#Core.PojoCategories">POJO Categories</a> for
+ * a complete definition of supported POJOs.
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> body = req.getBody(<jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse into an int array.</jc>
+ * <jk>int</jk>[] body = req.getBody(<jk>int</jk>[].<jk>class</jk>);
+
+ * <jc>// Parse into a bean.</jc>
+ * MyBean body = req.getBody(MyBean.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of objects.</jc>
+ * List body = req.getBody(LinkedList.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of object keys/values.</jc>
+ * Map body = req.getBody(TreeMap.<jk>class</jk>);
+ * </p>
+ *
+ * @param type The class type to instantiate.
+ * @param <T> The class type to instantiate.
+ * @return The input parsed to a POJO.
+ * @throws IOException If a problem occurred trying to read from the reader.
+ * @throws ParseException If the input contains a syntax error or is malformed for the requested {@code Accept}
+ * header or is not valid for the specified type.
+ */
+ public <T> T asType(Class<T> type) throws IOException, ParseException {
+ return parse(beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Reads the input from the HTTP request as JSON, XML, or HTML and converts the input to a POJO.
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into a linked-list of strings.</jc>
+ * List<String> body = req.getBody(LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
+ * List<List<String>> body = req.getBody(LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of string keys/values.</jc>
+ * Map<String,String> body = req.getBody(TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
+ * Map<String,List<MyBean>> body = req.getBody(TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
+ * {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
+ * {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @param <T> The class type to instantiate.
+ * @return The input parsed to a POJO.
+ */
+ public <T> T asType(Type type, Type...args) {
+ return (T)parse(beanSession.getClassMeta(type, args));
+ }
+
+ /**
+ * Returns the HTTP body content as a plain string.
+ * <p>
+ * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query
+ * string.
+ *
+ * @return The incoming input from the connection as a plain string.
+ * @throws IOException If a problem occurred trying to read from the reader.
+ */
+ public String asString() throws IOException {
+ if (body == null)
+ body = IOUtils.readBytes(getInputStream(), 1024);
+ return new String(body, IOUtils.UTF8);
+ }
+
+ /**
+ * Returns the HTTP body content as a simple hexadecimal character string.
+ *
+ * @return The incoming input from the connection as a plain string.
+ * @throws IOException If a problem occurred trying to read from the reader.
+ */
+ public String asHex() throws IOException {
+ if (body == null)
+ body = IOUtils.readBytes(getInputStream(), 1024);
+ return StringUtils.toHex(body);
+ }
+
+ /**
+ * Returns the HTTP body content as a {@link Reader}.
+ * <p>
+ * If {@code allowHeaderParams} init parameter is true, then first looks for {@code &body=xxx} in the URL query
+ * string.
+ * <p>
+ * Automatically handles GZipped input streams.
+ * @return The body contents as a reader.
+ * @throws IOException
+ */
+ public BufferedReader getReader() throws IOException {
+ Reader r = getUnbufferedReader();
+ if (r instanceof BufferedReader)
+ return (BufferedReader)r;
+ int len = req.getContentLength();
+ int buffSize = len <= 0 ? 8192 : Math.max(len, 8192);
+ return new BufferedReader(r, buffSize);
+ }
+
+ /**
+ * Same as {@link #getReader()}, but doesn't encapsulate the result in a {@link BufferedReader};
+ *
+ * @return An unbuffered reader.
+ * @throws IOException
+ */
+ protected Reader getUnbufferedReader() throws IOException {
+ if (body != null)
+ return new CharSequenceReader(new String(body, IOUtils.UTF8));
+ return new InputStreamReader(getInputStream(), req.getCharacterEncoding());
+ }
+
+ /**
+ * Returns the HTTP body content as an {@link InputStream}.
+ * <p>
+ * Automatically handles GZipped input streams.
+ *
+ * @return The negotiated input stream.
+ * @throws IOException If any error occurred while trying to get the input stream or wrap it
+ * in the GZIP wrapper.
+ */
+ public ServletInputStream getInputStream() throws IOException {
+
+ if (body != null)
+ return new ServletInputStream2(body);
+
+ Encoder enc = getEncoder();
+
+ ServletInputStream is = req.getRawInputStream();
+ if (enc != null) {
+ final InputStream is2 = enc.getInputStream(is);
+ return new ServletInputStream2(is2);
+ }
+ return is;
+ }
+
+ /**
+ * Returns the parser and media type matching the request <code>Content-Type</code> header.
+ *
+ * @return The parser matching the request <code>Content-Type</code> header, or <jk>null</jk>
+ * if no matching parser was found.
+ * Includes the matching media type.
+ */
+ public ParserMatch getParserMatch() {
+ MediaType mediaType = headers.getContentType();
+ if (mediaType == null) {
+ if (body != null)
+ mediaType = MediaType.UON;
+ else
+ mediaType = MediaType.JSON;
+ }
+ ParserMatch pm = parsers.getParserMatch(mediaType);
+
+ // If no patching parser for URL-encoding, use the one defined on the servlet.
+ if (pm == null && mediaType.equals(MediaType.URLENCODING))
+ pm = new ParserMatch(MediaType.URLENCODING, urlEncodingParser);
+
+ return pm;
+ }
+
+ /**
+ * Returns the parser matching the request <code>Content-Type</code> header.
+ *
+ * @return The parser matching the request <code>Content-Type</code> header, or <jk>null</jk>
+ * if no matching parser was found.
+ */
+ public Parser getParser() {
+ ParserMatch pm = getParserMatch();
+ return (pm == null ? null : pm.getParser());
+ }
+
+ /**
+ * Returns the reader parser matching the request <code>Content-Type</code> header.
+ *
+ * @return The reader parser matching the request <code>Content-Type</code> header, or <jk>null</jk>
+ * if no matching reader parser was found, or the matching parser was an input stream parser.
+ */
+ public ReaderParser getReaderParser() {
+ Parser p = getParser();
+ if (p != null && p.isReaderParser())
+ return (ReaderParser)p;
+ return null;
+ }
+
+ /* Workhorse method */
+ private <T> T parse(ClassMeta<T> cm) throws RestException {
+
+ try {
+ if (cm.isReader())
+ return (T)getReader();
+
+ if (cm.isInputStream())
+ return (T)getInputStream();
+
+ TimeZone timeZone = headers.getTimeZone();
+ Locale locale = req.getLocale();
+ ParserMatch pm = getParserMatch();
+
+ if (pm != null) {
+ Parser p = pm.getParser();
+ MediaType mediaType = pm.getMediaType();
+ try {
+ req.getProperties().append("mediaType", mediaType).append("characterEncoding", req.getCharacterEncoding());
+ if (! p.isReaderParser()) {
+ InputStreamParser p2 = (InputStreamParser)p;
+ ParserSession session = p2.createSession(getInputStream(), req.getProperties(), req.getJavaMethod(), req.getContext().getResource(), locale, timeZone, mediaType);
+ return p2.parseSession(session, cm);
+ }
+ ReaderParser p2 = (ReaderParser)p;
+ ParserSession session = p2.createSession(getUnbufferedReader(), req.getProperties(), req.getJavaMethod(), req.getContext().getResource(), locale, timeZone, mediaType);
+ return p2.parseSession(session, cm);
+ } catch (ParseException e) {
+ throw new RestException(SC_BAD_REQUEST,
+ "Could not convert request body content to class type ''{0}'' using parser ''{1}''.",
+ cm, p.getClass().getName()
+ ).initCause(e);
+ }
+ }
+
+ throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
+ "Unsupported media-type in request header ''Content-Type'': ''{0}''\n\tSupported media-types: {1}",
+ headers.getContentType(), req.getParserGroup().getSupportedMediaTypes()
+ );
+
+ } catch (IOException e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR,
+ "I/O exception occurred while attempting to handle request ''{0}''.",
+ req.getDescription()
+ ).initCause(e);
+ }
+ }
+
+ private Encoder getEncoder() {
+ if (encoder == null) {
+ String ce = req.getHeader("content-encoding");
+ if (! (ce == null || ce.isEmpty())) {
+ ce = ce.trim();
+ encoder = encoders.getEncoder(ce);
+ if (encoder == null)
+ throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
+ "Unsupported encoding in request header ''Content-Encoding'': ''{0}''\n\tSupported codings: {1}",
+ req.getHeader("content-encoding"), encoders.getSupportedEncodings()
+ );
+ }
+
+ if (encoder != null)
+ contentLength = -1;
+ }
+ // Note that if this is the identity encoder, we want to return null
+ // so that we don't needlessly wrap the input stream.
+ if (encoder == IdentityEncoder.INSTANCE)
+ return null;
+ return encoder;
+ }
+
+ /**
+ * Returns the content length of the body.
+ * @return The content length of the body in bytes.
+ */
+ public int getContentLength() {
+ return contentLength == 0 ? req.getRawContentLength() : contentLength;
+ }
+
+ /**
+ * ServletInputStream wrapper around a normal input stream.
+ */
+ private static class ServletInputStream2 extends ServletInputStream {
+
+ private final InputStream is;
+
+ private ServletInputStream2(InputStream is) {
+ this.is = is;
+ }
+
+ private ServletInputStream2(byte[] b) {
+ this(new ByteArrayInputStream(b));
+ }
+
+ @Override /* InputStream */
+ public final int read() throws IOException {
+ return is.read();
+ }
+
+ @Override /* InputStream */
+ public final void close() throws IOException {
+ is.close();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/321f6bde/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
new file mode 100644
index 0000000..12d2157
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RequestFormData.java
@@ -0,0 +1,271 @@
+// ***************************************************************************************************************************
+// * 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;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents the parsed form data parameters in an HTTP request.
+ */
+@SuppressWarnings("unchecked")
+public class RequestFormData extends LinkedHashMap<String,String[]> {
+ private static final long serialVersionUID = 1L;
+
+ private UrlEncodingParser parser;
+ private BeanSession beanSession;
+
+ RequestFormData setParser(UrlEncodingParser parser) {
+ this.parser = parser;
+ return this;
+ }
+
+ RequestFormData setBeanSession(BeanSession beanSession) {
+ this.beanSession = beanSession;
+ return this;
+ }
+
+ /**
+ * Sets a request form data parameter value.
+ *
+ * @param name The parameter name.
+ * @param value The parameter value.
+ */
+ public void put(String name, Object value) {
+ super.put(name, new String[]{StringUtils.toString(value)});
+ }
+
+ /**
+ * Returns a form data parameter value.
+ * <p>
+ * Parameter lookup is case-insensitive (consistent with WAS, but differs from Tomcat).
+ * <p>
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ * <li>Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the underlying servlet API.
+ * <li>This method returns the raw unparsed value, and differs from calling <code>getFormDataParameter(name, String.<jk>class</js>)</code>
+ * which will convert the value from UON notation:
+ * <ul>
+ * <li><js>"null"</js> => <jk>null</jk>
+ * <li><js>"'null'"</js> => <js>"null"</js>
+ * <li><js>"'foo bar'"</js> => <js>"foo bar"</js>
+ * <li><js>"foo~~bar"</js> => <js>"foo~bar"</js>
+ * </ul>
+ * </ul>
+ *
+ * @param name The form data parameter name.
+ * @return The parameter value, or <jk>null</jk> if parameter does not exist.
+ */
+ public String getFirst(String name) {
+ String[] v = get(name);
+ if (v == null || v.length == 0)
+ return null;
+ if (v.length == 1 && v[0] != null && v[0].isEmpty()) {
+ // Fix for behavior difference between Tomcat and WAS.
+ // getParameter("foo") on "&foo" in Tomcat returns "".
+ // getParameter("foo") on "&foo" in WAS returns null.
+ if (containsKey(name))
+ return null;
+ }
+ return v[0];
+ }
+
+ /**
+ * Same as {@link #getFirst(String)} except returns a default value if <jk>null</jk> or empty.
+ *
+ * @param name The form data parameter name.
+ * @param def The default value.
+ * @return The parameter value, or the default value if <jk>null</jk> or empty.
+ */
+ public String getFirst(String name, String def) {
+ String val = getFirst(name);
+ if (val == null || val.isEmpty())
+ return def;
+ return val;
+ }
+
+ /**
+ * Returns the specified form data parameter value converted to a POJO using the
+ * {@link UrlEncodingParser} registered with this servlet.
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into an integer.</jc>
+ * <jk>int</jk> myparam = req.getFormDataParameter(<js>"myparam"</js>, <jk>int</jk>.<jk>class</jk>);
+ *
+ * <jc>// Parse into an int array.</jc>
+ * <jk>int</jk>[] myparam = req.getFormDataParameter(<js>"myparam"</js>, <jk>int</jk>[].<jk>class</jk>);
+
+ * <jc>// Parse into a bean.</jc>
+ * MyBean myparam = req.getFormDataParameter(<js>"myparam"</js>, MyBean.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of objects.</jc>
+ * List myparam = req.getFormDataParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of object keys/values.</jc>
+ * Map myparam = req.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>);
+ * </p>
+ * <p>
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ * <li>Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the underlying servlet API.
+ * </ul>
+ *
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Class<T> type) throws ParseException {
+ return parse(name, beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} except returns a default value if not specified.
+ *
+ * @param name The parameter name.
+ * @param def The default value if the parameter was not specified or is <jk>null</jk>.
+ * @param type The class type to convert the parameter value to.
+ * @param <T> The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, T def, Class<T> type) throws ParseException {
+ return parse(name, def, beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Same as {@link #get(String, Class)} except for use on multi-part parameters
+ * (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=(1,2,3)"</js>)
+ * <p>
+ * This method must only be called when parsing into classes of type Collection or array.
+ *
+ * @param name The parameter name.
+ * @param type The class type to convert the parameter value to.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T getAll(String name, Class<T> type) throws ParseException {
+ return parseAll(name, beanSession.getClassMeta(type));
+ }
+
+ /**
+ * Returns the specified form data parameter value converted to a POJO using the
+ * {@link UrlEncodingParser} registered with this servlet.
+ * <p>
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ * <li>Calling this method on URL-Encoded FORM posts causes the body content to be loaded and parsed by the underlying servlet API.
+ * <li>Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
+ * </ul>
+ * <p>
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into a linked-list of strings.</jc>
+ * List<String> myparam = req.getFormDataParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a linked-list of linked-lists of strings.</jc>
+ * List<List<String>> myparam = req.getFormDataParameter(<js>"myparam"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map of string keys/values.</jc>
+ * Map<String,String> myparam = req.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+ *
+ * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
+ * Map<String,List<MyBean>> myparam = req.getFormDataParameter(<js>"myparam"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * @param name The parameter name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T get(String name, Type type, Type...args) throws ParseException {
+ return (T)parse(name, beanSession.getClassMeta(type, args));
+ }
+
+ /**
+ * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters
+ * (e.g. <js>"key=1&key=2&key=3"</js> instead of <js>"key=(1,2,3)"</js>)
+ * <p>
+ * This method must only be called when parsing into classes of type Collection or array.
+ *
+ * @param name The parameter name.
+ * @param type The type of object to create.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * @param args The type arguments of the class if it's a collection or map.
+ * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+ * <br>Ignored if the main type is not a map or collection.
+ * @return The parameter value converted to the specified class type.
+ * @throws ParseException
+ */
+ public <T> T getAll(String name, Type type, Type...args) throws ParseException {
+ return (T)parseAll(name, beanSession.getClassMeta(type, args));
+ }
+
+ /* Workhorse method */
+ <T> T parse(String name, T def, ClassMeta<T> cm) throws ParseException {
+ String val = getFirst(name);
+ if (val == null)
+ return def;
+ return parseValue(val, cm);
+ }
+
+ /* Workhorse method */
+ <T> T parse(String name, ClassMeta<T> cm) throws ParseException {
+ String val = getFirst(name);
+ if (cm.isPrimitive() && (val == null || val.isEmpty()))
+ return cm.getPrimitiveDefault();
+ return parseValue(val, cm);
+ }
+
+ /* Workhorse method */
+ @SuppressWarnings("rawtypes")
+ <T> T parseAll(String name, ClassMeta<T> cm) throws ParseException {
+ String[] p = get(name);
+ if (p == null)
+ return null;
+ if (cm.isArray()) {
+ List c = new ArrayList();
+ for (int i = 0; i < p.length; i++)
+ c.add(parseValue(p[i], cm.getElementType()));
+ return (T)ArrayUtils.toArray(c, cm.getElementType().getInnerClass());
+ } else if (cm.isCollection()) {
+ try {
+ Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList());
+ for (int i = 0; i < p.length; i++)
+ c.add(parseValue(p[i], cm.getElementType()));
+ return (T)c;
+ } catch (ParseException e) {
+ throw e;
+ } catch (Exception e) {
+ // Typically an instantiation exception.
+ throw new ParseException(e);
+ }
+ }
+ throw new ParseException("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array.");
+ }
+
+ private <T> T parseValue(String val, ClassMeta<T> c) throws ParseException {
+ return parser.parsePart(val, c);
+ }
+}