Added:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
==============================================================================
---
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
(added)
+++
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
Fri Sep 8 23:25:34 2017
@@ -0,0 +1,1217 @@
+//
***************************************************************************************************************************
+// * 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 java.util.Collections.*;
+import static java.util.logging.Level.*;
+import static javax.servlet.http.HttpServletResponse.*;
+import static org.apache.juneau.internal.IOUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.nio.charset.*;
+import java.text.*;
+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.encoders.*;
+import org.apache.juneau.html.*;
+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.rest.widget.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.uon.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * Represents an HTTP request for a REST resource.
+ *
+ * <p>
+ * Equivalent to {@link HttpServletRequest} except with some additional
convenience methods.
+ *
+ * <p>
+ * For reference, given the URL
<js>"http://localhost:9080/contextRoot/servletPath/foo?bar=baz#qux"</js>, the
+ * following methods return the following values....
+ * <table class='styled'>
+ * <tr><th>Method</th><th>Value</th></tr>
+ * <tr><td>{@code getContextPath()}</td><td>{@code /contextRoot}</td></tr>
+ * <tr><td>{@code getPathInfo()}</td><td>{@code /foo}</td></tr>
+ * <tr><td>{@code getPathTranslated()}</td><td>{@code
path-to-deployed-war-on-filesystem/foo}</td></tr>
+ * <tr><td>{@code getQueryString()}</td><td>{@code bar=baz}</td></tr>
+ * <tr><td>{@code getRequestURI()}</td><td>{@code
/contextRoot/servletPath/foo}</td></tr>
+ * <tr><td>{@code getRequestURL()}</td><td>{@code
http://localhost:9080/contextRoot/servletPath/foo}</td></tr>
+ * <tr><td>{@code getServletPath()}</td><td>{@code /servletPath}</td></tr>
+ * </table>
+ *
+ * <p>
+ * Refer to <a class="doclink" href="package-summary.html#TOC">REST Servlet
API</a> for information about using this
+ * class.
+ */
+@SuppressWarnings("unchecked")
+public final class RestRequest extends HttpServletRequestWrapper {
+
+ private final RestContext context;
+
+ private final String method, stylesheet;
+ private RequestBody body;
+ private Method javaMethod;
+ private ObjectMap properties;
+ private SerializerGroup serializerGroup;
+ private ParserGroup parserGroup;
+ 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 RequestQuery queryParams;
+ private RequestFormData formData;
+ private Map<String,String> defFormData;
+ private RequestPathMatch pathParams;
+ private boolean isPost;
+ private UriContext uriContext;
+ private String charset, defaultCharset;
+ private RequestHeaders headers;
+ private ConfigFile cf;
+ private Swagger swagger, fileSwagger;
+ private Map<String,Widget> widgets;
+
+ /**
+ * Constructor.
+ */
+ RestRequest(RestContext context, HttpServletRequest req) throws
ServletException {
+ super(req);
+ this.context = context;
+
+ try {
+ isPost = req.getMethod().equalsIgnoreCase("POST");
+
+ // 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)
+
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 = getQuery().getString("method");
+ if (context.allowMethodParam(m))
+ _method = m;
+
+ method = _method;
+
+ String _stylesheet = getQuery().getString("stylesheet");
+ if (_stylesheet != null)
+ req.getSession().setAttribute("stylesheet",
_stylesheet);
+ stylesheet =
(String)req.getSession().getAttribute("stylesheet");
+
+ 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 = getQuery().getString("body");
+ if (b != null) {
+ headers.put("Content-Type",
UonSerializer.DEFAULT.getResponseContentType());
+ body.load(b.getBytes(UTF8));
+ }
+ }
+
+ if (context.isAllowHeaderParams())
+ headers.setQueryParams(queryParams);
+
+ debug = "true".equals(getQuery().getString("debug",
"false")) || "true".equals(getHeaders().getString("Debug", "false"));
+
+ this.pathParams = new RequestPathMatch();
+
+ } catch (RestException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ServletException(e);
+ }
+ }
+
+ /*
+ * Called from RestServlet after a match has been made but before the
guard or method invocation.
+ */
+ @SuppressWarnings("hiding")
+ final void init(Method javaMethod, ObjectMap properties,
Map<String,String> defHeader,
+ Map<String,String> defQuery, Map<String,String>
defFormData, String defaultCharset,
+ SerializerGroup mSerializers, ParserGroup mParsers,
UrlEncodingParser mUrlEncodingParser,
+ BeanContext beanContext, EncoderGroup encoders,
Map<String,Widget> widgets) {
+ this.javaMethod = javaMethod;
+ this.properties = properties;
+ this.urlEncodingParser = mUrlEncodingParser;
+ this.beanSession = beanContext.createSession();
+ this.pathParams
+ .setParser(urlEncodingParser)
+ .setBeanSession(beanSession);
+ this.queryParams
+ .addDefault(defQuery)
+ .setParser(urlEncodingParser)
+ .setBeanSession(beanSession);
+ this.headers
+ .addDefault(defHeader)
+ .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.defFormData = defFormData;
+ this.widgets = widgets;
+
+ if (debug) {
+ String msg = ""
+ + "\n=== HTTP Request (incoming)
===================================================="
+ + toString()
+ + "\n=== END
========================================================================";
+ context.getLogger().log(Level.WARNING, msg);
+ }
+
+ if (isPlainText())
+
this.properties.put(SerializerContext.SERIALIZER_useWhitespace, true);
+ }
+
+ /**
+ * Returns a string of the form <js>"HTTP method-name full-url"</js>
+ *
+ * @return A description of the request.
+ */
+ public String getDescription() {
+ String qs = getQueryString();
+ return "HTTP " + getMethod() + " " + getRequestURI() + (qs ==
null ? "" : "?" + qs);
+ }
+
+ /**
+ * Same as {@link #getAttribute(String)} but returns a default value if
not found.
+ *
+ * @param name The request attribute name.
+ * @param def The default value if the attribute doesn't exist.
+ * @return The request attribute value.
+ */
+ public Object getAttribute(String name, Object def) {
+ Object o = super.getAttribute(name);
+ return (o == null ? def : o);
+ }
+
+ /**
+ * Shorthand method for calling {@link #setAttribute(String, Object)}
fluently.
+ *
+ * @param name The request attribute name.
+ * @param value The request attribute value.
+ * @return This object (for method chaining).
+ */
+ public RestRequest attr(String name, Object value) {
+ setAttribute(name, value);
+ return this;
+ }
+
+
+ /**
+ * Resolves the specified property.
+ *
+ * @param cm
+ * The <code>CallMethod</code> object where the
<code>HtmlDocSerializer</code> settings are defined.
+ * Optional value. If not specified, then won't resolve
<code>HtmlDocSerializer</code> properties.
+ * @param category
+ * The property category.
+ * The possible values are:
+ * <ul>
+ * <li>
+ * <js>"Attribute"</js> - Value returned by {@link
HttpServletRequest#getAttribute(String)}.
+ * <li>
+ * <js>"FormData"</js> - Value returned by {@link
RestRequest#getFormData(String)}.
+ * <li>
+ * <js>"Header"</js> - Value returned by {@link
RestRequest#getHeader(String)}.
+ * <li>
+ * <js>"HtmlDocSerializer"</js>
+ * <br>Valid names:
+ * <ul>
+ * <li><js>"aside"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_aside}
+ * <li><js>"footer"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_footer}
+ * <li><js>"header"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_header}
+ * <li><js>"links.list"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_links}
+ * <li><js>"nav"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_nav}
+ * <li><js>"noResultsMessage"</js> - See
{@link HtmlDocSerializerContext#HTMLDOC_noResultsMessage}
+ * <li><js>"nowrap"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_nowrap}
+ * <li><js>"script.list"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_script}
+ * <li><js>"style.list"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_style}
+ * <li><js>"stylesheet"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_stylesheet}
+ * <li><js>"template"</js> - See {@link
HtmlDocSerializerContext#HTMLDOC_template}
+ * </ul>
+ * <li>
+ * <js>"Path"</js> - Value returned by {@link
RestRequest#getPath(String)}.
+ * <li>
+ * <js>"Query"</js> = Value returned by {@link
RestRequest#getQuery(String)}.
+ * <li>
+ * <js>"Request"</js>
+ * <br>Valid names:
+ * <ul>
+ * <li><js>"contextPath"</js> - Value
returned by {@link RestRequest#getContextPath()}
+ * <li><js>"method"</js> - Value returned
by {@link RestRequest#getMethod()}
+ * <li><js>"methodDescription"</js> -
Value returned by {@link RestRequest#getMethodDescription()}
+ * <li><js>"methodSummary"</js> - Value
returned by {@link RestRequest#getMethodSummary()}
+ * <li><js>"pathInfo"</js> - Value
returned by {@link RestRequest#getPathInfo()}
+ * <li><js>"requestParentURI"</js> - Value
returned by {@link UriContext#getRootRelativePathInfoParent()}
+ * <li><js>"requestURI"</js> - Value
returned by {@link RestRequest#getRequestURI()}
+ * <li><js>"servletClass"</js> - The class
name of the servlet
+ * <li><js>"servletClassSimple"</js> - The
simple class name of the servlet.
+ * <li><js>"servletDescription"</js> -
Value returned by {@link RestRequest#getServletDescription()}
+ * <li><js>"servletParentURI"</js> - Value
returned by {@link UriContext#getRootRelativeServletPathParent()}
+ * <li><js>"servletPath"</js> - See {@link
RestRequest#getServletPath()}
+ * <li><js>"servletTitle"</js> - See
{@link RestRequest#getServletTitle()}
+ * <li><js>"servletURI"</js> - See {@link
UriContext#getRootRelativeServletPath()}
+ * <li><js>"siteName"</js> - See {@link
RestRequest#getSiteName()}
+ * </ul>
+ * </ul>
+ * @param name The property name.
+ * @return The resolve property, or <jk>null</jk> if it wasn't found.
+ */
+ public Object resolveProperty(CallMethod cm, String category, String
name) {
+ char c = category.charAt(0);
+ if (c == 'A') {
+ if ("Attribute".equals(category))
+ return getAttribute(name);
+ } else if (c == 'F') {
+ if ("FormData".equals(category))
+ return getFormData(name);
+ } else if (c == 'H') {
+ if ("Header".equals(category))
+ return getHeader(name);
+ if ("HtmlDocSerializer".equals(category) && cm != null)
{
+ char c2 = StringUtils.charAt(name, 0);
+ if (c2 == 'a') {
+ if ("aside".equals(name))
+ return cm.htmlAside == null ?
null : resolveVars(cm.htmlAside);
+ } else if (c2 == 'f') {
+ if ("footer".equals(name))
+ return cm.htmlFooter == null ?
null : resolveVars(cm.htmlFooter);
+ } else if (c2 == 'h') {
+ if ("header".equals(name))
+ return cm.htmlHeader == null ?
null : resolveVars(cm.htmlHeader);
+ } else if (c2 == 'l') {
+ if ("links.list".equals(name)) {
+ if (cm.htmlLinks == null ||
cm.htmlLinks.length == 0)
+ return null;
+ try {
+ List<String> la = new
ArrayList<String>();
+ for (String l :
cm.htmlLinks) {
+ // Temporary
backwards compatibility with JSON object format.
+ if
(l.startsWith("{")) {
+
ObjectMap m = new ObjectMap(l);
+ for
(Map.Entry<String,Object> e : m.entrySet())
+
la.add(resolveVars(e.getKey()) + ":" +
resolveVars(StringUtils.toString(e.getValue())));
+ } else {
+
la.add(resolveVars(l));
+ }
+ }
+ return la;
+ } catch (ParseException e) {
+ throw new
RuntimeException(e);
+ }
+ }
+ } else if (c2 == 'n') {
+ if ("nav".equals(name))
+ return cm.htmlNav == null ?
null : resolveVars(cm.htmlNav);
+ if ("noResultsMessage".equals(name))
+ return cm.htmlNoResultsMessage
== null ? null : resolveVars(cm.htmlNoResultsMessage);
+ if ("nowrap".equals(name))
+ return cm.htmlNoWrap;
+ } else if (c2 == 's') {
+ if ("script.list".equals(name)) {
+ Set<String> l = new
LinkedHashSet<String>();
+ if (cm.htmlScript != null)
+
l.add(resolveVars(cm.htmlScript));
+ for (Widget w :
getWidgets().values()) {
+ String script;
+ try {
+ script =
w.getScript(this);
+ } catch (Exception e) {
+ script =
e.getLocalizedMessage();
+ }
+ if (script != null)
+
l.add(resolveVars(script));
+ }
+ return l;
+ }
+ if ("style.list".equals(name)) {
+ Set<String> l = new
LinkedHashSet<String>();
+ if (cm.htmlStyle != null)
+
l.add(resolveVars(cm.htmlStyle));
+ for (Widget w :
getWidgets().values()) {
+ String style;
+ try {
+ style =
w.getStyle(this);
+ } catch (Exception e) {
+ style =
e.getLocalizedMessage();
+ }
+ if (style != null)
+
l.add(resolveVars(style));
+ }
+ return l;
+ }
+ if ("stylesheet".equals(name)) {
+ String s = getStylesheet();
+ // Exclude absolute URIs to
stylesheets for security reasons.
+ if (s == null ||
isAbsoluteUri(s))
+ s = cm.htmlStylesheet;
+ return s == null ? null :
resolveVars(s);
+ }
+ } else if (c2 == 't') {
+ if ("template".equals(name))
+ return cm.htmlTemplate;
+ }
+ }
+ } else if (c == 'P') {
+ if ("Path".equals(category))
+ return getPath(name);
+ } else if (c == 'Q') {
+ if ("Query".equals(category))
+ return getQuery(name);
+ } else if (c == 'R') {
+ if ("Request".equals(category)) {
+ char c2 = StringUtils.charAt(name, 0);
+ if (c2 == 'c') {
+ if ("contextPath".equals(name))
+ return getContextPath();
+ } else if (c2 == 'm') {
+ if ("method".equals(name))
+ return getMethod();
+ if ("methodDescription".equals(name))
+ return getMethodDescription();
+ if ("methodSummary".equals(name))
+ return getMethodSummary();
+ } else if (c2 == 'p') {
+ if ("pathInfo".equals(name))
+ return getPathInfo();
+ } else if (c2 == 'r') {
+ if ("requestParentURI".equals(name))
+ return
getUriContext().getRootRelativePathInfoParent();
+ if ("requestURI".equals(name))
+ return getRequestURI();
+ } else if (c2 == 's') {
+ if ("servletClass".equals(name))
+ return
getContext().getResource().getClass().getName();
+ if ("servletClassSimple".equals(name))
+ return
getContext().getResource().getClass().getSimpleName();
+ if ("servletDescription".equals(name))
+ return getServletDescription();
+ if ("servletParentURI".equals(name))
+ return
getUriContext().getRootRelativeServletPathParent();
+ if ("servletPath".equals(name))
+ return getServletPath();
+ if ("servletTitle".equals(name))
+ return getServletTitle();
+ if ("servletURI".equals(name))
+ return
getUriContext().getRootRelativeServletPath();
+ if ("siteName".equals(name))
+ return getSiteName();
+ }
+ }
+ }
+ return null;
+ }
+
+
//--------------------------------------------------------------------------------
+ // Properties
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Servlet calls this method to initialize the properties.
+ */
+ RestRequest setProperties(ObjectMap properties) {
+ this.properties = properties;
+ return this;
+ }
+
+ /**
+ * Retrieve the properties active for this request.
+ *
+ * <p>
+ * These properties can be modified by the request.
+ *
+ * @return The properties active for this request.
+ */
+ public ObjectMap getProperties() {
+ return this.properties;
+ }
+
+ /**
+ * Shortcut for calling <code>getProperties().append(name,
value);</code> fluently.
+ *
+ * @param name The property name.
+ * @param value The property value.
+ * @return This object (for method chaining).
+ */
+ public RestRequest prop(String name, Object value) {
+ this.properties.append(name, value);
+ return this;
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // Headers
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Returns the headers on this request.
+ *
+ * @return The headers on this request. Never <jk>null</jk>.
+ */
+ public RequestHeaders getHeaders() {
+ return headers;
+ }
+
+ @Override /* ServletRequest */
+ public String getHeader(String name) {
+ return getHeaders().getString(name);
+ }
+
+ @Override /* ServletRequest */
+ public Enumeration<String> getHeaders(String name) {
+ String[] v = headers.get(name);
+ if (v == null || v.length == 0)
+ return Collections.enumeration(Collections.EMPTY_LIST);
+ return Collections.enumeration(Arrays.asList(v));
+ }
+
+ /**
+ * Returns the media types that are valid for <code>Content-Type</code>
headers on the request.
+ *
+ * @return The set of media types registered in the parser group of
this request.
+ */
+ public List<MediaType> getSupportedMediaTypes() {
+ return parserGroup.getSupportedMediaTypes();
+ }
+
+ /**
+ * Sets the charset to expect on the request body.
+ */
+ @Override /* ServletRequest */
+ public void setCharacterEncoding(String charset) {
+ this.charset = charset;
+ }
+
+ /**
+ * Returns the charset specified on the <code>Content-Type</code>
header, or <js>"UTF-8"</js> if not specified.
+ */
+ @Override /* ServletRequest */
+ public String getCharacterEncoding() {
+ if (charset == null) {
+ // Determine charset
+ // NOTE: Don't use super.getCharacterEncoding()
because the spec is implemented inconsistently.
+ // Jetty returns the default charset instead of null if
the character is not specified on the request.
+ String h = getHeader("Content-Type");
+ if (h != null) {
+ int i = h.indexOf(";charset=");
+ if (i > 0)
+ charset = h.substring(i+9).trim();
+ }
+ if (charset == null)
+ charset = defaultCharset;
+ if (! Charset.isSupported(charset))
+ throw new
RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported charset in header
''Content-Type'': ''{0}''", h);
+ }
+ return charset;
+ }
+
+ @Override /* ServletRequest */
+ public Locale getLocale() {
+ String h = headers.getString("Accept-Language");
+ if (h != null) {
+ MediaTypeRange[] mr = MediaTypeRange.parse(h);
+ if (mr.length > 0)
+ return toLocale(mr[0].getMediaType().getType());
+ }
+ return super.getLocale();
+ }
+
+ @Override /* ServletRequest */
+ public Enumeration<Locale> getLocales() {
+ String h = headers.getString("Accept-Language");
+ if (h != null) {
+ MediaTypeRange[] mr = MediaTypeRange.parse(h);
+ if (mr.length > 0) {
+ List<Locale> l = new
ArrayList<Locale>(mr.length);
+ for (MediaTypeRange r : mr)
+
l.add(toLocale(r.getMediaType().getType()));
+ return enumeration(l);
+ }
+ }
+ return super.getLocales();
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // Query parameters
+
//--------------------------------------------------------------------------------
+
+ /**
+ * 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 RequestQuery getQuery() {
+ return queryParams;
+ }
+
+ /**
+ * Shortcut for calling <code>getQuery().getString(name)</code>.
+ *
+ * @param name The query parameter name.
+ * @return The query parameter value, or <jk>null<jk> if not found.
+ */
+ public String getQuery(String name) {
+ return getQuery().getString(name);
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // Form data parameters
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Retrieves the URL-encoded form data from the request if the body has
already been cached locally.
+ *
+ * @return The URL-encoded form data from the request.
+ */
+ 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());
+ }
+ }
+ }
+ formData.addDefault(defFormData);
+ return formData;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Shortcut for calling <code>getFormData().getString(name)</code>.
+ *
+ * @param name The form data parameter name.
+ * @return The form data parameter value, or <jk>null<jk> if not found.
+ */
+ public String getFormData(String name) {
+ return getFormData().getString(name);
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // Path parameters
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Retrieves the URL-encoded form data from the request if the body has
already been cached locally.
+ *
+ * @return The URL-encoded form data from the request.
+ */
+ public RequestPathMatch getPathMatch() {
+ return pathParams;
+ }
+
+ /**
+ * Shortcut for calling <code>getPathMatch().get(name)</code>.
+ *
+ * @param name The path variable name.
+ * @return The path variable value, or <jk>null<jk> if not found.
+ */
+ public String getPath(String name) {
+ return getPathMatch().get(name);
+ }
+
+
//--------------------------------------------------------------------------------
+ // Body methods
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Returns the body of this HTTP request.
+ *
+ * @return The body of this HTTP request.
+ */
+ public RequestBody getBody() {
+ return 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.
+ */
+ @Override /* ServletRequest */
+ public BufferedReader getReader() throws IOException {
+ return getBody().getReader();
+ }
+
+ /**
+ * 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.
+ */
+ @Override /* ServletRequest */
+ public ServletInputStream getInputStream() throws IOException {
+ return getBody().getInputStream();
+ }
+
+ ServletInputStream getRawInputStream() throws IOException {
+ return super.getInputStream();
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // URI-related methods
+
//--------------------------------------------------------------------------------
+
+ @Override /* HttpServletRequest */
+ public String getContextPath() {
+ String cp = context.getContextPath();
+ return cp == null ? super.getContextPath() : cp;
+ }
+
+ @Override /* HttpServletRequest */
+ public String getServletPath() {
+ String cp = context.getContextPath();
+ String sp = super.getServletPath();
+ return cp == null || ! sp.startsWith(cp) ? sp :
sp.substring(cp.length());
+ }
+
+ /**
+ * Returns the URI context of the request.
+ *
+ * <p>
+ * The URI context contains all the information about the URI of the
request, such as the servlet URI, context
+ * path, etc...
+ *
+ * @return The URI context of the request.
+ */
+ public UriContext getUriContext() {
+ if (uriContext == null) {
+ String scheme = getScheme();
+ int port = getServerPort();
+ StringBuilder authority = new
StringBuilder(getScheme()).append("://").append(getServerName());
+ if (! (port == 80 && "http".equals(scheme) || port ==
443 && "https".equals(scheme)))
+ authority.append(':').append(port);
+ uriContext = new UriContext(authority.toString(),
getContextPath(), getServletPath(), super.getPathInfo());
+ }
+ return uriContext;
+ }
+
+ /**
+ * Returns a URI resolver that can be used to convert URIs to absolute
or root-relative form.
+ *
+ * @param resolution The URI resolution rule.
+ * @param relativity The relative URI relativity rule.
+ * @return The URI resolver for this request.
+ */
+ public UriResolver getUriResolver(UriResolution resolution,
UriRelativity relativity) {
+ return new UriResolver(resolution, relativity, getUriContext());
+ }
+
+ /**
+ * Shortcut for calling {@link #getUriResolver()} using {@link
UriResolution#ROOT_RELATIVE} and
+ * {@link UriRelativity#RESOURCE}
+ *
+ * @return The URI resolver for this request.
+ */
+ public UriResolver getUriResolver() {
+ return new UriResolver(UriResolution.ROOT_RELATIVE,
UriRelativity.RESOURCE, getUriContext());
+ }
+
+ /**
+ * Returns the URI for this request.
+ *
+ * <p>
+ * Similar to {@link #getRequestURI()} but returns the value as a
{@link URI}.
+ * It also gives you the capability to override the query parameters
(e.g. add new query parameters to the existing
+ * URI).
+ *
+ * @param includeQuery If <jk>true</jk> include the query parameters on
the request.
+ * @param addQueryParams Augment the request URI with the specified
query parameters.
+ * @return A new URI.
+ */
+ public URI getUri(boolean includeQuery, Map<String,?> addQueryParams) {
+ String uri = getRequestURI();
+ if (includeQuery || addQueryParams != null) {
+ StringBuilder sb = new StringBuilder(uri);
+ RequestQuery rq = this.queryParams.copy();
+ if (addQueryParams != null)
+ for (Map.Entry<String,?> e :
addQueryParams.entrySet())
+ rq.put(e.getKey(), e.getValue());
+ if (! rq.isEmpty())
+ sb.append('?').append(rq.toQueryString());
+ uri = sb.toString();
+ }
+ try {
+ return new URI(uri);
+ } catch (URISyntaxException e) {
+ // Shouldn't happen.
+ throw new RuntimeException(e);
+ }
+ }
+
+
//--------------------------------------------------------------------------------
+ // Labels
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Returns the localized site name.
+ *
+ * <p>
+ * The site name is intended to be a title that can be applied to the
entire site.
+ *
+ * <p>
+ * One possible use is if you want to add the same title to the top of
all pages by defining a header on a
+ * common parent class like so:
+ * <p class='bcode'>
+ * htmldoc=<ja>@HtmlDoc</ja>(
+ * header={
+ * <js>"<h1>$R{siteName}</h1>"</js>,
+ * <js>"<h2>$R{servletTitle}</h2>"</js>
+ * }
+ * )
+ * </p>
+ *
+ * <p>
+ * Equivalent to calling {@link
RestInfoProvider#getSiteName(RestRequest)} with this object.
+ *
+ * @return The localized servlet label.
+ */
+ public String getSiteName() {
+ return context.getInfoProvider().getSiteName(this);
+ }
+
+ /**
+ * Returns the localized servlet title.
+ *
+ * <p>
+ * Equivalent to calling {@link RestInfoProvider#getTitle(RestRequest)}
with this object.
+ *
+ * @return The localized servlet label.
+ */
+ public String getServletTitle() {
+ return context.getInfoProvider().getTitle(this);
+ }
+
+ /**
+ * Returns the localized servlet description.
+ *
+ * <p>
+ * Equivalent to calling {@link
RestInfoProvider#getDescription(RestRequest)} with this object.
+ *
+ * @return The localized servlet description.
+ */
+ public String getServletDescription() {
+ return context.getInfoProvider().getDescription(this);
+ }
+
+ /**
+ * Returns the localized method summary.
+ *
+ * <p>
+ * Equivalent to calling {@link
RestInfoProvider#getMethodSummary(String, RestRequest)} with this object.
+ *
+ * @return The localized method description.
+ */
+ public String getMethodSummary() {
+ return
context.getInfoProvider().getMethodSummary(javaMethod.getName(), this);
+ }
+
+ /**
+ * Returns the localized method description.
+ *
+ * <p>
+ * Equivalent to calling {@link
RestInfoProvider#getMethodDescription(String, RestRequest)} with this object.
+ *
+ * @return The localized method description.
+ */
+ public String getMethodDescription() {
+ return
context.getInfoProvider().getMethodDescription(javaMethod.getName(), this);
+ }
+
+
//--------------------------------------------------------------------------------
+ // Other methods
+
//--------------------------------------------------------------------------------
+
+ /**
+ * Returns the value of the <jk>"stylesheet"</js> parameter.
+ *
+ * @return The value of the <jk>"stylesheet"</js> parameter, or
<jk>null</jk> if it wasn't specified.
+ */
+ protected String getStylesheet() {
+ return stylesheet;
+ }
+
+ /**
+ * Returns the serializers associated with this request.
+ *
+ * @return The serializers associated with this request.
+ */
+ public SerializerGroup getSerializerGroup() {
+ return serializerGroup;
+ }
+
+ /**
+ * Returns the parsers associated with this request.
+ *
+ * @return The parsers associated with this request.
+ */
+ public ParserGroup getParserGroup() {
+ return parserGroup;
+ }
+
+
+ /**
+ * Returns the method of this request.
+ *
+ * <p>
+ * If <code>allowHeaderParams</code> init parameter is <jk>true</jk>,
then first looks for
+ * <code>&method=xxx</code> in the URL query string.
+ */
+ @Override /* ServletRequest */
+ public String getMethod() {
+ 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 getBody().getContentLength();
+ }
+
+ int getRawContentLength() {
+ return super.getContentLength();
+ }
+
+ /**
+ * Returns <jk>true</jk> if <code>&plainText=true</code> was
specified as a URL parameter.
+ *
+ * <p>
+ * This indicates that the <code>Content-Type</code> of the output
should always be set to <js>"text/plain"</js>
+ * to make it easy to render in a browser.
+ *
+ * <p>
+ * This feature is useful for debugging.
+ *
+ * @return <jk>true</jk> if {@code &plainText=true} was specified
as a URL parameter
+ */
+ public boolean isPlainText() {
+ return "true".equals(getQuery().getString("plainText",
"false"));
+ }
+
+ /**
+ * Shortcut method for calling {@link MessageBundle#getString(Locale,
String, Object...)} based on the request locale.
+ *
+ * @param key The message key.
+ * @param args Optional {@link MessageFormat}-style arguments.
+ * @return The localized message.
+ */
+ public String getMessage(String key, Object...args) {
+ return context.getMessages().getString(getLocale(), key, args);
+ }
+
+ /**
+ * Returns the resource bundle for the request locale.
+ *
+ * @return The resource bundle. Never <jk>null</jk>.
+ */
+ public MessageBundle getResourceBundle() {
+ return context.getMessages().getBundle(getLocale());
+ }
+
+ /**
+ * Returns the servlet handling the request.
+ *
+ * <p>
+ * Can be used to access servlet-init parameters or annotations during
requests, such as in calls to
+ * {@link RestGuard#guard(RestRequest, RestResponse)}..
+ *
+ * @return The servlet handling the request.
+ */
+ public RestContext getContext() {
+ return context;
+ }
+
+ /**
+ * Returns the java method handling the request.
+ *
+ * <p>
+ * Can be used to access the method name or method annotations during
requests, such as in calls to
+ * {@link RestGuard#guard(RestRequest, RestResponse)}.
+ *
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ * <li>This returns null when evaluating servlet-level guards
since the method has not been resolved at that
+ * point of execution.
+ * </ul>
+ *
+ * @return The Java method handling the request, or <code>null</code>
if the method has not yet been resolved.
+ */
+ public Method getJavaMethod() {
+ return javaMethod;
+ }
+
+ /**
+ * Returns the {@link BeanSession} associated with this request.
+ *
+ * @return The request bean session.
+ */
+ public BeanSession getBeanSession() {
+ return beanSession;
+ }
+
+ /**
+ * Returns the variable resolver session for this request using session
objects created by
+ * {@link RestCallHandler#getSessionObjects(RestRequest)}.
+ *
+ * <p>
+ * See {@link RestContext#getVarResolver()} for the list of supported
variables.
+ *
+ * @return The variable resolver for this request.
+ */
+ public VarResolverSession getVarResolverSession() {
+ if (varSession == null)
+ varSession =
context.getVarResolver().createSession(context.getCallHandler().getSessionObjects(this));
+ return varSession;
+ }
+
+ /**
+ * Shortcut for calling
<code>getVarResolverSession().resolve(input)</code>.
+ *
+ * @param input The input string to resolve variables in.
+ * @return The string with variables resolved, or <jk>null</jk> if
input is null.
+ */
+ public String resolveVars(String input) {
+ return getVarResolverSession().resolve(input);
+ }
+
+ /**
+ * Returns an instance of a {@link ReaderResource} that represents the
contents of a resource text file from the
+ * classpath.
+ *
+ * @param name The name of the resource (i.e. the value normally passed
to {@link Class#getResourceAsStream(String)}.
+ * @param resolveVars
+ * If <jk>true</jk>, any {@link
org.apache.juneau.rest.annotation.Parameter} variables will be
+ * resolved by the variable resolver returned by {@link
#getVarResolverSession()}.
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ * @param mediaType The value to set as the <js>"Content-Type"</js>
header for this object.
+ * @return A new reader resource, or <jk>null</jk> if resource could
not be found.
+ * @throws IOException
+ */
+ public ReaderResource getReaderResource(String name, boolean
resolveVars, MediaType mediaType) throws IOException {
+ String s = context.getResourceAsString(name, getLocale());
+ if (s == null)
+ return null;
+ ReaderResource.Builder b = new
ReaderResource.Builder().mediaType(mediaType).contents(s);
+ if (resolveVars)
+ b.varResolver(getVarResolverSession());
+ return b.build();
+ }
+
+ /**
+ * Same as {@link #getReaderResource(String, boolean, MediaType)}
except uses the resource mime-type map
+ * constructed using {@link RestConfig#addMimeTypes(String...)} to
determine the media type.
+ *
+ * @param name The name of the resource (i.e. the value normally passed
to {@link Class#getResourceAsStream(String)}.
+ * @param resolveVars
+ * If <jk>true</jk>, any {@link
org.apache.juneau.rest.annotation.Parameter} variables will be
+ * resolved by the variable resolver returned by {@link
#getVarResolverSession()}.
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ * @return A new reader resource, or <jk>null</jk> if resource could
not be found.
+ * @throws IOException
+ */
+ public ReaderResource getReaderResource(String name, boolean
resolveVars) throws IOException {
+ return getReaderResource(name, resolveVars,
MediaType.forString(context.getMediaTypeForName(name)));
+ }
+
+ /**
+ * Same as {@link #getReaderResource(String, boolean)} with
<code>resolveVars == <jk>false</jk></code>
+ *
+ * @param name The name of the resource (i.e. the value normally passed
to {@link Class#getResourceAsStream(String)}.
+ * @return A new reader resource, or <jk>null</jk> if resource could
not be found.
+ * @throws IOException
+ */
+ public ReaderResource getReaderResource(String name) throws IOException
{
+ return getReaderResource(name, false,
MediaType.forString(context.getMediaTypeForName(name)));
+ }
+
+ /**
+ * Returns the config file associated with the servlet.
+ *
+ * @return
+ * The config file associated with the servlet, or <jk>null</jk>
if servlet does not have a config file
+ * associated with it.
+ */
+ public ConfigFile getConfigFile() {
+ if (cf == null)
+ cf =
context.getConfigFile().getResolving(getVarResolverSession());
+ return cf;
+ }
+
+ /**
+ * Returns the localized swagger associated with the servlet.
+ *
+ * @return
+ * The swagger associated with the servlet.
+ * Never <jk>null</jk>.
+ */
+ public Swagger getSwagger() {
+ if (swagger == null)
+ swagger = context.getInfoProvider().getSwagger(this);
+ return swagger;
+ }
+
+ /**
+ * Returns the widgets used for resolving <js>"$W{...}"</js> string
variables.
+ *
+ * @return
+ * The widgets used for resolving <js>"$W{...}"</js> string
variables.
+ * Never <jk>null</jk>.
+ */
+ public Map<String,Widget> getWidgets() {
+ return widgets;
+ }
+
+ /**
+ * Returns the localized Swagger from the file system.
+ *
+ * <p>
+ * Looks for a file called <js>"{ServletClass}_{locale}.json"</js> in
the same package as this servlet and returns
+ * it as a parsed {@link Swagger} object.
+ *
+ * <p>
+ * Returned objects are cached for later quick-lookup.
+ *
+ * @return The parsed swagger object, or <jk>null</jk> if the swagger
file could not be found.
+ */
+ protected Swagger getSwaggerFromFile() {
+ if (fileSwagger == null)
+ fileSwagger =
context.getInfoProvider().getSwaggerFromFile(this.getLocale());
+ if (fileSwagger == null)
+ fileSwagger = Swagger.NULL;
+ return fileSwagger == Swagger.NULL ? null : fileSwagger;
+ }
+
+ @Override /* Object */
+ public String toString() {
+ StringBuilder sb = new
StringBuilder("\n").append(getDescription()).append("\n");
+ sb.append("---Headers---\n");
+ for (Enumeration<String> e = getHeaderNames();
e.hasMoreElements();) {
+ String h = e.nextElement();
+ sb.append("\t").append(h).append(":
").append(getHeader(h)).append("\n");
+ }
+ sb.append("---Default Servlet Headers---\n");
+ 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 {
+ sb.append("---Body UTF-8---\n");
+ sb.append(body.asString()).append("\n");
+ sb.append("---Body Hex---\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.");
+ }
+ }
+ return sb.toString();
+ }
+
+
+
//--------------------------------------------------------------------------------
+ // Utility methods
+
//--------------------------------------------------------------------------------
+
+ /*
+ * Converts an Accept-Language value entry to a Locale.
+ */
+ private static Locale toLocale(String lang) {
+ String country = "";
+ int i = lang.indexOf('-');
+ if (i > -1) {
+ country = lang.substring(i+1).trim();
+ lang = lang.substring(0,i).trim();
+ }
+ return new Locale(lang, country);
+ }
+
+
+ void setJavaMethod(Method method) {
+ this.javaMethod = method;
+ }
+}
\ No newline at end of file
Propchange:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
==============================================================================
---
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
(added)
+++
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
Fri Sep 8 23:25:34 2017
@@ -0,0 +1,53 @@
+//
***************************************************************************************************************************
+// * 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 org.apache.juneau.rest.annotation.*;
+
+/**
+ * Class used to resolve {@link Class} objects to instances.
+ *
+ * <p>
+ * Used to convert classes defined via {@link RestResource#children()
@RestResource.children()} into child instances.
+ *
+ * <p>
+ * Subclasses can be created to provide customized resource resolution.
+ * These can be associated with REST resources in one of the following ways:
+ * <ul>
+ * <li>{@link RestResource#resourceResolver()
@RestResource.resourceResolver()} annotation.
+ * <li>{@link RestConfig#setResourceResolver(Class)}/{@link
RestConfig#setResourceResolver(RestResourceResolver)}
+ * methods.
+ * </ul>
+ *
+ * <p>
+ * An instance of this class can also be passed in through the servlet context
as the context attribute
+ * {@link RestContext#REST_resourceResolver}.
+ */
+public interface RestResourceResolver {
+
+ /**
+ * Resolves the specified class to a resource object.
+ *
+ * <p>
+ * Subclasses can override this method to provide their own custom
resolution.
+ *
+ * <p>
+ * The default implementation simply creates a new class instance using
{@link Class#newInstance()}.
+ *
+ * @param c The class to resolve.
+ * @param config The initialization configuration for the resource.
+ * @return The instance of that class.
+ * @throws RestServletException If class could not be resolved.
+ */
+ Object resolve(Class<?> c, RestConfig config) throws
RestServletException;
+}
Propchange:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
==============================================================================
---
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
(added)
+++
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
Fri Sep 8 23:25:34 2017
@@ -0,0 +1,59 @@
+//
***************************************************************************************************************************
+// * 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.ClassUtils.*;
+
+import java.lang.reflect.*;
+
+/**
+ * Denotes the default resolver.
+ *
+ * <p>
+ * The default implementation simply instantiates the class using one of the
following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestConfig)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * </ul>
+ *
+ * <p>
+ * The former constructor can be used to get access to the {@link RestConfig}
object to get access to the
+ * config file and initialization information or make programmatic
modifications to the resource before
+ * full initialization.
+ *
+ * <p>
+ * Non-<code>RestServlet</code> classes can also add the following two methods
to get access to the
+ * {@link RestConfig} and {@link RestContext} objects:
+ * <ul>
+ * <li><code><jk>public void</jk> init(RestConfig);</code>
+ * <li><code><jk>public void</jk> init(RestContext);</code>
+ * </ul>
+ *
+ */
+public class RestResourceResolverSimple implements RestResourceResolver {
+
+ @Override /* RestResourceResolver */
+ public Object resolve(Class<?> c, RestConfig config) throws
RestServletException {
+ try {
+ Constructor<?> c1 = findPublicConstructor(c,
RestConfig.class);
+ if (c1 != null)
+ return c1.newInstance(config);
+ c1 = findPublicConstructor(c);
+ if (c1 != null)
+ return c1.newInstance();
+ } catch (Exception e) {
+ throw new RestServletException("Could not instantiate
resource class ''{0}''", c.getName()).initCause(e);
+ }
+ throw new RestServletException("Could not find public
constructor for class ''{0}''.", c);
+ }
+}
\ No newline at end of file
Propchange:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResourceResolverSimple.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
==============================================================================
---
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
(added)
+++
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
Fri Sep 8 23:25:34 2017
@@ -0,0 +1,803 @@
+//
***************************************************************************************************************************
+// * 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 java.nio.charset.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.html.*;
+import org.apache.juneau.http.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * Represents an HTTP response for a REST resource.
+ *
+ * <p>
+ * Essentially an extended {@link HttpServletResponse} with some special
convenience methods that allow you to easily
+ * output POJOs as responses.
+ *
+ * <p>
+ * Since this class extends {@link HttpServletResponse}, developers are free
to use these convenience methods, or
+ * revert to using lower level methods like any other servlet response.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <ja>@RestMethod</ja>(name=<js>"GET"</js>)
+ * <jk>public void</jk> doGet(RestRequest req, RestResponse res) {
+ * res.setPageTitle(<js>"My title"</js>)
+ * .setOutput(<js>"Simple string response"</js>);
+ * }
+ * </p>
+ *
+ * <p>
+ * Refer to <a class="doclink" href="package-summary.html#TOC">REST Servlet
API</a> for information about using this
+ * class.
+ */
+public final class RestResponse extends HttpServletResponseWrapper {
+
+ private final RestRequest request;
+ private Object output; // The POJO being sent to
the output.
+ private boolean isNullOutput; // The output is null (as
opposed to not being set at all)
+ private ObjectMap properties; // Response properties
+ SerializerGroup serializerGroup;
+ UrlEncodingSerializer urlEncodingSerializer; // The serializer used to
convert arguments passed into Redirect objects.
+ private EncoderGroup encoders;
+ private ServletOutputStream os;
+ private PrintWriter w;
+
+ /**
+ * Constructor.
+ */
+ RestResponse(RestContext context, RestRequest req, HttpServletResponse
res) {
+ super(res);
+ this.request = req;
+
+ for (Map.Entry<String,Object> e :
context.getDefaultResponseHeaders().entrySet())
+ setHeader(e.getKey(), e.getValue().toString());
+
+ try {
+ String passThroughHeaders =
req.getHeader("x-response-headers");
+ if (passThroughHeaders != null) {
+ PartParser p = context.getUrlEncodingParser();
+ ObjectMap m = p.parse(PartType.HEADER,
passThroughHeaders, context.getBeanContext().getClassMeta(ObjectMap.class));
+ for (Map.Entry<String,Object> e : m.entrySet())
+ setHeader(e.getKey(),
e.getValue().toString());
+ }
+ } catch (Exception e1) {
+ throw new RestException(SC_BAD_REQUEST, "Invalid format
for header 'x-response-headers'. Must be in URL-encoded
format.").initCause(e1);
+ }
+ }
+
+ /*
+ * Called from RestServlet after a match has been made but before the
guard or method invocation.
+ */
+ @SuppressWarnings("hiding")
+ final void init(ObjectMap properties, String defaultCharset,
SerializerGroup mSerializers, UrlEncodingSerializer mUrlEncodingSerializer,
EncoderGroup encoders) {
+ this.properties = properties;
+ this.serializerGroup = mSerializers;
+ this.urlEncodingSerializer = mUrlEncodingSerializer;
+ this.encoders = encoders;
+
+ // Find acceptable charset
+ String h = request.getHeader("accept-charset");
+ String charset = null;
+ if (h == null)
+ charset = defaultCharset;
+ else for (MediaTypeRange r : MediaTypeRange.parse(h)) {
+ if (r.getQValue() > 0) {
+ MediaType mt = r.getMediaType();
+ if (mt.getType().equals("*"))
+ charset = defaultCharset;
+ else if (Charset.isSupported(mt.getType()))
+ charset = mt.getType();
+ if (charset != null)
+ break;
+ }
+ }
+
+ if (charset == null)
+ throw new RestException(SC_NOT_ACCEPTABLE, "No
supported charsets in header ''Accept-Charset'': ''{0}''",
request.getHeader("Accept-Charset"));
+ super.setCharacterEncoding(charset);
+ }
+
+ /**
+ * Gets the serializer group for the response.
+ *
+ * @return The serializer group for the response.
+ */
+ public SerializerGroup getSerializerGroup() {
+ return serializerGroup;
+ }
+
+ /**
+ * Returns the media types that are valid for <code>Accept</code>
headers on the request.
+ *
+ * @return The set of media types registered in the parser group of
this request.
+ */
+ public List<MediaType> getSupportedMediaTypes() {
+ return serializerGroup.getSupportedMediaTypes();
+ }
+
+ /**
+ * Returns the codings that are valid for <code>Accept-Encoding</code>
and <code>Content-Encoding</code> headers on
+ * the request.
+ *
+ * @return The set of media types registered in the parser group of
this request.
+ * @throws RestServletException
+ */
+ public List<String> getSupportedEncodings() throws RestServletException
{
+ return encoders.getSupportedEncodings();
+ }
+
+ /**
+ * Sets the HTTP output on the response.
+ *
+ * <p>
+ * Calling this method is functionally equivalent to returning the
object in the REST Java method.
+ *
+ * <p>
+ * Can be of any of the following types:
+ * <ul>
+ * <li> {@link InputStream}
+ * <li> {@link Reader}
+ * <li> Any serializable type defined in <a class="doclink"
+ *
href="../../../../overview-summary.html#Core.PojoCategories">POJO Categories</a>
+ * </ul>
+ *
+ * <p>
+ * If it's an {@link InputStream} or {@link Reader}, you must also
specify the <code>Content-Type</code> using the
+ * {@link #setContentType(String)} method.
+ *
+ * @param output The output to serialize to the connection.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setOutput(Object output) {
+ this.output = output;
+ this.isNullOutput = output == null;
+ return this;
+ }
+
+ /**
+ * Add a serializer property to send to the serializers to override a
default value.
+ *
+ * <p>
+ * Can be any value specified in the following classes:
+ * <ul>
+ * <li>{@link SerializerContext}
+ * <li>{@link JsonSerializerContext}
+ * <li>{@link XmlSerializerContext}
+ * </ul>
+ *
+ * @param key The setting name.
+ * @param value The setting value.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setProperty(String key, Object value) {
+ properties.put(key, value);
+ return this;
+ }
+
+ /**
+ * Returns the properties set via {@link #setProperty(String, Object)}.
+ *
+ * @return A map of all the property values set.
+ */
+ public ObjectMap getProperties() {
+ return properties;
+ }
+
+ /**
+ * Shortcut method that allows you to use var-args to simplify setting
array output.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * <jc>// Instead of...</jc>
+ * response.setOutput(<jk>new</jk> Object[]{x,y,z});
+ *
+ * <jc>// ...call this...</jc>
+ * response.setOutput(x,y,z);
+ * </p>
+ *
+ * @param output The output to serialize to the connection.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setOutputs(Object...output) {
+ this.output = output;
+ return this;
+ }
+
+ /**
+ * Returns the output that was set by calling {@link
#setOutput(Object)}.
+ *
+ * @return The output object.
+ */
+ public Object getOutput() {
+ return output;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this response has any output associated
with it.
+ *
+ * @return <jk>true</jk> if {@code setInput()} has been called.
+ */
+ public boolean hasOutput() {
+ return output != null || isNullOutput;
+ }
+
+ /**
+ * Sets the output to a plain-text message regardless of the content
type.
+ *
+ * @param text The output text to send.
+ * @return This object (for method chaining).
+ * @throws IOException If a problem occurred trying to write to the
writer.
+ */
+ public RestResponse sendPlainText(String text) throws IOException {
+ setContentType("text/plain");
+ getNegotiatedWriter().write(text);
+ return this;
+ }
+
+ /**
+ * Equivalent to {@link HttpServletResponse#getOutputStream()}, except
wraps the output stream if an {@link Encoder}
+ * was found that matched the <code>Accept-Encoding</code> header.
+ *
+ * @return A negotiated output stream.
+ * @throws IOException
+ */
+ public ServletOutputStream getNegotiatedOutputStream() throws
IOException {
+ if (os == null) {
+ Encoder encoder = null;
+
+ String ae = request.getHeader("Accept-Encoding");
+ if (! (ae == null || ae.isEmpty())) {
+ EncoderMatch match = encoders != null ?
encoders.getEncoderMatch(ae) : null;
+ if (match == null) {
+ // Identity should always match unless
"identity;q=0" or "*;q=0" is specified.
+ if
(ae.matches(".*(identity|\\*)\\s*;\\s*q\\s*=\\s*(0(?!\\.)|0\\.0).*")) {
+ throw new
RestException(SC_NOT_ACCEPTABLE,
+ "Unsupported encoding
in request header ''Accept-Encoding'': ''{0}''\n\tSupported codings: {1}",
+ ae,
encoders.getSupportedEncodings()
+ );
+ }
+ } else {
+ encoder = match.getEncoder();
+ String encoding =
match.getEncoding().toString();
+
+ // Some clients don't recognize
identity as an encoding, so don't set it.
+ if (! encoding.equals("identity"))
+ setHeader("content-encoding",
encoding);
+ }
+ }
+ os = getOutputStream();
+ if (encoder != null) {
+ final OutputStream os2 =
encoder.getOutputStream(os);
+ os = new ServletOutputStream(){
+ @Override /* OutputStream */
+ public final void write(byte[] b, int
off, int len) throws IOException {
+ os2.write(b, off, len);
+ }
+ @Override /* OutputStream */
+ public final void write(int b) throws
IOException {
+ os2.write(b);
+ }
+ @Override /* OutputStream */
+ public final void flush() throws
IOException {
+ os2.flush();
+ }
+ @Override /* OutputStream */
+ public final void close() throws
IOException {
+ os2.close();
+ }
+ @Override /* ServletOutputStream */
+ public boolean isReady() {
+ return true;
+ }
+ @Override /* ServletOutputStream */
+ public void
setWriteListener(WriteListener arg0) {
+ throw new NoSuchMethodError();
+ }
+ };
+ }
+ }
+ return os;
+ }
+
+ @Override /* ServletResponse */
+ public ServletOutputStream getOutputStream() throws IOException {
+ if (os == null)
+ os = super.getOutputStream();
+ return os;
+ }
+
+ /**
+ * Returns <jk>true</jk> if {@link #getOutputStream()} has been called.
+ *
+ * @return <jk>true</jk> if {@link #getOutputStream()} has been called.
+ */
+ public boolean getOutputStreamCalled() {
+ return os != null;
+ }
+
+ /**
+ * Returns the writer to the response body.
+ *
+ * <p>
+ * This methods bypasses any specified encoders and returns a regular
unbuffered writer.
+ * Use the {@link #getNegotiatedWriter()} method if you want to use the
matched encoder (if any).
+ */
+ @Override /* ServletResponse */
+ public PrintWriter getWriter() throws IOException {
+ return getWriter(true);
+ }
+
+ /**
+ * Convenience method meant to be used when rendering directly to a
browser with no buffering.
+ *
+ * <p>
+ * Sets the header <js>"x-content-type-options=nosniff"</js> so that
output is rendered immediately on IE and Chrome
+ * without any buffering for content-type sniffing.
+ *
+ * @param contentType The value to set as the <code>Content-Type</code>
on the response.
+ * @return The raw writer.
+ * @throws IOException
+ */
+ public PrintWriter getDirectWriter(String contentType) throws
IOException {
+ setContentType(contentType);
+ setHeader("x-content-type-options", "nosniff");
+ return getWriter();
+ }
+
+ /**
+ * Equivalent to {@link HttpServletResponse#getWriter()}, except wraps
the output stream if an {@link Encoder} was
+ * found that matched the <code>Accept-Encoding</code> header and sets
the <code>Content-Encoding</code>
+ * header to the appropriate value.
+ *
+ * @return The negotiated writer.
+ * @throws IOException
+ */
+ public PrintWriter getNegotiatedWriter() throws IOException {
+ return getWriter(false);
+ }
+
+ private PrintWriter getWriter(boolean raw) throws IOException {
+ if (w != null)
+ return w;
+
+ // If plain text requested, override it now.
+ if (request.isPlainText())
+ setHeader("Content-Type", "text/plain");
+
+ try {
+ OutputStream out = (raw ? getOutputStream() :
getNegotiatedOutputStream());
+ w = new PrintWriter(new OutputStreamWriter(out,
getCharacterEncoding()));
+ return w;
+ } catch (UnsupportedEncodingException e) {
+ String ce = getCharacterEncoding();
+ setCharacterEncoding("UTF-8");
+ throw new RestException(SC_NOT_ACCEPTABLE, "Unsupported
charset in request header ''Accept-Charset'': ''{0}''", ce);
+ }
+ }
+
+ /**
+ * Returns the <code>Content-Type</code> header stripped of the charset
attribute if present.
+ *
+ * @return The <code>media-type</code> portion of the
<code>Content-Type</code> header.
+ */
+ public MediaType getMediaType() {
+ return MediaType.forString(getContentType());
+ }
+
+ /**
+ * Redirects to the specified URI.
+ *
+ * <p>
+ * Relative URIs are always interpreted as relative to the context root.
+ * This is similar to how WAS handles redirect requests, and is
different from how Tomcat handles redirect requests.
+ */
+ @Override /* ServletResponse */
+ public void sendRedirect(String uri) throws IOException {
+ char c = (uri.length() > 0 ? uri.charAt(0) : 0);
+ if (c != '/' && uri.indexOf("://") == -1)
+ uri = request.getContextPath() + '/' + uri;
+ super.sendRedirect(uri);
+ }
+
+ /**
+ * Returns the URL-encoding serializer associated with this response.
+ *
+ * @return The URL-encoding serializer associated with this response.
+ */
+ public UrlEncodingSerializer getUrlEncodingSerializer() {
+ return urlEncodingSerializer;
+ }
+
+ @Override /* ServletResponse */
+ public void setHeader(String name, String value) {
+ // Jetty doesn't set the content type correctly if set through
this method.
+ // Tomcat/WAS does.
+ if (name.equalsIgnoreCase("Content-Type"))
+ super.setContentType(value);
+ else
+ super.setHeader(name, value);
+ }
+
+ /**
+ * Sets the HTML header section contents.
+ *
+ * <p>
+ * The format of this value is HTML.
+ *
+ * <p>
+ * The page header normally contains the title and description, but
this value can be used to override the contents
+ * to be whatever you want.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no header.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#header()
@HtmlDoc.header()} annotation.
+ *
+ * @param value
+ * The HTML header section contents.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlHeader(Object value) {
+ return setProperty(HtmlDocSerializerContext.HTMLDOC_header,
value);
+ }
+
+ /**
+ * Sets the links in the HTML nav section.
+ *
+ * <p>
+ * The format of this value is a lax-JSON map of key/value pairs where
the keys are the link text and the values are
+ * relative (to the servlet) or absolute URLs.
+ *
+ * <p>
+ * The page links are positioned immediately under the title and text.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no value.
+ *
+ * <p>
+ * This field can also use URIs of any support type in {@link
UriResolver}.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#links()
@HtmlDoc.links()} annotation.
+ *
+ * @param value
+ * The HTML nav section links links.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlLinks(String[] value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_links, value);
+ return this;
+ }
+
+ /**
+ * Sets the HTML nav section contents.
+ *
+ * <p>
+ * The format of this value is HTML.
+ *
+ * <p>
+ * The nav section of the page contains the links.
+ *
+ * <p>
+ * The format of this value is HTML.
+ *
+ * <p>
+ * When a value is specified, the {@link #setHtmlLinks(String[])} value
will be ignored.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no value.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#nav()
@HtmlDoc.nav()} annotation.
+ *
+ * @param value
+ * The HTML nav section contents.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlNav(Object value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_nav, value);
+ return this;
+ }
+
+ /**
+ * Sets the HTML aside section contents.
+ *
+ * <p>
+ * The format of this value is HTML.
+ *
+ * <p>
+ * The aside section typically floats on the right side of the page.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no value.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#aside()
@HtmlDoc.aside()} annotation.
+ *
+ * @param value
+ * The HTML aside section contents.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to waste
+ * string concatenation cycles on non-HTML
views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlAside(Object value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_aside, value);
+ return this;
+ }
+
+ /**
+ * Sets the HTML footer section contents.
+ *
+ * <p>
+ * The format of this value is HTML.
+ *
+ * <p>
+ * The footer section typically floats on the bottom of the page.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no value.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#footer()
@HtmlDoc.footer()} annotation.
+ *
+ * @param value
+ * The HTML footer section contents.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlFooter(Object value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_footer, value);
+ return this;
+ }
+
+ /**
+ * Sets the HTML CSS style section contents.
+ *
+ * <p>
+ * The format of this value is CSS.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no value.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#style()
@HtmlDoc.style()} annotation.
+ *
+ * @param value
+ * The HTML CSS style section contents.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlStyle(Object value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_style, value);
+ return this;
+ }
+
+ /**
+ * Sets the CSS URL in the HTML CSS style section.
+ *
+ * <p>
+ * The format of this value is a comma-delimited list of URLs.
+ *
+ * <p>
+ * Specifies the URL to the stylesheet to add as a link in the style
tag in the header.
+ *
+ * <p>
+ * The format of this value is CSS.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>) and can use URL protocols defined
+ * by {@link UriResolver}.
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link
HtmlDoc#stylesheet() @HtmlDoc.stylesheet()} annotation.
+ *
+ * @param value
+ * The CSS URL in the HTML CSS style section.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlStylesheet(Object value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_stylesheet,
value);
+ return this;
+ }
+
+ /**
+ * Sets the HTML script section contents.
+ *
+ * <p>
+ * The format of this value is Javascript.
+ *
+ * <p>
+ * This field can contain variables (e.g.
<js>"$L{my.localized.variable}"</js>).
+ * <br>See {@link RestContext#getVarResolver()} for the list of
supported variables.
+ *
+ * <p>
+ * A value of <js>"NONE"</js> can be used to force no value.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#script()
@HtmlDoc.script()} annotation.
+ *
+ * @param value
+ * The HTML script section contents.
+ * Object will be converted to a string using {@link
Object#toString()}.
+ * <p>
+ * <ul class='doctree'>
+ * <li class='info'>
+ * <b>Tip:</b> Use {@link StringMessage} to
generate value with delayed serialization so as not to
+ * waste string concatenation cycles on
non-HTML views.
+ * </ul>
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlScript(Object value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_script, value);
+ return this;
+ }
+
+ /**
+ * Shorthand method for forcing the rendered HTML content to be no-wrap.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#nowrap()
@HtmlDoc.nowrap()} annotation.
+ *
+ * @param value The new nowrap setting.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlNoWrap(boolean value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_nowrap, value);
+ return this;
+ }
+
+ /**
+ * Specifies the text to display when serializing an empty array or
collection.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link
HtmlDoc#noResultsMessage() @HtmlDoc.noResultsMessage()}
+ * annotation.
+ *
+ * @param value The text to display when serializing an empty array or
collection.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlNoResultsMessage(Object value) {
+
properties.put(HtmlDocSerializerContext.HTMLDOC_noResultsMessage, value);
+ return this;
+ }
+
+ /**
+ * Specifies the template class to use for rendering the HTML page.
+ *
+ * <p>
+ * By default, uses {@link HtmlDocTemplateBasic} to render the
contents, although you can provide your own custom
+ * renderer or subclasses from the basic class to have full control
over how the page is rendered.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#template()
@HtmlDoc.template()} annotation.
+ *
+ * @param value The HTML page template to use to render the HTML page.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlTemplate(Class<? extends HtmlDocTemplate>
value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_template,
value);
+ return this;
+ }
+
+ /**
+ * Specifies the template class to use for rendering the HTML page.
+ *
+ * <p>
+ * By default, uses {@link HtmlDocTemplateBasic} to render the
contents, although you can provide your own custom
+ * renderer or subclasses from the basic class to have full control
over how the page is rendered.
+ *
+ * <p>
+ * This is the programmatic equivalent to the {@link HtmlDoc#template()
@HtmlDoc.template()} annotation.
+ *
+ * @param value The HTML page template to use to render the HTML page.
+ * @return This object (for method chaining).
+ */
+ public RestResponse setHtmlTemplate(HtmlDocTemplate value) {
+ properties.put(HtmlDocSerializerContext.HTMLDOC_template,
value);
+ return this;
+ }
+
+ @Override /* ServletResponse */
+ public void flushBuffer() throws IOException {
+ if (w != null)
+ w.flush();
+ if (os != null)
+ os.flush();
+ super.flushBuffer();
+ }
+}
\ No newline at end of file
Propchange:
release/incubator/juneau/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
------------------------------------------------------------------------------
svn:mime-type = text/plain