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/09/08 23:25:37 UTC
svn commit: r21540 [11/27] - in /release/incubator/juneau:
juneau-rest-client/ juneau-rest-client/.settings/ juneau-rest-client/bin/
juneau-rest-client/src/ juneau-rest-client/src/main/
juneau-rest-client/src/main/java/ juneau-rest-client/src/main/java...
Added: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
==============================================================================
--- release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java (added)
+++ release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java Fri Sep 8 23:25:34 2017
@@ -0,0 +1,372 @@
+// ***************************************************************************************************************************
+// * 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.internal.ArrayUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+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;
+ }
+
+ /**
+ * Adds default entries to these form-data parameters.
+ *
+ * <p>
+ * This includes the default form-data parameters defined on the servlet and method levels.
+ *
+ * @param defaultEntries The default entries. Can be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestFormData 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 || v.length == 0 || StringUtils.isEmpty(v[0]))
+ put(key, new String[]{value});
+ }
+ }
+ 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).
+ *
+ * <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 getString(String name) {
+ String[] v = get(name);
+ if (v == null || v.length == 0)
+ return null;
+
+ // Fix for behavior difference between Tomcat and WAS.
+ // getParameter("foo") on "&foo" in Tomcat returns "".
+ // getParameter("foo") on "&foo" in WAS returns null.
+ if (v.length == 1 && v[0] == null)
+ return "";
+
+ return v[0];
+ }
+
+ /**
+ * Same as {@link #getString(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 parameter does not exist or is <jk>null</jk> or empty.
+ */
+ public String getString(String name, String def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : s;
+ }
+
+ /**
+ * Same as {@link #getString(String)} but converts the value to an integer.
+ *
+ * @param name The form data parameter name.
+ * @return The parameter value, or <code>0</code> if parameter does not exist or is <jk>null</jk> or empty.
+ */
+ public int getInt(String name) {
+ return getInt(name, 0);
+ }
+
+ /**
+ * Same as {@link #getString(String,String)} but converts the value to an integer.
+ *
+ * @param name The form data parameter name.
+ * @param def The default value.
+ * @return The parameter value, or the default value if parameter does not exist or is <jk>null</jk> or empty.
+ */
+ public int getInt(String name, int def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+ }
+
+ /**
+ * Same as {@link #getString(String)} but converts the value to a boolean.
+ *
+ * @param name The form data parameter name.
+ * @return The parameter value, or <jk>false</jk> if parameter does not exist or is <jk>null</jk> or empty.
+ */
+ public boolean getBoolean(String name) {
+ return getBoolean(name, false);
+ }
+
+ /**
+ * Same as {@link #getString(String,String)} but converts the value to a boolean.
+ *
+ * @param name The form data parameter name.
+ * @param def The default value.
+ * @return The parameter value, or the default value if parameter does not exist or is <jk>null</jk> or empty.
+ */
+ public boolean getBoolean(String name, boolean def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+ }
+
+ /**
+ * Returns the specified form data parameter value converted to a POJO using the {@link UrlEncodingParser}
+ * registered with this servlet.
+ *
+ * <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>
+ *
+ * <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.
+ *
+ * <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>
+ *
+ * <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 = getString(name);
+ if (val == null)
+ return def;
+ return parseValue(val, cm);
+ }
+
+ /* Workhorse method */
+ <T> T parse(String name, ClassMeta<T> cm) throws ParseException {
+ String val = getString(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)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.parse(PartType.FORM_DATA, val, c);
+ }
+
+ /**
+ * Converts the form data parameters to a readable string.
+ *
+ * @param sorted Sort the form data parameters by name.
+ * @return A JSON string containing the contents of the form data parameters.
+ */
+ public String toString(boolean sorted) {
+ Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : new LinkedHashMap<String,Object>());
+ for (Map.Entry<String,String[]> e : this.entrySet()) {
+ String[] v = e.getValue();
+ m.put(e.getKey(), v.length == 1 ? v[0] : v);
+ }
+ return JsonSerializer.DEFAULT_LAX.toString(m);
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+}
Propchange: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestFormData.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
==============================================================================
--- release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java (added)
+++ release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java Fri Sep 8 23:25:34 2017
@@ -0,0 +1,808 @@
+// ***************************************************************************************************************************
+// * 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.internal.ArrayUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+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.json.*;
+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 || v.length == 0 || StringUtils.isEmpty(v[0]))
+ 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();
+ String[] s = new String[]{v};
+ while (values.hasMoreElements())
+ s = 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.
+ *
+ * @param name The header name.
+ * @return The header value, or <jk>null</jk> if it doesn't exist.
+ */
+ public String getString(String name) {
+ 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 null;
+ return v[0];
+ }
+
+ /**
+ * 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 getString(String name, String def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : s;
+ }
+
+ /**
+ * Same as {@link #getString(String)} but converts the value to an integer.
+ *
+ * @param name The HTTP header name.
+ * @return The header value, or the default value if the header isn't present.
+ */
+ public int getInt(String name) {
+ return getInt(name, 0);
+ }
+
+ /**
+ * Same as {@link #getString(String,String)} but converts the value to an integer.
+ *
+ * @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 int getInt(String name, int def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+ }
+
+ /**
+ * Same as {@link #getString(String)} but converts the value to a boolean.
+ *
+ * @param name The HTTP header name.
+ * @return The header value, or the default value if the header isn't present.
+ */
+ public boolean getBoolean(String name) {
+ return getBoolean(name, false);
+ }
+
+ /**
+ * Same as {@link #getString(String,String)} but converts the value to a boolean.
+ *
+ * @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 boolean getBoolean(String name, boolean def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s);
+ }
+
+ /**
+ * 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 convertible from a <code>String</code>
+ * (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertible From Strings</a>).
+ *
+ * <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 = getString(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 = getString(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 convertible from a <code>String</code>
+ * (See <a class="doclink" href="package-summary.html#PojosConvertableFromString">POJOs Convertible From Strings</a>).
+ *
+ * <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.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(String name, Type type, Type...args) throws ParseException {
+ String h = getString(name);
+ return (T)parser.parse(PartType.HEADER, h, beanSession.getClassMeta(type, args));
+ }
+
+ /**
+ * Returns a copy of this object, but only with the specified header names copied.
+ *
+ * @param headers The headers to include in the copy.
+ * @return A new headers object.
+ */
+ public RequestHeaders subset(String...headers) {
+ RequestHeaders rh2 = new RequestHeaders().setParser(parser).setBeanSession(beanSession).setQueryParams(queryParams);
+ for (String h : headers)
+ if (containsKey(h))
+ rh2.put(h, get(h));
+ return rh2;
+ }
+
+ /**
+ * Same as {@link #subset(String...)}, but allows you to specify header names as a comma-delimited list.
+ *
+ * @param headers The headers to include in the copy.
+ * @return A new headers object.
+ */
+ public RequestHeaders subset(String headers) {
+ return subset(split(headers));
+ }
+
+ /**
+ * 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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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(getString("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 = getString("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(getString("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(getString("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(getString("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(getString("Warning"));
+ }
+
+ /**
+ * Converts the headers to a readable string.
+ *
+ * @param sorted Sort the headers by name.
+ * @return A JSON string containing the contents of the headers.
+ */
+ public String toString(boolean sorted) {
+ Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : new LinkedHashMap<String,Object>());
+ for (Map.Entry<String,String[]> e : this.entrySet()) {
+ String[] v = e.getValue();
+ m.put(e.getKey(), v.length == 1 ? v[0] : v);
+ }
+ return JsonSerializer.DEFAULT_LAX.toString(m);
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+}
Propchange: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestHeaders.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
==============================================================================
--- release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java (added)
+++ release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java Fri Sep 8 23:25:34 2017
@@ -0,0 +1,222 @@
+// ***************************************************************************************************************************
+// * 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.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Contains information about the matched path on the HTTP request.
+ *
+ * <p>
+ * Provides access to the matched path variables and path match remainder.
+ */
+@SuppressWarnings("unchecked")
+public class RequestPathMatch extends TreeMap<String,String> {
+ private static final long serialVersionUID = 1L;
+
+ private UrlEncodingParser parser;
+ private BeanSession beanSession;
+ private String remainder;
+
+ RequestPathMatch() {
+ super(String.CASE_INSENSITIVE_ORDER);
+ }
+
+ RequestPathMatch setParser(UrlEncodingParser parser) {
+ this.parser = parser;
+ return this;
+ }
+
+ RequestPathMatch setBeanSession(BeanSession beanSession) {
+ this.beanSession = beanSession;
+ return this;
+ }
+
+ RequestPathMatch setRemainder(String remainder) {
+ this.remainder = remainder;
+ 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) {
+ super.put(name, value.toString());
+ }
+
+ /**
+ * Returns the specified path parameter converted to a POJO.
+ *
+ * <p>
+ * The type can be any POJO type convertible from a <code>String</code> (See <a class="doclink"
+ * href="package-summary.html#PojosConvertibleFromString">POJOs Convertible From Strings</a>).
+ *
+ * <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 convertible from a <code>String</code> (See <a class="doclink"
+ * href="package-summary.html#PojosConvertibleFromString">POJOs Convertible From Strings</a>).
+ *
+ * <p>
+ * Use this method if you want to parse into a parameterized <code>Map</code>/<code>Collection</code> object.
+ *
+ * <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.parse(PartType.PATH, attr.toString(), cm);
+ if (t == null && cm.isPrimitive())
+ return cm.getPrimitiveDefault();
+ return t;
+ }
+
+ /**
+ * Returns the decoded remainder of the URL following any path pattern matches.
+ *
+ * <p>
+ * The behavior of path remainder is shown below given the path pattern "/foo/*":
+ * <table class='styled'>
+ * <tr>
+ * <th>URL</th>
+ * <th>Path Remainder</th>
+ * </tr>
+ * <tr>
+ * <td><code>/foo</code></td>
+ * <td><jk>null</jk></td>
+ * </tr>
+ * <tr>
+ * <td><code>/foo/</code></td>
+ * <td><js>""</js></td>
+ * </tr>
+ * <tr>
+ * <td><code>/foo//</code></td>
+ * <td><js>"/"</js></td>
+ * </tr>
+ * <tr>
+ * <td><code>/foo///</code></td>
+ * <td><js>"//"</js></td>
+ * </tr>
+ * <tr>
+ * <td><code>/foo/a/b</code></td>
+ * <td><js>"a/b"</js></td>
+ * </tr>
+ * <tr>
+ * <td><code>/foo//a/b/</code></td>
+ * <td><js>"/a/b/"</js></td>
+ * </tr>
+ * <tr>
+ * <td><code>/foo/a%2Fb</code></td>
+ * <td><js>"a/b"</js></td>
+ * </tr>
+ * </table>
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <jc>// REST method</jc>
+ * <ja>@RestMethod</ja>(name=<js>"GET"</js>,path=<js>"/foo/{bar}/*"</js>)
+ * <jk>public</jk> String doGetById(RequestPathParams pathParams, <jk>int</jk> bar) {
+ * <jk>return</jk> pathParams.getRemainder();
+ * }
+ *
+ * <jc>// Prints "path/remainder"</jc>
+ * <jk>new</jk> RestCall(servletPath + <js>"/foo/123/path/remainder"</js>).connect();
+ * </p>
+ *
+ * @return The path remainder string.
+ */
+ public String getRemainder() {
+ return urlDecode(remainder);
+ }
+
+ /**
+ * Same as {@link #getRemainder()} but doesn't decode characters.
+ *
+ * @return The un-decoded path remainder.
+ */
+ public String getRemainderUndecoded() {
+ return remainder;
+ }
+}
Propchange: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestPathMatch.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
==============================================================================
--- release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java (added)
+++ release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java Fri Sep 8 23:25:34 2017
@@ -0,0 +1,505 @@
+// ***************************************************************************************************************************
+// * 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.internal.ArrayUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * 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;
+ }
+
+ /**
+ * Create a copy of the request query parameters.
+ */
+ RequestQuery copy() {
+ RequestQuery rq = new RequestQuery();
+ rq.putAll(this);
+ return rq;
+ }
+
+ /**
+ * Adds default entries to these query parameters.
+ *
+ * <p>
+ * This includes the default queries defined on the servlet and method levels.
+ *
+ * @param defaultEntries The default entries. Can be <jk>null</jk>.
+ * @return This object (for method chaining).
+ */
+ public RequestQuery 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 || v.length == 0 || StringUtils.isEmpty(v[0]))
+ put(key, new String[]{value});
+ }
+ }
+ 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.
+ *
+ * <p>
+ * If multiple query parameters have the same name, this returns only the first instance.
+ *
+ * @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 getString(String name) {
+ String[] v = get(name);
+ if (v == null || v.length == 0)
+ return null;
+
+ // Fix for behavior difference between Tomcat and WAS.
+ // getParameter("foo") on "&foo" in Tomcat returns "".
+ // getParameter("foo") on "&foo" in WAS returns null.
+ if (v.length == 1 && v[0] == null)
+ return "";
+
+ return v[0];
+ }
+
+ /**
+ * Same as {@link #getString(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 getString(String name, String def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : s;
+ }
+
+ /**
+ * Same as {@link #getString(String)} but converts the value to an integer.
+ *
+ * @param name The URL parameter name.
+ * @return
+ * The parameter value, or <code>0</code> if parameter not specified or has no value
+ * (e.g. <js>"&foo"</js>.
+ */
+ public int getInt(String name) {
+ return getInt(name, 0);
+ }
+
+ /**
+ * Same as {@link #getString(String,String)} but converts the value to an integer.
+ *
+ * @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 int getInt(String name, int def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : Integer.parseInt(s);
+ }
+
+ /**
+ * Same as {@link #getString(String)} but converts the value to a boolean.
+ *
+ * @param name The URL parameter name.
+ * @return
+ * The parameter value, or <jk>false</jk> if parameter not specified or has no value
+ * (e.g. <js>"&foo"</js>.
+ */
+ public boolean getBoolean(String name) {
+ return getBoolean(name, false);
+ }
+
+ /**
+ * Same as {@link #getString(String,String)} but converts the value to a boolean.
+ *
+ * @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 boolean getBoolean(String name, boolean def) {
+ String s = getString(name);
+ return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(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.
+ *
+ * <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.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode'>
+ * <jc>// Parse into a linked-list of strings.</jc>
+ * List<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>
+ * List<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;
+ }
+
+ /**
+ * Locates the special search query arguments in the query and returns them as a {@link SearchArgs} object.
+ *
+ * <p>
+ * The query arguments are as follows:
+ * <ul>
+ * <li>
+ * <js>"&s="</js> - A comma-delimited list of column-name/search-token pairs.
+ * <br>Example: <js>"&s=column1=foo*,column2=*bar"</js>
+ * <li>
+ * <js>"&v="</js> - A comma-delimited list column names to view.
+ * <br>Example: <js>"&v=column1,column2"</js>
+ * <li>
+ * <js>"&o="</js> - A comma-delimited list column names to sort by.
+ * <br>Column names can be suffixed with <js>'-'</js> to indicate descending order.
+ * <br>Example: <js>"&o=column1,column2-"</js>
+ * <li>
+ * <js>"&p="</js> - The zero-index row number of the first row to display.
+ * <br>Example: <js>"&p=100"</js>
+ * <li>
+ * <js>"&l="</js> - The number of rows to return.
+ * <br><code>0</code> implies return all rows.
+ * <br>Example: <js>"&l=100"</js>
+ * <li>
+ * <js>"&i="</js> - The case-insensitive search flag.
+ * <br>Example: <js>"&i=true"</js>
+ * </ul>
+ *
+ * <p>
+ * Whitespace is trimmed in the parameters.
+ *
+ * @return
+ * A new {@link SearchArgs} object initialized with the special search query arguments.
+ * <jk>null</jk> if no search arguments were found.
+ */
+ public SearchArgs getSearchArgs() {
+ if (hasAny("s","v","o","p","l","i")) {
+ return new SearchArgs.Builder()
+ .search(getString("s"))
+ .view(getString("v"))
+ .sort(getString("o"))
+ .position(getInt("p"))
+ .limit(getInt("l"))
+ .ignoreCase(getBoolean("i"))
+ .build();
+ }
+ return null;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the query parameters contains any of the specified names.
+ *
+ * @param paramNames The parameter names to check for.
+ * @return <jk>true</jk> if the query parameters contains any of the specified names.
+ */
+ public boolean hasAny(String...paramNames) {
+ for (String p : paramNames)
+ if (containsKey(p))
+ return true;
+ return false;
+ }
+
+ /* Workhorse method */
+ private <T> T parse(String name, T def, ClassMeta<T> cm) throws ParseException {
+ String val = getString(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 = getString(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)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.parse(PartType.QUERY, val, c);
+ }
+
+ /**
+ * Converts the query parameters to a readable string.
+ *
+ * @param sorted Sort the query parameters by name.
+ * @return A JSON string containing the contents of the query parameters.
+ */
+ public String toString(boolean sorted) {
+ Map<String,Object> m = (sorted ? new TreeMap<String,Object>() : new LinkedHashMap<String,Object>());
+ for (Map.Entry<String,String[]> e : this.entrySet()) {
+ String[] v = e.getValue();
+ m.put(e.getKey(), v.length == 1 ? v[0] : v);
+ }
+ return JsonSerializer.DEFAULT_LAX.toString(m);
+ }
+
+ /**
+ * Converts this object to a query string.
+ *
+ * <p>
+ * Returned query string does not start with <js>'?'</js>.
+ *
+ * @return A new query string, or an empty string if this object is empty.
+ */
+ public String toQueryString() {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String,String[]> e : this.entrySet()) {
+ for (int i = 0; i < e.getValue().length; i++) {
+ if (sb.length() > 0)
+ sb.append("&");
+ sb.append(XmlUtils.urlEncode(e.getKey())).append('=').append(XmlUtils.urlEncode(e.getValue()[i]));
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+}
Propchange: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RequestQuery.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
==============================================================================
--- release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java (added)
+++ release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java Fri Sep 8 23:25:34 2017
@@ -0,0 +1,112 @@
+// ***************************************************************************************************************************
+// * 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.io.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.response.*;
+
+/**
+ * Defines the interface for handlers that convert POJOs to appropriate HTTP responses.
+ *
+ * <p>
+ * The {@link RestServlet} API uses the concept of registered response handlers for converting objects returned by REST
+ * methods or set through {@link RestResponse#setOutput(Object)} into appropriate HTTP responses.
+ *
+ * <p>
+ * Response handlers can be associated with {@link RestServlet RestServlets} through the following ways:
+ * <ul class='spaced-list'>
+ * <li>
+ * Through the {@link RestResource#responseHandlers @RestResource.responseHandlers} annotation.
+ * <li>
+ * By calling the {@link RestConfig#addResponseHandlers(Class...)} and augmenting or creating your
+ * own list of handlers.
+ * </ul>
+ *
+ * <p>
+ * By default, {@link RestServlet RestServlets} are registered with the following response handlers:
+ * <ul class='spaced-list'>
+ * <li>
+ * {@link DefaultHandler} - Serializes POJOs using the Juneau serializer API.
+ * <li>
+ * {@link ReaderHandler} - Pipes the output of {@link Reader Readers} to the response writer
+ * ({@link RestResponse#getWriter()}).
+ * <li>
+ * {@link InputStreamHandler} - Pipes the output of {@link InputStream InputStreams} to the response output
+ * stream ({@link RestResponse#getOutputStream()}).
+ * <li>
+ * {@link RedirectHandler} - Handles {@link Redirect} objects.
+ * <li>
+ * {@link WritableHandler} - Handles {@link Writable} objects.
+ * <li>
+ * {@link StreamableHandler} - Handles {@link Streamable} objects.
+ * </ul>
+ *
+ * <p>
+ * Response handlers can be used to process POJOs that cannot normally be handled through Juneau serializers, or
+ * because it's simply easier to define response handlers for special cases.
+ *
+ * <p>
+ * The following example shows how to create a response handler to handle special <code>Foo</code> objects outside the
+ * normal Juneau architecture.
+ * <p class='bcode'>
+ * <ja>@RestResource</ja>(
+ * path=<js>"/example"</js>,
+ * responseHandlers=FooHandler.<jk>class</jk>
+ * )
+ * <jk>public class</jk> Example <jk>extends</jk> RestServlet {
+ *
+ * <ja>@RestMethod</ja>(name=<js>"GET"</js>, path=<js>"/"</js>)
+ * <jk>public</jk> Foo test1() {
+ * <jk>return new</jk> Foo(<js>"123"</js>);
+ * }
+ *
+ * <jk>public static class</jk> FooHandler <jk>implements</jk> ResponseHandler {
+ * <ja>@Override</ja>
+ * <jk>public boolean</jk> handle(RestRequest req, RestResponse res, Object output) <jk>throws</jk> IOException, RestException {
+ * <jk>if</jk> (output <jk>instanceof</jk> Foo) {
+ * Foo foo = (Foo)output;
+ * <jc>// Set some headers and body content.</jc>
+ * res.setHeader(<js>"Foo-ID"</js>, foo.getId());
+ * res.getWriter().write(<js>"foo.id="</js> + foo.getId());
+ * <jk>return true</jk>; <jc>// We handled it.</jc>
+ * }
+ * <jk>return false</jk>; <jc>// We didn't handle it.</jc>
+ * }
+ * }
+ * }
+ * </p>
+ */
+public interface ResponseHandler {
+
+ /**
+ * Process this response if possible.
+ * This method should return <jk>false</jk> if it wasn't able to process the response.
+ *
+ * @param req The HTTP servlet request.
+ * @param res The HTTP servlet response;
+ * @param output The POJO returned by the REST method that now needs to be sent to the response.
+ * @return true If this handler handled the response.
+ * @throws IOException
+ * If low-level exception occurred on output stream.
+ * Results in a {@link HttpServletResponse#SC_INTERNAL_SERVER_ERROR} error.
+ * @throws RestException
+ * If some other exception occurred.
+ * Can be used to provide an appropriate HTTP response code and message.
+ */
+ boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException;
+}
Propchange: release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResponseHandler.java
------------------------------------------------------------------------------
svn:mime-type = text/plain