REST refactoring.
diff --git a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethodAnnotation_Test.java b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethodAnnotation_Test.java
index 5ecf817..3e46c26 100644
--- a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethodAnnotation_Test.java
+++ b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/RestMethodAnnotation_Test.java
@@ -36,6 +36,7 @@
RestMethod a1 = RestMethodAnnotation.create()
.clientVersion("clientVersion")
.consumes("consumes")
+ .context(RestMethodContext.class)
.converters(RestConverter.class)
.debug("debug")
.defaultAccept("defaultAccept")
@@ -68,6 +69,7 @@
RestMethod a2 = RestMethodAnnotation.create()
.clientVersion("clientVersion")
.consumes("consumes")
+ .context(RestMethodContext.class)
.converters(RestConverter.class)
.debug("debug")
.defaultAccept("defaultAccept")
@@ -103,6 +105,7 @@
+ "{"
+ "clientVersion:'clientVersion',"
+ "consumes:['consumes'],"
+ + "context:'org.apache.juneau.rest.RestMethodContext',"
+ "converters:['org.apache.juneau.rest.RestConverter'],"
+ "debug:'debug',"
+ "defaultAccept:'defaultAccept',"
@@ -178,6 +181,7 @@
@RestMethod(
clientVersion="clientVersion",
consumes="consumes",
+ context=RestMethodContext.class,
converters=RestConverter.class,
debug="debug",
defaultAccept="defaultAccept",
@@ -212,6 +216,7 @@
@RestMethod(
clientVersion="clientVersion",
consumes="consumes",
+ context=RestMethodContext.class,
converters=RestConverter.class,
debug="debug",
defaultAccept="defaultAccept",
diff --git a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/Rest_Context_Test.java b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/Rest_Context_Test.java
index e09b6ce..cba61b1 100644
--- a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/Rest_Context_Test.java
+++ b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/annotation/Rest_Context_Test.java
@@ -91,7 +91,7 @@
@Test
public void a05_invalidConstructor() throws Exception {
- assertThrown(()->client(A5.class)).contains("Invalid class specified for REST_context");
+ assertThrown(()->client(A5.class)).contains("Could not create instance");
}
//------------------------------------------------------------------------------------------------------------------
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 4863d25..5c3212a 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
@@ -4575,7 +4575,7 @@
if (mi.isNotPublic())
throw new RestServletException("@RestMethod method {0}.{1} must be defined as public.", rci.inner().getName(), mi.getSimpleName());
- RestMethodContextBuilder rmcb = new RestMethodContextBuilder(resource, mi.inner(), this);
+ RestMethodContextBuilder rmcb = new RestMethodContextBuilder(mi.inner(), this);
RestMethodContext rmc = rmcb.build();
String httpMethod = rmc.getHttpMethod();
@@ -4584,11 +4584,13 @@
// We override the CallMethod.invoke() method to insert our logic.
if ("RRPC".equals(httpMethod)) {
- RestMethodContextBuilder smb = new RestMethodContextBuilder(resource, mi.inner(), this);
- smb.dotAll();
+ RestMethodContext smb = new RestMethodContextBuilder(mi.inner(), this)
+ .dotAll()
+ .context(RrpcRestMethodContext.class)
+ .build();
x
- .add("GET", smb.build(RrpcRestMethodContext.class))
- .add("POST", smb.build(RrpcRestMethodContext.class));
+ .add("GET", smb)
+ .add("POST", smb);
} else {
x.add(rmc);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index 9079e9d..5b60ff0 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -185,10 +185,9 @@
try {
PropertyStore ps = getPropertyStore();
Class<? extends RestContext> c = ps.getClassProperty(REST_context, RestContext.class, RestContext.class);
- ConstructorInfo ci = ClassInfo.of(c).getConstructor(Visibility.PUBLIC, RestContextBuilder.class);
- if (ci == null)
- throw new InternalServerError("Invalid class specified for REST_context. Must extend from RestContext and provide a public constructor of the form T(RestContextBuilder).");
- return ci.invoke(this);
+ BeanFactory bf = new BeanFactory(beanFactory, resource);
+ bf.addBean(RestContextBuilder.class, this);
+ return bf.createBean(c);
} catch (Exception e) {
throw toHttpException(e, InternalServerError.class);
}
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 0a68e4d..1f1d218 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
@@ -65,6 +65,14 @@
@ConfigurableContext(nocache=true)
public class RestMethodContext extends BeanContext implements Comparable<RestMethodContext> {
+ /** Represents a null value for the {@link RestMethod#context()} annotation.*/
+ @SuppressWarnings("javadoc")
+ public static final class Null extends RestMethodContext {
+ public Null(RestMethodContextBuilder builder) throws Exception {
+ super(builder);
+ }
+ }
+
//-------------------------------------------------------------------------------------------------------------------
// Configurable properties
//-------------------------------------------------------------------------------------------------------------------
@@ -157,6 +165,37 @@
public static final String RESTMETHOD_clientVersion = PREFIX + ".clientVersion.s";
/**
+ * Configuration property: REST method context class.
+ *
+ * <ul class='spaced-list'>
+ * <li><b>ID:</b> {@link org.apache.juneau.rest.RestMethodContext#RESTMETHOD_context RESTMETHOD_context}
+ * <li><b>Name:</b> <js>"RestMethodContext.context.c"</js>
+ * <li><b>Data type:</b> <c>Class<? extends {@link org.apache.juneau.rest.RestMethodContext}></c>
+ * <li><b>Default:</b> {@link org.apache.juneau.rest.RestMethodContext}
+ * <li><b>Session property:</b> <jk>false</jk>
+ * <li><b>Annotations:</b>
+ * <ul>
+ * <li class='ja'>{@link org.apache.juneau.rest.annotation.RestMethod#context()}
+ * </ul>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link org.apache.juneau.rest.RestMethodContextBuilder#context(Class)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Allows you to extend the {@link RestMethodContext} class to modify how any of the functions are implemented.
+ *
+ * <p>
+ * The subclass must provide the following:
+ * <ul>
+ * <li>A public constructor that takes in one parameter that should be passed to the super constructor: {@link RestMethodContextBuilder}.
+ * </ul>
+ */
+ public static final String RESTMETHOD_context = PREFIX + ".context.c";
+
+ /**
* Configuration property: Debug mode.
*
* <h5 class='section'>Property:</h5>
@@ -638,16 +677,17 @@
/**
* Context constructor.
*
- * @param ps The property store with settings.
+ * @param builder The builder for this object.
* @throws ServletException If context could not be created.
*/
- public RestMethodContext(PropertyStore ps) throws ServletException {
- super(ps);
+ public RestMethodContext(RestMethodContextBuilder builder) throws ServletException {
+ super(builder.getPropertyStore());
try {
- context = getInstanceProperty("RestMethodContext.restContext.o", RestContext.class);
- method = getInstanceProperty("RestMethodContext.restMethod.o", Method.class);
- boolean dotAll = getBooleanProperty("RestMethodContext.dotAll.b", false);
+ context = builder.restContext;
+ method = builder.restMethod;
+
+ PropertyStore ps = getPropertyStore();
methodInvoker = new MethodInvoker(method, context.getMethodExecStats(method));
mi = MethodInfo.of(method).accessible();
@@ -681,7 +721,7 @@
requiredMatchers = matchers.stream().filter(x -> x.required()).toArray(RestMatcher[]::new);
optionalMatchers = matchers.stream().filter(x -> ! x.required()).toArray(RestMatcher[]::new);
- pathMatchers = createPathMatchers(r, beanFactory, dotAll).asArray();
+ pathMatchers = createPathMatchers(r, beanFactory).asArray();
beanFactory.addBean(UrlPathMatcher[].class, pathMatchers);
beanFactory.addBean(UrlPathMatcher.class, pathMatchers.length > 0 ? pathMatchers[0] : null);
@@ -1223,14 +1263,14 @@
*
* @param resource The REST resource object.
* @param beanFactory The bean factory to use for retrieving and creating beans.
- * @param dotAll If {@link RestMethodContextBuilder#dotAll()} was specified.
* @return The HTTP part parser for this REST resource.
* @throws Exception If parser could not be instantiated.
* @seealso #RESTMETHOD_paths
*/
- protected UrlPathMatcherList createPathMatchers(Object resource, BeanFactory beanFactory, boolean dotAll) throws Exception {
+ protected UrlPathMatcherList createPathMatchers(Object resource, BeanFactory beanFactory) throws Exception {
UrlPathMatcherList x = UrlPathMatcherList.create();
+ boolean dotAll = getBooleanProperty("RestMethodContext.dotAll.b", false);
for (String p : getArrayProperty(RESTMETHOD_path, String.class)) {
if (dotAll && ! p.endsWith("/*"))
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContextBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContextBuilder.java
index bd3dbda..02824cd 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContextBuilder.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContextBuilder.java
@@ -13,17 +13,18 @@
package org.apache.juneau.rest;
import static java.util.Arrays.*;
+import static org.apache.juneau.rest.HttpRuntimeException.*;
import static org.apache.juneau.rest.RestMethodContext.*;
import java.lang.annotation.*;
import java.util.*;
import java.util.function.*;
-import javax.servlet.*;
-
import org.apache.http.*;
import org.apache.juneau.*;
+import org.apache.juneau.cp.*;
import org.apache.juneau.http.*;
+import org.apache.juneau.http.exception.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.reflect.*;
import org.apache.juneau.rest.annotation.*;
@@ -35,22 +36,29 @@
*/
public class RestMethodContextBuilder extends BeanContextBuilder {
- @Override
+ RestContext restContext;
+ Method restMethod;
+
+ @Override /* BeanContextBuilder */
public RestMethodContext build() {
try {
- return new RestMethodContext(getPropertyStore());
- } catch (ServletException e) {
- throw new RuntimeException(e);
+ PropertyStore ps = getPropertyStore();
+ Class<? extends RestMethodContext> c = ps.getClassProperty(RESTMETHOD_context, RestMethodContext.class, RestMethodContext.class);
+ BeanFactory bf = new BeanFactory(restContext.rootBeanFactory, restContext.getResource());
+ bf.addBean(RestMethodContextBuilder.class, this);
+ return bf.createBean(c);
+ } catch (Exception e) {
+ throw toHttpException(e, InternalServerError.class);
}
}
- RestMethodContextBuilder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException {
- set("RestMethodContext.restContext.o", context);
- set("RestMethodContext.restMethod.o", method);
- set("RestMethodContext.restObject.o", context.getResource()); // Added to force a new cache hash.
+ RestMethodContextBuilder(java.lang.reflect.Method method, RestContext context) throws RestServletException {
+
+ this.restContext = context;
+ this.restMethod = method;
String sig = method.getDeclaringClass().getName() + '.' + method.getName();
- MethodInfo mi = MethodInfo.of(servlet.getClass(), method);
+ MethodInfo mi = MethodInfo.of(context.getResource().getClass(), method);
try {
@@ -113,6 +121,25 @@
}
/**
+ * <i><l>RestMethodContext</l> configuration property: </i> REST method context class.
+ *
+ * Allows you to extend the {@link RestMethodContext} class to modify how any of the methods are implemented.
+ *
+ * <p>
+ * The subclass must provide the following:
+ * <ul>
+ * <li>A public constructor that takes in one parameter that should be passed to the super constructor: {@link RestMethodContextBuilder}.
+ * </ul>
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ @FluentSetter
+ public RestMethodContextBuilder context(Class<? extends RestMethodContext> value) {
+ return set(RESTMETHOD_context, value);
+ }
+
+ /**
* <i><l>RestMethodContext</l> configuration property: </i> Debug mode.
*
* <p>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RrpcRestMethodContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RrpcRestMethodContext.java
index accaafe..8d0c591 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RrpcRestMethodContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RrpcRestMethodContext.java
@@ -35,11 +35,11 @@
/**
* Constructor.
*
- * @param ps The property store containing the settings for this context.
+ * @param builder The builder for this method context.
* @throws ServletException Problem with metadata was detected.
*/
- public RrpcRestMethodContext(PropertyStore ps) throws ServletException {
- super(ps);
+ public RrpcRestMethodContext(RestMethodContextBuilder builder) throws ServletException {
+ super(builder);
ClassMeta<?> interfaceClass = getClassMeta(mi.inner().getGenericReturnType());
meta = new RrpcInterfaceMeta(interfaceClass.getInnerClass(), null);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
index b88cccb..7f0ed4a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethod.java
@@ -117,6 +117,21 @@
String[] consumes() default {};
/**
+ * Allows you to extend the {@link RestContext} class to modify how any of the methods are implemented.
+ *
+ * <p>
+ * The subclass must provide the following:
+ * <ul>
+ * <li>A public constructor that takes in one parameter that should be passed to the super constructor: {@link RestMethodContextBuilder}.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jm'>{@link RestMethodContextBuilder#context(Class)}
+ * </ul>
+ */
+ Class<? extends RestMethodContext> context() default RestMethodContext.Null.class;
+
+ /**
* Class-level response converters.
*
* <p>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodAnnotation.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodAnnotation.java
index e7f899f..db221bf 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodAnnotation.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodAnnotation.java
@@ -61,6 +61,7 @@
Class<? extends RestConverter>[] converters = new Class[0];
Class<? extends RestGuard>[] guards = new Class[0];
Class<? extends RestMatcher>[] matchers = new Class[0];
+ Class<? extends RestMethodContext> context = RestMethodContext.Null.class;
Class<?>[] encoders=new Class<?>[0], parsers=new Class<?>[0], serializers=new Class<?>[0];
int priority = 0;
MethodSwagger swagger = MethodSwaggerAnnotation.DEFAULT;
@@ -106,6 +107,17 @@
}
/**
+ * Sets the {@link RestMethod#context()} property on this annotation.
+ *
+ * @param value The new value for this property.
+ * @return This object (for method chaining).
+ */
+ public Builder context(Class<? extends RestMethodContext> value) {
+ this.context = value;
+ return this;
+ }
+
+ /**
* Sets the {@link RestMethod#converters()} property on this annotation.
*
* @param value The new value for this property.
@@ -413,6 +425,7 @@
private final Class<? extends RestConverter>[] converters;
private final Class<? extends RestGuard>[] guards;
private final Class<? extends RestMatcher>[] matchers;
+ private final Class<? extends RestMethodContext> context;
private final Class<?>[] encoders, parsers, serializers;
private final int priority;
private final MethodSwagger swagger;
@@ -423,6 +436,7 @@
super(b);
this.clientVersion = b.clientVersion;
this.consumes = copyOf(b.consumes);
+ this.context = b.context;
this.converters = copyOf(b.converters);
this.debug = b.debug;
this.defaultAccept = b.defaultAccept;
@@ -463,6 +477,11 @@
}
@Override /* RestMethod */
+ public Class<? extends RestMethodContext> context() {
+ return context;
+ }
+
+ @Override /* RestMethod */
public Class<? extends RestConverter>[] converters() {
return converters;
}
@@ -615,6 +634,7 @@
psb.set(REST_serializers, merge(ConverterUtils.toType(psb.peek(REST_serializers), Object[].class), a.serializers()));
psb.set(REST_parsers, merge(ConverterUtils.toType(psb.peek(REST_parsers), Object[].class), a.parsers()));
psb.set(REST_encoders, merge(ConverterUtils.toType(psb.peek(REST_encoders), Object[].class), a.encoders()));
+ psb.setIf(a.context() != RestMethodContext.Null.class, RESTMETHOD_context, a.context());
psb.setIfNotEmpty(REST_produces, stringList(a.produces()));
psb.setIfNotEmpty(REST_consumes, stringList(a.consumes()));
stringStream(a.defaultRequestHeaders()).map(x -> BasicHeader.ofPair(x)).forEach(x -> psb.appendTo(RESTMETHOD_defaultRequestHeaders, x));