All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.rest.RestContext Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
// * with the License.  You may obtain a copy of the License at                                                              *
// *                                                                                                                         *
// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
// *                                                                                                                         *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
// * specific language governing permissions and limitations under the License.                                              *
// ***************************************************************************************************************************
package org.apache.juneau.rest;

import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
import static org.apache.juneau.internal.IOUtils.*;
import static org.apache.juneau.internal.StringUtils.*;

import java.io.*;
import java.lang.reflect.*;
import java.lang.reflect.Method;
import java.nio.charset.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;

import javax.activation.*;
import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.juneau.*;
import org.apache.juneau.config.*;
import org.apache.juneau.encoders.*;
import org.apache.juneau.html.*;
import org.apache.juneau.http.*;
import org.apache.juneau.http.annotation.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.httppart.bean.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.msgpack.*;
import org.apache.juneau.oapi.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.plaintext.*;
import org.apache.juneau.remote.*;
import org.apache.juneau.rest.annotation.*;
import org.apache.juneau.rest.converters.*;
import org.apache.juneau.rest.exception.*;
import org.apache.juneau.rest.helper.*;
import org.apache.juneau.rest.reshandlers.*;
import org.apache.juneau.rest.util.*;
import org.apache.juneau.rest.vars.*;
import org.apache.juneau.rest.widget.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.soap.*;
import org.apache.juneau.svl.*;
import org.apache.juneau.uon.*;
import org.apache.juneau.urlencoding.*;
import org.apache.juneau.utils.*;
import org.apache.juneau.xml.*;
import org.apache.juneau.xmlschema.*;

