You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/03/17 02:17:06 UTC
[1/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Repository: incubator-juneau
Updated Branches:
refs/heads/master db0cf72ba -> 07843d641
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java
deleted file mode 100644
index f01c339..0000000
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletContext.java
+++ /dev/null
@@ -1,306 +0,0 @@
-// ***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
-// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
-// * with the License. You may obtain a copy of the License at *
-// * *
-// * http://www.apache.org/licenses/LICENSE-2.0 *
-// * *
-// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
-// * specific language governing permissions and limitations under the License. *
-// ***************************************************************************************************************************
-package org.apache.juneau.rest;
-
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.internal.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.serializer.*;
-
-/**
- * Configurable properties on the {@link RestServlet} class.
- * <p>
- * Properties can be set on the {@link RestServlet} class using the {@link RestResource#properties} or {@link RestMethod#properties} annotations.
- * <p>
- * These properties can also be passed in as servlet init parameters or system properties.
- * <p>
- * Some of these properties are only applicable on the servlet class, and others can be specified on the servlet class or method.<br>
- * These distinctions are noted below.
- * <p>
- * See {@link PropertyStore} for more information about context properties.
- */
-public final class RestServletContext extends Context {
-
- /**
- * <b>Configuration property:</b> Enable header URL parameters.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.allowHeaderParams"</js>
- * <li><b>Data type:</b> <code>Boolean</code>
- * <li><b>Default:</b> <jk>true</jk>
- * </ul>
- * <p>
- * When enabled, headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query parameters.
- * For example: <js>"?Accept=text/json&Content-Type=text/json"</js>
- * <p>
- * Parameter names are case-insensitive.
- * <p>
- * Useful for debugging REST interface using only a browser.
- * <p>
- * Applicable to servlet class only.
- */
- public static final String REST_allowHeaderParams = "RestServlet.allowHeaderParams";
-
- /**
- * <b>Configuration property:</b> Enable <js>"method"</js> URL parameter for specific HTTP methods.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.allowMethodParam"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>""</js>
- * </ul>
- * <p>
- * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> URL parameter on a regular GET request.
- * For example: <js>"?method=OPTIONS"</js>
- * <p>
- * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
- * Parameter name is case-insensitive.
- * Use "*" to represent all methods.
- * For backwards compatibility, "true" also means "*".
- * <p>
- * Note that per the <a class="doclink" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">HTTP specification</a>, special care should
- * be taken when allowing non-safe (POST, PUT, DELETE) methods to be invoked through GET requests.
- * <p>
- * Applicable to servlet class only.
- * <p>
- * Example: <js>"HEAD,OPTIONS"</js>
- */
- public static final String REST_allowMethodParam = "RestServlet.allowMethodParam";
-
- /**
- * <b>Configuration property:</b> Enable <js>"body"</js> URL parameter.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.allowBodyParam"</js>
- * <li><b>Data type:</b> <code>Boolean</code>
- * <li><b>Default:</b> <jk>true</jk>
- * </ul>
- * <p>
- * When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the <js>"body"</js> URL parameter.
- * For example: <js>"?body={name:'John%20Smith',age:45}"</js>
- * <p>
- * Parameter name is case-insensitive.
- * <p>
- * Useful for debugging PUT and POST methods using only a browser.
- * <p>
- * Applicable to servlet class only.
- */
- public static final String REST_allowBodyParam = "RestServlet.allowBodyParam";
-
- /**
- * <b>Configuration property:</b> Render stack traces.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.renderResponseStackTraces"</js>
- * <li><b>Data type:</b> <code>Boolean</code>
- * <li><b>Default:</b> <jk>false</jk>
- * </ul>
- * <p>
- * Render stack traces in HTTP response bodies when errors occur.
- * <p>
- * When enabled, Java stack traces will be rendered in the output response.
- * Useful for debugging, although allowing stack traces to be rendered may cause security concerns.
- * <p>
- * Applicable to servlet class only.
- */
- public static final String REST_renderResponseStackTraces = "RestServlet.renderResponseStackTraces";
-
- /**
- * <b>Configuration property:</b> Use stack trace hashes.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.useStackTraceHashes"</js>
- * <li><b>Data type:</b> <code>Boolean</code>
- * <li><b>Default:</b> <jk>true</jk>
- * </ul>
- * <p>
- * When enabled, the number of times an exception has occurred will be determined based on stack trace hashsums,
- * made available through the {@link RestException#getOccurrence()} method.
- * <p>
- * Applicable to servlet class only.
- */
- public static final String REST_useStackTraceHashes = "RestServlet.useStackTraceHashes";
-
- /**
- * <b>Configuration property:</b> Default character encoding.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.defaultCharset"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>"utf-8"</js>
- * </ul>
- * <p>
- * The default character encoding for the request and response if not specified on the request.
- * <p>
- * Applicable to servlet class and methods.
- */
- public static final String REST_defaultCharset = "RestServlet.defaultCharset";
-
- /**
- * <b>Configuration property:</b> Expected format of request parameters.
- * <p>
- * <ul>
- * <li><b>Name:</b> <js>"RestServlet.paramFormat"</js>
- * <li><b>Data type:</b> <code>String</code>
- * <li><b>Default:</b> <js>"UON"</js>
- * </ul>
- * <p>
- * Possible values:
- * <ul class='spaced-list'>
- * <li><js>"UON"</js> - URL-Encoded Object Notation.<br>
- * This notation allows for request parameters to contain arbitrarily complex POJOs.
- * <li><js>"PLAIN"</js> - Plain text.<br>
- * This treats request parameters as plain text.<br>
- * Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
- * </ul>
- * <p>
- * Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but
- * <js>"foo"</js> when using UON mode.
- * <p>
- * The format can also be specified per-parameter using the {@link FormData#format() @FormData.format()} and {@link Query#format() @Query.format()}
- * annotations.
- * <p>
- * Applicable to servlet class and methods.
- */
- public static final String REST_paramFormat = "RestServlet.paramFormat";
-
- //--------------------------------------------------------------------------------
- // Automatically added properties.
- //--------------------------------------------------------------------------------
-
- /**
- * The request servlet path.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getServletPath()}
- */
- public static final String REST_servletPath = "RestServlet.servletPath";
-
- /**
- * The request servlet URI.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getServletURI()}
- */
- public static final String REST_servletURI = "RestServlet.servletURI";
-
- /**
- * The request servlet URI.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getRelativeServletURI()}
- */
- public static final String REST_relativeServletURI = "RestServlet.relativeServletURI";
-
- /**
- * The request URI path info.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getPathInfo()}
- */
- public static final String REST_pathInfo = "RestServlet.pathInfo";
-
- /**
- * The request URI.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getRequestURI()}
- */
- public static final String REST_requestURI = "RestServlet.requestURI";
-
- /**
- * The request method.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getMethod()}
- */
- public static final String REST_method = "RestServlet.method";
-
- /**
- * The localized servlet title.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getServletTitle()}
- */
- public static final String REST_servletTitle = "RestServlet.servletTitle";
-
- /**
- * The localized servlet description.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getServletDescription()}
- */
- public static final String REST_servletDescription = "RestServlet.servletDescription";
-
- /**
- * The localized method summary.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getMethodSummary()}
- */
- public static final String REST_methodSummary = "RestServlet.methodSummary";
-
- /**
- * The localized method description.
- * <p>
- * Automatically added to properties return by {@link RestServlet#createRequestProperties(org.apache.juneau.ObjectMap, RestRequest)}
- * and are therefore available through {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
- * <p>
- * Equivalent to the value returned by {@link RestRequest#getMethodDescription()}
- */
- public static final String REST_methodDescription = "RestServlet.methodDescription";
-
- final boolean allowHeaderParams, allowBodyParam, renderResponseStackTraces, useStackTraceHashes;
- final String defaultCharset, paramFormat;
- final Set<String> allowMethodParams;
-
- /**
- * Constructor.
- * <p>
- * Typically only called from {@link PropertyStore#getContext(Class)}.
- *
- * @param ps The property store that created this context.
- */
- public RestServletContext(PropertyStore ps) {
- super(ps);
- allowHeaderParams = ps.getProperty(REST_allowHeaderParams, boolean.class, true);
- allowBodyParam = ps.getProperty(REST_allowBodyParam, boolean.class, true);
- renderResponseStackTraces = ps.getProperty(REST_renderResponseStackTraces, boolean.class, false);
- useStackTraceHashes = ps.getProperty(REST_useStackTraceHashes, boolean.class, true);
- defaultCharset = ps.getProperty(REST_defaultCharset, String.class, "utf-8");
- paramFormat = ps.getProperty(REST_paramFormat, String.class, "");
-
- Set<String> s = new LinkedHashSet<String>();
- for (String m : StringUtils.split(ps.getProperty(REST_allowMethodParam, String.class, ""), ','))
- if (m.equals("true")) // For backwards compatibility when this was a boolean field.
- s.add("*");
- else
- s.add(m.toUpperCase());
- allowMethodParams = Collections.unmodifiableSet(s);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
index 8af7efe..8117108 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletDefault.java
@@ -13,7 +13,7 @@
package org.apache.juneau.rest;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.html.*;
@@ -211,9 +211,4 @@ public abstract class RestServletDefault extends RestServlet {
public Swagger getOptions(RestRequest req) {
return req.getSwagger();
}
-
- @Override /* RestServlet */
- public boolean hasOptionsPage() {
- return true;
- }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
index 872d372..0864d54 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServletGroupDefault.java
@@ -35,7 +35,7 @@ public abstract class RestServletGroupDefault extends RestServletDefault {
*/
@RestMethod(name="GET", path="/", description="Child resources")
public ChildResourceDescriptions getChildren(RestRequest req) {
- return new ChildResourceDescriptions(this, req);
+ return new ChildResourceDescriptions(getContext(), req);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
index b8f017b..713da69 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestUtils.java
@@ -245,4 +245,13 @@ public final class RestUtils {
throw new RuntimeException("Could not find servlet path in request URI. URI=["+requestURI+"], servletPath=["+servletPath+"]", e);
}
}
+
+ static String[] parseHeader(String s) {
+ int i = s.indexOf(':');
+ if (i == -1)
+ return null;
+ String name = s.substring(0, i).trim().toLowerCase(Locale.ENGLISH);
+ String val = s.substring(i+1).trim();
+ return new String[]{name,val};
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
index 055b805..9a16812 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/FormData.java
@@ -86,7 +86,7 @@ public @interface FormData {
* <li><js>"PLAIN"</js> - Plain text.<br>
* This treats request parameters as plain text.<br>
* Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
- * <li><js>"INHERIT"</js> (default) - Inherit from the {@link RestServletContext#REST_paramFormat} property on the servlet method or class.
+ * <li><js>"INHERIT"</js> (default) - Inherit from the {@link RestContext#REST_paramFormat} property on the servlet method or class.
* </ul>
* <p>
* Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
index 4bac857..8e411d3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/Query.java
@@ -82,7 +82,7 @@ public @interface Query {
* <li><js>"PLAIN"</js> - Plain text.<br>
* This treats request parameters as plain text.<br>
* Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
- * <li><js>"INHERIT"</js> (default) - Inherit from the {@link RestServletContext#REST_paramFormat} property on the servlet method or class.
+ * <li><js>"INHERIT"</js> (default) - Inherit from the {@link RestContext#REST_paramFormat} property on the servlet method or class.
* </ul>
* <p>
* Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
index 6b0a991..4856070 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
@@ -502,4 +502,9 @@ public @interface RestMethod {
* </ul>
*/
String clientVersion() default "";
+
+ /**
+ * TODO
+ */
+ String[] links() default "";
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
index 13adbc2..b49bdd3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/annotation/RestResource.java
@@ -20,7 +20,8 @@ import java.lang.annotation.*;
import javax.servlet.http.*;
import org.apache.juneau.*;
-import org.apache.juneau.encoders.*;
+import org.apache.juneau.encoders.Encoder;
+import org.apache.juneau.ini.*;
import org.apache.juneau.jena.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
@@ -31,8 +32,10 @@ import org.apache.juneau.utils.*;
import org.apache.juneau.xml.*;
/**
- * Optionally used to associate metadata on an instance of {@link RestServlet}.
+ * Used to denote that a class is a REST resource and to associate metadata on it.
* <p>
+ * Usually used on a subclass of {@link RestServlet}, but can be used to annotate any class that you want to expose as a REST resource.
+ *
* Refer to <a class='doclink' href='../package-summary.html#TOC'>org.apache.juneau.rest</a> doc for information on using this class.
*/
@Documented
@@ -46,9 +49,8 @@ public @interface RestResource {
* <p>
* This annotation is used to provide localized messages for the following methods:
* <ul>
- * <li>{@link RestServlet#getMessage(java.util.Locale, String, Object...)}
- * <li>{@link RestServlet#getTitle(RestRequest)}
- * <li>{@link RestServlet#getDescription(RestRequest)}
+ * <li>{@link RestRequest#getMessage(String, Object...)}
+ * <li>{@link RestContext#getMessages()}
* </ul>
* <p>
* Refer to the {@link MessageBundle} class for a description of the message key formats
@@ -69,7 +71,9 @@ public @interface RestResource {
* <p>
* Typically, guards will be used for permissions checking on the user making the request,
* but it can also be used for other purposes like pre-call validation of a request.
- */
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addGuards(Class...)}/{@link RestConfig#addGuards(RestGuard...)} methods.
+ */
Class<? extends RestGuard>[] guards() default {};
/**
@@ -82,6 +86,8 @@ public @interface RestResource {
* Can be used for performing post-processing on the response object before serialization.
* <p>
* Default converter implementations are provided in the <a class='doclink' href='../converters/package-summary.html#TOC'>org.apache.juneau.rest.converters</a> package.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addConverters(Class...)}/{@link RestConfig#addConverters(RestConverter...)} methods.
*/
Class<? extends RestConverter>[] converters() default {};
@@ -90,14 +96,16 @@ public @interface RestResource {
* <p>
* Shortcut to add bean filters to the bean contexts of the objects returned by the following methods:
* <ul>
- * <li>{@link RestServlet#getBeanContext()}
- * <li>{@link RestServlet#getSerializers()}
- * <li>{@link RestServlet#getParsers()}
+ * <li>{@link RestContext#getBeanContext()}
+ * <li>{@link RestContext#getSerializers()}
+ * <li>{@link RestContext#getParsers()}
* </ul>
* <p>
* If the specified class is an instance of {@link BeanFilterBuilder}, then a filter built from that builder is added.
* Any other classes are wrapped in a {@link InterfaceBeanFilterBuilder} to indicate that subclasses should
* be treated as the specified class type.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestConfig#addBeanFilters(Class...)} method.
*/
Class<?>[] beanFilters() default {};
@@ -106,13 +114,15 @@ public @interface RestResource {
* <p>
* Shortcut to add POJO swaps to the bean contexts of the objects returned by the following methods:
* <ul>
- * <li>{@link RestServlet#getBeanContext()}
- * <li>{@link RestServlet#getSerializers()}
- * <li>{@link RestServlet#getParsers()}
+ * <li>{@link RestContext#getBeanContext()}
+ * <li>{@link RestContext#getSerializers()}
+ * <li>{@link RestContext#getParsers()}
* </ul>
* <p>
* If the specified class is an instance of {@link PojoSwap}, then that swap is added.
* Any other classes are wrapped in a {@link SurrogateSwap}.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestConfig#addPojoSwaps(Class...)} method.
*/
Class<?>[] pojoSwaps() default {};
@@ -121,14 +131,14 @@ public @interface RestResource {
* <p>
* Shortcut for specifying class-level properties on this servlet to the objects returned by the following methods:
* <ul>
- * <li>{@link RestServlet#getBeanContext()}
- * <li>{@link RestServlet#getSerializers()}
- * <li>{@link RestServlet#getParsers()}
+ * <li>{@link RestContext#getBeanContext()}
+ * <li>{@link RestContext#getSerializers()}
+ * <li>{@link RestContext#getParsers()}
* </ul>
* <p>
* Any of the following property names can be specified:
* <ul>
- * <li>{@link RestServletContext}
+ * <li>{@link RestContext}
* <li>{@link BeanContext}
* <li>{@link SerializerContext}
* <li>{@link ParserContext}
@@ -144,6 +154,8 @@ public @interface RestResource {
* <p>
* In some cases, properties can be overridden at runtime through the {@link RestResponse#setProperty(String, Object)} method
* or through a {@link Properties @Properties} annotated method parameter.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#setProperty(String, Object)}/{@link RestConfig#setProperties(java.util.Map)} methods.
*/
Property[] properties() default {};
@@ -151,6 +163,8 @@ public @interface RestResource {
* Specifies a list of {@link Serializer} classes to add to the list of serializers available for this servlet.
* <p>
* This annotation can only be used on {@link Serializer} classes that have no-arg constructors.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addSerializers(Class...)}/{@link RestConfig#addSerializers(Serializer...)} methods.
*/
Class<? extends Serializer>[] serializers() default {};
@@ -158,6 +172,8 @@ public @interface RestResource {
* Specifies a list of {@link Parser} classes to add to the list of parsers available for this servlet.
* <p>
* This annotation can only be used on {@link Parser} classes that have no-arg constructors.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addParsers(Class...)}/{@link RestConfig#addParsers(Parser...)} methods.
*/
Class<? extends Parser>[] parsers() default {};
@@ -167,6 +183,8 @@ public @interface RestResource {
* HTTP responses.
* <p>
* See {@link ResponseHandler} for details.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addResponseHandlers(Class...)}/{@link RestConfig#addResponseHandlers(ResponseHandler...)} methods.
*/
Class<? extends ResponseHandler>[] responseHandlers() default {};
@@ -185,6 +203,8 @@ public @interface RestResource {
* ...
* }
* </p>
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addEncoders(Class...)}/{@link RestConfig#addEncoders(Encoder...)} methods.
*/
Class<? extends Encoder>[] encoders() default {};
@@ -208,6 +228,8 @@ public @interface RestResource {
* ...
* }
* </p>
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addDefaultRequestHeader(String, Object)}/{@link RestConfig#addDefaultRequestHeaders(String...)} methods.
*/
String[] defaultRequestHeaders() default {};
@@ -230,6 +252,8 @@ public @interface RestResource {
* ...
* }
* </p>
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addDefaultResponseHeader(String, Object)}/{@link RestConfig#addDefaultResponseHeaders(String...)} methods.
*/
String[] defaultResponseHeaders() default {};
@@ -270,6 +294,8 @@ public @interface RestResource {
* </p>
* </dd>
* </dl>
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addChildResource(String, Object)}/{@link RestConfig#addChildResources(Class...)}/{@link RestConfig#addChildResources(Object...)} methods.
*/
Class<?>[] children() default {};
@@ -281,6 +307,8 @@ public @interface RestResource {
* <p>
* This annotation is ignored on top-level servlets (i.e. servlets defined in <code>web.xml</code> files).
* Therefore, implementers can optionally specify a path value for documentation purposes.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestConfig#setPath(String)} method.
*/
String path() default "";
@@ -288,7 +316,7 @@ public @interface RestResource {
* Optional servlet title.
* <p>
* It is used to populate the Swagger title field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getTitle(RestRequest)} method.
+ * This value can be retrieved programmatically through the {@link RestRequest#getServletTitle()} method.
* <p>
* The default value pulls the label from the <code>label</code> entry in the servlet resource bundle.
* (e.g. <js>"title = foo"</js> or <js>"MyServlet.title = foo"</js>).
@@ -296,6 +324,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/info/title</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getTitle(RestRequest)} method.
*/
String title() default "";
@@ -303,7 +333,7 @@ public @interface RestResource {
* Optional servlet description.
* <p>
* It is used to populate the Swagger description field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getDescription(RestRequest)} method.
+ * This value can be retrieved programmatically through the {@link RestRequest#getServletDescription()} method.
* <p>
* The default value pulls the description from the <code>description</code> entry in the servlet resource bundle.
* (e.g. <js>"description = foo"</js> or <js>"MyServlet.description = foo"</js>).
@@ -311,6 +341,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/info/description</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getDescription(RestRequest)} method.
*/
String description() default "";
@@ -318,7 +350,6 @@ public @interface RestResource {
* Optional servlet terms-of-service for this API.
* <p>
* It is used to populate the Swagger terms-of-service field.
- * This value can be retrieved programmatically through the {@link RestServlet#getTermsOfService(RestRequest)} method.
* <p>
* The default value pulls the description from the <code>termsOfService</code> entry in the servlet resource bundle.
* (e.g. <js>"termsOfService = foo"</js> or <js>"MyServlet.termsOfService = foo"</js>).
@@ -326,6 +357,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/info/termsOfService</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getTermsOfService(RestRequest)} method.
*/
String termsOfService() default "";
@@ -333,7 +366,6 @@ public @interface RestResource {
* Optional contact information for the exposed API.
* <p>
* It is used to populate the Swagger contact field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getContact(RestRequest)} method.
* <p>
* A simplified JSON string with the following fields:
* <p class='bcode'>
@@ -355,6 +387,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/info/contact</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getContact(RestRequest)} method.
*/
String contact() default "";
@@ -362,7 +396,6 @@ public @interface RestResource {
* Optional license information for the exposed API.
* <p>
* It is used to populate the Swagger license field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getLicense(RestRequest)} method.
* <p>
* A simplified JSON string with the following fields:
* <p class='bcode'>
@@ -383,6 +416,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/info/license</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getLicense(RestRequest)} method.
*/
String license() default "";
@@ -390,7 +425,6 @@ public @interface RestResource {
* Provides the version of the application API (not to be confused with the specification version).
* <p>
* It is used to populate the Swagger version field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getVersion(RestRequest)} method.
* <p>
* The default value pulls the description from the <code>version</code> entry in the servlet resource bundle.
* (e.g. <js>"version = 2.0"</js> or <js>"MyServlet.version = 2.0"</js>).
@@ -398,6 +432,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/info/version</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getVersion(RestRequest)} method.
*/
String version() default "";
@@ -405,7 +441,6 @@ public @interface RestResource {
* Optional tagging information for the exposed API.
* <p>
* It is used to populate the Swagger tags field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getTags(RestRequest)} method.
* <p>
* A simplified JSON string with the following fields:
* <p class='bcode'>
@@ -432,6 +467,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/tags</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getTags(RestRequest)} method.
*/
String tags() default "";
@@ -439,7 +476,6 @@ public @interface RestResource {
* Optional external documentation information for the exposed API.
* <p>
* It is used to populate the Swagger external documentation field and to display on HTML pages.
- * This value can be retrieved programmatically through the {@link RestServlet#getExternalDocs(RestRequest)} method.
* <p>
* A simplified JSON string with the following fields:
* <p class='bcode'>
@@ -460,6 +496,8 @@ public @interface RestResource {
* This field can contain variables (e.g. "$L{my.localized.variable}").
* <p>
* Corresponds to the swagger field <code>/tags</code>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestInfoProvider#getExternalDocs(RestRequest)} method.
*/
String externalDocs() default "";
@@ -469,6 +507,8 @@ public @interface RestResource {
* The configuration file .
* <p>
* This field can contain variables (e.g. "$L{my.localized.variable}").
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestConfig#setConfigFile(ConfigFile)} method.
*/
String config() default "";
@@ -517,6 +557,8 @@ public @interface RestResource {
* Multiple stylesheets can be specified as a comma-delimited list.
* When multiple stylesheets are specified, their contents will be concatenated and return in the order specified
* in the list.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#addStyleSheet(Object...)}/{@link RestConfig#addStyleSheet(Class, String)} methods.
*/
String stylesheet() default "";
@@ -547,6 +589,8 @@ public @interface RestResource {
* <li><code>org.apache.juneau.rest.mydocs</code> package (since <code>RestServletDefault</code> is in <code>org.apache.juneau.rest</code>).
* <li><code>[working-dir]/mydocs</code> directory.
* </ol>
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#setFavIcon(Object)}/{@link RestConfig#setFavIcon(Class, String)} methods.
*/
String favicon() default "";
@@ -558,8 +602,6 @@ public @interface RestResource {
* Mappings are cumulative from parent to child. Child resources can override mappings made on parent resources.
* <p>
* If the file cannot be located, the request will return {@link HttpServletResponse#SC_NOT_FOUND}.
- * <p>
- * The media type on the response is determined by the {@link RestServlet#getMimetypesFileTypeMap()} method.
*
* <h5 class='section'>Example:</h5>
* <p class='bcode'>
@@ -581,6 +623,8 @@ public @interface RestResource {
* <li><code>org.apache.juneau.rest.docs</code> package (since <code>RestServletDefault</code> is in <code>org.apache.juneau.rest</code>).
* <li><code>[working-dir]/docs</code> directory.
* </ol>
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestConfig#addStaticFiles(Class, String)} method.
*/
String staticFiles() default "";
@@ -591,10 +635,66 @@ public @interface RestResource {
* changes. Used in conjunction with {@link RestMethod#clientVersion()} annotation.
* <p>
* If not specified, uses <js>"X-Client-Version"</js>.
+ * <p>
+ * The programmatic equivalent to this annotation is the {@link RestConfig#setClientVersionHeader(String)} method.
*/
String clientVersionHeader() default "";
/**
+ * Specifies the resolver class to use for resolving child resources by class name.
+ * <p>
+ * The default implementation simply instantiates the class using one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestConfig)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * </ul>
+ * The former constructor can be used to get access to the {@link RestConfig} object to get access to the
+ * config file and initialization information or make programmatic modifications to the resource before
+ * full initialization.
+ * <p>
+ * Non-<code>RestServlet</code> classes can also add the following two methods to get access to the
+ * {@link RestConfig} and {@link RestContext} objects:
+ * <ul>
+ * <li><jk>public void</jk> init(RestConfig);</code>
+ * <li><jk>public void</jk> init(RestContext);</code>
+ * </ul>
+ * <p>
+ * Subclasses can be used to provide customized resolution of REST resource class instances.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#setResourceResolver(Class)}/{@link RestConfig#setResourceResolver(RestResourceResolver)} methods.
+ */
+ Class<? extends RestResourceResolver> resourceResolver() default RestResourceResolver.class;
+
+ /**
+ * Specifies the logger class to use for logging.
+ * <p>
+ * The default logger performs basic error logging to the Java logger.
+ * Subclasses can be used to customize logging behavior on the resource.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods.
+ */
+ Class<? extends RestLogger> logger() default RestLogger.Normal.class;
+
+ /**
+ * Specifies the REST call handler class.
+ * <p>
+ * This class handles the basic lifecycle of an HTTP REST call.
+ * Subclasses can be used to customize how these HTTP calls are handled.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#setCallHandler(Class)}/{@link RestConfig#setCallHandler(RestCallHandler)} methods.
+ */
+ Class<? extends RestCallHandler> callHandler() default RestCallHandler.class;
+
+ /**
+ * Specifies the class used to retrieve title/description/swagger information about a resource.
+ * <p>
+ * Subclasses can be used to customize the documentation on a resource.
+ * <p>
+ * The programmatic equivalent to this annotation are the {@link RestConfig#setInfoProvider(Class)}/{@link RestConfig#setInfoProvider(RestInfoProvider)} methods.
+ */
+ Class<? extends RestInfoProvider> infoProvider() default RestInfoProvider.class;
+
+ /**
* TODO
*/
String[] links() default "";
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
index fdfd0bb..be489da 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaDefault.java
@@ -13,7 +13,7 @@
package org.apache.juneau.rest.jena;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.html.*;
@@ -252,9 +252,4 @@ public abstract class RestServletJenaDefault extends RestServlet {
public Swagger getOptions(RestRequest req) {
return req.getSwagger();
}
-
- @Override /* RestServlet */
- public boolean hasOptionsPage() {
- return true;
- }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
index 8c6e635..130ab25 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/jena/RestServletJenaGroupDefault.java
@@ -36,6 +36,6 @@ public abstract class RestServletJenaGroupDefault extends RestServletJenaDefault
*/
@RestMethod(name="GET", path="/", description="Child resources")
public ChildResourceDescriptions getChildren(RestRequest req) {
- return new ChildResourceDescriptions(this, req);
+ return new ChildResourceDescriptions(getContext(), req);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java b/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
index 216b34d..23ed8bf 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/labels/ChildResourceDescriptions.java
@@ -29,25 +29,25 @@ public class ChildResourceDescriptions extends LinkedList<ResourceDescription> {
/**
* Constructor.
*
- * @param servlet The servlet that this bean describes.
+ * @param context The servlet context that this bean describes.
* @param req The HTTP servlet request.
*/
- public ChildResourceDescriptions(RestServlet servlet, RestRequest req) {
- this(servlet, req, false);
+ public ChildResourceDescriptions(RestContext context, RestRequest req) {
+ this(context, req, false);
}
/**
* Constructor.
*
- * @param servlet The servlet that this bean describes.
+ * @param context The servlet context that this bean describes.
* @param req The HTTP servlet request.
* @param sort If <jk>true</jk>, list will be ordered by name alphabetically.
* Default is to maintain the order as specified in the annotation.
*/
- public ChildResourceDescriptions(RestServlet servlet, RestRequest req, boolean sort) {
+ public ChildResourceDescriptions(RestContext context, RestRequest req, boolean sort) {
String uri = req.getTrimmedRequestURI();
- for (Map.Entry<String,RestServlet> e : servlet.getChildResources().entrySet())
- add(new ResourceDescription(uri, e.getKey(), e.getValue().getTitle(req)));
+ for (Map.Entry<String,RestContext> e : context.getChildResources().entrySet())
+ add(new ResourceDescription(uri, e.getKey(), e.getValue().getInfoProvider().getTitle(req)));
if (sort)
Collections.sort(this);
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/package.html b/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
index da2ff80..79c7753 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/package.html
@@ -1053,7 +1053,7 @@
</p>
<ul>
<li>Annotations: {@link org.apache.juneau.rest.annotation.RestResource#title()}, {@link org.apache.juneau.rest.annotation.RestResource#description()}
- <li>By overriding methods on the servlet class: {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}, {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)}
+ <li>By overriding methods on the servlet class: {@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}, {@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)}
<li>By defining properties in the resource bundle.
<li>By specifying them in a Swagger JSON file.
</ul>
@@ -1139,8 +1139,8 @@
<jk>public class</jk> ExampleResource <jk>extends</jk> RestServletDefault {
</p>
<p>
- Another option is to override the {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}
- and {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} methods.
+ Another option is to override the {@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}
+ and {@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)} methods.
</p>
<h6 class='topic'>Method Description, Input, and Responses</h6>
<p>
@@ -1213,13 +1213,13 @@
<td><ck>label</ck></td>
<td>Servlet label</td>
<td>{@link org.apache.juneau.rest.annotation.RestResource#title() @RestResource.title()}</td>
- <td>{@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}</td>
+ <td>{@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}</td>
</tr>
<tr>
<td><ck>description</ck></td>
<td>Servlet description</td>
<td>{@link org.apache.juneau.rest.annotation.RestResource#description() @RestResource.description()}</td>
- <td>{@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)}</td>
+ <td>{@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)}</td>
</tr>
<tr>
<td><ck>[javaMethodName].summary</ck></td>
@@ -1297,7 +1297,7 @@
<ul class='javahierarchy'>
<li class='n'>{@link org.apache.juneau.rest.annotation.RestResource#serializers() @RestResource.serializers()} - Annotation on servlet class.
<li class='n'>{@link org.apache.juneau.rest.annotation.RestMethod#serializers() @RestMethod.serializers()} - Annotation on individual servlet methods.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} - Override method to set the serializers programmatically.
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} - Override method to set the serializers programmatically.
</ul>
<p>
The following are equivalent ways of defining serializers used by a servlet...
@@ -1356,7 +1356,7 @@
<ul class='javahierarchy'>
<li class='n'>{@link org.apache.juneau.rest.annotation.RestResource#parsers() @RestResource.parsers()} - Annotation on servlet class.
<li class='n'>{@link org.apache.juneau.rest.annotation.RestMethod#parsers() @RestMethod.parsers()} - Annotation on individual servlet methods.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])} - Override method to set the parsers programmatically.
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addParsers(Class[])} - Override method to set the parsers programmatically.
</ul>
<p>
The following are equivalent ways of defining parsers used by a servlet...
@@ -1464,22 +1464,22 @@
}
</p>
<p>
- In particular, the {@link org.apache.juneau.rest.RestServletContext} class has a variety of properties
+ In particular, the {@link org.apache.juneau.rest.RestContext} class has a variety of properties
for controlling the behavior of the {@link org.apache.juneau.rest.RestServlet} class itself.
</p>
<p>
There are also ways to provide properties programmatically.
</p>
<ul class='spaced-list'>
- <li>By overriding the {@link org.apache.juneau.rest.RestServlet#createProperties()} method.
- <li>By overriding the {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} and
- {@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])} methods and setting properties on the
+ <li>By overriding the {@link org.apache.juneau.rest.RestConfig#setProperties(Map)} method.
+ <li>By overriding the {@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} and
+ {@link org.apache.juneau.rest.RestConfig#addParsers(Class[])} methods and setting properties on the
serializers and parsers directly.
</ul>
<h6 class='topic'>Additional Information</h6>
<ul class='javahierarchy'>
- <li class='c'>{@link org.apache.juneau.rest.RestServletContext}
+ <li class='c'>{@link org.apache.juneau.rest.RestContext}
<br>Properties associated with the {@link org.apache.juneau.rest.RestServlet} class.
<li class='n'>{@link org.apache.juneau.rest.annotation.RestMethod#serializersInherit @RestMethod.serializersInherit()}
<br>Controls how serializers inherit properties from the servlet class.
@@ -1531,9 +1531,9 @@
Transforms can also be defined programmatically through the following:
</p>
<ul class='spaced-list'>
- <li>By overriding the {@link org.apache.juneau.rest.RestServlet#createBeanFilters()} and {@link org.apache.juneau.rest.RestServlet#createPojoSwaps()} methods.
- <li>By overriding the {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} and
- {@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])} methods and setting transforms on the
+ <li>By overriding the {@link org.apache.juneau.rest.RestConfig#addBeanFilters(Class[])} and {@link org.apache.juneau.rest.RestConfig#addPojoSwaps(Class[])} methods.
+ <li>By overriding the {@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} and
+ {@link org.apache.juneau.rest.RestConfig#addParsers(Class[])} methods and setting transforms on the
serializers and parsers directly.
</ul>
@@ -1601,7 +1601,7 @@
When guards are associated at the class-level, it's equivalent to associating guards on all Java methods on the servlet.
</p>
<p>
- Class-level guards can also be created programmatically by overriding the {@link org.apache.juneau.rest.RestServlet#createGuards(ObjectMap)} method.
+ Class-level guards can also be created programmatically by overriding the {@link org.apache.juneau.rest.RestConfig#addGuards(Class[])} method.
</p>
<h6 class='topic'>Additional Information</h6>
<ul class='javahierarchy'>
@@ -1692,7 +1692,7 @@
</ul>
</ul>
<p>
- Class-level converters can be created programmatically by overriding the {@link org.apache.juneau.rest.RestServlet#createConverters(ObjectMap)} method.
+ Class-level converters can be created programmatically by overriding the {@link org.apache.juneau.rest.RestConfig#addConverters(Class[])} method.
</p>
<p>
Note that from the example above, you can specify more than one converter.
@@ -1812,21 +1812,11 @@
Children can also be defined programmatically by overriding any of the following methods:
</p>
<ul class='javahierarchy'>
- <li class='a'>{@link org.apache.juneau.rest.RestServlet}
+ <li class='a'>{@link org.apache.juneau.rest.RestConfig}
<ul>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getChildClasses() getChildClasses()}
- <br>Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children() @RestResource.children()} annotation.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createChildren() createChildren()}
- <br>Creates instances of classes returned by {@link org.apache.juneau.rest.RestServlet#getChildClasses() getChildClasses()}.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createChildrenMap() createChildrenMap()}
- <br>Uses {@link org.apache.juneau.rest.RestServlet#createChildren() createChildren()} and
- {@link org.apache.juneau.rest.annotation.RestResource#path() @RestResource.path()} to create a map of child URLs to child servlets.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#addChildResource(String,RestServlet) addChildResource(String,RestServlet)}
- <br>Can be used to programmatically add a REST servlet.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#resolveChild(Class) resolveChild(Class)}
- <br>An interceptor method that can be used to provide custom resolution of a child resource.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#replaceChild(RestServlet) replaceChild(RestServlet)}
- <br>Allows a child to be replaced at runtime without affecting the parent.
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addChildResource(String,Object)}
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addChildResources(Class[])}
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addChildResources(Object[])}
</ul>
</ul>
</div>
@@ -1965,18 +1955,18 @@
<ul class='javahierarchy'>
<li class='a'>{@link org.apache.juneau.rest.RestServlet}
<ul>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getVarResolver()}
+ <li class='m'>{@link org.apache.juneau.rest.RestContext#getVarResolver()}
<br>Returns the <l>VarResolver</l> associated with a servlet.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createVarResolver()}
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addVars(Class[])}
<br>The method used to create the servlet <l>VarResolver</l>.
<br>Subclasses can override this method to provide their own resolver.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getSessionObjects(RestRequest)}
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#getSessionObjects(RestRequest)}
<br>Defines the session objects for the var resolver.
<br>Subclasses can override this method to provide additional session objects.
</ul>
</ul>
<p>
- The default {@link org.apache.juneau.rest.RestServlet#createVarResolver()} method provides
+ The default {@link org.apache.juneau.rest.RestContext#getVarResolver()} method provides
support the following string variable types:
</p>
<table class='styled'>
@@ -1998,7 +1988,7 @@
</tr>
<tr>
<td><ck>$C{key}</ck><br><ck>$C{key,default}</ck></td>
- <td>Values from the config file returned by {@link org.apache.juneau.rest.RestServlet#getConfig()}.</td>
+ <td>Values from the config file returned by {@link org.apache.juneau.rest.RestContext#getConfigFile()}.</td>
</tr>
</table>
<p>
@@ -2041,7 +2031,7 @@
}
</p>
<p>
- The default {@link org.apache.juneau.rest.RestServlet#createVarResolver()} method provides
+ The default {@link org.apache.juneau.rest.RestRequest#getVarResolverSession()} method provides
support for all the servlet-level variables, and augments it with the following request-specific
variable types:
</p>
@@ -2235,7 +2225,7 @@
<h6 class='topic'>Notes:</h6>
<ul class='spaced-list'>
<li>Mappings are cumulative from parent to child. Child resources can override mappings made on parent resources.
- <li>The media type on the response is determined by the {@link org.apache.juneau.rest.RestServlet#getMimetypesFileTypeMap()} method.
+ <li>The media type on the response is determined by the {@link org.apache.juneau.rest.RestContext#getMediaTypeForName(String)} method.
</ul>
</div>
@@ -2244,19 +2234,19 @@
<h3 class='topic' onclick='toggle(this)'>4.16 - Listener Methods</h3>
<div class='topic'>
<p>
- Various convenience listener methods are provided on the {@link org.apache.juneau.rest.RestServlet} class
+ Various convenience listener methods are provided on the {@link org.apache.juneau.rest.RestCallHandler} class
that subclasses can use to intercept requests:
</p>
<ul class='javahierarchy'>
- <li class='a'>{@link org.apache.juneau.rest.RestServlet}
+ <li class='a'>{@link org.apache.juneau.rest.RestCallHandler}
<ul>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#onPreCall(RestRequest) onPreCall(RestRequest)}
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#onPreCall(RestRequest) onPreCall(RestRequest)}
<br>Callback method that gets invoked right before the REST Java method is invoked.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
<br>Callback method that gets invoked right after the REST Java method is invoked, but before the serializer is invoked.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#onSuccess(RestRequest,RestResponse,long) onSuccess(RestRequest,RestResponse,long)}
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#onSuccess(RestRequest,RestResponse,long) onSuccess(RestRequest,RestResponse,long)}
<br>Callback method for listening for successful completion of requests.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
+ <li class='m'>{@link org.apache.juneau.rest.RestLogger#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
<br>Callback method for logging errors during HTTP requests.
</ul>
</ul>
@@ -2349,10 +2339,10 @@
Default headers can also be specified programmatically by overriding the following methods:
</p>
<ul class='javahierarchy'>
- <li class='a'>{@link org.apache.juneau.rest.RestServlet}
+ <li class='a'>{@link org.apache.juneau.rest.RestConfig}
<ul>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createDefaultRequestHeaders(ObjectMap) createDefaultRequestHeaders()}
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createDefaultResponseHeaders(ObjectMap) createDefaultResponseHeaders()}
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addDefaultRequestHeaders(String[])}
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addDefaultResponseHeaders(String[])}
</ul>
</ul>
</div>
@@ -2365,14 +2355,11 @@
The following overridable methods are provided for handling errors on requests:
</p>
<ul class='javahierarchy'>
- <li class='a'>{@link org.apache.juneau.rest.RestServlet}
+ <li class='a'>{@link org.apache.juneau.rest.RestCallHandler}
<ul>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
- <br>Gets called when an error occurs on a request call.
- <br>Default implementation logs the error.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#renderError(HttpServletRequest,HttpServletResponse,RestException) renderError(HttpServletRequest,HttpServletResponse,RestException)}
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#renderError(HttpServletRequest,HttpServletResponse,RestException) renderError(HttpServletRequest,HttpServletResponse,RestException)}
<br>Method that produces the error message on the HTTP response.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#handleNotFound(int,RestRequest,RestResponse) handleNotFound(int,RestRequest,RestResponse)}
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#handleNotFound(int,RestRequest,RestResponse) handleNotFound(int,RestRequest,RestResponse)}
<br>Method that gets called when no method/path pattern match the incoming request.
</ul>
@@ -2381,12 +2368,15 @@
The following convenience methods are provided for logging:
</p>
<ul class='javahierarchy'>
- <li class='a'>{@link org.apache.juneau.rest.RestServlet}
+ <li class='a'>{@link org.apache.juneau.rest.RestLogger}
<ul>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getLogger() getLogger()}
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#log(Level,String,Object[]) log(Level,String,Object[])}
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#log(Level,Throwable,String,Object[]) log(Level,Throwable,String,Object[])}
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#logObjects(Level,String,Object[]) logObject(Level,String,Object[])}
+ <li class='m'>{@link org.apache.juneau.rest.RestLogger#getLogger() getLogger()}
+ <li class='m'>{@link org.apache.juneau.rest.RestLogger#onError(HttpServletRequest,HttpServletResponse,RestException) onError(HttpServletRequest,HttpServletResponse,RestException)}
+ <br>Gets called when an error occurs on a request call.
+ <br>Default implementation logs the error.
+ <li class='m'>{@link org.apache.juneau.rest.RestLogger#log(Level,String,Object[]) log(Level,String,Object[])}
+ <li class='m'>{@link org.apache.juneau.rest.RestLogger#log(Level,Throwable,String,Object[]) log(Level,Throwable,String,Object[])}
+ <li class='m'>{@link org.apache.juneau.rest.RestLogger#logObjects(Level,String,Object[]) logObject(Level,String,Object[])}
</ul>
</ul>
<p>
@@ -2485,7 +2475,7 @@
It should be noted that the Configuration API is used extensively in the Microservice API in order to externally configure microservices.
</p>
<p>
- Once a config file has been associated with a REST servlet, it can be accessed through the {@link org.apache.juneau.rest.RestServlet#getConfig()}
+ Once a config file has been associated with a REST servlet, it can be accessed through the {@link org.apache.juneau.rest.RestContext#getConfigFile()}
method.
</p>
<p>
@@ -2513,7 +2503,7 @@
<jk>public</jk> MyRestServlet <jk>extends</jk> RestServlet {
</p>
<p>
- It's even possible to reference request-level variables in your config file if you use {@link org.apache.juneau.rest.RestRequest#getConfig()}
+ It's even possible to reference request-level variables in your config file if you use {@link org.apache.juneau.rest.RestRequest#getConfigFile()}
to access the config file:
</p>
<p class='bcode'>
@@ -2588,8 +2578,7 @@
<br>Juneau Configuration API Javadocs.
<li class='p'><a href='../microservice/package-summary.html#TOC'><l>org.apache.juneau.microservice</l></a>
<br>Juneau Microservice API Javadocs.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfig()}
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfigMgr()}
+ <li class='m'>{@link org.apache.juneau.rest.RestContext#getConfigFile()}
</ul>
</div>
@@ -2813,7 +2802,7 @@
}
</p>
<p>
- To support overloaded methods, the {@link org.apache.juneau.rest.RestServletContext#REST_allowMethodParam} property
+ To support overloaded methods, the {@link org.apache.juneau.rest.RestContext#REST_allowMethodParam} property
must be set on your servlet.
</p>
<p class='bcode'>
@@ -2864,14 +2853,14 @@
<td class='code'>&method=X</td>
<td>
Overload the HTTP method as a GET parameter (e.g <l>"POST"</l>).
- <br>Must be enabled via {@link org.apache.juneau.rest.RestServletContext#REST_allowMethodParam} property.
+ <br>Must be enabled via {@link org.apache.juneau.rest.RestContext#REST_allowMethodParam} property.
</td>
</tr>
<tr>
<td class='code'>&X=headerValue</td>
<td>
Specify a header value as a GET parameter.
- <br>Must be enabled via {@link org.apache.juneau.rest.RestServletContext#REST_allowHeaderParams} property.
+ <br>Must be enabled via {@link org.apache.juneau.rest.RestContext#REST_allowHeaderParams} property.
</td>
</tr>
<tr>
@@ -2879,7 +2868,7 @@
<td>
Pass in the HTTP body content on PUT and POST methods as a UON-encoded GET parameter.
<br>
- <br>Must be enabled via {@link org.apache.juneau.rest.RestServletContext#REST_allowBodyParam} property.
+ <br>Must be enabled via {@link org.apache.juneau.rest.RestContext#REST_allowBodyParam} property.
</td>
</tr>
</table>
@@ -3041,10 +3030,10 @@
</p>
<ul class='spaced-list'>
<li>Specifying additional handlers through the {@link org.apache.juneau.rest.annotation.RestResource#responseHandlers() @RestResource.responseHandlers()} annotation.
- <li>Overriding and extending the {@link org.apache.juneau.rest.RestServlet#createResponseHandlers(ObjectMap)} method.
+ <li>Overriding and extending the {@link org.apache.juneau.rest.RestConfig#addResponseHandlers(Class[])} method.
</ul>
<p>
- The {@link org.apache.juneau.rest.RestServlet#handleResponse(RestRequest,RestResponse,Object)} method can also be
+ The {@link org.apache.juneau.rest.RestCallHandler#handleResponse(RestRequest,RestResponse,Object)} method can also be
overridden to bypass the response handler API and process the POJO yourself.
</p>
</div>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
index 6ba3997..15a96ad 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java
@@ -68,7 +68,7 @@ public abstract class RemoteableServlet extends RestServletDefault {
List<Link> l = new LinkedList<Link>();
boolean useAll = ! useOnlyAnnotated();
for (Class<?> c : getServiceMap().keySet()) {
- if (useAll || getBeanContext().getClassMeta(c).isRemoteable())
+ if (useAll || getContext().getBeanContext().getClassMeta(c).isRemoteable())
l.add(new Link(c.getName(), "{0}/{1}", req.getRequestURI(), c.getName())); //$NON-NLS-1$
}
return l;
@@ -131,7 +131,7 @@ public abstract class RemoteableServlet extends RestServletDefault {
private Map<String,java.lang.reflect.Method> getMethods(String javaInterface) throws Exception {
Class<?> c = getInterfaceClass(javaInterface);
- ClassMeta<?> cm = getBeanContext().getClassMeta(c);
+ ClassMeta<?> cm = getContext().getBeanContext().getClassMeta(c);
return (useOnlyAnnotated() ? cm.getRemoteableMethods() : cm.getPublicMethods());
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
index 52650e1..f7f6df3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java
@@ -30,9 +30,6 @@ import org.apache.juneau.serializer.*;
* <p>
* The <code>Content-Type</code> header is set to the mime-type defined on the selected
* serializer based on the {@link Produces#contentType() @Produces.contentType} annotation.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * default implementation of the {@link RestServlet#createResponseHandlers} method.
*/
public class DefaultHandler implements ResponseHandler {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
index 1eebcfb..78d3948 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java
@@ -23,9 +23,6 @@ import org.apache.juneau.utils.*;
* Simply pipes the contents of the {@link InputStream} to {@link RestResponse#getNegotiatedOutputStream()}.
* <p>
* Sets the <code>Content-Type</code> response header to whatever was set via {@link RestResponse#setContentType(String)}.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * default implementation of the {@link RestServlet#createResponseHandlers} method.
*/
public final class InputStreamHandler implements ResponseHandler {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
index 6f5b6ef..d34f280 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java
@@ -21,9 +21,6 @@ import org.apache.juneau.utils.*;
* Response handler for {@link Reader} objects.
* <p>
* Simply pipes the contents of the {@link Reader} to {@link RestResponse#getNegotiatedWriter()}.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * default implementation of the {@link RestServlet#createResponseHandlers} method.
*/
public final class ReaderHandler implements ResponseHandler {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
index b2054ce..499a834 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java
@@ -19,9 +19,6 @@ import org.apache.juneau.rest.*;
/**
* Response handler for {@link Redirect} objects.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * default implementation of the {@link RestServlet#createResponseHandlers} method.
*/
public final class RedirectHandler implements ResponseHandler {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
index 041665b..8a2f107 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java
@@ -22,9 +22,6 @@ import org.apache.juneau.rest.*;
* Response handler for {@link Writable} and {@link ReaderResource} objects.
* <p>
* Uses the {@link Writable#writeTo(Writer)} method to send the contents to the {@link RestResponse#getNegotiatedWriter()} writer.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * default implementation of the {@link RestServlet#createResponseHandlers} method.
*/
public final class StreamableHandler implements ResponseHandler {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
index 7d84368..b453d1f 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java
@@ -22,9 +22,6 @@ import org.apache.juneau.rest.*;
* Response handler for {@link Writable} and {@link ReaderResource} objects.
* <p>
* Uses the {@link Writable#writeTo(Writer)} method to send the contents to the {@link RestResponse#getNegotiatedWriter()} writer.
- * <p>
- * This handler is registered by default on {@link RestServlet RestServlets} via the
- * default implementation of the {@link RestServlet#createResponseHandlers} method.
*/
public final class WritableHandler implements ResponseHandler {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
index 7f534ce..b141695 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java
@@ -44,6 +44,6 @@ public class ServletInitParamVar extends DefaultingVar {
@Override /* Parameter */
public String resolve(VarResolverSession session, String key) {
- return session.getSessionObject(RestRequest.class, RequestVar.SESSION_req).getServlet().getServletConfig().getInitParameter(key);
+ return session.getSessionObject(RestRequest.class, RequestVar.SESSION_req).getContext().getServletInitParameter(key);
}
}
[6/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
index e690d88..d9424e7 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ParamsResource.java
@@ -12,7 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.test;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.util.*;
@@ -35,7 +35,8 @@ import org.apache.juneau.urlencoding.*;
serializers=PlainTextSerializer.class,
properties={
@Property(name=REST_allowMethodParam, value="*")
- }
+ },
+ pojoSwaps={CalendarSwap.DateMedium.class}
)
public class ParamsResource extends RestServletDefault {
private static final long serialVersionUID = 1L;
@@ -106,11 +107,6 @@ public class ParamsResource extends RestServletDefault {
res.setOutput("PUT /uuid/"+uuid);
}
- @Override /* RestServlet */
- public Class<?>[] createPojoSwaps() {
- return new Class[]{CalendarSwap.DateMedium.class};
- }
-
//====================================================================================================
// @FormData annotation - GET
//====================================================================================================
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
index a38cb68..e1b7f5c 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/PathResource.java
@@ -33,7 +33,7 @@ public class PathResource extends RestServletDefault {
//====================================================================================================
@RestMethod(name="GET", path="/")
public String doGet() {
- return getPath();
+ return getContext().getPath();
}
@RestResource(
@@ -47,7 +47,7 @@ public class PathResource extends RestServletDefault {
// Basic tests
@RestMethod(name="GET", path="/")
public String doGet() {
- return getPath();
+ return getContext().getPath();
}
}
@@ -59,7 +59,7 @@ public class PathResource extends RestServletDefault {
// Basic tests
@RestMethod(name="GET", path="/")
public String doGet() {
- return getPath();
+ return getContext().getPath();
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
index 67634e4..f24c34c 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/Root.java
@@ -66,6 +66,6 @@ public class Root extends RestServletDefault {
@RestMethod(name="GET", path="/")
public ChildResourceDescriptions doGet(RestRequest req) {
- return new ChildResourceDescriptions(this, req);
+ return new ChildResourceDescriptions(getContext(), req);
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
index eb46760..81bce1d 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ErrorConditionsTest.java
@@ -127,7 +127,7 @@ public class ErrorConditionsTest extends RestTestcase {
} catch (RestCallException e) {
checkErrorResponse(debug, e, SC_BAD_REQUEST,
"Could not convert request body content to class type 'org.apache.juneau.rest.test.ErrorConditionsResource$Test3c' using parser 'org.apache.juneau.json.JsonParser'.",
- "Caused by (RuntimeException): Test error");
+ "Test error");
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
index 22d84e0..caf9041 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/TestUtils.java
@@ -49,14 +49,24 @@ public class TestUtils {
System.err.println(r); // NOT DEBUG
e.printStackTrace();
}
- if (status != e.getResponseCode())
+ if (status != e.getResponseCode()) {
+ dumpResponse(r, "Response status code was not correct. Expected: ''{0}''. Actual: ''{1}''", status, e.getResponseCode());
throw new AssertionFailedError(MessageFormat.format("Response status code was not correct. Expected: ''{0}''. Actual: ''{1}''", status, e.getResponseCode()));
+ }
for (String s : contains) {
if (r == null || ! r.contains(s)) {
if (! debug)
- System.err.println(r); // NOT DEBUG
+ dumpResponse(r, "Response did not have the following expected text: ''{0}''", s);
throw new AssertionFailedError(MessageFormat.format("Response did not have the following expected text: ''{0}''", s));
}
}
}
+
+ private static void dumpResponse(String r, String msg, Object...args) {
+ System.err.println("*** Failure ****************************************************************************************"); // NOT DEBUG
+ System.err.println(MessageFormat.format(msg, args));
+ System.err.println("*** Response-Start *********************************************************************************"); // NOT DEBUG
+ System.err.println(r); // NOT DEBUG
+ System.err.println("*** Response-End ***********************************************************************************"); // NOT DEBUG
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
new file mode 100644
index 0000000..5cfa4e5
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallMethod.java
@@ -0,0 +1,996 @@
+// ***************************************************************************************************************************
+// * 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.dto.swagger.SwaggerBuilder.*;
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.rest.CallMethod.ParamType.*;
+import static org.apache.juneau.rest.RestContext.*;
+import static org.apache.juneau.rest.annotation.Inherit.*;
+import static org.apache.juneau.serializer.SerializerContext.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.annotation.Properties;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.urlencoding.*;
+
+/**
+ * Represents a single Java servlet/resource method annotated with {@link RestMethod @RestMethod}.
+ */
+@SuppressWarnings("hiding")
+final class CallMethod implements Comparable<CallMethod> {
+ private final java.lang.reflect.Method method;
+ private final String httpMethod;
+ private final UrlPathPattern pathPattern;
+ private final CallMethod.MethodParam[] params;
+ private final RestGuard[] guards;
+ private final RestMatcher[] optionalMatchers;
+ private final RestMatcher[] requiredMatchers;
+ private final RestConverter[] converters;
+ private final SerializerGroup serializers;
+ private final ParserGroup parsers;
+ private final EncoderGroup encoders;
+ private final UrlEncodingParser urlEncodingParser;
+ private final UrlEncodingSerializer urlEncodingSerializer;
+ private final ObjectMap properties;
+ private final Map<String,String> defaultRequestHeaders;
+ private final String defaultEncoding;
+ private final boolean deprecated;
+ private final String description, tags, summary, externalDocs;
+ private final Integer priority;
+ private final org.apache.juneau.rest.annotation.Parameter[] parameters;
+ private final Response[] responses;
+ private final RestContext context;
+
+ CallMethod(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
+ Builder b = new Builder(servlet, method, context);
+ this.context = context;
+ this.method = method;
+ this.httpMethod = b.httpMethod;
+ this.pathPattern = b.pathPattern;
+ this.params = b.params;
+ this.guards = b.guards;
+ this.optionalMatchers = b.optionalMatchers;
+ this.requiredMatchers = b.requiredMatchers;
+ this.converters = b.converters;
+ this.serializers = b.serializers;
+ this.parsers = b.parsers;
+ this.encoders = b.encoders;
+ this.urlEncodingParser = b.urlEncodingParser;
+ this.urlEncodingSerializer = b.urlEncodingSerializer;
+ this.properties = b.properties;
+ this.defaultRequestHeaders = b.defaultRequestHeaders;
+ this.defaultEncoding = b.defaultEncoding;
+ this.deprecated = b.deprecated;
+ this.description = b.description;
+ this.tags = b.tags;
+ this.summary = b.summary;
+ this.externalDocs = b.externalDocs;
+ this.priority = b.priority;
+ this.parameters = b.parameters;
+ this.responses = b.responses;
+ }
+
+ private static class Builder {
+ private String httpMethod, defaultEncoding, description, tags, summary, externalDocs;
+ private UrlPathPattern pathPattern;
+ private CallMethod.MethodParam[] params;
+ private RestGuard[] guards;
+ private RestMatcher[] optionalMatchers, requiredMatchers;
+ private RestConverter[] converters;
+ private SerializerGroup serializers;
+ private ParserGroup parsers;
+ private EncoderGroup encoders;
+ private UrlEncodingParser urlEncodingParser;
+ private UrlEncodingSerializer urlEncodingSerializer;
+ private ObjectMap properties;
+ private Map<String,String> defaultRequestHeaders;
+ private boolean plainParams, deprecated;
+ private Integer priority;
+ private org.apache.juneau.rest.annotation.Parameter[] parameters;
+ private Response[] responses;
+
+ private Builder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
+ try {
+
+ RestMethod m = method.getAnnotation(RestMethod.class);
+ if (m == null)
+ throw new RestServletException("@RestMethod annotation not found on method ''{0}.{1}''", method.getDeclaringClass().getName(), method.getName());
+
+ if (! m.description().isEmpty())
+ description = m.description();
+ if (! m.tags().isEmpty())
+ tags = m.tags();
+ if (! m.summary().isEmpty())
+ summary = m.summary();
+ if (! m.externalDocs().isEmpty())
+ externalDocs = m.externalDocs();
+ deprecated = m.deprecated();
+ parameters = m.parameters();
+ responses = m.responses();
+ serializers = context.getSerializers();
+ parsers = context.getParsers();
+ urlEncodingSerializer = context.getUrlEncodingSerializer();
+ urlEncodingParser = context.getUrlEncodingParser();
+ encoders = context.getEncoders();
+ properties = context.getProperties();
+
+ List<Inherit> si = Arrays.asList(m.serializersInherit());
+ List<Inherit> pi = Arrays.asList(m.parsersInherit());
+
+ SerializerGroupBuilder sgb = null;
+ ParserGroupBuilder pgb = null;
+ UrlEncodingParserBuilder uepb = null;
+
+ if (m.serializers().length > 0 || m.parsers().length > 0 || m.properties().length > 0 || m.beanFilters().length > 0 || m.pojoSwaps().length > 0) {
+ sgb = new SerializerGroupBuilder();
+ pgb = new ParserGroupBuilder();
+ uepb = new UrlEncodingParserBuilder(urlEncodingParser.createPropertyStore());
+
+ if (si.contains(SERIALIZERS) || m.serializers().length == 0)
+ sgb.append(serializers.getSerializers());
+
+ if (pi.contains(PARSERS) || m.parsers().length == 0)
+ pgb.append(parsers.getParsers());
+ }
+
+ httpMethod = m.name().toUpperCase(Locale.ENGLISH);
+ if (httpMethod.equals("") && method.getName().startsWith("do"))
+ httpMethod = method.getName().substring(2).toUpperCase(Locale.ENGLISH);
+ if (httpMethod.equals(""))
+ throw new RestServletException("@RestMethod name not specified on method ''{0}.{1}''", method.getDeclaringClass().getName(), method.getName());
+ if (httpMethod.equals("METHOD"))
+ httpMethod = "*";
+
+ priority = m.priority();
+
+ String p = m.path();
+ converters = new RestConverter[m.converters().length];
+ for (int i = 0; i < converters.length; i++)
+ converters[i] = m.converters()[i].newInstance();
+
+ guards = new RestGuard[m.guards().length];
+ for (int i = 0; i < guards.length; i++)
+ guards[i] = m.guards()[i].newInstance();
+
+ List<RestMatcher> optionalMatchers = new LinkedList<RestMatcher>(), requiredMatchers = new LinkedList<RestMatcher>();
+ for (int i = 0; i < m.matchers().length; i++) {
+ Class<? extends RestMatcher> c = m.matchers()[i];
+ RestMatcher matcher = null;
+ if (ClassUtils.isParentClass(RestMatcherReflecting.class, c))
+ matcher = c.getConstructor(Object.class, Method.class).newInstance(servlet, method);
+ else
+ matcher = c.newInstance();
+ if (matcher.mustMatch())
+ requiredMatchers.add(matcher);
+ else
+ optionalMatchers.add(matcher);
+ }
+ if (! m.clientVersion().isEmpty())
+ requiredMatchers.add(new ClientVersionMatcher(context.getClientVersionHeader(), method));
+
+ this.requiredMatchers = requiredMatchers.toArray(new RestMatcher[requiredMatchers.size()]);
+ this.optionalMatchers = optionalMatchers.toArray(new RestMatcher[optionalMatchers.size()]);
+
+ Class<?>[] beanFilters = context.getBeanFilters(), pojoSwaps = context.getPojoSwaps();
+
+ if (sgb != null) {
+ sgb.append(m.serializers());
+ if (si.contains(TRANSFORMS))
+ sgb.beanFilters(beanFilters).pojoSwaps(pojoSwaps);
+ if (si.contains(PROPERTIES))
+ sgb.properties(properties);
+ for (Property p1 : m.properties())
+ sgb.property(p1.name(), p1.value());
+ sgb.beanFilters(m.beanFilters());
+ sgb.pojoSwaps(m.pojoSwaps());
+ }
+
+ if (pgb != null) {
+ pgb.append(m.parsers());
+ if (pi.contains(TRANSFORMS))
+ pgb.beanFilters(beanFilters).pojoSwaps(pojoSwaps);
+ if (pi.contains(PROPERTIES))
+ pgb.properties(properties);
+ for (Property p1 : m.properties())
+ pgb.property(p1.name(), p1.value());
+ pgb.beanFilters(m.beanFilters());
+ pgb.pojoSwaps(m.pojoSwaps());
+ }
+
+ if (uepb != null) {
+ for (Property p1 : m.properties())
+ uepb.property(p1.name(), p1.value());
+ uepb.beanFilters(m.beanFilters());
+ uepb.pojoSwaps(m.pojoSwaps());
+ }
+
+ if (m.properties().length > 0) {
+ properties = new ObjectMap().setInner(properties);
+ for (Property p1 : m.properties()) {
+ properties.put(p1.name(), p1.value());
+ }
+ }
+
+ if (m.encoders().length > 0 || ! m.inheritEncoders()) {
+ EncoderGroupBuilder g = new EncoderGroupBuilder();
+ if (m.inheritEncoders())
+ g.append(encoders);
+ else
+ g.append(IdentityEncoder.INSTANCE);
+
+ for (Class<? extends Encoder> c : m.encoders()) {
+ try {
+ g.append(c);
+ } catch (Exception e) {
+ throw new RestServletException("Exception occurred while trying to instantiate Encoder ''{0}''", c.getSimpleName()).initCause(e);
+ }
+ }
+ encoders = g.build();
+ }
+
+ defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+ for (String s : m.defaultRequestHeaders()) {
+ String[] h = RestUtils.parseHeader(s);
+ if (h == null)
+ throw new RestServletException("Invalid default request header specified: ''{0}''. Must be in the format: ''Header-Name: header-value''", s);
+ defaultRequestHeaders.put(h[0], h[1]);
+ }
+
+ defaultEncoding = properties.getString(REST_defaultCharset, context.getDefaultCharset());
+ String paramFormat = properties.getString(REST_paramFormat, context.getParamFormat());
+ plainParams = paramFormat.equals("PLAIN");
+
+ pathPattern = new UrlPathPattern(p);
+
+ int attrIdx = 0;
+ Type[] pt = method.getGenericParameterTypes();
+ Annotation[][] pa = method.getParameterAnnotations();
+ params = new CallMethod.MethodParam[pt.length];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = new CallMethod.MethodParam(pt[i], method, pa[i], plainParams, pathPattern, attrIdx);
+ attrIdx = params[i].attrIdx;
+ }
+
+ if (sgb != null)
+ serializers = sgb.build();
+ if (pgb != null)
+ parsers = pgb.build();
+ if (uepb != null)
+ urlEncodingParser = uepb.build();
+
+ // Need this to access methods in anonymous inner classes.
+ method.setAccessible(true);
+ } catch (Exception e) {
+ throw new RestServletException("Exception occurred while initializing method ''{0}''", method.getName()).initCause(e);
+ }
+ }
+ }
+
+ /**
+ * Represents a single parameter in the Java method.
+ */
+ private static class MethodParam {
+
+ private final ParamType paramType;
+ private final Type type;
+ private final String name;
+ private final boolean multiPart, plainParams;
+ private final int attrIdx;
+
+ private MethodParam(Type type, Method method, Annotation[] annotations, boolean methodPlainParams, UrlPathPattern pathPattern, int attrIdx) throws ServletException {
+ this.type = type;
+
+ ParamType _paramType = null;
+ String _name = "";
+ boolean _multiPart = false, _plainParams = false;
+
+ boolean isClass = type instanceof Class;
+ if (isClass && isParentClass(HttpServletRequest.class, (Class<?>)type))
+ _paramType = REQ;
+ else if (isClass && isParentClass(HttpServletResponse.class, (Class<?>)type))
+ _paramType = RES;
+ else for (Annotation a : annotations) {
+ if (a instanceof Path) {
+ Path a2 = (Path)a;
+ _paramType = PATH;
+ _name = a2.value();
+ } else if (a instanceof Header) {
+ Header h = (Header)a;
+ _paramType = HEADER;
+ _name = h.value();
+ } else if (a instanceof FormData) {
+ FormData p = (FormData)a;
+ if (p.multipart())
+ assertCollection(type, method);
+ _paramType = FORMDATA;
+ _multiPart = p.multipart();
+ _plainParams = p.format().equals("INHERIT") ? methodPlainParams : p.format().equals("PLAIN");
+ _name = p.value();
+ } else if (a instanceof Query) {
+ Query p = (Query)a;
+ if (p.multipart())
+ assertCollection(type, method);
+ _paramType = QUERY;
+ _multiPart = p.multipart();
+ _plainParams = p.format().equals("INHERIT") ? methodPlainParams : p.format().equals("PLAIN");
+ _name = p.value();
+ } else if (a instanceof HasFormData) {
+ HasFormData p = (HasFormData)a;
+ _paramType = HASFORMDATA;
+ _name = p.value();
+ } else if (a instanceof HasQuery) {
+ HasQuery p = (HasQuery)a;
+ _paramType = HASQUERY;
+ _name = p.value();
+ } else if (a instanceof Body) {
+ _paramType = BODY;
+ } else if (a instanceof org.apache.juneau.rest.annotation.Method) {
+ _paramType = METHOD;
+ if (type != String.class)
+ throw new ServletException("@Method parameters must be of type String");
+ } else if (a instanceof PathRemainder) {
+ _paramType = PATHREMAINDER;
+ if (type != String.class)
+ throw new ServletException("@PathRemainder parameters must be of type String");
+ } else if (a instanceof Properties) {
+ _paramType = PROPS;
+ _name = "PROPERTIES";
+ } else if (a instanceof Messages) {
+ _paramType = MESSAGES;
+ _name = "MESSAGES";
+ }
+ }
+ if (_paramType == null)
+ _paramType = PATH;
+
+ if (_paramType == PATH && _name.isEmpty()) {
+ if (pathPattern.getVars().length <= attrIdx)
+ throw new RestServletException("Number of attribute parameters in method ''{0}'' exceeds the number of URL pattern variables.", method.getName());
+ _name = pathPattern.getVars()[attrIdx++];
+ }
+
+ this.paramType = _paramType;
+ this.name = _name;
+ this.multiPart = _multiPart;
+ this.plainParams = _plainParams;
+ this.attrIdx = attrIdx;
+ }
+
+ /**
+ * Throws an exception if the specified type isn't an array or collection.
+ */
+ private static void assertCollection(Type t, Method m) throws ServletException {
+ ClassMeta<?> cm = BeanContext.DEFAULT.getClassMeta(t);
+ if (! cm.isCollectionOrArray())
+ throw new ServletException("Use of multipart flag on parameter that's not an array or Collection on method" + m);
+ }
+
+ private Object getValue(RestRequest req, RestResponse res) throws Exception {
+ BeanSession session = req.getBeanSession();
+ switch(paramType) {
+ case REQ: return req;
+ case RES: return res;
+ case PATH: return req.getPathParameter(name, type);
+ case BODY: return req.getBody(type);
+ case HEADER: return req.getHeader(name, type);
+ case METHOD: return req.getMethod();
+ case FORMDATA: {
+ if (multiPart)
+ return req.getFormDataParameters(name, type);
+ if (plainParams)
+ return session.convertToType(req.getFormDataParameter(name), session.getClassMeta(type));
+ return req.getFormDataParameter(name, type);
+ }
+ case QUERY: {
+ if (multiPart)
+ return req.getQueryParameters(name, type);
+ if (plainParams)
+ return session.convertToType(req.getQueryParameter(name), session.getClassMeta(type));
+ return req.getQueryParameter(name, type);
+ }
+ case HASFORMDATA: return session.convertToType(req.hasFormDataParameter(name), session.getClassMeta(type));
+ case HASQUERY: return session.convertToType(req.hasQueryParameter(name), session.getClassMeta(type));
+ case PATHREMAINDER: return req.getPathRemainder();
+ case PROPS: return res.getProperties();
+ case MESSAGES: return req.getResourceBundle();
+ default: return null;
+ }
+ }
+ }
+
+ static enum ParamType {
+ REQ, RES, PATH, BODY, HEADER, METHOD, FORMDATA, QUERY, HASFORMDATA, HASQUERY, PATHREMAINDER, PROPS, MESSAGES;
+
+ private String getSwaggerParameterType() {
+ switch(this) {
+ case PATH: return "path";
+ case HEADER: return "header";
+ case FORMDATA: return "formData";
+ case QUERY: return "query";
+ case BODY: return "body";
+ default: return null;
+ }
+ }
+ }
+
+ /**
+ * Returns <jk>true</jk> if this Java method has any guards or matchers.
+ */
+ boolean hasGuardsOrMatchers() {
+ return (guards.length != 0 || requiredMatchers.length != 0 || optionalMatchers.length != 0);
+ }
+
+ /**
+ * Returns the HTTP method name (e.g. <js>"GET"</js>).
+ */
+ String getHttpMethod() {
+ return httpMethod;
+ }
+
+ /**
+ * Returns the path pattern for this method.
+ */
+ String getPathPattern() {
+ return pathPattern.toString();
+ }
+
+ /**
+ * Returns the localized Swagger for this Java method.
+ */
+ Operation getSwaggerOperation(RestRequest req) throws ParseException {
+ Operation o = operation()
+ .operationId(method.getName())
+ .description(getDescription(req))
+ .tags(getTags(req))
+ .summary(getSummary(req))
+ .externalDocs(getExternalDocs(req))
+ .parameters(getParameters(req))
+ .responses(getResponses(req));
+
+ if (isDeprecated())
+ o.deprecated(true);
+
+ if (! parsers.getSupportedMediaTypes().equals(context.getParsers().getSupportedMediaTypes()))
+ o.consumes(parsers.getSupportedMediaTypes());
+
+ if (! serializers.getSupportedMediaTypes().equals(context.getSerializers().getSupportedMediaTypes()))
+ o.produces(serializers.getSupportedMediaTypes());
+
+ return o;
+ }
+
+ private Operation getSwaggerOperationFromFile(RestRequest req) {
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getPaths() != null && s.getPaths().get(pathPattern.getPatternString()) != null)
+ return s.getPaths().get(pathPattern.getPatternString()).get(httpMethod);
+ return null;
+ }
+
+ /**
+ * Returns the localized summary for this Java method.
+ */
+ String getSummary(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ if (summary != null)
+ return vr.resolve(summary);
+ String summary = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".summary");
+ if (summary != null)
+ return vr.resolve(summary);
+ Operation o = getSwaggerOperationFromFile(req);
+ if (o != null)
+ return o.getSummary();
+ return null;
+ }
+
+ /**
+ * Returns the localized description for this Java method.
+ */
+ String getDescription(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ if (description != null)
+ return vr.resolve(description);
+ String description = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".description");
+ if (description != null)
+ return vr.resolve(description);
+ Operation o = getSwaggerOperationFromFile(req);
+ if (o != null)
+ return o.getDescription();
+ return null;
+ }
+
+ /**
+ * Returns the localized Swagger tags for this Java method.
+ */
+ private List<String> getTags(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ try {
+ if (tags != null)
+ return jp.parse(vr.resolve(tags), ArrayList.class, String.class);
+ String tags = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".tags");
+ if (tags != null)
+ return jp.parse(vr.resolve(tags), ArrayList.class, String.class);
+ Operation o = getSwaggerOperationFromFile(req);
+ if (o != null)
+ return o.getTags();
+ return null;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns the localized Swagger external docs for this Java method.
+ */
+ private ExternalDocumentation getExternalDocs(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ try {
+ if (externalDocs != null)
+ return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
+ String externalDocs = context.getMessages().findFirstString(req.getLocale(), method.getName() + ".externalDocs");
+ if (externalDocs != null)
+ return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
+ Operation o = getSwaggerOperationFromFile(req);
+ if (o != null)
+ return o.getExternalDocs();
+ return null;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns the Swagger deprecated flag for this Java method.
+ */
+ private boolean isDeprecated() {
+ return deprecated;
+ }
+
+ /**
+ * Returns the localized Swagger parameter information for this Java method.
+ */
+ private List<ParameterInfo> getParameters(RestRequest req) throws ParseException {
+ Operation o = getSwaggerOperationFromFile(req);
+ if (o != null && o.getParameters() != null)
+ return o.getParameters();
+
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ Map<String,ParameterInfo> m = new TreeMap<String,ParameterInfo>();
+
+ // First parse @RestMethod.parameters() annotation.
+ for (org.apache.juneau.rest.annotation.Parameter v : parameters) {
+ String in = vr.resolve(v.in());
+ ParameterInfo p = parameterInfo(in, vr.resolve(v.name()));
+
+ if (! v.description().isEmpty())
+ p.description(vr.resolve(v.description()));
+ if (v.required())
+ p.required(v.required());
+
+ if ("body".equals(in)) {
+ if (! v.schema().isEmpty())
+ p.schema(jp.parse(vr.resolve(v.schema()), SchemaInfo.class));
+ } else {
+ if (v.allowEmptyValue())
+ p.allowEmptyValue(v.allowEmptyValue());
+ if (! v.collectionFormat().isEmpty())
+ p.collectionFormat(vr.resolve(v.collectionFormat()));
+ if (! v._default().isEmpty())
+ p._default(vr.resolve(v._default()));
+ if (! v.format().isEmpty())
+ p.format(vr.resolve(v.format()));
+ if (! v.items().isEmpty())
+ p.items(jp.parse(vr.resolve(v.items()), Items.class));
+ p.type(vr.resolve(v.type()));
+ }
+ m.put(p.getIn() + '.' + p.getName(), p);
+ }
+
+ // Next, look in resource bundle.
+ String prefix = method.getName() + ".req";
+ for (String key : context.getMessages().keySet(prefix)) {
+ if (key.length() > prefix.length()) {
+ String value = vr.resolve(context.getMessages().getString(key));
+ String[] parts = key.substring(prefix.length() + 1).split("\\.");
+ String in = parts[0], name, field;
+ boolean isBody = "body".equals(in);
+ if (parts.length == (isBody ? 2 : 3)) {
+ if ("body".equals(in)) {
+ name = null;
+ field = parts[1];
+ } else {
+ name = parts[1];
+ field = parts[2];
+ }
+ String k2 = in + '.' + name;
+ ParameterInfo p = m.get(k2);
+ if (p == null) {
+ p = parameterInfoStrict(in, name);
+ m.put(k2, p);
+ }
+
+ if (field.equals("description"))
+ p.description(value);
+ else if (field.equals("required"))
+ p.required(Boolean.valueOf(value));
+
+ if ("body".equals(in)) {
+ if (field.equals("schema"))
+ p.schema(jp.parse(value, SchemaInfo.class));
+ } else {
+ if (field.equals("allowEmptyValue"))
+ p.allowEmptyValue(Boolean.valueOf(value));
+ else if (field.equals("collectionFormat"))
+ p.collectionFormat(value);
+ else if (field.equals("default"))
+ p._default(value);
+ else if (field.equals("format"))
+ p.format(value);
+ else if (field.equals("items"))
+ p.items(jp.parse(value, Items.class));
+ else if (field.equals("type"))
+ p.type(value);
+ }
+ } else {
+ System.err.println("Unknown bundle key '"+key+"'");
+ }
+ }
+ }
+
+ // Finally, look for parameters defined on method.
+ for (CallMethod.MethodParam mp : this.params) {
+ String in = mp.paramType.getSwaggerParameterType();
+ if (in != null) {
+ String k2 = in + '.' + ("body".equals(in) ? null : mp.name);
+ ParameterInfo p = m.get(k2);
+ if (p == null) {
+ p = parameterInfoStrict(in, mp.name);
+ m.put(k2, p);
+ }
+ }
+ }
+
+ if (m.isEmpty())
+ return null;
+ return new ArrayList<ParameterInfo>(m.values());
+ }
+
+ /**
+ * Returns the localized Swagger response information about this Java method.
+ */
+ @SuppressWarnings("unchecked")
+ private Map<Integer,ResponseInfo> getResponses(RestRequest req) throws ParseException {
+ Operation o = getSwaggerOperationFromFile(req);
+ if (o != null && o.getResponses() != null)
+ return o.getResponses();
+
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ Map<Integer,ResponseInfo> m = new TreeMap<Integer,ResponseInfo>();
+ Map<String,HeaderInfo> m2 = new TreeMap<String,HeaderInfo>();
+
+ // First parse @RestMethod.parameters() annotation.
+ for (Response r : responses) {
+ int httpCode = r.value();
+ String description = r.description().isEmpty() ? RestUtils.getHttpResponseText(r.value()) : vr.resolve(r.description());
+ ResponseInfo r2 = responseInfo(description);
+
+ if (r.headers().length > 0) {
+ for (org.apache.juneau.rest.annotation.Parameter v : r.headers()) {
+ HeaderInfo h = headerInfoStrict(vr.resolve(v.type()));
+ if (! v.collectionFormat().isEmpty())
+ h.collectionFormat(vr.resolve(v.collectionFormat()));
+ if (! v._default().isEmpty())
+ h._default(vr.resolve(v._default()));
+ if (! v.description().isEmpty())
+ h.description(vr.resolve(v.description()));
+ if (! v.format().isEmpty())
+ h.format(vr.resolve(v.format()));
+ if (! v.items().isEmpty())
+ h.items(jp.parse(vr.resolve(v.items()), Items.class));
+ r2.header(v.name(), h);
+ m2.put(httpCode + '.' + v.name(), h);
+ }
+ }
+ m.put(httpCode, r2);
+ }
+
+ // Next, look in resource bundle.
+ String prefix = method.getName() + ".res";
+ for (String key : context.getMessages().keySet(prefix)) {
+ if (key.length() > prefix.length()) {
+ String value = vr.resolve(context.getMessages().getString(key));
+ String[] parts = key.substring(prefix.length() + 1).split("\\.");
+ int httpCode = Integer.parseInt(parts[0]);
+ ResponseInfo r2 = m.get(httpCode);
+ if (r2 == null) {
+ r2 = responseInfo(null);
+ m.put(httpCode, r2);
+ }
+
+ String name = parts.length > 1 ? parts[1] : "";
+
+ if ("header".equals(name) && parts.length > 3) {
+ String headerName = parts[2];
+ String field = parts[3];
+
+ String k2 = httpCode + '.' + headerName;
+ HeaderInfo h = m2.get(k2);
+ if (h == null) {
+ h = headerInfoStrict("string");
+ m2.put(k2, h);
+ r2.header(name, h);
+ }
+ if (field.equals("collectionFormat"))
+ h.collectionFormat(value);
+ else if (field.equals("default"))
+ h._default(value);
+ else if (field.equals("description"))
+ h.description(value);
+ else if (field.equals("format"))
+ h.format(value);
+ else if (field.equals("items"))
+ h.items(jp.parse(value, Items.class));
+ else if (field.equals("type"))
+ h.type(value);
+
+ } else if ("description".equals(name)) {
+ r2.description(value);
+ } else if ("schema".equals(name)) {
+ r2.schema(jp.parse(value, SchemaInfo.class));
+ } else if ("examples".equals(name)) {
+ r2.examples(jp.parse(value, TreeMap.class));
+ } else {
+ System.err.println("Unknown bundle key '"+key+"'");
+ }
+ }
+ }
+
+ return m.isEmpty() ? null : m;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified request object can call this method.
+ */
+ boolean isRequestAllowed(RestRequest req) {
+ for (RestGuard guard : guards) {
+ req.setJavaMethod(method);
+ if (! guard.isRequestAllowed(req))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Workhorse method.
+ *
+ * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
+ * @return The HTTP response code.
+ */
+ int invoke(String pathInfo, RestRequest req, RestResponse res) throws RestException {
+
+ String[] patternVals = pathPattern.match(pathInfo);
+ if (patternVals == null)
+ return SC_NOT_FOUND;
+
+ String remainder = null;
+ if (patternVals.length > pathPattern.getVars().length)
+ remainder = patternVals[pathPattern.getVars().length];
+ for (int i = 0; i < pathPattern.getVars().length; i++)
+ req.setPathParameter(pathPattern.getVars()[i], patternVals[i]);
+
+ req.init(method, remainder, createRequestProperties(properties, req), defaultRequestHeaders, defaultEncoding, serializers, parsers, urlEncodingParser, encoders);
+ res.init(req.getProperties(), defaultEncoding, serializers, urlEncodingSerializer, encoders);
+
+ // Class-level guards
+ for (RestGuard guard : context.getGuards())
+ if (! guard.guard(req, res))
+ return SC_UNAUTHORIZED;
+
+ // If the method implements matchers, test them.
+ for (RestMatcher m : requiredMatchers)
+ if (! m.matches(req))
+ return SC_PRECONDITION_FAILED;
+ if (optionalMatchers.length > 0) {
+ boolean matches = false;
+ for (RestMatcher m : optionalMatchers)
+ matches |= m.matches(req);
+ if (! matches)
+ return SC_PRECONDITION_FAILED;
+ }
+
+ context.getCallHandler().onPreCall(req);
+
+ Object[] args = new Object[params.length];
+ for (int i = 0; i < params.length; i++) {
+ try {
+ args[i] = params[i].getValue(req, res);
+ } catch (RestException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RestException(SC_BAD_REQUEST,
+ "Invalid data conversion. Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.",
+ params[i].paramType.name(), params[i].name, params[i].type, method.getDeclaringClass().getName(), method.getName()
+ ).initCause(e);
+ }
+ }
+
+ try {
+
+ for (RestGuard guard : guards)
+ if (! guard.guard(req, res))
+ return SC_OK;
+
+ Object output = method.invoke(context.getResource(), args);
+ if (! method.getReturnType().equals(Void.TYPE))
+ if (output != null || ! res.getOutputStreamCalled())
+ res.setOutput(output);
+
+ context.getCallHandler().onPostCall(req, res);
+
+ if (res.hasOutput()) {
+ output = res.getOutput();
+ for (RestConverter converter : converters)
+ output = converter.convert(req, output, context.getBeanContext().getClassMetaForObject(output));
+ res.setOutput(output);
+ }
+ } catch (IllegalArgumentException e) {
+ throw new RestException(SC_BAD_REQUEST,
+ "Invalid argument type passed to the following method: ''{0}''.\n\tArgument types: {1}",
+ method.toString(), ClassUtils.getReadableClassNames(args)
+ );
+ } catch (InvocationTargetException e) {
+ Throwable e2 = e.getTargetException(); // Get the throwable thrown from the doX() method.
+ if (e2 instanceof RestException)
+ throw (RestException)e2;
+ if (e2 instanceof ParseException)
+ throw new RestException(SC_BAD_REQUEST, e2);
+ if (e2 instanceof InvalidDataConversionException)
+ throw new RestException(SC_BAD_REQUEST, e2);
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e2);
+ } catch (RestException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ return SC_OK;
+ }
+
+ /**
+ * This method creates all the request-time properties.
+ */
+ static ObjectMap createRequestProperties(final ObjectMap methodProperties, final RestRequest req) {
+ @SuppressWarnings("serial")
+ ObjectMap m = new ObjectMap() {
+ @Override /* Map */
+ public Object get(Object key) {
+ Object o = super.get(key);
+ if (o == null) {
+ String k = key.toString();
+ if (k.indexOf('.') != -1) {
+ String prefix = k.substring(0, k.indexOf('.'));
+ String remainder = k.substring(k.indexOf('.')+1);
+ if ("path".equals(prefix))
+ return req.getPathParameter(remainder);
+ if ("query".equals(prefix))
+ return req.getQueryParameter(remainder);
+ if ("formData".equals(prefix))
+ return req.getFormDataParameter(remainder);
+ if ("header".equals(prefix))
+ return req.getHeader(remainder);
+ }
+ if (k.equals(SERIALIZER_absolutePathUriBase)) {
+ int serverPort = req.getServerPort();
+ String serverName = req.getServerName();
+ return req.getScheme() + "://" + serverName + (serverPort == 80 || serverPort == 443 ? "" : ":" + serverPort);
+ }
+ if (k.equals(REST_servletPath))
+ return req.getServletPath();
+ if (k.equals(REST_servletURI))
+ return req.getServletURI();
+ if (k.equals(REST_relativeServletURI))
+ return req.getRelativeServletURI();
+ if (k.equals(REST_pathInfo))
+ return req.getPathInfo();
+ if (k.equals(REST_requestURI))
+ return req.getRequestURI();
+ if (k.equals(REST_method))
+ return req.getMethod();
+ if (k.equals(REST_servletTitle))
+ return req.getServletTitle();
+ if (k.equals(REST_servletDescription))
+ return req.getServletDescription();
+ if (k.equals(REST_methodSummary))
+ return req.getMethodSummary();
+ if (k.equals(REST_methodDescription))
+ return req.getMethodDescription();
+ o = req.getPathParameter(k);
+ if (o == null)
+ o = req.getHeader(k);
+ }
+ if (o instanceof String)
+ o = req.getVarResolverSession().resolve(o.toString());
+ return o;
+ }
+ };
+ m.setInner(methodProperties);
+ return m;
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return "SimpleMethod: name=" + httpMethod + ", path=" + pathPattern.getPatternString();
+ }
+
+ /*
+ * compareTo() method is used to keep SimpleMethods ordered in the CallRouter list.
+ * It maintains the order in which matches are made during requests.
+ */
+ @Override /* Comparable */
+ public int compareTo(CallMethod o) {
+ int c;
+
+ c = priority.compareTo(o.priority);
+ if (c != 0)
+ return c;
+
+ c = pathPattern.compareTo(o.pathPattern);
+ if (c != 0)
+ return c;
+
+ c = Utils.compare(o.requiredMatchers.length, requiredMatchers.length);
+ if (c != 0)
+ return c;
+
+ c = Utils.compare(o.optionalMatchers.length, optionalMatchers.length);
+ if (c != 0)
+ return c;
+
+ c = Utils.compare(o.guards.length, guards.length);
+ if (c != 0)
+ return c;
+
+ return 0;
+ }
+
+ @Override /* Object */
+ public boolean equals(Object o) {
+ if (! (o instanceof CallMethod))
+ return false;
+ return (compareTo((CallMethod)o) == 0);
+ }
+
+ @Override /* Object */
+ public int hashCode() {
+ return super.hashCode();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java b/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java
new file mode 100644
index 0000000..bbac14c
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/CallRouter.java
@@ -0,0 +1,98 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.util.*;
+
+import javax.servlet.http.*;
+
+/**
+ * Represents a group of CallMethods on a REST resource that handle the same HTTP Method name but
+ * with different paths/matchers/guards/etc...
+ * <p>
+ * Incoming requests for a particular HTTP method type (e.g. <js>"GET"</js>) are handed off to this class
+ * and then dispatched to the appropriate CallMethod.
+ */
+class CallRouter {
+ private final CallMethod[] callMethods;
+
+ private CallRouter(CallMethod[] callMethods) {
+ this.callMethods = callMethods;
+ }
+
+ /**
+ * Builder class.
+ */
+ static class Builder {
+ private List<CallMethod> childMethods = new ArrayList<CallMethod>();
+ private Set<String> collisions = new HashSet<String>();
+ private String httpMethodName;
+
+ Builder(String httpMethodName) {
+ this.httpMethodName = httpMethodName;
+ }
+
+ String getHttpMethodName() {
+ return httpMethodName;
+ }
+
+ Builder add(CallMethod m) throws RestServletException {
+ if (! m.hasGuardsOrMatchers()) {
+ String p = m.getHttpMethod() + ":" + m.getPathPattern();
+ if (collisions.contains(p))
+ throw new RestServletException("Duplicate Java methods assigned to the same method/pattern: ''{0}''", p);
+ collisions.add(p);
+ }
+ childMethods.add(m);
+ return this;
+ }
+
+ CallRouter build() {
+ Collections.sort(childMethods);
+ return new CallRouter(childMethods.toArray(new CallMethod[childMethods.size()]));
+ }
+ }
+
+ /**
+ * Workhorse method.
+ * <p>
+ * Routes this request to one of the CallMethods.
+ *
+ * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta)
+ * @return The HTTP response code.
+ */
+ int invoke(String pathInfo, RestRequest req, RestResponse res) throws RestException {
+ if (callMethods.length == 1)
+ return callMethods[0].invoke(pathInfo, req, res);
+
+ int maxRc = 0;
+ for (CallMethod m : callMethods) {
+ int rc = m.invoke(pathInfo, req, res);
+ if (rc == SC_OK)
+ return SC_OK;
+ maxRc = Math.max(maxRc, rc);
+ }
+ return maxRc;
+ }
+
+ @Override /* Object */
+ public String toString() {
+ StringBuilder sb = new StringBuilder("CallRouter: [\n");
+ for (CallMethod sm : callMethods)
+ sb.append("\t" + sm + "\n");
+ sb.append("]");
+ return sb.toString();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java b/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
index 4f65615..ccf4dd1 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/ClientVersionMatcher.java
@@ -20,7 +20,7 @@ import org.apache.juneau.rest.annotation.*;
* <p>
* See {@link RestResource#clientVersionHeader} and {@link RestMethod#clientVersion} for more info.
*/
-public class ClientVersionMatcher extends RestMatcherReflecting {
+public class ClientVersionMatcher extends RestMatcher {
private final String clientVersionHeader;
private final VersionRange range;
@@ -28,12 +28,12 @@ public class ClientVersionMatcher extends RestMatcherReflecting {
/**
* Constructor.
*
- * @param servlet The servlet.
+ * @param clientVersionHeader The HTTP request header name containing the client version.
+ * If <jk>null</jk> or an empty string, uses <js>"X-Client-Version"</js>
* @param javaMethod The version string that the client version must match.
*/
- protected ClientVersionMatcher(RestServlet servlet, java.lang.reflect.Method javaMethod) {
- super(servlet, javaMethod);
- this.clientVersionHeader = servlet.getClientVersionHeader();
+ protected ClientVersionMatcher(String clientVersionHeader, java.lang.reflect.Method javaMethod) {
+ this.clientVersionHeader = StringUtils.isEmpty(clientVersionHeader) ? "X-Client-Version" : clientVersionHeader;
RestMethod m = javaMethod.getAnnotation(RestMethod.class);
range = new VersionRange(m.clientVersion());
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
index 348346a..f08d679 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/Redirect.java
@@ -15,7 +15,6 @@ package org.apache.juneau.rest;
import java.net.*;
import java.text.*;
-import org.apache.juneau.*;
import org.apache.juneau.urlencoding.*;
/**
@@ -62,7 +61,7 @@ import org.apache.juneau.urlencoding.*;
* </p>
* <p>
* This class is handled by {@link org.apache.juneau.rest.response.RedirectHandler}, a built-in default
- * response handler created by {@link RestServlet#createResponseHandlers(ObjectMap)}.
+ * response handler created in {@link RestConfig}.
*/
public final class Redirect {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
index 6607e64..da7c3e9 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/ResponseHandler.java
@@ -30,7 +30,7 @@ import org.apache.juneau.rest.response.*;
* Response handlers can be associated with {@link RestServlet RestServlets} through the following ways:
* <ul class='spaced-list'>
* <li>Through the {@link RestResource#responseHandlers @RestResource.responseHandlers} annotation.
- * <li>By overriding {@link RestServlet#createResponseHandlers(ObjectMap)} and augmenting or creating your
+ * <li>By calling the {@link RestConfig#addResponseHandlers(Class...)} and augmenting or creating your
* own list of handlers.
* </ul>
* <p>
@@ -40,6 +40,8 @@ import org.apache.juneau.rest.response.*;
* <li>{@link ReaderHandler} - Pipes the output of {@link Reader Readers} to the response writer ({@link RestResponse#getWriter()}).
* <li>{@link InputStreamHandler} - Pipes the output of {@link InputStream InputStreams} to the response output stream ({@link RestResponse#getOutputStream()}).
* <li>{@link RedirectHandler} - Handles {@link Redirect} objects.
+ * <li>{@link WritableHandler} - Handles {@link Writable} objects.
+ * <li>{@link StreamableHandler} - Handles {@link Streamable} objects.
* </ul>
* <p>
* Response handlers can be used to process POJOs that cannot normally be handled through Juneau serializers, or
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java
new file mode 100644
index 0000000..5d6a780
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestCallHandler.java
@@ -0,0 +1,348 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static java.util.logging.Level.*;
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.vars.*;
+
+/**
+ * Class that handles the basic lifecycle of an HTTP REST call.
+ * <p>
+ * Subclasses can override these methods to tailor how HTTP REST calls are handled.
+ * Subclasses MUST implement a public constructor that takes in a {@link RestContext} object.
+ * <p>
+ * RestCallHandlers are associated with servlets/resources in one of the following ways:
+ * <ul>
+ * <li>The {@link RestResource#callHandler @RestResource.callHandler()} annotation.
+ * <li>The {@link RestConfig#setCallHandler(Class)} method.
+ * </ul>
+ */
+public class RestCallHandler {
+
+ private final RestContext context;
+ private final RestLogger logger;
+ private final RestServlet restServlet;
+ private final Map<String,CallRouter> callRouters;
+
+ /**
+ * Constructor.
+ * @param context The resource context.
+ */
+ public RestCallHandler(RestContext context) {
+ this.context = context;
+ this.logger = context.getLogger();
+ this.callRouters = context.getCallRouters();
+ this.restServlet = context.getRestServlet(); // Null if this isn't a RestServlet!
+ }
+
+ /**
+ * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object.
+ * <p>
+ * Subclasses may choose to override this method to provide a specialized request object.
+ *
+ * @param req The request object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
+ * @return The wrapped request object.
+ * @throws ServletException If any errors occur trying to interpret the request.
+ */
+ protected RestRequest createRequest(HttpServletRequest req) throws ServletException {
+ return new RestRequest(context, req);
+ }
+
+ /**
+ * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object
+ * and the request returned by {@link #createRequest(HttpServletRequest)}.
+ * <p>
+ * Subclasses may choose to override this method to provide a specialized response object.
+ *
+ * @param req The request object returned by {@link #createRequest(HttpServletRequest)}.
+ * @param res The response object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
+ * @return The wrapped response object.
+ * @throws ServletException If any errors occur trying to interpret the request or response.
+ */
+ protected RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException {
+ return new RestResponse(context, req, res);
+ }
+
+ /**
+ * The main service method.
+ * <p>
+ * Subclasses can optionally override this method if they want to tailor the behavior of requests.
+ *
+ * @param r1 The incoming HTTP servlet request object.
+ * @param r2 The incoming HTTP servlet response object.
+ * @throws ServletException
+ * @throws IOException
+ */
+ protected void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
+
+ logger.log(FINE, "HTTP: {0} {1}", r1.getMethod(), r1.getRequestURI());
+ long startTime = System.currentTimeMillis();
+
+ try {
+ context.checkForInitException();
+
+ String pathInfo = RestUtils.getPathInfoUndecoded(r1); // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
+
+ // If this resource has child resources, try to recursively call them.
+ if (pathInfo != null && context.hasChildResources() && (! pathInfo.equals("/"))) {
+ int i = pathInfo.indexOf('/', 1);
+ String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
+ RestContext childResource = context.getChildResource(pathInfoPart);
+ if (childResource != null) {
+ final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
+ final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
+ final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
+ @Override /* ServletRequest */
+ public String getPathInfo() {
+ return RestUtils.decode(pathInfoRemainder);
+ }
+ @Override /* ServletRequest */
+ public String getServletPath() {
+ return servletPath;
+ }
+ };
+ childResource.getCallHandler().service(childRequest, r2);
+ return;
+ }
+ }
+
+ RestRequest req = createRequest(r1);
+ RestResponse res = createResponse(req, r2);
+ String method = req.getMethod();
+ String methodUC = method.toUpperCase(Locale.ENGLISH);
+
+ StreamResource r = null;
+ if (pathInfo != null) {
+ String p = pathInfo.substring(1);
+ if (p.equals("favicon.ico"))
+ r = context.getFavIcon();
+ else if (p.equals("style.css"))
+ r = context.getStyleSheet();
+ else if (context.isStaticFile(p))
+ r = context.resolveStaticFile(p);
+ }
+
+ if (r != null) {
+ res.setStatus(SC_OK);
+ res.setOutput(r);
+ } else {
+ // If the specified method has been defined in a subclass, invoke it.
+ int rc = SC_METHOD_NOT_ALLOWED;
+ if (callRouters.containsKey(methodUC)) {
+ rc = callRouters.get(methodUC).invoke(pathInfo, req, res);
+ } else if (callRouters.containsKey("*")) {
+ rc = callRouters.get("*").invoke(pathInfo, req, res);
+ }
+
+ // If not invoked above, see if it's an OPTIONs request
+ if (rc != SC_OK)
+ handleNotFound(rc, req, res);
+ }
+
+ if (res.hasOutput()) {
+ Object output = res.getOutput();
+
+ // Do any class-level transforming.
+ for (RestConverter converter : context.getConverters())
+ output = converter.convert(req, output, context.getBeanContext().getClassMetaForObject(output));
+
+ res.setOutput(output);
+
+ // Now serialize the output if there was any.
+ // Some subclasses may write to the OutputStream or Writer directly.
+ handleResponse(req, res, output);
+ }
+
+ onSuccess(req, res, System.currentTimeMillis() - startTime);
+
+ } catch (RestException e) {
+ handleError(r1, r2, e);
+ } catch (Throwable e) {
+ handleError(r1, r2, new RestException(SC_INTERNAL_SERVER_ERROR, e));
+ }
+ logger.log(FINE, "HTTP: [{0} {1}] finished in {2}ms", r1.getMethod(), r1.getRequestURI(), System.currentTimeMillis()-startTime);
+ }
+
+ /**
+ * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method or returned by
+ * the Java method.
+ * <p>
+ * Subclasses may override this method if they wish to modify the way the output is rendered or support
+ * other output formats.
+ * <p>
+ * The default implementation simply iterates through the response handlers on this resource
+ * looking for the first one whose {@link ResponseHandler#handle(RestRequest, RestResponse, Object)} method returns <jk>true</jk>.
+ *
+ * @param req The HTTP request.
+ * @param res The HTTP response.
+ * @param output The output to serialize in the response.
+ * @throws IOException
+ * @throws RestException
+ */
+ protected void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException {
+ // Loop until we find the correct handler for the POJO.
+ for (ResponseHandler h : context.getResponseHandlers())
+ if (h.handle(req, res, output))
+ return;
+ throw new RestException(SC_NOT_IMPLEMENTED, "No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'");
+ }
+
+ /**
+ * Handle the case where a matching method was not found.
+ * <p>
+ * Subclasses can override this method to provide a 2nd-chance for specifying a response.
+ * The default implementation will simply throw an exception with an appropriate message.
+ *
+ * @param rc The HTTP response code.
+ * @param req The HTTP request.
+ * @param res The HTTP response.
+ * @throws Exception
+ */
+ protected void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception {
+ String pathInfo = req.getPathInfo();
+ String methodUC = req.getMethod();
+ String onPath = pathInfo == null ? " on no pathInfo" : String.format(" on path '%s'", pathInfo);
+ if (rc == SC_NOT_FOUND)
+ throw new RestException(rc, "Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath);
+ else if (rc == SC_PRECONDITION_FAILED)
+ throw new RestException(rc, "Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath);
+ else if (rc == SC_METHOD_NOT_ALLOWED)
+ throw new RestException(rc, "Method ''{0}'' not found on resource.", methodUC);
+ else
+ throw new ServletException("Invalid method response: " + rc);
+ }
+
+ /**
+ * Method for handling response errors.
+ * <p>
+ * The default implementation logs the error and calls {@link #renderError(HttpServletRequest,HttpServletResponse,RestException)}.
+ * <p>
+ * Subclasses can override this method to provide their own custom error response handling.
+ *
+ * @param req The servlet request.
+ * @param res The servlet response.
+ * @param e The exception that occurred.
+ * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
+ */
+ protected synchronized void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
+ e.setOccurrence(context == null ? 0 : context.getStackTraceOccurrence(e));
+ logger.onError(req, res, e);
+ renderError(req, res, e);
+ }
+
+ /**
+ * Method for rendering response errors.
+ * <p>
+ * The default implementation renders a plain text English message, optionally with a stack trace
+ * if {@link RestContext#REST_renderResponseStackTraces} is enabled.
+ * <p>
+ * Subclasses can override this method to provide their own custom error response handling.
+ *
+ * @param req The servlet request.
+ * @param res The servlet response.
+ * @param e The exception that occurred.
+ * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
+ */
+ protected void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
+
+ int status = e.getStatus();
+ res.setStatus(status);
+ res.setContentType("text/plain");
+ res.setHeader("Content-Encoding", "identity");
+ PrintWriter w = null;
+ try {
+ w = res.getWriter();
+ } catch (IllegalStateException e2) {
+ w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), IOUtils.UTF8));
+ }
+ String httpMessage = RestUtils.getHttpResponseText(status);
+ if (httpMessage != null)
+ w.append("HTTP ").append(String.valueOf(status)).append(": ").append(httpMessage).append("\n\n");
+ if (context != null && context.isRenderResponseStackTraces())
+ e.printStackTrace(w);
+ else
+ w.append(e.getFullStackMessage(true));
+ w.flush();
+ w.close();
+ }
+
+ /**
+ * Callback method for listening for successful completion of requests.
+ * <p>
+ * Subclasses can override this method for gathering performance statistics.
+ * <p>
+ * The default implementation does nothing.
+ *
+ * @param req The HTTP request.
+ * @param res The HTTP response.
+ * @param time The time in milliseconds it took to process the request.
+ */
+ protected void onSuccess(RestRequest req, RestResponse res, long time) {
+ if (restServlet != null)
+ restServlet.onSuccess(req, res, time);
+ }
+
+ /**
+ * Callback method that gets invoked right before the REST Java method is invoked.
+ * <p>
+ * Subclasses can override this method to override request headers or set request-duration properties
+ * before the Java method is invoked.
+ *
+ * @param req The HTTP servlet request object.
+ * @throws RestException If any error occurs.
+ */
+ protected void onPreCall(RestRequest req) throws RestException {
+ if (restServlet != null)
+ restServlet.onPreCall(req);
+ }
+
+ /**
+ * Callback method that gets invoked right after the REST Java method is invoked, but before
+ * the serializer is invoked.
+ * <p>
+ * Subclasses can override this method to override request and response headers, or
+ * set/override properties used by the serializer.
+ *
+ * @param req The HTTP servlet request object.
+ * @param res The HTTP servlet response object.
+ * @throws RestException If any error occurs.
+ */
+ protected void onPostCall(RestRequest req, RestResponse res) throws RestException {
+ if (restServlet != null)
+ restServlet.onPostCall(req, res);
+ }
+
+ /**
+ * Returns the session objects for the specified request.
+ * <p>
+ * The default implementation simply returns a single map containing <code>{'req':req}</code>.
+ *
+ * @param req The REST request.
+ * @return The session objects for that request.
+ */
+ public Map<String,Object> getSessionObjects(RestRequest req) {
+ Map<String,Object> m = new HashMap<String,Object>();
+ m.put(RequestVar.SESSION_req, req);
+ return m;
+ }
+}
[3/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java
new file mode 100644
index 0000000..2b55b47
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java
@@ -0,0 +1,233 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.text.*;
+import java.util.logging.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.rest.annotation.*;
+
+/**
+ * Logging utility class.
+ * <p>
+ * Subclasses can override these methods to tailor logging of HTTP requests.
+ * Subclasses MUST implement a no-arg public constructor.
+ * <p>
+ * RestLoggers are associated with servlets/resources in one of the following ways:
+ * <ul>
+ * <li>The {@link RestResource#logger @RestResource.logger()} annotation.
+ * <li>The {@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods.
+ * </ul>
+ */
+public abstract class RestLogger {
+
+ /**
+ * Returns the Java logger used for logging.
+ * <p>
+ * Subclasses can provide their own logger.
+ * The default implementation returns the logger created using <code>Logger.getLogger(getClass())</code>.
+ *
+ * @return The logger used for logging.
+ */
+ protected abstract Logger getLogger();
+
+ /**
+ * Log a message to the logger.
+ * <p>
+ * Subclasses can override this method if they wish to log messages using a library other than
+ * Java Logging (e.g. Apache Commons Logging).
+ *
+ * @param level The log level.
+ * @param cause The cause.
+ * @param msg The message to log.
+ * @param args Optional {@link MessageFormat}-style arguments.
+ */
+ protected abstract void log(Level level, Throwable cause, String msg, Object...args);
+
+ /**
+ * Log a message.
+ * <p>
+ * Equivalent to calling <code>log(level, <jk>null</jk>, msg, args);</code>
+ *
+ * @param level The log level.
+ * @param msg The message to log.
+ * @param args Optional {@link MessageFormat}-style arguments.
+ */
+ protected void log(Level level, String msg, Object...args) {
+ log(level, null, msg, args);
+ }
+
+ /**
+ * Same as {@link #log(Level, String, Object...)} excepts runs the arguments through {@link JsonSerializer#DEFAULT_LAX_READABLE}.
+ * <p>
+ * Serialization of arguments do not occur if message is not logged, so it's safe to use this method from within debug log statements.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode'>
+ * logObjects(<jsf>DEBUG</jsf>, <js>"Pojo contents:\n{0}"</js>, myPojo);
+ * </p>
+ *
+ * @param level The log level.
+ * @param msg The message to log.
+ * @param args Optional {@link MessageFormat}-style arguments.
+ */
+ protected void logObjects(Level level, String msg, Object...args) {
+ for (int i = 0; i < args.length; i++)
+ args[i] = JsonSerializer.DEFAULT_LAX_READABLE.toStringObject(args[i]);
+ log(level, null, msg, args);
+ }
+
+ /**
+ * Callback method for logging errors during HTTP requests.
+ * <p>
+ * Typically, subclasses will override this method and log errors themselves.
+ * <p>
+ * The default implementation simply logs errors to the <code>RestServlet</code> logger.
+ * <p>
+ * Here's a typical implementation showing how stack trace hashing can be used to reduce log file sizes...
+ * </p>
+ * <p class='bcode'>
+ * <jk>protected void</jk> onError(HttpServletRequest req, HttpServletResponse res, RestException e, <jk>boolean</jk> noTrace) {
+ * String qs = req.getQueryString();
+ * String msg = <js>"HTTP "</js> + req.getMethod() + <js>" "</js> + e.getStatus() + <js>" "</js> + req.getRequestURI() + (qs == <jk>null</jk> ? <js>""</js> : <js>"?"</js> + qs);
+ * <jk>int</jk> c = e.getOccurrence();
+ *
+ * <jc>// REST_useStackTraceHashes is disabled, so we have to log the exception every time.</jc>
+ * <jk>if</jk> (c == 0)
+ * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%s] %s"</js>, e.getStatus(), msg), e);
+ *
+ * <jc>// This is the first time we've countered this error, so log a stack trace
+ * // unless ?noTrace was passed in as a URL parameter.</jc>
+ * <jk>else if</jk> (c == 1 && ! noTrace)
+ * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s"</js>, e.hashCode(), e.getStatus(), c, msg), e);
+ *
+ * <jc>// This error occurred before.
+ * // Only log the message, not the stack trace.</jc>
+ * <jk>else</jk>
+ * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s, %s"</js>, e.hashCode(), e.getStatus(), c, msg, e.getLocalizedMessage()));
+ * }
+ * </p>
+ *
+ * @param req The servlet request object.
+ * @param res The servlet response object.
+ * @param e Exception indicating what error occurred.
+ */
+ protected void onError(HttpServletRequest req, HttpServletResponse res, RestException e) {
+ if (shouldLog(req, res, e)) {
+ String qs = req.getQueryString();
+ String msg = "HTTP " + req.getMethod() + " " + e.getStatus() + " " + req.getRequestURI() + (qs == null ? "" : "?" + qs);
+ int c = e.getOccurrence();
+ if (shouldLogStackTrace(req, res, e)) {
+ msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg;
+ log(Level.WARNING, e, msg);
+ } else {
+ msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg + ", " + e.getLocalizedMessage();
+ log(Level.WARNING, msg);
+ }
+ }
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified exception should be logged.
+ * <p>
+ * Subclasses can override this method to provide their own logic for determining when exceptions are logged.
+ * <p>
+ * The default implementation will return <jk>false</jk> if <js>"noTrace=true"</js> is passed in the query string.
+ *
+ * @param req The HTTP request.
+ * @param res The HTTP response.
+ * @param e The exception.
+ * @return <jk>true</jk> if exception should be logged.
+ */
+ protected boolean shouldLog(HttpServletRequest req, HttpServletResponse res, RestException e) {
+ String q = req.getQueryString();
+ return (q == null ? true : q.indexOf("noTrace=true") == -1);
+ }
+
+ /**
+ * Returns <jk>true</jk> if a stack trace should be logged for this exception.
+ * <p>
+ * Subclasses can override this method to provide their own logic for determining when stack traces are logged.
+ * <p>
+ * The default implementation will only log a stack trace if {@link RestException#getOccurrence()} returns <code>1</code>
+ * and the exception is not one of the following:
+ * </p>
+ * <ul>
+ * <li>{@link HttpServletResponse#SC_UNAUTHORIZED}
+ * <li>{@link HttpServletResponse#SC_FORBIDDEN}
+ * <li>{@link HttpServletResponse#SC_NOT_FOUND}
+ * </ul>
+ *
+ * @param req The HTTP request.
+ * @param res The HTTP response.
+ * @param e The exception.
+ * @return <jk>true</jk> if stack trace should be logged.
+ */
+ protected boolean shouldLogStackTrace(HttpServletRequest req, HttpServletResponse res, RestException e) {
+ if (e.getOccurrence() == 1) {
+ switch (e.getStatus()) {
+ case SC_UNAUTHORIZED:
+ case SC_FORBIDDEN:
+ case SC_NOT_FOUND: return false;
+ default: return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * NO-OP logger.
+ * <p>
+ * Disables all logging.
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+ public static class NoOp extends RestLogger {
+
+ @Override /* RestLogger */
+ protected Logger getLogger() {
+ return null;
+ }
+
+ @Override /* RestLogger */
+ protected void log(Level level, Throwable cause, String msg, Object...args) {}
+ }
+
+ /**
+ * Default logger.
+ * <p>
+ * Logs all messages to the logger returned by <code>Logger.<jsm>getLogger</jsm>(getClass().getName())</code>
+ */
+ public static class Normal extends RestLogger {
+
+ private final JuneauLogger logger = JuneauLogger.getLogger(getClass());
+
+ @Override /* RestLogger */
+ protected Logger getLogger() {
+ return logger;
+ }
+
+ @Override /* RestLogger */
+ protected void log(Level level, Throwable cause, String msg, Object...args) {
+ if (args.length > 0)
+ msg = MessageFormat.format(msg, args);
+ getLogger().log(level, msg, cause);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
index 4a2619d..2a1cc16 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java
@@ -15,7 +15,7 @@ package org.apache.juneau.rest;
import java.lang.reflect.*;
/**
- * Subclass of {@link RestMatcher} that gives access to the servlet and Java method it's applied to.
+ * Subclass of {@link RestMatcher} that gives access to the servlet/resource and Java method it's applied to.
* <p>
* Essentially the same as {@link RestMatcher} except has a constructor where the
* Java method is passed in so that you can access annotations defined on it to tailor
@@ -26,8 +26,8 @@ public abstract class RestMatcherReflecting extends RestMatcher {
/**
* Constructor.
*
- * @param servlet The REST servlet.
+ * @param resource The REST servlet.
* @param javaMethod The Java method that this rest matcher is defined on.
*/
- protected RestMatcherReflecting(RestServlet servlet, Method javaMethod) {}
+ protected RestMatcherReflecting(Object resource, Method javaMethod) {}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
index cc60feb..98aba93 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -19,6 +19,7 @@ import static javax.servlet.http.HttpServletResponse.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
+import java.nio.charset.*;
import java.text.*;
import java.util.*;
import java.util.logging.*;
@@ -63,13 +64,15 @@ import org.apache.juneau.utils.*;
@SuppressWarnings("unchecked")
public final class RestRequest extends HttpServletRequestWrapper {
- private final RestServlet servlet;
+ private final RestContext context;
+
private final String method;
private String pathRemainder, body;
private Method javaMethod;
private ObjectMap properties;
private SerializerGroup serializerGroup;
private ParserGroup parserGroup;
+ private EncoderGroup encoders;
private Encoder encoder;
private int contentLength;
private final boolean debug;
@@ -89,17 +92,17 @@ public final class RestRequest extends HttpServletRequestWrapper {
/**
* Constructor.
*/
- RestRequest(RestServlet servlet, HttpServletRequest req) throws ServletException {
+ RestRequest(RestContext context, HttpServletRequest req) throws ServletException {
super(req);
+ this.context = context;
try {
- this.servlet = servlet;
isPost = req.getMethod().equalsIgnoreCase("POST");
// If this is a POST, we want to parse the query parameters ourselves to prevent
// the servlet code from processing the HTTP body as URL-Encoded parameters.
if (isPost)
- queryParams = servlet.getUrlEncodingParser().parseIntoSimpleMap(getQueryString());
+ queryParams = context.getUrlEncodingParser().parseIntoSimpleMap(getQueryString());
else {
queryParams = req.getParameterMap();
}
@@ -109,23 +112,23 @@ public final class RestRequest extends HttpServletRequestWrapper {
String _method = super.getMethod();
String m = getQueryParameter("method");
- if (! StringUtils.isEmpty(m) && (servlet.context.allowMethodParams.contains(m) || servlet.context.allowMethodParams.contains("*")))
+ if (context.allowMethodParam(m))
_method = m;
method = _method;
- if (servlet.context.allowBodyParam) {
+ if (context.isAllowBodyParam()) {
body = getQueryParameter("body");
if (body != null)
setHeader("Content-Type", UonSerializer.DEFAULT.getResponseContentType());
}
- defaultServletHeaders = servlet.getDefaultRequestHeaders();
+ defaultServletHeaders = context.getDefaultRequestHeaders();
debug = "true".equals(getQueryParameter("debug", "false"));
if (debug) {
- servlet.log(Level.INFO, toString());
+ context.getLogger().log(Level.INFO, toString());
}
} catch (RestException e) {
@@ -139,7 +142,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
* Called from RestServlet after a match has been made but before the guard or method invocation.
*/
@SuppressWarnings("hiding")
- final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser) {
+ final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser, EncoderGroup encoders) {
this.javaMethod = javaMethod;
this.pathRemainder = pathRemainder;
this.properties = properties;
@@ -149,6 +152,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
this.urlEncodingParser = mUrlEncodingParser;
this.beanSession = urlEncodingParser.getBeanContext().createSession();
this.defaultCharset = defaultCharset;
+ this.encoders = encoders;
}
/**
@@ -403,7 +407,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
}
if (charset == null)
charset = defaultCharset;
- if (! RestServlet.availableCharsets.containsKey(charset))
+ if (! Charset.isSupported(charset))
throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported charset in header ''Content-Type'': ''{0}''", h);
}
return charset;
@@ -1299,11 +1303,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
properties.append("mediaType", mediaType).append("characterEncoding", getCharacterEncoding());
if (! p.isReaderParser()) {
InputStreamParser p2 = (InputStreamParser)p;
- ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), getServlet(), locale, timeZone, mediaType);
+ ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType);
return p2.parseSession(session, cm);
}
ReaderParser p2 = (ReaderParser)p;
- ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), getServlet(), locale, timeZone, mediaType);
+ ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType);
return p2.parseSession(session, cm);
} catch (ParseException e) {
throw new RestException(SC_BAD_REQUEST,
@@ -1576,45 +1580,45 @@ public final class RestRequest extends HttpServletRequestWrapper {
/**
* Returns the localized servlet title.
* <p>
- * Equivalent to calling {@link RestServlet#getTitle(RestRequest)} with this object.
+ * Equivalent to calling {@link RestInfoProvider#getTitle(RestRequest)} with this object.
*
* @return The localized servlet label.
*/
public String getServletTitle() {
- return servlet.getTitle(this);
+ return context.getInfoProvider().getTitle(this);
}
/**
* Returns the localized servlet description.
* <p>
- * Equivalent to calling {@link RestServlet#getDescription(RestRequest)} with this object.
+ * Equivalent to calling {@link RestInfoProvider#getDescription(RestRequest)} with this object.
*
* @return The localized servlet description.
*/
public String getServletDescription() {
- return servlet.getDescription(this);
+ return context.getInfoProvider().getDescription(this);
}
/**
* Returns the localized method summary.
* <p>
- * Equivalent to calling {@link RestServlet#getMethodSummary(String, RestRequest)} with this object.
+ * Equivalent to calling {@link RestInfoProvider#getMethodSummary(String, RestRequest)} with this object.
*
* @return The localized method description.
*/
public String getMethodSummary() {
- return servlet.getMethodSummary(javaMethod.getName(), this);
+ return context.getInfoProvider().getMethodSummary(javaMethod.getName(), this);
}
/**
* Returns the localized method description.
* <p>
- * Equivalent to calling {@link RestServlet#getMethodDescription(String, RestRequest)} with this object.
+ * Equivalent to calling {@link RestInfoProvider#getMethodDescription(String, RestRequest)} with this object.
*
* @return The localized method description.
*/
public String getMethodDescription() {
- return servlet.getMethodDescription(javaMethod.getName(), this);
+ return context.getInfoProvider().getMethodDescription(javaMethod.getName(), this);
}
@@ -1713,14 +1717,14 @@ public final class RestRequest extends HttpServletRequestWrapper {
}
/**
- * Shortcut method for calling {@link RestServlet#getMessage(Locale, String, Object...)} based on the request locale.
+ * Shortcut method for calling {@link MessageBundle#getString(Locale, String, Object...)} based on the request locale.
*
* @param key The message key.
* @param args Optional {@link MessageFormat}-style arguments.
* @return The localized message.
*/
public String getMessage(String key, Object...args) {
- return servlet.getMessage(getLocale(), key, args);
+ return context.getMessages().getString(getLocale(), key, args);
}
/**
@@ -1729,7 +1733,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
* @return The resource bundle. Never <jk>null</jk>.
*/
public MessageBundle getResourceBundle() {
- return servlet.getMessages(getLocale());
+ return context.getMessages().getBundle(getLocale());
}
/**
@@ -1740,8 +1744,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
*
* @return The servlet handling the request.
*/
- public RestServlet getServlet() {
- return servlet;
+ public RestContext getContext() {
+ return context;
}
/**
@@ -1772,13 +1776,13 @@ public final class RestRequest extends HttpServletRequestWrapper {
}
/**
- * Returns the variable resolver session for this request using session objects created by {@link RestServlet#getSessionObjects(RestRequest)}.
+ * Returns the variable resolver session for this request using session objects created by {@link RestCallHandler#getSessionObjects(RestRequest)}.
*
* @return The variable resolver for this request.
*/
public VarResolverSession getVarResolverSession() {
if (varSession == null)
- varSession = servlet.getVarResolver().createSession(servlet.getSessionObjects(this));
+ varSession = context.getVarResolver().createSession(context.getCallHandler().getSessionObjects(this));
return varSession;
}
@@ -1803,7 +1807,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
* @throws IOException
*/
public ReaderResource getReaderResource(String name, boolean resolveVars, MediaType mediaType) throws IOException {
- String s = servlet.getResourceAsString(name, getLocale());
+ String s = context.getResourceAsString(name, getLocale());
if (s == null)
return null;
ReaderResource.Builder b = new ReaderResource.Builder().mediaType(mediaType).contents(s);
@@ -1813,8 +1817,8 @@ public final class RestRequest extends HttpServletRequestWrapper {
}
/**
- * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses {@link RestServlet#getMimetypesFileTypeMap()}
- * to determine the media type.
+ * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses the resource mime-type map
+ * constructed using {@link RestConfig#addMimeTypes(String...)} to determine the media type.
*
* @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}.
* @param resolveVars If <jk>true</jk>, any {@link org.apache.juneau.rest.annotation.Parameter} variables will be resolved by the variable resolver returned
@@ -1823,7 +1827,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
* @throws IOException
*/
public ReaderResource getReaderResource(String name, boolean resolveVars) throws IOException {
- return getReaderResource(name, resolveVars, MediaType.forString(servlet.getMimetypesFileTypeMap().getContentType(name)));
+ return getReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name)));
}
/**
@@ -1834,7 +1838,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
* @throws IOException
*/
public ReaderResource getReaderResource(String name) throws IOException {
- return getReaderResource(name, false, MediaType.forString(servlet.getMimetypesFileTypeMap().getContentType(name)));
+ return getReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name)));
}
/**
@@ -1842,9 +1846,9 @@ public final class RestRequest extends HttpServletRequestWrapper {
*
* @return The config file associated with the servlet, or <jk>null</jk> if servlet does not have a config file associated with it.
*/
- public ConfigFile getConfig() {
+ public ConfigFile getConfigFile() {
if (cf == null)
- cf = servlet.getConfig().getResolving(getVarResolverSession());
+ cf = context.getConfigFile().getResolving(getVarResolverSession());
return cf;
}
@@ -1855,7 +1859,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
public Swagger getSwagger() {
if (swagger == null)
- swagger = servlet.getSwagger(this);
+ swagger = context.getInfoProvider().getSwagger(this);
return swagger;
}
@@ -1871,7 +1875,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
protected Swagger getSwaggerFromFile() {
if (fileSwagger == null)
- fileSwagger = servlet.getSwaggerFromFile(this.getLocale());
+ fileSwagger = context.getInfoProvider().getSwaggerFromFile(this.getLocale());
if (fileSwagger == null)
fileSwagger = Swagger.NULL;
return fileSwagger == Swagger.NULL ? null : fileSwagger;
@@ -1895,7 +1899,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
sb.append(getBodyAsString()).append("\n");
} catch (Exception e1) {
sb.append(e1.getLocalizedMessage());
- servlet.log(WARNING, e1, "Error occurred while trying to read debug input.");
+ context.getLogger().log(WARNING, e1, "Error occurred while trying to read debug input.");
}
}
return sb.toString();
@@ -1942,11 +1946,11 @@ public final class RestRequest extends HttpServletRequestWrapper {
String ce = getHeader("content-encoding");
if (! (ce == null || ce.isEmpty())) {
ce = ce.trim();
- encoder = servlet.getEncoders().getEncoder(ce);
+ encoder = encoders.getEncoder(ce);
if (encoder == null)
throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE,
"Unsupported encoding in request header ''Content-Encoding'': ''{0}''\n\tSupported codings: {1}",
- getHeader("content-encoding"), servlet.getEncoders().getSupportedEncodings()
+ getHeader("content-encoding"), encoders.getSupportedEncodings()
);
}
@@ -1966,7 +1970,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
private String getOverriddenHeader(String name) {
String h = null;
- if (servlet.context.allowHeaderParams)
+ if (context.isAllowHeaderParams())
h = getQueryParameter(name);
if (h != null)
return h;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
new file mode 100644
index 0000000..7989eb9
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java
@@ -0,0 +1,80 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import java.lang.reflect.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.annotation.*;
+
+/**
+ * Class used to resolve {@link Class} objects to instances.
+ * <p>
+ * Used to convert classes defined via {@link RestResource#children() @RestResource.children()} into child instances.
+ * <p>
+ * Subclasses can be created to provide customized resource resolution.
+ * These can be associated with REST resources in one of the following ways:
+ * <ul>
+ * <li>{@link RestResource#resourceResolver() @RestResource.resourceResolver()} annotation.
+ * <li>{@link RestConfig#setResourceResolver(Class)}/{@link RestConfig#setResourceResolver(RestResourceResolver)} methods.
+ * </ul>
+ * <p>
+ * The default implementation simply instantiates the class using one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestConfig)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * </ul>
+ * The former constructor can be used to get access to the {@link RestConfig} object to get access to the
+ * config file and initialization information or make programmatic modifications to the resource before
+ * full initialization.
+ * <p>
+ * Non-<code>RestServlet</code> classes can also add the following two methods to get access to the
+ * {@link RestConfig} and {@link RestContext} objects:
+ * <ul>
+ * <li><jk>public void</jk> init(RestConfig);</code>
+ * <li><jk>public void</jk> init(RestContext);</code>
+ * </ul>
+ */
+public class RestResourceResolver {
+
+ /**
+ * Denotes the default resolver.
+ */
+ public static final class DEFAULT extends RestResourceResolver {}
+
+ /**
+ * Resolves the specified class to a resource object.
+ * <p>
+ * Subclasses can override this method to provide their own custom resolution.
+ * <p>
+ * The default implementation simply creates a new class instance using {@link Class#newInstance()}.
+ *
+ * @param c The class to resolve.
+ * @param config The initialization configuration for the resource.
+ * @return The instance of that class.
+ * @throws RestServletException If class could not be resolved.
+ */
+ public Object resolve(Class<?> c, RestConfig config) throws RestServletException {
+ try {
+ Constructor<?> c1 = ClassUtils.findPublicConstructor(c, RestConfig.class);
+ if (c1 != null)
+ return c1.newInstance(config);
+ c1 = ClassUtils.findPublicConstructor(c);
+ if (c1 != null)
+ return c1.newInstance();
+ } catch (Exception e) {
+ throw new RestServletException("Could not instantiate resource class ''{0}''", c.getName()).initCause(e);
+ }
+ throw new RestServletException("Could not find public constructor for class ''{0}''.", c);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
index 3fa5398..637dc7e 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -13,6 +13,7 @@
package org.apache.juneau.rest;
import java.io.*;
+import java.nio.charset.*;
import java.util.*;
import javax.servlet.*;
@@ -55,24 +56,22 @@ public final class RestResponse extends HttpServletResponseWrapper {
SerializerGroup serializerGroup;
UrlEncodingSerializer urlEncodingSerializer; // The serializer used to convert arguments passed into Redirect objects.
private EncoderGroup encoders;
- private final RestServlet servlet;
private ServletOutputStream os;
/**
* Constructor.
*/
- RestResponse(RestServlet servlet, RestRequest req, HttpServletResponse res) {
+ RestResponse(RestContext context, RestRequest req, HttpServletResponse res) {
super(res);
this.request = req;
- this.servlet = servlet;
- for (Map.Entry<String,Object> e : servlet.getDefaultResponseHeaders().entrySet())
+ for (Map.Entry<String,Object> e : context.getDefaultResponseHeaders().entrySet())
setHeader(e.getKey(), e.getValue().toString());
try {
String passThroughHeaders = req.getHeader("x-response-headers");
if (passThroughHeaders != null) {
- ObjectMap m = servlet.getUrlEncodingParser().parseParameter(passThroughHeaders, ObjectMap.class);
+ ObjectMap m = context.getUrlEncodingParser().parseParameter(passThroughHeaders, ObjectMap.class);
for (Map.Entry<String,Object> e : m.entrySet())
setHeader(e.getKey(), e.getValue().toString());
}
@@ -101,7 +100,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
MediaType mt = r.getMediaType();
if (mt.getType().equals("*"))
charset = defaultCharset;
- else if (RestServlet.availableCharsets.containsKey(mt.getType()))
+ else if (Charset.isSupported(mt.getType()))
charset = mt.getType();
if (charset != null)
break;
@@ -138,7 +137,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
* @throws RestServletException
*/
public List<String> getSupportedEncodings() throws RestServletException {
- return servlet.getEncoders().getSupportedEncodings();
+ return encoders.getSupportedEncodings();
}
/**
[2/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
index f876360..d7288d3 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -12,48 +12,17 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
-import static java.lang.String.*;
import static java.util.logging.Level.*;
import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.dto.swagger.SwaggerBuilder.*;
-import static org.apache.juneau.internal.ArrayUtils.*;
-import static org.apache.juneau.internal.ClassUtils.*;
-import static org.apache.juneau.rest.RestServlet.ParamType.*;
-import static org.apache.juneau.rest.RestServletContext.*;
-import static org.apache.juneau.rest.annotation.Inherit.*;
-import static org.apache.juneau.serializer.SerializerContext.*;
import java.io.*;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.*;
-import java.lang.reflect.Method;
-import java.nio.charset.*;
import java.text.*;
-import java.util.*;
-import java.util.concurrent.*;
import java.util.logging.*;
-import javax.activation.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.juneau.*;
-import org.apache.juneau.dto.swagger.*;
-import org.apache.juneau.encoders.*;
-import org.apache.juneau.encoders.Encoder;
-import org.apache.juneau.ini.*;
-import org.apache.juneau.internal.*;
-import org.apache.juneau.json.*;
-import org.apache.juneau.parser.*;
-import org.apache.juneau.parser.ParseException;
-import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.rest.annotation.Properties;
-import org.apache.juneau.rest.response.*;
-import org.apache.juneau.rest.vars.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.svl.*;
-import org.apache.juneau.svl.vars.*;
-import org.apache.juneau.urlencoding.*;
import org.apache.juneau.utils.*;
/**
@@ -61,3265 +30,253 @@ import org.apache.juneau.utils.*;
* <p>
* Refer to <a class="doclink" href="package-summary.html#TOC">REST Servlet API</a> for information about using this class.
*/
-@SuppressWarnings({"rawtypes","hiding"})
+@SuppressWarnings("hiding")
public abstract class RestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
- static final SortedMap<String,Charset> availableCharsets = new TreeMap<String,Charset>(String.CASE_INSENSITIVE_ORDER);
- static {
- availableCharsets.putAll(Charset.availableCharsets());
- }
- // Map of HTTP method names (e.g. GET/PUT/...) to ResourceMethod implementations for it. Populated during resource initialization.
- private final Map<String,ResourceMethod> restMethods = new LinkedHashMap<String,ResourceMethod>();
-
- // The list of all @RestMethod annotated methods in the order they appear in the class.
- private final Map<String,MethodMeta> javaRestMethods = new LinkedHashMap<String,MethodMeta>();
-
- // Child resources of this resource defined through getX() methods on this class.
- private final Map<String,RestServlet> childResources = new LinkedHashMap<String,RestServlet>();
-
- private RestServlet parentResource;
-
- private ServletConfig servletConfig;
- private volatile boolean isInitialized = false;
- private Exception initException; // Exception thrown by init() method (cached so it can be thrown on all subsequent requests).
- private JuneauLogger logger;
- private MessageBundle msgs; // NLS messages.
-
- private Map<Integer,Integer> stackTraceHashes = new HashMap<Integer,Integer>();
- private String path;
-
- private LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsChildFirst, restResourceAnnotationsParentFirst;
-
- private UrlEncodingSerializer urlEncodingSerializer;
- private UrlEncodingParser urlEncodingParser;
- private ObjectMap properties;
- private RestGuard[] guards;
- private Class<?>[] beanFilters, pojoSwaps;
- private RestConverter[] converters;
- private TreeMap<String,String> defaultRequestHeaders;
- private Map<String,Object> defaultResponseHeaders;
- private EncoderGroup encoders;
- private SerializerGroup serializers;
- private ParserGroup parsers;
- private MimetypesFileTypeMap mimetypesFileTypeMap;
- private BeanContext beanContext;
- private VarResolver varResolver;
- private String title, description, termsOfService, contact, license, version, tags, externalDocs;
- private Map<String,byte[]> resourceStreams = new ConcurrentHashMap<String,byte[]>();
- private Map<String,String> resourceStrings = new ConcurrentHashMap<String,String>();
- private ConfigFile configFile, resolvingConfigFile;
- private String configPath;
- private StreamResource styleSheet, favIcon;
- private Map<String,String> staticFilesMap;
- private String[] staticFilesPrefixes;
- private ResponseHandler[] responseHandlers;
- private String clientVersionHeader = "";
- private ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<Locale,Swagger>();
-
- RestServletContext context;
-
- // In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package.
- private Map<String,StreamResource> staticFilesCache = new ConcurrentHashMap<String,StreamResource>();
-
- // The following code block is executed before the constructor is called to
- // allow the config file to be accessed during object creation.
- // e.g. private String myConfig = getConfig().getString("myConfig");
- {
- VarResolverBuilder vrb = createVarResolver();
-
- // @RestResource annotations from bottom to top.
- restResourceAnnotationsChildFirst = ReflectionUtils.findAnnotationsMap(RestResource.class, getClass());
-
- // @RestResource annotations from top to bottom.
- restResourceAnnotationsParentFirst = CollectionUtils.reverse(restResourceAnnotationsChildFirst);
-
- for (RestResource r : restResourceAnnotationsParentFirst.values()) {
- if (! r.config().isEmpty())
- configPath = r.config();
- }
-
- try {
- configFile = createConfigFile(vrb);
- vrb.contextObject(ConfigFileVar.SESSION_config, configFile);
- } catch (IOException e) {
- this.initException = e;
- }
+ private RestConfig config;
+ private RestContext context;
+ private boolean isInitialized = false;
+ private Exception initException;
- varResolver = vrb.build();
- }
@Override /* Servlet */
- public synchronized void init(ServletConfig servletConfig) throws ServletException {
+ public final synchronized void init(ServletConfig servletConfig) throws ServletException {
try {
- log(FINE, "Servlet {0} init called.", getClass().getName());
- this.servletConfig = servletConfig;
-
- if (isInitialized)
- return;
-
- super.init(servletConfig);
-
- // Find resource resource bundle location.
- for (Map.Entry<Class<?>,RestResource> e : restResourceAnnotationsChildFirst.entrySet()) {
- Class<?> c = e.getKey();
- RestResource r = e.getValue();
- if (! r.messages().isEmpty()) {
- if (msgs == null)
- msgs = new MessageBundle(c, r.messages());
- else
- msgs.addSearchPath(c, r.messages());
- }
- }
- for (RestResource r : restResourceAnnotationsParentFirst.values()) {
- if (! r.title().isEmpty())
- title = r.title();
- if (! r.description().isEmpty())
- description = r.description();
- if (! r.clientVersionHeader().isEmpty())
- clientVersionHeader = r.clientVersionHeader();
- if (! r.termsOfService().isEmpty())
- termsOfService = r.termsOfService();
- if (! r.contact().isEmpty())
- contact = r.contact();
- if (! r.license().isEmpty())
- license = r.license();
- if (! r.version().isEmpty())
- version = r.version();
- if (! r.tags().isEmpty())
- tags = r.tags();
- if (! r.externalDocs().isEmpty())
- externalDocs = r.externalDocs();
- }
-
- if (msgs == null)
- msgs = new MessageBundle(this.getClass(), "");
- if (clientVersionHeader.isEmpty())
- clientVersionHeader = "X-Client-Version";
-
- styleSheet = createStyleSheet();
- favIcon = createFavIcon();
- staticFilesMap = Collections.unmodifiableMap(createStaticFilesMap());
- staticFilesPrefixes = staticFilesMap.keySet().toArray(new String[0]);
-
- properties = createProperties();
- beanFilters = createBeanFilters();
- pojoSwaps = createPojoSwaps();
- context = PropertyStore.create().setProperties(properties).getContext(RestServletContext.class);
- beanContext = createBeanContext(properties, beanFilters, pojoSwaps);
- urlEncodingSerializer = createUrlEncodingSerializer(properties, beanFilters, pojoSwaps).build();
- urlEncodingParser = createUrlEncodingParser(properties, beanFilters, pojoSwaps).build();
- serializers = createSerializers(properties, beanFilters, pojoSwaps).build();
- parsers = createParsers(properties, beanFilters, pojoSwaps).build();
- converters = createConverters(properties);
- encoders = createEncoders(properties).build();
- guards = createGuards(properties);
- mimetypesFileTypeMap = createMimetypesFileTypeMap(properties);
- defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
- defaultRequestHeaders.putAll(createDefaultRequestHeaders(properties));
- defaultResponseHeaders = createDefaultResponseHeaders(properties);
- responseHandlers = createResponseHandlers(properties);
-
- // Discover the @RestMethod methods available on the resource.
- List<String> methodsFound = new LinkedList<String>(); // Temporary to help debug transient duplicate method issue.
- for (java.lang.reflect.Method method : this.getClass().getMethods()) {
- if (method.isAnnotationPresent(RestMethod.class)) {
- RestMethod a = method.getAnnotation(RestMethod.class);
- methodsFound.add(method.getName() + "," + a.name() + "," + a.path());
- try {
- if (! Modifier.isPublic(method.getModifiers()))
- throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", this.getClass().getName(), method.getName());
-
- MethodMeta sm = new MethodMeta(method);
- javaRestMethods.put(method.getName(), sm);
- ResourceMethod rm = restMethods.get(sm.httpMethod);
- if (rm == null)
- restMethods.put(sm.httpMethod, sm);
- else if (rm instanceof MultiMethod)
- ((MultiMethod)rm).addSimpleMethod(sm);
- else
- restMethods.put(sm.httpMethod, new MultiMethod((MethodMeta)rm, sm));
- } catch (RestServletException e) {
- throw new RestServletException("Problem occurred trying to serialize methods on class {0}, methods={1}", this.getClass().getName(), JsonSerializer.DEFAULT_LAX.serialize(methodsFound)).initCause(e);
- }
- }
+ RestConfig rsc = new RestConfig(servletConfig, this.getClass(), null);
+ init(rsc);
+ if (! isInitialized) {
+ // Subclass may not have called super.init(RestServletConfig), so initialize here.
+ createContext(rsc);
+ super.init(servletConfig);
}
-
- for (ResourceMethod m : restMethods.values())
- m.complete();
-
- // Discover the child resources.
- childResources.putAll(createChildrenMap());
-
- for (RestServlet child : childResources.values())
- child.init(servletConfig);
-
- varResolver = varResolver
- .builder()
- .vars(LocalizationVar.class, RequestVar.class, SerializedRequestAttrVar.class, ServletInitParamVar.class, UrlEncodeVar.class)
- .build()
- ;
-
} catch (RestException e) {
// Thrown RestExceptions are simply caught and rethrown on subsequent calls to service().
initException = e;
log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
- title = String.valueOf(initException.getLocalizedMessage());
} catch (ServletException e) {
initException = e;
log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
- title = String.valueOf(initException.getLocalizedMessage());
throw e;
} catch (Exception e) {
initException = e;
log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
- title = String.valueOf(initException.getLocalizedMessage());
throw new ServletException(e);
} catch (Throwable e) {
initException = new Exception(e);
log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName());
- title = String.valueOf(initException.getLocalizedMessage());
throw new ServletException(e);
} finally {
isInitialized = true;
}
}
-
- //--------------------------------------------------------------------------------
- // Initialization methods
- //--------------------------------------------------------------------------------
-
- /**
- * Creates the child resources of this resource.
- * <p>
- * Default implementation calls {@link #createChildren()} and uses the {@link RestResource#path() @RestResource.path()} annotation
- * on each child to identify the subpath for the resource which become the keys in this map.
- * It then calls the {@link #setParent(RestServlet)} method on the child resource.
- * <p>
- * Subclasses can override this method to programatically create child resources
- * without using the {@link RestResource#children() @RestResource.children()} annotation.
- * When overridding this method, you are responsible for calling {@link #setParent(RestServlet)} on the
- * child resources.
- *
- * @return The new mutable list of child resource instances.
- * @throws Exception If an error occurred during servlet instantiation.
- */
- protected Map<String,RestServlet> createChildrenMap() throws Exception {
- Map<String,RestServlet> m = new LinkedHashMap<String,RestServlet>();
- for (RestServlet r : createChildren()) {
- r.setParent(this);
- String p = r.findPath();
- if (p == null)
- throw new RestServletException("Child resource ''{0}'' does not define a ''@RestResource.path'' attribute.", r.getClass().getName());
- m.put(p, r);
- }
- return m;
- }
-
- /**
- * Creates instances of child resources for this servlet.
- * <p>
- * Default implementation uses the {@link RestResource#children() @RestResource.children()} annotation to identify and
- * instantiate children.
- * <p>
- * Subclasses can override this method to programatically create child resources
- * without using the {@link RestResource#children() @RestResource.children()} annotation.
- *
- * @return The new mutable list of child resource instances.
- * @throws Exception If an error occurred during servlet instantiation.
- */
- protected List<RestServlet> createChildren() throws Exception {
- List<RestServlet> l = new LinkedList<RestServlet>();
- for (Class<?> c : getChildClasses()) {
- if (isParentClass(RestServlet.class, c))
- l.add((RestServlet)c.newInstance());
- else
- l.add(resolveChild(c));
- }
- return l;
- }
-
- /**
- * Programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
- * <p>
- * Subclasses can override this method to provide customized list of child resources.
- * (e.g. different children based on values specified in the config file).
- * <p>
- * Default implementation simply returns the value from the {@link RestResource#children() @RestResource.children()} annotation.
- *
- * @return The new mutable list of child resource instances.
- * @throws Exception If an error occurred during servlet instantiation.
- */
- protected Class<?>[] getChildClasses() throws Exception {
- List<Class<?>> l = new ArrayList<Class<?>>();
- List<RestResource> rr = ReflectionUtils.findAnnotations(RestResource.class, getClass());
- for (RestResource r : rr)
- l.addAll(Arrays.asList(r.children()));
- return l.toArray(new Class<?>[l.size()]);
- }
-
- /**
- * Creates the class-level properties associated with this servlet.
- * <p>
- * Subclasses can override this method to provide their own class-level properties for this servlet, typically
- * by calling <code><jk>super</jk>.createProperties()</code> and appending to the map.
- * However, in most cases, the existing set of properties can be added to by overridding {@link #getProperties()}
- * and appending to the map returned by <code><jk>super</jk>.getProperties()</code>
- * <p>
- * By default, the map returned by this method contains the following:
- * </p>
- * <ul class='spaced-list'>
- * <li>Servlet-init parameters.
- * <li>{@link RestResource#properties()} annotations in parent-to-child order.
- * <li>{@link SerializerContext#SERIALIZER_relativeUriBase} from {@link ServletConfig#getServletContext()}.
- * </ul>
- *
- * @return The resource properties as an {@link ObjectMap}.
- */
- protected ObjectMap createProperties() {
- ObjectMap m = new ObjectMap();
-
- ServletContext ctx = servletConfig.getServletContext();
-
- // Workaround for bug in Jetty that causes context path to always end in "null".
- String ctxPath = ctx.getContextPath();
- if (ctxPath.endsWith("null"))
- ctxPath = ctxPath.substring(0, ctxPath.length()-4);
- m.put(SERIALIZER_relativeUriBase, ctxPath);
-
- // Get the initialization parameters.
- for (Enumeration ep = servletConfig.getInitParameterNames(); ep.hasMoreElements();) {
- String p = (String)ep.nextElement();
- String initParam = servletConfig.getInitParameter(p);
- m.put(p, initParam);
- }
-
- // Properties are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values())
- for (Property p : r.properties())
- m.append(getVarResolver().resolve(p.name()), getVarResolver().resolve(p.value()));
-
- return m;
- }
-
- /**
- * Creates the class-level bean filters associated with this servlet.
- * <p>
- * Subclasses can override this method to provide their own class-level bean filters for this servlet.
- * <p>
- * By default, returns the bean filters specified through the {@link RestResource#beanFilters() @RestResource.beanFilters()} annotation in child-to-parent order.
- * (i.e. bean filters will be applied in child-to-parent order with child annotations overriding parent annotations when
- * the same filters are applied).
- *
- * @return The new set of transforms associated with this servet.
- */
- protected Class<?>[] createBeanFilters() {
- List<Class<?>> l = new LinkedList<Class<?>>();
-
- // Bean filters are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsChildFirst.values())
- for (Class c : r.beanFilters())
- l.add(c);
-
- return l.toArray(new Class<?>[l.size()]);
- }
-
- /**
- * Creates the class-level POJO swaps associated with this servlet.
- * <p>
- * Subclasses can override this method to provide their own class-level POJO swaps for this servlet.
- * <p>
- * By default, returns the transforms specified through the {@link RestResource#pojoSwaps() @RestResource.pojoSwaps()} annotation in child-to-parent order.
- * (i.e. POJO swaps will be applied in child-to-parent order with child annotations overriding parent annotations when
- * the same swaps are applied).
- *
- * @return The new set of transforms associated with this servet.
- */
- protected Class<?>[] createPojoSwaps() {
- List<Class<?>> l = new LinkedList<Class<?>>();
-
- // Swaps are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsChildFirst.values())
- for (Class c : r.pojoSwaps())
- l.add(c);
-
- return l.toArray(new Class<?>[l.size()]);
- }
-
- /**
- * Creates the {@link BeanContext} object used for parsing path variables and header values.
- * <p>
- * Subclasses can override this method to provide their own specialized bean context.
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
- * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
- * @return The new bean context.
- * @throws Exception If bean context not be constructed for any reason.
- */
- protected BeanContext createBeanContext(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- return PropertyStore.create().addBeanFilters(beanFilters).addPojoSwaps(pojoSwaps).setProperties(properties).getBeanContext();
- }
-
- /**
- * Creates the URL-encoding serializer used for serializing object passed to {@link Redirect}.
- * <p>
- * Subclasses can override this method to provide their own specialized serializer.
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
- * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
- * @return The new URL-Encoding serializer.
- * @throws Exception If the serializer could not be constructed for any reason.
- */
- protected UrlEncodingSerializerBuilder createUrlEncodingSerializer(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- return new UrlEncodingSerializerBuilder().properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
- }
-
- /**
- * Creates the URL-encoding parser used for parsing URL query parameters.
- * <p>
- * Subclasses can override this method to provide their own specialized parser.
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
- * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
- * @return The new URL-Encoding parser.
- * @throws Exception If the parser could not be constructed for any reason.
- */
- protected UrlEncodingParserBuilder createUrlEncodingParser(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- return new UrlEncodingParserBuilder().properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
- }
-
/**
- * Creates the serializer group containing serializers used for serializing output POJOs in HTTP responses.
+ * Resource initialization method.
* <p>
- * Subclasses can override this method to provide their own set of serializers for this servlet.
- * They can do this by either creating a new {@link SerializerGroup} from scratch, or appending to the
- * group returned by <code><jk>super</jk>.createSerializers()</code>.
+ * Identical to {@link Servlet#init(ServletConfig)} except the config object provides
+ * access to the external config file, configuration properties, and variable resolver
+ * defined for this resource.
* <p>
- * By default, returns the serializers defined through {@link RestResource#serializers() @RestResource.serializers()} on this class
- * and all parent classes.
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
- * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
- * @return The group of serializers.
- * @throws Exception If serializer group could not be constructed for any reason.
- */
- protected SerializerGroupBuilder createSerializers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- SerializerGroupBuilder g = new SerializerGroupBuilder();
-
- // Serializers are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values())
- g.append(reverse(r.serializers()));
-
- return g.properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
- }
-
- /**
- * Creates the parser group containing parsers used for parsing input into POJOs from HTTP requests.
+ * Classes can also use {@link HttpServlet#init()} and {@link RestServlet#getServletConfig()}
+ * as well to perform initialization.
* <p>
- * Subclasses can override this method to provide their own set of parsers for this servlet.
- * They can do this by either creating a new {@link ParserGroup} from scratch, or appending to the
- * group returned by <code><jk>super</jk>.createParsers()</code>.
+ * Note that if you override this method, you must first call <code><jk>super</jk>.init(servletConfig)</code>!
* <p>
- * By default, returns the parsers defined through {@link RestResource#parsers() @RestResource.parsers()} on this class
- * and all parent classes.
+ * Resource classes that don't extend from {@link RestServlet} can add this method to their class
+ * to get access to the config object.
*
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @param beanFilters Servlet-level bean filters returned by {@link #createBeanFilters()}.
- * @param pojoSwaps Servlet-level POJO swaps returned by {@link #createPojoSwaps()}.
- * @return The group of parsers.
- * @throws Exception If parser group could not be constructed for any reason.
+ * @param config The servlet configuration.
+ * @throws Exception Any exception can be thrown to signal an initialization failure.
*/
- protected ParserGroupBuilder createParsers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- ParserGroupBuilder g = new ParserGroupBuilder();
-
- // Parsers are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values())
- g.append(reverse(r.parsers()));
-
- return g.properties(properties).beanFilters(beanFilters).pojoSwaps(pojoSwaps);
+ public synchronized void init(RestConfig config) throws Exception {
+ if (isInitialized)
+ return;
+ createContext(config);
+ super.init(config);
+ init(context);
}
/**
- * Creates the class-level converters associated with this servlet.
+ * Convenience method if you want to perform initialization on your resource after all configuration settings
+ * have been made.
* <p>
- * Subclasses can override this method to provide their own class-level converters for this servlet.
+ * This allows you to get access to the {@link RestContext} object during initialization.
* <p>
- * By default, returns the converters specified through the {@link RestResource#converters() @RestResource.converters()} annotation in child-to-parent order.
- * (e.g. converters on children will be called before converters on parents).
+ * The default implementation does nothing.
*
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return The new set of transforms associated with this servet.
- * @throws RestServletException
+ * @param context The servlet context containing all the set-in-stone configurations for this resource.
+ * @throws Exception Any exception can be thrown to signal an initialization failure.
*/
- protected RestConverter[] createConverters(ObjectMap properties) throws RestServletException {
- List<RestConverter> l = new LinkedList<RestConverter>();
+ public synchronized void init(RestContext context) throws Exception {}
- // Converters are loaded in child-to-parent order.
- for (RestResource r : restResourceAnnotationsChildFirst.values())
- for (Class<? extends RestConverter> c : r.converters())
- try {
- l.add(c.newInstance());
- } catch (Exception e) {
- throw new RestServletException("Exception occurred while trying to instantiate RestConverter ''{0}''", c.getSimpleName()).initCause(e);
- }
- return l.toArray(new RestConverter[l.size()]);
+ private synchronized void createContext(RestConfig config) throws Exception {
+ if (isInitialized)
+ return;
+ this.config = config;
+ this.context = new RestContext(this, config);
+ this.isInitialized = true;
}
- /**
- * Creates the {@link EncoderGroup} for this servlet for handling various encoding schemes.
- * <p>
- * Subclasses can override this method to provide their own encoder group, typically by
- * appending to the group returned by <code><jk>super</jk>.createEncoders()</code>.
- * <p>
- * By default, returns a group containing {@link IdentityEncoder#INSTANCE} and all encoders
- * specified through {@link RestResource#encoders() @RestResource.encoders()} annotations in parent-to-child order.
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return The new encoder group associated with this servet.
- * @throws RestServletException
- */
- protected EncoderGroupBuilder createEncoders(ObjectMap properties) throws RestServletException {
- EncoderGroupBuilder g = new EncoderGroupBuilder().append(IdentityEncoder.INSTANCE);
-
- // Encoders are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values())
- for (Class<? extends Encoder> c : reverse(r.encoders()))
- try {
- g.append(c);
- } catch (Exception e) {
- throw new RestServletException("Exception occurred while trying to instantiate Encoder ''{0}''", c.getSimpleName()).initCause(e);
- }
- return g;
- }
+ //--------------------------------------------------------------------------------
+ // Other methods
+ //--------------------------------------------------------------------------------
/**
- * Creates the class-level guards associated with this servlet.
- * <p>
- * Subclasses can override this method to provide their own class-level guards for this servlet.
+ * The main service method.
* <p>
- * By default, returns the guards specified through the {@link RestResource#guards() @RestResource.guards()} annotation in child-to-parent order.
- * (i.e. guards on children will be called before guards on parents).
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return The new set of guards associated with this servet.
- * @throws RestServletException
+ * Subclasses can optionally override this method if they want to tailor the behavior of requests.
*/
- protected RestGuard[] createGuards(ObjectMap properties) throws RestServletException {
- List<RestGuard> l = new LinkedList<RestGuard>();
+ @Override /* Servlet */
+ public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
+ try {
- // Guards are loaded in child-to-parent order.
- for (RestResource r : restResourceAnnotationsChildFirst.values())
- for (Class<? extends RestGuard> c : reverse(r.guards()))
- try {
- l.add(c.newInstance());
- } catch (Exception e) {
- throw new RestServletException("Exception occurred while trying to instantiate RestGuard ''{0}''", c.getSimpleName()).initCause(e);
- }
+ if (initException != null) {
+ if (initException instanceof RestException)
+ throw (RestException)initException;
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, initException);
+ }
+ if (context == null)
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet not initialized. init(RestServletConfig) was not called.");
+ if (! isInitialized)
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet has not been initialized");
- return l.toArray(new RestGuard[l.size()]);
- }
+ context.getCallHandler().service(r1, r2);
- /**
- * Creates an instance of {@link MimetypesFileTypeMap} that is used to determine
- * the media types of static files.
- * <p>
- * Subclasses can override this method to provide their own mappings, or augment the existing
- * map by appending to <code><jk>super</jk>.createMimetypesFileTypeMap()</code>
- *
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return A new reusable MIME-types map.
- */
- protected MimetypesFileTypeMap createMimetypesFileTypeMap(ObjectMap properties) {
- MimetypesFileTypeMap m = new MimetypesFileTypeMap();
- m.addMimeTypes("text/css css CSS");
- m.addMimeTypes("text/html html htm HTML");
- m.addMimeTypes("text/plain txt text TXT");
- m.addMimeTypes("application/javascript js");
- m.addMimeTypes("image/png png");
- m.addMimeTypes("image/gif gif");
- m.addMimeTypes("application/xml xml XML");
- m.addMimeTypes("application/json json JSON");
- return m;
+ } catch (RestException e) {
+ context.getCallHandler().handleError(r1, r2, e);
+ } catch (Throwable e) {
+ context.getCallHandler().handleError(r1, r2, new RestException(SC_INTERNAL_SERVER_ERROR, e));
+ }
}
/**
- * Creates the set of default request headers for this servlet.
+ * Returns the read-only context object that contains all the configuration information about this resource.
* <p>
- * Default request headers are default values for when HTTP requests do not specify a header value.
- * For example, you can specify a default value for <code>Accept</code> if a request does not specify that header value.
+ * This object is <jk>null</jk> during the call to {@link #init(RestConfig)} but is populated
+ * by the time {@link #init()} is called.
* <p>
- * Subclasses can override this method to provide their own class-level default request headers for this servlet.
- * <p>
- * By default, returns the default request headers specified through the {@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()}
- * annotation in parent-to-child order.
- * (e.g. headers defined on children will override the same headers defined on parents).
+ * Resource classes that don't extend from {@link RestServlet} can add the following method to their
+ * class to get access to this context object:
+ * <p class='bcode'>
+ * <jk>public void</jk> init(RestServletContext context) <jk>throws</jk> Exception;
+ * </p>
*
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return The new set of default request headers associated with this servet.
- * @throws RestServletException
+ * @return The context information on this servlet.
*/
- protected Map<String,String> createDefaultRequestHeaders(ObjectMap properties) throws RestServletException {
- Map<String,String> m = new HashMap<String,String>();
-
- // Headers are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values()) {
- for (String s : r.defaultRequestHeaders()) {
- String[] h = parseHeader(s);
- if (h == null)
- throw new RestServletException("Invalid default request header specified: ''{0}''. Must be in the format: ''Header-Name: header-value''", s);
- m.put(h[0], h[1]);
- }
- }
-
- return m;
+ protected RestContext getContext() {
+ return context;
}
/**
- * Creates the set of default response headers for this servlet.
+ * Callback method for listening for successful completion of requests.
* <p>
- * Default response headers are headers that will be appended to all responses if those headers have not already been
- * set on the response object.
+ * Subclasses can override this method for gathering performance statistics.
* <p>
- * Subclasses can override this method to provide their own class-level default response headers for this servlet.
+ * The default implementation does nothing.
* <p>
- * By default, returns the default response headers specified through the {@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()}
- * annotation in parent-to-child order.
- * (e.g. headers defined on children will override the same headers defined on parents).
+ * Resources that don't extend from {@link RestServlet} can implement an equivalent method by
+ * overriding the {@link RestCallHandler#onSuccess(RestRequest, RestResponse, long)} method.
*
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return The new set of default response headers associated with this servet.
- * @throws RestServletException
+ * @param req The HTTP request.
+ * @param res The HTTP response.
+ * @param time The time in milliseconds it took to process the request.
*/
- protected Map<String,Object> createDefaultResponseHeaders(ObjectMap properties) throws RestServletException {
- Map<String,Object> m = new LinkedHashMap<String,Object>();
-
- // Headers are loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values()) {
- for (String s : r.defaultResponseHeaders()) {
- String[] h = parseHeader(s);
- if (h == null)
- throw new RestServletException("Invalid default response header specified: ''{0}''. Must be in the format: ''Header-Name: header-value''", s);
- m.put(h[0], h[1]);
- }
- }
-
- return m;
- }
+ protected void onSuccess(RestRequest req, RestResponse res, long time) {}
/**
- * Creates the class-level response handlers associated with this servlet.
+ * Callback method that gets invoked right before the REST Java method is invoked.
* <p>
- * Subclasses can override this method to provide their own class-level response handlers for this servlet.
+ * Subclasses can override this method to override request headers or set request-duration properties
+ * before the Java method is invoked.
* <p>
- * By default, returns the handlers specified through the {@link RestResource#responseHandlers() @RestResource.responseHandlers()}
- * annotation in parent-to-child order.
- * (e.g. handlers on children will be called before handlers on parents).
+ * Resources that don't extend from {@link RestServlet} can implement an equivalent method by
+ * overriding the {@link RestCallHandler#onPreCall(RestRequest)} method.
*
- * @param properties Servlet-level properties returned by {@link #createProperties()}.
- * @return The new set of response handlers associated with this servet.
- * @throws RestException
+ * @param req The HTTP servlet request object.
+ * @throws RestException If any error occurs.
*/
- protected ResponseHandler[] createResponseHandlers(ObjectMap properties) throws RestException {
- List<ResponseHandler> l = new LinkedList<ResponseHandler>();
-
- // Loaded in parent-to-child order to allow overrides.
- for (RestResource r : restResourceAnnotationsParentFirst.values())
- for (Class<? extends ResponseHandler> c : r.responseHandlers())
- try {
- l.add(c.newInstance());
- } catch (Exception e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
-
- // Add the default handlers.
- l.add(new StreamableHandler());
- l.add(new WritableHandler());
- l.add(new ReaderHandler());
- l.add(new InputStreamHandler());
- l.add(new RedirectHandler());
- l.add(new DefaultHandler());
-
- return l.toArray(new ResponseHandler[l.size()]);
- }
-
-
- //--------------------------------------------------------------------------------
- // Other methods
- //--------------------------------------------------------------------------------
+ protected void onPreCall(RestRequest req) throws RestException {}
/**
- * Returns the localized Swagger from the file system.
+ * Callback method that gets invoked right after the REST Java method is invoked, but before
+ * the serializer is invoked.
* <p>
- * Looks for a file called <js>"{ServletClass}_{locale}.json"</js> in the same package
- * as this servlet and returns it as a parsed {@link Swagger} object.
+ * Subclasses can override this method to override request and response headers, or
+ * set/override properties used by the serializer.
* <p>
- * Returned objects are cached for later quick-lookup.
+ * Resources that don't extend from {@link RestServlet} can implement an equivalent method by
+ * overriding the {@link RestCallHandler#onPostCall(RestRequest,RestResponse)} method.
*
- * @param locale The locale of the swagger.
- * @return The parsed swagger object, or <jk>null</jk> if the swagger file could not be found.
- * @throws RestException
- */
- protected Swagger getSwaggerFromFile(Locale locale) throws RestException {
- Swagger s = swaggers.get(locale);
- if (s == null) {
- try {
- s = getResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
- swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
- } catch (Exception e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
- return s == Swagger.NULL ? null : s;
- }
-
-
- /**
- * Returns the localized swagger for this REST resource.
- *
- * @param req The incoming HTTP request.
- * @return A new Swagger instance.
- * @throws RestException
+ * @param req The HTTP servlet request object.
+ * @param res The HTTP servlet response object.
+ * @throws RestException If any error occurs.
*/
- protected Swagger getSwagger(RestRequest req) throws RestException {
- try {
- // If a file is defined, use that.
- Swagger s = req.getSwaggerFromFile();
- if (s != null)
- return s;
-
- s = swagger(
- info(getTitle(req), getVersion(req))
- .contact(getContact(req))
- .license(getLicense(req))
- .description(getDescription(req))
- .termsOfService(getTermsOfService(req))
- )
- .consumes(getSupportedAcceptTypes())
- .produces(getSupportedContentTypes())
- .tags(getTags(req))
- .externalDocs(getExternalDocs(req));
-
- for (MethodMeta sm : javaRestMethods.values()) {
- if (sm.isRequestAllowed(req)) {
- Operation o = sm.getSwaggerOperation(req);
- s.path(
- sm.pathPattern.getPatternString(),
- sm.httpMethod.toLowerCase(),
- o
- );
- }
- }
- return s;
- } catch (RestException e) {
- throw e;
- } catch (Exception e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
+ protected void onPostCall(RestRequest req, RestResponse res) throws RestException {}
/**
- * Sets the parent of this resource.
+ * Convenience method for calling <code>getContext().getLogger().log(level, msg, args);</code>
*
- * @param parent The parent of this resource.
+ * @param level The log level.
+ * @param msg The message to log.
+ * @param args Optional {@link MessageFormat}-style arguments.
*/
- protected void setParent(RestServlet parent) {
- this.parentResource = parent;
+ protected void log(Level level, String msg, Object...args) {
+ if (context != null)
+ context.getLogger().log(level, msg, args);
}
/**
- * Returns the parent of this resource.
+ * Convenience method for calling <code>getContext().getLogger().log(level, cause, msg, args);</code>
*
- * @return The parent of this resource, or <jk>null</jk> if resource has no parent.
+ * @param level The log level.
+ * @param cause The cause.
+ * @param msg The message to log.
+ * @param args Optional {@link MessageFormat}-style arguments.
*/
- public RestServlet getParent() {
- return this.parentResource;
- }
-
- private static String[] parseHeader(String s) {
- int i = s.indexOf(':');
- if (i == -1)
- return null;
- String name = s.substring(0, i).trim().toLowerCase(Locale.ENGLISH);
- String val = s.substring(i+1).trim();
- return new String[]{name,val};
+ protected void log(Level level, Throwable cause, String msg, Object...args) {
+ if (context != null)
+ context.getLogger().log(level, cause, msg, args);
}
- /**
- * Creates a {@link RestRequest} object based on the specified incoming {@link HttpServletRequest} object.
- * <p>
- * Subclasses may choose to override this method to provide a specialized request object.
- *
- * @param req The request object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
- * @return The wrapped request object.
- * @throws ServletException If any errors occur trying to interpret the request.
- */
- protected RestRequest createRequest(HttpServletRequest req) throws ServletException {
- return new RestRequest(this, req);
+ @Override /* GenericServlet */
+ public RestConfig getServletConfig() {
+ return config;
}
- /**
- * Creates a {@link RestResponse} object based on the specified incoming {@link HttpServletResponse} object
- * and the request returned by {@link #createRequest(HttpServletRequest)}.
- * <p>
- * Subclasses may choose to override this method to provide a specialized response object.
- *
- * @param req The request object returned by {@link #createRequest(HttpServletRequest)}.
- * @param res The response object from the {@link #service(HttpServletRequest, HttpServletResponse)} method.
- * @return The wrapped response object.
- * @throws ServletException If any erros occur trying to interpret the request or response.
- */
- protected RestResponse createResponse(RestRequest req, HttpServletResponse res) throws ServletException {
- return new RestResponse(this, req, res);
+ @Override /* GenericServlet */
+ public void destroy() {
+ context.destroy();
+ super.destroy();
}
/**
- * Returns whether this resource class can provide an OPTIONS page.
- * <p>
- * By default, returns <jk>false</jk>.
- * <p>
- * Subclasses can override this method to cause the <code>options</code> link to show up in the HTML serialized output.
- *
- * @return <jk>true</jk> if this resource has implemented a {@code getOptions()} method.
+ * Convenience method for calling <code>getContext().getMessages();</code>
+ * @return The resource bundle for this resource. Never <jk>null</jk>.
+ * @see RestContext#getProperties()
*/
- public boolean hasOptionsPage() {
- return false;
+ public MessageBundle getMessages() {
+ return context.getMessages();
}
/**
- * Specify a class-level property.
- * <p>
- * Typically, properties in {@link RestServletContext} can be set in the {@link Servlet#init(ServletConfig)} method.
- *
- * @param key The property name.
- * @param value The property value.
- * @return This object (for method chaining).
+ * Convenience method for calling <code>getContext().getProperties();</code>
+ * @return The resource properties as an {@link ObjectMap}.
+ * @see RestContext#getProperties()
*/
- public synchronized RestServlet setProperty(String key, Object value) {
- getProperties().put(key, value);
- return this;
+ public ObjectMap getProperties() {
+ return getContext().getProperties();
}
/**
- * The main service method.
- * <p>
- * Subclasses can optionally override this method if they want to tailor the behavior of requests.
- */
- @Override /* Servlet */
- public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, IOException {
-
- log(FINE, "HTTP: {0} {1}", r1.getMethod(), r1.getRequestURI());
- long startTime = System.currentTimeMillis();
-
- try {
-
- if (initException != null) {
- if (initException instanceof RestException)
- throw (RestException)initException;
- throw new RestException(SC_INTERNAL_SERVER_ERROR, initException);
- }
-
- if (! isInitialized)
- throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet has not been initialized");
-
- String pathInfo = RestUtils.getPathInfoUndecoded(r1); // Can't use r1.getPathInfo() because we don't want '%2F' resolved.
-
- // If this resource has child resources, try to recursively call them.
- if (pathInfo != null && (! childResources.isEmpty()) && (! pathInfo.equals("/"))) {
- int i = pathInfo.indexOf('/', 1);
- String pathInfoPart = i == -1 ? pathInfo.substring(1) : pathInfo.substring(1, i);
- RestServlet childResource = childResources.get(pathInfoPart);
- if (childResource != null) {
- final String pathInfoRemainder = (i == -1 ? null : pathInfo.substring(i));
- final String servletPath = r1.getServletPath() + "/" + pathInfoPart;
- final HttpServletRequest childRequest = new HttpServletRequestWrapper(r1) {
- @Override /* ServletRequest */
- public String getPathInfo() {
- return RestUtils.decode(pathInfoRemainder);
- }
- @Override /* ServletRequest */
- public String getServletPath() {
- return servletPath;
- }
- };
- childResource.service(childRequest, r2);
- return;
- }
- }
-
- RestRequest req = createRequest(r1);
- RestResponse res = createResponse(req, r2);
- String method = req.getMethod();
- String methodUC = method.toUpperCase(Locale.ENGLISH);
-
- StreamResource r = null;
- if (pathInfo != null) {
- String p = pathInfo.substring(1);
- if (p.equals("favicon.ico"))
- r = favIcon;
- else if (p.equals("style.css"))
- r = styleSheet;
- else if (StringUtils.pathStartsWith(p, staticFilesPrefixes))
- r = resolveStaticFile(p);
- }
-
- if (r != null) {
- res.setStatus(SC_OK);
- res.setOutput(r);
- } else {
- // If the specified method has been defined in a subclass, invoke it.
- int rc = SC_METHOD_NOT_ALLOWED;
- if (restMethods.containsKey(methodUC)) {
- rc = restMethods.get(methodUC).invoke(method, pathInfo, this, req, res);
- } else if (restMethods.containsKey("*")) {
- rc = restMethods.get("*").invoke(method, pathInfo, this, req, res);
- }
-
- // If not invoked above, see if it's an OPTIONs request
- if (rc != SC_OK)
- handleNotFound(rc, req, res);
- }
-
- if (res.hasOutput()) {
- Object output = res.getOutput();
-
- // Do any class-level transforming.
- for (RestConverter converter : getConverters())
- output = converter.convert(req, output, getBeanContext().getClassMetaForObject(output));
-
- res.setOutput(output);
-
- // Now serialize the output if there was any.
- // Some subclasses may write to the OutputStream or Writer directly.
- handleResponse(req, res, output);
- }
-
- onSuccess(req, res, System.currentTimeMillis() - startTime);
-
- } catch (RestException e) {
- handleError(r1, r2, e);
- } catch (Throwable e) {
- handleError(r1, r2, new RestException(SC_INTERNAL_SERVER_ERROR, e));
- }
- log(FINE, "HTTP: [{0} {1}] finished in {2}ms", r1.getMethod(), r1.getRequestURI(), System.currentTimeMillis()-startTime);
- }
-
- /**
- * Handle the case where a matching method was not found.
- * <p>
- * Subclasses can override this method to provide a 2nd-chance for specifying a response.
- * The default implementation will simply throw an exception with an appropriate message.
- *
- * @param rc The HTTP response code.
- * @param req The HTTP request.
- * @param res The HTTP response.
- * @throws Exception
- */
- protected void handleNotFound(int rc, RestRequest req, RestResponse res) throws Exception {
- String pathInfo = req.getPathInfo();
- String methodUC = req.getMethod();
- String onPath = pathInfo == null ? " on no pathInfo" : format(" on path '%s'", pathInfo);
- if (rc == SC_NOT_FOUND)
- throw new RestException(rc, "Method ''{0}'' not found on resource with matching pattern{1}.", methodUC, onPath);
- else if (rc == SC_PRECONDITION_FAILED)
- throw new RestException(rc, "Method ''{0}'' not found on resource{1} with matching matcher.", methodUC, onPath);
- else if (rc == SC_METHOD_NOT_ALLOWED)
- throw new RestException(rc, "Method ''{0}'' not found on resource.", methodUC);
- else
- throw new ServletException("Invalid method response: " + rc);
- }
-
- private synchronized void handleError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
- Integer c = 1;
- if (context.useStackTraceHashes) {
- int h = e.hashCode();
- c = stackTraceHashes.get(h);
- if (c == null)
- c = 1;
- else
- c++;
- stackTraceHashes.put(h, c);
- e.setOccurrence(c);
- }
- onError(req, res, e);
- renderError(req, res, e);
- }
-
- /**
- * Method for rendering response errors.
- * <p>
- * The default implementation renders a plain text English message, optionally with a stack trace
- * if {@link RestServletContext#REST_renderResponseStackTraces} is enabled.
- * <p>
- * Subclasses can override this method to provide their own custom error response handling.
- *
- * @param req The servlet request.
- * @param res The servlet response.
- * @param e The exception that occurred.
- * @throws IOException Can be thrown if a problem occurred trying to write to the output stream.
- */
- protected void renderError(HttpServletRequest req, HttpServletResponse res, RestException e) throws IOException {
-
- int status = e.getStatus();
- res.setStatus(status);
- res.setContentType("text/plain");
- res.setHeader("Content-Encoding", "identity");
- PrintWriter w = null;
- try {
- w = res.getWriter();
- } catch (IllegalStateException e2) {
- w = new PrintWriter(new OutputStreamWriter(res.getOutputStream(), IOUtils.UTF8));
- }
- String httpMessage = RestUtils.getHttpResponseText(status);
- if (httpMessage != null)
- w.append("HTTP ").append(String.valueOf(status)).append(": ").append(httpMessage).append("\n\n");
- if (context.renderResponseStackTraces)
- e.printStackTrace(w);
- else
- w.append(e.getFullStackMessage(true));
- w.flush();
- w.close();
- }
-
- /**
- * Callback method for logging errors during HTTP requests.
- * <p>
- * ypically, subclasses will override this method and log errors themselves.
- * <p>
- * The default implementation simply logs errors to the <code>RestServlet</code> logger.
- * <p>
- * Here's a typical implementation showing how stack trace hashing can be used to reduce log file sizes...
- * </p>
- * <p class='bcode'>
- * <jk>protected void</jk> onError(HttpServletRequest req, HttpServletResponse res, RestException e, <jk>boolean</jk> noTrace) {
- * String qs = req.getQueryString();
- * String msg = <js>"HTTP "</js> + req.getMethod() + <js>" "</js> + e.getStatus() + <js>" "</js> + req.getRequestURI() + (qs == <jk>null</jk> ? <js>""</js> : <js>"?"</js> + qs);
- * <jk>int</jk> c = e.getOccurrence();
- *
- * <jc>// REST_useStackTraceHashes is disabled, so we have to log the exception every time.</jc>
- * <jk>if</jk> (c == 0)
- * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%s] %s"</js>, e.getStatus(), msg), e);
- *
- * <jc>// This is the first time we've countered this error, so log a stack trace
- * // unless ?noTrace was passed in as a URL parameter.</jc>
- * <jk>else if</jk> (c == 1 && ! noTrace)
- * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s"</js>, e.hashCode(), e.getStatus(), c, msg), e);
- *
- * <jc>// This error occurred before.
- * // Only log the message, not the stack trace.</jc>
- * <jk>else</jk>
- * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s, %s"</js>, e.hashCode(), e.getStatus(), c, msg, e.getLocalizedMessage()));
- * }
- * </p>
- *
- * @param req The servlet request object.
- * @param res The servlet response object.
- * @param e Exception indicating what error occurred.
- */
- protected void onError(HttpServletRequest req, HttpServletResponse res, RestException e) {
- if (shouldLog(req, res, e)) {
- String qs = req.getQueryString();
- String msg = "HTTP " + req.getMethod() + " " + e.getStatus() + " " + req.getRequestURI() + (qs == null ? "" : "?" + qs);
- int c = e.getOccurrence();
- if (shouldLogStackTrace(req, res, e)) {
- msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg;
- log(Level.WARNING, e, msg);
- } else {
- msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg + ", " + e.getLocalizedMessage();
- log(Level.WARNING, msg);
- }
- }
- }
-
- /**
- * Returns <jk>true</jk> if the specified exception should be logged.
- * <p>
- * Subclasses can override this method to provide their own logic for determining when exceptions are logged.
- * <p>
- * The default implementation will return <jk>false</jk> if <js>"noTrace=true"</js> is passed in the query string.
- *
- * @param req The HTTP request.
- * @param res The HTTP response.
- * @param e The exception.
- * @return <jk>true</jk> if exception should be logged.
- */
- protected boolean shouldLog(HttpServletRequest req, HttpServletResponse res, RestException e) {
- String q = req.getQueryString();
- return (q == null ? true : q.indexOf("noTrace=true") == -1);
- }
-
- /**
- * Returns <jk>true</jk> if a stack trace should be logged for this exception.
- * <p>
- * Subclasses can override this method to provide their own logic for determining when stack traces are logged.
- * <p>
- * The default implementation will only log a stack trace if {@link RestException#getOccurrence()} returns <code>1</code>
- * and the exception is not one of the following:
- * </p>
- * <ul>
- * <li>{@link HttpServletResponse#SC_UNAUTHORIZED}
- * <li>{@link HttpServletResponse#SC_FORBIDDEN}
- * <li>{@link HttpServletResponse#SC_NOT_FOUND}
- * </ul>
- *
- * @param req The HTTP request.
- * @param res The HTTP response.
- * @param e The exception.
- * @return <jk>true</jk> if stack trace should be logged.
- */
- protected boolean shouldLogStackTrace(HttpServletRequest req, HttpServletResponse res, RestException e) {
- if (e.getOccurrence() == 1) {
- switch (e.getStatus()) {
- case SC_UNAUTHORIZED:
- case SC_FORBIDDEN:
- case SC_NOT_FOUND: return false;
- default: return true;
- }
- }
- return false;
- }
-
- /**
- * Log a message.
- * <p>
- * Equivalent to calling <code>log(level, <jk>null</jk>, msg, args);</code>
- *
- * @param level The log level.
- * @param msg The message to log.
- * @param args Optional {@link MessageFormat}-style arguments.
- */
- protected void log(Level level, String msg, Object...args) {
- log(level, null, msg, args);
- }
-
- /**
- * Same as {@link #log(Level, String, Object...)} excepts runs the
- * arguments through {@link JsonSerializer#DEFAULT_LAX_READABLE}.
- * <p>
- * Serialization of arguments do not occur if message is not logged, so
- * it's safe to use this method from within debug log statements.
- *
- * <h5 class='section'>Example:</h5>
- * <p class='bcode'>
- * logObjects(<jsf>DEBUG</jsf>, <js>"Pojo contents:\n{0}"</js>, myPojo);
- * </p>
- *
- * @param level The log level.
- * @param msg The message to log.
- * @param args Optional {@link MessageFormat}-style arguments.
- */
- protected void logObjects(Level level, String msg, Object...args) {
- for (int i = 0; i < args.length; i++)
- args[i] = JsonSerializer.DEFAULT_LAX_READABLE.toStringObject(args[i]);
- log(level, null, msg, args);
- }
-
- /**
- * Log a message to the logger returned by {@link #getLogger()}.
- * <p>
- * Subclasses can override this method if they wish to log messages using a library other than
- * Java Logging (e.g. Apache Commons Logging).
- *
- * @param level The log level.
- * @param cause The cause.
- * @param msg The message to log.
- * @param args Optional {@link MessageFormat}-style arguments.
- */
- protected void log(Level level, Throwable cause, String msg, Object...args) {
- JuneauLogger log = getLogger();
- if (args.length > 0)
- msg = MessageFormat.format(msg, args);
- log.log(level, msg, cause);
- }
-
- /**
- * Callback method for listening for successful completion of requests.
- * <p>
- * Subclasses can override this method for gathering performance statistics.
- * <p>
- * The default implementation does nothing.
- *
- * @param req The HTTP request.
- * @param res The HTTP response.
- * @param time The time in milliseconds it took to process the request.
- */
- protected void onSuccess(RestRequest req, RestResponse res, long time) {}
-
- /**
- * Callback method that gets invoked right before the REST Java method is invoked.
- * <p>
- * Subclasses can override this method to override request headers or set request-duration properties
- * before the Java method is invoked.
- *
- * @param req The HTTP servlet request object.
- * @throws RestException If any error occurs.
- */
- protected void onPreCall(RestRequest req) throws RestException {}
-
- /**
- * Callback method that gets invoked right after the REST Java method is invoked, but before
- * the serializer is invoked.
- * <p>
- * Subclasses can override this method to override request and response headers, or
- * set/override properties used by the serializer.
- *
- * @param req The HTTP servlet request object.
- * @param res The HTTP servlet response object.
- * @throws RestException If any error occurs.
- */
- protected void onPostCall(RestRequest req, RestResponse res) throws RestException {}
-
- /**
- * The main method for serializing POJOs passed in through the {@link RestResponse#setOutput(Object)} method.
- * <p>
- * Subclasses may override this method if they wish to modify the way the output is rendered, or support
- * other output formats.
- *
- * @param req The HTTP request.
- * @param res The HTTP response.
- * @param output The output to serialize in the response.
- * @throws IOException
- * @throws RestException
- */
- protected void handleResponse(RestRequest req, RestResponse res, Object output) throws IOException, RestException {
- // Loop until we find the correct handler for the POJO.
- for (ResponseHandler h : getResponseHandlers())
- if (h.handle(req, res, output))
- return;
- throw new RestException(SC_NOT_IMPLEMENTED, "No response handlers found to process output of type '"+(output == null ? null : output.getClass().getName())+"'");
- }
-
- @Override /* GenericServlet */
- public ServletConfig getServletConfig() {
- return servletConfig;
- }
-
- @Override /* GenericServlet */
- public void destroy() {
- for (RestServlet r : childResources.values())
- r.destroy();
- super.destroy();
- }
-
- /**
- * Resolve a static resource file.
- * <p>
- * Subclasses can override this method to provide their own way to resolve files.
- *
- * @param pathInfo The unencoded path info.
- * @return The resource, or <jk>null</jk> if the resource could not be resolved.
- * @throws IOException
- */
- protected StreamResource resolveStaticFile(String pathInfo) throws IOException {
- if (! staticFilesCache.containsKey(pathInfo)) {
- String p = RestUtils.decode(RestUtils.trimSlashes(pathInfo));
- if (p.indexOf("..") != -1)
- throw new RestException(SC_NOT_FOUND, "Invalid path");
- for (Map.Entry<String,String> e : staticFilesMap.entrySet()) {
- String key = RestUtils.trimSlashes(e.getKey());
- if (p.startsWith(key)) {
- String remainder = (p.equals(key) ? "" : p.substring(key.length()));
- if (remainder.isEmpty() || remainder.startsWith("/")) {
- String p2 = RestUtils.trimSlashes(e.getValue()) + remainder;
- InputStream is = getResource(p2, null);
- if (is != null) {
- try {
- int i = p2.lastIndexOf('/');
- String name = (i == -1 ? p2 : p2.substring(i+1));
- String mediaType = getMimetypesFileTypeMap().getContentType(name);
- ObjectMap headers = new ObjectMap().append("Cache-Control", "max-age=86400, public");
- staticFilesCache.put(pathInfo, new StreamResource(MediaType.forString(mediaType), headers, is));
- return staticFilesCache.get(pathInfo);
- } finally {
- is.close();
- }
- }
- }
- }
- }
- }
- return staticFilesCache.get(pathInfo);
- }
-
- /**
- * Returns a list of valid {@code Accept} content types for this resource.
- * <p>
- * Typically used by subclasses during {@code OPTIONS} requests.
- * <p>
- * The default implementation resturns the list from {@link ParserGroup#getSupportedMediaTypes()}
- * from the parser group returned by {@link #getParsers()}.
- * <p>
- * Subclasses can override or expand this list as they see fit.
- *
- * @return The list of valid {@code Accept} content types for this resource.
- * @throws RestServletException
- */
- public Collection<MediaType> getSupportedAcceptTypes() throws RestServletException {
- return getParsers().getSupportedMediaTypes();
- }
-
- /**
- * Returns a list of valid {@code Content-Types} for input for this resource.
- * <p>
- * Typically used by subclasses during {@code OPTIONS} requests.
- * <p>
- * The default implementation resturns the list from {@link SerializerGroup#getSupportedMediaTypes()}
- * from the parser group returned by {@link #getSerializers()}.
- * <p>
- * Subclasses can override or expand this list as they see fit.
- *
- * @return The list of valid {@code Content-Type} header values for this resource.
- * @throws RestServletException
- */
- public Collection<MediaType> getSupportedContentTypes() throws RestServletException {
- return getSerializers().getSupportedMediaTypes();
- }
-
- /**
- * Returns the localized summary of the specified java method on this servlet.
- * <p>
- * Subclasses can override this method to provide their own summary.
- * <p>
- * The default implementation returns the summary from the following locations (whichever matches first):
- * </p>
- * <ol>
- * <li>{@link RestMethod#summary() @RestMethod.summary()} annotation on the method.
- * <li><ck>[ClassName].[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * </ol>
- *
- * @param javaMethodName The name of the Java method whose description we're retrieving.
- * @param req The current request.
- * @return The localized summary of the method, or a blank string if no summary was found.
- */
- public String getMethodSummary(String javaMethodName, RestRequest req) {
- MethodMeta m = javaRestMethods.get(javaMethodName);
- if (m != null)
- return m.getSummary(req);
- return "";
- }
-
- /**
- * Returns the localized description of the specified java method on this servlet.
- * <p>
- * Subclasses can override this method to provide their own description.
- * <p>
- * The default implementation returns the description from the following locations (whichever matches first):
- * </p>
- * <ol>
- * <li>{@link RestMethod#description() @RestMethod.description()} annotation on the method.
- * <li><ck>[ClassName].[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * </ol>
- *
- * @param javaMethodName The name of the Java method whose description we're retrieving.
- * @param req The current request.
- * @return The localized description of the method, or a blank string if no description was found.
- */
- public String getMethodDescription(String javaMethodName, RestRequest req) {
- MethodMeta m = javaRestMethods.get(javaMethodName);
- if (m != null)
- return m.getDescription(req);
- return "";
- }
-
- /**
- * Returns the localized title of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own title.
- * <p>
- * The default implementation returns the description from the following locations (whichever matches first):
- * <p>
- * <ol>
- * <li>{@link RestResource#title() @RestResourcel.title()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].title</ck> property in resource bundle identified by {@link RestResource#messages() @ResourceBundle.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>title</ck> in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/title</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
- */
- public String getTitle(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- if (title != null)
- return vr.resolve(title);
- String title = msgs.findFirstString(req.getLocale(), "title");
- if (title != null)
- return vr.resolve(title);
- Swagger s = req.getSwaggerFromFile();
- if (s != null && s.getInfo() != null)
- return s.getInfo().getTitle();
- return null;
- }
-
- /**
- * Returns the localized description of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own description.
- * <p>
- * The default implementation returns the description from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#description() @RestResource.description()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/description</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
- */
- public String getDescription(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- if (description != null)
- return vr.resolve(description);
- String description = msgs.findFirstString(req.getLocale(), "description");
- if (description != null)
- return vr.resolve(description);
- Swagger s = req.getSwaggerFromFile();
- if (s != null && s.getInfo() != null)
- return s.getInfo().getDescription();
- return null;
- }
-
- /**
- * Returns the localized contact information of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own contact information.
- * <p>
- * The default implementation returns the contact information from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#contact() @RestResource.contact()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/contact</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
- */
- public Contact getContact(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- JsonParser jp = JsonParser.DEFAULT;
- try {
- if (contact != null)
- return jp.parse(vr.resolve(contact), Contact.class);
- String contact = msgs.findFirstString(req.getLocale(), "contact");
- if (contact != null)
- return jp.parse(vr.resolve(contact), Contact.class);
- Swagger s = req.getSwaggerFromFile();
- if (s != null && s.getInfo() != null)
- return s.getInfo().getContact();
- return null;
- } catch (ParseException e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
-
- /**
- * Returns the localized license information of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own license information.
- * <p>
- * The default implementation returns the license information from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#license() @RestResource.license()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/license</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
- */
- public License getLicense(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- JsonParser jp = JsonParser.DEFAULT;
- try {
- if (license != null)
- return jp.parse(vr.resolve(license), License.class);
- String license = msgs.findFirstString(req.getLocale(), "license");
- if (license != null)
- return jp.parse(vr.resolve(license), License.class);
- Swagger s = req.getSwaggerFromFile();
- if (s != null && s.getInfo() != null)
- return s.getInfo().getLicense();
- return null;
- } catch (ParseException e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
-
- /**
- * Returns the terms-of-service information of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own terms-of-service information.
- * <p>
- * The default implementation returns the terms-of-service information from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#termsOfService() @RestResource.termsOfService()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/termsOfService</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
- */
- public String getTermsOfService(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- if (termsOfService != null)
- return vr.resolve(termsOfService);
- String termsOfService = msgs.findFirstString(req.getLocale(), "termsOfService");
- if (termsOfService != null)
- return vr.resolve(termsOfService);
- Swagger s = req.getSwaggerFromFile();
- if (s != null && s.getInfo() != null)
- return s.getInfo().getTermsOfService();
- return null;
- }
-
- /**
- * Returns the version information of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own version information.
- * <p>
- * The default implementation returns the version information from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/version</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
- */
- public String getVersion(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- if (version != null)
- return vr.resolve(version);
- String version = msgs.findFirstString(req.getLocale(), "version");
- if (version != null)
- return vr.resolve(version);
- Swagger s = req.getSwaggerFromFile();
- if (s != null && s.getInfo() != null)
- return s.getInfo().getVersion();
- return null;
- }
-
- /**
- * Returns the version information of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own version information.
- * <p>
- * The default implementation returns the version information from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/version</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
- */
- public List<Tag> getTags(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- JsonParser jp = JsonParser.DEFAULT;
- try {
- if (tags != null)
- return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
- String tags = msgs.findFirstString(req.getLocale(), "tags");
- if (tags != null)
- return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
- Swagger s = req.getSwaggerFromFile();
- if (s != null)
- return s.getTags();
- return null;
- } catch (Exception e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
-
- /**
- * Returns the version information of this REST resource.
- * <p>
- * Subclasses can override this method to provide their own version information.
- * <p>
- * The default implementation returns the version information from the following locations (whichever matches first):
- * <ol>
- * <li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
- * <li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
- * annotation for this class, then any parent classes.
- * <li><ck>/info/version</ck> entry in swagger file.
- * </ol>
- *
- * @param req The current request.
- * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
- */
- public ExternalDocumentation getExternalDocs(RestRequest req) {
- VarResolverSession vr = req.getVarResolverSession();
- JsonParser jp = JsonParser.DEFAULT;
- try {
- if (externalDocs != null)
- return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
- String externalDocs = msgs.findFirstString(req.getLocale(), "externalDocs");
- if (externalDocs != null)
- return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
- Swagger s = req.getSwaggerFromFile();
- if (s != null)
- return s.getExternalDocs();
- return null;
- } catch (Exception e) {
- throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
- }
- }
-
- /**
- * Returns the resource bundle identified by the {@link RestResource#messages() @RestResource.messages()} annotation for the default locale.
- *
- * @return The resource bundle. Never <jk>null</jk>.
- */
- public MessageBundle getMessages() {
- return msgs;
- }
-
- /**
- * Returns the resource bundle identified by the {@link RestResource#messages() @RestResource.messages()} annotation for the specified locale.
- *
- * @param locale The resource bundle locale.
- * @return The resource bundle. Never <jk>null</jk>.
- */
- public MessageBundle getMessages(Locale locale) {
- return msgs.getBundle(locale);
- }
-
- /**
- * Gets a localized message from the resource bundle identified by the {@link RestResource#messages() @RestResource.messages()} annotation.
- * <p>
- * If resource bundle location was not specified, or the resource bundle was not found,
- * returns the string <js>"{!!key}"</js>.
- * <p>
- * If message was not found in the resource bundle, returns the string <js>"{!key}"</js>.
- *
- * @param locale The client locale.
- * @param key The resource bundle key.
- * @param args Optional {@link MessageFormat}-style arguments.
- * @return The localized message.
- */
- public String getMessage(Locale locale, String key, Object...args) {
- return msgs.getString(locale, key, args);
- }
-
- /**
- * Programmatically adds the specified resource as a child to this resource.
- * <p>
- * This method can be used in a resources {@link #init()} method to define child resources
- * accessible through a child URL.
- * <p>
- * Typically, child methods are defined via {@link RestResource#children() @RestResource.children()}. However, this
- * method is provided to handle child resources determined at runtime.
- *
- * @param name The sub-URL under which this resource is accessible.<br>
- * For example, if the parent resource URL is <js>"/foo"</js>, and this name is <js>"bar"</js>, then
- * the child resource will be accessible via the URL <js>"/foo/bar"</js>.
- * @param resource The child resource.
- * @throws ServletException Thrown by the child init() method.
- */
- protected void addChildResource(String name, RestServlet resource) throws ServletException {
- resource.init(getServletConfig());
- childResources.put(name, resource);
- }
-
- /**
- * 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<String,RestServlet> getChildResources() {
- return Collections.unmodifiableMap(childResources);
- }
-
- /**
- * Returns the path for this servlet as defined by the {@link RestResource#path()} annotation
- * on this class concatenated with those on all parent classes.
- * <p>
- * If path is not specified, returns <js>"/"</js>.
- * <p>
- * Path always starts with <js>"/"</js>.
- *
- * @return The servlet path.
- */
- public String getPath() {
- if (path == null) {
- LinkedList<String> l = new LinkedList<String>();
- RestServlet r = this;
- while (r != null) {
- String p = r.findPath();
- if (p == null)
- break;
- l.addFirst(p);
- r = r.parentResource;
- }
- StringBuilder sb = new StringBuilder();
- for (String p : l)
- sb.append('/').append(p);
- path = sb.toString();
- }
- return path;
- }
-
- private String findPath() {
- List<RestResource> rrc = ReflectionUtils.findAnnotations(RestResource.class, getClass());
- for (RestResource rc : rrc) {
- String p = rc.path();
- if (StringUtils.startsWith(p, '/'))
- p = p.substring(1);
- if (! p.isEmpty())
- return p;
- }
- return null;
- }
-
- /**
- * Returns the config file for this servlet.
- * <p>
- * Subclasses can override this method to provide their own config file.
- * <p>
- * The default implementation uses the path defined by the {@link RestResource#config() @RestResource.config()} property resolved
- * by {@link ConfigMgr#DEFAULT}.
- * @param vrb
- *
- * @return The config file for this servlet.
- * @throws IOException
- */
- protected ConfigFile createConfigFile(VarResolverBuilder vrb) throws IOException {
- String cf = vrb.build().resolve(configPath);
- if (cf.isEmpty())
- return getConfigMgr().create();
- return getConfigMgr().get(cf);
- }
-
- /**
- * Creates the stylesheet for this servlet.
- * <p>
- * The stylesheet is made available on the path <js>"/servlet-path/style.css"</js>.
- * <p>
- * Subclasses can override this method to provide their own stylesheet.
- * <p>
- * The default implementation uses the {@link RestResource#stylesheet() @RestResource.stylesheet()} annotation
- * to determine the stylesheet name and then searches the classpath then working directory
- * for that stylesheet.
- *
- * @return The stylesheet to use for this servlet, or <jk>null</jk> if the stylesheet could not be found.
- * @throws IOException If stylesheet could not be loaded.
- */
- protected StreamResource createStyleSheet() throws IOException {
- for (RestResource r : restResourceAnnotationsChildFirst.values())
- if (! r.stylesheet().isEmpty()) {
- List<InputStream> contents = new ArrayList<InputStream>();
-
- for (String path : StringUtils.split(getVarResolver().resolve(r.stylesheet()), ','))
- contents.add(getResource(path, null));
-
- return new StreamResource(MediaType.forString("text/css"), contents.toArray());
- }
- return null;
- }
-
- /**
- * Creates the favicon for this servlet.
- * <p>
- * The favicon is made available on the path <js>"/servlet-path/favicon.ico"</js>.
- * <p>
- * Subclasses can override this method to provide their own favorites icon.
- * <p>
- * The default implementation uses the {@link RestResource#favicon() @RestResource.favicon()} annotation
- * to determine the file name and then searches the classpath then working directory
- * for that file.
- *
- * @return The icon file to use for this servlet.
- * @throws IOException If icon file could not be loaded.
- */
- protected StreamResource createFavIcon() throws IOException {
- for (RestResource r : restResourceAnnotationsChildFirst.values()) {
- if (! r.favicon().isEmpty()) {
- String path = getVarResolver().resolve(r.favicon());
- InputStream is = getResource(path, null);
- if (is != null) {
- try {
- return new StreamResource(MediaType.forString("image/x-icon"), is);
- } finally {
- is.close();
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Creates the static files map for this servlet.
- * <p>
- * This map defines static files that can be served up through subpaths on this servlet.
- * The map keys are subpaths (e.g. <js>"htdocs"</js>) and the values are locations to look in
- * the classpath and working directory for those files.
- * <p>
- * Subclasses can override this method to provide their own mappings.
- * <p>
- * The default implementation uses the {@link RestResource#staticFiles() @RestResource.staticFiles()} annotation
- * to determine the mappings.
- *
- * @return The list of static file mappings.
- * @throws ParseException
- */
- @SuppressWarnings("unchecked")
- protected Map<String,String> createStaticFilesMap() throws ParseException {
- Map<String,String> m = new LinkedHashMap<String,String>();
-
<TRUNCATED>
[5/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java
new file mode 100644
index 0000000..8e10182
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestConfig.java
@@ -0,0 +1,1153 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static org.apache.juneau.internal.ArrayUtils.*;
+import static org.apache.juneau.serializer.SerializerContext.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.activation.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.encoders.Encoder;
+import org.apache.juneau.ini.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.response.*;
+import org.apache.juneau.rest.vars.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.svl.vars.*;
+
+/**
+ * Defines the initial configuration of a <code>RestServlet</code> or <code>@RestResource</code> annotated object.
+ * <p>
+ * An extension of the {@link ServletConfig} object used during servlet initialization.
+ * <p>
+ * Provides access to the following initialized resources:
+ * <ul>
+ * <li>{@link #getConfigFile()} - The external configuration file for this resource.
+ * <li>{@link #getProperties()} - The modifiable configuration properties for this resource.
+ * <li>{@link #getVarResolverBuilder()} - The variable resolver for this resource.
+ * </ul>
+ * <p>
+ * Methods are provided for overriding or augmenting the information provided by the <ja>@RestResource</ja> annotation.
+ * In general, most information provided in the <ja>@RestResource</ja> annotation can be specified programmatically
+ * through calls on this object.
+ * <p>
+ * To interact with this object, simply implement the following init method in your resource class:
+ * <p class='bcode'>
+ * <jk>public synchronized void</jk> init(RestConfig config) <jk>throws</jk> Exception {
+ * config.addPojoSwaps(CalendarSwap.<jsf>RFC2822DTZ</jsf>.<jk>class</jk>);
+ * config.setProperty(<jsf>PARSER_debug</jsf>, <jk>true</jk>);
+ * <jk>super</jk>.init(config); <jc>// Make sure this is the last line! (or just leave it out entirely)</jc>
+ * }
+ * </p>
+ * <p>
+ * Note that this method is identical to {@link HttpServlet#init(ServletConfig)} except you get access to
+ * this object instead. Also, this method can throw any exception, not just a {@link ServletException}.
+ * <p>
+ * The parent <code>init(RestServletConfig)</code> method will construct a read-only {@link RestContext} object
+ * that contains a snapshot of these settings. If you call <code><jk>super</jk>.init(RestServletConfig)<code> before
+ * you modify this config object, you won't see the changes!
+ */
+@SuppressWarnings("hiding")
+public class RestConfig implements ServletConfig {
+
+ final ServletConfig inner;
+
+ //---------------------------------------------------------------------------
+ // The following fields are meant to be modifiable.
+ // They should not be declared final.
+ // Read-only snapshots of these will be made in RestServletContext.
+ //---------------------------------------------------------------------------
+
+ ObjectMap properties;
+ ConfigFile configFile;
+ VarResolverBuilder varResolverBuilder;
+
+ List<Class<?>>
+ beanFilters = new ArrayList<Class<?>>(),
+ pojoSwaps = new ArrayList<Class<?>>();
+ SerializerGroupBuilder serializers = new SerializerGroupBuilder();
+ ParserGroupBuilder parsers = new ParserGroupBuilder();
+ EncoderGroupBuilder encoders = new EncoderGroupBuilder().append(IdentityEncoder.INSTANCE);
+ List<Object> converters = new ArrayList<Object>();
+ List<Object> guards = new ArrayList<Object>();
+ MimetypesFileTypeMap mimeTypes = new MimetypesFileTypeMap();
+ Map<String,String> defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+ Map<String,Object> defaultResponseHeaders = new LinkedHashMap<String,Object>();
+ List<Object> responseHandlers = new ArrayList<Object>();
+ List<Object> childResources = new ArrayList<Object>();
+ List<MediaType> supportedContentTypes, supportedAcceptTypes;
+ List<Object> styleSheets;
+ Object favIcon;
+ List<Object> staticFiles;
+ RestContext parentContext;
+ String path;
+ String clientVersionHeader = "X-Client-Version";
+
+ Object resourceResolver = RestResourceResolver.class;
+ Object logger = RestLogger.Normal.class;
+ Object callHandler = RestCallHandler.class;
+ Object infoProvider = RestInfoProvider.class;
+
+ /**
+ * Constructor.
+ * @param config The servlet config passed into the servlet by the servlet container.
+ * @param resource The class annotated with <ja>@RestResource</ja>.
+ * @throws ServletException Something bad happened.
+ */
+ RestConfig(ServletConfig config, Class<?> resourceClass, RestContext parentContext) throws ServletException {
+ this.inner = config;
+ this.parentContext = parentContext;
+ try {
+
+ properties = new ObjectMap();
+ configFile = ConfigMgr.DEFAULT.create();
+ varResolverBuilder = new VarResolverBuilder()
+ .vars(
+ SystemPropertiesVar.class,
+ EnvVariablesVar.class,
+ ConfigFileVar.class,
+ IfVar.class,
+ SwitchVar.class
+ );
+
+ VarResolver vr = varResolverBuilder.build();
+
+ Map<Class<?>,RestResource> restResourceAnnotationsParentFirst = ReflectionUtils.findAnnotationsMapParentFirst(RestResource.class, resourceClass);
+
+ // Find our config file. It's the last non-empty @RestResource.config().
+ String configPath = "";
+ for (RestResource r : restResourceAnnotationsParentFirst.values())
+ if (! r.config().isEmpty())
+ configPath = r.config();
+ String cf = vr.resolve(configPath);
+ if (! cf.isEmpty())
+ configFile = ConfigMgr.DEFAULT.get(cf);
+ configFile = configFile.getResolving(vr);
+
+ // Add our config file to the variable resolver.
+ varResolverBuilder.contextObject(ConfigFileVar.SESSION_config, configFile);
+ vr = varResolverBuilder.build();
+
+ // Add our servlet URI to our properties.
+ ServletContext ctx = config.getServletContext();
+ String ctxPath = ctx.getContextPath();
+ // Workaround for bug in Jetty that causes context path to always end in "null".
+ if (ctxPath.endsWith("null"))
+ ctxPath = ctxPath.substring(0, ctxPath.length()-4);
+ properties.put(SERIALIZER_relativeUriBase, ctxPath);
+
+ // Add the servlet init parameters to our properties.
+ for (Enumeration<String> ep = config.getInitParameterNames(); ep.hasMoreElements();) {
+ String p = ep.nextElement();
+ String initParam = config.getInitParameter(p);
+ properties.put(vr.resolve(p), vr.resolve(initParam));
+ }
+
+ // Load stuff from parent-to-child order.
+ // This allows child settings to overwrite parent settings.
+ for (Map.Entry<Class<?>,RestResource> e : restResourceAnnotationsParentFirst.entrySet()) {
+ Class<?> c = e.getKey();
+ RestResource r = e.getValue();
+ for (Property p : r.properties())
+ properties.append(vr.resolve(p.name()), vr.resolve(p.value()));
+ addSerializers(reverse(r.serializers())); // TODO - why reverse?
+ addParsers(reverse(r.parsers())); // TODO - why reverse?
+ addEncoders(reverse(r.encoders())); // TODO - why reverse?
+ addDefaultRequestHeaders(r.defaultRequestHeaders());
+ addDefaultResponseHeaders(r.defaultResponseHeaders());
+ addResponseHandlers(r.responseHandlers());
+ addConverters(r.converters());
+ addGuards(reverse(r.guards()));
+ addChildResources(r.children());
+ addBeanFilters(r.beanFilters());
+ addPojoSwaps(r.pojoSwaps());
+ if (! r.stylesheet().isEmpty())
+ setStyleSheet(c, r.stylesheet());
+ if (! r.favicon().isEmpty())
+ setFavIcon(c, r.favicon());
+ if (! r.staticFiles().isEmpty())
+ addStaticFiles(c, r.staticFiles());
+ if (! r.path().isEmpty())
+ setPath(r.path());
+ if (! r.clientVersionHeader().isEmpty())
+ setClientVersionHeader(r.clientVersionHeader());
+ if (r.resourceResolver() != RestResourceResolver.class)
+ setResourceResolver(r.resourceResolver());
+ if (r.logger() != RestLogger.Normal.class)
+ setLogger(r.logger());
+ if (r.callHandler() != RestCallHandler.class)
+ setCallHandler(r.callHandler());
+ if (r.infoProvider() != RestInfoProvider.class)
+ setInfoProvider(r.infoProvider());
+ }
+
+ addResponseHandlers(
+ StreamableHandler.class,
+ WritableHandler.class,
+ ReaderHandler.class,
+ InputStreamHandler.class,
+ RedirectHandler.class,
+ DefaultHandler.class
+ );
+
+ addMimeTypes(
+ "text/css css CSS",
+ "text/html html htm HTML",
+ "text/plain txt text TXT",
+ "application/javascript js",
+ "image/png png",
+ "image/gif gif",
+ "application/xml xml XML",
+ "application/json json JSON"
+ );
+ } catch (Exception e) {
+ throw new ServletException(e);
+ }
+ }
+
+ /**
+ * Adds the specified {@link Var} classes to this config.
+ * <p>
+ * These variables affect the variable resolver returned by {@link RestRequest#getVarResolverSession()} which is
+ * used to resolve string variables of the form <js>"$X{...}"</js>.
+ * <p>
+ * By default, this config includes the following variables:
+ * <ul class='spaced-list'>
+ * <li>{@link SystemPropertiesVar}
+ * <li>{@link EnvVariablesVar}
+ * <li>{@link ConfigFileVar}
+ * <li>{@link IfVar}
+ * <li>{@link SwitchVar}
+ * </ul>
+ * <p>
+ * Later during the construction of {@link RestContext}, we add the following variables:
+ * <ul>
+ * <li>{@link LocalizationVar}
+ * <li>{@link RequestVar}
+ * <li>{@link SerializedRequestAttrVar}
+ * <li>{@link ServletInitParamVar}
+ * <li>{@link UrlEncodeVar}
+ * </ul>
+ *
+ * @param vars The {@link Var} classes to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addVars(Class<?>...vars) {
+ this.varResolverBuilder.vars(vars);
+ return this;
+ }
+
+ /**
+ * Adds a var context object to this config.
+ * <p>
+ * Var context objects are read-only objects associated with the variable resolver for
+ * vars that require external information.
+ * <p>
+ * For example, the {@link ConfigFileVar} needs access to this resource's {@link ConfigFile} through the {@link ConfigFileVar#SESSION_config}
+ * object that can be specified as either a session object (temporary) or context object (permanent).
+ * In this case, we call the following code to add it to the context map:
+ * <p class='bcode'>
+ * config.addVarContextObject(<jsf>SESSION_config</jsf>, configFile);
+ * </p>
+ *
+ * @param name The context object key (i.e. the name that the Var class looks for).
+ * @param object The context object.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addVarContextObject(String name, Object object) {
+ this.varResolverBuilder.contextObject(name, object);
+ return this;
+ }
+
+ /**
+ * Overwrites the default config file with a custom config file.
+ * <p>
+ * By default, the config file is determined using the {@link RestResource#config() @RestResource.config()} annotation.
+ * This method allows you to programmatically override it with your own custom config file.
+ *
+ * @param configFile The new config file.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setConfigFile(ConfigFile configFile) {
+ this.configFile = configFile;
+ return this;
+ }
+
+ /**
+ * Sets a property on this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#properties()} annotation.
+ *
+ * @param key The property name.
+ * @param value The property value.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setProperty(String key, Object value) {
+ this.properties.put(key, value);
+ return this;
+ }
+
+ /**
+ * Sets multiple properties on this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#properties() @RestResource.properties()} annotation.
+ * <p>
+ * Values in the map are added to the existing properties and are overwritten if duplicates are found.
+ *
+ * @param properties The new properties to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setProperties(Map<String,Object> properties) {
+ this.properties.putAll(properties);
+ return this;
+ }
+
+ /**
+ * Adds class-level bean filters to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#beanFilters() @RestResource.beanFilters()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param beanFilters The bean filters to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addBeanFilters(Class<?>...beanFilters) {
+ this.beanFilters.addAll(Arrays.asList(beanFilters));
+ return this;
+ }
+
+ /**
+ * Adds class-level pojo swaps to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#pojoSwaps() @RestResource.pojoSwaps()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param pojoSwaps The pojo swaps to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addPojoSwaps(Class<?>...pojoSwaps) {
+ this.pojoSwaps.addAll(Arrays.asList(pojoSwaps));
+ return this;
+ }
+
+ /**
+ * Adds class-level serializers to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#serializers() @RestResource.serializers()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param serializers The serializer classes to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addSerializers(Class<?>...serializers) {
+ this.serializers.append(serializers);
+ return this;
+ }
+
+ /**
+ * Adds class-level serializers to this resource.
+ * <p>
+ * Same as {@link #addSerializers(Class...)} except allows you to pass in serializer instances.
+ * The actual serializer ends up being the result of this operation using the bean filters, pojo swaps, and properties on this config:
+ * <p class='bcode'>
+ * serializer = serializer.builder().beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+ * </p>
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param serializers The serializers to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addSerializers(Serializer...serializers) {
+ this.serializers.append(serializers);
+ return this;
+ }
+
+ /**
+ * Adds class-level parsers to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#parsers() @RestResource.parsers()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param parsers The parser classes to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addParsers(Class<?>...parsers) {
+ this.parsers.append(parsers);
+ return this;
+ }
+
+ /**
+ * Adds class-level parsers to this resource.
+ * <p>
+ * Same as {@link #addParsers(Class...)} except allows you to pass in parser instances.
+ * The actual parser ends up being the result of this operation using the bean filters, pojo swaps, and properties on this config:
+ * <p class='bcode'>
+ * parser = parser.builder().beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+ * </p>
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param parsers The parsers to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addParsers(Parser...parsers) {
+ this.parsers.append(parsers);
+ return this;
+ }
+
+ /**
+ * Adds class-level encoders to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#encoders() @RestResource.encoders()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ * <p>
+ * By default, only the {@link IdentityEncoder} is included in this list.
+ *
+ * @param encoders The parser classes to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addEncoders(Class<?>...encoders) {
+ this.encoders.append(encoders);
+ return this;
+ }
+
+ /**
+ * Adds class-level encoders to this resource.
+ * <p>
+ * Same as {@link #addEncoders(Class...)} except allows you to pass in encoder instances.
+ *
+ * @param encoders The encoders to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addEncoders(Encoder...encoders) {
+ this.encoders.append(encoders);
+ return this;
+ }
+
+ /**
+ * Adds class-level converters to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#converters() @RestResource.converters()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ * <p>
+ * By default, this config includes the following converters:
+ * <ul class='spaced-list'>
+ * <li>{@link StreamableHandler}
+ * <li>{@link WritableHandler}
+ * <li>{@link ReaderHandler}
+ * <li>{@link InputStreamHandler}
+ * <li>{@link RedirectHandler}
+ * <li>{@link DefaultHandler}
+ * </ul>
+ *
+ * @param converters The converter classes to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addConverters(Class<?>...converters) {
+ this.converters.addAll(Arrays.asList(converters));
+ return this;
+ }
+
+ /**
+ * Adds class-level encoders to this resource.
+ * <p>
+ * Same as {@link #addConverters(Class...)} except allows you to pass in converter instances.
+ *
+ * @param converters The converters to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addConverters(RestConverter...converters) {
+ this.converters.addAll(Arrays.asList(converters));
+ return this;
+ }
+
+ /**
+ * Adds class-level guards to this resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#guards() @RestResource.guards()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param guards The guard classes to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addGuards(Class<?>...guards) {
+ this.guards.addAll(Arrays.asList(guards));
+ return this;
+ }
+
+ /**
+ * Adds class-level guards to this resource.
+ * <p>
+ * Same as {@link #addGuards(Class...)} except allows you to pass in guard instances.
+ *
+ * @param guards The guards to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addGuards(RestGuard...guards) {
+ this.guards.addAll(Arrays.asList(guards));
+ return this;
+ }
+
+ /**
+ * Adds MIME-type definitions.
+ * <p>
+ * These definitions are used in the following locations for setting the media type on responses:
+ * <ul>
+ * <li>{@link RestRequest#getReaderResource(String)}
+ * <li>Static files resolved through {@link RestResource#staticFiles()}
+ * </ul>
+ * <p>
+ * Refer to {@link MimetypesFileTypeMap#addMimeTypes(String)} for an explanation of the format.
+ * <p>
+ * By default, this config includes the following mime-type definitions:
+ * <ul class='spaced-list'>
+ * <li><js>"text/css css CSS"</js>
+ * <li><js>"text/html html htm HTML"</js>
+ * <li><js>"text/plain txt text TXT"</js>
+ * <li><js>"application/javascript js"</js>
+ * <li><js>"image/png png"</js>
+ * <li><js>"image/gif gif"</js>
+ * <li><js>"application/xml xml XML"</js>
+ * <li><js>"application/json json JSON"</js>
+ * </ul>
+ *
+ * @param mimeTypes The MIME-types to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addMimeTypes(String...mimeTypes) {
+ for (String mimeType : mimeTypes)
+ this.mimeTypes.addMimeTypes(mimeType);
+ return this;
+ }
+
+ /**
+ * Adds class-level default HTTP request headers to this resource.
+ * <p>
+ * Default request headers are default values for when HTTP requests do not specify a header value.
+ * For example, you can specify a default value for <code>Accept</code> if a request does not specify that header value.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()} annotation.
+ *
+ * @param name The HTTP header name.
+ * @param value The HTTP header value.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addDefaultRequestHeader(String name, Object value) {
+ this.defaultRequestHeaders.put(name, StringUtils.toString(value));
+ return this;
+ }
+
+ /**
+ * Adds class-level default HTTP request headers to this resource.
+ * <p>
+ * Default request headers are default values for when HTTP requests do not specify a header value.
+ * For example, you can specify a default value for <code>Accept</code> if a request does not specify that header value.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()} annotation.
+ *
+ * @param headers HTTP headers of the form <js>"Name: Value"</js>.
+ * @return This object (for method chaining).
+ * @throws RestServletException If header string is not correctly formatted.
+ */
+ public RestConfig addDefaultRequestHeaders(String...headers) throws RestServletException {
+ for (String header : headers) {
+ String[] h = RestUtils.parseHeader(header);
+ if (h == null)
+ throw new RestServletException("Invalid default request header specified: ''{0}''. Must be in the format: ''Header-Name: header-value''", header);
+ addDefaultRequestHeader(h[0], h[1]);
+ }
+ return this;
+ }
+
+ /**
+ * Adds class-level default HTTP response headers to this resource.
+ * <p>
+ * Default response headers are headers that will be appended to all responses if those headers have not already been
+ * set on the response object.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()} annotation.
+ * <p>
+ * Values are added AFTER those found in the annotation and therefore take precedence over those defined via the annotation.
+ *
+ * @param name The HTTP header name.
+ * @param value The HTTP header value.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addDefaultResponseHeader(String name, Object value) {
+ this.defaultResponseHeaders.put(name, value);
+ return this;
+ }
+
+ /**
+ * Adds class-level default HTTP response headers to this resource.
+ * <p>
+ * Default response headers are headers that will be appended to all responses if those headers have not already been
+ * set on the response object.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()} annotation.
+ *
+ * @param headers HTTP headers of the form <js>"Name: Value"</js>.
+ * @return This object (for method chaining).
+ * @throws RestServletException If header string is not correctly formatted.
+ */
+ public RestConfig addDefaultResponseHeaders(String...headers) throws RestServletException {
+ for (String header : headers) {
+ String[] h = RestUtils.parseHeader(header);
+ if (h == null)
+ throw new RestServletException("Invalid default response header specified: ''{0}''. Must be in the format: ''Header-Name: header-value''", header);
+ addDefaultResponseHeader(h[0], h[1]);
+ }
+ return this;
+ }
+
+ /**
+ * Adds class-level response handler classes to this resource.
+ * <p>
+ * Response handlers are responsible for converting various POJOs returned by REST methods into actual HTTP responses.
+ * <p>
+ * By default, this config includes the following response handlers:
+ * <ul class='spaced-list'>
+ * <li>{@link StreamableHandler}
+ * <li>{@link WritableHandler}
+ * <li>{@link ReaderHandler}
+ * <li>{@link InputStreamHandler}
+ * <li>{@link RedirectHandler}
+ * <li>{@link DefaultHandler}
+ * </ul>
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#responseHandlers() @RestResource.responseHandlers()} annotation.
+ *
+ * @param responseHandlers The response handlers to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addResponseHandlers(Class<?>...responseHandlers) {
+ this.responseHandlers.addAll(Arrays.asList(responseHandlers));
+ return this;
+ }
+
+ /**
+ * Adds class-level response handlers to this resource.
+ * <p>
+ * Same as {@link #addResponseHandlers(Class...)} except allows you to pass in response handler instances.
+ *
+ * @param responseHandlers The response handlers to add to this config.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addResponseHandlers(ResponseHandler...responseHandlers) {
+ this.responseHandlers.addAll(Arrays.asList(responseHandlers));
+ return this;
+ }
+
+ /**
+ * Adds a child resource to this resource.
+ * <p>
+ * Child resources are resources that are accessed under the path of the parent resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
+ *
+ * @param path The child path of the resource. Must conform to {@link RestResource#path()} format.
+ * @param child The child resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addChildResource(String path, Object child) {
+ this.childResources.add(new Pair<String,Object>(path, child));
+ return this;
+ }
+
+ /**
+ * Add child resources to this resource.
+ * <p>
+ * Child resources are resources that are accessed under the path of the parent resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
+ *
+ * @param children The child resources to add to this resource.
+ * Children must be annotated with {@link RestResource#path()} to identify the child path.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addChildResources(Object...children) {
+ this.childResources.addAll(Arrays.asList(children));
+ return this;
+ }
+
+ /**
+ * Add child resources to this resource.
+ * <p>
+ * Child resources are resources that are accessed under the path of the parent resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#children() @RestResource.children()} annotation.
+ *
+ * @param children The child resources to add to this resource.
+ * Children must be annotated with {@link RestResource#path()} to identify the child path.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addChildResources(Class<?>...children) {
+ this.childResources.addAll(Arrays.asList(children));
+ return this;
+ }
+
+ /**
+ * Specifies the list of supported <code>Accept</code> media types for this resource.
+ * <p>
+ * This overrides the media types inferred from the parsers on this resource.
+ * <p>
+ * There is no annotation equivalent to this method call.
+ *
+ * @param mediaTypes The new list of media types supported by this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setSupportedAcceptTypes(String...mediaTypes) {
+ supportedAcceptTypes = new ArrayList<MediaType>();
+ for (String mediaType : mediaTypes)
+ supportedAcceptTypes.add(MediaType.forString(mediaType));
+ return this;
+ }
+
+ /**
+ * Specifies the list of supported <code>Accept</code> media types for this resource.
+ * <p>
+ * This overrides the media types inferred from the parsers on this resource.
+ * <p>
+ * There is no annotation equivalent to this method call.
+ *
+ * @param mediaTypes The new list of media types supported by this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setSupportedAcceptTypes(MediaType...mediaTypes) {
+ supportedAcceptTypes = Arrays.asList(mediaTypes);
+ return this;
+ }
+
+ /**
+ * Specifies the list of supported <code>Content-Type</code> media types for this resource.
+ * <p>
+ * This overrides the media types inferred from the serializers on this resource.
+ * <p>
+ * There is no annotation equivalent to this method call.
+ *
+ * @param mediaTypes The new list of media types supported by this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setSupportedContentTypes(String...mediaTypes) {
+ supportedContentTypes = new ArrayList<MediaType>();
+ for (String mediaType : mediaTypes)
+ supportedContentTypes.add(MediaType.forString(mediaType));
+ return this;
+ }
+
+ /**
+ * Specifies the list of supported <code>Content-Type</code> media types for this resource.
+ * <p>
+ * This overrides the media types inferred from the serializers on this resource.
+ * <p>
+ * There is no annotation equivalent to this method call.
+ *
+ * @param mediaTypes The new list of media types supported by this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setSupportedContentTypes(MediaType...mediaTypes) {
+ supportedContentTypes = Arrays.asList(mediaTypes);
+ return this;
+ }
+
+ /**
+ * Specifies the stylesheets that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#stylesheet() @RestResource.stylesheet()} annotation.
+ * <p>
+ * The object types can be any of the following:
+ * <ul>
+ * <li>{@link InputStream}
+ * <li>{@link Reader}
+ * <li>{@link File}
+ * <li>{@link CharSequence}
+ * <li><code><jk>byte</jk>[]</code>
+ * </ul>
+ * The contents of all these stylesheets will be aggregated into a single page in the order they are specified in this list.
+ *
+ * @param styleSheets The new list of style sheets that make up the <code>styles.css</code> page.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setStyleSheet(Object...styleSheets) {
+ this.styleSheets = new ArrayList<Object>(Arrays.asList(styleSheets));
+ return this;
+ }
+
+ /**
+ * Specifies the stylesheet that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#stylesheet() @RestResource.stylesheet()} annotation.
+ * <p>
+ * Use this method to specify a resource located in the classpath.
+ * This call uses the {@link Class#getResourceAsStream(String)} method to retrieve the stylesheet contents.
+ *
+ * @param resourceClass The resource class used to resolve the resource stream.
+ * @param resourcePath The path passed to the {@link Class#getResourceAsStream(String)} method.
+ * Can also be a path starting with <js>"file://"</js> denoting a location to pull from the file system.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setStyleSheet(Class<?> resourceClass, String resourcePath) {
+ this.styleSheets = new ArrayList<Object>();
+ this.styleSheets.add(new Pair<Class<?>,String>(resourceClass, resourcePath));
+ return this;
+ }
+
+ /**
+ * Adds to the stylesheet that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+ * <p>
+ * Same as {@link #setStyleSheet(Object...)} except appends to the existing list instead of replacing.
+ *
+ * @param styleSheets The list of style sheets to add that make up the <code>styles.css</code> page.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addStyleSheet(Object...styleSheets) {
+ if (this.styleSheets == null)
+ this.styleSheets = new ArrayList<Object>();
+ this.styleSheets.addAll(Arrays.asList(styleSheets));
+ return this;
+ }
+
+ /**
+ * Adds to the stylesheet that make up the contents of the page <js>"/resource-path/styles.css"</js>.
+ * <p>
+ * Same as {@link #setStyleSheet(Class,String)} except appends to the existing list instead of replacing.
+ *
+ * @param resourceClass The resource class used to resolve the resource stream.
+ * @param resourcePath The path passed to the {@link Class#getResourceAsStream(String)} method.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addStyleSheet(Class<?> resourceClass, String resourcePath) {
+ if (this.styleSheets == null)
+ this.styleSheets = new ArrayList<Object>();
+ this.styleSheets.add(new Pair<Class<?>,String>(resourceClass, resourcePath));
+ return this;
+ }
+
+ /**
+ * Specifies the icon contents that make up the contents of the page <js>"/resource-path/favicon.ico"</js>.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#favicon() @RestResource.favicon()} annotation.
+ * <p>
+ * The object type can be any of the following:
+ * <ul>
+ * <li>{@link InputStream}
+ * <li>{@link File}
+ * <li><code><jk>byte</jk>[]</code>
+ * </ul>
+ *
+ * @param favIcon The contents that make up the <code>favicon.ico</code> page.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setFavIcon(Object favIcon) {
+ this.favIcon = favIcon;
+ return this;
+ }
+
+ /**
+ * Specifies the icon contents that make up the contents of the page <js>"/resource-path/favicon.ico"</js>.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#favicon() @RestResource.favicon()} annotation.
+ * <p>
+ * Use this method to specify a resource located in the classpath.
+ * This call uses the {@link Class#getResourceAsStream(String)} method to retrieve the stylesheet contents.
+ *
+ * @param resourceClass The resource class used to resolve the resource stream.
+ * @param resourcePath The path passed to the {@link Class#getResourceAsStream(String)} method.
+ * Can also be a path starting with <js>"file://"</js> denoting a location to pull from the file system.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setFavIcon(Class<?> resourceClass, String resourcePath) {
+ this.favIcon = new Pair<Class<?>,String>(resourceClass, resourcePath);
+ return this;
+ }
+
+ /**
+ * Appends to the static files resource map.
+ * <p>
+ * Use this method to specify resources located in the classpath to be served up as static files.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#staticFiles() @RestResource.staticFiles()} annotation.
+ *
+ * @param resourceClass The resource class used to resolve the resource streams.
+ * @param staticFilesString A JSON string denoting a map of child URLs to classpath subdirectories.
+ * For example, if this string is <js>"{htdocs:'docs'}"</js> with class <code>com.foo.MyResource</code>,
+ * then URLs of the form <js>"/resource-path/htdocs/..."</js> will resolve to files located in the <code>com.foo.docs</code> package.
+ * @return This object (for method chaining).
+ */
+ public RestConfig addStaticFiles(Class<?> resourceClass, String staticFilesString) {
+ if (staticFiles == null)
+ staticFiles = new ArrayList<Object>();
+ staticFiles.add(new Pair<Class<?>,Object>(resourceClass, staticFilesString));
+ return this;
+ }
+
+ /**
+ * Overrides the default REST resource resolver.
+ * <p>
+ * The resource resolver is used to resolve instances from {@link Class} objects defined in the {@link RestResource#children()} annotation.
+ * The default value is the base class {@link RestResourceResolver}.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#resourceResolver() @RestResource.resourceResolver()} annotation.
+ *
+ * @param resourceResolver The new resource resolver.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setResourceResolver(Class<? extends RestResourceResolver> resourceResolver) {
+ this.resourceResolver = resourceResolver;
+ return this;
+ }
+
+ /**
+ * Overrides the default REST resource resolver.
+ * <p>
+ * Same as {@link #setResourceResolver(Class)} except allows you to specify an instance instead of a class.
+ *
+ * @param resourceResolver The new resource resolver.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setResourceResolver(RestResourceResolver resourceResolver) {
+ this.resourceResolver = resourceResolver;
+ return this;
+ }
+
+ /**
+ * Sets the URL path of the resource <js>"/foobar"</js>.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#path() @RestResource.path()} annotation.
+ *
+ * @param path The URL path of this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setPath(String path) {
+ if (StringUtils.startsWith(path, '/'))
+ path = path.substring(1);
+ this.path = path;
+ return this;
+ }
+
+ /**
+ * Sets name of the header used to denote the client version on HTTP requests.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#clientVersionHeader() @RestResource.clientVersionHeader()} annotation.
+ *
+ * @param clientVersionHeader The name of the HTTP header that denotes the client version.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setClientVersionHeader(String clientVersionHeader) {
+ this.clientVersionHeader = clientVersionHeader;
+ return this;
+ }
+
+ /**
+ * Overrides the logger for the resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#logger() @RestResource.logger()} annotation.
+ *
+ * @param logger The new logger for this resource. Can be <jk>null</jk> to disable logging.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setLogger(Class<? extends RestLogger> logger) {
+ this.logger = logger;
+ return this;
+ }
+
+ /**
+ * Overrides the logger for the resource.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#logger() @RestResource.logger()} annotation.
+ *
+ * @param logger The new logger for this resource. Can be <jk>null</jk> to disable logging.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setLogger(RestLogger logger) {
+ this.logger = logger;
+ return this;
+ }
+
+ /**
+ * Overrides the call handler for the resource.
+ * <p>
+ * The call handler is the object that handles execution of REST HTTP calls.
+ * Subclasses can be created that customize the behavior of how REST calls are handled.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#callHandler() @RestResource.callHandler()} annotation.
+ *
+ * @param restHandler The new call handler for this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setCallHandler(Class<? extends RestCallHandler> restHandler) {
+ this.callHandler = restHandler;
+ return this;
+ }
+
+ /**
+ * Overrides the call handler for the resource.
+ * <p>
+ * The call handler is the object that handles execution of REST HTTP calls.
+ * Subclasses can be created that customize the behavior of how REST calls are handled.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#callHandler() @RestResource.callHandler()} annotation.
+ *
+ * @param restHandler The new call handler for this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setCallHandler(RestCallHandler restHandler) {
+ this.callHandler = restHandler;
+ return this;
+ }
+
+ /**
+ * Overrides the info provider for the resource.
+ * <p>
+ * The info provider provides all the various information about a resource such as the Swagger documentation.
+ * Subclasses can be created that customize the information.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#infoProvider() @RestResource.infoProvider()} annotation.
+ *
+ * @param infoProvider The new info provider for this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setInfoProvider(Class<? extends RestInfoProvider> infoProvider) {
+ this.infoProvider = infoProvider;
+ return this;
+ }
+
+ /**
+ * Overrides the info provider for the resource.
+ * <p>
+ * The info provider provides all the various information about a resource such as the Swagger documentation.
+ * Subclasses can be created that customize the information.
+ * <p>
+ * This is the programmatic equivalent to the {@link RestResource#infoProvider() @RestResource.infoProvider()} annotation.
+ *
+ * @param infoProvider The new info provider for this resource.
+ * @return This object (for method chaining).
+ */
+ public RestConfig setInfoProvider(RestInfoProvider infoProvider) {
+ this.infoProvider = infoProvider;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PropertyStore} object initialized with the properties defined in this config.
+ * @return A new property store.
+ */
+ protected PropertyStore createPropertyStore() {
+ return PropertyStore.create().addProperties(properties);
+ }
+
+
+ //----------------------------------------------------------------------------------------------------
+ // Methods that give access to the config file, var resolver, and properties.
+ //----------------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the external configuration file for this resource.
+ * <p>
+ * The configuration file location is determined via the {@link RestResource#config() @RestResource.config()} annotation on the resource.
+ * <p>
+ * The config file can be programmatically overridden by adding the following method to your resource:
+ * <p class='bcode'>
+ * <jk>public</jk> ConfigFile createConfigFile(ServletConfig servletConfig) <jk>throws</jk> ServletException;
+ * </p>
+ * <p>
+ * If a config file is not set up, then an empty config file will be returned that is not backed by any file.
+ *
+ * @return The external config file for this resource. Never <jk>null</jk>.
+ */
+ public ConfigFile getConfigFile() {
+ return configFile;
+ }
+
+ /**
+ * Returns the configuration properties for this resource.
+ * <p>
+ * The configuration properties are determined via the {@link RestResource#properties()} annotation on the resource.
+ * <p>
+ * The configuration properties can be augmented programmatically by adding the following method to your resource:
+ * <p class='bcode'>
+ * <jk>public</jk> ObjectMap createProperties(ServletConfig servletConfig) <jk>throws</jk> ServletException;
+ * </p>
+ * <p>
+ * These properties can be modified during servlet initialization.
+ * However, any modifications made after {@link RestServlet#init(RestConfig)} has been called will have no effect.
+ *
+ * @return The configuration properties for this resource. Never <jk>null</jk>.
+ */
+ public ObjectMap getProperties() {
+ return properties;
+ }
+
+ /**
+ * Creates the variable resolver for this resource.
+ * <p>
+ * The variable resolver returned by this method can resolve the following variables:
+ * <ul>
+ * <li>{@link SystemPropertiesVar}
+ * <li>{@link EnvVariablesVar}
+ * <li>{@link ConfigFileVar}
+ * <li>{@link IfVar}
+ * <li>{@link SwitchVar}
+ * </ul>
+ * <p>
+ * Note that the variables supported here are only a subset of those returned by {@link RestRequest#getVarResolverSession()}.
+ *
+ * @return The variable resolver for this resource. Never <jk>null</jk>.
+ */
+ public VarResolverBuilder getVarResolverBuilder() {
+ return varResolverBuilder;
+ }
+
+
+ //----------------------------------------------------------------------------------------------------
+ // Methods inherited from ServletConfig
+ //----------------------------------------------------------------------------------------------------
+
+ @Override /* ServletConfig */
+ public String getInitParameter(String name) {
+ return inner.getInitParameter(name);
+ }
+
+ @Override /* ServletConfig */
+ public Enumeration<String> getInitParameterNames() {
+ return inner.getInitParameterNames();
+ }
+
+ @Override /* ServletConfig */
+ public ServletContext getServletContext() {
+ return inner.getServletContext();
+ }
+
+ @Override /* ServletConfig */
+ public String getServletName() {
+ return inner.getServletName();
+ }
+}
[7/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Posted by ja...@apache.org.
Allow @RestResource/@RestMethod annotations to be used on any classes.
Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/07843d64
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/07843d64
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/07843d64
Branch: refs/heads/master
Commit: 07843d641d9911843e3c489bd96b5999de7faa84
Parents: db0cf72
Author: JamesBognar <ja...@apache.org>
Authored: Thu Mar 16 22:16:57 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Thu Mar 16 22:16:57 2017 -0400
----------------------------------------------------------------------
.../java/org/apache/juneau/jena/package.html | 2 +-
.../org/apache/juneau/utils/ClassUtilsTest.java | 36 +
.../org/apache/juneau/internal/ArrayUtils.java | 21 +-
.../org/apache/juneau/internal/ClassUtils.java | 90 +
.../apache/juneau/internal/CollectionUtils.java | 2 +-
.../org/apache/juneau/internal/IOUtils.java | 32 +
.../org/apache/juneau/internal/ObjectUtils.java | 36 +
.../java/org/apache/juneau/internal/Pair.java | 64 +
.../apache/juneau/internal/ReflectionUtils.java | 18 +-
.../java/org/apache/juneau/json/package.html | 2 +-
.../java/org/apache/juneau/uon/package.html | 2 +-
.../org/apache/juneau/urlencoding/package.html | 2 +-
.../java/org/apache/juneau/xml/package.html | 2 +-
juneau-core/src/main/javadoc/overview.html | 226 +-
.../juneau/examples/rest/DirectoryResource.java | 11 +-
.../examples/rest/DockerRegistryResource.java | 11 +-
.../examples/rest/SampleRemoteableServlet.java | 2 +-
.../juneau/examples/rest/SqlQueryResource.java | 23 +-
.../rest/addressbook/AddressBookResource.java | 4 +-
.../apache/juneau/microservice/Resource.java | 19 +-
.../juneau/microservice/ResourceGroup.java | 19 +-
.../org/apache/juneau/microservice/package.html | 12 +-
.../microservice/resources/ConfigResource.java | 14 +-
.../resources/DirectoryResource.java | 2 +-
.../microservice/resources/LogsResource.java | 25 +-
.../juneau/rest/jaxrs/JuneauProvider.java | 2 +-
.../juneau/rest/test/AcceptCharsetResource.java | 2 +-
.../apache/juneau/rest/test/ConfigResource.java | 4 +-
.../juneau/rest/test/ContentResource.java | 2 +-
.../apache/juneau/rest/test/GroupsResource.java | 17 +-
.../apache/juneau/rest/test/ParamsResource.java | 10 +-
.../apache/juneau/rest/test/PathResource.java | 6 +-
.../java/org/apache/juneau/rest/test/Root.java | 2 +-
.../juneau/rest/test/ErrorConditionsTest.java | 2 +-
.../org/apache/juneau/rest/test/TestUtils.java | 14 +-
.../java/org/apache/juneau/rest/CallMethod.java | 996 ++++++
.../java/org/apache/juneau/rest/CallRouter.java | 98 +
.../juneau/rest/ClientVersionMatcher.java | 10 +-
.../java/org/apache/juneau/rest/Redirect.java | 3 +-
.../org/apache/juneau/rest/ResponseHandler.java | 4 +-
.../org/apache/juneau/rest/RestCallHandler.java | 348 ++
.../java/org/apache/juneau/rest/RestConfig.java | 1153 ++++++
.../org/apache/juneau/rest/RestContext.java | 1418 ++++++++
.../org/apache/juneau/rest/RestException.java | 4 +-
.../apache/juneau/rest/RestInfoProvider.java | 499 +++
.../java/org/apache/juneau/rest/RestLogger.java | 233 ++
.../juneau/rest/RestMatcherReflecting.java | 6 +-
.../org/apache/juneau/rest/RestRequest.java | 84 +-
.../juneau/rest/RestResourceResolver.java | 80 +
.../org/apache/juneau/rest/RestResponse.java | 13 +-
.../org/apache/juneau/rest/RestServlet.java | 3321 +-----------------
.../apache/juneau/rest/RestServletContext.java | 306 --
.../apache/juneau/rest/RestServletDefault.java | 7 +-
.../juneau/rest/RestServletGroupDefault.java | 2 +-
.../java/org/apache/juneau/rest/RestUtils.java | 9 +
.../apache/juneau/rest/annotation/FormData.java | 2 +-
.../apache/juneau/rest/annotation/Query.java | 2 +-
.../juneau/rest/annotation/RestMethod.java | 5 +
.../juneau/rest/annotation/RestResource.java | 152 +-
.../rest/jena/RestServletJenaDefault.java | 7 +-
.../rest/jena/RestServletJenaGroupDefault.java | 2 +-
.../rest/labels/ChildResourceDescriptions.java | 14 +-
.../java/org/apache/juneau/rest/package.html | 125 +-
.../rest/remoteable/RemoteableServlet.java | 4 +-
.../juneau/rest/response/DefaultHandler.java | 3 -
.../rest/response/InputStreamHandler.java | 3 -
.../juneau/rest/response/ReaderHandler.java | 3 -
.../juneau/rest/response/RedirectHandler.java | 3 -
.../juneau/rest/response/StreamableHandler.java | 3 -
.../juneau/rest/response/WritableHandler.java | 3 -
.../juneau/rest/vars/ServletInitParamVar.java | 2 +-
71 files changed, 5775 insertions(+), 3890 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
----------------------------------------------------------------------
diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
index c29250c..5f1d7aa 100644
--- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
+++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/package.html
@@ -1083,7 +1083,7 @@
The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
These variables are replaced at runtime based on the HTTP request locale.
Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
- See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+ See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
</p>
<p>
This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
index ce2d30e..688c2bc 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/utils/ClassUtilsTest.java
@@ -112,4 +112,40 @@ public class ClassUtilsTest {
public void getClassFromReadableName() throws Exception {
fail("Not implemented");
}
+
+ //====================================================================================================
+ // findPublicMethod
+ //====================================================================================================
+ @Test
+ public void testFindPublicMethod() {
+
+ assertNotNull(findPublicMethod(B.class, "m1", void.class));
+ assertNull(findPublicMethod(B.class, "m1", int.class));
+
+ assertNull(findPublicMethod(B.class, "m2", void.class));
+
+ assertNull(findPublicMethod(B.class, "m3", void.class));
+ assertNotNull(findPublicMethod(B.class, "m3", int.class));
+
+ assertNotNull(findPublicMethod(B.class, "m4", CharSequence.class));
+ assertNotNull(findPublicMethod(B.class, "m4", Object.class));
+ assertNull(findPublicMethod(B.class, "m4", String.class));
+
+ assertNotNull(findPublicMethod(B.class, "m5", void.class, int.class, CharSequence.class));
+ assertNotNull(findPublicMethod(B.class, "m5", void.class, int.class, String.class));
+ assertNull(findPublicMethod(B.class, "m5", void.class, int.class, Object.class));
+
+ assertNull(findPublicMethod(B.class, "m5", void.class, int.class));
+ assertNull(findPublicMethod(B.class, "m5", void.class, int.class, CharSequence.class, CharSequence.class));
+ }
+
+ public static class B {
+
+ public void m1() {};
+ protected void m2() {};
+ public int m3() { return 0; }
+ public CharSequence m4() { return ""; }
+
+ public void m5(int f1, CharSequence f2) {}
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
index 12c7614..effd363 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
@@ -195,9 +195,9 @@ public final class ArrayUtils {
* @param componentType The component type of the collection.
* @return A new array.
*/
- public static <T> Object toArray(Collection<T> c, Class<T> componentType) {
+ public static <T> Object toArray(Collection<?> c, Class<T> componentType) {
Object a = Array.newInstance(componentType, c.size());
- Iterator<T> it = c.iterator();
+ Iterator<?> it = c.iterator();
int i = 0;
while (it.hasNext())
Array.set(a, i++, it.next());
@@ -205,6 +205,23 @@ public final class ArrayUtils {
}
/**
+ * Shortcut for calling <code>myList.toArray(new T[myList.size()]);</code>
+ *
+ * @param c The collection being converted to an array.
+ * @param componentType The component type of the array.
+ * @return The collection converted to an array.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T[] toObjectArray(Collection<?> c, Class<T> componentType) {
+ Object a = Array.newInstance(componentType, c.size());
+ Iterator<?> it = c.iterator();
+ int i = 0;
+ while (it.hasNext())
+ Array.set(a, i++, it.next());
+ return (T[])a;
+ }
+
+ /**
* Copies the specified array into the specified list.
* Works on both object and primitive arrays.
*
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 088d70a..4ba239f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -426,6 +426,96 @@ public final class ClassUtils {
}
}
+ /**
+ * Finds a public method with the specified parameters.
+ *
+ * @param c The class to look for the method.
+ * @param name The method name.
+ * @param returnType The return type of the method.
+ * Can be a super type of the actual return type.
+ * For example, if the actual return type is <code>CharSequence</code>, then <code>Object</code> will match but <code>String</code> will not.
+ * @param parameterTypes The parameter types of the method.
+ * Can be subtypes of the actual parameter types.
+ * For example, if the parameter type is <code>CharSequence</code>, then <code>String</code> will match but <code>Object</code> will not.
+ * @return The matched method, or <jk>null</jk> if no match was found.
+ */
+ public static Method findPublicMethod(Class<?> c, String name, Class<?> returnType, Class<?>...parameterTypes) {
+ for (Method m : c.getMethods()) {
+ if (isPublic(m) && m.getName().equals(name)) {
+ Class<?> rt = m.getReturnType();
+ if (isParentClass(returnType, rt)) {
+ Class<?>[] pt = m.getParameterTypes();
+ if (pt.length == parameterTypes.length) {
+ boolean matches = true;
+ for (int i = 0; i < pt.length; i++) {
+ if (! isParentClass(pt[i], parameterTypes[i])) {
+ matches = false;
+ break;
+ }
+ }
+ if (matches)
+ return m;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds a public constructor with the specified parameters without throwing an exception.
+ *
+ * @param c The class to search for a constructor.
+ * @param parameterTypes The parameter types in the constructor.
+ * Can be subtypes of the actual constructor argument types.
+ * @return The matching constructor, or <jk>null</jk> if constructor could not be found.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> Constructor<T> findPublicConstructor(Class<T> c, Class<?>...parameterTypes) {
+ for (Constructor<?> n : c.getConstructors()) {
+ if (isPublic(n)) {
+ Class<?>[] pt = n.getParameterTypes();
+ if (pt.length == parameterTypes.length) {
+ boolean matches = true;
+ for (int i = 0; i < pt.length; i++) {
+ if (! isParentClass(pt[i], parameterTypes[i])) {
+ matches = false;
+ break;
+ }
+ }
+ if (matches)
+ return (Constructor<T>)n;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds the public constructor that can take in the specified arguments.
+ *
+ * @param c The class we're trying to construct.
+ * @param args The arguments we want to pass into the constructor.
+ * @return The constructor, or <jk>null</jk> if a public constructor could not be found that takes
+ * in the specified arguments.
+ */
+ public static <T> Constructor<T> findPublicConstructor(Class<T> c, Object...args) {
+ return findPublicConstructor(c, getClasses(args));
+ }
+
+ /**
+ * Returns the class types for the specified arguments.
+ *
+ * @param args The objects we're getting the classes of.
+ * @return The classes of the arguments.
+ */
+ public static Class<?>[] getClasses(Object...args) {
+ Class<?>[] pt = new Class<?>[args.length];
+ for (int i = 0; i < args.length; i++)
+ pt[i] = args[i] == null ? null : args[i].getClass();
+ return pt;
+ }
+
// This code is inherently unsafe (but still potentially useful?)
//
// /**
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
index 7c11d22..c413427 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/CollectionUtils.java
@@ -25,7 +25,7 @@ public class CollectionUtils {
* @param in The map to reverse the order on.
* @return A new {@link LinkedHashMap} with keys in reverse order.
*/
- public static <K,V> LinkedHashMap<K,V> reverse(LinkedHashMap<K,V> in) {
+ public static <K,V> LinkedHashMap<K,V> reverse(Map<K,V> in) {
if (in == null)
return null;
LinkedHashMap<K,V> m = new LinkedHashMap<K,V>();
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
index 72ece2c..77caa63 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/IOUtils.java
@@ -361,4 +361,36 @@ public final class IOUtils {
closeQuietly((Writer)o2);
}
}
+
+ /**
+ * Converts an object to an <code>InputStream</code>.
+ *
+ * @param o The object to convert to an input stream.
+ * Can be any of the following:
+ * <ul>
+ * <li>{@link InputStream}
+ * <li>{@link Reader}
+ * <li>{@link File}
+ * <li>{@link CharSequence} - Converted to UTF-8 stream.
+ * <li><code><jk>byte</jk>[]</code>
+ * <li><code><jk>null</jk></code> - Returns null.
+ * </ul>
+ * @return The object converted to an input stream.
+ * @throws IOException If invalid object passed in or file could not be read.
+ */
+ public static InputStream toInputStream(Object o) throws IOException {
+ if (o == null)
+ return null;
+ if (o instanceof InputStream)
+ return (InputStream)o;
+ if (o instanceof File)
+ return new FileInputStream((File)o);
+ if (o instanceof byte[])
+ return new ByteArrayInputStream((byte[])o);
+ if (o instanceof CharSequence)
+ return new ByteArrayInputStream(((CharSequence)o).toString().getBytes(UTF8));
+ if (o instanceof Reader)
+ return new ByteArrayInputStream(IOUtils.read((Reader)o).getBytes(UTF8));
+ throw new IOException("Invalid object type passed to IOUtils.toInputStream(Object): " + o.getClass().getName());
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java
new file mode 100644
index 0000000..fac4337
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ObjectUtils.java
@@ -0,0 +1,36 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.internal;
+
+/**
+ * Object-related utility methods.
+ */
+public class ObjectUtils {
+
+ /**
+ * Returns <jk>true</jk> if the specified objects are equal.
+ * <p>
+ * Gracefully handles <jk>null</jk>s.
+ *
+ * @param o1 Object #1
+ * @param o2 Object #2
+ * @return <jk>true</jk> if the objects are equal or both <jk>null</jk>.
+ */
+ public static boolean equals(Object o1, Object o2) {
+ if (o1 == null && o2 == null)
+ return true;
+ if (o1 == null || o2 == null)
+ return false;
+ return o1.equals(o2);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java b/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java
new file mode 100644
index 0000000..93cda29
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/Pair.java
@@ -0,0 +1,64 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.internal;
+
+/**
+ * Represents a simple object pair.
+ * @param <F> The first object type.
+ * @param <S> The second object type.
+ */
+public class Pair<F,S> {
+ private final F first;
+ private final S second;
+
+ /**
+ * Constructor.
+ *
+ * @param first The first object in the pair.
+ * @param second The second object in the pair.
+ */
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Returns the first object in the pair.
+ * @return The first object in the pair.
+ */
+ public F first() {
+ return first;
+ }
+
+ /**
+ * Returns the second object in the pair.
+ * @return The second object in the pair.
+ */
+ public S second() {
+ return second;
+ }
+
+ @Override /* Object */
+ public boolean equals(Object o) {
+ if (o instanceof Pair) {
+ Pair<?,?> p = (Pair<?,?>)o;
+ return ObjectUtils.equals(first, p.first) && ObjectUtils.equals(second, p.second);
+ }
+ return false;
+ }
+
+ @Override /* Object */
+ public int hashCode() {
+ return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
index 0a0c602..acb2043 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ReflectionUtils.java
@@ -102,7 +102,7 @@ public final class ReflectionUtils {
}
/**
- * Sames as {@link #findAnnotations(Class, Class)} except returns the annotations as a map
+ * Same as {@link #findAnnotations(Class, Class)} except returns the annotations as a map
* with the keys being the class on which the annotation was found.
* <p>
* Results are ordered child-to-parent.
@@ -110,7 +110,7 @@ public final class ReflectionUtils {
* @param <T> The annotation class type.
* @param a The annotation class type.
* @param c The class being searched.
- * @return The found matches, or an empty array if annotation was not found.
+ * @return The found matches, or an empty map if annotation was not found.
*/
public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMap(Class<T> a, Class<?> c) {
LinkedHashMap<Class<?>,T> m = new LinkedHashMap<Class<?>,T>();
@@ -118,6 +118,18 @@ public final class ReflectionUtils {
return m;
}
+ /**
+ * Same as {@link #findAnnotationsMap(Class, Class)} except returns results in parent-to-child order.
+ *
+ * @param <T> The annotation class type.
+ * @param a The annotation class type.
+ * @param c The class being searched.
+ * @return The found matches, or an empty map if annotation was not found.
+ */
+ public static <T extends Annotation> LinkedHashMap<Class<?>,T> findAnnotationsMapParentFirst(Class<T> a, Class<?> c) {
+ return CollectionUtils.reverse(findAnnotationsMap(a, c));
+ }
+
private static <T extends Annotation> void findAnnotationsMap(Class<T> a, Class<?> c, Map<Class<?>,T> m) {
if (c == null)
return;
@@ -163,6 +175,8 @@ public final class ReflectionUtils {
* @return An input stream on the specified resource, or <jk>null</jk> if the resource could not be found.
*/
public static InputStream getResource(Class<?> c, String name) {
+ if (name == null)
+ return null;
while (c != null) {
InputStream is = c.getResourceAsStream(name);
if (is != null)
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/json/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/package.html b/juneau-core/src/main/java/org/apache/juneau/json/package.html
index 06e6db6..f302ec9 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/json/package.html
@@ -1022,7 +1022,7 @@
The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
These variables are replaced at runtime based on the HTTP request locale.
Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
- See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+ See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
</p>
<p>
This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/uon/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/package.html b/juneau-core/src/main/java/org/apache/juneau/uon/package.html
index e6b1d10..6c2c6ca 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/package.html
@@ -1005,7 +1005,7 @@
The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
These variables are replaced at runtime based on the HTTP request locale.
Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
- See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+ See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
</p>
<p>
This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html b/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
index 4d623d1..ddb09b2 100644
--- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html
@@ -1005,7 +1005,7 @@
The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
These variables are replaced at runtime based on the HTTP request locale.
Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
- See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+ See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
</p>
<p>
This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/java/org/apache/juneau/xml/package.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/package.html b/juneau-core/src/main/java/org/apache/juneau/xml/package.html
index 4d9d595..4c0fbeb 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/package.html
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/package.html
@@ -2941,7 +2941,7 @@
The <code>$L{...}</code> variable represent localized strings pulled from the resource bundle identified by the <code>messages</code> annotation.
These variables are replaced at runtime based on the HTTP request locale.
Several built-in runtime variable types are defined, and the API can be extended to include user-defined variables.
- See {@link org.apache.juneau.rest.RestServlet#getVarResolver()} for more information.
+ See {@link org.apache.juneau.rest.RestContext#getVarResolver()} for more information.
</p>
<p>
This document won't go into all the details of the Juneau <code>RestServlet</code> class.<br>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index bab4158..9a959d0 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -633,7 +633,7 @@
<li class='c'><a class='doclink' href='org/apache/juneau/xml/XmlParserContext.html#ConfigProperties'>XmlParserContext</a> - Configurable properties on the XML parser.
</ul>
</ul>
- <li class='c'><a class='doclink' href='org/apache/juneau/server/RestServletContext.html#ConfigProperties'>RestServletContext</a> - Configurable properties on the REST servlet.
+ <li class='c'><a class='doclink' href='org/apache/juneau/server/RestContext.html#ConfigProperties'>RestContext</a> - Configurable properties on the REST servlet.
</ul>
</div>
@@ -2625,7 +2625,7 @@
</p>
<p>
The <l>title</l> and <l>description</l> keys identify the localized values
- return by the {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)} and {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} methods.
+ return by the {@link org.apache.juneau.rest.RestRequest#getServletTitle()} and {@link org.apache.juneau.rest.RestRequest#getServletDescription()} methods.
</p>
<p>
The <l>children</l> annotation defines the child resources of this router resource.
@@ -2646,7 +2646,7 @@
It should be noted that child resources do not need to be defined this way.
They could also be defined as servlets in the same way as the root resource.
The <l>children</l> annotation approach simply makes it easier to define them without having to touch the <l>web.xml</l> file again.
- Child resources can also be defined programmatically by overriding the {@link org.apache.juneau.rest.RestServlet#createChildren()} method.
+ Child resources can also be defined programmatically by using the {@link org.apache.juneau.rest.RestConfig#addChildResources(Class[])} method.
</p>
<p>
Note that these router pages can be arbitrarily nested deep.
@@ -3131,8 +3131,8 @@
<h6 class='topic'>Additional Information</h6>
<ul class='javahierarchy'>
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#createVarResolver()} - Servlet and request variables.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getSessionObjects(RestRequest)} - Var resolver session objects.
+ <li class='m'>{@link org.apache.juneau.rest.RestConfig#addVars(Class[])} - Servlet and request variables.
+ <li class='m'>{@link org.apache.juneau.rest.RestCallHandler#getSessionObjects(RestRequest)} - Var resolver session objects.
</ul>
</div>
@@ -3208,31 +3208,27 @@
{@link org.apache.juneau.rest.annotation.RestResource#pojoSwaps() @RestResopurce.pojoSwaps()} annotations are used
to set behavior properties on the resource's underlying bean context, serializers, and parsers.
You're using them here to modify the behavior of serialization for all content types.
- The annotations are functionally equivalent to overriding the {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} method, as follows:
+ The annotations are functionally equivalent to using the {@link org.apache.juneau.rest.RestConfig} class, as follows:
</p>
<h6 class='figure'>Hypothetical RequestEchoResource.createSerializers() method</h6>
<p class='bcode'>
<jd>/** Override the default rest serializers to add some transforms */</jd>
<ja>@Override</ja>
- <jk>protected</jk> SerializerGroupBuilder createSerializers(ObjectMap properties, Class[] beanFilters, Class[] pojoSwaps) {
-
- <jc>// You'll just reuse the parent serializer group</jc>
- SerializerGroupBuilder b = <jk>super</jk>.createSerializers(properties, beanFilters, pojoSwaps);
-
+ <jk>public synchronize void</jk> init(RestConfig config) throws Exception {
<jc>// Add bean filters for the HttpServletRequest, HttpSession, and ServletContext objects
// so that you don't show vendor-specific properties on subclasses.
// Add Enumeration POJO swap to be able to render the contents of Enumeration properties.
// The max depth and detect recursion options prevent any possible runaway serializations.
// This shouldn't happen, but future JEE APIs may introduce deep hierarchies or loops.</jc>
- b
- .beanFilters(HttpServletRequest.<jk>class</jk>, HttpSession.<jk>class</jk>, ServletContext.<jk>class</jk>)
- .pojoSwaps(EnumerationSwap.<jk>class</jk>)
- .maxDepth(10)
- .detectRecursions(<jk>true</jk>);
+ config
+ .addBeanFilters(HttpServletRequest.<jk>class</jk>, HttpSession.<jk>class</jk>, ServletContext.<jk>class</jk>)
+ .addPojoSwaps(EnumerationSwap.<jk>class</jk>)
+ .setProperty(<jsf>SERIALIZER_maxDepth</jsf>, 10)
+ .setProperty(<jsf>SERIALIZER_detectRecursions</jsf>, <jk>true</jk>)
.property(<jsf>HTMLDOC_links</jsf>, <js>"{...}"</js>);
- <jc>// Return the updated group</jc>
- <jk>return</jk> b;
+ <jc>// Don't forget to call this!</jc>
+ <jk>super</jk>.init(config);
}
</p>
<p>
@@ -4248,7 +4244,7 @@
http://localhost:10000/addressBook/people/3/name?method=PUT&content="'Barack Hussein Obama'"
</p>
<p>
- The ability to overload methods is enabled through the {@link org.apache.juneau.rest.RestServletContext#REST_allowMethodParam} property.
+ The ability to overload methods is enabled through the {@link org.apache.juneau.rest.RestContext#REST_allowMethodParam} property.
</p>
</div>
</div>
@@ -4590,7 +4586,7 @@
<ul class='spaced-list'>
<li>Accessing a docker registry REST API as POJOs using {@link org.apache.juneau.rest.client.RestClient}.
<li>Using the {@link org.apache.juneau.rest.labels.ResourceDescription} class to implement a top-level 'router' page.
- <li>Using the {@link org.apache.juneau.rest.RestServlet#getConfig()} method to access external configuration file values.
+ <li>Using the {@link org.apache.juneau.rest.RestContext#getConfigFile()} method to access external configuration file values.
</ul>
<p>
Pointing a browser to the resource shows the following:
@@ -4663,7 +4659,7 @@
<h6 class='topic'>Additional Information</h6>
<ul class='javahierarchy'>
<li class='c'>{@link org.apache.juneau.rest.labels.ResourceDescription}
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfig()}
+ <li class='m'>{@link org.apache.juneau.rest.RestContext#getConfigFile()}
</ul>
</div>
@@ -4975,7 +4971,7 @@
</p>
<ul class='spaced-list'>
<li>Using the {@link org.apache.juneau.dto.ResultSetList} to serialize database result sets.
- <li>Using {@link org.apache.juneau.rest.RestServlet#getConfig()} to access config properties.
+ <li>Using {@link org.apache.juneau.rest.RestContext#getConfigFile()} to access config properties.
<li>Using form entry beans.
</ul>
<p>
@@ -5449,8 +5445,8 @@
<h3 class='topic' onclick='toggle(this)'>9.2.2 - Apply transforms to a subset of serializers or parsers</h3>
<div class='topic'>
<p>
- The {@link org.apache.juneau.rest.RestServlet#createSerializers(ObjectMap,Class[],Class[])} and {@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])}
- methods are the servlet methods that get called during servlet initialization to create the serializer and
+ The {@link org.apache.juneau.rest.RestConfig#addSerializers(Class[])} and {@link org.apache.juneau.rest.RestConfig#addParsers(Class[])}
+ methods are the methods that get called during servlet initialization to create the serializer and
parser groups.
These methods can be overridden to customize individual serializers and parsers in a way that can't be done using annotations.
</p>
@@ -5460,10 +5456,9 @@
</p>
<p class='bcode'>
<ja>@Override</ja>
- <jk>protected</jk> SerializerGroup createSerializers(ObjectMap properties, Class[] beanFilters, Class[] pojoSwaps) <jk>throws</jk> Exception {
- SerializerGroup g = <jk>super</jk>.createSerializers(properties, beanFilters, pojoSwaps);
- g.getSerializer(<js>"text/html"</js>).pojoSwaps(DoubleSwap.<jk>class</jk>);
- <jk>return</jk> g;
+ <jk>public synchronized void</jk> init(RestServletConfig config) <jk>throws</jk> Exception {
+ config.addSerializer(<jk>new</jk> HtmlSerializerBuilder().pojoSwaps(DoubleSwap.<jk>class</jk>).build());
+ super.init(config);
}
</p>
</div>
@@ -5593,7 +5588,7 @@
<h5 class='toc'>What's new in each release</h5>
<ul class='toc'>
- <li><p><a class='doclink' href='#6.1.1'>6.1.1 (TBD)</a></p>
+ <li><p><a class='doclink' href='#6.2.0'>6.2.0 (TBD)</a></p>
<li><p><a class='doclink' href='#6.1.0'>6.1.0 (Feb 25, 2017)</a></p>
<li><p><a class='doclink' href='#6.0.1'>6.0.1 (Jan 3, 2017)</a></p>
<li><p><a class='doclink' href='#6.0.0'>6.0.0 (Oct 3, 2016)</a></p>
@@ -5661,11 +5656,11 @@
<!-- ======================================================================================================== -->
- <a id="6.1.1"></a>
- <h3 class='topic' onclick='toggle(this)'>6.1.1 (TBD)</h3>
+ <a id="6.2.0"></a>
+ <h3 class='topic' onclick='toggle(this)'>6.2.0 (TBD)</h3>
<div class='topic'>
<p>
- Juneau 6.1.1 is ...
+ Juneau 6.2.0 is a major update.
</p>
<h6 class='topic'>org.apache.juneau</h6>
@@ -5732,6 +5727,48 @@
<h6 class='topic'>org.apache.juneau.rest</h6>
<ul class='spaced-list'>
+ <li>{@link org.apache.juneau.rest.annotation.RestResource @RestResource} annotation can now be applied to
+ any class! You're no longer restricted to subclassing your resources from {@link org.apache.juneau.rest.RestServlet}.
+ <br>This is a major enhancement in the API. Anything you could do by subclassing from <code>RestServlet</code>
+ should have an equivalent for non-<code>RestServlet</code> classes.
+ <br>The only restriction is that the top-level resource must subclass from <code>RestServlet</code>.
+ Child resources do not.
+ <br><br>
+ The majority of code has been split up into two separate classes:
+ <ul>
+ <li>{@link org.apache.juneau.rest.RestConfig} - A modifiable configuration of a resource. Subclasses from {@link javax.servlet.ServletConfig}.
+ <li>{@link org.apache.juneau.rest.RestContext} - A read-only configuration that's the result of a snapshot of the config.
+ </ul>
+ <br><br>
+ The {@link org.apache.juneau.rest.RestServlet} class now has the following initialization method that allows you to override
+ the config settings define via annotations:
+ <ul>
+ <li>{@link org.apache.juneau.rest.RestServlet#init(RestConfig)} - A modifiable configuration of a resource.
+ </ul>
+ Non-<code>RestServlet</code> classes must have one of the following to allow it to be instantiated:
+ <ul>
+ <li>A <code><jk>public</jk> T(RestConfig)</code> constructor.
+ <li>A <code><jk>public</jk> T()</code> constructor.
+ <li>The parent resource must have a customized {@link org.apache.juneau.rest.RestResourceResolver} for instantiating it.
+ </ul>
+ <br>
+ Non-<code>RestServlet</code> classes can optionally include the following init methods to gain access to the config and context:
+ <ul>
+ <li><code><jk>public</jk> init(RestConfig)</code>
+ <li><code><jk>public</jk> init(RestContext)</code>
+ </ul>
+ <li>New annotations added to {@link org.apache.juneau.rest.annotation.RestResource @RestResource} to allow non-<code>RestServlet</code>
+ resources to do the same as subclassing directly from <code>RestServlet</code>:
+ <ul>
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#resourceResolver() resourceResolver()}
+ - Specify a {@link org.apache.juneau.rest.RestResourceResolver} class for resolving child resources.
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#callHandler() callHandler()}
+ - Specify a {@link org.apache.juneau.rest.RestCallHandler} class for handling the lifecycle of a REST call.
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#infoProvider() infoProvider()}
+ - Specify a {@link org.apache.juneau.rest.RestInfoProvider} class for customizing title/description/Swagger information on a REST resource.
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#logger() logger()}
+ - Specify a {@link org.apache.juneau.rest.RestLogger} class for handling logging.
+ </ul>
<li>{@link org.apache.juneau.rest.annotation.RestResource#stylesheet()} can now take in a comma-delimited list of stylesheet paths.
<li>{@link org.apache.juneau.rest.StreamResource} can now contain multiple sources from a variety of source types (e.g. <code><jk>byte</jk>[]</code> arrays, <code>InputStreams</code>, <code>Files</code>, etc...)
and is now immutable. It also includes a new {@link org.apache.juneau.rest.StreamResource.Builder} class.
@@ -6126,16 +6163,16 @@
<li>Eliminated <code>ResourceOptions</code> and related code.
<li>New annotations and related methods:
<ul>
- <li>{@link org.apache.juneau.rest.annotation.RestResource#title() @RestResource.title()} / {@link org.apache.juneau.rest.RestServlet#getTitle(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#description() @RestResource.description()} / {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#termsOfService() @RestResource.termsOfService()} / {@link org.apache.juneau.rest.RestServlet#getTermsOfService(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#contact() @RestResource.contact()} / {@link org.apache.juneau.rest.RestServlet#getContact(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#license() @RestResource.license()} / {@link org.apache.juneau.rest.RestServlet#getLicense(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#version() @RestResource.version()} / {@link org.apache.juneau.rest.RestServlet#getVersion(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#tags() @RestResource.tags()} / {@link org.apache.juneau.rest.RestServlet#getTags(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestResource#externalDocs() @RestResource.externalDocs()} / {@link org.apache.juneau.rest.RestServlet#getExternalDocs(RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestMethod#summary() @RestMethod.summary()} / {@link org.apache.juneau.rest.RestServlet#getMethodSummary(String,RestRequest)}
- <li>{@link org.apache.juneau.rest.annotation.RestMethod#description() @RestMethod.description()} / {@link org.apache.juneau.rest.RestServlet#getMethodDescription(String,RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#title() @RestResource.title()} / {@link org.apache.juneau.rest.RestInfoProvider#getTitle(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#description() @RestResource.description()} / {@link org.apache.juneau.rest.RestInfoProvider#getDescription(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#termsOfService() @RestResource.termsOfService()} / {@link org.apache.juneau.rest.RestInfoProvider#getTermsOfService(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#contact() @RestResource.contact()} / {@link org.apache.juneau.rest.RestInfoProvider#getContact(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#license() @RestResource.license()} / {@link org.apache.juneau.rest.RestInfoProvider#getLicense(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#version() @RestResource.version()} / {@link org.apache.juneau.rest.RestInfoProvider#getVersion(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#tags() @RestResource.tags()} / {@link org.apache.juneau.rest.RestInfoProvider#getTags(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestResource#externalDocs() @RestResource.externalDocs()} / {@link org.apache.juneau.rest.RestInfoProvider#getExternalDocs(RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestMethod#summary() @RestMethod.summary()} / {@link org.apache.juneau.rest.RestInfoProvider#getMethodSummary(String,RestRequest)}
+ <li>{@link org.apache.juneau.rest.annotation.RestMethod#description() @RestMethod.description()} / {@link org.apache.juneau.rest.RestInfoProvider#getMethodDescription(String,RestRequest)}
<li>{@link org.apache.juneau.rest.annotation.RestMethod#externalDocs() @RestMethod.externalDocs()}
<li>{@link org.apache.juneau.rest.annotation.RestMethod#tags() @RestMethod.tags()}
<li>{@link org.apache.juneau.rest.annotation.RestMethod#deprecated() @RestMethod.deprecated()}
@@ -6144,23 +6181,23 @@
</ul>
</li>
</ul>
- <li>New {@link org.apache.juneau.rest.RestServletContext#paramFormat} context property.
+ <li>New <code><del>RestServletContext.paramFormat</del></code> context property.
<li>New/updated methods on {@link org.apache.juneau.rest.RestServlet}:
<ul>
- <li>{@link org.apache.juneau.rest.RestServlet#createProperties()}
- <li>{@link org.apache.juneau.rest.RestServlet#createBeanContext(ObjectMap,Class[],Class[])}
- <li>{@link org.apache.juneau.rest.RestServlet#createBeanFilters()}
- <li>{@link org.apache.juneau.rest.RestServlet#createPojoSwaps()}
- <li>{@link org.apache.juneau.rest.RestServlet#createParsers(ObjectMap,Class[],Class[])}
- <li>{@link org.apache.juneau.rest.RestServlet#createUrlEncodingSerializer(ObjectMap,Class[],Class[])}
- <li>{@link org.apache.juneau.rest.RestServlet#createUrlEncodingParser(ObjectMap,Class[],Class[])}
- <li>{@link org.apache.juneau.rest.RestServlet#createConverters(ObjectMap)}
- <li>{@link org.apache.juneau.rest.RestServlet#createDefaultRequestHeaders(ObjectMap)}
- <li>{@link org.apache.juneau.rest.RestServlet#createDefaultResponseHeaders(ObjectMap)}
- <li>{@link org.apache.juneau.rest.RestServlet#createEncoders(ObjectMap)}
- <li>{@link org.apache.juneau.rest.RestServlet#createGuards(ObjectMap)}
- <li>{@link org.apache.juneau.rest.RestServlet#createMimetypesFileTypeMap(ObjectMap)}
- <li>{@link org.apache.juneau.rest.RestServlet#createResponseHandlers(ObjectMap)}
+ <li><code><del>RestServlet.createProperties()</del></code>
+ <li><code><del>RestServlet.createBeanContext(ObjectMap,Class[],Class[])</del></code>
+ <li><code><del>RestServlet.createBeanFilters()</del></code>
+ <li><code><del>RestServlet.createPojoSwaps()</del></code>
+ <li><code><del>RestServlet.createParsers(ObjectMap,Class[],Class[])</del></code>
+ <li><code><del>RestServlet.createUrlEncodingSerializer(ObjectMap,Class[],Class[])</del></code>
+ <li><code><del>RestServlet.createUrlEncodingParser(ObjectMap,Class[],Class[])</del></code>
+ <li><code><del>RestServlet.createConverters(ObjectMap)</del></code>
+ <li><code><del>RestServlet.createDefaultRequestHeaders(ObjectMap)</del></code>
+ <li><code><del>RestServlet.createDefaultResponseHeaders(ObjectMap)</del></code>
+ <li><code><del>RestServlet.createEncoders(ObjectMap)</del></code>
+ <li><code><del>RestServlet.createGuards(ObjectMap)</del></code>
+ <li><code><del>RestServlet.createMimetypesFileTypeMap(ObjectMap)</del></code>
+ <li><code><del>RestServlet.createResponseHandlers(ObjectMap)</del></code>
</ul>
</li>
<li>New client-version annotations:
@@ -6223,10 +6260,9 @@
<h6 class='topic'>Server</h6>
<ul class='spaced-list'>
- <li>New methods on {@link org.apache.juneau.rest.RestServlet}:
+ <li>New methods on {@link org.apache.juneau.rest.RestContext}:
<ul>
- <li>{@link org.apache.juneau.rest.RestServlet#getMessages()}
- <li>{@link org.apache.juneau.rest.RestServlet#getMessages(Locale)}
+ <li>{@link org.apache.juneau.rest.RestContext#getMessages()}
</ul>
</ul>
@@ -6569,15 +6605,15 @@
</ul>
<li>New methods in {@link org.apache.juneau.rest.RestServlet}:
<ul>
- <li>{@link org.apache.juneau.rest.RestServlet#getChildClasses()} - Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children()} annotation.
- <li>{@link org.apache.juneau.rest.RestServlet#shouldLog(HttpServletRequest,HttpServletResponse,RestException)}
- <li>{@link org.apache.juneau.rest.RestServlet#shouldLogStackTrace(HttpServletRequest,HttpServletResponse,RestException)}
- <li>{@link org.apache.juneau.rest.RestServlet#logObjects(Level,String,Object[])}
- <li>{@link org.apache.juneau.rest.RestServlet#resolveStaticFile(String)}
- <li>{@link org.apache.juneau.rest.RestServlet#createStyleSheet()}
- <li>{@link org.apache.juneau.rest.RestServlet#createFavIcon()}
- <li>{@link org.apache.juneau.rest.RestServlet#createStaticFilesMap()}
- <li>{@link org.apache.juneau.rest.RestServlet#getConfigMgr()}
+ <li><code><del>RestServlet.getChildClasses()</del></code> - Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children()} annotation.
+ <li><code><del>RestServlet.shouldLog(HttpServletRequest,HttpServletResponse,RestException)</del></code>
+ <li><code><del>RestServlet.shouldLogStackTrace(HttpServletRequest,HttpServletResponse,RestException)</del></code>
+ <li><code><del>RestServlet.logObjects(Level,String,Object[])</del></code>
+ <li><code><del>RestServlet.resolveStaticFile(String)</del></code>
+ <li><code><del>RestServlet.createStyleSheet()</del></code>
+ <li><code><del>RestServlet.createFavIcon()</del></code>
+ <li><code><del>RestServlet.createStaticFilesMap()</del></code>
+ <li><code><del>RestServlet.getConfigMgr()</del></code>
</ul>
<li>Removed {@link org.apache.juneau.jso.JsoParser}
from {@link org.apache.juneau.rest.RestServletDefault} and {@link org.apache.juneau.rest.jena.RestServletJenaDefault}.
@@ -6709,7 +6745,7 @@
<li>Changes to {@link org.apache.juneau.rest.RestServlet}:
<ul>
<li>New methods for accessing external INI config files:<br>
- {@link org.apache.juneau.rest.RestServlet#getConfig()}<br>
+ <code><del>RestServlet.getConfig()</del></code><br>
<code><del>RestServlet.createConfigFile()</del></code>
<li>New <js>"$C{...}"</js> variable that resolve to INI config file values.
<li>New <js>"$UE{...}"</js> variable that URL-encodes the value inside the variable.
@@ -6718,16 +6754,16 @@
<del><code>RestServlet.getResourceAsString(String)</code></del><br>
<del><code>RestServlet.getResource(Class,String,String)</code></del>.
Useful if you want to load predefined POJOs from JSON files in your classpath.
- <li>New {@link org.apache.juneau.rest.RestServlet#handleNotFound(int,RestRequest,RestResponse)} method for customized handling
+ <li>New <code><del>RestServlet.handleNotFound(int,RestRequest,RestResponse)</del></code> method for customized handling
of when a resource or method was not found.
</ul>
<li>{@link org.apache.juneau.rest.RestServletDefault} now automatically processes <js>"/favicon.ico"</js> requests by
- overriding the new {@link org.apache.juneau.rest.RestServlet#handleNotFound(int,RestRequest,RestResponse)} method.
+ overriding the new <code><del>RestServlet.handleNotFound(int,RestRequest,RestResponse)</del></code> method.
<li>New {@link org.apache.juneau.rest.RestRequest} methods:
<ul>
<li>{@link org.apache.juneau.rest.RestRequest#resolveVars(String)}
<li><code>RestRequest.getVarResource(String)</code>
- <li>{@link org.apache.juneau.rest.RestRequest#getConfig()}
+ <li><code><del>RestRequest.getConfig()</del></code>
</ul>
<li>New {@link org.apache.juneau.rest.RestResponse} methods:
<ul>
@@ -6816,11 +6852,11 @@
These replace the various <code>description</code> annotations added 2 days ago with a simpler design.
<li>New methods on {@link org.apache.juneau.rest.RestServlet}:
<ul>
- <li>{@link org.apache.juneau.rest.RestServlet#getMethodDescription(String,RestRequest)} so that subclasses
+ <li><code><del>RestServlet.getMethodDescription(String,RestRequest)</del></code> so that subclasses
can override the method description in the OPTIONS page.
<li><del><code>RestServlet.createRequestVarResolver(RestRequest)</code></del> so that subclasses
can override and augment the variable resolver.
- <li>{@link org.apache.juneau.rest.RestServlet#resolveChild(Class)} and {@link org.apache.juneau.rest.RestServlet#replaceChild(RestServlet)}
+ <li><code><del>RestServlet.resolveChild(Class)</del></code> and <code><del>RestServlet.replaceChild(RestServlet)</del></code>
classes that allows customized resolution of servlet instances (e.g. if services are defined in OSGi).
</ul>
<li>Reverted the <del><code>MethodDescription</code></del> back to 5.1.0.16 since it was being used by someone.
@@ -6904,7 +6940,7 @@
<li><code>$R{trimmedRequestURI}</code> - Returns value from {@link org.apache.juneau.rest.RestRequest#getTrimmedRequestURI()}
<li><code>$E{var}</code> - Environment variables.
</ul>
- <li>Added methods {@link org.apache.juneau.rest.RestServlet#getDescription(RestRequest)} and <del><code>RestServlet.getLabel(RestRequest)</code></del>.
+ <li>Added methods <code><del>RestServlet.getDescription(RestRequest)</del></code> and <del><code>RestServlet.getLabel(RestRequest)</code></del>.
<li>{@link org.apache.juneau.rest.RestServletDefault} and {@link org.apache.juneau.rest.jena.RestServletJenaDefault} now provide default HTML titles
and descriptions:
<p class='bcode'>
@@ -7456,7 +7492,7 @@
</p>
<ul class='spaced-list'>
- <li>New {@link org.apache.juneau.rest.RestServlet#getPath()} method.
+ <li>New <code><del>RestServlet.getPath()</del></code> method.
<li>New <code>SerializerContext.getJavaMethod()</code> and <code>ParserContext.getJavaMethod()</code>
to allow access to REST methods that invoked the serializers or parsers.
For example, can be used to access additional annotations on REST methods to perform special handing
@@ -7691,20 +7727,20 @@
<li>
New methods on {@link org.apache.juneau.rest.RestServlet} that allow easier customization by subclasses:
<ul>
- <li><code>RestServlet.createConfigFactory()</code></li>
- <li><code>RestServlet.createConverters()</code></li>
- <li><code>RestServlet.createDefaultRequestHeaders()</code></li>
- <li><code>RestServlet.createDefaultResponseHeaders()</code></li>
- <li><code>RestServlet.createEncoders()</code></li>
- <li><code>RestServlet.createFilters()</code></li>
- <li><code>RestServlet.createGuards()</code></li>
- <li><code>RestServlet.createMimetypesFileTypeMap()</code></li>
- <li><code>RestServlet.createParsers()</code></li>
- <li>{@link org.apache.juneau.rest.RestServlet#createProperties()}</li>
- <li>{@link org.apache.juneau.rest.RestServlet#createRequestProperties(ObjectMap,RestRequest)}</li>
- <li><code>RestServlet.createRequestVarResolver(RestRequest)</code></li>
- <li><code>RestServlet.createSerializers()</code></li>
- <li><code>RestServlet.createUrlEncodingParser()</code></li>
+ <li><code><del>RestServlet.createConfigFactory()</del></code></li>
+ <li><code><del>RestServlet.createConverters()</del></code></li>
+ <li><code><del>RestServlet.createDefaultRequestHeaders()</del></code></li>
+ <li><code><del>RestServlet.createDefaultResponseHeaders()</del></code></li>
+ <li><code><del>RestServlet.createEncoders()</del></code></li>
+ <li><code><del>RestServlet.createFilters()</del></code></li>
+ <li><code><del>RestServlet.createGuards()</del></code></li>
+ <li><code><del>RestServlet.createMimetypesFileTypeMap()</del></code></li>
+ <li><code><del>RestServlet.createParsers()</del></code></li>
+ <li><code><del>RestServlet.createProperties()</del></code></li>
+ <li><code><del>RestServlet.createRequestProperties(ObjectMap,RestRequest)</del></code></li>
+ <li><code><del>RestServlet.createRequestVarResolver(RestRequest)</del></code></li>
+ <li><code><del>RestServlet.createSerializers()</del></code></li>
+ <li><code><del>RestServlet.createUrlEncodingParser()</del></code></li>
</ul>
</li>
<li>
@@ -8537,7 +8573,7 @@
Support for <code>Accept</code> and <code>Content-Type</code> <js>"application/x-www-form-urlencoded"</js> added by default on {@link org.apache.juneau.rest.RestServletDefault}.
</li>
<li>
- New {@link org.apache.juneau.rest.RestServlet#renderError(HttpServletRequest,HttpServletResponse,RestException)} method to allow customized handling of response errors.
+ New <code><del>RestServlet.renderError(HttpServletRequest,HttpServletResponse,RestException)</del></code> method to allow customized handling of response errors.
</li>
</ul>
</div>
@@ -8557,7 +8593,7 @@
<h6 class='topic'>REST server API changes</h6>
<ul class='spaced-list'>
<li>New <code>RestServletProperties</code> class that defines all the class-level properties that can be set on the servlet.</li>
- <li>Properties can be set through {@link org.apache.juneau.rest.annotation.RestResource#properties() @RestResource.properties} annotation, or new {@link org.apache.juneau.rest.RestServlet#setProperty(String,Object)} method.</li>
+ <li>Properties can be set through {@link org.apache.juneau.rest.annotation.RestResource#properties() @RestResource.properties} annotation, or new <code><del>RestServlet.setProperty(String,Object)</del></code> method.</li>
<li>New <js>"?noTrace"</js> URL parameter to prevent stack traces from being logged (for JUnit testing of error conditions).</li>
<li>New <code>RestServletProperties.REST_useStackTraceHashes</code> property to prevent the same stack trace from being logged multiple times.</li>
<li>New <code>RestServletProperties.REST_renderResponseStackTraces</code> property for preventing stack traces in responses for security reasons.</li>
@@ -9041,6 +9077,4 @@
</div>
</div>
-</body>
-
-
+</body>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
index 6a8512c..f0b6a3d 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java
@@ -15,15 +15,13 @@ package org.apache.juneau.examples.rest;
import static java.util.logging.Level.*;
import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.*;
-import javax.servlet.*;
-
import org.apache.juneau.*;
import org.apache.juneau.microservice.*;
import org.apache.juneau.rest.*;
@@ -58,9 +56,10 @@ public class DirectoryResource extends Resource {
private static Logger logger = Logger.getLogger(DirectoryResource.class.getName());
- @Override /* Servlet */
- public void init() throws ServletException {
- ObjectMap p = getProperties();
+ @Override /* RestServlet */
+ public synchronized void init(RestConfig config) throws Exception {
+ super.init(config);
+ ObjectMap p = config.getProperties();
rootDir = new File(p.getString("rootDir"));
allowViews = p.getBoolean("allowViews", false);
allowDeletes = p.getBoolean("allowDeletes", false);
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
index eab51fa..00bc907 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DockerRegistryResource.java
@@ -16,8 +16,7 @@ import static org.apache.juneau.html.HtmlDocSerializerContext.*;
import java.util.*;
-import javax.servlet.*;
-
+import org.apache.juneau.ini.*;
import org.apache.juneau.microservice.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
@@ -38,13 +37,15 @@ public class DockerRegistryResource extends Resource {
private static final long serialVersionUID = 1L;
// Get registry URL from examples.cfg file.
- private String registryUrl = getConfig().getString("DockerRegistry/url");
+ private String registryUrl;
RestClient rc;
@Override /* Servlet */
- public void init() throws ServletException {
- super.init();
+ public synchronized void init(RestConfig servletConfig) throws Exception {
+ super.init(servletConfig);
+ ConfigFile cf = servletConfig.getConfigFile();
+ registryUrl = cf.getString("DockerRegistry/url");
rc = new RestClientBuilder().build();
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
index 68f457b..4173623 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SampleRemoteableServlet.java
@@ -13,7 +13,7 @@
package org.apache.juneau.examples.rest;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.util.*;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
index bd270d9..e32d302 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SqlQueryResource.java
@@ -43,17 +43,20 @@ import org.apache.juneau.rest.annotation.Body;
public class SqlQueryResource extends Resource {
private static final long serialVersionUID = 1L;
- private ConfigFile cf = getConfig();
-
- private String driver = cf.getString("SqlQueryResource/driver");
- private String connectionUrl = cf.getString("SqlQueryResource/connectionUrl");
- private boolean
- allowUpdates = cf.getBoolean("SqlQueryResource/allowUpdates", false),
- allowTempUpdates = cf.getBoolean("SqlQueryResource/allowTempUpdates", false),
+ private String driver, connectionUrl;
+ private boolean allowUpdates, allowTempUpdates, includeRowNums;
+
+ @Override /* RestServlet */
+ public synchronized void init(RestConfig servletConfig) throws Exception {
+ super.init(servletConfig);
+ ConfigFile cf = servletConfig.getConfigFile();
+
+ driver = cf.getString("SqlQueryResource/driver");
+ connectionUrl = cf.getString("SqlQueryResource/connectionUrl");
+ allowUpdates = cf.getBoolean("SqlQueryResource/allowUpdates", false);
+ allowTempUpdates = cf.getBoolean("SqlQueryResource/allowTempUpdates", false);
includeRowNums = cf.getBoolean("SqlQueryResource/includeRowNums", false);
-
- @Override /* Servlet */
- public void init() {
+
try {
Class.forName(driver).newInstance();
} catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
index e077abf..b8a7a50 100644
--- a/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
+++ b/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/addressbook/AddressBookResource.java
@@ -17,7 +17,7 @@ import static org.apache.juneau.examples.addressbook.AddressBook.*;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
import static org.apache.juneau.jena.RdfCommonContext.*;
import static org.apache.juneau.jena.RdfSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.util.*;
@@ -288,7 +288,7 @@ public class AddressBookResource extends ResourceJena {
)
};
- return new DataSet(items, addressBook, this.getBeanContext().createSession());
+ return new DataSet(items, addressBook, this.getContext().getBeanContext().createSession());
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
index c0b9326..6102193 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/Resource.java
@@ -16,14 +16,13 @@ import static org.apache.juneau.html.HtmlDocSerializerContext.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.svl.*;
import org.apache.juneau.svl.vars.*;
/**
* Superclass for all REST resources.
* <p>
* In additional to the functionality of the {@link RestServletDefault} group,
- * augments the {@link #createVarResolver()} method with the following additional variable types:
+ * augments the {@link RestContext#getVarResolver()} method with the following additional variable types:
* <ul class='spaced-list'>
* <li><code class='snippet'>$ARG{...}</code> - Command line arguments pulled from {@link Microservice#getArgs()}.<br>
* <h6 class='figure'>Example:</h6>
@@ -48,14 +47,12 @@ import org.apache.juneau.svl.vars.*;
)
public abstract class Resource extends RestServletDefault {
- /**
- * Adds $ARG and $MF variables to variable resolver defined on {@link RestServlet#createVarResolver()}.
- */
- @Override
- protected VarResolverBuilder createVarResolver() {
- return super.createVarResolver()
- .vars(ArgsVar.class, ManifestFileVar.class)
- .contextObject(ArgsVar.SESSION_args, Microservice.getArgs())
- .contextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+ @Override /* RestServlet */
+ public synchronized void init(RestConfig config) throws Exception {
+ config
+ .addVars(ArgsVar.class, ManifestFileVar.class)
+ .addVarContextObject(ArgsVar.SESSION_args, Microservice.getArgs())
+ .addVarContextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+ super.init(config);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
index 610575d..440bd28 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/ResourceGroup.java
@@ -16,14 +16,13 @@ import static org.apache.juneau.html.HtmlDocSerializerContext.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.svl.*;
import org.apache.juneau.svl.vars.*;
/**
* Superclass for all REST resource groups.
* <p>
* In additional to the functionality of the {@link RestServletGroupDefault} group,
- * augments the {@link #createVarResolver()} method with the following additional variable types:
+ * augments the {@link RestContext#getVarResolver()} method with the following additional variable types:
* <ul class='spaced-list'>
* <li><jk>$ARG{...}</jk> - Command line arguments.<br>
* Resolves values from {@link Microservice#getArgs()}.<br>
@@ -49,14 +48,12 @@ import org.apache.juneau.svl.vars.*;
)
public abstract class ResourceGroup extends RestServletGroupDefault {
- /**
- * Adds $ARG and $MF variables to variable resolver defined on {@link RestServlet#createVarResolver()}.
- */
- @Override
- protected VarResolverBuilder createVarResolver() {
- return super.createVarResolver()
- .vars(ArgsVar.class, ManifestFileVar.class)
- .contextObject(ArgsVar.SESSION_args, Microservice.getArgs())
- .contextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+ @Override /* RestServlet */
+ public synchronized void init(RestConfig config) throws Exception {
+ config
+ .addVars(ArgsVar.class, ManifestFileVar.class)
+ .addVarContextObject(ArgsVar.SESSION_args, Microservice.getArgs())
+ .addVarContextObject(ManifestFileVar.SESSION_manifest, Microservice.getManifest());
+ super.init(config);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
index b546032..f77d119 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/package.html
@@ -612,8 +612,9 @@
<li><l>$MF{key}, $MF{key,default}</l> - Manifest file entries.
<li><l>$ARG{key}, $ARG{key,default}</l> - Command-line arguments.
</ul>
- Additional user-defined variables can be defined by overriding the {@link org.apache.juneau.microservice.Microservice#createVarResolver()} method.
- <li class='m'>{@link org.apache.juneau.rest.RestServlet#getConfig()} - An instance method to access it from inside a REST servlet.<br>
+ Additional user-defined variables can be defined by overriding the {@link org.apache.juneau.microservice.Microservice#createVarResolver()} method
+ and using the {@link org.apache.juneau.rest.RestConfig#addVars(Class...)} method.
+ <li class='m'>{@link org.apache.juneau.rest.RestContext#getConfigFile()} - An instance method to access it from inside a REST servlet.<br>
The following variables are available in addition to the variables defined above:
<ul>
<li><l>$I{key}, $I{key,default}</l> - Servlet initialization parameters.
@@ -640,9 +641,10 @@
}
</p>
<p>
- Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.microservice.Resource#createVarResolver()} method.
+ Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.microservice.Resource#init(RestConfig)} method
+ and using the {@link org.apache.juneau.rest.RestConfig#addVars(Class...)} method.
</p>
- <li class='m'>{@link org.apache.juneau.rest.RestRequest#getConfig()} - An instance method to access it from inside a REST method.<br>
+ <li class='m'>{@link org.apache.juneau.rest.RestRequest#getConfigFile()} - An instance method to access it from inside a REST method.<br>
The following variables are available in addition to the variables defined above:
<ul>
<li><l>$L{key}, $L{key,args}</l> - Localized variables pulled from {@link org.apache.juneau.rest.RestRequest#getMessage(String, Object...)}.
@@ -709,7 +711,7 @@
<ck>MyHelloResource.HelloMessage</ck> = <cv>Hello {0}!</cv>
</p>
<p>
- Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.rest.RestServlet#createVarResolver()} method.
+ Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.rest.RestConfig#addVars(Class...)} method.
</p>
</ul>
<p>
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
index c7f1b08..f258ec8 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/ConfigResource.java
@@ -49,7 +49,7 @@ public class ConfigResource extends Resource {
*/
@RestMethod(name="GET", path="/", description="Show contents of config file.")
public ConfigFile getConfigContents() throws Exception {
- return getConfig();
+ return getServletConfig().getConfigFile();
}
/**
@@ -68,7 +68,7 @@ public class ConfigResource extends Resource {
tr(th().child("Contents")),
tr(th().child(
textarea().name("contents").rows(40).cols(120).style("white-space:pre;word-wrap:normal;overflow-x:scroll;font-family:monospace;")
- .text(getConfig().toString()))
+ .text(getConfigContents().toString()))
)
)
)
@@ -143,7 +143,7 @@ public class ConfigResource extends Resource {
)
public ConfigFile setConfigContents(@Body Reader contents) throws Exception {
ConfigFile cf2 = ConfigMgr.DEFAULT.create().load(contents);
- return getConfig().merge(cf2).save();
+ return getConfigContents().merge(cf2).save();
}
/**
@@ -162,7 +162,7 @@ public class ConfigResource extends Resource {
}
)
public ObjectMap setConfigSection(@Path("section") String section, @Body Map<String,String> contents) throws Exception {
- getConfig().setSection(section, contents);
+ getConfigContents().setSection(section, contents);
return getSection(section);
}
@@ -184,12 +184,12 @@ public class ConfigResource extends Resource {
}
)
public String setConfigSection(@Path("section") String section, @Path("key") String key, @Body String value) throws Exception {
- getConfig().put(section, key, value, false);
+ getConfigContents().put(section, key, value, false);
return getSection(section).getString(key);
}
- private ObjectMap getSection(String name) {
- ObjectMap m = getConfig().getSectionMap(name);
+ private ObjectMap getSection(String name) throws Exception {
+ ObjectMap m = getConfigContents().getSectionMap(name);
if (m == null)
throw new RestException(SC_NOT_FOUND, "Section not found.");
return m;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
index da1cae9..0922221 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java
@@ -15,7 +15,7 @@ package org.apache.juneau.microservice.resources;
import static java.util.logging.Level.*;
import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.io.*;
import java.net.*;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
index ea1bc2f..b2011a4 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/resources/LogsResource.java
@@ -14,7 +14,7 @@ package org.apache.juneau.microservice.resources;
import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.html.HtmlDocSerializerContext.*;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.io.*;
import java.net.*;
@@ -53,14 +53,8 @@ import org.apache.juneau.transforms.*;
public class LogsResource extends Resource {
private static final long serialVersionUID = 1L;
- private ConfigFile cf = getConfig();
-
- private File logDir = new File(cf.getString("Logging/logDir", "."));
- private LogEntryFormatter leFormatter = new LogEntryFormatter(
- cf.getString("Logging/format", "[{date} {level}] {msg}%n"),
- cf.getString("Logging/dateFormat", "yyyy.MM.dd hh:mm:ss"),
- cf.getBoolean("Logging/useStackTraceHashes")
- );
+ private File logDir;
+ private LogEntryFormatter leFormatter;
private final FileFilter filter = new FileFilter() {
@Override /* FileFilter */
@@ -68,6 +62,19 @@ public class LogsResource extends Resource {
return f.isDirectory() || f.getName().endsWith(".log");
}
};
+
+ @Override /* RestServlet */
+ public synchronized void init(RestConfig config) throws Exception {
+ super.init(config);
+ ConfigFile cf = config.getConfigFile();
+
+ logDir = new File(cf.getString("Logging/logDir", "."));
+ leFormatter = new LogEntryFormatter(
+ cf.getString("Logging/format", "[{date} {level}] {msg}%n"),
+ cf.getString("Logging/dateFormat", "yyyy.MM.dd hh:mm:ss"),
+ cf.getBoolean("Logging/useStackTraceHashes")
+ );
+ }
/**
* [GET /*] - Get file details or directory listing.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
----------------------------------------------------------------------
diff --git a/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java b/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
index 6ded12d..e2a6571 100644
--- a/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
+++ b/juneau-rest-jaxrs/src/main/java/org/apache/juneau/rest/jaxrs/JuneauProvider.java
@@ -65,7 +65,7 @@ public @interface JuneauProvider {
* <p>
* Any of the following property names can be specified:
* <ul>
- * <li>{@link RestServletContext}
+ * <li>{@link RestContext}
* <li>{@link BeanContext}
* <li>{@link SerializerContext}
* <li>{@link ParserContext}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
index ee3a172..9ab29c6 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/AcceptCharsetResource.java
@@ -12,7 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.test;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.io.*;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
index 5be60cb..e8f84cc 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
@@ -28,11 +28,11 @@ public class ConfigResource extends Resource {
@RestMethod(name="GET", path="/")
public ConfigFile test1(RestRequest req) {
- return req.getConfig();
+ return req.getConfigFile();
}
@RestMethod(name="GET", path="/{key}/{class}")
public Object test2(RestRequest req, @Path("key") String key, @Path("class") Class<?> c) throws Exception {
- return req.getConfig().getObject(c, key);
+ return req.getConfigFile().getObject(c, key);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
index 3da65d6..6575ec9 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ContentResource.java
@@ -12,7 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest.test;
-import static org.apache.juneau.rest.RestServletContext.*;
+import static org.apache.juneau.rest.RestContext.*;
import java.util.*;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
index 0ca0557..adad8d2 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/GroupsResource.java
@@ -29,6 +29,12 @@ import org.apache.juneau.serializer.*;
public class GroupsResource extends RestServlet {
private static final long serialVersionUID = 1L;
+ @Override /* RestServlet */
+ public synchronized void init(RestConfig config) throws Exception {
+ config.addSerializers(SSerializer.class).addParsers(PParser.class);
+ super.init(config);
+ }
+
@Produces("text/s1,text/s2")
public static class SSerializer extends WriterSerializer {
@@ -56,17 +62,6 @@ public class GroupsResource extends RestServlet {
}
}
-
- @Override /* RestServlet */
- public SerializerGroupBuilder createSerializers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- return super.createSerializers(properties, beanFilters, pojoSwaps).append(SSerializer.class);
- }
-
- @Override /* RestServlet */
- public ParserGroupBuilder createParsers(ObjectMap properties, Class<?>[] beanFilters, Class<?>[] pojoSwaps) throws Exception {
- return super.createParsers(properties, beanFilters, pojoSwaps).append(PParser.class);
- }
-
//====================================================================================================
// Serializer defined on class.
//====================================================================================================
[4/7] incubator-juneau git commit: Allow @RestResource/@RestMethod
annotations to be used on any classes.
Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
new file mode 100644
index 0000000..243afee
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -0,0 +1,1418 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+import javax.activation.*;
+import javax.servlet.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.encoders.*;
+import org.apache.juneau.ini.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.vars.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.svl.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.utils.*;
+
+/**
+ * Contains all the configuration on a REST resource and the entry points for handling REST calls.
+ * <p>
+ * See {@link PropertyStore} for more information about context properties.
+ */
+public final class RestContext extends Context {
+
+ /**
+ * <b>Configuration property:</b> Enable header URL parameters.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.allowHeaderParams"</js>
+ * <li><b>Data type:</b> <code>Boolean</code>
+ * <li><b>Default:</b> <jk>true</jk>
+ * </ul>
+ * <p>
+ * When enabled, headers such as <js>"Accept"</js> and <js>"Content-Type"</js> to be passed in as URL query parameters.
+ * For example: <js>"?Accept=text/json&Content-Type=text/json"</js>
+ * <p>
+ * Parameter names are case-insensitive.
+ * <p>
+ * Useful for debugging REST interface using only a browser.
+ * <p>
+ * Applicable to servlet class only.
+ */
+ public static final String REST_allowHeaderParams = "RestServlet.allowHeaderParams";
+
+ /**
+ * <b>Configuration property:</b> Enable <js>"method"</js> URL parameter for specific HTTP methods.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.allowMethodParam"</js>
+ * <li><b>Data type:</b> <code>String</code>
+ * <li><b>Default:</b> <js>""</js>
+ * </ul>
+ * <p>
+ * When specified, the HTTP method can be overridden by passing in a <js>"method"</js> URL parameter on a regular GET request.
+ * For example: <js>"?method=OPTIONS"</js>
+ * <p>
+ * Format is a comma-delimited list of HTTP method names that can be passed in as a method parameter.
+ * Parameter name is case-insensitive.
+ * Use "*" to represent all methods.
+ * For backwards compatibility, "true" also means "*".
+ * <p>
+ * Note that per the <a class="doclink" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html">HTTP specification</a>, special care should
+ * be taken when allowing non-safe (POST, PUT, DELETE) methods to be invoked through GET requests.
+ * <p>
+ * Applicable to servlet class only.
+ * <p>
+ * Example: <js>"HEAD,OPTIONS"</js>
+ */
+ public static final String REST_allowMethodParam = "RestServlet.allowMethodParam";
+
+ /**
+ * <b>Configuration property:</b> Enable <js>"body"</js> URL parameter.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.allowBodyParam"</js>
+ * <li><b>Data type:</b> <code>Boolean</code>
+ * <li><b>Default:</b> <jk>true</jk>
+ * </ul>
+ * <p>
+ * When enabled, the HTTP body content on PUT and POST requests can be passed in as text using the <js>"body"</js> URL parameter.
+ * For example: <js>"?body={name:'John%20Smith',age:45}"</js>
+ * <p>
+ * Parameter name is case-insensitive.
+ * <p>
+ * Useful for debugging PUT and POST methods using only a browser.
+ * <p>
+ * Applicable to servlet class only.
+ */
+ public static final String REST_allowBodyParam = "RestServlet.allowBodyParam";
+
+ /**
+ * <b>Configuration property:</b> Render stack traces.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.renderResponseStackTraces"</js>
+ * <li><b>Data type:</b> <code>Boolean</code>
+ * <li><b>Default:</b> <jk>false</jk>
+ * </ul>
+ * <p>
+ * Render stack traces in HTTP response bodies when errors occur.
+ * <p>
+ * When enabled, Java stack traces will be rendered in the output response.
+ * Useful for debugging, although allowing stack traces to be rendered may cause security concerns.
+ * <p>
+ * Applicable to servlet class only.
+ */
+ public static final String REST_renderResponseStackTraces = "RestServlet.renderResponseStackTraces";
+
+ /**
+ * <b>Configuration property:</b> Use stack trace hashes.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.useStackTraceHashes"</js>
+ * <li><b>Data type:</b> <code>Boolean</code>
+ * <li><b>Default:</b> <jk>true</jk>
+ * </ul>
+ * <p>
+ * When enabled, the number of times an exception has occurred will be determined based on stack trace hashsums,
+ * made available through the {@link RestException#getOccurrence()} method.
+ * <p>
+ * Applicable to servlet class only.
+ */
+ public static final String REST_useStackTraceHashes = "RestServlet.useStackTraceHashes";
+
+ /**
+ * <b>Configuration property:</b> Default character encoding.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.defaultCharset"</js>
+ * <li><b>Data type:</b> <code>String</code>
+ * <li><b>Default:</b> <js>"utf-8"</js>
+ * </ul>
+ * <p>
+ * The default character encoding for the request and response if not specified on the request.
+ * <p>
+ * Applicable to servlet class and methods.
+ */
+ public static final String REST_defaultCharset = "RestServlet.defaultCharset";
+
+ /**
+ * <b>Configuration property:</b> Expected format of request parameters.
+ * <p>
+ * <ul>
+ * <li><b>Name:</b> <js>"RestServlet.paramFormat"</js>
+ * <li><b>Data type:</b> <code>String</code>
+ * <li><b>Default:</b> <js>"UON"</js>
+ * </ul>
+ * <p>
+ * Possible values:
+ * <ul class='spaced-list'>
+ * <li><js>"UON"</js> - URL-Encoded Object Notation.<br>
+ * This notation allows for request parameters to contain arbitrarily complex POJOs.
+ * <li><js>"PLAIN"</js> - Plain text.<br>
+ * This treats request parameters as plain text.<br>
+ * Only POJOs directly convertable from <l>Strings</l> can be represented in parameters when using this mode.
+ * </ul>
+ * <p>
+ * Note that the parameter value <js>"(foo)"</js> is interpreted as <js>"(foo)"</js> when using plain mode, but
+ * <js>"foo"</js> when using UON mode.
+ * <p>
+ * The format can also be specified per-parameter using the {@link FormData#format() @FormData.format()} and {@link Query#format() @Query.format()}
+ * annotations.
+ * <p>
+ * Applicable to servlet class and methods.
+ */
+ public static final String REST_paramFormat = "RestServlet.paramFormat";
+
+
+ //--------------------------------------------------------------------------------
+ // Automatically added properties.
+ //--------------------------------------------------------------------------------
+
+ /**
+ * The request servlet path.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getServletPath()}
+ */
+ public static final String REST_servletPath = "RestServlet.servletPath";
+
+ /**
+ * The request servlet URI.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getServletURI()}
+ */
+ public static final String REST_servletURI = "RestServlet.servletURI";
+
+ /**
+ * The request servlet URI.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getRelativeServletURI()}
+ */
+ public static final String REST_relativeServletURI = "RestServlet.relativeServletURI";
+
+ /**
+ * The request URI path info.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getPathInfo()}
+ */
+ public static final String REST_pathInfo = "RestServlet.pathInfo";
+
+ /**
+ * The request URI.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getRequestURI()}
+ */
+ public static final String REST_requestURI = "RestServlet.requestURI";
+
+ /**
+ * The request method.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getMethod()}
+ */
+ public static final String REST_method = "RestServlet.method";
+
+ /**
+ * The localized servlet title.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getServletTitle()}
+ */
+ public static final String REST_servletTitle = "RestServlet.servletTitle";
+
+ /**
+ * The localized servlet description.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getServletDescription()}
+ */
+ public static final String REST_servletDescription = "RestServlet.servletDescription";
+
+ /**
+ * The localized method summary.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getMethodSummary()}
+ */
+ public static final String REST_methodSummary = "RestServlet.methodSummary";
+
+ /**
+ * The localized method description.
+ * <p>
+ * Automatically added to properties returned by {@link SerializerSession#getProperties()} and {@link ParserSession#getProperties()}.
+ * <p>
+ * Equivalent to the value returned by {@link RestRequest#getMethodDescription()}
+ */
+ public static final String REST_methodDescription = "RestServlet.methodDescription";
+
+ private final Object resource;
+ private final RestConfig config;
+ private final boolean
+ allowHeaderParams,
+ allowBodyParam,
+ renderResponseStackTraces,
+ useStackTraceHashes;
+ private final String
+ defaultCharset,
+ paramFormat,
+ clientVersionHeader,
+ fullPath;
+ private final Set<String> allowMethodParams;
+
+ private final ObjectMap properties;
+ private final Class<?>[]
+ beanFilters,
+ pojoSwaps;
+ private final SerializerGroup serializers;
+ private final ParserGroup parsers;
+ private final UrlEncodingSerializer urlEncodingSerializer;
+ private final UrlEncodingParser urlEncodingParser;
+ private final EncoderGroup encoders;
+ private final MediaType[]
+ supportedContentTypes,
+ supportedAcceptTypes;
+ private final Map<String,String> defaultRequestHeaders;
+ private final Map<String,Object> defaultResponseHeaders;
+ private final BeanContext beanContext;
+ private final RestConverter[] converters;
+ private final RestGuard[] guards;
+ private final ResponseHandler[] responseHandlers;
+ private final MimetypesFileTypeMap mimetypesFileTypeMap;
+ private final StreamResource styleSheet, favIcon;
+ private final Map<String,String> staticFilesMap;
+ private final String[] staticFilesPrefixes;
+ private final MessageBundle msgs;
+ private final ConfigFile configFile;
+ private final VarResolver varResolver;
+ private final Map<String,CallRouter> callRouters;
+ private final Map<String,CallMethod> callMethods;
+ private final Map<String,RestContext> childResources;
+ private final RestLogger logger;
+ private final RestCallHandler callHandler;
+ private final RestInfoProvider infoProvider;
+ private final RestException initException;
+
+ // In-memory cache of images and stylesheets in the org.apache.juneau.rest.htdocs package.
+ private final Map<String,StreamResource> staticFilesCache = new ConcurrentHashMap<String,StreamResource>();
+ private final Map<String,byte[]> resourceStreams = new ConcurrentHashMap<String,byte[]>();
+ private final Map<String,String> resourceStrings = new ConcurrentHashMap<String,String>();
+ private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes = new ConcurrentHashMap<Integer,AtomicInteger>();
+
+
+ /**
+ * Constructor.
+ *
+ * @param resource The resource class (a class annotated with {@link RestResource @RestResource}).
+ * @param config The servlet configuration object.
+ * @throws Exception If any initialization problems were encountered.
+ */
+ @SuppressWarnings("unchecked")
+ public RestContext(Object resource, RestConfig config) throws Exception {
+ super(null);
+ try {
+ this.resource = resource;
+ this.config = config;
+
+ Builder b = new Builder(resource, config);
+ this.allowHeaderParams = b.allowHeaderParams;
+ this.allowBodyParam = b.allowBodyParam;
+ this.renderResponseStackTraces = b.renderResponseStackTraces;
+ this.useStackTraceHashes = b.useStackTraceHashes;
+ this.allowMethodParams = Collections.unmodifiableSet(b.allowMethodParams);
+ this.defaultCharset = b.defaultCharset;
+ this.paramFormat = b.paramFormat;
+ this.varResolver = b.varResolver;
+ this.configFile = b.configFile;
+ this.properties = b.properties;
+ this.beanFilters = b.beanFilters;
+ this.pojoSwaps = b.pojoSwaps;
+ this.serializers = b.serializers;
+ this.parsers = b.parsers;
+ this.urlEncodingSerializer = b.urlEncodingSerializer;
+ this.urlEncodingParser = b.urlEncodingParser;
+ this.encoders = b.encoders;
+ this.supportedContentTypes = ArrayUtils.toObjectArray(b.supportedContentTypes, MediaType.class);
+ this.supportedAcceptTypes = ArrayUtils.toObjectArray(b.supportedAcceptTypes, MediaType.class);
+ this.clientVersionHeader = b.clientVersionHeader;
+ this.defaultRequestHeaders = Collections.unmodifiableMap(b.defaultRequestHeaders);
+ this.defaultResponseHeaders = Collections.unmodifiableMap(b.defaultResponseHeaders);
+ this.beanContext = b.beanContext;
+ this.converters = b.converters.toArray(new RestConverter[b.converters.size()]);
+ this.guards = b.guards.toArray(new RestGuard[b.guards.size()]);
+ this.responseHandlers = ArrayUtils.toObjectArray(b.responseHandlers, ResponseHandler.class);
+ this.mimetypesFileTypeMap = b.mimetypesFileTypeMap;
+ this.styleSheet = b.styleSheet;
+ this.favIcon = b.favIcon;
+ this.staticFilesMap = Collections.unmodifiableMap(b.staticFilesMap);
+ this.staticFilesPrefixes = b.staticFilesPrefixes;
+ this.msgs = b.messageBundle;
+ this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>()); // Not unmodifiable on purpose so that children can be replaced.
+ this.logger = b.logger;
+ this.fullPath = b.fullPath;
+
+ //----------------------------------------------------------------------------------------------------
+ // Initialize the child resources.
+ // Done after initializing fields above since we pass this object to the child resources.
+ //----------------------------------------------------------------------------------------------------
+ List<String> methodsFound = new LinkedList<String>(); // Temporary to help debug transient duplicate method issue.
+ Map<String,CallRouter.Builder> routers = new LinkedHashMap<String,CallRouter.Builder>();
+ Map<String,CallMethod> _javaRestMethods = new LinkedHashMap<String,CallMethod>();
+ for (java.lang.reflect.Method method : resource.getClass().getMethods()) {
+ if (method.isAnnotationPresent(RestMethod.class)) {
+ RestMethod a = method.getAnnotation(RestMethod.class);
+ methodsFound.add(method.getName() + "," + a.name() + "," + a.path());
+ try {
+ if (! Modifier.isPublic(method.getModifiers()))
+ throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", this.getClass().getName(), method.getName());
+
+ CallMethod sm = new CallMethod(resource, method, this);
+ _javaRestMethods.put(method.getName(), sm);
+
+ String httpMethod = sm.getHttpMethod();
+ if (! routers.containsKey(httpMethod))
+ routers.put(httpMethod, new CallRouter.Builder(httpMethod));
+
+ routers.get(httpMethod).add(sm);
+
+ } catch (RestServletException e) {
+ throw new RestServletException("Problem occurred trying to serialize methods on class {0}, methods={1}", this.getClass().getName(), JsonSerializer.DEFAULT_LAX.serialize(methodsFound)).initCause(e);
+ }
+ }
+ }
+ this.callMethods = Collections.unmodifiableMap(_javaRestMethods);
+
+ Map<String,CallRouter> _callRouters = new LinkedHashMap<String,CallRouter>();
+ for (CallRouter.Builder crb : routers.values())
+ _callRouters.put(crb.getHttpMethodName(), crb.build());
+ this.callRouters = Collections.unmodifiableMap(_callRouters);
+
+ // Initialize our child resources.
+ RestResourceResolver rrr = resolve(RestResourceResolver.class, config.resourceResolver);
+ for (Object o : config.childResources) {
+ String path = null;
+ Object r = null;
+ if (o instanceof Pair) {
+ Pair<String,Object> p = (Pair<String,Object>)o;
+ path = p.first();
+ r = p.second();
+ } else if (o instanceof Class<?>) {
+ Class<?> c = (Class<?>)o;
+ r = c;
+ } else {
+ r = o;
+ }
+
+ RestConfig childConfig = null;
+
+ if (o instanceof Class) {
+ Class<?> oc = (Class<?>)o;
+ childConfig = new RestConfig(config.inner, oc, this);
+ r = rrr.resolve(oc, childConfig);
+ } else {
+ r = o;
+ childConfig = new RestConfig(config.inner, o.getClass(), this);
+ }
+
+ if (r instanceof RestServlet) {
+ RestServlet rs = (RestServlet)r;
+ rs.init(childConfig);
+ path = childConfig.path;
+ childResources.put(path, rs.getContext());
+ } else {
+
+ // Call the init(RestConfig) method.
+ java.lang.reflect.Method m2 = ClassUtils.findPublicMethod(r.getClass(), "init", Void.class, RestConfig.class);
+ if (m2 != null)
+ m2.invoke(r, childConfig);
+
+ RestContext rc2 = new RestContext(r, childConfig);
+
+ // Call the init(RestContext) method.
+ m2 = ClassUtils.findPublicMethod(r.getClass(), "init", Void.class, RestContext.class);
+ if (m2 != null)
+ m2.invoke(r, rc2);
+
+ path = childConfig.path;
+ childResources.put(path, rc2);
+ }
+ }
+
+ callHandler = config.callHandler == null ? new RestCallHandler(this) : resolve(RestCallHandler.class, config.callHandler, this);
+ infoProvider = config.infoProvider == null ? new RestInfoProvider(this) : resolve(RestInfoProvider.class, config.infoProvider, this);
+
+ } catch (RestException e) {
+ initException = e;
+ throw e;
+ } catch (Exception e) {
+ initException = new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ throw e;
+ }
+ initException = null;
+ }
+
+ private static class Builder {
+
+ boolean allowHeaderParams, allowBodyParam, renderResponseStackTraces, useStackTraceHashes;
+ VarResolver varResolver;
+ ConfigFile configFile;
+ ObjectMap properties;
+ Class<?>[] beanFilters;
+ Class<?>[] pojoSwaps;
+ SerializerGroup serializers;
+ ParserGroup parsers;
+ UrlEncodingSerializer urlEncodingSerializer;
+ UrlEncodingParser urlEncodingParser;
+ EncoderGroup encoders;
+ String clientVersionHeader = "", defaultCharset, paramFormat;
+ List<MediaType> supportedContentTypes, supportedAcceptTypes;
+ Map<String,String> defaultRequestHeaders = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+ Map<String,Object> defaultResponseHeaders;
+ BeanContext beanContext;
+ List<RestConverter> converters = new ArrayList<RestConverter>();
+ List<RestGuard> guards = new ArrayList<RestGuard>();
+ List<ResponseHandler> responseHandlers = new ArrayList<ResponseHandler>();
+ MimetypesFileTypeMap mimetypesFileTypeMap;
+ StreamResource styleSheet, favIcon;
+ Map<String,String> staticFilesMap;
+ String[] staticFilesPrefixes;
+ MessageBundle messageBundle;
+ Set<String> allowMethodParams = new LinkedHashSet<String>();
+ RestLogger logger;
+ String fullPath;
+
+ @SuppressWarnings("unchecked")
+ private Builder(Object resource, RestConfig sc) throws Exception {
+
+ PropertyStore ps = sc.createPropertyStore();
+
+ LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsChildFirst = ReflectionUtils.findAnnotationsMap(RestResource.class, resource.getClass());
+
+ allowHeaderParams = ps.getProperty(REST_allowHeaderParams, boolean.class, true);
+ allowBodyParam = ps.getProperty(REST_allowBodyParam, boolean.class, true);
+ renderResponseStackTraces = ps.getProperty(REST_renderResponseStackTraces, boolean.class, false);
+ useStackTraceHashes = ps.getProperty(REST_useStackTraceHashes, boolean.class, true);
+ defaultCharset = ps.getProperty(REST_defaultCharset, String.class, "utf-8");
+ paramFormat = ps.getProperty(REST_paramFormat, String.class, "");
+
+ for (String m : StringUtils.split(ps.getProperty(REST_allowMethodParam, String.class, ""), ','))
+ if (m.equals("true")) // For backwards compatibility when this was a boolean field.
+ allowMethodParams.add("*");
+ else
+ allowMethodParams.add(m.toUpperCase());
+
+ varResolver = sc.varResolverBuilder
+ .vars(LocalizationVar.class, RequestVar.class, SerializedRequestAttrVar.class, ServletInitParamVar.class, UrlEncodeVar.class)
+ .build()
+ ;
+ configFile = sc.configFile.getResolving(this.varResolver);
+ properties = sc.properties;
+ Collections.reverse(sc.beanFilters);
+ Collections.reverse(sc.pojoSwaps);
+ beanFilters = ArrayUtils.toObjectArray(sc.beanFilters, Class.class);
+ pojoSwaps = ArrayUtils.toObjectArray(sc.pojoSwaps, Class.class);
+ clientVersionHeader = sc.clientVersionHeader;
+
+ // Find resource resource bundle location.
+ for (Map.Entry<Class<?>,RestResource> e : restResourceAnnotationsChildFirst.entrySet()) {
+ Class<?> c = e.getKey();
+ RestResource r = e.getValue();
+ if (! r.messages().isEmpty()) {
+ if (messageBundle == null)
+ messageBundle = new MessageBundle(c, r.messages());
+ else
+ messageBundle.addSearchPath(c, r.messages());
+ }
+ }
+
+ if (messageBundle == null)
+ messageBundle = new MessageBundle(resource.getClass(), "");
+
+ ps.addBeanFilters(beanFilters).addPojoSwaps(pojoSwaps).setProperties(properties);
+
+ serializers = sc.serializers.beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+ parsers = sc.parsers.beanFilters(beanFilters).pojoSwaps(pojoSwaps).properties(properties).build();
+ urlEncodingSerializer = new UrlEncodingSerializer(ps);
+ urlEncodingParser = new UrlEncodingParser(ps);
+ encoders = sc.encoders.build();
+ supportedContentTypes = sc.supportedContentTypes != null ? sc.supportedContentTypes : serializers.getSupportedMediaTypes();
+ supportedAcceptTypes = sc.supportedAcceptTypes != null ? sc.supportedAcceptTypes : parsers.getSupportedMediaTypes();
+ defaultRequestHeaders.putAll(sc.defaultRequestHeaders);
+ defaultResponseHeaders = Collections.unmodifiableMap(new LinkedHashMap<String,Object>(sc.defaultResponseHeaders));
+ beanContext = ps.getBeanContext();
+
+ for (Object o : sc.converters)
+ converters.add(resolve(RestConverter.class, o));
+
+ for (Object o : sc.guards)
+ guards.add(resolve(RestGuard.class, o));
+
+ for (Object o : sc.responseHandlers)
+ responseHandlers.add(resolve(ResponseHandler.class, o));
+
+ mimetypesFileTypeMap = sc.mimeTypes;
+
+ VarResolver vr = sc.getVarResolverBuilder().build();
+
+ if (sc.styleSheets != null) {
+ List<InputStream> contents = new ArrayList<InputStream>();
+ for (Object o : sc.styleSheets) {
+ if (o instanceof Pair) {
+ Pair<Class<?>,String> p = (Pair<Class<?>,String>)o;
+ for (String path : StringUtils.split(vr.resolve(StringUtils.toString(p.second())), ','))
+ if (path.startsWith("file://"))
+ contents.add(new FileInputStream(path));
+ else
+ contents.add(ReflectionUtils.getResource(p.first(), path));
+ } else {
+ contents.add(IOUtils.toInputStream(o));
+ }
+ }
+ styleSheet = new StreamResource(MediaType.forString("text/css"), contents.toArray());
+ }
+
+ if (sc.favIcon != null) {
+ Object o = sc.favIcon;
+ InputStream is = null;
+ if (o instanceof Pair) {
+ Pair<Class<?>,String> p = (Pair<Class<?>,String>)o;
+ is = ReflectionUtils.getResource(p.first(), vr.resolve(p.second()));
+ } else {
+ is = IOUtils.toInputStream(o);
+ }
+ if (is != null)
+ favIcon = new StreamResource(MediaType.forString("image/x-icon"), is);
+ }
+
+ staticFilesMap = new LinkedHashMap<String,String>();
+ if (sc.staticFiles != null) {
+ for (Object o : sc.staticFiles) {
+ if (o instanceof Pair) {
+ Pair<Class<?>,String> p = (Pair<Class<?>,String>)o;
+ // TODO - Currently doesn't take parent class location into account.
+ staticFilesMap.putAll(JsonParser.DEFAULT.parse(vr.resolve(p.second()), LinkedHashMap.class));
+ } else {
+ throw new RuntimeException("TODO");
+ }
+ }
+ }
+ staticFilesPrefixes = staticFilesMap.keySet().toArray(new String[0]);
+
+ logger = sc.logger == null ? new RestLogger.NoOp() : resolve(RestLogger.class, sc.logger);
+
+ fullPath = (sc.parentContext == null ? "" : (sc.parentContext.fullPath + '/')) + sc.path;
+ }
+ }
+
+ /**
+ * Returns the variable resolver for this servlet.
+ * <p>
+ * Variable resolvers are used to replace variables in property values.
+ * </p>
+ * <h6 class='figure'>Example:</h6>
+ * <p class='bcode'>
+ * <ja>@RestResource</ja>(
+ * messages=<js>"nls/Messages"</js>,
+ * properties={
+ * <ja>@Property</ja>(name=<js>"title"</js>,value=<js>"$L{title}"</js>), <jc>// Localized variable in Messages.properties</jc>
+ * <ja>@Property</ja>(name=<js>"javaVendor"</js>,value=<js>"$S{java.vendor,Oracle}"</js>), <jc>// System property with default value</jc>
+ * <ja>@Property</ja>(name=<js>"foo"</js>,value=<js>"bar"</js>),
+ * <ja>@Property</ja>(name=<js>"bar"</js>,value=<js>"baz"</js>),
+ * <ja>@Property</ja>(name=<js>"v1"</js>,value=<js>"$R{foo}"</js>), <jc>// Request variable. value="bar"</jc>
+ * <ja>@Property</ja>(name=<js>"v2"</js>,value=<js>"$R{$R{foo}}"</js>) <jc>// Nested request variable. value="baz"</jc>
+ * }
+ * )
+ * <jk>public class</jk> MyRestResource <jk>extends</jk> RestServletDefault {
+ * </p>
+ * <p>
+ * A typical usage pattern is using variables for resolving URL links when rendering HTML:
+ * </p>
+ * <p class='bcode'>
+ * <ja>@RestMethod</ja>(
+ * name=<js>"GET"</js>, path=<js>"/{name}/*"</js>,
+ * properties={
+ * <ja>@Property</ja>(
+ * name=<jsf>HTMLDOC_links</jsf>,
+ * value=<js>"{up:'$R{requestParentURI}', options:'?method=OPTIONS', editLevel:'$R{servletURI}/editLevel?logger=$R{attribute.name}'}"</js>
+ * )
+ * }
+ * )
+ * <jk>public</jk> LoggerEntry getLogger(RestRequest req, <ja>@Path</ja> String name) <jk>throws</jk> Exception {
+ * </p>
+ * <p>
+ * Calls to <code>req.getProperties().getString(<js>"key"</js>)</code> returns strings with variables resolved.
+ *
+ * @return The var resolver in use by this resource.
+ */
+ public VarResolver getVarResolver() {
+ return varResolver;
+ }
+
+ /**
+ * Returns the config file associated with this servlet.
+ * <p>
+ * The config file is identified via one of the following:
+ * <ul>
+ * <li>{@link RestResource#config() @RestResource.config()} annotation.
+ * <li>{@link RestConfig#setConfigFile(ConfigFile)} method.
+ * </ul>
+ *
+ * @return The resolving config file associated with this servlet. Never <jk>null</jk>.
+ */
+ public ConfigFile getConfigFile() {
+ return configFile;
+ }
+
+ /**
+ * Resolve a static resource file.
+ * <p>
+ * The location of static resources are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#staticFiles() @RestResource.staticFiles()} annotation.
+ * <li>{@link RestConfig#addStaticFiles(Class, String)} method.
+ * </ul>
+ *
+ * @param pathInfo The unencoded path info.
+ * @return The resource, or <jk>null</jk> if the resource could not be resolved.
+ * @throws IOException
+ */
+ public StreamResource resolveStaticFile(String pathInfo) throws IOException {
+ if (! staticFilesCache.containsKey(pathInfo)) {
+ String p = RestUtils.decode(RestUtils.trimSlashes(pathInfo));
+ if (p.indexOf("..") != -1)
+ throw new RestException(SC_NOT_FOUND, "Invalid path");
+ for (Map.Entry<String,String> e : staticFilesMap.entrySet()) {
+ String key = RestUtils.trimSlashes(e.getKey());
+ if (p.startsWith(key)) {
+ String remainder = (p.equals(key) ? "" : p.substring(key.length()));
+ if (remainder.isEmpty() || remainder.startsWith("/")) {
+ String p2 = RestUtils.trimSlashes(e.getValue()) + remainder;
+ InputStream is = getResource(p2, null);
+ if (is != null) {
+ try {
+ int i = p2.lastIndexOf('/');
+ String name = (i == -1 ? p2 : p2.substring(i+1));
+ String mediaType = mimetypesFileTypeMap.getContentType(name);
+ ObjectMap headers = new ObjectMap().append("Cache-Control", "max-age=86400, public");
+ staticFilesCache.put(pathInfo, new StreamResource(MediaType.forString(mediaType), headers, is));
+ return staticFilesCache.get(pathInfo);
+ } finally {
+ is.close();
+ }
+ }
+ }
+ }
+ }
+ }
+ return staticFilesCache.get(pathInfo);
+ }
+
+ /**
+ * Same as {@link Class#getResourceAsStream(String)} except if it doesn't find the resource
+ * on this class, searches up the parent hierarchy chain.
+ * <p>
+ * If the resource cannot be found in the classpath, then an attempt is made to look in the
+ * JVM working directory.
+ * <p>
+ * If the <code>locale</code> is specified, then we look for resources whose name matches that locale.
+ * For example, if looking for the resource <js>"MyResource.txt"</js> for the Japanese locale, we will
+ * look for files in the following order:
+ * <ol>
+ * <li><js>"MyResource_ja_JP.txt"</js>
+ * <li><js>"MyResource_ja.txt"</js>
+ * <li><js>"MyResource.txt"</js>
+ * </ol>
+ *
+ * @param name The resource name.
+ * @param locale Optional locale.
+ * @return An input stream of the resource, or <jk>null</jk> if the resource could not be found.
+ * @throws IOException
+ */
+ protected InputStream getResource(String name, Locale locale) throws IOException {
+ String n = (locale == null || locale.toString().isEmpty() ? name : name + '|' + locale);
+ if (! resourceStreams.containsKey(n)) {
+ InputStream is = ReflectionUtils.getLocalizedResource(resource.getClass(), name, locale);
+ if (is == null && name.indexOf("..") == -1) {
+ for (String n2 : FileUtils.getCandidateFileNames(name, locale)) {
+ File f = new File(n2);
+ if (f.exists() && f.canRead()) {
+ is = new FileInputStream(f);
+ break;
+ }
+ }
+ }
+ if (is != null) {
+ try {
+ resourceStreams.put(n, ByteArrayCache.DEFAULT.cache(is));
+ } finally {
+ is.close();
+ }
+ }
+ }
+ byte[] b = resourceStreams.get(n);
+ return b == null ? null : new ByteArrayInputStream(b);
+ }
+
+ /**
+ * Reads the input stream from {@link #getResource(String, Locale)} into a String.
+ *
+ * @param name The resource name.
+ * @param locale Optional locale.
+ * @return The contents of the stream as a string, or <jk>null</jk> if the resource could not be found.
+ * @throws IOException If resource could not be found.
+ */
+ public String getResourceAsString(String name, Locale locale) throws IOException {
+ String n = (locale == null || locale.toString().isEmpty() ? name : name + '|' + locale);
+ if (! resourceStrings.containsKey(n)) {
+ String s = IOUtils.read(getResource(name, locale));
+ if (s == null)
+ throw new IOException("Resource '"+name+"' not found.");
+ resourceStrings.put(n, s);
+ }
+ return resourceStrings.get(n);
+ }
+
+ /**
+ * Reads the input stream from {@link #getResource(String, Locale)} and parses it into a POJO
+ * using the parser matched by the specified media type.
+ * <p>
+ * Useful if you want to load predefined POJOs from JSON files in your classpath.
+ *
+ * @param c The class type of the POJO to create.
+ * @param mediaType The media type of the data in the stream (e.g. <js>"text/json"</js>)
+ * @param name The resource name (e.g. "htdocs/styles.css").
+ * @param locale Optional locale.
+ * @return The parsed resource, or <jk>null</jk> 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> T getResource(Class<T> c, MediaType mediaType, String name, Locale locale) throws IOException, ServletException {
+ InputStream is = getResource(name, locale);
+ if (is == null)
+ return null;
+ try {
+ Parser p = parsers.getParser(mediaType);
+ if (p != null) {
+ try {
+ if (p.isReaderParser())
+ return p.parse(new InputStreamReader(is, IOUtils.UTF8), c);
+ return p.parse(is, c);
+ } catch (ParseException e) {
+ throw new ServletException("Could not parse resource '' as media type '"+mediaType+"'.");
+ }
+ }
+ 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()} annotation or {@link RestConfig#setPath(String)} method
+ * concatenated with those on all parent classes.
+ * <p>
+ * If path is not specified, returns <js>"/"</js>.
+ * <p>
+ * Path always starts with <js>"/"</js>.
+ *
+ * @return The servlet path.
+ */
+ public String getPath() {
+ return fullPath;
+ }
+
+ /**
+ * Returns the logger to use for this resource.
+ * <p>
+ * The logger for a resource is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#logger() @RestResource.logger()} annotation.
+ * <li>{@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods.
+ * </ul>
+ *
+ * @return The logger to use for this resource. Never <jk>null</jk>.
+ */
+ public RestLogger getLogger() {
+ return logger;
+ }
+
+ /**
+ * Returns the resource bundle used by this resource.
+ * <p>
+ * The resource bundle is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#messages() @RestResource.messages()} annotation.
+ * </ul>
+ *
+ * @return The resource bundle for this resource. Never <jk>null</jk>.
+ */
+ public MessageBundle getMessages() {
+ return msgs;
+ }
+
+ /**
+ * Returns the REST information provider used by this resource.
+ * <p>
+ * The information provider is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#infoProvider() @RestResource.infoProvider()} annotation.
+ * <li>{@link RestConfig#setInfoProvider(Class)}/{@link RestConfig#setInfoProvider(RestInfoProvider)} methods.
+ * </ul>
+ *
+ * @return The information provider for this resource. Never <jk>null</jk>.
+ */
+ public RestInfoProvider getInfoProvider() {
+ return infoProvider;
+ }
+
+ /**
+ * Returns the REST call handler used by this resource.
+ * <p>
+ * The call handler is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#callHandler() @RestResource.callHandler()} annotation.
+ * <li>{@link RestConfig#setCallHandler(Class)}/{@link RestConfig#setCallHandler(RestCallHandler)} methods.
+ * </ul>
+ *
+ * @return The call handler for this resource. Never <jk>null</jk>.
+ */
+ protected RestCallHandler getCallHandler() {
+ return callHandler;
+ }
+
+ /**
+ * Returns a map of HTTP method names to call routers.
+ *
+ * @return A map with HTTP method names uppercased as the keys, and call routers as the values.
+ */
+ protected Map<String,CallRouter> getCallRouters() {
+ return callRouters;
+ }
+
+ /**
+ * Returns the resource object.
+ * <p>
+ * 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 <jk>null</jk>.
+ */
+ protected Object getResource() {
+ return resource;
+ }
+
+ /**
+ * Returns the resource object as a {@link RestServlet}.
+ *
+ * @return The resource object cast to {@link RestServlet}, or
+ * <jk>null</jk> if the resource doesn't subclass from {@link RestServlet}
+ */
+ protected 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 {@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.
+ * <p>
+ * Properties at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#properties() @RestResource.properties()} annotation.
+ * <li>{@link RestConfig#setProperty(String, Object)}/{@link RestConfig#setProperties(Map)} methods.
+ * </ul>
+ * <p>
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ * <li>The returned {@code Map} is mutable. Therefore, subclasses are free to override
+ * or set additional initialization parameters in their {@code init()} method.
+ * </ul>
+ *
+ * @return The resource properties as an {@link ObjectMap}.
+ */
+ public ObjectMap getProperties() {
+ return properties;
+ }
+
+ /**
+ * Returns the serializers registered with this resource.
+ * <p>
+ * Serializers at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#serializers() @RestResource.serializers()} annotation.
+ * <li>{@link RestConfig#addSerializers(Class...)}/{@link RestConfig#addSerializers(Serializer...)} methods.
+ * </ul>
+ *
+ * @return The serializers registered with this resource.
+ */
+ public SerializerGroup getSerializers() {
+ return serializers;
+ }
+
+ /**
+ * Returns the parsers registered with this resource.
+ * <p>
+ * Parsers at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#parsers() @RestResource.parsers()} annotation.
+ * <li>{@link RestConfig#addParsers(Class...)}/{@link RestConfig#addParsers(Parser...)} methods.
+ * </ul>
+ *
+ * @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 <jk>null</jk> if not found.
+ */
+ public String getServletInitParameter(String name) {
+ return config.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<String,RestContext> getChildResources() {
+ return Collections.unmodifiableMap(childResources);
+ }
+
+ /**
+ * Returns the number of times this exception was thrown based on a hash of its stacktrace.
+ *
+ * @param e The exception to check.
+ * @return The number of times this exception was thrown, or <code>0</code> if <code>stackTraceHashes</code>
+ * setting is not enabled.
+ */
+ protected int getStackTraceOccurrence(Throwable e) {
+ if (! useStackTraceHashes)
+ return 0;
+ int h = e.hashCode();
+ stackTraceHashes.putIfAbsent(h, new AtomicInteger());
+ return stackTraceHashes.get(h).incrementAndGet();
+ }
+
+ /**
+ * Returns the value of the {@link #REST_renderResponseStackTraces} setting.
+ * @return The value of the {@link #REST_renderResponseStackTraces} setting.
+ */
+ protected boolean isRenderResponseStackTraces() {
+ return renderResponseStackTraces;
+ }
+
+ /**
+ * Returns the value of the {@link #REST_allowHeaderParams} setting.
+ * @return The value of the {@link #REST_allowHeaderParams} setting.
+ */
+ protected boolean isAllowHeaderParams() {
+ return allowHeaderParams;
+ }
+
+ /**
+ * Returns the value of the {@link #REST_allowBodyParam} setting.
+ * @return The value of the {@link #REST_allowBodyParam} setting.
+ */
+ protected boolean isAllowBodyParam() {
+ return allowBodyParam;
+ }
+
+ /**
+ * Returns the value of the {@link #REST_defaultCharset} setting.
+ * @return The value of the {@link #REST_defaultCharset} setting.
+ */
+ protected String getDefaultCharset() {
+ return defaultCharset;
+ }
+
+ /**
+ * Returns the value of the {@link #REST_paramFormat} setting.
+ * @return The value of the {@link #REST_paramFormat} setting.
+ */
+ protected String getParamFormat() {
+ return paramFormat;
+ }
+
+ /**
+ * Returns the name of the client version header name used by this resource.
+ * <p>
+ * The client version header is the name of the HTTP header on requests that identify a client version.
+ * <p>
+ * The client version header is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#clientVersionHeader() @RestResource.clientVersion()} annotation.
+ * </ul>
+ *
+ * @return The name of the client version header used by this resource. Never <jk>null</jk>.
+ */
+ protected String getClientVersionHeader() {
+ return clientVersionHeader;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified <code>Method</code> GET parameter value can be used to override
+ * the method name in the HTTP header.
+ *
+ * @param m The method name, uppercased.
+ * @return <jk>true</jk> if this resource allows the specified method to be overridden.
+ */
+ protected boolean allowMethodParam(String m) {
+ return (! StringUtils.isEmpty(m) && (allowMethodParams.contains(m) || allowMethodParams.contains("*")));
+ }
+
+ /**
+ * Returns the bean filters associated with this resource.
+ * <p>
+ * Bean filters at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#beanFilters() @RestResource.beanFilters()} annotation.
+ * <li>{@link RestConfig#addBeanFilters(Class...)} method.
+ * </ul>
+ *
+ * @return The bean filters associated with this resource. Never <jk>null</jk>.
+ */
+ protected Class<?>[] getBeanFilters() {
+ return beanFilters;
+ }
+
+ /**
+ * Returns the POJO swaps associated with this resource.
+ * <p>
+ * POJO swaps at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#pojoSwaps() @RestResource.pojoSwaps()} annotation.
+ * <li>{@link RestConfig#addPojoSwaps(Class...)} method.
+ * </ul>
+ *
+ * @return The POJO swaps associated with this resource. Never <jk>null</jk>.
+ */
+ protected Class<?>[] getPojoSwaps() {
+ return pojoSwaps;
+ }
+
+ /**
+ * Returns the URL-encoding parser associated with this resource.
+ * @return The URL-encoding parser associated with this resource. Never <jk>null</jk>.
+ */
+ protected UrlEncodingParser getUrlEncodingParser() {
+ return urlEncodingParser;
+ }
+
+ /**
+ * Returns the URL-encoding serializer associated with this resource.
+ * @return The URL-encoding serializer associated with this resource. Never <jk>null</jk>.
+ */
+ protected UrlEncodingSerializer getUrlEncodingSerializer() {
+ return urlEncodingSerializer;
+ }
+
+ /**
+ * Returns the encoders associated with this resource.
+ * <p>
+ * Encoders are used to provide various types of encoding such as <code>gzip</code> encoding.
+ * <p>
+ * Encoders at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#encoders() @RestResource.encoders()} annotation.
+ * <li>{@link RestConfig#addEncoders(Class...)}/{@link RestConfig#addEncoders(org.apache.juneau.encoders.Encoder...)} methods.
+ * </ul>
+ *
+ * @return The encoders associated with this resource. Never <jk>null</jk>.
+ */
+ protected EncoderGroup getEncoders() {
+ return encoders;
+ }
+
+ /**
+ * Returns the explicit list of supported accept types for this resource.
+ * <p>
+ * By default, this is simply the list of accept types supported by the registered parsers, but
+ * can be overridden via the {@link RestConfig#setSupportedAcceptTypes(MediaType...)}/{@link RestConfig#setSupportedAcceptTypes(String...)} methods.
+ *
+ * @return The supported <code>Accept</code> header values for this resource. Never <jk>null</jk>.
+ */
+ protected MediaType[] getSupportedAcceptTypes() {
+ return supportedAcceptTypes;
+ }
+
+ /**
+ * Returns the explicit list of supported content types for this resource.
+ * <p>
+ * By default, this is simply the list of content types supported by the registered serializers, but
+ * can be overridden via the {@link RestConfig#setSupportedContentTypes(MediaType...)}/{@link RestConfig#setSupportedContentTypes(String...)} methods.
+ *
+ * @return The supported <code>Content-Type</code> header values for this resource. Never <jk>null</jk>.
+ */
+ protected MediaType[] getSupportedContentTypes() {
+ return supportedContentTypes;
+ }
+
+ /**
+ * Returns the default request headers for this resource.
+ * <p>
+ * These are headers automatically added to requests if not present.
+ * <p>
+ * Default request headers are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#defaultRequestHeaders() @RestResource.defaultRequestHeaders()} annotation.
+ * <li>{@link RestConfig#addDefaultRequestHeader(String, Object)}/{@link RestConfig#addDefaultRequestHeaders(String...)} methods.
+ * </ul>
+ *
+ * @return The default request headers for this resource. Never <jk>null</jk>.
+ */
+ protected Map<String,String> getDefaultRequestHeaders() {
+ return defaultRequestHeaders;
+ }
+
+ /**
+ * Returns the default response headers for this resource.
+ * <p>
+ * These are headers automatically added to responses if not otherwise specified during the request.
+ * <p>
+ * Default response headers are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#defaultResponseHeaders() @RestResource.defaultResponseHeaders()} annotation.
+ * <li>{@link RestConfig#addDefaultResponseHeader(String, Object)}/{@link RestConfig#addDefaultResponseHeaders(String...)} methods.
+ * </ul>
+ *
+ * @return The default response headers for this resource. Never <jk>null</jk>.
+ */
+ public Map<String,Object> getDefaultResponseHeaders() {
+ return defaultResponseHeaders;
+ }
+
+ /**
+ * Returns the converters associated with this resource at the class level.
+ * <p>
+ * Converters are used to 'convert' POJOs from one form to another before being passed of to the response handlers.
+ * <p>
+ * Converters at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#converters() @RestResource.converters()} annotation.
+ * <li>{@link RestConfig#addConverters(Class...)}/{@link RestConfig#addConverters(RestConverter...)} methods.
+ * </ul>
+ *
+ * @return The converters associated with this resource. Never <jk>null</jk>.
+ */
+ protected RestConverter[] getConverters() {
+ return converters;
+ }
+
+ /**
+ * Returns the guards associated with this resource at the class level.
+ * <p>
+ * Guards are used to restrict access to resources.
+ * <p>
+ * Guards at the class level are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#guards() @RestResource.guards()} annotation.
+ * <li>{@link RestConfig#addGuards(Class...)}/{@link RestConfig#addGuards(RestGuard...)} methods.
+ * </ul>
+ *
+ * @return The guards associated with this resource. Never <jk>null</jk>.
+ */
+ protected RestGuard[] getGuards() {
+ return guards;
+ }
+
+ /**
+ * Returns the response handlers associated with this resource.
+ * <p>
+ * Response handlers are used to convert POJOs returned by REST Java methods into actual HTTP responses.
+ * <p>
+ * Response handlers are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#responseHandlers() @RestResource.responseHandlers()} annotation.
+ * <li>{@link RestConfig#addResponseHandlers(Class...)}/{@link RestConfig#addResponseHandlers(ResponseHandler...)} methods.
+ * </ul>
+ *
+ * @return The response handlers associated with this resource. Never <jk>null</jk>.
+ */
+ protected ResponseHandler[] getResponseHandlers() {
+ return responseHandlers;
+ }
+
+ /**
+ * Returns the media type for the specified file name.
+ * <p>
+ * The list of MIME-type mappings can be augmented through the {@link RestConfig#addMimeTypes(String...)} method.
+ * See that method for a description of predefined MIME-type mappings.
+ *
+ * @param name The file name.
+ * @return The MIME-type, or <jk>null</jk> if it could not be determined.
+ */
+ protected String getMediaTypeForName(String name) {
+ return mimetypesFileTypeMap.getContentType(name);
+ }
+
+ /**
+ * Returns the favicon of the resource.
+ * <p>
+ * This is the icon served up under <js>"/favicon.ico"</jk> recognized by browsers.
+ * <p>
+ * The favicon is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#favicon() @RestResource.favicon()} annotation.
+ * <li>{@link RestConfig#setFavIcon(Object)}/{@link RestConfig#setFavIcon(Class, String)} methods.
+ * </ul>
+ *
+ * @return The favicon of this resource. Can be <jk>null</jk>.
+ */
+ protected StreamResource getFavIcon() {
+ return favIcon;
+ }
+
+ /**
+ * Returns the stylesheet for use in the HTML views of the resource.
+ * <p>
+ * This is the contents of the page served up under <js>"/styles.css"</jk>.
+ * <p>
+ * The stylesheet is defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#stylesheet() @RestResource.stylesheet()} annotation.
+ * <li>{@link RestConfig#setStyleSheet(Object...)}/{@link RestConfig#setStyleSheet(Class, String)} methods.
+ * </ul>
+ *
+ * @return The aggregated stylesheet of this resource. Never <jk>null</jk>.
+ */
+ protected StreamResource getStyleSheet() {
+ return styleSheet;
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified path refers to a static file.
+ * <p>
+ * Static files are files pulled from the classpath and served up directly to the browser.
+ * <p>
+ * Static files are defined via one of the following:
+ * <ul>
+ * <li>{@link RestResource#staticFiles() @RestResource.staticFiles()} annotation.
+ * <li>{@link RestConfig#addStaticFiles(Class, String)} method.
+ * </ul>
+ *
+ * @param p The URL path remainder after the servlet match.
+ * @return <jk>true</jk> if the specified path refers to a static file.
+ */
+ protected boolean isStaticFile(String p) {
+ return StringUtils.pathStartsWith(p, staticFilesPrefixes);
+ }
+
+ /**
+ * Returns the REST Java methods defined in this resource.
+ * <p>
+ * These are the methods annotated with the {@link RestMethod @RestMethod} annotation.
+ *
+ * @return A map of Java method names to call method objects.
+ */
+ protected Map<String,CallMethod> getCallMethods() {
+ return callMethods;
+ }
+
+ /**
+ * Calls {@link Servlet#destroy()} on any child resources defined on this resource.
+ */
+ protected void destroy() {
+ for (RestContext r : childResources.values()) {
+ r.destroy();
+ if (r.resource instanceof Servlet)
+ ((Servlet)r.resource).destroy();
+ }
+ }
+
+ /**
+ * Returns <jk>true</jk> if this resource has any child resources associated with it.
+ * @return <jk>true</jk> if this resource has any child resources associated with it.
+ */
+ protected boolean hasChildResources() {
+ return ! childResources.isEmpty();
+ }
+
+ /**
+ * Returns the context of the child resource associated with the specified path.
+ *
+ * @param path The path of the child resource to resolve.
+ * @return The resolved context, or <jk>null</jk> if it could not be resolved.
+ */
+ protected RestContext getChildResource(String path) {
+ return childResources.get(path);
+ }
+
+
+ //----------------------------------------------------------------------------------------------------
+ // Utility methods
+ //----------------------------------------------------------------------------------------------------
+
+ /**
+ * Takes in an object of type T or a Class<T> and either casts or constructs a T.
+ */
+ @SuppressWarnings("unchecked")
+ private static <T> T resolve(Class<T> c, Object o, Object...cArgs) throws RestServletException {
+ if (c.isInstance(o))
+ return (T)o;
+ if (! (o instanceof Class))
+ throw new RestServletException("Invalid object type passed to resolve: ''{0}''. Must be an object of type T or a Class<? extend T>.", o.getClass());
+ Constructor<T> n = ClassUtils.findPublicConstructor((Class<T>)o, cArgs);
+ if (n == null)
+ throw new RestServletException("Could not find public constructor for class ''{0}'' that takes in args {1}", c, JsonSerializer.DEFAULT_LAX.toString(ClassUtils.getClasses(cArgs)));
+ try {
+ return n.newInstance(cArgs);
+ } catch (Exception e) {
+ throw new RestServletException("Exception occurred while constructing class ''{0}''", c).initCause(e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
index 77d695a..299883a 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestException.java
@@ -116,9 +116,9 @@ public class RestException extends RuntimeException {
/**
* Returns the number of times this exception occurred on this servlet.
* <p>
- * This only gets set if {@link RestServletContext#REST_useStackTraceHashes} is enabled on the servlet.
+ * This only gets set if {@link RestContext#REST_useStackTraceHashes} is enabled on the servlet.
*
- * @return The occurrence number if {@link RestServletContext#REST_useStackTraceHashes} is enabled, or <code>0</code> otherwise.
+ * @return The occurrence number if {@link RestContext#REST_useStackTraceHashes} is enabled, or <code>0</code> otherwise.
*/
public int getOccurrence() {
return occurrence;
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
new file mode 100644
index 0000000..e43f514
--- /dev/null
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestInfoProvider.java
@@ -0,0 +1,499 @@
+// ***************************************************************************************************************************
+// * 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.dto.swagger.SwaggerBuilder.*;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.svl.*;
+
+/**
+ * Class that provides documentation and other related information about a REST resource.
+ * <p>
+ * Subclasses can override these methods to tailor how HTTP REST resources are documented.
+ * Subclasses MUST implement a public constructor that takes in a {@link RestContext} object.
+ * <p>
+ * RestInfoProviders are associated with servlets/resources in one of the following ways:
+ * <ul>
+ * <li>The {@link RestResource#infoProvider @RestResource.infoProvider()} annotation.
+ * <li>The {@link RestConfig#setInfoProvider(Class)}/{@link RestConfig#setInfoProvider(RestInfoProvider)} methods.
+ * </ul>
+ */
+@SuppressWarnings("hiding")
+public class RestInfoProvider {
+
+ private final RestContext context;
+ private final String
+ title,
+ description,
+ termsOfService,
+ contact,
+ license,
+ version,
+ tags,
+ externalDocs;
+ private final ConcurrentHashMap<Locale,Swagger> swaggers = new ConcurrentHashMap<Locale,Swagger>();
+
+ /**
+ * Constructor.
+ * @param context The resource context.
+ */
+ public RestInfoProvider(RestContext context) {
+ this.context = context;
+
+ Builder b = new Builder(context);
+ this.title = b.title;
+ this.description = b.description;
+ this.termsOfService = b.termsOfService;
+ this.contact = b.contact;
+ this.license = b.license;
+ this.version = b.version;
+ this.tags = b.tags;
+ this.externalDocs = b.externalDocs;
+ }
+
+ private static class Builder {
+ private String
+ title,
+ description,
+ termsOfService,
+ contact,
+ license,
+ version,
+ tags,
+ externalDocs;
+
+ Builder(RestContext context) {
+
+ LinkedHashMap<Class<?>,RestResource> restResourceAnnotationsParentFirst = ReflectionUtils.findAnnotationsMapParentFirst(RestResource.class, context.getResource().getClass());
+
+ for (RestResource r : restResourceAnnotationsParentFirst.values()) {
+ if (! r.title().isEmpty())
+ title = r.title();
+ if (! r.description().isEmpty())
+ description = r.description();
+ if (! r.termsOfService().isEmpty())
+ termsOfService = r.termsOfService();
+ if (! r.contact().isEmpty())
+ contact = r.contact();
+ if (! r.license().isEmpty())
+ license = r.license();
+ if (! r.version().isEmpty())
+ version = r.version();
+ if (! r.tags().isEmpty())
+ tags = r.tags();
+ if (! r.externalDocs().isEmpty())
+ externalDocs = r.externalDocs();
+ }
+ }
+ }
+
+ /**
+ * Returns the localized swagger for this REST resource.
+ *
+ * @param req The incoming HTTP request.
+ * @return A new Swagger instance.
+ * @throws RestException
+ */
+ protected Swagger getSwagger(RestRequest req) throws RestException {
+ try {
+ // If a file is defined, use that.
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null)
+ return s;
+
+ s = swagger(
+ info(getTitle(req), getVersion(req))
+ .contact(getContact(req))
+ .license(getLicense(req))
+ .description(getDescription(req))
+ .termsOfService(getTermsOfService(req))
+ )
+ .consumes(context.getSupportedAcceptTypes())
+ .produces(context.getSupportedContentTypes())
+ .tags(getTags(req))
+ .externalDocs(getExternalDocs(req));
+
+ for (CallMethod sm : context.getCallMethods().values()) {
+ if (sm.isRequestAllowed(req)) {
+ Operation o = sm.getSwaggerOperation(req);
+ s.path(
+ sm.getPathPattern(),
+ sm.getHttpMethod().toLowerCase(),
+ o
+ );
+ }
+ }
+ return s;
+ } catch (RestException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns the localized Swagger from the file system.
+ * <p>
+ * Looks for a file called <js>"{ServletClass}_{locale}.json"</js> in the same package
+ * as this servlet and returns it as a parsed {@link Swagger} object.
+ * <p>
+ * Returned objects are cached for later quick-lookup.
+ *
+ * @param locale The locale of the swagger.
+ * @return The parsed swagger object, or <jk>null</jk> if the swagger file could not be found.
+ * @throws RestException
+ */
+ protected Swagger getSwaggerFromFile(Locale locale) throws RestException {
+ Swagger s = swaggers.get(locale);
+ if (s == null) {
+ try {
+ s = context.getResource(Swagger.class, MediaType.JSON, getClass().getSimpleName() + ".json", locale);
+ swaggers.putIfAbsent(locale, s == null ? Swagger.NULL : s);
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+ return s == Swagger.NULL ? null : s;
+ }
+
+ /**
+ * Returns the localized summary of the specified java method on this servlet.
+ * <p>
+ * Subclasses can override this method to provide their own summary.
+ * <p>
+ * The default implementation returns the summary from the following locations (whichever matches first):
+ * </p>
+ * <ol>
+ * <li>{@link RestMethod#summary() @RestMethod.summary()} annotation on the method.
+ * <li><ck>[ClassName].[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>[javaMethodName].summary</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * </ol>
+ *
+ * @param javaMethodName The name of the Java method whose description we're retrieving.
+ * @param req The current request.
+ * @return The localized summary of the method, or a blank string if no summary was found.
+ */
+ public String getMethodSummary(String javaMethodName, RestRequest req) {
+ CallMethod m = context.getCallMethods().get(javaMethodName);
+ if (m != null)
+ return m.getSummary(req);
+ return "";
+ }
+
+ /**
+ * Returns the localized description of the specified java method on this servlet.
+ * <p>
+ * Subclasses can override this method to provide their own description.
+ * <p>
+ * The default implementation returns the description from the following locations (whichever matches first):
+ * </p>
+ * <ol>
+ * <li>{@link RestMethod#description() @RestMethod.description()} annotation on the method.
+ * <li><ck>[ClassName].[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>[javaMethodName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * </ol>
+ *
+ * @param javaMethodName The name of the Java method whose description we're retrieving.
+ * @param req The current request.
+ * @return The localized description of the method, or a blank string if no description was found.
+ */
+ protected String getMethodDescription(String javaMethodName, RestRequest req) {
+ CallMethod m = context.getCallMethods().get(javaMethodName);
+ if (m != null)
+ return m.getDescription(req);
+ return "";
+ }
+
+ /**
+ * Returns the localized title of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own title.
+ * <p>
+ * The default implementation returns the description from the following locations (whichever matches first):
+ * <p>
+ * <ol>
+ * <li>{@link RestResource#title() @RestResourcel.title()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].title</ck> property in resource bundle identified by {@link RestResource#messages() @ResourceBundle.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>title</ck> in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/title</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
+ */
+ public String getTitle(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ if (this.title != null)
+ return vr.resolve(this.title);
+ String title = context.getMessages().findFirstString(req.getLocale(), "title");
+ if (title != null)
+ return vr.resolve(title);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getInfo() != null)
+ return s.getInfo().getTitle();
+ return null;
+ }
+
+ /**
+ * Returns the localized description of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own description.
+ * <p>
+ * The default implementation returns the description from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#description() @RestResource.description()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>description</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/description</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized description of this REST resource, or <jk>null</jk> if no resource description was found.
+ */
+ public String getDescription(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ if (this.description != null)
+ return vr.resolve(this.description);
+ String description = context.getMessages().findFirstString(req.getLocale(), "description");
+ if (description != null)
+ return vr.resolve(description);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getInfo() != null)
+ return s.getInfo().getDescription();
+ return null;
+ }
+
+ /**
+ * Returns the localized contact information of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own contact information.
+ * <p>
+ * The default implementation returns the contact information from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#contact() @RestResource.contact()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>contact</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/contact</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+ */
+ public Contact getContact(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ try {
+ if (this.contact != null)
+ return jp.parse(vr.resolve(this.contact), Contact.class);
+ String contact = context.getMessages().findFirstString(req.getLocale(), "contact");
+ if (contact != null)
+ return jp.parse(vr.resolve(contact), Contact.class);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getInfo() != null)
+ return s.getInfo().getContact();
+ return null;
+ } catch (ParseException e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns the localized license information of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own license information.
+ * <p>
+ * The default implementation returns the license information from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#license() @RestResource.license()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>license</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/license</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+ */
+ public License getLicense(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ try {
+ if (this.license != null)
+ return jp.parse(vr.resolve(this.license), License.class);
+ String license = context.getMessages().findFirstString(req.getLocale(), "license");
+ if (license != null)
+ return jp.parse(vr.resolve(license), License.class);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getInfo() != null)
+ return s.getInfo().getLicense();
+ return null;
+ } catch (ParseException e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns the terms-of-service information of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own terms-of-service information.
+ * <p>
+ * The default implementation returns the terms-of-service information from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#termsOfService() @RestResource.termsOfService()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>termsOfService</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/termsOfService</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+ */
+ public String getTermsOfService(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ if (this.termsOfService != null)
+ return vr.resolve(this.termsOfService);
+ String termsOfService = context.getMessages().findFirstString(req.getLocale(), "termsOfService");
+ if (termsOfService != null)
+ return vr.resolve(termsOfService);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getInfo() != null)
+ return s.getInfo().getTermsOfService();
+ return null;
+ }
+
+ /**
+ * Returns the version information of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own version information.
+ * <p>
+ * The default implementation returns the version information from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/version</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+ */
+ public String getVersion(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ if (this.version != null)
+ return vr.resolve(this.version);
+ String version = context.getMessages().findFirstString(req.getLocale(), "version");
+ if (version != null)
+ return vr.resolve(version);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null && s.getInfo() != null)
+ return s.getInfo().getVersion();
+ return null;
+ }
+
+ /**
+ * Returns the version information of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own version information.
+ * <p>
+ * The default implementation returns the version information from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/version</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+ */
+ public List<Tag> getTags(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ try {
+ if (this.tags != null)
+ return jp.parse(vr.resolve(this.tags), ArrayList.class, Tag.class);
+ String tags = context.getMessages().findFirstString(req.getLocale(), "tags");
+ if (tags != null)
+ return jp.parse(vr.resolve(tags), ArrayList.class, Tag.class);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null)
+ return s.getTags();
+ return null;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ /**
+ * Returns the version information of this REST resource.
+ * <p>
+ * Subclasses can override this method to provide their own version information.
+ * <p>
+ * The default implementation returns the version information from the following locations (whichever matches first):
+ * <ol>
+ * <li>{@link RestResource#version() @RestResource.version()} annotation on this class, and then any parent classes.
+ * <li><ck>[ClassName].version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>version</ck> property in resource bundle identified by {@link RestResource#messages() @RestResource.messages()}
+ * annotation for this class, then any parent classes.
+ * <li><ck>/info/version</ck> entry in swagger file.
+ * </ol>
+ *
+ * @param req The current request.
+ * @return The localized contact information of this REST resource, or <jk>null</jk> if no contact information was found.
+ */
+ public ExternalDocumentation getExternalDocs(RestRequest req) {
+ VarResolverSession vr = req.getVarResolverSession();
+ JsonParser jp = JsonParser.DEFAULT;
+ try {
+ if (this.externalDocs != null)
+ return jp.parse(vr.resolve(this.externalDocs), ExternalDocumentation.class);
+ String externalDocs = context.getMessages().findFirstString(req.getLocale(), "externalDocs");
+ if (externalDocs != null)
+ return jp.parse(vr.resolve(externalDocs), ExternalDocumentation.class);
+ Swagger s = req.getSwaggerFromFile();
+ if (s != null)
+ return s.getExternalDocs();
+ return null;
+ } catch (Exception e) {
+ throw new RestException(SC_INTERNAL_SERVER_ERROR, e);
+ }
+ }
+}