This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 59f6991 REST refactoring.
59f6991 is described below
commit 59f69919df53c78e280fb1f0e27322de08ffb7e0
Author: JamesBognar <[email protected]>
AuthorDate: Sat Jan 16 13:01:15 2021 -0500
REST refactoring.
---
.../org/apache/juneau/utils/MethodInvokerTest.java | 2 +-
.../java/org/apache/juneau/BasicException.java | 10 +
.../java/org/apache/juneau/reflect/MethodInfo.java | 13 +-
.../org/apache/juneau/utils/MethodInvoker.java | 41 ++-
.../apache/juneau/rest/mock/MockRestClient.java | 6 +-
.../java/org/apache/juneau/rest/Swagger_Test.java | 4 +-
.../apache/juneau/rest/testutils/TestUtils.java | 5 +-
.../main/java/org/apache/juneau/rest/RestCall.java | 283 ++++++++++++---------
.../java/org/apache/juneau/rest/RestContext.java | 268 +++++++++----------
.../org/apache/juneau/rest/RestMethodContext.java | 9 +-
.../java/org/apache/juneau/rest/RestServlet.java | 2 +-
11 files changed, 369 insertions(+), 274 deletions(-)
diff --git
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
index 200c232..608e808 100644
---
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
+++
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/MethodInvokerTest.java
@@ -81,7 +81,7 @@ public class MethodInvokerTest {
MethodExecStats mes = new MethodExecStats(m);
MethodInvoker mi = new MethodInvoker(m, mes);
- assertEquals(m, mi.inner());
+ assertEquals(m, mi.inner().inner());
assertEquals("A", mi.getDeclaringClass().getSimpleName());
assertEquals("foo", mi.getName());
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
index 4e0bc03..39343bc 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
@@ -66,4 +66,14 @@ public class BasicException extends Exception {
public <T extends Throwable> T getCause(Class<T> c) {
return ThrowableUtils.getCause(c, this);
}
+
+ /**
+ * Returns the caused-by exception if there is one.
+ *
+ * @return The caused-by exception if there is one, or this exception
if there isn't.
+ */
+ public Throwable unwrap() {
+ Throwable t = getCause();
+ return t == null ? this : t;
+ }
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
index a8de582..7c47ca3 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
@@ -436,8 +436,10 @@ public final class MethodInfo extends ExecutableInfo
implements Comparable<Metho
public <T> T invoke(Object obj, Object...args) throws
ExecutableException {
try {
return (T)m.invoke(obj, args);
- } catch (IllegalAccessException | InvocationTargetException e) {
+ } catch (IllegalAccessException e) {
throw new ExecutableException(e);
+ } catch (InvocationTargetException e) {
+ throw new ExecutableException(e.getTargetException());
}
}
@@ -569,6 +571,15 @@ public final class MethodInfo extends ExecutableInfo
implements Comparable<Metho
return m.isBridge();
}
+ /**
+ * Returns the name of this method.
+ *
+ * @return The name of this method
+ */
+ public String getName() {
+ return m.getName();
+ }
+
@Override
public int compareTo(MethodInfo o) {
int i = getSimpleName().compareTo(o.getSimpleName());
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
index d89206a..91006cd 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/MethodInvoker.java
@@ -13,14 +13,19 @@
package org.apache.juneau.utils;
import java.lang.reflect.*;
+import java.util.*;
+import java.util.stream.*;
+import org.apache.juneau.*;
+import org.apache.juneau.cp.*;
import org.apache.juneau.mstat.*;
+import org.apache.juneau.reflect.*;
/**
* A wrapper around a {@link Method#invoke(Object, Object...)} method that
allows for basic instrumentation.
*/
public class MethodInvoker {
- private final Method m;
+ private final MethodInfo m;
private final MethodExecStats stats;
/**
@@ -30,7 +35,7 @@ public class MethodInvoker {
* @param stats The instrumentor.
*/
public MethodInvoker(Method m, MethodExecStats stats) {
- this.m = m;
+ this.m = MethodInfo.of(m);
this.stats = stats;
}
@@ -39,7 +44,7 @@ public class MethodInvoker {
*
* @return The inner method.
*/
- public Method inner() {
+ public MethodInfo inner() {
return m;
}
@@ -49,32 +54,46 @@ public class MethodInvoker {
* @param o The object the underlying method is invoked from.
* @param args The arguments used for the method call.
* @return The result of dispatching the method represented by this
object on {@code obj} with parameters {@code args}
- * @throws IllegalAccessException Thrown from underlying method.
- * @throws IllegalArgumentException Thrown from underlying method.
- * @throws InvocationTargetException Thrown from underlying method.
+ * @throws ExecutableException If error occurred trying to invoke the
method.
*/
- public Object invoke(Object o, Object...args) throws
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+ public Object invoke(Object o, Object...args) throws
ExecutableException {
long startTime = System.nanoTime();
stats.started();
try {
- return m.invoke(o, args);
+ return m.inner().invoke(o, args);
} catch (IllegalAccessException|IllegalArgumentException e) {
stats.error(e);
- throw e;
+ throw new ExecutableException(e);
} catch (InvocationTargetException e) {
stats.error(e.getTargetException());
- throw e;
+ throw new ExecutableException(e.getTargetException());
} finally {
stats.finished(System.nanoTime() - startTime);
}
}
/**
+ * Invokes the wrapped method using parameters from the specified bean
factory.
+ *
+ * @param beanFactory The bean factory to use to resolve parameters.
+ * @param o The object to invoke the method on.
+ * @return The result of invoking the method.
+ * @throws ExecutableException If error occurred trying to invoke the
method.
+ */
+ public Object invokeUsingFactory(BeanFactory beanFactory, Object o)
throws ExecutableException {
+ List<ClassInfo> missing;
+ missing = beanFactory.getMissingParamTypes(m.getParamTypes());
+ if (missing.isEmpty())
+ return invoke(o,
beanFactory.getParams(m.getParamTypes()));
+ throw new ExecutableException("Could not find prerequisites to
invoke method ''{0}'': {1}", m.getFullName(),
missing.stream().map(x->x.getSimpleName()).collect(Collectors.joining(",")));
+ }
+
+ /**
* Convenience method for calling <c>inner().getDeclaringClass()</c>
*
* @return The declaring class of the method.
*/
- public Class<?> getDeclaringClass() {
+ public ClassInfo getDeclaringClass() {
return m.getDeclaringClass();
}
diff --git
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
index f47d9d5..1948382 100644
---
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
+++
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
@@ -48,7 +48,7 @@ import org.apache.juneau.rest.logging.*;
* The class itself extends from {@link RestClient} providing it with the
rich feature set of that API and combines
* it with the Apache HttpClient {@link HttpClientConnection} interface
for processing requests.
* The class converts {@link HttpRequest} objects to instances of {@link
MockServletRequest} and {@link MockServletResponse} which are passed directly
- * to the call handler on the resource class {@link
RestContext#execute(HttpServletRequest,HttpServletResponse)}.
+ * to the call handler on the resource class {@link
RestContext#execute(Object,HttpServletRequest,HttpServletResponse)}.
* In effect, you're fully testing your REST API as if it were running in a
live servlet container, yet not
* actually having to run in a servlet container.
* All aspects of the client and server side code are tested, yet no servlet
container is required. The actual
@@ -238,6 +238,7 @@ public class MockRestClient extends RestClient implements
HttpClientConnection {
//-------------------------------------------------------------------------------------------------------------------
private final RestContext restBeanCtx;
+ private final Object restObject;
private final String contextPath, servletPath;
private final Map<String,String> pathVars;
@@ -256,6 +257,7 @@ public class MockRestClient extends RestClient implements
HttpClientConnection {
public MockRestClient(PropertyStore ps) {
super(preInit(ps));
this.restBeanCtx =
getInstanceProperty(MOCKRESTCLIENT_restBeanCtx, RestContext.class);
+ this.restObject = restBeanCtx.getResource();
this.contextPath =
getStringProperty(MOCKRESTCLIENT_contextPath, "");
this.servletPath =
getStringProperty(MOCKRESTCLIENT_servletPath, "");
this.pathVars = getMapProperty(MOCKRESTCLIENT_pathVars,
String.class);
@@ -753,7 +755,7 @@ public class MockRestClient extends RestClient implements
HttpClientConnection {
public HttpResponse receiveResponseHeader() throws HttpException,
IOException {
try {
MockServletResponse res = MockServletResponse.create();
- restBeanCtx.execute(sreq.get(), res);
+ restBeanCtx.execute(restObject, sreq.get(), res);
// If the status isn't set, something's broken.
if (res.getStatus() == 0)
diff --git
a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
index 560b256..87691f8 100644
---
a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
+++
b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/Swagger_Test.java
@@ -44,14 +44,14 @@ public class Swagger_Test {
private Swagger getSwaggerWithFile(Object resource) throws Exception {
RestContext rc =
RestContext.create(resource).fileFinder(TestClasspathFileFinder.class).build();
- RestRequest req = rc.createRequest(new RestCall(rc, new
MockServletRequest(), null));
+ RestRequest req = rc.createRequest(new RestCall(resource, rc,
new MockServletRequest(), null));
RestInfoProvider ip = rc.getInfoProvider();
return ip.getSwagger(req);
}
private static Swagger getSwagger(Object resource) throws Exception {
RestContext rc = RestContext.create(resource).build();
- RestRequest req = rc.createRequest(new RestCall(rc, new
MockServletRequest(), null));
+ RestRequest req = rc.createRequest(new RestCall(resource, rc,
new MockServletRequest(), null));
RestInfoProvider ip = rc.getInfoProvider();
return ip.getSwagger(req);
}
diff --git
a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/testutils/TestUtils.java
b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/testutils/TestUtils.java
index 4db0efa..36a8336 100755
---
a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/testutils/TestUtils.java
+++
b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/testutils/TestUtils.java
@@ -26,8 +26,9 @@ public class TestUtils extends
org.apache.juneau.testutils.TestUtils {
*/
public static Swagger getSwagger(Class<?> c) {
try {
- RestContext rc =
RestContext.create(c.newInstance()).build();
- RestRequest req = rc.createRequest(new RestCall(rc, new
MockServletRequest(), null));
+ Object r = c.newInstance();
+ RestContext rc = RestContext.create(r).build();
+ RestRequest req = rc.createRequest(new RestCall(r, rc,
new MockServletRequest(), null));
RestInfoProvider ip = rc.getInfoProvider();
return ip.getSwagger(req);
} catch (Exception e) {
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
index 2fb2d92..79ab9c3 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCall.java
@@ -18,6 +18,7 @@ import java.util.*;
import javax.servlet.http.*;
+import org.apache.juneau.cp.*;
import org.apache.juneau.httppart.bean.*;
import org.apache.juneau.rest.logging.*;
import org.apache.juneau.rest.util.*;
@@ -32,6 +33,7 @@ public class RestCall {
*/
private static final String REST_PATHVARS_ATTR = "juneau.pathVars";
+ private Object resource;
private HttpServletRequest req;
private HttpServletResponse res;
private RestRequest rreq;
@@ -42,56 +44,73 @@ public class RestCall {
private String pathInfoUndecoded;
private long startTime = System.currentTimeMillis();
private RestLogger logger;
+ private BeanFactory beanFactory;
private UrlPathMatch urlPathMatch;
/**
* Constructor.
*
+ * @param resource The REST object.
* @param context The REST context object.
* @param req The incoming HTTP servlet request object.
* @param res The incoming HTTP servlet response object.
*/
- public RestCall(RestContext context, HttpServletRequest req,
HttpServletResponse res) {
- context(context).request(req).response(res);
+ public RestCall(Object resource, RestContext context,
HttpServletRequest req, HttpServletResponse res) {
+ resource(resource).context(context).request(req).response(res);
}
//------------------------------------------------------------------------------------------------------------------
- // Request/response objects.
+ // Fluent setters.
//------------------------------------------------------------------------------------------------------------------
/**
* Overrides the request object on the REST call.
*
- * @param req The new HTTP servlet request.
+ * @param value The new HTTP servlet request.
* @return This object (for method chaining).
*/
- public RestCall request(HttpServletRequest req) {
- this.req = req;
- this.urlPath = null;
- this.pathInfoUndecoded = null;
+ public RestCall resource(Object value) {
+ resource = value;
+ return this;
+ }
+
+ /**
+ * Overrides the request object on the REST call.
+ *
+ * @param value The new HTTP servlet request.
+ * @return This object (for method chaining).
+ */
+ public RestCall request(HttpServletRequest value) {
+ req = value;
+ urlPath = null;
+ pathInfoUndecoded = null;
+ beanFactory.addBean(HttpServletRequest.class, value);
return this;
}
/**
* Overrides the response object on the REST call.
*
- * @param res The new HTTP servlet response.
+ * @param value The new HTTP servlet response.
* @return This object (for method chaining).
*/
- public RestCall response(HttpServletResponse res) {
- this.res = res;
+ public RestCall response(HttpServletResponse value) {
+ res = value;
+ beanFactory.addBean(HttpServletResponse.class, value);
return this;
}
/**
* Overrides the context object on this call.
*
- * @param context The context that's creating this call.
+ * @param value The context that's creating this call.
* @return This object (for method chaining).
*/
- public RestCall context(RestContext context) {
- this.context = context;
+ public RestCall context(RestContext value) {
+ context = value;
+ beanFactory = new BeanFactory(value.rootBeanFactory,
value.getResource());
+ beanFactory.addBean(RestContext.class, value);
return this;
}
@@ -104,205 +123,223 @@ public class RestCall {
* @return This object (for method chaining).
*/
public RestCall restMethodContext(RestMethodContext value) {
- this.rmethod = value;
+ rmethod = value;
+ beanFactory.addBean(RestMethodContext.class, value);
return this;
}
/**
* Set the {@link RestRequest} object on this REST call.
*
- * @param rreq The {@link RestRequest} object on this REST call.
+ * @param value The {@link RestRequest} object on this REST call.
* @return This object (for method chaining).
*/
- public RestCall restRequest(RestRequest rreq) {
- request(rreq);
- this.rreq = rreq;
+ public RestCall restRequest(RestRequest value) {
+ request(value);
+ rreq = value;
+ beanFactory.addBean(RestRequest.class, value);
return this;
}
/**
* Set the {@link RestResponse} object on this REST call.
*
- * @param rres The {@link RestResponse} object on this REST call.
+ * @param value The {@link RestResponse} object on this REST call.
* @return This object (for method chaining).
*/
- public RestCall restResponse(RestResponse rres) {
- response(rres);
- this.rres = rres;
- this.rreq.setResponse(rres);
+ public RestCall restResponse(RestResponse value) {
+ response(value);
+ rres = value;
+ rreq.setResponse(value);
+ beanFactory.addBean(RestResponse.class, value);
return this;
}
/**
- * Returns the HTTP servlet request of this REST call.
+ * Sets the logger to use when logging this call.
*
- * @return the HTTP servlet request of this REST call.
+ * @param value The logger to use when logging this call.
+ * @return This object (for method chaining).
*/
- public HttpServletRequest getRequest() {
- return req;
+ public RestCall logger(RestLogger value) {
+ logger = value;
+ beanFactory.addBean(RestLogger.class, value);
+ return this;
}
+
/**
- * Returns the HTTP servlet response of this REST call.
+ * Adds resolved <c><ja>@Resource</ja>(path)</c> variable values to
this call.
*
- * @return the HTTP servlet response of this REST call.
+ * @param value The variables to add to this call.
+ * @return This object (for method chaining).
*/
- public HttpServletResponse getResponse() {
- return res;
+ @SuppressWarnings("unchecked")
+ public RestCall pathVars(Map<String,String> value) {
+ if (value != null && ! value.isEmpty()) {
+ Map<String,String> m =
(Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
+ if (m == null) {
+ m = new TreeMap<>();
+ req.setAttribute(REST_PATHVARS_ATTR, m);
+ }
+ m.putAll(value);
+ }
+ return this;
}
/**
- * Returns the REST request of this REST call.
+ * Enables or disabled debug mode on this call.
*
- * @return the REST request of this REST call.
+ * @param value The debug flag value.
+ * @return This object (for method chaining).
+ * @throws IOException Occurs if request body could not be cached into
memory.
*/
- public RestRequest getRestRequest() {
- return rreq;
+ public RestCall debug(boolean value) throws IOException {
+ if (value) {
+ req = CachingHttpServletRequest.wrap(req);
+ res = CachingHttpServletResponse.wrap(res);
+ req.setAttribute("Debug", true);
+ } else {
+ req.removeAttribute("Debug");
+ }
+ return this;
}
/**
- * Returns the REST response of this REST call.
+ * Sets the HTTP status on this call.
*
- * @return the REST response of this REST call.
+ * @param value The status code.
+ * @return This object (for method chaining).
*/
- public RestResponse getRestResponse() {
- return rres;
+ public RestCall status(int value) {
+ res.setStatus(value);
+ return this;
}
/**
- * Returns the method context of this call.
+ * Identifies that an exception occurred during this call.
*
- * @return The method context of this call.
+ * @param value The thrown exception.
+ * @return This object (for method chaining).
*/
- public RestMethodContext getRestMethodContext() {
- return rmethod;
+ public RestCall exception(Throwable value) {
+ req.setAttribute("Exception", value);
+ beanFactory.addBean(Throwable.class, value);
+ return this;
}
/**
- * Returns the Java method of this call.
+ * Sets metadata about the response.
*
- * @return The java method of this call, or <jk>null</jk> if it hasn't
been determined yet.
+ * @param value The metadata about the response.
+ * @return This object (for method chaining).
*/
- public Method getJavaMethod() {
- return rmethod == null ? null : rmethod.method;
+ public RestCall responseMeta(ResponseBeanMeta value) {
+ if (rres != null)
+ rres.setResponseMeta(value);
+ return this;
}
/**
- * Adds resolved <c><ja>@Resource</ja>(path)</c> variable values to
this call.
+ * Sets the output object to serialize as the response of this call.
*
- * @param vars The variables to add to this call.
+ * @param value The response output POJO.
+ * @return This object (for method chaining).
*/
- @SuppressWarnings("unchecked")
- public void addPathVars(Map<String,String> vars) {
- if (vars != null && ! vars.isEmpty()) {
- Map<String,String> m =
(Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
- if (m == null) {
- m = new TreeMap<>();
- req.setAttribute(REST_PATHVARS_ATTR, m);
- }
- m.putAll(vars);
- }
+ public RestCall output(Object value) {
+ if (rres != null)
+ rres.setOutput(value);
+ return this;
}
/**
- * Returns resolved <c><ja>@Resource</ja>(path)</c> variable values on
this call.
+ * Sets the URL path pattern match on this call.
*
- * @return Resolved <c><ja>@Resource</ja>(path)</c> variable values on
this call.
+ * @param value The match pattern.
+ * @return This object (for method chaining).
*/
- @SuppressWarnings("unchecked")
- public Map<String,String> getPathVars() {
- Map<String,String> m =
(Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
- return m == null ? Collections.emptyMap() : m;
+ public RestCall urlPathMatch(UrlPathMatch value) {
+ urlPathMatch = value;
+ beanFactory.addBean(UrlPathMatch.class, value);
+ return this;
}
//------------------------------------------------------------------------------------------------------------------
- // Setters.
+ // Getters.
//------------------------------------------------------------------------------------------------------------------
/**
- * Sets the logger to use when logging this call.
+ * Returns the HTTP servlet request of this REST call.
*
- * @param logger The logger to use when logging this call.
- * @return This object (for method chaining).
+ * @return the HTTP servlet request of this REST call.
*/
- public RestCall logger(RestLogger logger) {
- this.logger = logger;
- return this;
+ public HttpServletRequest getRequest() {
+ return req;
}
/**
- * Enables or disabled debug mode on this call.
+ * Returns the HTTP servlet response of this REST call.
*
- * @param b The debug flag value.
- * @return This object (for method chaining).
- * @throws IOException Occurs if request body could not be cached into
memory.
+ * @return the HTTP servlet response of this REST call.
*/
- public RestCall debug(boolean b) throws IOException {
- if (b) {
- req = CachingHttpServletRequest.wrap(req);
- res = CachingHttpServletResponse.wrap(res);
- req.setAttribute("Debug", true);
- } else {
- req.removeAttribute("Debug");
- }
- return this;
+ public HttpServletResponse getResponse() {
+ return res;
}
/**
- * Sets the HTTP status on this call.
+ * Returns the REST request of this REST call.
*
- * @param code The status code.
- * @return This object (for method chaining).
+ * @return the REST request of this REST call.
*/
- public RestCall status(int code) {
- res.setStatus(code);
- return this;
+ public RestRequest getRestRequest() {
+ return rreq;
}
/**
- * Identifies that an exception occurred during this call.
+ * Returns the REST response of this REST call.
*
- * @param e The thrown exception.
- * @return This object (for method chaining).
+ * @return the REST response of this REST call.
*/
- public RestCall exception(Throwable e) {
- req.setAttribute("Exception", e);
- return this;
+ public RestResponse getRestResponse() {
+ return rres;
}
/**
- * Sets metadata about the response.
+ * Returns the method context of this call.
*
- * @param meta The metadata about the response.
- * @return This object (for method chaining).
+ * @return The method context of this call.
*/
- public RestCall responseMeta(ResponseBeanMeta meta) {
- if (rres != null)
- rres.setResponseMeta(meta);
- return this;
+ public RestMethodContext getRestMethodContext() {
+ return rmethod;
}
/**
- * Sets the output object to serialize as the response of this call.
+ * Returns the bean factory of this call.
*
- * @param output The response output POJO.
- * @return This object (for method chaining).
+ * @return The bean factory of this call.
*/
- public RestCall output(Object output) {
- if (rres != null)
- rres.setOutput(output);
- return this;
+ public BeanFactory getBeanFactory() {
+ return beanFactory;
}
/**
- * Sets the URL path pattern match on this call.
+ * Returns the Java method of this call.
*
- * @param urlPathMatch The match pattern.
- * @return This object (for method chaining).
+ * @return The java method of this call, or <jk>null</jk> if it hasn't
been determined yet.
*/
- public RestCall urlPathMatch(UrlPathMatch urlPathMatch) {
- this.urlPathMatch = urlPathMatch;
- return this;
+ public Method getJavaMethod() {
+ return rmethod == null ? null : rmethod.method;
+ }
+
+ /**
+ * Returns resolved <c><ja>@Resource</ja>(path)</c> variable values on
this call.
+ *
+ * @return Resolved <c><ja>@Resource</ja>(path)</c> variable values on
this call.
+ */
+ @SuppressWarnings("unchecked")
+ public Map<String,String> getPathVars() {
+ Map<String,String> m =
(Map<String,String>)req.getAttribute(REST_PATHVARS_ATTR);
+ return m == null ? Collections.emptyMap() : m;
}
/**
@@ -322,6 +359,7 @@ public class RestCall {
public Throwable getException() {
return (Throwable)req.getAttribute("Exception");
}
+
//------------------------------------------------------------------------------------------------------------------
// Lifecycle methods.
//------------------------------------------------------------------------------------------------------------------
@@ -451,4 +489,13 @@ public class RestCall {
public RestContext getContext() {
return context;
}
+
+ /**
+ * Returns the REST object.
+ *
+ * @return The rest object.
+ */
+ public Object getResource() {
+ return resource;
+ }
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 765d9e7..34f9280 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -19,7 +19,6 @@ import static org.apache.juneau.internal.IOUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.rest.util.RestUtils.*;
import static org.apache.juneau.rest.HttpRuntimeException.*;
-import static org.apache.juneau.BasicIllegalArgumentException.*;
import static org.apache.juneau.Enablement.*;
import static java.util.Collections.*;
import static java.util.Arrays.*;
@@ -3223,12 +3222,6 @@ public class RestContext extends BeanContext {
private final RestMethodParam[][]
preCallMethodParams,
postCallMethodParams;
- private final Class<?>[][]
- postInitMethodParams,
- postInitChildFirstMethodParams,
- startCallMethodParams,
- endCallMethodParams,
- destroyMethodParams;
private final FileFinder fileFinder;
private final StaticFiles staticFiles;
@@ -3407,6 +3400,12 @@ public class RestContext extends BeanContext {
this.childResources = Collections.synchronizedMap(new
LinkedHashMap<String,RestContext>()); // Not unmodifiable on purpose so that
children can be replaced.
+ this.startCallMethods =
createStartCallMethods(r).stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).toArray(MethodInvoker[]::new);
+ this.endCallMethods =
createEndCallMethods(r).stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).toArray(MethodInvoker[]::new);
+ this.postInitMethods =
createPostInitMethods(r).stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).toArray(MethodInvoker[]::new);
+ this.postInitChildFirstMethods =
createPostInitChildFirstMethods(r).stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).toArray(MethodInvoker[]::new);
+ this.destroyMethods =
createDestroyMethods(r).stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).toArray(MethodInvoker[]::new);
+
//----------------------------------------------------------------------------------------------------
// Initialize the child resources.
// Done after initializing fields above since we pass
this object to the child resources.
@@ -3414,22 +3413,11 @@ public class RestContext extends BeanContext {
List<String> methodsFound = new LinkedList<>(); //
Temporary to help debug transient duplicate method issue.
MethodMapBuilder methodMapBuilder = new
MethodMapBuilder();
AMap<String,Method>
- _startCallMethods = AMap.of(),
_preCallMethods = AMap.of(),
- _postCallMethods = AMap.of(),
- _endCallMethods = AMap.of(),
- _postInitMethods = AMap.of(),
- _postInitChildFirstMethods = AMap.of(),
- _destroyMethods = AMap.of();
+ _postCallMethods = AMap.of();
AList<RestMethodParam[]>
_preCallMethodParams = AList.of(),
_postCallMethodParams = AList.of();
- AList<Class<?>[]>
- _startCallMethodParams = AList.of(),
- _endCallMethodParams = AList.of(),
- _postInitMethodParams = AList.of(),
- _postInitChildFirstMethodParams = AList.of(),
- _destroyMethodParams = AList.of();
for (MethodInfo mi : rci.getPublicMethods()) {
RestMethod a =
mi.getLastAnnotation(RestMethod.class);
@@ -3542,51 +3530,6 @@ public class RestContext extends BeanContext {
}
break;
}
- case START_CALL: {
- if (!
_startCallMethods.containsKey(sig)) {
-
m.setAccessible();
-
_startCallMethods.put(sig, m.inner());
-
_startCallMethodParams.add((Class<?>[])m.getRawParamTypes().toArray());
-
assertArgsOnlyOfType(m, HttpServletRequest.class, HttpServletResponse.class);
- }
- break;
- }
- case END_CALL: {
- if (!
_endCallMethods.containsKey(sig)) {
-
m.setAccessible();
-
_endCallMethods.put(sig, m.inner());
-
_endCallMethodParams.add((Class<?>[])m.getRawParamTypes().toArray());
-
assertArgsOnlyOfType(m, HttpServletRequest.class, HttpServletResponse.class);
- }
- break;
- }
- case POST_INIT: {
- if (!
_postInitMethods.containsKey(sig)) {
-
m.setAccessible();
-
_postInitMethods.put(sig, m.inner());
-
_postInitMethodParams.add((Class<?>[])m.getRawParamTypes().toArray());
-
assertArgsOnlyOfType(m, RestContext.class);
- }
- break;
- }
- case POST_INIT_CHILD_FIRST: {
- if (!
_postInitChildFirstMethods.containsKey(sig)) {
-
m.setAccessible();
-
_postInitChildFirstMethods.put(sig, m.inner());
-
_postInitChildFirstMethodParams.add((Class<?>[])m.getRawParamTypes().toArray());
-
assertArgsOnlyOfType(m, RestContext.class);
- }
- break;
- }
- case DESTROY: {
- if (!
_destroyMethods.containsKey(sig)) {
-
m.setAccessible();
-
_destroyMethods.put(sig, m.inner());
-
_destroyMethodParams.add((Class<?>[])m.getRawParamTypes().toArray());
-
assertArgsOnlyOfType(m, RestContext.class);
- }
- break;
- }
default: // Ignore INIT
}
}
@@ -3594,18 +3537,8 @@ public class RestContext extends BeanContext {
this.preCallMethods =
_preCallMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_preCallMethods.size()]);
this.postCallMethods =
_postCallMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_postCallMethods.size()]);
- this.startCallMethods =
_startCallMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_startCallMethods.size()]);
- this.endCallMethods =
_endCallMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_endCallMethods.size()]);
- this.postInitMethods =
_postInitMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_postInitMethods.size()]);
- this.postInitChildFirstMethods =
_postInitChildFirstMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_postInitChildFirstMethods.size()]);
- this.destroyMethods =
_destroyMethods.values().stream().map(x->new MethodInvoker(x,
getMethodExecStats(x))).collect(Collectors.toList()).toArray(new
MethodInvoker[_destroyMethods.size()]);
this.preCallMethodParams =
_preCallMethodParams.toArray(new
RestMethodParam[_preCallMethodParams.size()][]);
this.postCallMethodParams =
_postCallMethodParams.toArray(new
RestMethodParam[_postCallMethodParams.size()][]);
- this.startCallMethodParams =
_startCallMethodParams.toArray(new Class[_startCallMethodParams.size()][]);
- this.endCallMethodParams =
_endCallMethodParams.toArray(new Class[_endCallMethodParams.size()][]);
- this.postInitMethodParams =
_postInitMethodParams.toArray(new Class[_postInitMethodParams.size()][]);
- this.postInitChildFirstMethodParams =
_postInitChildFirstMethodParams.toArray(new
Class[_postInitChildFirstMethodParams.size()][]);
- this.destroyMethodParams =
_destroyMethodParams.toArray(new Class[_destroyMethodParams.size()][]);
this.methodMap = methodMapBuilder.getMap();
this.methods = methodMapBuilder.getList();
@@ -4624,6 +4557,91 @@ public class RestContext extends BeanContext {
}
/**
+ * Instantiates the list of {@link HookEvent#START_CALL} methods.
+ *
+ * @param resource The REST resource object.
+ * @return The default response headers for this REST object.
+ */
+ protected List<Method> createStartCallMethods(Object resource) {
+ Map<String,Method> x = AMap.of();
+
+ for (MethodInfo m :
ClassInfo.ofProxy(resource).getAllMethodsParentFirst())
+ for (RestHook h : m.getAnnotations(RestHook.class))
+ if (h.value() == HookEvent.START_CALL)
+ x.put(m.getSignature(),
m.accessible().inner());
+
+ return AList.of(x.values());
+ }
+
+ /**
+ * Instantiates the list of {@link HookEvent#END_CALL} methods.
+ *
+ * @param resource The REST resource object.
+ * @return The default response headers for this REST object.
+ */
+ protected List<Method> createEndCallMethods(Object resource) {
+ Map<String,Method> x = AMap.of();
+
+ for (MethodInfo m :
ClassInfo.ofProxy(resource).getAllMethodsParentFirst())
+ for (RestHook h : m.getAnnotations(RestHook.class))
+ if (h.value() == HookEvent.END_CALL)
+ x.put(m.getSignature(),
m.accessible().inner());
+
+ return AList.of(x.values());
+ }
+
+ /**
+ * Instantiates the list of {@link HookEvent#POST_INIT} methods.
+ *
+ * @param resource The REST resource object.
+ * @return The default response headers for this REST object.
+ */
+ protected List<Method> createPostInitMethods(Object resource) {
+ Map<String,Method> x = AMap.of();
+
+ for (MethodInfo m :
ClassInfo.ofProxy(resource).getAllMethodsParentFirst())
+ for (RestHook h : m.getAnnotations(RestHook.class))
+ if (h.value() == HookEvent.POST_INIT)
+ x.put(m.getSignature(),
m.accessible().inner());
+
+ return AList.of(x.values());
+ }
+
+ /**
+ * Instantiates the list of {@link HookEvent#POST_INIT_CHILD_FIRST}
methods.
+ *
+ * @param resource The REST resource object.
+ * @return The default response headers for this REST object.
+ */
+ protected List<Method> createPostInitChildFirstMethods(Object resource)
{
+ Map<String,Method> x = AMap.of();
+
+ for (MethodInfo m :
ClassInfo.ofProxy(resource).getAllMethodsParentFirst())
+ for (RestHook h : m.getAnnotations(RestHook.class))
+ if (h.value() ==
HookEvent.POST_INIT_CHILD_FIRST)
+ x.put(m.getSignature(),
m.accessible().inner());
+
+ return AList.of(x.values());
+ }
+
+ /**
+ * Instantiates the list of {@link HookEvent#DESTROY} methods.
+ *
+ * @param resource The REST resource object.
+ * @return The default response headers for this REST object.
+ */
+ protected List<Method> createDestroyMethods(Object resource) {
+ Map<String,Method> x = AMap.of();
+
+ for (MethodInfo m :
ClassInfo.ofProxy(resource).getAllMethodsParentFirst())
+ for (RestHook h : m.getAnnotations(RestHook.class))
+ if (h.value() == HookEvent.DESTROY)
+ x.put(m.getSignature(),
m.accessible().inner());
+
+ return AList.of(x.values());
+ }
+
+ /**
* Returns the bean factory associated with this context.
*
* <p>
@@ -5310,14 +5328,15 @@ public class RestContext extends BeanContext {
* Wraps an incoming servlet request/response pair into a single {@link
RestCall} object.
*
* <p>
- * This is the first method called by {@link
#execute(HttpServletRequest, HttpServletResponse)}.
+ * This is the first method called by {@link #execute(Object,
HttpServletRequest, HttpServletResponse)}.
*
+ * @param resource The REST object.
* @param req The rest request.
* @param res The rest response.
* @return The wrapped request/response pair.
*/
- protected RestCall createCall(HttpServletRequest req,
HttpServletResponse res) {
- return new RestCall(this, req, res).logger(getCallLogger());
+ protected RestCall createCall(Object resource, HttpServletRequest req,
HttpServletResponse res) {
+ return new RestCall(resource, this, req,
res).logger(getCallLogger());
}
/**
@@ -5352,14 +5371,15 @@ public class RestContext extends BeanContext {
* <p>
* Subclasses can optionally override this method if they want to
tailor the behavior of requests.
*
+ * @param resource The REST object.
* @param r1 The incoming HTTP servlet request object.
* @param r2 The incoming HTTP servlet response object.
* @throws ServletException General servlet exception.
* @throws IOException Thrown by underlying stream.
*/
- public void execute(HttpServletRequest r1, HttpServletResponse r2)
throws ServletException, IOException {
+ public void execute(Object resource, HttpServletRequest r1,
HttpServletResponse r2) throws ServletException, IOException {
- RestCall call = createCall(r1, r2);
+ RestCall call = createCall(resource, r1, r2);
// Must be careful not to bleed thread-locals.
if (this.call.get() != null)
@@ -5381,7 +5401,7 @@ public class RestContext extends BeanContext {
UrlPath upi2 = UrlPath.of(pi == null ? sp : sp
+ pi);
UrlPathMatch uppm = pathMatcher.match(upi2);
if (uppm != null && ! uppm.hasEmptyVars()) {
- call.addPathVars(uppm.getVars());
+ call.pathVars(uppm.getVars());
call.request(
new
OverrideableHttpServletRequest(call.getRequest())
.pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))
@@ -5401,11 +5421,11 @@ public class RestContext extends BeanContext {
UrlPathMatch uppm =
upp.match(call.getUrlPath());
if (uppm != null) {
if (! uppm.hasEmptyVars()) {
-
call.addPathVars(uppm.getVars());
+
call.pathVars(uppm.getVars());
HttpServletRequest
childRequest = new OverrideableHttpServletRequest(call.getRequest())
.pathInfo(nullIfEmpty(urlDecode(uppm.getSuffix())))
.servletPath(call.getServletPath() + uppm.getPrefix());
-
rc.execute(childRequest, call.getResponse());
+
rc.execute(rc.getResource(), childRequest, call.getResponse()); // TODO -
resource needs to be dynamically retrieved.
} else {
call.debug(isDebug(call)).status(SC_NOT_FOUND).finish();
}
@@ -5693,10 +5713,19 @@ public class RestContext extends BeanContext {
* Called at the start of a request to invoke all {@link
HookEvent#START_CALL} methods.
*
* @param call The current request.
+ * @throws HttpException If thrown from call methods.
*/
- protected void startCall(RestCall call) {
- for (int i = 0; i < startCallMethods.length; i++)
- startOrFinish(getResource(), startCallMethods[i],
startCallMethodParams[i], call.getRequest(), call.getResponse());
+ protected void startCall(RestCall call) throws HttpException {
+ for (MethodInvoker x : startCallMethods) {
+ try {
+ x.invokeUsingFactory(call.getBeanFactory(),
call.getContext().getResource());
+ } catch (ExecutableException e) {
+ Throwable t = e.unwrap();
+ if (! (t instanceof HttpException))
+ t = new InternalServerError(e);
+ throw (HttpException)t;
+ }
+ }
}
/**
@@ -5743,28 +5772,16 @@ public class RestContext extends BeanContext {
* Called at the end of a request to invoke all {@link
HookEvent#END_CALL} methods.
*
* <p>
- * This is the very last method called in {@link
#execute(HttpServletRequest, HttpServletResponse)}.
+ * This is the very last method called in {@link #execute(Object,
HttpServletRequest, HttpServletResponse)}.
*
* @param call The current request.
*/
protected void finishCall(RestCall call) {
- for (int i = 0; i < endCallMethods.length; i++)
- startOrFinish(getResource(), endCallMethods[i],
endCallMethodParams[i], call.getRequest(), call.getResponse());
- }
-
- private static void startOrFinish(Object resource, MethodInvoker m,
Class<?>[] p, HttpServletRequest req, HttpServletResponse res) throws
HttpException, InternalServerError {
- if (m != null) {
- Object[] args = new Object[p.length];
- for (int i = 0; i < p.length; i++) {
- if (p[i] == HttpServletRequest.class)
- args[i] = req;
- else if (p[i] == HttpServletResponse.class)
- args[i] = res;
- }
+ for (MethodInvoker x : endCallMethods) {
try {
- m.invoke(resource, args);
- } catch (Exception e) {
- throw toHttpException(e,
InternalServerError.class);
+ x.invokeUsingFactory(call.getBeanFactory(),
call.getResource());
+ } catch (ExecutableException e) {
+ logger.log(Level.WARNING, e.unwrap(),
()->format("Error occurred invoking finish-call method ''{0}''.", x.getName()));
}
}
}
@@ -5784,11 +5801,16 @@ public class RestContext extends BeanContext {
try {
mi.accessible().invoke(resource, this);
} catch (ExecutableException e) {
- throw new ServletException(e);
+ throw new ServletException(e.unwrap());
+ }
+ }
+ for (MethodInvoker x : postInitMethods) {
+ try {
+ x.invokeUsingFactory(beanFactory,
getResource());
+ } catch (ExecutableException e) {
+ throw new ServletException(e.unwrap());
}
}
- for (int i = 0; i < postInitMethods.length; i++)
- postInitOrDestroy(getResource(), postInitMethods[i],
postInitMethodParams[i]);
for (RestContext childContext : this.childResources.values())
childContext.postInit();
return this;
@@ -5805,42 +5827,26 @@ public class RestContext extends BeanContext {
return this;
for (RestContext childContext : this.childResources.values())
childContext.postInitChildFirst();
- for (int i = 0; i < postInitChildFirstMethods.length; i++)
- postInitOrDestroy(getResource(),
postInitChildFirstMethods[i], postInitChildFirstMethodParams[i]);
- initialized.set(true);
- return this;
- }
-
- private void postInitOrDestroy(Object r, MethodInvoker m, Class<?>[] p)
{
- if (m != null) {
- Object[] args = new Object[p.length];
- for (int i = 0; i < p.length; i++) {
- if (p[i] == RestContext.class)
- args[i] = this;
- else if (p[i] == RestContextBuilder.class)
- args[i] = this.builder;
- else if (p[i] == ServletConfig.class)
- args[i] = this.builder.inner;
- }
+ for (MethodInvoker x : postInitChildFirstMethods) {
try {
- m.invoke(r, args);
- } catch (Exception e) {
- if (e instanceof RuntimeException &&
ClassInfo.of(e).hasAnnotation(Response.class))
- throw (RuntimeException)e;
- throw new InternalServerError(e);
+ x.invokeUsingFactory(beanFactory,
getResource());
+ } catch (ExecutableException e) {
+ throw new ServletException(e.unwrap());
}
}
+ initialized.set(true);
+ return this;
}
/**
* Called during servlet initialization to invoke all {@link
HookEvent#DESTROY} methods.
*/
protected void destroy() {
- for (int i = 0; i < destroyMethods.length; i++) {
+ for (MethodInvoker x : destroyMethods) {
try {
- postInitOrDestroy(getResource(),
destroyMethods[i], destroyMethodParams[i]);
- } catch (Exception e) {
- e.printStackTrace();
+ x.invokeUsingFactory(beanFactory,
getResource());
+ } catch (ExecutableException e) {
+ getLogger().log(Level.WARNING, e.unwrap(),
()->format("Error occurred invoking servlet-destroy method ''{0}''.",
x.getName()));
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
index f40fa4f..331630c 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
@@ -23,7 +23,6 @@ import static org.apache.juneau.rest.util.RestUtils.*;
import static org.apache.juneau.rest.HttpRuntimeException.*;
import java.lang.annotation.*;
-import java.lang.reflect.*;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.*;
@@ -1712,8 +1711,8 @@ public class RestMethodContext extends BeanContext
implements Comparable<RestMet
if (output != null || !
res.getOutputStreamCalled())
res.setOutput(output);
}
- } catch (InvocationTargetException e) {
- Throwable e2 = e.getTargetException();
// Get the throwable thrown from the doX() method.
+ } catch (ExecutableException e) {
+ Throwable e2 = e.unwrap(); // Get
the throwable thrown from the doX() method.
res.setStatus(500);
ResponsePartMeta rpm = getResponseBodyMeta(e2);
ResponseBeanMeta rbm = getResponseBeanMeta(e2);
@@ -1736,8 +1735,8 @@ public class RestMethodContext extends BeanContext
implements Comparable<RestMet
"Invalid argument type passed to the following
method: ''{0}''.\n\tArgument types: {1}",
mi.toString(), mi.getFullName()
);
- } catch (InvocationTargetException e) {
- throw e.getTargetException();
+ } catch (ExecutableException e) {
+ throw e.unwrap();
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
index f9bec8e..05187e2 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -129,7 +129,7 @@ public abstract class RestServlet extends HttpServlet {
throw initException.get();
if (context.get() == null)
throw new InternalServerError("Servlet {0} not
initialized. init(ServletConfig) was not called. This can occur if you've
overridden this method but didn't call super.init(RestConfig).",
getClass().getName());
- getContext().execute(r1, r2);
+ getContext().execute(this, r1, r2);
} catch (Throwable e) {
r2.sendError(SC_INTERNAL_SERVER_ERROR,
e.getLocalizedMessage());