/**
 * Contains all the configuration on a REST resource and the entry points for handling REST calls.
 *
 * 
See Also:
*
    *
*/ public final class RestContext extends BeanContext { //------------------------------------------------------------------------------------------------------------------- // Configurable properties //------------------------------------------------------------------------------------------------------------------- private static final String PREFIX = "RestContext."; /** * Configuration property: Allow body URL parameter. * *
Property:
*
    *
  • Name: "RestContext.allowBodyParam.b" *
  • Data type: Boolean *
  • Default: true *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#allowBodyParam()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#allowBodyParam(boolean)} *
    *
* *
Description:
*

* When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the "body" * URL parameter. *
* For example: *

* ?body=(name='John%20Smith',age=45) *

* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(allowBodyParam="$C{REST/allowBodyParam,false}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.allowBodyParam(false); * * // Same, but using property. * builder.set(REST_allowBodyParam, false); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.allowBodyParam(false); * } * } *

* *
Notes:
*
    *
  • * 'body' parameter name is case-insensitive. *
  • * Useful for debugging PUT and POST methods using only a browser. *
*/ public static final String REST_allowBodyParam = PREFIX + "allowBodyParam.b"; /** * Configuration property: Allowed method parameters. * *
Property:
*
    *
  • Name: "RestContext.allowedMethodParams.s" *
  • Data type: String *
  • Default: "HEAD,OPTIONS" *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#allowedMethodParams()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#allowedMethodParams(String...)} *
    *
* *
Description:
*

* When specified, the HTTP method can be overridden by passing in a "method" URL parameter on a regular * GET request. *
* For example: *

* ?method=OPTIONS *

* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(allowMethodParams="$C{REST/allowMethodParams,HEAD\,OPTIONS\,PUT}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.allowMethodParams("HEAD,OPTIONS,PUT"); * * // Same, but using property. * builder.set(REST_allowMethodParams, "HEAD,OPTIONS,PUT"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.allowMethodParams("HEAD", "OPTIONS", "PUT"); * } * } *

* *
Notes:
*
    *
  • * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter. *
  • * 'method' parameter name is case-insensitive. *
  • * Use "*" to represent all methods. *
* *

* Note that per the {@doc RFC2616.section9 HTTP specification}, special care should * be taken when allowing non-safe (POST, PUT, DELETE) methods to be invoked through GET requests. */ public static final String REST_allowedMethodParams = PREFIX + "allowedMethodParams.s"; /** * Configuration property: Allow header URL parameters. * *

Property:
*
    *
  • Name: "RestContext.allowHeaderParams.b" *
  • Data type: Boolean *
  • Default: true *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#allowHeaderParams()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#allowHeaderParams(boolean)} *
    *
* *
Description:
*

* When enabled, headers such as "Accept" and "Content-Type" to be passed in as URL query * parameters. *
* For example: *

* ?Accept=text/json&Content-Type=text/json *

* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(allowMethodParams="$C{REST/allowHeaderParams,false}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.allowHeaderParams(false); * * // Same, but using property. * builder.set(REST_allowHeaderParams, false); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.allowHeaderParams(false); * } * } *

* *
Notes:
*
    *
  • * Header names are case-insensitive. *
  • * Useful for debugging REST interface using only a browser. *
*/ public static final String REST_allowHeaderParams = PREFIX + "allowHeaderParams.b"; /** * Configuration property: REST call handler. * *
Property:
*
    *
  • Name: "RestContext.callHandler.o" *
  • Data type: {@link RestCallHandler} | Class<? extends {@link RestCallHandler}> *
  • Default: {@link BasicRestCallHandler} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#callHandler()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#callHandler(Class)} *
    • {@link RestContextBuilder#callHandler(RestCallHandler)} *
    *
* *
Description:
*

* This class handles the basic lifecycle of an HTTP REST call. *
Subclasses can be used to customize how these HTTP calls are handled. * *

Example:
*

* // Our customized call handler. * public class MyRestCallHandler extends BasicRestCallHandler { * * // Must provide this constructor! * public MyRestCallHandler(RestContext context) { * super(context); * } * * @Override * public RestRequest createRequest(HttpServletRequest req) throws ServletException { * // Low-level handling of requests. * ... * } * * @Override * public void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException { * // Low-level handling of responses. * ... * } * * @Override * public void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception { * // Low-level handling of various error conditions. * ... * } * } * * // Option #1 - Registered via annotation resolving to a config file setting with default value. * @RestResource(callHandler=MyRestCallHandler.class) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.callHandler(MyRestCallHandler.class); * * // Same, but using property. * builder.set(REST_callHandler, MyRestCallHandler.class); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.callHandler(MyRestCallHandler.class); * } * } *

* *
Notes:
*
    *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(RestContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
*/ public static final String REST_callHandler = PREFIX + "callHandler.o"; /** * Configuration property: Children. * *
Property:
*
    *
  • Name: "RestContext.children.lo" *
  • Data type: List<Class | Object | {@link RestChild}> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#children()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#child(String,Object)} *
    • {@link RestContextBuilder#children(Class...)} *
    • {@link RestContextBuilder#children(Object...)} *
    *
* *
Description:
*

* Defines children of this resource. * *

* A REST child resource is simply another servlet or object that is initialized as part of the ascendant resource and has a * servlet path directly under the ascendant resource object path. *
The main advantage to defining servlets as REST children is that you do not need to define them in the * web.xml file of the web application. *
This can cut down on the number of entries that show up in the web.xml file if you are defining * large numbers of servlets. * *

* Child resources must specify a value for {@link RestResource#path() @RestResource(path)} that identifies the subpath of the child resource * relative to the ascendant path UNLESS you use the {@link RestContextBuilder#child(String, Object)} method to register it. * *

* Child resources can be nested arbitrarily deep using this technique (i.e. children can also have children). * *

*
Servlet initialization:
*
*

* A child resource will be initialized immediately after the ascendant servlet/resource is initialized. *
The child resource receives the same servlet config as the ascendant servlet/resource. *
This allows configuration information such as servlet initialization parameters to filter to child * resources. *

*
*
Runtime behavior:
*
*

* As a rule, methods defined on the HttpServletRequest object will behave as if the child * servlet were deployed as a top-level resource under the child's servlet path. *
For example, the getServletPath() and getPathInfo() methods on the * HttpServletRequest object will behave as if the child resource were deployed using the * child's servlet path. *
Therefore, the runtime behavior should be equivalent to deploying the child servlet in the * web.xml file of the web application. *

*
*
* *
Example:
*

* // Our child resource. * @RestResource(path="/child") * public class MyChildResource {...} * * // Option #1 - Registered via annotation. * @RestResource(children={MyChildResource.class}) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.children(MyChildResource.class); * * // Same, but using property. * builder.addTo(REST_children, MyChildResource.class)); * * // Use a pre-instantiated object instead. * builder.child("/child", new MyChildResource()); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.children(MyChildResource.class); * } * } * *

Notes:
*
    *
  • * When defined as classes, instances are resolved using the registered {@link #REST_resourceResolver} which * by default is {@link BasicRestResourceResolver} which requires the class have one of the following * constructors: *
      *
    • public T(RestContextBuilder) *
    • public T() *
    *
* *
See Also:
*
    *
*/ public static final String REST_children = PREFIX + "children.lo"; /** * Configuration property: Classpath resource finder. * *
Property:
*
    *
  • Name: "RestContext.classpathResourceFinder.o" *
  • Data type: {@link ClasspathResourceFinder} *
  • Default: {@link ClasspathResourceFinderBasic} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#classpathResourceFinder()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#classpathResourceFinder(Class)} *
    • {@link RestContextBuilder#classpathResourceFinder(ClasspathResourceFinder)} *
    *
* *
Description:
*

* Used to retrieve localized files from the classpath. * *

* Used by the following methods: *

    *
  • {@link RestContext} *
      *
    • {@link #getClasspathResource(String,Locale) getClasspathResource(String,Locale)} *
    • {@link #getClasspathResource(Class,String,Locale) getClasspathResource(Class,String,Locale)} *
    • {@link #getClasspathResource(Class,MediaType,String,Locale) getClasspathResource(Class,MediaType,String,Locale)} *
    • {@link #getClasspathResource(Class,Class,MediaType,String,Locale) getClasspathResource(Class,Class,MediaType,String,Locale)} *
    • {@link #getClasspathResourceAsString(String,Locale) getClasspathResourceAsString(String,Locale)} *
    • {@link #getClasspathResourceAsString(Class,String,Locale) getClasspathResourceAsString(Class,String,Locale)} *
    • {@link #resolveStaticFile(String) resolveStaticFile(String)} *
    *
  • {@link RestRequest} *
      *
    • {@link RestRequest#getClasspathReaderResource(String) getClasspathReaderResource(String)} *
    • {@link RestRequest#getClasspathReaderResource(String,boolean) getClasspathReaderResource(String,boolean)} *
    • {@link RestRequest#getClasspathReaderResource(String,boolean,MediaType) getClasspathReaderResource(String,boolean,MediaType)} *
    *
* *

* It also affects the behavior of the {@link #REST_staticFiles} property. * *

Example:
*

* // Our customized classpath resource finder. * public class MyClasspathResourceFinder extends ClasspathResourceFinderBasic { * @Override * public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException { * // Do your own resolution. * } * } * * // Option #1 - Registered via annotation. * @RestResource(classpathResourceFinder=MyClasspathResourceFinder.class) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.classpathResourceFinder(MyClasspathResourceFinder.class); * * // Same, but using property. * builder.set(REST_classpathResourceFinder, MyClasspathResourceFinder.class)); * * // Use a pre-instantiated object instead. * builder.classpathResourceFinder(new MyClasspathResourceFinder()); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.classpathResourceFinder(MyClasspathResourceFinder.class); * } * } * *

Notes:
*
    *
  • * The default value is {@link ClasspathResourceFinderBasic} which provides basic support for finding localized * resources on the classpath and JVM working directory. *
    The {@link ClasspathResourceFinderRecursive} is another option that also recursively searches for resources * up the class-hierarchy. *
    Each of these classes can be extended to provide customized handling of resource retrieval. *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(RestContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
*/ public static final String REST_classpathResourceFinder = PREFIX + "classpathResourceFinder.o"; /** * Configuration property: Client version header. * *
Property:
*
    *
  • Name: "RestContext.clientVersionHeader.s" *
  • Data type: String *
  • Default: "X-Client-Version" *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#clientVersionHeader()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#clientVersionHeader(String)} *
    *
* *
Description:
*

* Specifies the name of the header used to denote the client version on HTTP requests. * *

* The client version is used to support backwards compatibility for breaking REST interface changes. *
Used in conjunction with {@link RestMethod#clientVersion() @RestMethod(clientVersion)} annotation. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(clientVersionHeader="$C{REST/clientVersionHeader,Client-Version}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.clientVersionHeader("Client-Version"); * * // Same, but using property. * builder.set(REST_clientVersionHeader, "Client-Version"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.clientVersionHeader("Client-Version"); * } * } *

*

* // Call this method if Client-Version is at least 2.0. * // Note that this also matches 2.0.1. * @RestMethod(name=GET, path="/foobar", clientVersion="2.0") * public Object method1() { * ... * } * * // Call this method if Client-Version is at least 1.1, but less than 2.0. * @RestMethod(name=GET, path="/foobar", clientVersion="[1.1,2.0)") * public Object method2() { * ... * } * * // Call this method if Client-Version is less than 1.1. * @RestMethod(name=GET, path="/foobar", clientVersion="[0,1.1)") * public Object method3() { * ... * } *

*/ public static final String REST_clientVersionHeader = PREFIX + "clientVersionHeader.s"; /** * Configuration property: Class-level response converters. * *
Property:
*
    *
  • Name: "RestContext.converters.lo" *
  • Data type: List<{@link RestConverter} | Class<? extends {@link RestConverter}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#converters()} *
    • {@link RestMethod#converters()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#converters(Class...)} *
    • {@link RestContextBuilder#converters(RestConverter...)} *
    *
* *
Description:
*

* Associates one or more {@link RestConverter converters} with a resource class. *
These converters get called immediately after execution of the REST method in the same order specified in the * annotation. *
The object passed into this converter is the object returned from the Java method or passed into * the {@link RestResponse#setOutput(Object)} method. * *

* Can be used for performing post-processing on the response object before serialization. * *

* When multiple converters are specified, they're executed in the order they're specified in the annotation * (e.g. first the results will be traversed, then the resulting node will be searched/sorted). * *

Example:
*

* // Our converter. * public class MyConverter implements RestConverter { * @Override * public Object convert(RestRequest req, Object o) { * // Do something with object and return another object. * // Or just return the same object for a no-op. * } * } * * // Option #1 - Registered via annotation resolving to a config file setting with default value. * @RestResource(converters={MyConverter.class}) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.converters(MyConverter.class); * * // Same, but using property. * builder.set(REST_converters, MyConverter.class); * * // Pass in an instance instead. * builder.converters(new MyConverter()); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.converters(MyConverter.class); * } * } *

* *
See Also:
*
    *
  • {@link Traversable} - Allows URL additional path info to address individual elements in a POJO tree. *
  • {@link Queryable} - Allows query/view/sort functions to be performed on POJOs. *
  • {@link Introspectable} - Allows Java public methods to be invoked on the returned POJOs. *
* *
See Also:
*
    *
* *
Notes:
*
    *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(BeanContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
*/ public static final String REST_converters = PREFIX + "converters.lo"; /** * Configuration property: Debug mode. * *
Property:
*
    *
  • Name: "RestContext.debug.b" *
  • Data type: Boolean *
  • Default: false *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#debug()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#debug(boolean)} *
    *
* *
Description:
*

* Enables the following: *

    *
  • A message and stack trace is printed to STDERR when {@link BasicRestCallHandler#handleError(HttpServletRequest, HttpServletResponse, Throwable)} is called. *
*/ public static final String REST_debug = PREFIX + "debug.b"; /** * Configuration property: Default character encoding. * *
Property:
*
    *
  • Name: "RestContext.defaultCharset.s" *
  • Data type: String *
  • Default: "utf-8" *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#defaultCharset()} *
    • {@link RestMethod#defaultCharset()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#defaultCharset(String)} *
    • {@link RestContextBuilder#defaultCharset(Charset)} *
    *
* *
Description:
*

* The default character encoding for the request and response if not specified on the request. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(defaultCharset="$C{REST/defaultCharset,US-ASCII}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.defaultCharset("US-ASCII"); * * // Same, but using property. * builder.set(REST_defaultCharset, "US-ASCII"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.defaultCharset("US-ASCII"); * } * * // Override at the method level. * @RestMethod(defaultCharset="UTF-16") * public Object myMethod() {...} * } *

*/ public static final String REST_defaultCharset = PREFIX + "defaultCharset.s"; /** * Configuration property: Default request headers. * *
Property:
*
    *
  • Name: "RestContext.defaultRequestHeaders.smo" *
  • Data type: Map<String,String> *
  • Default: empty map *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#defaultRequestHeaders()} *
    • {@link RestMethod#defaultRequestHeaders()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#defaultRequestHeader(String,Object)} *
    • {@link RestContextBuilder#defaultRequestHeaders(String...)} *
    *
* *
Description:
*

* Specifies default values for request headers if they're not passed in through the request. * *

Notes:
*
    *
  • * Strings are in the format "Header-Name: header-value". *
  • * Affects values returned by {@link RestRequest#getHeader(String)} when the header is not present on the request. *
  • * The most useful reason for this annotation is to provide a default Accept header when one is not * specified so that a particular default {@link Serializer} is picked. *
* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(defaultRequestHeaders={"Accept: application/json", "My-Header: $C{REST/myHeaderValue}"}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder * .defaultRequestHeader("Accept", "application/json"); * .defaultRequestHeaders("My-Header: foo"); * * // Same, but using property. * builder.addTo(REST_defaultRequestHeaders, "Accept", "application/json"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.defaultRequestHeader("Accept", "application/json"); * } * * // Override at the method level. * @RestMethod(defaultRequestHeaders={"Accept: text/xml"}) * public Object myMethod() {...} * } *

*/ public static final String REST_defaultRequestHeaders = PREFIX + "defaultRequestHeaders.smo"; /** * Configuration property: Default response headers. * *
Property:
*
    *
  • Name: "RestContext.defaultResponseHeaders.omo" *
  • Data type: Map<String,String> *
  • Default: empty map *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#defaultResponseHeaders()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#defaultResponseHeader(String,Object)} *
    • {@link RestContextBuilder#defaultResponseHeaders(String...)} *
    *
* *
Description:
*

* Specifies default values for response headers if they're not set after the Java REST method is called. * *

Notes:
*
    *
  • * Strings are in the format "Header-Name: header-value". *
  • * This is equivalent to calling {@link RestResponse#setHeader(String, String)} programmatically in each of * the Java methods. *
  • * The header value will not be set if the header value has already been specified (hence the 'default' in the name). *
* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(defaultResponseHeaders={"Content-Type: $C{REST/defaultContentType,text/plain}","My-Header: $C{REST/myHeaderValue}"}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder * .defaultResponseHeader("Content-Type", "text/plain"); * .defaultResponseHeaders("My-Header: foo"); * * // Same, but using property. * builder * .addTo(REST_defaultRequestHeaders, "Accept", "application/json"); * .addTo(REST_defaultRequestHeaders, "My-Header", "foo"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.defaultResponseHeader("Content-Type", "text/plain"); * } * } *

*/ public static final String REST_defaultResponseHeaders = PREFIX + "defaultResponseHeaders.omo"; /** * Configuration property: Compression encoders. * *
Property:
*
    *
  • Name: "RestContext.encoders.o" *
  • Data type: List<{@link Encoder} | Class<? extends {@link Encoder}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#encoders()} *
    • {@link RestMethod#encoders()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#encoders(Class...)} *
    • {@link RestContextBuilder#encoders(Encoder...)} *
    *
* *
Description:
*

* These can be used to enable various kinds of compression (e.g. "gzip") on requests and responses. * *

Example:
*

* // Option #1 - Registered via annotation. * @RestResource(encoders={GzipEncoder.class}) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.encoders(GzipEncoder.class); * * // Same, but using property. * builder.addTo(REST_encoders, GzipEncoder.class); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.encoders(GzipEncoder.class); * } * * // Override at the method level. * @RestMethod(encoders={MySpecialEncoder.class}, inherit={"ENCODERS"}) * public Object myMethod() {...} * } *

* *
Notes:
*
    *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(BeanContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
* *
See Also:
*
    *
*/ public static final String REST_encoders = PREFIX + "encoders.lo"; /** * Configuration property: Class-level guards. * *
Property:
*
    *
  • Name: "RestContext.guards.lo" *
  • Data type: List<{@link RestGuard} | Class<? extends {@link RestGuard}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#guards()} *
    • {@link RestMethod#guards()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#guards(Class...)} *
    • {@link RestContextBuilder#guards(RestGuard...)} *
    *
* *
Description:
*

* Associates one or more {@link RestGuard RestGuards} with all REST methods defined in this class. *
These guards get called immediately before execution of any REST method in this class. * *

* If multiple guards are specified, ALL guards must pass. *
Note that this is different than matchers were only ONE matcher needs to pass. * *

Example:
*

* // Define a guard that only lets Billy make a request. * public BillyGuard extends RestGuard { * @Override * public boolean isRequestAllowed(RestRequest req) { * return req.getUserPrincipal().getName().equals("Billy"); * } * } * * // Option #1 - Registered via annotation. * @RestResource(guards={BillyGuard.class}) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.guards(BillyGuard.class); * * // Same, but using property. * builder.addTo(REST_guards, BillyGuard.class); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.guards(BillyGuard.class); * } * * // Override at the method level. * @RestMethod(guards={SomeOtherGuard.class}) * public Object myMethod() {...} * } *

* *
Notes:
*
    *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(RestContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
* *
See Also:
*
    *
*/ public static final String REST_guards = PREFIX + "guards.lo"; /** * Configuration property: REST info provider. * *
Property:
*
    *
  • Name: "RestContext.infoProvider.o" *
  • Data type: {@link RestInfoProvider} | Class<? extends {@link RestInfoProvider}> *
  • Default: {@link BasicRestInfoProvider} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#infoProvider()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#infoProvider(Class)} *
    • {@link RestContextBuilder#infoProvider(RestInfoProvider)} *
    *
* *
Description:
*

* Class used to retrieve title/description/swagger information about a resource. * *

Example:
*

* // Our customized info provider. * // Extend from the default implementation and selectively override values. * public class MyRestInfoProvider extends BasicRestInfoProvider { * * // Must provide this constructor! * public MyRestInfoProvider(RestContext context) { * super(context); * } * * @Override * public Swagger getSwaggerFromFile(RestRequest req) throws RestException { * // Provide our own method of retrieving swagger from file system. * } * * @Override * public Swagger getSwagger(RestRequest req) throws RestException { * Swagger s = super.getSwagger(req); * // Made inline modifications to generated swagger. * return s; * } * * @Override * public String getSiteName(RestRequest req) { * // Override the site name. * } * } * * // Option #1 - Registered via annotation resolving to a config file setting with default value. * @RestResource(infoProvider=MyRestInfoProvider.class) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.infoProvider(MyRestInfoProvider.class); * * // Same, but using property. * builder.set(REST_infoProvider, MyRestInfoProvider.class); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.infoProvider(MyRestInfoProvider.class); * } * } *

* *
Notes:
*
    *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(RestContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
*/ public static final String REST_infoProvider = PREFIX + "infoProvider.o"; /** * Configuration property: REST logger. * *
Property:
*
    *
  • Name: "RestContext.logger.o" *
  • Data type: {@link RestLogger} | Class<? extends {@link RestLogger}> *
  • Default: {@link BasicRestLogger} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#logger()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#logger(Class)} *
    • {@link RestContextBuilder#logger(RestLogger)} *
    *
* *
Description:
*

* Specifies the logger to use for logging. * *

* Two implementations are provided by default: *

    *
  • {@link BasicRestLogger} - Default logging. *
  • {@link NoOpRestLogger} - Logging disabled. *
* *

* Loggers are accessible through the following: *

    *
  • {@link RestContext#getLogger() RestContext.getLogger()} *
  • {@link RestRequest#getLogger() RestRequest.getLogger()} *
* *
Example:
*

* // Our customized logger. * public class MyRestLogger extends BasicRestLogger { * * @Override * public void log(Level level, Throwable cause, String msg, Object...args) { * // Handle logging ourselves. * } * } * * // Option #1 - Registered via annotation resolving to a config file setting with default value. * @RestResource(logger=MyRestLogger.class) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.logger(MyRestLogger.class); * * // Same, but using property. * builder.set(REST_logger, MyRestLogger.class); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.logger(MyRestLogger.class); * } * } *

* *
See Also:
*
    *
*/ public static final String REST_logger = PREFIX + "logger.o"; /** * Configuration property: The maximum allowed input size (in bytes) on HTTP requests. * *
Property:
*
    *
  • Name: "RestContext.maxInput.s" *
  • Data type: String *
  • Default: "100M" *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#maxInput()} *
    • {@link RestMethod#maxInput()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#maxInput(String)} *
    *
* *
Description:
*

* Useful for alleviating DoS attacks by throwing an exception when too much input is received instead of resulting * in out-of-memory errors which could affect system stability. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(maxInput="$C{REST/maxInput,10M}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.maxInput("10M"); * * // Same, but using property. * builder.set(REST_maxInput, "10M"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.maxInput("10M"); * } * * // Override at the method level. * @RestMethod(maxInput="10M") * public Object myMethod() {...} * } *

* *
Notes:
*
    *
  • * String value that gets resolved to a long. *
  • * Can be suffixed with any of the following representing kilobytes, megabytes, and gigabytes: * 'K', 'M', 'G'. *
  • * A value of "-1" can be used to represent no limit. *
*/ public static final String REST_maxInput = PREFIX + "maxInput.s"; /** * Configuration property: Messages. * *
Property:
*
    *
  • Name: "RestContext.messages.lo" *
  • Data type: List<{@link MessageBundleLocation}> *
  • Default: null *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#messages()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#messages(String)}, *
    • {@link RestContextBuilder#messages(Class,String)} *
    • {@link RestContextBuilder#messages(MessageBundleLocation...)} *
    *
* *
Description:
*

* Identifies the location of the resource bundle for this class. * *

* This annotation is used to provide localized messages for the following methods: *

    *
  • {@link RestRequest#getMessage(String, Object...)} *
  • {@link RestContext#getMessages() RestContext.getMessages()} *
* *

* Messages are also available by passing either of the following parameter types into your Java method: *

    *
  • {@link ResourceBundle} - Basic Java resource bundle. *
  • {@link MessageBundle} - Extended resource bundle with several convenience methods. *
* *

* Messages passed into Java methods already have their locale set to that of the incoming request. * *

* The value can be a relative path like "nls/Messages", indicating to look for the resource bundle * "com.foo.sample.nls.Messages" if the resource class is in "com.foo.sample", or it can be an * absolute path like "com.foo.sample.nls.Messages" * *

Examples:
*

* package org.apache.foo; * * // Resolve messages to org/apache/foo/nls/MyMessages.properties * @RestResource(messages="nls/MyMessages") * public class MyResource {...} * * @RestMethod(name="GET", path="/hello/{you}") * public Object helloYou(RestRequest req, MessageBundle messages, @Path("name") String you)) { * String s; * * // Get it from the RestRequest object. * s = req.getMessage("HelloMessage", you); * * // Or get it from the method parameter. * s = messages.getString("HelloMessage", you); * * // Or get the message in a locale different from the request. * s = messages.getString(Locale.UK, "HelloMessage", you); * * return s; * } * } *

* *
Notes:
*
    *
  • Mappings are cumulative from super classes. *
    Therefore, you can find and retrieve messages up the class-hierarchy chain. *
* *
See Also:
*
    *
*/ public static final String REST_messages = PREFIX + "messages.lo"; /** * Configuration property: MIME types. * *
Property:
*
    *
  • Name: "RestContext.mimeTypes.ss" *
  • Data type: Set<String> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#mimeTypes()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#mimeTypes(String...)} *
    *
* *
Description:
*

* Defines MIME-type file type mappings. * *

* Used for specifying the content type on file resources retrieved through the following methods: *

    *
  • {@link RestContext#resolveStaticFile(String) RestContext.resolveStaticFile(String)} *
  • {@link RestRequest#getClasspathReaderResource(String,boolean,MediaType)} *
  • {@link RestRequest#getClasspathReaderResource(String,boolean)} *
  • {@link RestRequest#getClasspathReaderResource(String)} *
* *

* This list appends to the existing list provided by {@link ExtendedMimetypesFileTypeMap}. * *

Example:
*

* // Option #1 - Defined via annotation. * @RestResource(mimeTypes={"text/plain txt text TXT"}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.mimeTypes("text/plain txt text TXT"); * * // Same, but using property. * builder.addTo(REST_mimeTypes, "text/plain txt text TXT"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.mimeTypes("text/plain txt text TXT"); * } * } *

* *
Notes:
*
    *
  • * Values are .mime.types formatted entry string. *
    Example: "image/svg+xml svg" *
*/ public static final String REST_mimeTypes = PREFIX + "mimeTypes.ss"; /** * Configuration property: Java method parameter resolvers. * *
Property:
*
    *
  • Name: "RestContext.paramResolvers.lo" *
  • Data type: List<{@link RestMethodParam} | Class<? extends {@link RestMethodParam}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#paramResolvers()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#paramResolvers(Class...)} *
    • {@link RestContextBuilder#paramResolvers(RestMethodParam...)} *
    *
* *
Description:
*

* By default, the Juneau framework will automatically Java method parameters of various types (e.g. * RestRequest, Accept, Reader). * This setting allows you to provide your own resolvers for your own class types that you want resolved. * *

* For example, if you want to pass in instances of MySpecialObject to your Java method, define * the following resolver: *

* // Define a parameter resolver for resolving MySpecialObject objects. * public class MyRestParam extends RestMethodParam { * * // Must have no-arg constructor! * public MyRestParam() { * // First two parameters help with Swagger doc generation. * super(QUERY, "myparam", MySpecialObject.class); * } * * // The method that creates our object. * // In this case, we're taking in a query parameter and converting it to our object. * public Object resolve(RestRequest req, RestResponse res) throws Exception { * return new MySpecialObject(req.getQuery().get("myparam")); * } * } * * // Option #1 - Registered via annotation. * @RestResource(paramResolvers=MyRestParam.class) * public class MyResource { * * // Option #2 - Registered via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.paramResolvers(MyRestParam.class); * * // Same, but using property. * builder.addTo(REST_paramResolver, MyRestParam.class); * } * * // Option #3 - Registered via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.paramResolvers(MyRestParam.class); * } * * // Now pass it into your method. * @RestMethod(...) * public Object doMyMethod(MySpecialObject mySpeciaObject) { * // Do something with it. * } * } *

* *
Notes:
*
    *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(BeanContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
  • * Refer to {@link RestMethodParam} for the list of predefined parameter resolvers. *
*/ public static final String REST_paramResolvers = PREFIX + "paramResolvers.lo"; /** * Configuration property: Parsers. * *
Property:
*
    *
  • Name: "RestContext.parsers.lo" *
  • Data type: List<{@link Parser} | Class<? extends {@link Parser}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#parsers()} *
    • {@link RestMethod#parsers()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#parsers(Object...)} *
    • {@link RestContextBuilder#parsers(Class...)} *
    • {@link RestContextBuilder#parsers(boolean,Object...)} *
    *
* *
Description:
*

* Adds class-level parsers to this resource. * *

* Parsers are used to convert the body of HTTP requests into POJOs. *
Any of the Juneau framework parsers can be used in this setting. *
The parser selected is based on the request Content-Type header matched against the values returned by the following method * using a best-match algorithm: *

    *
  • {@link Parser#getMediaTypes()} *
* *
Example:
*

* // Option #1 - Defined via annotation. * @RestResource(parsers={JsonParser.class, XmlParser.class}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.parsers(JsonParser.class, XmlParser.class); * * // Same, but use pre-instantiated parsers. * builder.parsers(JsonParser.DEFAULT, XmlParser.DEFAULT); * * // Same, but using property. * builder.set(REST_parsers, JsonParser.class, XmlParser.class); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.parsers(JsonParser.class, XmlParser.class); * } * * // Override at the method level. * @RestMethod(parsers={HtmlParser.class}) * public Object myMethod(@Body MyPojo myPojo) { * // Do something with your parsed POJO. * } * } *

* *
Notes:
*
    *
  • * When defined as a class, properties/transforms defined on the resource/method are inherited. *
  • * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. *
  • * Typically, you'll want your resource to extend directly from {@link BasicRestServlet} which comes * preconfigured with the following parsers: *
      *
    • {@link JsonParser} *
    • {@link XmlParser} *
    • {@link HtmlParser} *
    • {@link UonParser} *
    • {@link UrlEncodingParser} *
    • {@link MsgPackParser} *
    • {@link PlainTextParser} *
    *
* *
See Also:
*
    *
*/ public static final String REST_parsers = PREFIX + "parsers.lo"; /** * Configuration property: HTTP part parser. * *
Property:
*
    *
  • Name: "RestContext.partParser.o" *
  • Data type: {@link HttpPartParser} | Class<? extends {@link HttpPartParser}> *
  • Default: {@link OpenApiParser} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#partParser()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#partParser(Class)} *
    • {@link RestContextBuilder#partParser(HttpPartParser)} *
    *
* *
Description:
*

* Specifies the {@link HttpPartParser} to use for parsing headers, query/form parameters, and URI parts. * *

* The default value is {@link OpenApiParser} which allows for both plain-text and URL-Encoded-Object-Notation values. *
If your parts contain text that can be confused with UON (e.g. "(foo)"), you can switch to * {@link SimplePartParser} which treats everything as plain text. * *

Example:
*

* // Option #1 - Defined via annotation. * @RestResource(partParser=SimplePartParser.class) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.partParser(SimplePartParser.class); * * // Same, but using property. * builder.set(REST_partParser, SimplePartParser.class); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.partParser(SimplePartParser.class); * } * * @RestMethod(...) * public Object myMethod(@Header("My-Header") MyParsedHeader h, @Query("myquery") MyParsedQuery q) { * // Do something with your parsed parts. * } * } *

* *
Notes:
*
    *
  • * When defined as a class, properties/transforms defined on the resource/method are inherited. *
  • * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. *
*/ public static final String REST_partParser = PREFIX + "partParser.o"; /** * Configuration property: HTTP part serializer. * *
Property:
*
    *
  • Name: "RestContext.partSerializer.o" *
  • Data type: {@link HttpPartSerializer} | Class<? extends {@link HttpPartSerializer}> *
  • Default: {@link OpenApiSerializer} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#partSerializer()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#partSerializer(Class)} *
    • {@link RestContextBuilder#partSerializer(HttpPartSerializer)} *
    *
* *
Description:
*

* Specifies the {@link HttpPartSerializer} to use for serializing headers, query/form parameters, and URI parts. * *

* The default value is {@link OpenApiSerializer} which serializes based on OpenAPI rules, but defaults to UON notation for beans and maps, and * plain text for everything else. *
Other options include: *

    *
  • {@link SimplePartSerializer} - Always serializes to plain text. *
  • {@link UonSerializer} - Always serializers to UON. *
* *
Example:
*

* // Option #1 - Defined via annotation. * @RestResource(partSerializer=SimplePartSerializer.class) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.partSerializer(SimplePartSerializer.class); * * // Same, but using property. * builder.set(REST_partSerializer, SimplePartSerializer.class); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.partSerializer(SimplePartSerializer.class); * } * * @RestMethod(...) * public Object myMethod(RestResponse res) { * // Set a header to a POJO. * res.setHeader("My-Header", new MyPojo()); * } * } *

* *
Notes:
*
    *
  • * When defined as a class, properties/transforms defined on the resource/method are inherited. *
  • * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. *
*/ public static final String REST_partSerializer = PREFIX + "partSerializer.o"; /** * Configuration property: Resource path. * *
Property:
*
    *
  • Name: "RestContext.path.s" *
  • Data type: String *
  • Default: null *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#path()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#path(String)} *
    *
* *
Description:
*

* Identifies the URL subpath relative to the ascendant resource. * *

* This setting is critical for the routing of HTTP requests from ascendant to child resources. * *

Example:
*

* // Option #1 - Defined via annotation. * @RestResource(path="/myResource") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.path("/myResource"); * * // Same, but using property. * builder.set(REST_path, "/myResource"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.path("/myResource"); * } * } *

* *

*

Notes:
*
    *
  • * This annotation is ignored on top-level servlets (i.e. servlets defined in web.xml files). *
    Therefore, implementers can optionally specify a path value for documentation purposes. *
  • * Typically, this setting is only applicable to resources defined as children through the * {@link RestResource#children() @RestResource(children)} annotation. *
    However, it may be used in other ways (e.g. defining paths for top-level resources in microservices). *
  • * Slashes are trimmed from the path ends. *
    As a convention, you may want to start your path with '/' simple because it make it easier to read. *
  • * This path is available through the following method: *
      *
    • {@link RestContext#getPath() RestContext.getPath()} *
    *
*/ public static final String REST_path = PREFIX + "path.s"; /** * Configuration property: Render response stack traces in responses. * *
Property:
*
    *
  • Name: "RestContext.renderResponseStackTraces.b" *
  • Data type: Boolean *
  • Default: false *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#renderResponseStackTraces()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#renderResponseStackTraces(boolean)} *
    • {@link RestContextBuilder#renderResponseStackTraces()} *
    *
* *
Description:
*

* Render stack traces in HTTP response bodies when errors occur. * *

Example:
*

* // Option #1 - Defined via annotation. * @RestResource(renderResponseStackTraces=true) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.renderResponseStackTraces(); * * // Same, but using property. * builder.set(REST_renderResponseStackTraces, true); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.renderResponseStackTraces(); * } * } *

* *
Notes:
*
    *
  • * Useful for debugging, although allowing stack traces to be rendered may cause security concerns so use * caution when enabling. *
  • * This setting is available through the following method: *
      *
    • {@link RestContext#isRenderResponseStackTraces() RestContext.isRenderResponseStackTraces()} *
    * That method is used by {@link BasicRestCallHandler#handleError(HttpServletRequest, HttpServletResponse, Throwable)}. *
*/ public static final String REST_renderResponseStackTraces = PREFIX + "renderResponseStackTraces.b"; /** * Configuration property: REST resource resolver. * *
Property:
*
    *
  • Name: "RestContext.resourceResolver.o" *
  • Data type: {@link RestResourceResolver} | Class<? extends {@link RestResourceResolver}> *
  • Default: {@link BasicRestResourceResolver} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#resourceResolver()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#resourceResolver(Class)} *
    • {@link RestContextBuilder#resourceResolver(RestResourceResolver)} *
    *
* *
Description:
*

* The resolver used for resolving instances of child resources. * *

* Can be used to provide customized resolution of REST resource class instances (e.g. resources retrieve from Spring). * *

Example:
*

* // Our custom resource resolver. * public class MyResourceResolver extends RestResourceResolverSimple { * * @Override * public Object resolve(Class<?> resourceType, RestContextBuilder builder) throws Exception { * Object resource = findOurResourceSomehow(resourceType); * * // If we can't resolve it, use default resolution. * if (resource == null) * resource = super.resolve(resourceType, builder); * * return resource; * } * } * * // Option #1 - Defined via annotation. * @RestResource(resourceResolver=MyResourceResolver.class) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.resourceResolver(MyResourceResolver.class); * * // Same, but using property. * builder.set(REST_resourceResolver, MyResourceResolver.class); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.resourceResolver(MyResourceResolver.class); * } * } *

* *
Notes:
*
    *
  • * Unless overridden, resource resolvers are inherited from ascendant resources. *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(RestContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
* *
See Also:
*
    *
*/ public static final String REST_resourceResolver = PREFIX + "resourceResolver.o"; /** * Configuration property: Response handlers. * *
Property:
*
    *
  • Name: "RestContext.responseHandlers.lo" *
  • Data type: List<{@link ResponseHandler} | Class<? extends {@link ResponseHandler}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#responseHandlers()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#responseHandlers(Class...)} *
    • {@link RestContextBuilder#responseHandlers(ResponseHandler...)} *
    *
* *
Description:
*

* Specifies a list of {@link ResponseHandler} classes that know how to convert POJOs returned by REST methods or * set via {@link RestResponse#setOutput(Object)} into appropriate HTTP responses. * *

* By default, the following response handlers are provided out-of-the-box: *

    *
  • {@link ReaderHandler} - {@link Reader} objects. *
  • {@link InputStreamHandler} - {@link InputStream} objects. *
  • {@link DefaultHandler} - All other POJOs. *
* *
Example:
*

* // Our custom response handler for MySpecialObject objects. * public class MyResponseHandler implements ResponseHandler { * * @Override * public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { * if (output instanceof MySpecialObject) { * try (Writer w = res.getNegotiatedWriter()) { * //Pipe it to the writer ourselves. * } * return true; // We handled it. * } * return false; // We didn't handle it. * } * } * * // Option #1 - Defined via annotation. * @RestResource(responseHandlers=MyResponseHandler.class) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.responseHandlers(MyResponseHandler.class); * * // Same, but using property. * builder.addTo(REST_responseHandlers, MyResponseHandler.class); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.responseHandlers(MyResponseHandler.class); * } * * @RestMethod(...) * public Object myMethod() { * // Return a special object for our handler. * return new MySpecialObject(); * } * } *

* *
Notes:
*
    *
  • * Response handlers resolvers are always inherited from ascendant resources. *
  • * When defined as a class, the implementation must have one of the following constructors: *
      *
    • public T(RestContext) *
    • public T() *
    *
  • * Inner classes of the REST resource class are allowed. *
*/ public static final String REST_responseHandlers = PREFIX + "responseHandlers.lo"; /** * Configuration property: Serializers. * *
Property:
*
    *
  • Name: "RestContext.serializers.lo" *
  • Data type: List<{@link Serializer} | Class<? extends {@link Serializer}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#serializers()} *
    • {@link RestMethod#serializers()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#serializers(Object...)} *
    • {@link RestContextBuilder#serializers(Class...)} *
    • {@link RestContextBuilder#serializers(boolean,Object...)} *
    *
* *
Description:
*

* Adds class-level serializers to this resource. * *

* Serializer are used to convert POJOs to HTTP response bodies. *
Any of the Juneau framework serializers can be used in this setting. *
The serializer selected is based on the request Accept header matched against the values returned by the following method * using a best-match algorithm: *

    *
  • {@link Serializer#getMediaTypeRanges()} *
* *
Example:
*

* // Option #1 - Defined via annotation. * @RestResource(serializers={JsonSerializer.class, XmlSerializer.class}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.serializers(JsonSerializer.class, XmlSerializer.class); * * // Same, but use pre-instantiated parsers. * builder.serializers(JsonSerializer.DEFAULT, XmlSerializer.DEFAULT); * * // Same, but using property. * builder.set(REST_serializers, JsonSerializer.class, XmlSerializer.class); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.serializers(JsonSerializer.class, XmlSerializer.class); * } * * // Override at the method level. * @RestMethod(serializers={HtmlSerializer.class}) * public MyPojo myMethod() { * // Return a POJO to be serialized. * return new MyPojo(); * } * } *

* *
Notes:
*
    *
  • * When defined as a class, properties/transforms defined on the resource/method are inherited. *
  • * When defined as an instance, properties/transforms defined on the resource/method are NOT inherited. *
  • * Typically, you'll want your resource to extend directly from {@link BasicRestServlet} which comes * preconfigured with the following serializers: *
      *
    • {@link HtmlDocSerializer} *
    • {@link HtmlStrippedDocSerializer} *
    • {@link HtmlSchemaDocSerializer} *
    • {@link JsonSerializer} *
    • {@link SimpleJsonSerializer} *
    • {@link JsonSchemaSerializer} *
    • {@link XmlDocSerializer} *
    • {@link XmlSchemaDocSerializer} *
    • {@link UonSerializer} *
    • {@link UrlEncodingSerializer} *
    • {@link MsgPackSerializer} *
    • {@link SoapXmlSerializer} *
    • {@link PlainTextSerializer} *
    *
* *
See Also:
*
    *
*

*/ public static final String REST_serializers = PREFIX + "serializers.lo"; /** * Configuration property: Static file response headers. * *

Property:
*
    *
  • Name: "RestContext.staticFileResponseHeaders.omo" *
  • Data type: Map<String,String> *
  • Default: {'Cache-Control': 'max-age=86400, public} *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#staticFileResponseHeaders()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#staticFileResponseHeaders(boolean,Map)} *
    • {@link RestContextBuilder#staticFileResponseHeaders(String...)} *
    • {@link RestContextBuilder#staticFileResponseHeader(String,String)} *
    *
* *
Description:
*

* Used to customize the headers on responses returned for statically-served files. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource( * staticFileResponseHeaders={ * "Cache-Control: $C{REST/cacheControl,nocache}", * "My-Header: $C{REST/myHeaderValue}" * } * ) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder * .staticFileResponseHeader("Cache-Control", "nocache"); * .staticFileResponseHeaders("My-Header: foo"); * * // Same, but using property. * builder * .addTo(REST_staticFileResponseHeaders, "Cache-Control", "nocache"); * .addTo(REST_staticFileResponseHeaders, "My-Header", "foo"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.staticFileResponseHeader("Cache-Control", "nocache"); * } * } *

* *
See Also:
*
    *
  • {@link #REST_staticFiles} for information about statically-served files. *
*/ public static final String REST_staticFileResponseHeaders = PREFIX + "staticFileResponseHeaders.omo"; /** * Configuration property: Static file mappings. * *
Property:
*
    *
  • Name: "RestContext.staticFiles.lo" *
  • Data type: List<StaticFileMapping> *
  • Default: null *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#staticFiles()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#staticFiles(String)}, *
    • {@link RestContextBuilder#staticFiles(Class,String)} *
    • {@link RestContextBuilder#staticFiles(String,String)} *
    • {@link RestContextBuilder#staticFiles(Class,String,String)} *
    • {@link RestContextBuilder#staticFiles(StaticFileMapping...)} *
    *
* *
Description:
*

* Used to define paths and locations of statically-served files such as images or HTML documents * from the classpath or file system. * *

* An example where this class is used is in the {@link RestResource#staticFiles} annotation: *

* package com.foo.mypackage; * * @RestResource( * path="/myresource", * staticFiles={ * "htdocs:docs", * "styles:styles" * } * ) * public class MyResource extends BasicRestServlet {...} *

* *

* In the example above, given a GET request to the following URL... *

* /myresource/htdocs/foobar.html *

*
...the servlet will attempt to find the foobar.html file in the following ordered locations: *
    *
  1. com.foo.mypackage.docs package. *
  2. [working-dir]/docs directory. *
* *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} for configuring how classpath resources are located and retrieved. *
  • {@link #REST_mimeTypes} for configuring the media types based on file extension. *
  • {@link #REST_staticFileResponseHeaders} for configuring response headers on statically served files. *
  • {@link #REST_useClasspathResourceCaching} for configuring static file caching. *
  • {@link RestContext#getClasspathResource(String,Locale)} for retrieving static files. *
* *
Notes:
*
    *
  • * Mappings are cumulative from super classes. *
  • * Child resources can override mappings made on parent class resources. *
*/ public static final String REST_staticFiles = PREFIX + "staticFiles.lo"; /** * Configuration property: Supported accept media types. * *
Property:
*
    *
  • Name: "RestContext.produces.ls" *
  • Data type: List<String> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#produces()} *
    • {@link RestMethod#produces()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#produces(boolean,String...)} *
    • {@link RestContextBuilder#produces(boolean,MediaType...)} *
    *
* *
Description:
*

* Overrides the media types inferred from the serializers that identify what media types can be produced by the resource. *
An example where this might be useful if you have serializers registered that handle media types that you * don't want exposed in the Swagger documentation. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(produces={"$C{REST/supportedProduces,application/json}"}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.produces(false, "application/json") * * // Same, but using property. * builder.set(REST_produces, "application/json"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.produces(false, "application/json"); * } * } *

* *

* This affects the returned values from the following: *

    *
  • {@link RestContext#getProduces() RestContext.getProduces()} *
  • {@link RestRequest#getProduces()} *
  • {@link RestInfoProvider#getSwagger(RestRequest)} - Affects produces field. *
*/ public static final String REST_produces = PREFIX + "produces.ls"; /** * Configuration property: Supported content media types. * *
Property:
*
    *
  • Name: "RestContext.consumes.ls" *
  • Data type: List<String> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#consumes()} *
    • {@link RestMethod#consumes()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#consumes(boolean,String...)} *
    • {@link RestContextBuilder#consumes(boolean,MediaType...)} *
    *
* *
Description:
*

* Overrides the media types inferred from the parsers that identify what media types can be consumed by the resource. *
An example where this might be useful if you have parsers registered that handle media types that you * don't want exposed in the Swagger documentation. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(consumes={"$C{REST/supportedConsumes,application/json}"}) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.consumes(false, "application/json") * * // Same, but using property. * builder.set(REST_consumes, "application/json"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.consumes(false, "application/json"); * } * } *

* *

* This affects the returned values from the following: *

    *
  • {@link RestContext#getConsumes() RestContext.getConsumes()} *
  • {@link RestRequest#getConsumes()} *
  • {@link RestInfoProvider#getSwagger(RestRequest)} - Affects consumes field. *
*/ public static final String REST_consumes = PREFIX + "consumes.ls"; /** * Configuration property: Use classpath resource caching. * *
Property:
*
    *
  • Name: "RestContext.useClasspathResourceCaching.b" *
  • Data type: Boolean *
  • Default: true *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#useClasspathResourceCaching()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#useClasspathResourceCaching(boolean)} *
    *
* *
Description:
*

* When enabled, resources retrieved via {@link RestContext#getClasspathResource(String, Locale)} (and related * methods) will be cached in memory to speed subsequent lookups. * *

Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(useClasspathResourceCaching="$C{REST/useClasspathResourceCaching,false}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.useClasspathResourceCaching(false) * * // Same, but using property. * builder.set(REST_useClasspathResourceCaching, false); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.useClasspathResourceCaching(false) * } * } *

* *
See Also:
*
    *
  • {@link #REST_staticFiles} for information about static files. *
*/ public static final String REST_useClasspathResourceCaching = PREFIX + "useClasspathResourceCaching.b"; /** * Configuration property: Use stack trace hashes. * *
Property:
*
    *
  • Name: "RestContext.useStackTraceHashes.b" *
  • Data type: Boolean *
  • Default: true *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#useStackTraceHashes()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#useStackTraceHashes(boolean)} *
    *
* *
Description:
*

* When enabled, the number of times an exception has occurred will be tracked based on stack trace hashsums. * *

* Affects the following methods: *

    *
  • {@link RestContext#getStackTraceOccurrence(Throwable) RestContext.getStackTraceOccurrance(Throwable)} *
  • {@link RestCallHandler#handleError(HttpServletRequest, HttpServletResponse, Throwable)} *
  • {@link RestException#getOccurrence()} - Returns the number of times this exception occurred. *
* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource(useStackTraceHashes="$C{REST/useStackTraceHashes,false}") * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.useStackTraceHashes(false) * * // Same, but using property. * builder.set(REST_useStackTraceHashes, false); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.useStackTraceHashes(false) * } * } *

*/ public static final String REST_useStackTraceHashes = PREFIX + "useStackTraceHashes.b"; /** * Configuration property: Resource URI authority path. * *
Property:
*
    *
  • Name: "RestContext.uriAuthority.s" *
  • Data type: String *
  • Default: null *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#uriAuthority()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#uriAuthority(String)} *
    *
* *
Description:
*

* Overrides the authority path value for this resource and any child resources. * *

* Affects the following methods: *

    *
  • {@link RestRequest#getAuthorityPath()} *
* *

* If you do not specify the authority, it is automatically calculated via the following: * *

* String scheme = request.getScheme(); * int port = request.getServerPort(); * StringBuilder sb = new StringBuilder(request.getScheme()).append("://").append(request.getServerName()); * if (! (port == 80 && "http".equals(scheme) || port == 443 && "https".equals(scheme))) * sb.append(':').append(port); * authorityPath = sb.toString(); *

* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource( * path="/servlet", * uriAuthority="$C{REST/authorityPathOverride,http://localhost:10000}" * ) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.uriAuthority("http://localhost:10000"); * * // Same, but using property. * builder.set(REST_uriAuthority, "http://localhost:10000"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.uriAuthority("http://localhost:10000"); * } * } *

*/ public static final String REST_uriAuthority = PREFIX + "uriAuthority.s"; /** * Configuration property: Resource URI context path. * *
Property:
*
    *
  • Name: "RestContext.uriContext.s" *
  • Data type: String *
  • Default: null *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#uriContext()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#uriContext(String)} *
    *
* *
Description:
*

* Overrides the context path value for this resource and any child resources. * *

* This setting is useful if you want to use "context:/child/path" URLs in child resource POJOs but * the context path is not actually specified on the servlet container. * *

* Affects the following methods: *

    *
  • {@link RestRequest#getContextPath()} - Returns the overridden context path for the resource. *
  • {@link RestRequest#getServletPath()} - Includes the overridden context path for the resource. *
* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource( * path="/servlet", * uriContext="$C{REST/contextPathOverride,/foo}" * ) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.uriContext("/foo"); * * // Same, but using property. * builder.set(REST_uriContext, "/foo"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.uriContext("/foo"); * } * } *

*/ public static final String REST_uriContext = PREFIX + "uriContext.s"; /** * Configuration property: URI resolution relativity. * *
Property:
*
    *
  • Name: "RestContext.uriRelativity.s" *
  • Data type: String *
  • Default: "RESOURCE" *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#uriRelativity()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#uriRelativity(String)} *
    *
* *
Description:
*

* Specifies how relative URIs should be interpreted by serializers. * *

* See {@link UriResolution} for possible values. * *

* Affects the following methods: *

    *
  • {@link RestRequest#getUriResolver()} *
* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource( * path="/servlet", * uriRelativity="$C{REST/uriRelativity,PATH_INFO}" * ) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.uriRelativity("PATH_INFO"); * * // Same, but using property. * builder.set(REST_uriRelativity, "PATH_INFO"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.uriRelativity("PATH_INFO"); * } * } *

*/ public static final String REST_uriRelativity = PREFIX + "uriRelativity.s"; /** * Configuration property: URI resolution. * *
Property:
*
    *
  • Name: "RestContext.uriResolution.s" *
  • Data type: String *
  • Default: "ROOT_RELATIVE" *
  • Session property: false *
  • Annotations: *
      *
    • {@link RestResource#uriResolution()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#uriResolution(String)} *
    *
* *
Description:
*

* Specifies how relative URIs should be interpreted by serializers. * *

* See {@link UriResolution} for possible values. * *

* Affects the following methods: *

    *
  • {@link RestRequest#getUriResolver()} *
* *
Example:
*

* // Option #1 - Defined via annotation resolving to a config file setting with default value. * @RestResource( * path="/servlet", * uriResolution="$C{REST/uriResolution,ABSOLUTE}" * ) * public class MyResource { * * // Option #2 - Defined via builder passed in through resource constructor. * public MyResource(RestContextBuilder builder) throws Exception { * * // Using method on builder. * builder.uriResolution("ABSOLUTE"); * * // Same, but using property. * builder.set(REST_uriResolution, "ABSOLUTE"); * } * * // Option #3 - Defined via builder passed in through init method. * @RestHook(INIT) * public void init(RestContextBuilder builder) throws Exception { * builder.uriResolution("ABSOLUTE"); * } * } *

*/ public static final String REST_uriResolution = PREFIX + "uriResolution.s"; /** * Configuration property: HTML Widgets. * *
Property:
*
    *
  • Name: "RestContext.widgets.lo" *
  • Data type: List<{@link Widget} | Class<? extends {@link Widget}>> *
  • Default: empty list *
  • Session property: false *
  • Annotations: *
      *
    • {@link HtmlDoc#widgets()} *
    *
  • Methods: *
      *
    • {@link RestContextBuilder#widgets(Class...)} *
    • {@link RestContextBuilder#widgets(Widget...)} *
    • {@link RestContextBuilder#widgets(boolean,Widget...)} *
    *
* *
Description:
*

* Defines widgets that can be used in conjunction with string variables of the form "$W{name}"to quickly * generate arbitrary replacement text. * * Widgets resolve the following variables: *

    *
  • "$W{name}" - Contents returned by {@link Widget#getHtml(RestRequest)}. *
  • "$W{name.script}" - Contents returned by {@link Widget#getScript(RestRequest)}. *
    The script contents are automatically inserted into the <head/script> section * in the HTML page. *
  • "$W{name.style}" - Contents returned by {@link Widget#getStyle(RestRequest)}. *
    The styles contents are automatically inserted into the <head/style> section * in the HTML page. *
* *

* The following examples shows how to associate a widget with a REST method and then have it rendered in the links * and aside section of the page: * *

* @RestMethod( * widgets={ * MyWidget.class * } * htmldoc=@HtmlDoc( * navlinks={ * "$W{MyWidget}" * }, * aside={ * "Check out this widget: $W{MyWidget}" * } * ) * ) *

* *
Notes:
*
    *
  • * Widgets are inherited from super classes, but can be overridden by reusing the widget name. *
* *
See Also:
*
    *
*/ public static final String REST_widgets = PREFIX + "widgets.lo"; //------------------------------------------------------------------------------------------------------------------- // Instance //------------------------------------------------------------------------------------------------------------------- private final Object resource; final RestContextBuilder builder; private final boolean allowHeaderParams, allowBodyParam, renderResponseStackTraces, useStackTraceHashes, useClasspathResourceCaching, debug; private final String defaultCharset, clientVersionHeader, uriAuthority, uriContext; private final long maxInput; final String fullPath; private final Map widgets; private final Set allowedMethodParams; private final RestContextProperties properties; private final Map,RestMethodParam> paramResolvers; private final SerializerGroup serializers; private final ParserGroup parsers; private final HttpPartSerializer partSerializer; private final HttpPartParser partParser; private final EncoderGroup encoders; private final List consumes, produces; private final Map defaultRequestHeaders, defaultResponseHeaders, staticFileResponseHeaders; private final BeanContext beanContext; private final RestConverter[] converters; private final RestGuard[] guards; private final ResponseHandler[] responseHandlers; private final MimetypesFileTypeMap mimetypesFileTypeMap; private final StaticFileMapping[] staticFiles; private final String[] staticFilesPaths; private final MessageBundle msgs; private final Config config; private final VarResolver varResolver; private final Map callRouters; private final Map callMethods; private final Map childResources; private final RestLogger logger; private final RestCallHandler callHandler; private final RestInfoProvider infoProvider; private final RestException initException; private final RestContext parentContext; private final RestResourceResolver resourceResolver; private final UriResolution uriResolution; private final UriRelativity uriRelativity; // Lifecycle methods private final Method[] postInitMethods, postInitChildFirstMethods, preCallMethods, postCallMethods, startCallMethods, endCallMethods, destroyMethods; private final RestMethodParam[][] preCallMethodParams, postCallMethodParams; private final Class[][] postInitMethodParams, postInitChildFirstMethodParams, startCallMethodParams, endCallMethodParams, destroyMethodParams; // In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package. private final Map staticFilesCache = new ConcurrentHashMap<>(); private final ClasspathResourceManager staticResourceManager; private final ConcurrentHashMap stackTraceHashes = new ConcurrentHashMap<>(); /** * Constructor. * * @param resource The resource annotated with @RestResource. * @return A new builder object. * @throws ServletException Something bad happened. */ public static RestContextBuilder create(Object resource) throws ServletException { return new RestContextBuilder(null, resource.getClass(), null).init(resource); } /** * Constructor. * * @param servletConfig The servlet config passed into the servlet by the servlet container. * @param resourceClass The class annotated with @RestResource. * @param parentContext The parent context, or null if there is no parent context. * @return A new builder object. * @throws ServletException Something bad happened. */ static RestContextBuilder create(ServletConfig servletConfig, Class resourceClass, RestContext parentContext) throws ServletException { return new RestContextBuilder(servletConfig, resourceClass, parentContext); } /** * Constructor. * * @param builder The servlet configuration object. * @throws Exception If any initialization problems were encountered. */ RestContext(RestContextBuilder builder) throws Exception { super(builder.getPropertyStore()); RestException _initException = null; try { ServletContext servletContext = builder.servletContext; this.resource = builder.resource; this.builder = builder; this.parentContext = builder.parentContext; PropertyStore ps = getPropertyStore().builder().add(builder.properties).build(); Class resourceClass = resource.getClass(); uriContext = nullIfEmpty(getStringProperty(REST_uriContext, null)); uriAuthority = nullIfEmpty(getStringProperty(REST_uriAuthority, null)); uriResolution = getProperty(REST_uriResolution, UriResolution.class, UriResolution.ROOT_RELATIVE); uriRelativity = getProperty(REST_uriRelativity, UriRelativity.class, UriRelativity.RESOURCE); allowHeaderParams = getBooleanProperty(REST_allowHeaderParams, true); allowBodyParam = getBooleanProperty(REST_allowBodyParam, true); allowedMethodParams = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(StringUtils.split(getStringProperty(REST_allowedMethodParams, "HEAD,OPTIONS"))))); renderResponseStackTraces = getBooleanProperty(REST_renderResponseStackTraces, false); useStackTraceHashes = getBooleanProperty(REST_useStackTraceHashes, true); debug = getBooleanProperty(REST_debug, super.isDebug()); defaultCharset = getStringProperty(REST_defaultCharset, "utf-8"); maxInput = getLongProperty(REST_maxInput, 100_000_000l); clientVersionHeader = getStringProperty(REST_clientVersionHeader, "X-Client-Version"); converters = getInstanceArrayProperty(REST_converters, resource, RestConverter.class, new RestConverter[0], true, this); guards = getInstanceArrayProperty(REST_guards, resource, RestGuard.class, new RestGuard[0], true, this); responseHandlers = getInstanceArrayProperty(REST_responseHandlers, resource, ResponseHandler.class, new ResponseHandler[0], true, this); Map,RestMethodParam> _paramResolvers = new HashMap<>(); for (RestMethodParam rp : getInstanceArrayProperty(REST_paramResolvers, RestMethodParam.class, new RestMethodParam[0], true, this)) _paramResolvers.put(rp.forClass(), rp); paramResolvers = unmodifiableMap(_paramResolvers); Map _defaultRequestHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); _defaultRequestHeaders.putAll(getMapProperty(REST_defaultRequestHeaders, String.class)); defaultRequestHeaders = unmodifiableMap(new LinkedHashMap<>(_defaultRequestHeaders)); defaultResponseHeaders = getMapProperty(REST_defaultResponseHeaders, Object.class); staticFileResponseHeaders = getMapProperty(REST_staticFileResponseHeaders, Object.class); logger = getInstanceProperty(REST_logger, resource, RestLogger.class, NoOpRestLogger.class, true, this); if (debug) logger.setLevel(Level.FINE); varResolver = builder.varResolverBuilder .vars( FileVar.class, LocalizationVar.class, RequestAttributeVar.class, RequestFormDataVar.class, RequestHeaderVar.class, RequestPathVar.class, RequestQueryVar.class, RequestVar.class, RestInfoVar.class, SerializedRequestAttrVar.class, ServletInitParamVar.class, SwaggerVar.class, UrlVar.class, UrlEncodeVar.class, WidgetVar.class ) .build() ; config = builder.config.resolving(this.varResolver.createSession()); properties = builder.properties; serializers = SerializerGroup.create().append(getInstanceArrayProperty(REST_serializers, Serializer.class, new Serializer[0], true, resource, ps)).build(); parsers = ParserGroup.create().append(getInstanceArrayProperty(REST_parsers, Parser.class, new Parser[0], true, resource, ps)).build(); partSerializer = getInstanceProperty(REST_partSerializer, HttpPartSerializer.class, OpenApiSerializer.class, true, resource, ps); partParser = getInstanceProperty(REST_partParser, HttpPartParser.class, OpenApiParser.class, true, resource, ps); encoders = new EncoderGroupBuilder().append(getInstanceArrayProperty(REST_encoders, Encoder.class, new Encoder[0], true, resource, ps)).build(); beanContext = BeanContext.create().apply(ps).build(); mimetypesFileTypeMap = new ExtendedMimetypesFileTypeMap(); for (String mimeType : getArrayProperty(REST_mimeTypes, String.class)) mimetypesFileTypeMap.addMimeTypes(mimeType); ClasspathResourceFinder rf = getInstanceProperty(REST_classpathResourceFinder, ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class, true, this); useClasspathResourceCaching = getProperty(REST_useClasspathResourceCaching, boolean.class, true); staticResourceManager = new ClasspathResourceManager(resourceClass, rf, useClasspathResourceCaching); consumes = getListProperty(REST_consumes, MediaType.class, parsers.getSupportedMediaTypes()); produces = getListProperty(REST_produces, MediaType.class, serializers.getSupportedMediaTypes()); staticFiles = ArrayUtils.reverse(getArrayProperty(REST_staticFiles, StaticFileMapping.class)); Set s = new TreeSet<>(); for (StaticFileMapping sfm : staticFiles) s.add(sfm.path); staticFilesPaths = s.toArray(new String[s.size()]); MessageBundleLocation[] mbl = getInstanceArrayProperty(REST_messages, MessageBundleLocation.class, new MessageBundleLocation[0]); if (mbl.length == 0) msgs = new MessageBundle(resourceClass, ""); else { msgs = new MessageBundle(mbl[0] != null ? mbl[0].baseClass : resourceClass, mbl[0].bundlePath); for (int i = 1; i < mbl.length; i++) msgs.addSearchPath(mbl[i] != null ? mbl[i].baseClass : resourceClass, mbl[i].bundlePath); } fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + builder.path; this.childResources = Collections.synchronizedMap(new LinkedHashMap()); // Not unmodifiable on purpose so that children can be replaced. Map _widgets = new LinkedHashMap<>(); for (Widget w : getInstanceArrayProperty(REST_widgets, resource, Widget.class, new Widget[0], true, ps)) _widgets.put(w.getName(), w); this.widgets = unmodifiableMap(_widgets); //---------------------------------------------------------------------------------------------------- // Initialize the child resources. // Done after initializing fields above since we pass this object to the child resources. //---------------------------------------------------------------------------------------------------- List methodsFound = new LinkedList<>(); // Temporary to help debug transient duplicate method issue. Map routers = new LinkedHashMap<>(); Map _javaRestMethods = new LinkedHashMap<>(); Map _startCallMethods = new LinkedHashMap<>(), _preCallMethods = new LinkedHashMap<>(), _postCallMethods = new LinkedHashMap<>(), _endCallMethods = new LinkedHashMap<>(), _postInitMethods = new LinkedHashMap<>(), _postInitChildFirstMethods = new LinkedHashMap<>(), _destroyMethods = new LinkedHashMap<>(); List _preCallMethodParams = new ArrayList<>(), _postCallMethodParams = new ArrayList<>(); List[]> _startCallMethodParams = new ArrayList<>(), _endCallMethodParams = new ArrayList<>(), _postInitMethodParams = new ArrayList<>(), _postInitChildFirstMethodParams = new ArrayList<>(), _destroyMethodParams = new ArrayList<>(); for (java.lang.reflect.Method method : resourceClass.getMethods()) { if (method.isAnnotationPresent(RestMethod.class)) { RestMethod a = method.getAnnotation(RestMethod.class); methodsFound.add(method.getName() + "," + a.name() + "," + a.path()); try { if (! isPublic(method)) throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", resourceClass.getName(), method.getName()); RestJavaMethod sm = new RestJavaMethod(resource, method, this); String httpMethod = sm.getHttpMethod(); // PROXY is a special case where a method returns an interface that we // can perform REST calls against. // We override the CallMethod.invoke() method to insert our logic. if ("PROXY".equals(httpMethod)) { final ClassMeta interfaceClass = beanContext.getClassMeta(method.getGenericReturnType()); final RemoteInterfaceMeta rim = new RemoteInterfaceMeta(interfaceClass.getInnerClass(), null); if (rim.getMethodsByPath().isEmpty()) throw new RestException(SC_INTERNAL_SERVER_ERROR, "Method {0} returns an interface {1} that doesn't define any remote methods.", getMethodSignature(method), interfaceClass.getReadableName()); sm = new RestJavaMethod(resource, method, this) { @Override int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable { int rc = super.invoke(pathInfo, req, res); if (rc != SC_OK) return rc; final Object o = res.getOutput(); if ("GET".equals(req.getMethod())) { res.setOutput(rim.getMethodsByPath().keySet()); return SC_OK; } else if ("POST".equals(req.getMethod())) { if (pathInfo.indexOf('/') != -1) pathInfo = pathInfo.substring(pathInfo.lastIndexOf('/')+1); pathInfo = urlDecode(pathInfo); RemoteInterfaceMethod rmm = rim.getMethodMetaByPath(pathInfo); if (rmm != null) { Method m = rmm.getJavaMethod(); try { // Parse the args and invoke the method. Parser p = req.getBody().getParser(); Object[] args = null; if (m.getGenericParameterTypes().length == 0) args = new Object[0]; else { try (Closeable in = p.isReaderParser() ? req.getReader() : req.getInputStream()) { args = p.parseArgs(in, m.getGenericParameterTypes()); } } Object output = m.invoke(o, args); res.setOutput(output); return SC_OK; } catch (Exception e) { throw new InternalServerError(e); } } } return SC_NOT_FOUND; } }; _javaRestMethods.put(method.getName(), sm); addToRouter(routers, "GET", sm); addToRouter(routers, "POST", sm); } else { _javaRestMethods.put(method.getName(), sm); addToRouter(routers, httpMethod, sm); } } catch (Throwable e) { throw new RestServletException("Problem occurred trying to serialize methods on class {0}, methods={1}", resourceClass.getName(), SimpleJsonSerializer.DEFAULT.serialize(methodsFound)).initCause(e); } } } for (Method m : ClassUtils.getAllMethods(resourceClass, true)) { if (ClassUtils.isPublic(m) && m.isAnnotationPresent(RestHook.class)) { HookEvent he = m.getAnnotation(RestHook.class).value(); String sig = ClassUtils.getMethodSignature(m); switch(he) { case PRE_CALL: { if (! _preCallMethods.containsKey(sig)) { setAccessible(m, false); _preCallMethods.put(sig, m); _preCallMethodParams.add(findParams(m, null, true)); } break; } case POST_CALL: { if (! _postCallMethods.containsKey(sig)) { setAccessible(m, false); _postCallMethods.put(sig, m); _postCallMethodParams.add(findParams(m, null, true)); } break; } case START_CALL: { if (! _startCallMethods.containsKey(sig)) { setAccessible(m, false); _startCallMethods.put(sig, m); _startCallMethodParams.add(m.getParameterTypes()); ClassUtils.assertArgsOfType(m, HttpServletRequest.class, HttpServletResponse.class); } break; } case END_CALL: { if (! _endCallMethods.containsKey(sig)) { setAccessible(m, false); _endCallMethods.put(sig, m); _endCallMethodParams.add(m.getParameterTypes()); ClassUtils.assertArgsOfType(m, HttpServletRequest.class, HttpServletResponse.class); } break; } case POST_INIT: { if (! _postInitMethods.containsKey(sig)) { setAccessible(m, false); _postInitMethods.put(sig, m); _postInitMethodParams.add(m.getParameterTypes()); ClassUtils.assertArgsOfType(m, RestContext.class); } break; } case POST_INIT_CHILD_FIRST: { if (! _postInitChildFirstMethods.containsKey(sig)) { setAccessible(m, false); _postInitChildFirstMethods.put(sig, m); _postInitChildFirstMethodParams.add(m.getParameterTypes()); ClassUtils.assertArgsOfType(m, RestContext.class); } break; } case DESTROY: { if (! _destroyMethods.containsKey(sig)) { setAccessible(m, false); _destroyMethods.put(sig, m); _destroyMethodParams.add(m.getParameterTypes()); ClassUtils.assertArgsOfType(m, RestContext.class); } break; } default: // Ignore INIT } } } this.callMethods = unmodifiableMap(_javaRestMethods); this.preCallMethods = _preCallMethods.values().toArray(new Method[_preCallMethods.size()]); this.postCallMethods = _postCallMethods.values().toArray(new Method[_postCallMethods.size()]); this.startCallMethods = _startCallMethods.values().toArray(new Method[_startCallMethods.size()]); this.endCallMethods = _endCallMethods.values().toArray(new Method[_endCallMethods.size()]); this.postInitMethods = _postInitMethods.values().toArray(new Method[_postInitMethods.size()]); this.postInitChildFirstMethods = _postInitChildFirstMethods.values().toArray(new Method[_postInitChildFirstMethods.size()]); this.destroyMethods = _destroyMethods.values().toArray(new Method[_destroyMethods.size()]); this.preCallMethodParams = _preCallMethodParams.toArray(new RestMethodParam[_preCallMethodParams.size()][]); this.postCallMethodParams = _postCallMethodParams.toArray(new RestMethodParam[_postCallMethodParams.size()][]); this.startCallMethodParams = _startCallMethodParams.toArray(new Class[_startCallMethodParams.size()][]); this.endCallMethodParams = _endCallMethodParams.toArray(new Class[_endCallMethodParams.size()][]); this.postInitMethodParams = _postInitMethodParams.toArray(new Class[_postInitMethodParams.size()][]); this.postInitChildFirstMethodParams = _postInitChildFirstMethodParams.toArray(new Class[_postInitChildFirstMethodParams.size()][]); this.destroyMethodParams = _destroyMethodParams.toArray(new Class[_destroyMethodParams.size()][]); Map _callRouters = new LinkedHashMap<>(); for (RestCallRouter.Builder crb : routers.values()) _callRouters.put(crb.getHttpMethodName(), crb.build()); this.callRouters = unmodifiableMap(_callRouters); // Initialize our child resources. resourceResolver = getInstanceProperty(REST_resourceResolver, resource, RestResourceResolver.class, parentContext == null ? BasicRestResourceResolver.class : parentContext.resourceResolver, true, this); for (Object o : getArrayProperty(REST_children, Object.class)) { String path = null; Object r = null; if (o instanceof RestChild) { RestChild rc = (RestChild)o; path = rc.path; r = rc.resource; } else if (o instanceof Class) { Class c = (Class)o; // Don't allow specifying yourself as a child. Causes an infinite loop. if (c == builder.resourceClass) continue; r = c; } else { r = o; } RestContextBuilder childBuilder = null; if (o instanceof Class) { Class oc = (Class)o; childBuilder = RestContext.create(builder.inner, oc, this); r = resourceResolver.resolve(resource, oc, childBuilder); } else { r = o; childBuilder = RestContext.create(builder.inner, o.getClass(), this); } childBuilder.init(r); if (r instanceof RestServlet) ((RestServlet)r).innerInit(childBuilder); childBuilder.servletContext(servletContext); RestContext rc2 = childBuilder.build(); if (r instanceof RestServlet) ((RestServlet)r).setContext(rc2); path = childBuilder.path; childResources.put(path, rc2); } callHandler = getInstanceProperty(REST_callHandler, resource, RestCallHandler.class, BasicRestCallHandler.class, true, this); infoProvider = getInstanceProperty(REST_infoProvider, resource, RestInfoProvider.class, BasicRestInfoProvider.class, true, this); } catch (RestException e) { _initException = e; throw e; } catch (Exception e) { _initException = new RestException(e, SC_INTERNAL_SERVER_ERROR); throw e; } finally { initException = _initException; } } private static void addToRouter(Map routers, String httpMethodName, RestJavaMethod cm) throws RestServletException { if (! routers.containsKey(httpMethodName)) routers.put(httpMethodName, new RestCallRouter.Builder(httpMethodName)); routers.get(httpMethodName).add(cm); } /** * Returns the resource resolver associated with this context. * *

* The resource resolver is used for instantiating child resource classes. * *

See Also:
*
    *
  • {@link #REST_resourceResolver} *
* * @return The resource resolver associated with this context. */ protected RestResourceResolver getResourceResolver() { return resourceResolver; } /** * Returns the variable resolver for this servlet. * *

* Variable resolvers are used to replace variables in property values. * They can be nested arbitrarily deep. * They can also return values that themselves contain other variables. * *

Example:
*

* @RestResource( * messages="nls/Messages", * properties={ * @Property(name="title",value="$L{title}"), // Localized variable in Messages.properties * @Property(name="javaVendor",value="$S{java.vendor,Oracle}"), // System property with default value * @Property(name="foo",value="bar"), * @Property(name="bar",value="baz"), * @Property(name="v1",value="$R{foo}"), // Request variable. value="bar" * @Property(name="v1",value="$R{foo,bar}"), // Request variable. value="bar" * } * ) * public class MyRestResource extends BasicRestServlet { *

* *

* A typical usage pattern involves using variables inside the {@link HtmlDoc @HtmlDoc} annotation: *

* @RestMethod( * name=GET, path="/{name}/*", * htmldoc=@HtmlDoc( * navlinks={ * "up: $R{requestParentURI}", * "options: servlet:/?method=OPTIONS", * "editLevel: servlet:/editLevel?logger=$A{attribute.name, OFF}" * } * header={ * "<h1>$L{MyLocalizedPageTitle}</h1>" * }, * aside={ * "$F{resources/AsideText.html}" * } * ) * ) * public LoggerEntry getLogger(RestRequest req, @Path String name) throws Exception { *

* *
See Also:
*
    *
  • {@link org.apache.juneau.rest.RestContextBuilder#vars(Class...)} - For adding custom vars. *
* * @return The var resolver in use by this resource. */ public VarResolver getVarResolver() { return varResolver; } /** * Returns the config file associated with this servlet. * *

* The config file is identified via one of the following: *

    *
  • {@link RestResource#config()} *
  • {@link RestContextBuilder#config(Config)} *
* * @return * The resolving config file associated with this servlet. *
Never null. */ public Config getConfig() { return config; } /** * Resolve a static resource file. * *

* The location of static resources are defined via: *

    *
  • {@link RestContext#REST_staticFiles RestContext.REST_staticFiles} *
* * @param pathInfo The unencoded path info. * @return The wrapped resource, never null. * @throws NotFound Invalid path. * @throws IOException */ protected StaticFile resolveStaticFile(String pathInfo) throws NotFound, IOException { if (! staticFilesCache.containsKey(pathInfo)) { String p = urlDecode(trimSlashes(pathInfo)); if (p.indexOf("..") != -1) throw new NotFound("Invalid path"); StreamResource sr = null; for (StaticFileMapping sfm : staticFiles) { String path = sfm.path; if (p.startsWith(path)) { String remainder = (p.equals(path) ? "" : p.substring(path.length())); if (remainder.isEmpty() || remainder.startsWith("/")) { String p2 = sfm.location + remainder; try (InputStream is = getClasspathResource(sfm.resourceClass, p2, null)) { if (is != null) { int i = p2.lastIndexOf('/'); String name = (i == -1 ? p2 : p2.substring(i+1)); String mediaType = mimetypesFileTypeMap.getContentType(name); Map responseHeaders = sfm.responseHeaders != null ? sfm.responseHeaders : staticFileResponseHeaders; sr = new StreamResource(MediaType.forString(mediaType), responseHeaders, is); break; } } } } } StaticFile sf = new StaticFile(sr); if (useClasspathResourceCaching) { if (staticFilesCache.size() > 100) staticFilesCache.clear(); staticFilesCache.put(pathInfo, sf); } return sf; } return staticFilesCache.get(pathInfo); } /** * A cached static file instance. */ protected class StaticFile { StreamResource resource; ResponseBeanMeta meta; /** * Constructor. * * @param resource */ protected StaticFile(StreamResource resource) { this.resource = resource; this.meta = resource == null ? null : ResponseBeanMeta.create(resource.getClass(), getPropertyStore()); } } /** * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource on this class, searches * up the parent hierarchy chain. * *

* If the resource cannot be found in the classpath, then an attempt is made to look in the JVM working directory. * *

* If the locale is specified, then we look for resources whose name matches that locale. *
For example, if looking for the resource "MyResource.txt" for the Japanese locale, we will look for * files in the following order: *

    *
  1. "MyResource_ja_JP.txt" *
  2. "MyResource_ja.txt" *
  3. "MyResource.txt" *
* *
Example:
*

* // A rest method that (unsafely!) returns the contents of a localized file * // from the classpath. * @RestMethod(path="/foo") * public Object myMethod(RestRequest req, @Query("file") String file) { * return getContext().getClasspathResource(file, req.getLocale()); * } *

* *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} *
* * @param name The resource name. * @param locale * Optional locale. *
If null, won't look for localized file names. * @return An input stream of the resource, or null if the resource could not be found. * @throws IOException */ public InputStream getClasspathResource(String name, Locale locale) throws IOException { return staticResourceManager.getStream(name, locale); } /** * Same as {@link #getClasspathResource(String, Locale)}, but allows you to override the class used for looking * up the classpath resource. * *
Example:
*

* // A rest method that (unsafely!) returns the contents of a localized file * // from the classpath. * @RestMethod(path="/foo") * public Object myMethod(RestRequest req, @Query("file") String file) { * return getContext().getClasspathResource(SomeOtherClass.class, file, req.getLocale()); * } *

* *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} *
* * @param baseClass * Overrides the default class to use for retrieving the classpath resource. *
If null, uses the REST resource class. * @param name The resource name. * @param locale * Optional locale. *
If null, won't look for localized file names. * @return An input stream of the resource, or null if the resource could not be found. * @throws IOException */ public InputStream getClasspathResource(Class baseClass, String name, Locale locale) throws IOException { return staticResourceManager.getStream(baseClass, name, locale); } /** * Reads the input stream from {@link #getClasspathResource(String, Locale)} into a String. * *
Example:
*

* // A rest method that (unsafely!) returns the contents of a localized file * // from the classpath. * @RestMethod(path="/foo") * public String myMethod(RestRequest req, @Query("file") String file) { * return getContext().getClasspathResourceAsString(file, req.getLocale()); * } *

* *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} *
* * @param name The resource name. * @param locale * Optional locale. *
If null, won't look for localized file names. * @return The contents of the stream as a string, or null if the resource could not be found. * @throws IOException If resource could not be found. */ public String getClasspathResourceAsString(String name, Locale locale) throws IOException { return staticResourceManager.getString(name, locale); } /** * Same as {@link #getClasspathResourceAsString(String, Locale)}, but allows you to override the class used for looking * up the classpath resource. * *
Example:
*

* // A rest method that (unsafely!) returns the contents of a localized file * // from the classpath. * @RestMethod(path="/foo") * public String myMethod(RestRequest req, @Query("file") String file) { * return getContext().getClasspathResourceAsString(SomeOtherClass.class, file, req.getLocale()); * } *

* *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} *
* * @param baseClass * Overrides the default class to use for retrieving the classpath resource. *
If null, uses the REST resource class. * @param name The resource name. * @param locale * Optional locale. *
If null, won't look for localized file names. * @return The contents of the stream as a string, or null if the resource could not be found. * @throws IOException If resource could not be found. */ public String getClasspathResourceAsString(Class baseClass, String name, Locale locale) throws IOException { return staticResourceManager.getString(baseClass, name, locale); } /** * Reads the input stream from {@link #getClasspathResource(String, Locale)} and parses it into a POJO using the parser * matched by the specified media type. * *

* Useful if you want to load predefined POJOs from JSON files in your classpath. * *

Example:
*

* // A rest method that (unsafely!) returns the contents of a localized file * // from the classpath parsed as an array of beans. * @RestMethod(path="/foo") * public MyBean[] myMethod(RestRequest req, @Query("file") String file) { * return getContext().getClasspathResource(MyBean[].class, JSON, file, req.getLocale()); * } *

* *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} *
* * @param c The class type of the POJO to create. * @param mediaType The media type of the data in the stream (e.g. "text/json") * @param name The resource name (e.g. "htdocs/styles.css"). * @param locale * Optional locale. *
If null, won't look for localized file names. * @return The parsed resource, or null if the resource could not be found. * @throws IOException * @throws ServletException If the media type was unknown or the input could not be parsed into a POJO. */ public T getClasspathResource(Class c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException { return getClasspathResource(null, c, mediaType, name, locale); } /** * Same as {@link #getClasspathResource(Class, MediaType, String, Locale)}, except overrides the class used * for retrieving the classpath resource. * *
See Also:
*
    *
  • {@link #REST_classpathResourceFinder} *
* *
Example:
*

* // A rest method that (unsafely!) returns the contents of a localized file * // from the classpath parsed as an array of beans. * @RestMethod(path="/foo") * public MyBean[] myMethod(RestRequest req, @Query("file") String file) { * return getContext().getClasspathResource(SomeOtherClass.class, MyBean[].class, JSON, file, req.getLocale()); * } *

* * @param baseClass * Overrides the default class to use for retrieving the classpath resource. *
If null, uses the REST resource class. * @param c The class type of the POJO to create. * @param mediaType The media type of the data in the stream (e.g. "text/json") * @param name The resource name (e.g. "htdocs/styles.css"). * @param locale * Optional locale. *
If null, won't look for localized file names. * @return The parsed resource, or null if the resource could not be found. * @throws IOException * @throws ServletException If the media type was unknown or the input could not be parsed into a POJO. */ public T getClasspathResource(Class baseClass, Class c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException { InputStream is = getClasspathResource(baseClass, name, locale); if (is == null) return null; try { Parser p = parsers.getParser(mediaType); if (p == null) { if (mediaType == MediaType.JSON) p = JsonParser.DEFAULT; if (mediaType == MediaType.XML) p = XmlParser.DEFAULT; if (mediaType == MediaType.HTML) p = HtmlParser.DEFAULT; if (mediaType == MediaType.UON) p = UonParser.DEFAULT; if (mediaType == MediaType.URLENCODING) p = UrlEncodingParser.DEFAULT; if (mediaType == MediaType.MSGPACK) p = MsgPackParser.DEFAULT; } if (p != null) { try { try (Closeable in = p.isReaderParser() ? new InputStreamReader(is, UTF8) : is) { return p.parse(in, c); } } catch (ParseException e) { throw new ServletException("Could not parse resource '"+name+" as media type '"+mediaType+"'.", e); } } throw new ServletException("Unknown media type '"+mediaType+"'"); } catch (Exception e) { throw new ServletException("Could not parse resource with name '"+name+"'", e); } } /** * Returns the path for this resource as defined by the {@link RestResource#path() @RestResource(path)} annotation or * {@link RestContextBuilder#path(String)} method concatenated with those on all parent classes. * *

* If path is not specified, returns "/". *
Path always starts with "/". * *

See Also:
*
    *
  • {@link #REST_path} *
* * @return The servlet path. */ public String getPath() { return fullPath; } /** * The widgets used for resolving "$W{...}" variables. * *
See Also:
*
    *
  • {@link #REST_widgets} *
* * @return The var resolver widgets as a map with keys being the name returned by {@link Widget#getName()}. */ public Map getWidgets() { return widgets; } /** * Returns the logger to use for this resource. * *
See Also:
*
    *
  • {@link #REST_logger} *
* * @return * The logger to use for this resource. *
Never null. */ public RestLogger getLogger() { return logger; } /** * Returns the resource bundle used by this resource. * *
See Also:
*
    *
  • {@link #REST_messages} *
* * @return * The resource bundle for this resource. *
Never null. */ public MessageBundle getMessages() { return msgs; } /** * Returns the REST information provider used by this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_infoProvider} *
* * @return * The information provider for this resource. *
Never null. */ public RestInfoProvider getInfoProvider() { return infoProvider; } /** * Returns the REST call handler used by this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_callHandler} *
* * @return * The call handler for this resource. *
Never null. */ public RestCallHandler getCallHandler() { return callHandler; } /** * Returns a map of HTTP method names to call routers. * * @return A map with HTTP method names upper-cased as the keys, and call routers as the values. */ protected Map getCallRouters() { return callRouters; } /** * Returns the resource object. * *

* This is the instance of the class annotated with the {@link RestResource @RestResource} annotation, usually * an instance of {@link RestServlet}. * * @return * The resource object. *
Never null. */ public Object getResource() { return resource; } /** * Returns the resource object as a {@link RestServlet}. * * @return * The resource object cast to {@link RestServlet}, or null if the resource doesn't subclass from * {@link RestServlet}. */ public RestServlet getRestServlet() { return resource instanceof RestServlet ? (RestServlet)resource : null; } /** * Throws a {@link RestException} if an exception occurred in the constructor of this object. * * @throws RestException The initialization exception wrapped in a {@link RestException}. */ protected void checkForInitException() throws RestException { if (initException != null) throw initException; } /** * Returns the parent resource context (if this resource was initialized from a parent). * *

* From this object, you can get access to the parent resource class itself using {@link #getResource()} or * {@link #getRestServlet()} * * @return The parent resource context, or null if there is no parent context. */ public RestContext getParentContext() { return parentContext; } /** * Returns the {@link BeanContext} object used for parsing path variables and header values. * * @return The bean context used for parsing path variables and header values. */ public BeanContext getBeanContext() { return beanContext; } /** * Returns the class-level properties associated with this servlet. * *

* Properties at the class level are defined via the following: *

    *
  • {@link RestResource#properties()} *
  • {@link RestContextBuilder#set(String, Object)} *
  • {@link RestContextBuilder#set(Map)} *
* *
Notes:
*
    *
  • * The returned {@code Map} is mutable. *
    Therefore, subclasses are free to override or set additional initialization parameters in their {@code init()} method. *
* * @return The resource properties as a {@link RestContextProperties}. */ public RestContextProperties getProperties() { return properties; } /** * Returns the serializers registered with this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_serializers} *
* * @return The serializers registered with this resource. */ public SerializerGroup getSerializers() { return serializers; } /** * Returns the parsers registered with this resource. * *

* Parsers at the class level are defined via the following: *

    *
  • {@link RestContext#REST_parsers} *
* * @return The parsers registered with this resource. */ public ParserGroup getParsers() { return parsers; } /** * Returns the servlet init parameter returned by {@link ServletConfig#getInitParameter(String)}. * * @param name The init parameter name. * @return The servlet init parameter, or null if not found. */ public String getServletInitParameter(String name) { return builder.getInitParameter(name); } /** * Returns the child resources associated with this servlet. * * @return * An unmodifiable map of child resources. * Keys are the {@link RestResource#path() @RestResource(path)} annotation defined on the child resource. */ public Map getChildResources() { return Collections.unmodifiableMap(childResources); } /** * Returns the number of times this exception was thrown based on a hash of its stacktrace. * *
See Also:
*
    *
  • {@link RestContext#REST_useStackTraceHashes} *
* * @param e The exception to check. * @return * The number of times this exception was thrown, or 0 if {@link #REST_useStackTraceHashes} * setting is not enabled. */ public int getStackTraceOccurrence(Throwable e) { if (! useStackTraceHashes) return 0; int h = e.hashCode(); stackTraceHashes.putIfAbsent(h, new AtomicInteger()); return stackTraceHashes.get(h).incrementAndGet(); } /** * Returns whether it's safe to render stack traces in HTTP responses. * *
See Also:
*
    *
  • {@link RestContext#REST_useStackTraceHashes} *
* * @return true if setting is enabled. */ public boolean isRenderResponseStackTraces() { return renderResponseStackTraces; } /** * Returns whether it's safe to pass header values in as GET parameters. * *
See Also:
*
    *
  • {@link RestContext#REST_allowHeaderParams} *
* * @return true if setting is enabled. */ public boolean isAllowHeaderParams() { return allowHeaderParams; } /** * Returns whether it's safe to pass the HTTP body as a "body" GET parameter. * *
See Also:
*
    *
  • {@link RestContext#REST_allowBodyParam} *
* * @return true if setting is enabled. */ public boolean isAllowBodyParam() { return allowBodyParam; } /** * Returns true if debug mode is enabled on this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_debug} *
* * @return true if setting is enabled. */ @Override public boolean isDebug() { return debug; } /** * Returns the default charset to use on requests and responses when not specified on the request. * *
See Also:
*
    *
  • {@link RestContext#REST_defaultCharset} *
* * @return * The default charset. *
Never null. */ public String getDefaultCharset() { return defaultCharset; } /** * Returns the maximum request input size in bytes. * *
See Also:
*
    *
  • {@link RestContext#REST_maxInput} *
* * @return The maximum request input size in bytes. */ public long getMaxInput() { return maxInput; } /** * Returns the name of the client version header name used by this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_clientVersionHeader} *
* * @return * The name of the client version header used by this resource. *
Never null. */ public String getClientVersionHeader() { return clientVersionHeader; } /** * Returns true if the specified Method GET parameter value can be used to override * the method name in the HTTP header. * *
See Also:
*
    *
  • {@link RestContext#REST_allowedMethodParams} *
* * @param m The method name, upper-cased. * @return true if this resource allows the specified method to be overridden. */ public boolean allowMethodParam(String m) { return (isNotEmpty(m) && (allowedMethodParams.contains(m) || allowedMethodParams.contains("*"))); } /** * Returns the HTTP-part parser associated with this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_partParser} *
* * @return * The HTTP-part parser associated with this resource. *
Never null. */ public HttpPartParser getPartParser() { return partParser; } /** * Returns the HTTP-part serializer associated with this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_partSerializer} *
* * @return * The HTTP-part serializer associated with this resource. *
Never null. */ public HttpPartSerializer getPartSerializer() { return partSerializer; } /** * Returns the encoders associated with this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_encoders} *
* * @return * The encoders associated with this resource. *
Never null. */ public EncoderGroup getEncoders() { return encoders; } /** * Returns the explicit list of supported accept types for this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_serializers} *
  • {@link RestContext#REST_produces} *
* * @return * The supported Accept header values for this resource. *
Never null. */ public List getProduces() { return produces; } /** * Returns the explicit list of supported content types for this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_parsers} *
  • {@link RestContext#REST_consumes} *
* * @return * The supported Content-Type header values for this resource. *
Never null. */ public List getConsumes() { return consumes; } /** * Returns the default request headers for this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_defaultRequestHeaders} *
* * @return * The default request headers for this resource. *
Never null. */ public Map getDefaultRequestHeaders() { return defaultRequestHeaders; } /** * Returns the default response headers for this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_defaultResponseHeaders} *
* * @return * The default response headers for this resource. *
Never null. */ public Map getDefaultResponseHeaders() { return defaultResponseHeaders; } /** * Returns the converters associated with this resource at the class level. * *
See Also:
*
    *
  • {@link RestContext#REST_converters} *
* * @return * The converters associated with this resource. *
Never null. */ public RestConverter[] getConverters() { return converters; } /** * Returns the guards associated with this resource at the class level. * *
See Also:
*
    *
  • {@link RestContext#REST_guards} *
* * @return * The guards associated with this resource. *
Never null. */ public RestGuard[] getGuards() { return guards; } /** * Returns the response handlers associated with this resource. * *
See Also:
*
    *
  • {@link RestContext#REST_responseHandlers} *
* * @return * The response handlers associated with this resource. *
Never null. */ protected ResponseHandler[] getResponseHandlers() { return responseHandlers; } /** * Returns true if this resource has any child resources associated with it. * *
See Also:
*
    *
  • {@link RestContext#REST_children} *
* * @return true if this resource has any child resources associated with it. */ public boolean hasChildResources() { return ! childResources.isEmpty(); } /** * Returns the context of the child resource associated with the specified path. * *
See Also:
*
    *
  • {@link RestContext#REST_children} *
* * @param path The path of the child resource to resolve. * @return The resolved context, or null if it could not be resolved. */ public RestContext getChildResource(String path) { return childResources.get(path); } /** * Returns the authority path of the resource. * *
See Also:
*
    *
  • {@link RestContext#REST_uriAuthority} *
* * @return * The authority path of this resource. *
If not specified, returns the context path of the ascendant resource. */ public String getUriAuthority() { if (uriAuthority != null) return uriAuthority; if (parentContext != null) return parentContext.getUriAuthority(); return null; } /** * Returns the context path of the resource. * *
See Also:
*
    *
  • {@link RestContext#REST_uriContext} *
* * @return * The context path of this resource. *
If not specified, returns the context path of the ascendant resource. */ public String getUriContext() { if (uriContext != null) return uriContext; if (parentContext != null) return parentContext.getUriContext(); return null; } /** * Returns the setting on how relative URIs should be interpreted as relative to. * *
See Also:
*
    *
  • {@link RestContext#REST_uriRelativity} *
* * @return * The URI-resolution relativity setting value. *
Never null. */ public UriRelativity getUriRelativity() { return uriRelativity; } /** * Returns the setting on how relative URIs should be resolved. * *
See Also:
*
    *
  • {@link RestContext#REST_uriResolution} *
* * @return * The URI-resolution setting value. *
Never null. */ public UriResolution getUriResolution() { return uriResolution; } /** * Returns the parameters defined on the specified Java method. * * @param method The Java method to check. * @return The parameters defined on the Java method. */ public RestMethodParam[] getRestMethodParams(Method method) { return callMethods.get(method.getName()).methodParams; } /** * Returns the media type for the specified file name. * *
See Also:
*
    *
  • {@link RestContext#REST_mimeTypes} *
* * @param name The file name. * @return The MIME-type, or null if it could not be determined. */ public String getMediaTypeForName(String name) { return mimetypesFileTypeMap.getContentType(name); } /** * Returns true if the specified path refers to a static file. * *

* Static files are files pulled from the classpath and served up directly to the browser. * *

See Also:
*
    *
  • {@link RestContext#REST_staticFiles} *
* * @param p The URL path remainder after the servlet match. * @return true if the specified path refers to a static file. */ public boolean isStaticFile(String p) { return pathStartsWith(p, staticFilesPaths); } /** * Returns the REST Java methods defined in this resource. * *

* These are the methods annotated with the {@link RestMethod @RestMethod} annotation. * * @return * An unmodifiable map of Java method names to call method objects. */ public Map getCallMethods() { return callMethods; } /** * Finds the {@link RestMethodParam} instances to handle resolving objects on the calls to the specified Java method. * * @param method The Java method being called. * @param pathPattern The parsed URL path pattern. * @param isPreOrPost Whether this is a {@link HookEvent#PRE_CALL} or {@link HookEvent#POST_CALL}. * @return The array of resolvers. * @throws ServletException If an annotation usage error was detected. */ protected RestMethodParam[] findParams(Method method, UrlPathPattern pathPattern, boolean isPreOrPost) throws ServletException { Type[] pt = method.getGenericParameterTypes(); RestMethodParam[] rp = new RestMethodParam[pt.length]; PropertyStore ps = getPropertyStore(); for (int i = 0; i < pt.length; i++) { Type t = pt[i]; if (t instanceof Class) { Class c = (Class)t; rp[i] = paramResolvers.get(c); if (rp[i] == null) rp[i] = RestParamDefaults.STANDARD_RESOLVERS.get(c); } if (hasAnnotation(Header.class, method, i)) { rp[i] = new RestParamDefaults.HeaderObject(method, i, ps); } else if (hasAnnotation(Query.class, method, i)) { rp[i] = new RestParamDefaults.QueryObject(method, i, ps); } else if (hasAnnotation(FormData.class, method, i)) { rp[i] = new RestParamDefaults.FormDataObject(method, i, ps); } else if (hasAnnotation(Path.class, method, i)) { rp[i] = new RestParamDefaults.PathObject(method, i, ps); } else if (hasAnnotation(Body.class, method, i)) { rp[i] = new RestParamDefaults.BodyObject(method, i, ps); } else if (hasAnnotation(Request.class, method, i)) { rp[i] = new RestParamDefaults.RequestObject(method, i, ps); } else if (hasAnnotation(Response.class, method, i)) { rp[i] = new RestParamDefaults.ResponseObject(method, i, ps); } else if (hasAnnotation(ResponseHeader.class, method, i)) { rp[i] = new RestParamDefaults.ResponseHeaderObject(method, i, ps); } else if (hasAnnotation(ResponseStatus.class, method, i)) { rp[i] = new RestParamDefaults.ResponseStatusObject(method, t); } else if (hasAnnotation(HasFormData.class, method, i)) { rp[i] = new RestParamDefaults.HasFormDataObject(method, i); } else if (hasAnnotation(HasQuery.class, method, i)) { rp[i] = new RestParamDefaults.HasQueryObject(method, i); } else if (hasAnnotation(org.apache.juneau.rest.annotation.Method.class, method, i)) { rp[i] = new RestParamDefaults.MethodObject(method, t); } if (rp[i] == null && ! isPreOrPost) throw new RestServletException("Invalid parameter specified for method ''{0}'' at index position {1}", method, i); } return rp; } /* * Calls all @RestHook(PRE) methods. */ void preCall(RestRequest req, RestResponse res) throws RestException { for (int i = 0; i < preCallMethods.length; i++) preOrPost(resource, preCallMethods[i], preCallMethodParams[i], req, res); } /* * Calls all @RestHook(POST) methods. */ void postCall(RestRequest req, RestResponse res) throws RestException { for (int i = 0; i < postCallMethods.length; i++) preOrPost(resource, postCallMethods[i], postCallMethodParams[i], req, res); } private static void preOrPost(Object resource, Method m, RestMethodParam[] mp, RestRequest req, RestResponse res) throws RestException { if (m != null) { Object[] args = new Object[mp.length]; for (int i = 0; i < mp.length; i++) { try { args[i] = mp[i].resolve(req, res); } catch (RestException e) { throw e; } catch (Exception e) { throw new BadRequest(e, "Invalid data conversion. Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.", mp[i].getParamType().name(), mp[i].getName(), mp[i].getType(), m.getDeclaringClass().getName(), m.getName() ); } } try { m.invoke(resource, args); } catch (RestException e) { throw e; } catch (Exception e) { throw new InternalServerError(e); } } } /* * Calls all @RestHook(START) methods. */ void startCall(HttpServletRequest req, HttpServletResponse res) { for (int i = 0; i < startCallMethods.length; i++) startOrFinish(resource, startCallMethods[i], startCallMethodParams[i], req, res); } /* * Calls all @RestHook(FINISH) methods. */ void finishCall(HttpServletRequest req, HttpServletResponse res) { for (int i = 0; i < endCallMethods.length; i++) startOrFinish(resource, endCallMethods[i], endCallMethodParams[i], req, res); } private static void startOrFinish(Object resource, Method m, Class[] p, HttpServletRequest req, HttpServletResponse res) throws RestException, InternalServerError { if (m != null) { Object[] args = new Object[p.length]; for (int i = 0; i < p.length; i++) { if (p[i] == HttpServletRequest.class) args[i] = req; else if (p[i] == HttpServletResponse.class) args[i] = res; } try { m.invoke(resource, args); } catch (RestException e) { throw e; } catch (Exception e) { throw new InternalServerError(e); } } } /** * Calls all @RestHook(POST_INIT) methods in parent-to-child order. * * @return This object (for method chaining). * @throws ServletException */ public RestContext postInit() throws ServletException { for (int i = 0; i < postInitMethods.length; i++) postInitOrDestroy(resource, postInitMethods[i], postInitMethodParams[i]); for (RestContext childContext : this.childResources.values()) childContext.postInit(); return this; } /** * Calls all @RestHook(POST_INIT_CHILD_FIRST) methods in child-to-parent order. * * @return This object (for method chaining). * @throws ServletException */ public RestContext postInitChildFirst() throws ServletException { for (RestContext childContext : this.childResources.values()) childContext.postInitChildFirst(); for (int i = 0; i < postInitChildFirstMethods.length; i++) postInitOrDestroy(resource, postInitChildFirstMethods[i], postInitChildFirstMethodParams[i]); return this; } private void postInitOrDestroy(Object r, Method m, Class[] p) { if (m != null) { Object[] args = new Object[p.length]; for (int i = 0; i < p.length; i++) { if (p[i] == RestContext.class) args[i] = this; else if (p[i] == RestContextBuilder.class) args[i] = this.builder; else if (p[i] == ServletConfig.class) args[i] = this.builder.inner; } try { m.invoke(r, args); } catch (RestException e) { throw e; } catch (Exception e) { throw new InternalServerError(e); } } } /** * Calls {@link Servlet#destroy()} on any child resources defined on this resource. */ protected void destroy() { for (int i = 0; i < destroyMethods.length; i++) { try { postInitOrDestroy(resource, destroyMethods[i], destroyMethodParams[i]); } catch (Exception e) { e.printStackTrace(); } } for (RestContext r : childResources.values()) { r.destroy(); if (r.resource instanceof Servlet) ((Servlet)r.resource).destroy(); } } /** * Unused. */ @Override /* BeanContextBuilder */ public BeanSession createSession(BeanSessionArgs args) { throw new NoSuchMethodError(); } /** * Unused. */ @Override /* BeanContextBuilder */ public BeanSessionArgs createDefaultSessionArgs() { throw new NoSuchMethodError(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy