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 2020/03/18 19:49:17 UTC
[juneau] branch master updated: JUNEAU-198 & JUNEAU-189
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 304d99c JUNEAU-198 & JUNEAU-189
304d99c is described below
commit 304d99cd252172cdd838ec1962b7db2402245a2e
Author: JamesBognar <ja...@apache.org>
AuthorDate: Wed Mar 18 15:48:58 2020 -0400
JUNEAU-198 & JUNEAU-189
Need improved ability to turn on debug in REST via environment
variables.
@ReestMethod(debug="true") does not appear to cause HTTP requests to be
logged.
---
.../apache/juneau/reflection/ClassInfoTest.java | 62 +-
.../juneau/reflection/ExecutableInfoTest.java | 12 +-
.../org/apache/juneau/internal/ClassUtils.java | 111 +-
.../java/org/apache/juneau/reflect/ClassInfo.java | 125 +-
.../org/apache/juneau/reflect/ExecutableInfo.java | 93 +-
.../org/apache/juneau/transform/AutoListSwap.java | 2 +-
.../org/apache/juneau/transform/AutoMapSwap.java | 2 +-
.../apache/juneau/transform/AutoNumberSwap.java | 2 +-
.../apache/juneau/transform/AutoObjectSwap.java | 2 +-
.../org/apache/juneau/utils/ReflectionMap.java | 96 +-
juneau-doc/docs/ReleaseNotes/8.1.4.html | 138 ++-
.../juneau/examples/rest/HelloWorldResource.java | 25 -
.../juneau/rest/mock2/MockServletRequest.java | 3 +-
.../java/org/apache/juneau/rest/DebugModeTest.java | 1195 ++++++++++++++++++++
.../java/org/apache/juneau/rest/BasicRest.java | 415 ++++++-
.../apache/juneau/rest/BasicRestCallHandler.java | 14 +-
.../apache/juneau/rest/BasicRestCallLogger.java | 25 +-
.../org/apache/juneau/rest/BasicRestConfig.java | 96 +-
...{BasicRestConfig.java => BasicRestMethods.java} | 123 +-
.../org/apache/juneau/rest/BasicRestServlet.java | 14 +-
.../java/org/apache/juneau/rest/Enablement.java | 13 +
.../{Enablement.java => NoOpRestCallLogger.java} | 38 +-
.../main/java/org/apache/juneau/rest/RestCall.java | 24 +-
.../apache/juneau/rest/RestCallLoggerConfig.java | 2 +-
.../java/org/apache/juneau/rest/RestContext.java | 146 ++-
.../org/apache/juneau/rest/RestContextBuilder.java | 27 +-
.../org/apache/juneau/rest/RestMethodContext.java | 106 +-
.../java/org/apache/juneau/rest/RestRequest.java | 13 +-
.../java/org/apache/juneau/rest/StatusStats.java | 1 +
.../org/apache/juneau/rest/annotation/Rest.java | 195 +++-
.../juneau/rest/annotation/RestConfigApply.java | 8 +-
.../apache/juneau/rest/annotation/RestMethod.java | 11 +-
.../rest/annotation/RestMethodConfigApply.java | 6 +-
33 files changed, 2614 insertions(+), 531 deletions(-)
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java
index 1f60dca..56a822c 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java
@@ -699,7 +699,7 @@ public class ClassInfoTest {
check("E1(int)", e1.getConstructor(Visibility.PRIVATE, int.class));
check(null, e1.getConstructor(Visibility.PUBLIC, int.class));
check("E3()", e3.getConstructor(Visibility.PUBLIC));
- check("E4(ClassInfoTest)", e4.getConstructor(Visibility.PUBLIC));
+ check(null, e4.getConstructor(Visibility.PUBLIC));
check("E5()", e5.getConstructor(Visibility.PUBLIC));
}
@@ -2259,6 +2259,66 @@ public class ClassInfoTest {
check("MM", mn.getParameterType(1, HashMap.class));
}
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // ClassInfo.getStaticCreator(Object...)
+ // ClassInfo.getStaticCreatorFuzzy(Object...)
+ //-----------------------------------------------------------------------------------------------------------------
+
+ public static class N1 {}
+ public static ClassInfo n1 = ClassInfo.of(N1.class);
+
+ @Test
+ public void n01_noCreators() {
+ assertNull(n1.getStaticCreator());
+ assertNull(n1.getStaticCreatorFuzzy());
+ }
+
+ public static class N2 {
+ public static N2 create() {return null;}
+ }
+ public static ClassInfo n2 = ClassInfo.of(N2.class);
+
+ @Test
+ public void n02_noArgCreator() {
+ assertNotNull(n2.getStaticCreator());
+ assertNotNull(n2.getStaticCreatorFuzzy());
+ }
+
+ public static class N3 {
+ public static N3 create(String foo, int bar) {return null;}
+ }
+ public static ClassInfo n3 = ClassInfo.of(N3.class);
+
+ @Test
+ public void n03_withArgCreators() {
+ assertNull(n3.getStaticCreator());
+ assertNull(n3.getStaticCreatorFuzzy());
+ assertNotNull(n3.getStaticCreator("foo", 123));
+ assertNotNull(n3.getStaticCreatorFuzzy("foo", 123));
+ assertNotNull(n3.getStaticCreator(123, "foo"));
+ assertNotNull(n3.getStaticCreatorFuzzy(123, "foo"));
+ assertNull(n3.getStaticCreator("foo", 123, new File(".")));
+ assertNotNull(n3.getStaticCreatorFuzzy("foo", 123, new File(".")));
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // ClassInfo.isParentOfFuzzyPrimitives(Class)
+ // ClassInfo.isParentOfFuzzyPrimitives(Type)
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void o01_isParentOfFuzzyPrimitives() {
+ assertTrue(ClassInfo.of(String.class).isParentOfFuzzyPrimitives(String.class));
+ assertTrue(ClassInfo.of(CharSequence.class).isParentOfFuzzyPrimitives(String.class));
+ assertFalse(ClassInfo.of(String.class).isParentOfFuzzyPrimitives(CharSequence.class));
+ assertTrue(ClassInfo.of(int.class).isParentOfFuzzyPrimitives(Integer.class));
+ assertTrue(ClassInfo.of(Integer.class).isParentOfFuzzyPrimitives(int.class));
+ assertTrue(ClassInfo.of(Number.class).isParentOfFuzzyPrimitives(int.class));
+ assertFalse(ClassInfo.of(int.class).isParentOfFuzzyPrimitives(Number.class));
+ assertFalse(ClassInfo.of(int.class).isParentOfFuzzyPrimitives(long.class));
+ }
+
//-----------------------------------------------------------------------------------------------------------------
// Other
//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ExecutableInfoTest.java b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ExecutableInfoTest.java
index d3b7ff8..18756e2 100644
--- a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ExecutableInfoTest.java
+++ b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ExecutableInfoTest.java
@@ -483,12 +483,12 @@ public class ExecutableInfoTest {
@Test
public void hasArgParents() {
- assertTrue(e_hasStringParam.hasParamTypeParents(String.class));
- assertTrue(e_hasStringParam.hasParamTypeParents(CharSequence.class));
- assertFalse(e_hasStringParam.hasParamTypeParents(StringBuilder.class));
- assertFalse(e_hasStringParam.hasParamTypeParents(new Class[0]));
- assertFalse(e_hasStringParam.hasParamTypeParents(String.class, String.class));
- assertFalse(e_hasStringParam.hasParamTypeParents(long.class));
+ assertTrue(e_hasStringParam.hasMatchingParamTypes(String.class));
+ assertFalse(e_hasStringParam.hasMatchingParamTypes(CharSequence.class));
+ assertFalse(e_hasStringParam.hasMatchingParamTypes(StringBuilder.class));
+ assertFalse(e_hasStringParam.hasMatchingParamTypes(new Class[0]));
+ assertFalse(e_hasStringParam.hasMatchingParamTypes(String.class, String.class));
+ assertFalse(e_hasStringParam.hasMatchingParamTypes(long.class));
}
@Test
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index 2a9f123..d125673 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -38,108 +38,6 @@ public final class ClassUtils {
}
/**
- * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types.
- *
- * @param paramTypes The parameters types specified on a method.
- * @param argTypes The class types of the arguments being passed to the method.
- * @return <jk>true</jk> if the arguments match the parameters.
- */
- public static boolean argsMatch(Class<?>[] paramTypes, Class<?>[] argTypes) {
- if (paramTypes.length == argTypes.length) {
- for (int i = 0; i < paramTypes.length; i++)
- if (! ClassInfo.of(paramTypes[i]).isParentOf(argTypes[i]))
- return false;
- return true;
- }
- return false;
- }
-
- /**
- * Returns <jk>true</jk> if the specified argument types are valid for the specified parameter types.
- *
- * @param paramTypes The parameters types specified on a method.
- * @param argTypes The class types of the arguments being passed to the method.
- * @return <jk>true</jk> if the arguments match the parameters.
- */
- public static boolean argsMatch(List<ClassInfo> paramTypes, Class<?>[] argTypes) {
- if (paramTypes.size() == argTypes.length) {
- for (int i = 0; i < paramTypes.size(); i++)
- if (! paramTypes.get(i).isParentOf(argTypes[i]))
- return false;
- return true;
- }
- return false;
- }
-
- /**
- * Returns a number representing the number of arguments that match the specified parameters.
- *
- * @param paramTypes The parameters types specified on a method.
- * @param argTypes The class types of the arguments being passed to the method.
- * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
- */
- public static int fuzzyArgsMatch(Class<?>[] paramTypes, Class<?>... argTypes) {
- int matches = 0;
- outer: for (Class<?> p : paramTypes) {
- ClassInfo pi = ClassInfo.of(p).getWrapperInfoIfPrimitive();
- for (Class<?> a : argTypes) {
- ClassInfo ai = ClassInfo.of(a).getWrapperInfoIfPrimitive();
- if (pi.isParentOf(ai.inner())) {
- matches++;
- continue outer;
- }
- }
- return -1;
- }
- return matches;
- }
-
- /**
- * Returns a number representing the number of arguments that match the specified parameters.
- *
- * @param paramTypes The parameters types specified on a method.
- * @param argTypes The class types of the arguments being passed to the method.
- * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
- */
- public static int fuzzyArgsMatch(Class<?>[] paramTypes, ClassInfo... argTypes) {
- int matches = 0;
- outer: for (Class<?> p : paramTypes) {
- ClassInfo pi = ClassInfo.of(p).getWrapperInfoIfPrimitive();
- for (ClassInfo a : argTypes) {
- ClassInfo ai = a.getWrapperInfoIfPrimitive();
- if (pi.isParentOf(ai.inner())) {
- matches++;
- continue outer;
- }
- }
- return -1;
- }
- return matches;
- }
-
- /**
- * Returns a number representing the number of arguments that match the specified parameters.
- *
- * @param paramTypes The parameters types specified on a method.
- * @param argTypes The class types of the arguments being passed to the method.
- * @return The number of matching arguments, or <c>-1</c> a parameter was found that isn't in the list of args.
- */
- public static int fuzzyArgsMatch(List<ClassInfo> paramTypes, Class<?>... argTypes) {
- int matches = 0;
- outer: for (ClassInfo p : paramTypes) {
- p = p.getWrapperInfoIfPrimitive();
- for (Class<?> a : argTypes) {
- if (p.isParentOf(a)) {
- matches++;
- continue outer;
- }
- }
- return -1;
- }
- return matches;
- }
-
- /**
* Returns the class types for the specified arguments.
*
* @param args The objects we're getting the classes of.
@@ -221,6 +119,11 @@ public final class ClassUtils {
if (c2 instanceof Class) {
try {
ClassInfo c3 = ClassInfo.of((Class<?>)c2);
+
+ MethodInfo mi = c3.getStaticCreator(args);
+ if (mi != null)
+ return mi.invoke(null, args);
+
if (c3.isInterface() || c3.isAbstract())
return null;
@@ -239,6 +142,10 @@ public final class ClassUtils {
// Finally use fuzzy matching.
if (fuzzyArgs) {
+ mi = c3.getStaticCreatorFuzzy(args);
+ if (mi != null)
+ return mi.invoke(null, getMatchingArgs(mi.getParamTypes(), args));
+
con = c3.getPublicConstructorFuzzy(args);
if (con != null)
return con.<T>invoke(getMatchingArgs(con.getParamTypes(), args));
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
index d63aa1f..41ddb37 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
@@ -567,6 +567,66 @@ public final class ClassInfo {
}
/**
+ * Find the public static creator method on this class.
+ *
+ * <p>
+ * Must have the following signature where T is the exact outer class.
+ * <p class='bcode w800'>
+ * public static T create(...);
+ * </p>
+ *
+ * <p>
+ * Must be able to take in all arguments specified in any order.
+ *
+ * @param args The arguments to pass to the create method.
+ * @return The static method, or <jk>null</jk> if it couldn't be found.
+ */
+ public MethodInfo getStaticCreator(Object...args) {
+ if (c != null) {
+ Class<?>[] argTypes = ClassUtils.getClasses(args);
+ for (MethodInfo m : getPublicMethods()) {
+ if (m.isAll(STATIC, PUBLIC, NOT_DEPRECATED) && m.hasReturnType(c) && m.getSimpleName().equals("create") && m.hasMatchingParamTypes(argTypes))
+ return m;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find the public static creator method on this class.
+ *
+ * <p>
+ * Must have the following signature where T is the exact outer class.
+ * <p class='bcode w800'>
+ * public static T create(...);
+ * </p>
+ *
+ * <p>
+ * Returned method can take in arguments in any order if they match. The method is guaranteed to not require additional
+ * arguments not specified.
+ *
+ * @param args The arguments to pass to the create method.
+ * @return The static method, or <jk>null</jk> if it couldn't be found.
+ */
+ public MethodInfo getStaticCreatorFuzzy(Object...args) {
+ int bestCount = -1;
+ MethodInfo bestMatch = null;
+ if (c != null) {
+ Class<?>[] argTypes = ClassUtils.getClasses(args);
+ for (MethodInfo m : getPublicMethods()) {
+ if (m.isAll(STATIC, PUBLIC, NOT_DEPRECATED) && m.hasReturnType(c) && m.getSimpleName().equals("create")) {
+ int mn = m.fuzzyArgsMatch(argTypes);
+ if (mn > bestCount) {
+ bestCount = mn;
+ bestMatch = m;
+ }
+ }
+ }
+ }
+ return bestMatch;
+ }
+
+ /**
* Find the public static method with the specified name and args.
*
* @param name The method name.
@@ -750,7 +810,7 @@ public final class ClassInfo {
ConstructorInfo bestMatch = null;
for (ConstructorInfo n : _getDeclaredConstructors()) {
if (vis.isVisible(n.inner())) {
- int m = ClassUtils.fuzzyArgsMatch(n.getParamTypes(), argTypes);
+ int m = n.fuzzyArgsMatch(argTypes);
if (m > bestCount) {
bestCount = m;
bestMatch = n;
@@ -765,7 +825,7 @@ public final class ClassInfo {
List<ClassInfo> paramTypes = n.getParamTypes();
if (isMemberClass)
paramTypes = paramTypes.subList(1, paramTypes.size());
- if (ClassUtils.argsMatch(paramTypes, argTypes) && vis.isVisible(n.inner()))
+ if (n.hasMatchingParamTypes(argTypes) && vis.isVisible(n.inner()))
return n;
}
@@ -1919,6 +1979,55 @@ public final class ClassInfo {
/**
* Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
*
+ * <p>
+ * Primitive classes are converted to wrapper classes and compared.
+ *
+ * <h5 class='section'>Examples:</h5>
+ * <p class='bcode w800'>
+ * ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>).isParentOfFuzzyPrimitives(String.<jk>class</jk>); <jc>// true</jc>
+ * ClassInfo.<jsm>of</jsm>(CharSequence.<jk>class</jk>).isParentOfFuzzyPrimitives(String.<jk>class</jk>); <jc>// true</jc>
+ * ClassInfo.<jsm>of</jsm>(String.<jk>class</jk>).isParentOfFuzzyPrimitives(CharSequence.<jk>class</jk>); <jc>// false</jc>
+ * ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfFuzzyPrimitives(Integer.<jk>class</jk>); <jc>// true</jc>
+ * ClassInfo.<jsm>of</jsm>(Integer.<jk>class</jk>).isParentOfFuzzyPrimitives(<jk>int</jk>.<jk>class</jk>); <jc>// true</jc>
+ * ClassInfo.<jsm>of</jsm>(Number.<jk>class</jk>).isParentOfFuzzyPrimitives(<jk>int</jk>.<jk>class</jk>); <jc>// true</jc>
+ * ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfFuzzyPrimitives(Number.<jk>class</jk>); <jc>// false</jc>
+ * ClassInfo.<jsm>of</jsm>(<jk>int</jk>.<jk>class</jk>).isParentOfFuzzyPrimitives(<jk>long</jk>.<jk>class</jk>); <jc>// false</jc>
+ * </p>
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
+ */
+ public boolean isParentOfFuzzyPrimitives(Class<?> child) {
+ if (c == null || child == null)
+ return false;
+ if (c.isAssignableFrom(child))
+ return true;
+ if (this.isPrimitive() || child.isPrimitive()) {
+ return this.getWrapperIfPrimitive().isAssignableFrom(of(child).getWrapperIfPrimitive());
+ }
+ return false;
+ }
+
+ /**
+ * Same as {@link #isParentOfFuzzyPrimitives(Class)} but takes in a {@link ClassInfo}.
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
+ */
+ public boolean isParentOfFuzzyPrimitives(ClassInfo child) {
+ if (c == null || child == null)
+ return false;
+ if (c.isAssignableFrom(child.inner()))
+ return true;
+ if (this.isPrimitive() || child.isPrimitive()) {
+ return this.getWrapperIfPrimitive().isAssignableFrom(child.getWrapperIfPrimitive());
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
+ *
* @param child The child class.
* @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
*/
@@ -1929,6 +2038,18 @@ public final class ClassInfo {
}
/**
+ * Returns <jk>true</jk> if this class is a parent or the same as <c>child</c>.
+ *
+ * @param child The child class.
+ * @return <jk>true</jk> if this class is a parent or the same as <c>child</c>.
+ */
+ public boolean isParentOfFuzzyPrimitives(Type child) {
+ if (child instanceof Class)
+ return isParentOfFuzzyPrimitives((Class<?>)child);
+ return false;
+ }
+
+ /**
* Returns <jk>true</jk> if this class is a child of <c>parent</c>.
*
* @param parent The parent class.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
index 026d63c..a0a7963 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
@@ -500,15 +500,19 @@ public abstract class ExecutableInfo {
* @param args The arguments to test for.
* @return <jk>true</jk> if this method has this arguments in the exact order.
*/
- public final boolean hasParamTypeParents(Class<?>...args) {
- Class<?>[] pt = _getRawParamTypes();
- if (pt.length == args.length) {
- for (int i = 0; i < pt.length; i++)
- if (! args[i].isAssignableFrom(pt[i]))
- return false;
- return true;
+ public final boolean hasMatchingParamTypes(Class<?>...args) {
+ ClassInfo[] pt = _getParamTypes();
+ if (pt.length != args.length)
+ return false;
+ for (int i = 0; i < pt.length; i++) {
+ boolean matched = false;
+ for (int j = 0; j < args.length; j++)
+ if (pt[i].isParentOfFuzzyPrimitives(args[j]))
+ matched = true;
+ if (! matched)
+ return false;
}
- return false;
+ return true;
}
/**
@@ -517,15 +521,19 @@ public abstract class ExecutableInfo {
* @param args The arguments to test for.
* @return <jk>true</jk> if this method has this arguments in the exact order.
*/
- public final boolean hasParamTypeParents(ClassInfo...args) {
- Class<?>[] pt = _getRawParamTypes();
- if (pt.length == args.length) {
- for (int i = 0; i < pt.length; i++)
- if (! args[i].inner().isAssignableFrom(pt[i]))
- return false;
- return true;
+ public final boolean hasMatchingParamTypes(ClassInfo...args) {
+ ClassInfo[] pt = _getParamTypes();
+ if (pt.length != args.length)
+ return false;
+ for (int i = 0; i < pt.length; i++) {
+ boolean matched = false;
+ for (int j = 0; j < args.length; j++)
+ if (pt[i].isParentOfFuzzyPrimitives(args[j].inner()))
+ matched = true;
+ if (! matched)
+ return false;
}
- return false;
+ return true;
}
/**
@@ -535,9 +543,34 @@ public abstract class ExecutableInfo {
* @return <jk>true</jk> if this method has at most only this arguments in any order.
*/
public final boolean hasFuzzyParamTypes(Class<?>...args) {
- return ClassUtils.fuzzyArgsMatch(_getRawParamTypes(), args) != -1;
+ return fuzzyArgsMatch(args) != -1;
+ }
+
+ /**
+ * Returns how well this method matches the specified arg types.
+ *
+ * <p>
+ * The number returned is the number of method arguments that match the passed in arg types.
+ * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
+ *
+ * @param argTypes The arg types to check against.
+ * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
+ */
+ public int fuzzyArgsMatch(Class<?>... argTypes) {
+ int matches = 0;
+ outer: for (ClassInfo pi : getParamTypes()) {
+ for (Class<?> a : argTypes) {
+ if (pi.isParentOfFuzzyPrimitives(a)) {
+ matches++;
+ continue outer;
+ }
+ }
+ return -1;
+ }
+ return matches;
}
+
/**
* Returns <jk>true</jk> if this method has at most only this arguments in any order.
*
@@ -545,7 +578,31 @@ public abstract class ExecutableInfo {
* @return <jk>true</jk> if this method has at most only this arguments in any order.
*/
public boolean hasFuzzyParamTypes(ClassInfo...args) {
- return ClassUtils.fuzzyArgsMatch(_getRawParamTypes(), args) != -1;
+ return fuzzyArgsMatch(args) != -1;
+ }
+
+ /**
+ * Returns how well this method matches the specified arg types.
+ *
+ * <p>
+ * The number returned is the number of method arguments that match the passed in arg types.
+ * <br>Returns <c>-1</c> if the method cannot take in one or more of the specified arguments.
+ *
+ * @param argTypes The arg types to check against.
+ * @return How many parameters match or <c>-1</c> if method cannot handle one or more of the arguments.
+ */
+ public int fuzzyArgsMatch(ClassInfo... argTypes) {
+ int matches = 0;
+ outer: for (ClassInfo pi : getParamTypes()) {
+ for (ClassInfo a : argTypes) {
+ if (pi.isParentOfFuzzyPrimitives(a)) {
+ matches++;
+ continue outer;
+ }
+ }
+ return -1;
+ }
+ return matches;
}
/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoListSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoListSwap.java
index 23d06c1..5be0d6a 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoListSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoListSwap.java
@@ -142,7 +142,7 @@ public class AutoListSwap<T> extends PojoSwap<T,List<?>> {
private static boolean isUnswapConstructor(BeanContext bc, ConstructorInfo cs, ClassInfo rt) {
return
cs.isNotDeprecated()
- && cs.hasParamTypeParents(rt)
+ && cs.hasMatchingParamTypes(rt)
&& ! bc.hasAnnotation(BeanIgnore.class, cs);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoMapSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoMapSwap.java
index ba1b813..34eba9d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoMapSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoMapSwap.java
@@ -142,7 +142,7 @@ public class AutoMapSwap<T> extends PojoSwap<T,Map<?,?>> {
private static boolean isUnswapConstructor(BeanContext bc, ConstructorInfo cs, ClassInfo rt) {
return
cs.isNotDeprecated()
- && cs.hasParamTypeParents(rt)
+ && cs.hasMatchingParamTypes(rt)
&& ! bc.hasAnnotation(BeanIgnore.class, cs);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoNumberSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoNumberSwap.java
index ebb89d6..7954f75 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoNumberSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoNumberSwap.java
@@ -168,7 +168,7 @@ public class AutoNumberSwap<T> extends PojoSwap<T,Number> {
private static boolean isUnswapConstructor(BeanContext bc, ConstructorInfo cs, ClassInfo rt) {
return
cs.isNotDeprecated()
- && cs.hasParamTypeParents(rt)
+ && cs.hasMatchingParamTypes(rt)
&& ! bc.hasAnnotation(BeanIgnore.class, cs);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoObjectSwap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoObjectSwap.java
index aea43c4..2796284 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoObjectSwap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/transform/AutoObjectSwap.java
@@ -143,7 +143,7 @@ public class AutoObjectSwap<T> extends PojoSwap<T,Object> {
private static boolean isUnswapConstructor(BeanContext bc, ConstructorInfo cs, ClassInfo rt) {
return
cs.isNotDeprecated()
- && cs.hasParamTypeParents(rt)
+ && cs.hasMatchingParamTypes(rt)
&& ! bc.hasAnnotation(BeanIgnore.class, cs);
}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ReflectionMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ReflectionMap.java
index 63f76f5..26ea3ce 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ReflectionMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/ReflectionMap.java
@@ -259,11 +259,21 @@ public class ReflectionMap<V> {
for (ClassEntry<V> e : classEntries)
if (e.matches(c))
if (ofType == null || ofType.isInstance(e.value))
- return Optional.of(e.value);
+ return Optional.ofNullable(e.value);
return Optional.empty();
}
/**
+ * Finds first value in this map that matches the specified class.
+ *
+ * @param c The class to test for.
+ * @return The matching object. Never <jk>null</jk>.
+ */
+ public Optional<V> find(Class<?> c) {
+ return find(c, null);
+ }
+
+ /**
* Finds all values in this map that matches the specified class.
*
* @param c The class to test for.
@@ -278,6 +288,16 @@ public class ReflectionMap<V> {
* Finds all values in this map that matches the specified class.
*
* @param c The class to test for.
+ * @return A modifiable list of matching values. Never <jk>null</jk>.
+ */
+ public List<V> findAll(Class<?> c) {
+ return appendAll(c, null, null);
+ }
+
+ /**
+ * Finds all values in this map that matches the specified class.
+ *
+ * @param c The class to test for.
* @param ofType Only return objects of the specified type.
* @param l The list to append values to. Can be <jk>null</jk>.
* @return The same list passed in or a new modifiable list if <jk>null</jk>.
@@ -287,7 +307,7 @@ public class ReflectionMap<V> {
l = AList.create();
if (! noClassEntries)
for (ClassEntry<V> e : classEntries)
- if (e.matches(c))
+ if (e.matches(c) && e.value != null)
if (ofType == null || ofType.isInstance(e.value))
l.add(e.value);
return l;
@@ -305,11 +325,21 @@ public class ReflectionMap<V> {
for (MethodEntry<V> e : methodEntries)
if (e.matches(m))
if (ofType == null || ofType.isInstance(e.value))
- return Optional.of(e.value);
+ return Optional.ofNullable(e.value);
return Optional.empty();
}
/**
+ * Finds first value in this map that matches the specified method.
+ *
+ * @param m The method to test for.
+ * @return The matching object. Never <jk>null</jk>.
+ */
+ public Optional<V> find(Method m) {
+ return find(m, null);
+ }
+
+ /**
* Finds all values in this map that matches the specified method.
*
* @param m The method to test for.
@@ -324,6 +354,16 @@ public class ReflectionMap<V> {
* Finds all values in this map that matches the specified method.
*
* @param m The method to test for.
+ * @return A modifiable list of matching values. Never <jk>null</jk>.
+ */
+ public List<V> findAll(Method m) {
+ return appendAll(m, null, null);
+ }
+
+ /**
+ * Finds all values in this map that matches the specified method.
+ *
+ * @param m The method to test for.
* @param ofType Only return objects of the specified type.
* @param l The list to append values to. Can be <jk>null</jk>.
* @return The same list passed in or a new modifiable list if <jk>null</jk>.
@@ -333,7 +373,7 @@ public class ReflectionMap<V> {
l = AList.create();
if (! noMethodEntries)
for (MethodEntry<V> e : methodEntries)
- if (e.matches(m))
+ if (e.matches(m) && e.value != null)
if (ofType == null || ofType.isInstance(e.value))
l.add(e.value);
return l;
@@ -351,11 +391,21 @@ public class ReflectionMap<V> {
for (FieldEntry<V> e : fieldEntries)
if (e.matches(f))
if (ofType == null || ofType.isInstance(e.value))
- return Optional.of(e.value);
+ return Optional.ofNullable(e.value);
return Optional.empty();
}
/**
+ * Finds first value in this map that matches the specified field.
+ *
+ * @param f The field to test for.
+ * @return The matching object. Never <jk>null</jk>.
+ */
+ public Optional<V> find(Field f) {
+ return find(f, null);
+ }
+
+ /**
* Finds all values in this map that matches the specified field.
*
* @param f The field to test for.
@@ -370,6 +420,16 @@ public class ReflectionMap<V> {
* Finds all values in this map that matches the specified field.
*
* @param f The field to test for.
+ * @return A modifiable list of matching values. Never <jk>null</jk>.
+ */
+ public List<V> findAll(Field f) {
+ return appendAll(f, null, null);
+ }
+
+ /**
+ * Finds all values in this map that matches the specified field.
+ *
+ * @param f The field to test for.
* @param ofType Only return objects of the specified type.
* @param l The list to append values to. Can be <jk>null</jk>.
* @return The same list passed in or a new modifiable list if <jk>null</jk>.
@@ -379,7 +439,7 @@ public class ReflectionMap<V> {
l = AList.create();
if (! noFieldEntries)
for (FieldEntry<V> e : fieldEntries)
- if (e.matches(f))
+ if (e.matches(f) && e.value != null)
if (ofType == null || ofType.isInstance(e.value))
l.add(e.value);
return l == null ? new ArrayList<>(0) : l;
@@ -397,11 +457,21 @@ public class ReflectionMap<V> {
for (ConstructorEntry<V> e : constructorEntries)
if (e.matches(c))
if (ofType == null || ofType.isInstance(e.value))
- return Optional.of(e.value);
+ return Optional.ofNullable(e.value);
return Optional.empty();
}
/**
+ * Finds first value in this map that matches the specified constructor.
+ *
+ * @param c The constructor to test for.
+ * @return The matching object. Never <jk>null</jk>.
+ */
+ public Optional<V> find(Constructor<?> c) {
+ return find(c, null);
+ }
+
+ /**
* Finds all values in this map that matches the specified constructor.
*
* @param c The constructor to test for.
@@ -416,6 +486,16 @@ public class ReflectionMap<V> {
* Finds all values in this map that matches the specified constructor.
*
* @param c The constructor to test for.
+ * @return A modifiable list of matching values. Never <jk>null</jk>.
+ */
+ public List<V> findAll(Constructor<?> c) {
+ return appendAll(c, null, null);
+ }
+
+ /**
+ * Finds all values in this map that matches the specified constructor.
+ *
+ * @param c The constructor to test for.
* @param ofType Only return objects of the specified type.
* @param l The list to append values to. Can be <jk>null</jk>.
* @return The same list passed in or a new modifiable list if <jk>null</jk>.
@@ -425,7 +505,7 @@ public class ReflectionMap<V> {
l = AList.create();
if (! noConstructorEntries)
for (ConstructorEntry<V> e : constructorEntries)
- if (e.matches(c))
+ if (e.matches(c) && e.value != null)
if (ofType == null || ofType.isInstance(e.value))
l.add(e.value);
return l;
diff --git a/juneau-doc/docs/ReleaseNotes/8.1.4.html b/juneau-doc/docs/ReleaseNotes/8.1.4.html
index 47f87fe..508e1d3 100644
--- a/juneau-doc/docs/ReleaseNotes/8.1.4.html
+++ b/juneau-doc/docs/ReleaseNotes/8.1.4.html
@@ -166,64 +166,134 @@
<li class='jic'>{@link oaj.utils.ClasspathResourceFinder} - Normally defined through {@link oajr.annotation.Rest#classpathResourceFinder() @Rest.classpathResourceFinder()}.
</ul>
<li>
- {@link oajr.RestServlet} now implements the {@link oajr.RestCallHandler}, {@link oajr.RestInfoProvider}, and {@link oajr.RestCallLogger}
- interfaces directly and provides the same functionality as {@link oajr.BasicRestCallHandler}, {@link oajr.BasicRestInfoProvider}, and
- {@link oajr.BasicRestCallLogger}. This makes it easier to tweak these methods without having to implement your own classes.
+ {@link oajr.RestServlet} and {@link oajr.BasicRest} now implement the {@link oajr.RestCallHandler}, {@link oajr.RestInfoProvider}, {@link oajr.RestCallLogger},
+ and {@link oaj.utils.ClasspathResourceFinder} interfaces directly and provides the same functionality as {@link oajr.BasicRestCallHandler},
+ {@link oajr.BasicRestInfoProvider}, {@link oajr.BasicRestCallLogger}, and {@link oaj.utils.ClasspathResourceFinderBasic}.
+
+ This makes it easier to tweak these methods without having to implement your own classes.
<br>The methods added for {@link oajr.RestCallHandler} are:
<ul>
- <li class='jac'>{@link oaj.RestServlet}
+ <li class='jac'>{@link oajr.RestServlet}
+ <ul>
+ <li class='jm'>{@link oajr.RestServlet#execute(HttpServletRequest, HttpServletResponse) execute(HttpServletRequest, HttpServletResponse)}
+ <li class='jm'>{@link oajr.RestServlet#createCall(HttpServletRequest, HttpServletResponse) createCall(HttpServletRequest, HttpServletResponse)}
+ <li class='jm'>{@link oajr.RestServlet#createRequest(RestCall) createRequest(RestCall)}
+ <li class='jm'>{@link oajr.RestServlet#createResponse(RestCall) createResponse(RestCall)}
+ <li class='jm'>{@link oajr.RestServlet#handleResponse(RestCall) handleResponse(RestCall)}
+ <li class='jm'>{@link oajr.RestServlet#handleNotFound(RestCall) handleNotFound(RestCall)}
+ <li class='jm'>{@link oajr.RestServlet#handleError(RestCall,Throwable) handleError(RestCall,Throwable)}
+ <li class='jm'>{@link oajr.RestServlet#convertThrowable(Throwable) throwable(Throwable)}
+ <li class='jm'>{@link oajr.RestServlet#getSessionObjects(RestRequest req, RestResponse res) getSessionObjects(RestRequest,RestResponse)}
+ </ul>
+ <li class='jac'>{@link oajr.BasicRest}
<ul>
- <li class='jm'>{@link oaj.RestServlet#execute(HttpServletRequest, HttpServletResponse) execute(HttpServletRequest, HttpServletResponse)}
- <li class='jm'>{@link oaj.RestServlet#createCall(HttpServletRequest, HttpServletResponse) createCall(HttpServletRequest, HttpServletResponse)}
- <li class='jm'>{@link oaj.RestServlet#createRequest(RestCall) createRequest(RestCall)}
- <li class='jm'>{@link oaj.RestServlet#createResponse(RestCall) createResponse(RestCall)}
- <li class='jm'>{@link oaj.RestServlet#handleResponse(RestCall) handleResponse(RestCall)}
- <li class='jm'>{@link oaj.RestServlet#handleNotFound(RestCall) handleNotFound(RestCall)}
- <li class='jm'>{@link oaj.RestServlet#handleError(RestCall,Throwable) handleError(RestCall,Throwable)}
- <li class='jm'>{@link oaj.RestServlet#convertThrowable(Throwable) throwable(Throwable)}
- <li class='jm'>{@link oaj.RestServlet#getSessionObjects(RestRequest req, RestResponse res) getSessionObjects(RestRequest,RestResponse)}
+ <li class='jm'>{@link oajr.BasicRest#execute(HttpServletRequest, HttpServletResponse) execute(HttpServletRequest, HttpServletResponse)}
+ <li class='jm'>{@link oajr.BasicRest#createCall(HttpServletRequest, HttpServletResponse) createCall(HttpServletRequest, HttpServletResponse)}
+ <li class='jm'>{@link oajr.BasicRest#createRequest(RestCall) createRequest(RestCall)}
+ <li class='jm'>{@link oajr.BasicRest#createResponse(RestCall) createResponse(RestCall)}
+ <li class='jm'>{@link oajr.BasicRest#handleResponse(RestCall) handleResponse(RestCall)}
+ <li class='jm'>{@link oajr.BasicRest#handleNotFound(RestCall) handleNotFound(RestCall)}
+ <li class='jm'>{@link oajr.BasicRest#handleError(RestCall,Throwable) handleError(RestCall,Throwable)}
+ <li class='jm'>{@link oajr.BasicRest#convertThrowable(Throwable) throwable(Throwable)}
+ <li class='jm'>{@link oajr.BasicRest#getSessionObjects(RestRequest req, RestResponse res) getSessionObjects(RestRequest,RestResponse)}
</ul>
</ul>
<br>The methods added for {@link oajr.RestInfoProvider} are:
<ul>
- <li class='jac'>{@link oaj.RestServlet}
+ <li class='jac'>{@link oajr.RestServlet}
+ <ul>
+ <li class='jm'>{@link oajr.RestServlet#getSwagger(RestRequest) getSwagger(RestRequest)}
+ <li class='jm'>{@link oajr.RestServlet#getSiteName(RestRequest) getSiteName(RestRequest)}
+ <li class='jm'>{@link oajr.RestServlet#getTitle(RestRequest) getTitle(RestRequest)}
+ <li class='jm'>{@link oajr.RestServlet#getDescription(RestRequest) getDescription(RestRequest)}
+ <li class='jm'>{@link oajr.RestServlet#getMethodSummary(Method,RestRequest) getMethodSummary(Method,RestRequest)}
+ <li class='jm'>{@link oajr.RestServlet#getMethodDescription(Method,RestRequest) getMethodDescription(Method,RestRequest)}
+ </ul>
+ <li class='jac'>{@link oajr.BasicRest}
<ul>
- <li class='jm'>{@link oaj.RestServlet#getSwagger(RestRequest) getSwagger(RestRequest)}
- <li class='jm'>{@link oaj.RestServlet#getSiteName(RestRequest) getSiteName(RestRequest)}
- <li class='jm'>{@link oaj.RestServlet#getTitle(RestRequest) getTitle(RestRequest)}
- <li class='jm'>{@link oaj.RestServlet#getDescription(RestRequest) getDescription(RestRequest)}
- <li class='jm'>{@link oaj.RestServlet#getMethodSummary(Method,RestRequest) getMethodSummary(Method,RestRequest)}
- <li class='jm'>{@link oaj.RestServlet#getMethodDescription(Method,RestRequest) getMethodDescription(Method,RestRequest)}
+ <li class='jm'>{@link oajr.BasicRest#getSwagger(RestRequest) getSwagger(RestRequest)}
+ <li class='jm'>{@link oajr.BasicRest#getSiteName(RestRequest) getSiteName(RestRequest)}
+ <li class='jm'>{@link oajr.BasicRest#getTitle(RestRequest) getTitle(RestRequest)}
+ <li class='jm'>{@link oajr.BasicRest#getDescription(RestRequest) getDescription(RestRequest)}
+ <li class='jm'>{@link oajr.BasicRest#getMethodSummary(Method,RestRequest) getMethodSummary(Method,RestRequest)}
+ <li class='jm'>{@link oajr.BasicRest#getMethodDescription(Method,RestRequest) getMethodDescription(Method,RestRequest)}
</ul>
</ul>
<br>The methods added for {@link oajr.RestCallLogger} are:
<ul>
- <li class='jac'>{@link oaj.RestServlet}
+ <li class='jac'>{@link oajr.RestServlet}
<ul>
- <li class='jm'>{@link oaj.RestServlet#log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse) log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse)}
+ <li class='jm'>{@link oajr.RestServlet#log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse) log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse)}
+ </ul>
+ <li class='jac'>{@link oajr.BasicRest}
+ <ul>
+ <li class='jm'>{@link oajr.BasicRest#log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse) log(RestCallLoggerConfig,HttpServletRequest,HttpServletResponse)}
</ul>
</ul>
<br>The methods added for {@link oaj.utils.ClassspathResourceFinder} are:
<ul>
- <li class='jac'>{@link oaj.RestServlet}
+ <li class='jac'>{@link oajr.RestServlet}
+ <ul>
+ <li class='jm'>{@link oajr.RestServlet#findResource(Class,String,Locale) findResource(Class,String,Locale)}
+ </ul>
+ <li class='jac'>{@link oajr.BasicRest}
<ul>
- <li class='jm'>{@link oaj.RestServlet#findResource(Class,String,Locale) findResource(Class,String,Locale)}
+ <li class='jm'>{@link oajr.BasicRest#findResource(Class,String,Locale) findResource(Class,String,Locale)}
</ul>
</ul>
<li>
- Added the following convenience hook methods on the {@link RestServlet} class:
+ Added the following convenience hook methods on the {@link oajr.RestServlet} and {@link oajr.BasicRest} classes:
<ul>
- <li class='jac'>{@link oaj.RestServlet}
+ <li class='jac'>{@link oajr.RestServlet}
<ul>
- <li class='jm'>{@link oaj.RestServlet#onInit(RestContextBuilder) onInit(RestContextBuilder)}
- <li class='jm'>{@link oaj.RestServlet#onPostInit(RestContext) onPostInit(RestContext)}
- <li class='jm'>{@link oaj.RestServlet#onPostInitChildFirst(RestContext) onPostInitChildFirst(RestContext)}
- <li class='jm'>{@link oaj.RestServlet#onDestroy(RestContext) onDestroy(RestContext)}
- <li class='jm'>{@link oaj.RestServlet#onStartCall(HttpServletRequest,HttpServletResponse) onStartCall(HttpServletRequest,HttpServletResponse)}
- <li class='jm'>{@link oaj.RestServlet#onPreCall(RestRequest,RestResponse) onPreCall(RestRequest,RestResponse)}
- <li class='jm'>{@link oaj.RestServlet#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
- <li class='jm'>{@link oaj.RestServlet#onEndCall(HttpServletRequest,HttpServletResponse) onEndCall(HttpServletRequest,HttpServletResponse)}
+ <li class='jm'>{@link oajr.RestServlet#onInit(RestContextBuilder) onInit(RestContextBuilder)}
+ <li class='jm'>{@link oajr.RestServlet#onPostInit(RestContext) onPostInit(RestContext)}
+ <li class='jm'>{@link oajr.RestServlet#onPostInitChildFirst(RestContext) onPostInitChildFirst(RestContext)}
+ <li class='jm'>{@link oajr.RestServlet#onDestroy(RestContext) onDestroy(RestContext)}
+ <li class='jm'>{@link oajr.RestServlet#onStartCall(HttpServletRequest,HttpServletResponse) onStartCall(HttpServletRequest,HttpServletResponse)}
+ <li class='jm'>{@link oajr.RestServlet#onPreCall(RestRequest,RestResponse) onPreCall(RestRequest,RestResponse)}
+ <li class='jm'>{@link oajr.RestServlet#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
+ <li class='jm'>{@link oajr.RestServlet#onEndCall(HttpServletRequest,HttpServletResponse) onEndCall(HttpServletRequest,HttpServletResponse)}
</ul>
+ <li class='jac'>{@link oajr.BasicRest}
+ <ul>
+ <li class='jm'>{@link oajr.BasicRest#onInit(RestContextBuilder) onInit(RestContextBuilder)}
+ <li class='jm'>{@link oajr.BasicRest#onPostInit(RestContext) onPostInit(RestContext)}
+ <li class='jm'>{@link oajr.BasicRest#onPostInitChildFirst(RestContext) onPostInitChildFirst(RestContext)}
+ <li class='jm'>{@link oajr.BasicRest#onDestroy(RestContext) onDestroy(RestContext)}
+ <li class='jm'>{@link oajr.BasicRest#onStartCall(HttpServletRequest,HttpServletResponse) onStartCall(HttpServletRequest,HttpServletResponse)}
+ <li class='jm'>{@link oajr.BasicRest#onPreCall(RestRequest,RestResponse) onPreCall(RestRequest,RestResponse)}
+ <li class='jm'>{@link oajr.BasicRest#onPostCall(RestRequest,RestResponse) onPostCall(RestRequest,RestResponse)}
+ <li class='jm'>{@link oajr.BasicRest#onEndCall(HttpServletRequest,HttpServletResponse) onEndCall(HttpServletRequest,HttpServletResponse)}
+ </ul>
+ </ul>
+ <li>
+ New {@link Rest#debugOn() @Rest(debugOn)} annotation for turning on debug mode using class/method identifiers:
+
+ <h5 class='figure'>Example:</h5>
+ <p class='bcode w800'>
+ <jc>// Turn on debug per-request on the class and always on the doX() method</jc>.
+ <ja>@Rest</ja>(
+ debugOn=<js>"MyResource=per-request,Mysource.doX=true"</js> <jc>// Typically defined via system or env property</jc>.
+ )
+ <jk>public class</jk> MyResource {
+
+ <ja>@RestMethod</ja>
+ <jk>public void</jk> String doX() {
+ ...
+ }
+ </p>
+ <li>
+ {@link oajr.BasicRestConfig} has been broken up into {@link oajr.BasicRestConfig} and {@link oajr.BasicRestMethods} so that
+ you're not forced to implement methods such as <c>getOptions()</c> and <c>getStats()</c> if you're implementing the interface
+ to configure your REST class.
+ <li>
+ Any of the following classes can now be instantiated with <c><jk>public static</jk> <jsm>create</jsm>()</c> methods:
+ <ul>
+ <li class='jc'>{@link oajr.RestCallHandler}
+ <li class='jc'>{@link oajr.RestCallLogger}
+ <li class='jc'>{@link oajr.RestInfoProvider}
+ <li class='jc'>{@link oaj.utils.ClasspathResourceFinder}
</ul>
</ul>
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/HelloWorldResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/HelloWorldResource.java
index 9d9658b..237c353 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/HelloWorldResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/HelloWorldResource.java
@@ -14,9 +14,7 @@ package org.apache.juneau.examples.rest;
import static org.apache.juneau.http.HttpMethodName.*;
-import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.html.annotation.*;
-import org.apache.juneau.http.exception.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.annotation.*;
@@ -51,27 +49,4 @@ public class HelloWorldResource implements BasicRestConfig {
public String sayHello() {
return "Hello world!";
}
-
- /**
- * Make a request to /helloWorld/badRequest to trigger a 400 Bad Request.
- *
- * @throws BadRequest A bad request.
- */
- @RestMethod
- public void getBadRequest() throws BadRequest {
- throw new BadRequest("example");
- }
-
- @Override /* BasicRestConfig */
- public Swagger getOptions(RestRequest req) {
- return req.getSwagger();
- }
-
- @Override /* BasicRestConfig */
- public void error() {}
-
- @Override /* BasicRestConfig */
- public RestContextStats getStats(RestRequest req) {
- return req.getContext().getStats();
- }
}
diff --git a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java
index e8f332c..82e0849 100644
--- a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java
+++ b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockServletRequest.java
@@ -1471,7 +1471,8 @@ public class MockServletRequest implements HttpServletRequest, MockHttpRequest {
*/
public MockServletRequest debug(boolean value) {
this.debug = value;
- header("X-Debug", value ? true : null);
+ if (value)
+ header("X-Debug", true);
return this;
}
diff --git a/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/DebugModeTest.java b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/DebugModeTest.java
new file mode 100644
index 0000000..41bed60
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-utest/src/test/java/org/apache/juneau/rest/DebugModeTest.java
@@ -0,0 +1,1195 @@
+// ***************************************************************************************************************************
+// * 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.junit.Assert.*;
+
+import java.util.logging.*;
+
+import javax.servlet.http.*;
+
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.mock2.*;
+import org.junit.*;
+import org.junit.runners.*;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class DebugModeTest {
+
+ public static final CaptureCallLogger LOGGER = new CaptureCallLogger();
+
+ public static class CaptureCallLogger extends BasicRestCallLogger {
+
+ private volatile String msg;
+
+ public static CaptureCallLogger create() {
+ return LOGGER;
+ }
+
+ private CaptureCallLogger() {
+ super(null);
+ }
+
+ @Override
+ protected synchronized void log(Level level, String msg, Throwable e) {
+ this.msg = StringUtils.emptyIfNull(msg);
+ }
+
+ synchronized CaptureCallLogger reset() {
+ this.msg = null;
+ return this;
+ }
+
+ synchronized String getMessage() {
+ return msg;
+ }
+
+ public synchronized CaptureCallLogger assertMessageContains(String s) {
+ assertNotNull(msg);
+ if (! msg.contains(s))
+ assertEquals("Substring not found.", s, msg);
+ return this;
+ }
+ }
+
+ private static void assertLogged(boolean b) {
+ assertEquals(b, LOGGER.getMessage() != null);
+ LOGGER.reset();
+ }
+
+ private static void assertLoggedContains(String s) {
+ String msg = LOGGER.getMessage();
+ assertLogged(true);
+ if (! msg.contains(s))
+ assertEquals("Substring not found.", s, msg);
+ LOGGER.reset();
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // @Rest(debug=""), various @RestMethod(debug)
+ //------------------------------------------------------------------------------------------------------------------
+
+ @Rest(callLogger=CaptureCallLogger.class)
+ public static class A1 implements BasicRestConfig {
+ @RestMethod
+ public boolean getA01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getA02(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getA03(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getA04(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="foo")
+ public boolean getA05(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA06(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA07(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest a1 = MockRest.build(A1.class);
+ static MockRest a1d = MockRest.create(A1.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void a01_debugDefault() throws Exception {
+
+ assertEquals("false", a1.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1d.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("false", a1.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1d.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a1.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+ assertEquals("true", a1d.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+
+ assertEquals("false", a1.get("/a04").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a1d.get("/a04").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a04");
+
+ assertEquals("false", a1.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1d.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a1.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a1d.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a1.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1d.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+ @Rest(callLogger=CaptureCallLogger.class)
+ public static class A1a extends BasicRest {
+ @RestMethod
+ public boolean getA01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getA02(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getA03(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getA04(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="foo")
+ public boolean getA05(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA06(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA07(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest a1a = MockRest.build(A1a.class);
+ static MockRest a1ad = MockRest.create(A1a.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void a01a_debugDefault() throws Exception {
+
+ assertEquals("false", a1a.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1ad.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("false", a1a.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1ad.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a1a.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+ assertEquals("true", a1ad.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+
+ assertEquals("false", a1a.get("/a04").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a1ad.get("/a04").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a04");
+
+ assertEquals("false", a1a.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1ad.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a1a.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a1ad.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a1a.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a1ad.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // @Rest(debug="true"), various @RestMethod(debug)
+ //------------------------------------------------------------------------------------------------------------------
+
+ @Rest(callLogger=CaptureCallLogger.class, debug="true")
+ public static class A2 implements BasicRestConfig {
+ @RestMethod
+ public boolean getA01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getA02(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getA03(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getA04(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="foo")
+ public boolean getA05(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA06(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA07(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest a2 = MockRest.build(A2.class);
+ static MockRest a2d = MockRest.create(A2.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void a02_debugTrue() throws Exception {
+
+ assertEquals("true", a2.get("/a01").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a2d.get("/a01").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a2.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a2d.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a2.get("/a03").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a2d.get("/a03").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a2.get("/a04").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a2d.get("/a04").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("true", a2.get("/a05").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a2d.get("/a05").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("true", a2.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a2d.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a2.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a2d.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // @Rest(debug="false"), various @RestMethod(debug)
+ //------------------------------------------------------------------------------------------------------------------
+
+ @Rest(callLogger=CaptureCallLogger.class,debug="false")
+ public static class A3 implements BasicRestConfig {
+ @RestMethod
+ public boolean getA01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getA02(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getA03(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getA04(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="foo")
+ public boolean getA05(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA06(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA07(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest a3 = MockRest.build(A3.class);
+ static MockRest a3d = MockRest.create(A3.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void a03_restDebugFalse() throws Exception {
+
+ assertEquals("false", a3.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a3d.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("false", a3.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a3d.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a3.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+ assertEquals("true", a3d.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+
+ assertEquals("false", a3.get("/a04").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a3d.get("/a04").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a04");
+
+ assertEquals("false", a3.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a3d.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a3.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a3d.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a3.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a3d.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // @Rest(debug="per-request"), various @RestMethod(debug)
+ //------------------------------------------------------------------------------------------------------------------
+
+ @Rest(callLogger=CaptureCallLogger.class,debug="per-request")
+ public static class A4 implements BasicRestConfig {
+ @RestMethod
+ public boolean getA01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getA02(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getA03(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getA04(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="foo")
+ public boolean getA05(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA06(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getA07(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest a4 = MockRest.build(A4.class);
+ static MockRest a4d = MockRest.create(A4.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void a04_debugPerRequest() throws Exception {
+
+ assertEquals("false", a4.get("/a01").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a4d.get("/a01").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a4.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a4d.get("/a02").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", a4.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+ assertEquals("true", a4d.get("/a03").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a03");
+
+ assertEquals("false", a4.get("/a04").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a4d.get("/a04").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /a04");
+
+ assertEquals("false", a4.get("/a05").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", a4d.get("/a05").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("true", a4.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", a4d.get("/a06").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", a4.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", a4d.get("/a07").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // Implement RestCallLogger directly.
+ //------------------------------------------------------------------------------------------------------------------
+
+ @Rest
+ public static class B1 implements BasicRestConfig, RestCallLogger {
+ @RestMethod
+ public boolean getB01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getB02(RestRequest req) {
+ return req.isDebug();
+ }
+ @Override
+ public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
+ LOGGER.log(config, req, res);
+ }
+ }
+ static MockRest b1 = MockRest.build(B1.class);
+
+ @Test
+ public void b01_debugDefault() throws Exception {
+
+ assertEquals("false", b1.get("/b01").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", b1.get("/b02").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /b02");
+ }
+
+ @Rest
+ public static class B2 extends BasicRest {
+ @RestMethod
+ public boolean getB01(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getB02(RestRequest req) {
+ return req.isDebug();
+ }
+ @Override
+ public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
+ LOGGER.log(config, req, res);
+ }
+ }
+ static MockRest b2 = MockRest.build(B2.class);
+
+ @Test
+ public void b02_debugDefault() throws Exception {
+
+ assertEquals("false", b2.get("/b01").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", b2.get("/b02").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /b02");
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ // @Rest(debugOn=""), various @RestMethod(debug)
+ //------------------------------------------------------------------------------------------------------------------
+
+ @Rest(
+ callLogger=CaptureCallLogger.class,
+ debugOn=""
+ + "C1.getC02a=false,C1.getC02b=false,C1.getC02c=FALSE,C1.getC02d=FALSE,C1.getC02e=FALSE,C1.getC02f=FALSE,"
+ + " C1.getC03a , C1.getC03b = true , C1.getC03c = TRUE , C1.getC03d = TRUE , C1.getC03e = TRUE , C1.getC03f = TRUE , "
+ + "C1.getC04a=per-request,C1.getC04b=per-request,C1.getC04c=PER-REQUEST,C1.getC04d=PER-REQUEST,C1.getC04e=PER-REQUEST,C1.getC04f=PER-REQUEST,"
+ + "C1.getC05a=foo,C1.getC05b=,C1.getC05c=foo,C1.getC05d=foo,C1.getC05e=foo,C1.getC05f=foo,"
+ )
+ public static class C1 implements BasicRestConfig {
+
+ @RestMethod
+ public boolean getC01a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC01b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC01c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC01d(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=false
+ @RestMethod
+ public boolean getC02a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC02b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC02c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC02d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC02e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC02f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=true
+ @RestMethod
+ public boolean getC03a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC03b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC03c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC03d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC03e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC03f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=per-request
+ @RestMethod
+ public boolean getC04a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC04b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC04c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC04d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC04e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC04f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=foo
+ @RestMethod
+ public boolean getC05a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC05b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC05c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC05d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC05e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC05f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ @RestMethod
+ public boolean getC06a(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC06b(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC06c(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC06d(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+
+ @RestMethod
+ public boolean getC07a(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC07b(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC07c(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC07d(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest c1 = MockRest.build(C1.class);
+ static MockRest c1d = MockRest.create(C1.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void c01_debugDefault() throws Exception {
+
+ assertEquals("false", c1.get("/c01a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c01a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c01b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c01b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1.get("/c01c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1d.get("/c01c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("false", c1.get("/c01d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c01d").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", c1.get("/c02a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c02a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c02b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c02b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c02c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c02c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c02d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c02d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c02e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c02e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c02f").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c02f").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", c1.get("/c03a").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03a");
+ assertEquals("true", c1d.get("/c03a").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03a");
+ assertEquals("true", c1.get("/c03b").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03b");
+ assertEquals("true", c1d.get("/c03b").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03b");
+ assertEquals("true", c1.get("/c03c").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03c");
+ assertEquals("true", c1d.get("/c03c").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03c");
+ assertEquals("true", c1.get("/c03d").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03d");
+ assertEquals("true", c1d.get("/c03d").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03d");
+ assertEquals("true", c1.get("/c03e").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03e");
+ assertEquals("true", c1d.get("/c03e").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03e");
+ assertEquals("true", c1.get("/c03f").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03f");
+ assertEquals("true", c1d.get("/c03f").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03f");
+
+ assertEquals("false", c1.get("/c04a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c04a").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04a");
+ assertEquals("false", c1.get("/c04b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c04b").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04b");
+ assertEquals("false", c1.get("/c04c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c04c").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04c");
+ assertEquals("false", c1.get("/c04d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c04d").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04d");
+ assertEquals("false", c1.get("/c04e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c04e").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04e");
+ assertEquals("false", c1.get("/c04f").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c1d.get("/c04f").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04f");
+
+ assertEquals("false", c1.get("/c05a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c05a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c05b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c05b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c05c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c05c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c05d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c05d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c05e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c05e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c05f").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c05f").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", c1.get("/c06a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1d.get("/c06a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1.get("/c06b").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1d.get("/c06b").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1.get("/c06c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1d.get("/c06c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1.get("/c06d").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c1d.get("/c06d").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", c1.get("/c07a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c07a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c07b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c07b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c07c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c07c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1.get("/c07d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c1d.get("/c07d").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+ static {
+ System.setProperty("C2DebugEnabled", "C2=true");
+ }
+ @Rest(
+ callLogger=CaptureCallLogger.class,
+ debugOn="$S{C2DebugEnabled},"
+ + "C2.getC02a=false,C2.getC02b=false,C2.getC02c=FALSE,C2.getC02d=FALSE,C2.getC02e=FALSE,C2.getC02f=FALSE,"
+ + " C2.getC03a , C2.getC03b = true , C2.getC03c = TRUE , C2.getC03d = TRUE , C2.getC03e = TRUE , C2.getC03f = TRUE , "
+ + "C2.getC04a=per-request,C2.getC04b=per-request,C2.getC04c=PER-REQUEST,C2.getC04d=PER-REQUEST,C2.getC04e=PER-REQUEST,C2.getC04f=PER-REQUEST,"
+ + "C2.getC05a=foo,C2.getC05b=,C2.getC05c=foo,C2.getC05d=foo,C2.getC05e=foo,C2.getC05f=foo,"
+ )
+ public static class C2 implements BasicRestConfig {
+
+ @RestMethod
+ public boolean getC01a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC01b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC01c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC01d(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=false
+ @RestMethod
+ public boolean getC02a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC02b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC02c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC02d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC02e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC02f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=true
+ @RestMethod
+ public boolean getC03a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC03b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC03c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC03d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC03e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC03f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=per-request
+ @RestMethod
+ public boolean getC04a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC04b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC04c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC04d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC04e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC04f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ // debug=foo
+ @RestMethod
+ public boolean getC05a(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC05b(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod
+ public boolean getC05c(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC05d(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC05e(RestRequest req) {
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC05f(RestRequest req) {
+ return req.isDebug();
+ }
+
+ @RestMethod
+ public boolean getC06a(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC06b(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC06c(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC06d(RestRequest req) throws Exception {
+ req.setDebug();
+ return req.isDebug();
+ }
+
+ @RestMethod
+ public boolean getC07a(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ @RestMethod(debug="false")
+ public boolean getC07b(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ @RestMethod(debug="true")
+ public boolean getC07c(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ @RestMethod(debug="per-request")
+ public boolean getC07d(RestRequest req) throws Exception {
+ req.setDebug(false);
+ return req.isDebug();
+ }
+ }
+ static MockRest c2 = MockRest.build(C2.class);
+ static MockRest c2d = MockRest.create(C2.class).simpleJson().header("X-Debug", true).build();
+
+ @Test
+ public void c02_debugTrue() throws Exception {
+
+ assertEquals("true", c2.get("/c01a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c01a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("false", c2.get("/c01b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c01b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2.get("/c01c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c01c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("false", c2.get("/c01d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c01d").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", c2.get("/c02a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c02a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c02b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c02b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c02c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c02c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c02d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c02d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c02e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c02e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c02f").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c02f").execute().getBodyAsString());
+ assertLogged(false);
+
+ assertEquals("true", c2.get("/c03a").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03a");
+ assertEquals("true", c2d.get("/c03a").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03a");
+ assertEquals("true", c2.get("/c03b").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03b");
+ assertEquals("true", c2d.get("/c03b").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03b");
+ assertEquals("true", c2.get("/c03c").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03c");
+ assertEquals("true", c2d.get("/c03c").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03c");
+ assertEquals("true", c2.get("/c03d").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03d");
+ assertEquals("true", c2d.get("/c03d").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03d");
+ assertEquals("true", c2.get("/c03e").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03e");
+ assertEquals("true", c2d.get("/c03e").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03e");
+ assertEquals("true", c2.get("/c03f").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03f");
+ assertEquals("true", c2d.get("/c03f").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c03f");
+
+ assertEquals("false", c2.get("/c04a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c04a").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04a");
+ assertEquals("false", c2.get("/c04b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c04b").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04b");
+ assertEquals("false", c2.get("/c04c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c04c").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04c");
+ assertEquals("false", c2.get("/c04d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c04d").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04d");
+ assertEquals("false", c2.get("/c04e").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c04e").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04e");
+ assertEquals("false", c2.get("/c04f").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("true", c2d.get("/c04f").execute().getBodyAsString());
+ assertLoggedContains("[200] HTTP GET /c04f");
+
+ assertEquals("true", c2.get("/c05a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c05a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c05b").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c05b").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c05c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c05c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c05d").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c05d").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c05e").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c05e").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c05f").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c05f").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("true", c2.get("/c06a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c06a").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c06b").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c06b").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c06c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c06c").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2.get("/c06d").execute().getBodyAsString());
+ assertLogged(true);
+ assertEquals("true", c2d.get("/c06d").execute().getBodyAsString());
+ assertLogged(true);
+
+ assertEquals("false", c2.get("/c07a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c07a").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c07b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c07b").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c07c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c07c").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2.get("/c07d").execute().getBodyAsString());
+ assertLogged(false);
+ assertEquals("false", c2d.get("/c07d").execute().getBodyAsString());
+ assertLogged(false);
+ }
+
+}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java
index f7f5c7f..65c4515 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRest.java
@@ -12,15 +12,22 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
+import static org.apache.juneau.rest.annotation.HookEvent.*;
+
+import java.io.*;
+import java.lang.reflect.Method;
import java.text.*;
+import java.util.*;
import java.util.logging.*;
+import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.html.annotation.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.utils.*;
import org.apache.juneau.http.exception.*;
/**
@@ -42,29 +49,35 @@ import org.apache.juneau.http.exception.*;
"stats: servlet:/stats"
}
)
-public abstract class BasicRest implements BasicRestConfig {
+public abstract class BasicRest implements BasicRestConfig, BasicRestMethods, RestCallHandler, RestInfoProvider, RestCallLogger, ClasspathResourceFinder {
private JuneauLogger logger = JuneauLogger.getLogger(getClass());
private volatile RestContext context;
+ private RestCallHandler callHandler;
+ private RestInfoProvider infoProvider;
+ private RestCallLogger callLogger;
+ private ClasspathResourceFinder resourceFinder;
/**
- * Post-initialization hook to retrieve the {@link RestContext} object for this resource.
+ * [OPTIONS /*] - Show resource options.
*
- * @param context The context for this resource.
+ * @param req The HTTP request.
+ * @return A bean containing the contents for the OPTIONS page.
*/
- @RestHook(HookEvent.POST_INIT)
- public synchronized void onPostInit(RestContext context) {
- this.context = context;
+ @Override /* BasicRestConfig */
+ public Swagger getOptions(RestRequest req) {
+ // Localized Swagger for this resource is available through the RestRequest object.
+ return req.getSwagger();
}
/**
- * [OPTIONS /*] - Show resource options.
+ * [GET /options] - Show resource options.
*
* @param req The HTTP request.
* @return A bean containing the contents for the OPTIONS page.
*/
@Override /* BasicRestConfig */
- public Swagger getOptions(RestRequest req) {
+ public Swagger getOptions2(RestRequest req) {
// Localized Swagger for this resource is available through the RestRequest object.
return req.getSwagger();
}
@@ -169,13 +182,292 @@ public abstract class BasicRest implements BasicRestConfig {
}
//-----------------------------------------------------------------------------------------------------------------
- // Request-time methods.
+ // Hook events
+ //-----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Method that gets called during servlet initialization.
+ *
+ * <p>
+ * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContextBuilder}
+ * object has been created and initialized with the annotations defined on the class, but before the
+ * {@link RestContext} object has been created.
+ *
+ * <p>
+ * An example of this is the <c>PetStoreResource</c> class that uses an init method to perform initialization
+ * of an internal data structure.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * <ja>@Rest</ja>(...)
+ * <jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
+ *
+ * <jc>// Our database.</jc>
+ * <jk>private</jk> Map<Integer,Pet> <jf>petDB</jf>;
+ *
+ * <ja>@Override</ja>
+ * <jk>public void</jk> onInit(RestContextBuilder builder) <jk>throws</jk> Exception {
+ * <jc>// Load our database from a local JSON file.</jc>
+ * <jf>petDB</jf> = JsonParser.<jsf>DEFAULT</jsf>.parse(getClass().getResourceAsStream(<js>"PetStore.json"</js>), LinkedHashMap.<jk>class</jk>, Integer.<jk>class</jk>, Pet.<jk>class</jk>);
+ * }
+ * }
+ * </p>
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple INIT methods can be defined on a class.
+ * <br>INIT methods on parent classes are invoked before INIT methods on child classes.
+ * <br>The order of INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception causing initialization of the servlet to fail.
+ * </ul>
+ *
+ * @param builder Context builder which can be used to configure the servlet.
+ * @throws Exception Any exception thrown will cause servlet to fail startup.
+ */
+ @RestHook(INIT)
+ public void onInit(RestContextBuilder builder) throws Exception {}
+
+ /**
+ * Method that gets called immediately after servlet initialization.
+ *
+ * <p>
+ * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
+ * object has been created.
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple POST_INIT methods can be defined on a class.
+ * <br>POST_INIT methods on parent classes are invoked before POST_INIT methods on child classes.
+ * <br>The order of POST_INIT method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception causing initialization of the servlet to fail.
+ * </ul>
+ *
+ * @param context The initialized context object.
+ * @throws Exception Any exception thrown will cause servlet to fail startup.
+ */
+ @RestHook(POST_INIT)
+ public void onPostInit(RestContext context) throws Exception {
+ this.context = context;
+ this.callHandler = new BasicRestCallHandler(context);
+ this.infoProvider = new BasicRestInfoProvider(context);
+ this.callLogger = new BasicRestCallLogger(context);
+ this.resourceFinder = new ClasspathResourceFinderBasic();
+ }
+
+ /**
+ * Identical to {@link #onPostInit(RestContext)} except the order of execution is child-resources first.
+ *
+ * <p>
+ * Use this method if you need to perform any kind of initialization on child resources before the parent resource.
+ *
+ * <p>
+ * This method is called from within the {@link Servlet#init(ServletConfig)} method after the {@link RestContext}
+ * object has been created and after the {@link #POST_INIT} methods have been called.
+ *
+ * <p>
+ * The only valid parameter type for this method is {@link RestContext} which can be used to retrieve information
+ * about the servlet.
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple POST_INIT_CHILD_FIRST methods can be defined on a class.
+ * <br>POST_INIT_CHILD_FIRST methods on parent classes are invoked before POST_INIT_CHILD_FIRST methods on child classes.
+ * <br>The order of POST_INIT_CHILD_FIRST method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception causing initialization of the servlet to fail.
+ * </ul>
+ *
+ * @param context The initialized context object.
+ * @throws Exception Any exception thrown will cause servlet to fail startup.
+ */
+ @RestHook(POST_INIT_CHILD_FIRST)
+ public void onPostInitChildFirst(RestContext context) throws Exception {}
+
+ /**
+ * Method that gets called during servlet destroy.
+ *
+ * <p>
+ * This method is called from within the {@link Servlet#destroy()}.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * <ja>@Rest</ja>(...)
+ * <jk>public class</jk> PetStoreResource <jk>extends</jk> ResourceJena {
+ *
+ * <jc>// Our database.</jc>
+ * <jk>private</jk> Map<Integer,Pet> <jf>petDB</jf>;
+ *
+ * <ja>@Override</ja>
+ * <jk>public void</jk> onDestroy(RestContext context) {
+ * <jf>petDB</jf> = <jk>null</jk>;
+ * }
+ * }
+ * </p>
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple DESTROY methods can be defined on a class.
+ * <br>DESTROY methods on child classes are invoked before DESTROY methods on parent classes.
+ * <br>The order of DESTROY method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * In general, destroy methods should not throw any exceptions, although if any are thrown, the stack trace will be
+ * printed to <c>System.err</c>.
+ * </ul>
+ *
+ * @param context The initialized context object.
+ * @throws Exception Any exception thrown will cause stack trace to be printed to <c>System.err</c>.
+ */
+ @RestHook(DESTROY)
+ public void onDestroy(RestContext context) throws Exception {}
+
+ /**
+ * A method that is called immediately after the <c>HttpServlet.service(HttpServletRequest, HttpServletResponse)</c>
+ * method is called.
+ *
+ * <p>
+ * Note that you only have access to the raw request and response objects at this point.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * <ja>@Rest</ja>(...)
+ * <jk>public class</jk> MyResource <jk>extends</jk> BasicRestServlet {
+ *
+ * <jc>// Add a request attribute to all incoming requests.</jc>
+ * <ja>@Override</ja>
+ * <jk>public void</jk> onStartCall(HttpServletRequest req, HttpServletResponse res) {
+ * req.setAttribute(<js>"foobar"</js>, <jk>new</jk> FooBar());
+ * }
+ * }
+ * </p>
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple START_CALL methods can be defined on a class.
+ * <br>START_CALL methods on parent classes are invoked before START_CALL methods on child classes.
+ * <br>The order of START_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception.
+ * <br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
+ * <br>All other exceptions cause an HTTP 500 error status code.
+ * </ul>
+ *
+ * @param req The HTTP servlet request object.
+ * @param res The HTTP servlet response object.
+ * @throws Exception Any exception.
+ */
+ @RestHook(START_CALL)
+ public void onStartCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
+
+ /**
+ * Method that gets called immediately before the <ja>@RestMethod</ja> annotated method gets called.
+ *
+ * <p>
+ * At this point, the {@link RestRequest} object has been fully initialized, and all {@link RestGuard} and
+ * {@link RestMatcher} objects have been called.
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple PRE_CALL methods can be defined on a class.
+ * <br>PRE_CALL methods on parent classes are invoked before PRE_CALL methods on child classes.
+ * <br>The order of PRE_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception.
+ * <br>{@link HttpException HttpExceptions} can be thrown to cause a particular HTTP error status code.
+ * <br>All other exceptions cause an HTTP 500 error status code.
+ * <li>
+ * It's advisable not to mess around with the HTTP body itself since you may end up consuming the body
+ * before the actual REST method has a chance to use it.
+ * </ul>
+ *
+ * @param req The request object.
+ * @param res The response object.
+ * @throws Exception Any exception.
+ */
+ @RestHook(PRE_CALL)
+ public void onPreCall(RestRequest req, RestResponse res) throws Exception {}
+
+ /**
+ * Method that gets called immediately after the <ja>@RestMethod</ja> annotated method gets called.
+ *
+ * <p>
+ * At this point, the output object returned by the method call has been set on the response, but
+ * {@link RestConverter RestConverters} have not yet been executed and the response has not yet been written.
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple POST_CALL methods can be defined on a class.
+ * <br>POST_CALL methods on parent classes are invoked before POST_CALL methods on child classes.
+ * <br>The order of POST_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception, although at this point it is too late to set an HTTP error status code.
+ * </ul>
+ *
+ * @param req The request object.
+ * @param res The response object.
+ * @throws Exception Any exception.
+ */
+ @RestHook(POST_CALL)
+ public void onPostCall(RestRequest req, RestResponse res) throws Exception {}
+
+ /**
+ * Method that gets called right before we exit the servlet service method.
+ *
+ * <p>
+ * At this point, the output has been written and flushed.
+ *
+ * <p>
+ * The following attributes are set on the {@link HttpServletRequest} object that can be useful for logging purposes:
+ * <ul>
+ * <li><js>"Exception"</js> - Any exceptions thrown during the request.
+ * <li><js>"ExecTime"</js> - Execution time of the request.
+ * </ul>
+ *
+ * <ul class='notes'>
+ * <li>
+ * The default implementation of this method is a no-op.
+ * <li>
+ * Multiple END_CALL methods can be defined on a class.
+ * <br>END_CALL methods on parent classes are invoked before END_CALL methods on child classes.
+ * <br>The order of END_CALL method invocations within a class is alphabetical, then by parameter count, then by parameter types.
+ * <li>
+ * The method can throw any exception, although at this point it is too late to set an HTTP error status code.
+ * <li>
+ * Note that if you override a parent method, you probably need to call <code><jk>super</jk>.parentMethod(...)</code>.
+ * <br>The method is still considered part of the parent class for ordering purposes even though it's
+ * overridden by the child class.
+ * </ul>
+ *
+ * @param req The HTTP servlet request object.
+ * @param res The HTTP servlet response object.
+ * @throws Exception Any exception.
+ */
+ @RestHook(END_CALL)
+ public void onEndCall(HttpServletRequest req, HttpServletResponse res) throws Exception {}
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // Other methods.
//-----------------------------------------------------------------------------------------------------------------
/**
* Returns the current HTTP request.
*
- * @return The current HTTP request.
+ * @return The current HTTP request, or <jk>null</jk> if it wasn't created.
*/
public synchronized RestRequest getRequest() {
return getContext().getRequest();
@@ -184,9 +476,110 @@ public abstract class BasicRest implements BasicRestConfig {
/**
* Returns the current HTTP response.
*
- * @return The current HTTP response
+ * @return The current HTTP response, or <jk>null</jk> if it wasn't created.
*/
public synchronized RestResponse getResponse() {
return getContext().getResponse();
}
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // RestCallHandler
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Override /* RestCallHandler */
+ public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
+ callHandler.execute(req, res);
+ }
+
+ @Override /* RestCallHandler */
+ public RestCall createCall(HttpServletRequest req, HttpServletResponse res) {
+ return callHandler.createCall(req, res);
+ }
+
+ @Override /* RestCallHandler */
+ public RestRequest createRequest(RestCall call) throws ServletException {
+ return callHandler.createRequest(call);
+ }
+
+ @Override /* RestCallHandler */
+ public RestResponse createResponse(RestCall call) throws ServletException {
+ return callHandler.createResponse(call);
+ }
+
+ @Override /* RestCallHandler */
+ public void handleResponse(RestCall call) throws Exception {
+ callHandler.handleResponse(call);
+ }
+
+ @Override /* RestCallHandler */
+ public void handleNotFound(RestCall call) throws Exception {
+ callHandler.handleNotFound(call);
+ }
+
+ @Override /* RestCallHandler */
+ public void handleError(RestCall call, Throwable e) throws Exception {
+ callHandler.handleError(call, e);
+ }
+
+ @Override /* RestCallHandler */
+ public Throwable convertThrowable(Throwable t) {
+ return callHandler.convertThrowable(t);
+ }
+
+ @Override /* RestCallHandler */
+ public Map<String,Object> getSessionObjects(RestRequest req, RestResponse res) {
+ return callHandler.getSessionObjects(req, res);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // RestInfoProvider
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Override /* RestInfoProvider */
+ public Swagger getSwagger(RestRequest req) throws Exception {
+ return infoProvider.getSwagger(req);
+ }
+
+ @Override /* RestInfoProvider */
+ public String getSiteName(RestRequest req) throws Exception {
+ return infoProvider.getSiteName(req);
+ }
+
+ @Override /* RestInfoProvider */
+ public String getTitle(RestRequest req) throws Exception {
+ return infoProvider.getTitle(req);
+ }
+
+ @Override /* RestInfoProvider */
+ public String getDescription(RestRequest req) throws Exception {
+ return infoProvider.getDescription(req);
+ }
+
+ @Override /* RestInfoProvider */
+ public String getMethodSummary(Method method, RestRequest req) throws Exception {
+ return infoProvider.getMethodSummary(method, req);
+ }
+
+ @Override /* RestInfoProvider */
+ public String getMethodDescription(Method method, RestRequest req) throws Exception {
+ return infoProvider.getMethodDescription(method, req);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // RestCallLogger
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Override /* RestCallLogger */
+ public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
+ callLogger.log(config, req, res);
+ }
+
+ //-----------------------------------------------------------------------------------------------------------------
+ // ClasspathResourceFinder
+ //-----------------------------------------------------------------------------------------------------------------
+
+ @Override /* ClasspathResourceFinder */
+ public InputStream findResource(Class<?> baseClass, String name, Locale locale) throws IOException {
+ return resourceFinder.findResource(baseClass, name, locale);
+ }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 9a4be75..3c5a964 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -136,7 +136,8 @@ public class BasicRestCallHandler implements RestCallHandler {
}
}
- call.debug(isDebug(call));
+ if (isDebug(call))
+ call.debug(true);
context.startCall(call);
@@ -209,12 +210,19 @@ public class BasicRestCallHandler implements RestCallHandler {
}
private boolean isDebug(RestCall call) {
- Enablement e = context.getDebug();
+ Enablement e = null;
+ RestMethodContext mc = call.getRestMethodContext();
+ if (mc != null)
+ e = mc.getDebug();
+ if (e == null)
+ e = context.getDebug();
if (e == TRUE)
return true;
if (e == FALSE)
return false;
- return "true".equalsIgnoreCase(call.getRequest().getHeader("X-Debug"));
+ if (e == PER_REQUEST)
+ return "true".equalsIgnoreCase(call.getRequest().getHeader("X-Debug"));
+ return false;
}
/**
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
index 21b6121..483e6ae 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallLogger.java
@@ -50,9 +50,9 @@ public class BasicRestCallLogger implements RestCallLogger {
*/
public BasicRestCallLogger(RestContext context) {
this.context = context;
- this.loggerName = context.getResource().getClass().getName();
+ this.loggerName = context == null ? getClass().getName() : context.getResource().getClass().getName();
this.logger = Logger.getLogger(getLoggerName());
- this.stackTraceDb = context.getStackTraceDb();
+ this.stackTraceDb = context == null ? null : context.getStackTraceDb();
}
/**
@@ -102,7 +102,8 @@ public class BasicRestCallLogger implements RestCallLogger {
* @return This object (for method chaining).
*/
public BasicRestCallLogger resetStackTraces() {
- stackTraceDb.reset();
+ if (stackTraceDb != null)
+ stackTraceDb.reset();
return this;
}
@@ -229,7 +230,21 @@ public class BasicRestCallLogger implements RestCallLogger {
sb.append("\n=== END ======================================================================");
}
- getLogger().log(level, sb.toString(), e);
+ log(level, sb.toString(), e);
+ }
+
+ /**
+ * Logs the specified message to the logger.
+ *
+ * <p>
+ * Subclasses can override this method to capture messages being sent to the logger.
+ *
+ * @param level The log level.
+ * @param msg The log message.
+ * @param e The exception.
+ */
+ protected void log(Level level, String msg, Throwable e) {
+ getLogger().log(level, msg, e);
}
private byte[] getRequestBody(HttpServletRequest req) {
@@ -245,7 +260,7 @@ public class BasicRestCallLogger implements RestCallLogger {
}
private StackTraceInfo getStackTraceInfo(RestCallLoggerConfig config, Throwable e) {
- if (e == null || ! config.isUseStackTraceHashing())
+ if (e == null || stackTraceDb == null || ! config.isUseStackTraceHashing())
return null;
stackTraceDb.add(e);
return stackTraceDb.getStackTraceInfo(e);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java
index 3611372..6f9f5c0 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java
@@ -12,15 +12,10 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
-import static org.apache.juneau.http.HttpMethodName.*;
-
import org.apache.juneau.annotation.*;
-import org.apache.juneau.dto.swagger.*;
-import org.apache.juneau.dto.swagger.ui.*;
import org.apache.juneau.html.*;
import org.apache.juneau.html.annotation.*;
import org.apache.juneau.json.*;
-import org.apache.juneau.jsonschema.annotation.*;
import org.apache.juneau.msgpack.*;
import org.apache.juneau.oapi.*;
import org.apache.juneau.plaintext.*;
@@ -103,9 +98,7 @@ import org.apache.juneau.xmlschema.XmlSchemaDocSerializer;
// Basic page navigation links.
navlinks={
- "up: request:/..",
- "options: servlet:/?method=OPTIONS",
- "stats: servlet:/stats"
+ "up: request:/.."
},
// Default stylesheet to use for the page.
@@ -124,92 +117,9 @@ import org.apache.juneau.xmlschema.XmlSchemaDocSerializer;
// By default, table cell contents should not wrap.
nowrap="true"
)
-@JsonSchemaConfig(
- // Add descriptions to the following types when not specified:
- addDescriptionsTo="bean,collection,array,map,enum",
- // Add x-example to the following types:
- addExamplesTo="bean,collection,array,map",
- // Don't generate schema information on the Swagger bean itself or HTML beans.
- ignoreTypes="Swagger,org.apache.juneau.dto.html5.*",
- // Use $ref references for bean definitions to reduce duplication in Swagger.
- useBeanDefs="true"
-)
@BeanConfig(
// When parsing generated beans, ignore unknown properties that may only exist as getters and not setters.
- ignoreUnknownBeanProperties="true",
- // POJO swaps to apply to all serializers/parsers on this method.
- pojoSwaps={
- // Use the SwaggerUI swap when rendering Swagger beans.
- // This is a per-media-type swap that only applies to text/html requests.
- SwaggerUI.class
- }
+ ignoreUnknownBeanProperties="true"
)
@SuppressWarnings("deprecation")
-public interface BasicRestConfig {
-
- /**
- * [OPTIONS /*] - Show resource options.
- *
- * @param req The HTTP request.
- * @return A bean containing the contents for the OPTIONS page.
- */
- @RestMethod(name=OPTIONS, path="/*",
- summary="Swagger documentation",
- description="Swagger documentation for this resource."
- )
- @HtmlDocConfig(
- // Should override config annotations defined on class.
- rank=10,
- // Override the nav links for the swagger page.
- navlinks={
- "back: servlet:/",
- "json: servlet:/?method=OPTIONS&Accept=text/json&plainText=true"
- },
- // Never show aside contents of page inherited from class.
- aside="NONE"
- )
- public Swagger getOptions(RestRequest req);
-
- /**
- * [* /error] - Error occurred.
- *
- * <p>
- * Servlet chains will often automatically redirect to <js>"/error"</js> when any sort of error condition occurs
- * (such as failed authentication) and will set appropriate response parameters (such as an <c>WWW-Authenticate</c>
- * response header).
- *
- * <p>
- * These responses should be left as-is without any additional processing.
- */
- @RestMethod(name=ANY, path="/error",
- summary="Error occurred",
- description="An error occurred during handling of the request."
- )
- public void error();
-
- /**
- * [GET /stats] - Timing statistics.
- *
- * <p>
- * Timing statistics for method invocations on this resource.
- *
- * @param req The HTTP request.
- * @return A collection of timing statistics for each annotated method on this resource.
- */
- @RestMethod(name=GET, path="/stats",
- summary="Timing statistics",
- description="Timing statistics for method invocations on this resource."
- )
- @HtmlDocConfig(
- // Should override config annotations defined on class.
- rank=10,
- // Override the nav links for the swagger page.
- navlinks={
- "back: servlet:/",
- "json: servlet:/stats?Accept=text/json&plainText=true"
- },
- // Never show aside contents of page inherited from class.
- aside="NONE"
- )
- public RestContextStats getStats(RestRequest req);
-}
+public interface BasicRestConfig {}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestMethods.java
similarity index 59%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java
copy to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestMethods.java
index 3611372..02d0edf 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestMethods.java
@@ -17,112 +17,22 @@ import static org.apache.juneau.http.HttpMethodName.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.dto.swagger.*;
import org.apache.juneau.dto.swagger.ui.*;
-import org.apache.juneau.html.*;
import org.apache.juneau.html.annotation.*;
-import org.apache.juneau.json.*;
import org.apache.juneau.jsonschema.annotation.*;
-import org.apache.juneau.msgpack.*;
-import org.apache.juneau.oapi.*;
-import org.apache.juneau.plaintext.*;
import org.apache.juneau.rest.annotation.*;
-import org.apache.juneau.serializer.annotation.*;
-import org.apache.juneau.soap.*;
-import org.apache.juneau.uon.*;
-import org.apache.juneau.urlencoding.*;
-import org.apache.juneau.xml.*;
-import org.apache.juneau.xmlschema.XmlSchemaDocSerializer;
/**
- * Basic configuration for a REST resource.
- *
- * <p>
- * Classes that don't extend from {@link BasicRestServlet} can implement this interface to
- * be configured with the same serializers/parsers/etc... as {@link BasicRestServlet}.
+ * TODO
*/
-@Rest(
-
- // Default serializers for all Java methods in the class.
- serializers={
- HtmlDocSerializer.class, // HTML must be listed first because Internet Explore does not include text/html in their Accept header.
- HtmlStrippedDocSerializer.class,
- HtmlSchemaDocSerializer.class,
- JsonSerializer.class,
- SimpleJsonSerializer.class,
- JsonSchemaSerializer.class,
- XmlDocSerializer.class,
- XmlSchemaDocSerializer.class,
- UonSerializer.class,
- UrlEncodingSerializer.class,
- OpenApiSerializer.class,
- MsgPackSerializer.class,
- SoapXmlSerializer.class,
- PlainTextSerializer.class
- },
-
- // Default parsers for all Java methods in the class.
- parsers={
- JsonParser.class,
- JsonParser.Simple.class,
- XmlParser.class,
- HtmlParser.class,
- UonParser.class,
- UrlEncodingParser.class,
- OpenApiParser.class,
- MsgPackParser.class,
- PlainTextParser.class
- },
-
- // Optional external configuration file.
- config="$S{juneau.configFile,SYSTEM_DEFAULT}",
-
- // These are static files that are served up by the servlet under the specified sub-paths.
- // For example, "/servletPath/htdocs/javadoc.css" resolves to the file "[servlet-package]/htdocs/javadoc.css"
- // By default, we define static files through the external configuration file.
- staticFiles="$C{REST/staticFiles,htdocs:/htdocs,htdocs:htdocs}",
-
- logging=@Logging(
- level="INFO",
- useStackTraceHashing="true",
- rules={
- @LoggingRule(codes="500-", level="WARNING")
- }
- )
-)
-@SerializerConfig(
- // Enable automatic resolution of URI objects to root-relative values.
- uriResolution="ROOT_RELATIVE"
-)
@HtmlDocConfig(
- // Default page header contents.
- header={
- "<h1>$R{resourceTitle}</h1>", // Use @Rest(title)
- "<h2>$R{methodSummary,resourceDescription}</h2>", // Use either @RestMethod(summary) or @Rest(description)
- "$C{REST/header}" // Extra header HTML defined in external config file.
- },
-
// Basic page navigation links.
navlinks={
"up: request:/..",
"options: servlet:/?method=OPTIONS",
"stats: servlet:/stats"
- },
-
- // Default stylesheet to use for the page.
- // Can be overridden from external config file.
- // Default is DevOps look-and-feel (aka Depression look-and-feel).
- stylesheet="$C{REST/theme,servlet:/htdocs/themes/devops.css}",
-
- // Default contents to add to the <head> section of the HTML page.
- // Use it to add a favicon link to the page.
- head="$C{REST/head}",
-
- // No default page footer contents.
- // Can be overridden from external config file.
- footer="$C{REST/footer}",
+ }
- // By default, table cell contents should not wrap.
- nowrap="true"
)
@JsonSchemaConfig(
// Add descriptions to the following types when not specified:
@@ -135,8 +45,6 @@ import org.apache.juneau.xmlschema.XmlSchemaDocSerializer;
useBeanDefs="true"
)
@BeanConfig(
- // When parsing generated beans, ignore unknown properties that may only exist as getters and not setters.
- ignoreUnknownBeanProperties="true",
// POJO swaps to apply to all serializers/parsers on this method.
pojoSwaps={
// Use the SwaggerUI swap when rendering Swagger beans.
@@ -144,8 +52,7 @@ import org.apache.juneau.xmlschema.XmlSchemaDocSerializer;
SwaggerUI.class
}
)
-@SuppressWarnings("deprecation")
-public interface BasicRestConfig {
+public interface BasicRestMethods {
/**
* [OPTIONS /*] - Show resource options.
@@ -171,6 +78,29 @@ public interface BasicRestConfig {
public Swagger getOptions(RestRequest req);
/**
+ * [GET /options] - Show resource options.
+ *
+ * @param req The HTTP request.
+ * @return A bean containing the contents for the OPTIONS page.
+ */
+ @RestMethod(name=OPTIONS, path="/*",
+ summary="Swagger documentation",
+ description="Swagger documentation for this resource."
+ )
+ @HtmlDocConfig(
+ // Should override config annotations defined on class.
+ rank=10,
+ // Override the nav links for the swagger page.
+ navlinks={
+ "back: servlet:/",
+ "json: servlet:/?Accept=text/json&plainText=true"
+ },
+ // Never show aside contents of page inherited from class.
+ aside="NONE"
+ )
+ public Swagger getOptions2(RestRequest req);
+
+ /**
* [* /error] - Error occurred.
*
* <p>
@@ -212,4 +142,5 @@ public interface BasicRestConfig {
aside="NONE"
)
public RestContextStats getStats(RestRequest req);
+
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
index 9b60cad..11fb1ba 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestServlet.java
@@ -164,7 +164,7 @@ import org.apache.juneau.xml.*;
"stats: servlet:/stats"
}
)
-public abstract class BasicRestServlet extends RestServlet implements BasicRestConfig {
+public abstract class BasicRestServlet extends RestServlet implements BasicRestConfig, BasicRestMethods {
private static final long serialVersionUID = 1L;
/**
@@ -180,6 +180,18 @@ public abstract class BasicRestServlet extends RestServlet implements BasicRestC
}
/**
+ * [GET /options] - Show resource options.
+ *
+ * @param req The HTTP request.
+ * @return A bean containing the contents for the OPTIONS page.
+ */
+ @Override /* BasicRestConfig */
+ public Swagger getOptions2(RestRequest req) {
+ // Localized Swagger for this resource is available through the RestRequest object.
+ return req.getSwagger();
+ }
+
+ /**
* [* /error] - Error occurred.
*
* <p>
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java
index beca659..53dfd76 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java
@@ -48,5 +48,18 @@ public enum Enablement {
}
return null;
}
+
+ /**
+ * Returns <jk>true</jk> if this enum is one of the specified values.
+ *
+ * @param values The values to check against.
+ * @return <jk>true</jk> if this enum is one of the specified values.
+ */
+ public boolean isOneOf(Enablement...values) {
+ for (Enablement v : values)
+ if (this == v)
+ return true;
+ return false;
+ }
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/NoOpRestCallLogger.java
similarity index 71%
copy from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java
copy to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/NoOpRestCallLogger.java
index beca659..9410447 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/Enablement.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/NoOpRestCallLogger.java
@@ -12,41 +12,15 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
-import org.apache.juneau.internal.*;
+import javax.servlet.http.*;
/**
- * Represents the enablement settings of a feature.
+ * Can be used to force call logging to be disabled.
*/
-public enum Enablement {
+public class NoOpRestCallLogger implements RestCallLogger {
- /**
- * Feature is always enabled.
- */
- TRUE,
-
- /**
- * Feature is enabled per HTTP request.
- */
- PER_REQUEST,
-
- /**
- * Feature is disabled.
- */
- FALSE;
-
- /**
- * Retrieves this enum using case-insensitive matching.
- *
- * @param s The enum name to resolve.
- * @return The resolved value.
- */
- public static Enablement fromString(String s) {
- if (! StringUtils.isEmpty(s)) {
- try {
- return valueOf(s.replace('-', '_').toUpperCase());
- } catch (IllegalArgumentException e) {}
- }
- return null;
+ @Override
+ public void log(RestCallLoggerConfig config, HttpServletRequest req, HttpServletResponse res) {
+ // Do nothing.
}
}
-
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 e69e5b4..f897a42 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
@@ -13,6 +13,7 @@
package org.apache.juneau.rest;
import java.io.*;
+import java.lang.reflect.*;
import java.util.*;
import javax.servlet.http.*;
@@ -76,10 +77,10 @@ public class RestCall {
/**
* Sets the method context on this call.
- *
- * Used for logging statistics on the method.
- *
- * @param value The new value.
+ *
+ * Used for logging statistics on the method.
+ *
+ * @param value The new value.
* @return This object (for method chaining).
*/
public RestCall restMethodContext(RestMethodContext value) {
@@ -150,13 +151,22 @@ public class RestCall {
/**
* Returns the method context of this call.
- *
+ *
* @return The method context of this call.
*/
public RestMethodContext getRestMethodContext() {
return rmethod;
}
+ /**
+ * Returns the Java method of this call.
+ *
+ * @return The java method of this call, or <jk>null</jk> if it hasn't been determined yet.
+ */
+ public Method getJavaMethod() {
+ return rmethod == null ? null : rmethod.method;
+ }
+
//------------------------------------------------------------------------------------------------------------------
// Setters.
//------------------------------------------------------------------------------------------------------------------
@@ -194,8 +204,10 @@ public class RestCall {
if (b) {
req = CachingHttpServletRequest.wrap(req);
res = CachingHttpServletResponse.wrap(res);
+ req.setAttribute("Debug", true);
+ } else {
+ req.removeAttribute("Debug");
}
- req.setAttribute("Debug", b);
return this;
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallLoggerConfig.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallLoggerConfig.java
index 4c925d0..a5e326b 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallLoggerConfig.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestCallLoggerConfig.java
@@ -30,7 +30,7 @@ public class RestCallLoggerConfig {
/**
* Default empty logging config.
*/
- public static final RestCallLoggerConfig DEFAULT = RestCallLoggerConfig.create().build();
+ public static final RestCallLoggerConfig DEFAULT_NOOP = RestCallLoggerConfig.create().build();
/**
* Default debug logging config.
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 ccda33e..879ba87 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
@@ -464,10 +464,21 @@ public final class RestContext extends BeanContext {
*
* <ul class='notes'>
* <li>
+ * The default call handler if not specified is {@link BasicRestCallHandler}.
+ * <li>
+ * The resource class itself will be used if it implements the {@link RestCallHandler} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
+ * The {@link RestServlet} class itself implements the {@link RestCallHandler} interface with the same
+ * functionality as {@link BasicRestCallHandler} that gets used if not overridden by this annotation.
+ * <br>Subclasses can also alter the behavior by overriding these methods.
+ * <li>
* When defined as a class, the implementation must have one of the following constructors:
* <ul>
* <li><code><jk>public</jk> T(RestContext)</code>
* <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
* </ul>
* <li>
* Inner classes of the REST resource class are allowed.
@@ -537,6 +548,28 @@ public final class RestContext extends BeanContext {
* }
* </p>
*
+ * <ul class='notes'>
+ * <li>
+ * The default call logger if not specified is {@link BasicRestCallLogger}.
+ * <li>
+ * The resource class itself will be used if it implements the {@link RestCallLogger} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
+ * The {@link RestServlet} class itself implements the {@link RestCallLogger} interface with the same
+ * functionality as {@link BasicRestCallLogger} that gets used if not overridden by this annotation.
+ * <br>Subclasses can also alter the behavior by overriding this method.
+ * <li>
+ * When defined as a class, the implementation must have one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestContext)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
+ * </ul>
+ * <li>
+ * Inner classes of the REST resource class are allowed.
+ * </ul>
+ *
* <ul class='seealso'>
* <li class='link'>{@doc juneau-rest-server.LoggingAndDebugging}
* </ul>
@@ -551,7 +584,7 @@ public final class RestContext extends BeanContext {
* <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_callLoggerConfig REST_callLoggerConfig}
* <li><b>Name:</b> <js>"RestContext.callLoggerConfig.o"</js>
* <li><b>Data type:</b> {@link org.apache.juneau.rest.RestCallLoggerConfig}
- * <li><b>Default:</b> {@link org.apache.juneau.rest.RestCallLoggerConfig#DEFAULT}
+ * <li><b>Default:</b> {@link org.apache.juneau.rest.RestCallLoggerConfig#DEFAULT_NOOP}
* <li><b>Session property:</b> <jk>false</jk>
* <li><b>Annotations:</b>
* <ul>
@@ -1033,11 +1066,47 @@ public final class RestContext extends BeanContext {
* <ul class='spaced-list'>
* <li>
* HTTP request/response bodies are cached in memory for logging purposes.
+ * <li>
+ * Request/response messages are automatically logged always or per request.
* </ul>
*/
public static final String REST_debug = PREFIX + ".debug.s";
/**
+ * Configuration property: Debug mode on specified classes/methods.
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>ID:</b> {@link org.apache.juneau.rest.RestContext#REST_debugOn REST_debugOn}
+ * <li><b>Name:</b> <js>"RestContext.debugOn.s"</js>
+ * <li><b>Data type:</b> <c>String</c> (comma-delimited)
+ * <li><b>System property:</b> <c>RestContext.debugOn</c>
+ * <li><b>Environment variable:</b> <c>RESTCONTEXT_DEBUGON</c>
+ * <li><b>Default:</b> Empty string
+ * <li><b>Session property:</b> <jk>false</jk>
+ * <li><b>Annotations:</b>
+ * <ul>
+ * <li class='ja'>{@link org.apache.juneau.rest.annotation.Rest#debugOn()}
+ * </ul>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link org.apache.juneau.rest.RestContextBuilder#debugOn(String)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Enables the following:
+ * <ul class='spaced-list'>
+ * <li>
+ * HTTP request/response bodies are cached in memory for logging purposes.
+ * <li>
+ * Request/response messages are automatically logged always or per request.
+ * </ul>
+ */
+ public static final String REST_debugOn = PREFIX + ".debugOn.s";
+
+ /**
* Configuration property: Default character encoding.
*
* <h5 class='section'>Property:</h5>
@@ -1362,10 +1431,21 @@ public final class RestContext extends BeanContext {
*
* <ul class='notes'>
* <li>
+ * The default info provider if not specified is {@link BasicRestInfoProvider}.
+ * <li>
+ * The resource class itself will be used if it implements the {@link RestInfoProvider} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
+ * The {@link RestServlet} class itself implements the {@link RestInfoProvider} interface with the same
+ * functionality as {@link BasicRestInfoProvider} that gets used if not overridden by this annotation.
+ * <br>Subclasses can also alter the behavior by overriding these methods.
+ * <li>
* When defined as a class, the implementation must have one of the following constructors:
* <ul>
* <li><code><jk>public</jk> T(RestContext)</code>
* <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
* </ul>
* <li>
* Inner classes of the REST resource class are allowed.
@@ -2362,6 +2442,8 @@ public final class RestContext extends BeanContext {
* <ul>
* <li><code><jk>public</jk> T(RestContext)</code>
* <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
* </ul>
* <li>
* Inner classes of the REST resource class are allowed.
@@ -3547,6 +3629,8 @@ public final class RestContext extends BeanContext {
private final ThreadLocal<RestRequest> req = new ThreadLocal<>();
private final ThreadLocal<RestResponse> res = new ThreadLocal<>();
+ private final ReflectionMap<Enablement> debugEnablement;
+
/**
* Constructor.
*
@@ -3634,9 +3718,32 @@ public final class RestContext extends BeanContext {
allowedMethodHeaders = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodHeaders, ""));
renderResponseStackTraces = getBooleanProperty(REST_renderResponseStackTraces, false);
useStackTraceHashes = getBooleanProperty(REST_useStackTraceHashes, true);
- debug = getInstanceProperty(REST_debug, Enablement.class, Enablement.FALSE);
clientVersionHeader = getStringProperty(REST_clientVersionHeader, "X-Client-Version");
+ ReflectionMap.Builder<Enablement> deb = ReflectionMap.create(Enablement.class);
+ for (String s : split(getStringProperty(REST_debugOn, ""))) {
+ s = s.trim();
+ if (! s.isEmpty()) {
+ int i = s.indexOf('=');
+ if (i == -1)
+ deb.append(s.trim(), Enablement.TRUE);
+ else
+ deb.append(s.substring(0, i).trim(), Enablement.fromString(s.substring(i+1).trim()));
+ }
+ }
+
+ Enablement de = getInstanceProperty(REST_debug, Enablement.class, Enablement.FALSE);
+ if (de != null)
+ deb.append(rci.getFullName(), de);
+ for (MethodInfo mi : rci.getPublicMethods())
+ for (RestMethod a : mi.getAnnotations(RestMethod.class))
+ if (a != null && ! a.debug().isEmpty())
+ deb.append(mi.getFullName(), Enablement.fromString(a.debug()));
+
+ this.debugEnablement = deb.build();
+
+ this.debug = debugEnablement.find(rci.inner(), Enablement.class).orElse(Enablement.FALSE);
+
responseHandlers = getInstanceArrayProperty(REST_responseHandlers, resource, ResponseHandler.class, new ResponseHandler[0], resourceResolver, this);
Map<Class<?>,RestMethodParam> _paramResolvers = new HashMap<>();
@@ -3654,17 +3761,13 @@ public final class RestContext extends BeanContext {
logger = getInstanceProperty(REST_logger, resource, RestLogger.class, NoOpRestLogger.class, resourceResolver, this);
- if (debug == Enablement.TRUE) {
- this.callLoggerConfig = RestCallLoggerConfig.DEFAULT_DEBUG;
- } else {
- Object clc = getProperty(REST_callLoggerConfig);
- if (clc instanceof RestCallLoggerConfig)
- this.callLoggerConfig = (RestCallLoggerConfig)clc;
- else if (clc instanceof ObjectMap)
- this.callLoggerConfig = RestCallLoggerConfig.create().apply((ObjectMap)clc).build();
- else
- this.callLoggerConfig = RestCallLoggerConfig.DEFAULT;
- }
+ Object clc = getProperty(REST_callLoggerConfig);
+ if (clc instanceof RestCallLoggerConfig)
+ this.callLoggerConfig = (RestCallLoggerConfig)clc;
+ else if (clc instanceof ObjectMap)
+ this.callLoggerConfig = RestCallLoggerConfig.create().apply((ObjectMap)clc).build();
+ else
+ this.callLoggerConfig = RestCallLoggerConfig.DEFAULT_NOOP;
this.stackTraceDb = new StackTraceDatabase(callLoggerConfig.getStackTraceHashingTimeout(), RestMethodContext.class);
@@ -4721,7 +4824,7 @@ public final class RestContext extends BeanContext {
* Returns <jk>true</jk> if debug mode is enabled on this resource.
*
* <div class='warn'>
- * <b>Deprecated</b> - Use {@link #getDebug()}
+ * <b>Deprecated</b> - Use {@link #getDebug(Method)}
* </div>
*
* <ul class='seealso'>
@@ -4737,12 +4840,15 @@ public final class RestContext extends BeanContext {
}
/**
- * Returns the debug setting on this context.
+ * Returns the debug setting on this context for the specified method.
*
- * @return The debug setting on this context.
+ * @param method The java method.
+ * @return The debug setting on this context or <jk>null</jk> not specified for this method.
*/
- public Enablement getDebug() {
- return debug;
+ public Enablement getDebug(Method method) {
+ if (method == null)
+ return null;
+ return debugEnablement.find(method).orElse(null);
}
/**
@@ -5317,6 +5423,10 @@ public final class RestContext extends BeanContext {
this.res.set(res);
}
+ Enablement getDebug() {
+ return debug;
+ }
+
/**
* Clear any request state information on this context.
* This should always be called in a finally block in the RestServlet.
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 6415d59..7b676d3 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
@@ -664,7 +664,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
*
* @param value
* The new value for this setting.
- * <br>The default is {@link RestCallLoggerConfig#DEFAULT}.
+ * <br>The default is {@link RestCallLoggerConfig#DEFAULT_NOOP}.
* @return This object (for method chaining).
*/
@ConfigurationProperty
@@ -868,6 +868,8 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
* <ul class='spaced-list'>
* <li>
* HTTP request/response bodies are cached in memory for logging purposes.
+ * <li>
+ * Request/response messages are automatically logged always or per request.
* </ul>
*
* @param value The new value for this setting.
@@ -879,6 +881,29 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
}
/**
+ * Configuration property: Debug mode on specified classes/methods.
+ *
+ * Enables the following:
+ * <ul class='spaced-list'>
+ * <li>
+ * HTTP request/response bodies are cached in memory for logging purposes.
+ * <li>
+ * Request/response messages are automatically logged.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_debugOn}
+ * </ul>
+ *
+ * @param value The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ @ConfigurationProperty
+ public RestContextBuilder debugOn(String value) {
+ return set(REST_debugOn, value);
+ }
+
+ /**
* Configuration property: Default character encoding.
*
* <p>
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 67ea0c9..5226e7b 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
@@ -12,6 +12,7 @@
// ***************************************************************************************************************************
package org.apache.juneau.rest;
+import static org.apache.juneau.rest.Enablement.*;
import static javax.servlet.http.HttpServletResponse.*;
import static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;
@@ -750,19 +751,15 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
this.supportedAcceptTypes = getListProperty(REST_produces, MediaType.class, serializers.getSupportedMediaTypes());
this.supportedContentTypes = getListProperty(REST_consumes, MediaType.class, parsers.getSupportedMediaTypes());
- this.debug = getInstanceProperty(RESTMETHOD_debug, Enablement.class, context.getDebug());
+ this.debug = context.getDebug(method);
- if (debug == Enablement.TRUE) {
- this.callLoggerConfig = RestCallLoggerConfig.DEFAULT_DEBUG;
- } else {
- Object clc = getProperty(RESTMETHOD_callLoggerConfig);
- if (clc instanceof RestCallLoggerConfig)
- this.callLoggerConfig = (RestCallLoggerConfig)clc;
- else if (clc instanceof ObjectMap)
- this.callLoggerConfig = RestCallLoggerConfig.create().parent(context.getCallLoggerConfig()).apply((ObjectMap)clc).build();
- else
- this.callLoggerConfig = context.getCallLoggerConfig();
- }
+ Object clc = getProperty(RESTMETHOD_callLoggerConfig);
+ if (clc instanceof RestCallLoggerConfig)
+ this.callLoggerConfig = (RestCallLoggerConfig)clc;
+ else if (clc instanceof ObjectMap)
+ this.callLoggerConfig = RestCallLoggerConfig.create().parent(context.getCallLoggerConfig()).apply((ObjectMap)clc).build();
+ else
+ this.callLoggerConfig = context.getCallLoggerConfig();
}
ResponseBeanMeta getResponseBeanMeta(Object o) {
@@ -901,7 +898,24 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
context.preCall(req, res);
- call.debug(req.isDebug()).loggerConfig(req.getCallLoggerConfig());
+ call.loggerConfig(callLoggerConfig);
+
+ if (debug == TRUE) {
+ call.debug(true);
+ call.loggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
+ } else if (debug == FALSE) {
+ call.debug(false);
+ call.loggerConfig(RestCallLoggerConfig.DEFAULT_NOOP);
+ } else if (debug == PER_REQUEST) {
+ boolean b = "true".equalsIgnoreCase(req.getHeader("X-Debug"));
+ if (b) {
+ call.debug(true);
+ call.loggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
+ } else {
+ call.debug(false);
+ call.loggerConfig(RestCallLoggerConfig.DEFAULT_NOOP);
+ }
+ }
Object[] args = new Object[methodParams.length];
for (int i = 0; i < methodParams.length; i++) {
@@ -921,6 +935,17 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
Object output;
try {
output = methodInvoker.invoke(context.getResource(), args);
+
+ // Handle manual call to req.setDebug().
+ Boolean debug = ObjectUtils.castOrNull(req.getAttribute("Debug"), Boolean.class);
+ if (debug == Boolean.TRUE) {
+ call.debug(true);
+ call.loggerConfig(RestCallLoggerConfig.DEFAULT_DEBUG);
+ } else if (debug == Boolean.FALSE) {
+ call.debug(false);
+ call.loggerConfig(RestCallLoggerConfig.DEFAULT_NOOP);
+ }
+
if (res.getStatus() == 0)
res.setStatus(200);
if (! method.getReturnType().equals(Void.TYPE)) {
@@ -956,26 +981,26 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
}
return SC_OK;
}
-
- protected void addStatusCode(int code) {
- AtomicInteger ai = statusCodes.get(code);
- if (ai == null) {
- synchronized(statusCodes) {
- ai = new AtomicInteger();
- statusCodes.putIfAbsent(code, ai);
- ai = statusCodes.get(code);
- }
- }
- ai.incrementAndGet();
- }
-
- protected Map<Integer,Integer> getStatusCodes() {
- Map<Integer,Integer> m = new TreeMap<>();
- for (Map.Entry<Integer,AtomicInteger> e : statusCodes.entrySet())
- m.put(e.getKey(), e.getValue().get());
- return m;
- }
-
+//
+// protected void addStatusCode(int code) {
+// AtomicInteger ai = statusCodes.get(code);
+// if (ai == null) {
+// synchronized(statusCodes) {
+// ai = new AtomicInteger();
+// statusCodes.putIfAbsent(code, ai);
+// ai = statusCodes.get(code);
+// }
+// }
+// ai.incrementAndGet();
+// }
+//
+// protected Map<Integer,Integer> getStatusCodes() {
+// Map<Integer,Integer> m = new TreeMap<>();
+// for (Map.Entry<Integer,AtomicInteger> e : statusCodes.entrySet())
+// m.put(e.getKey(), e.getValue().get());
+// return m;
+// }
+//
/*
* compareTo() method is used to keep SimpleMethods ordered in the RestCallRouter list.
* It maintains the order in which matches are made during requests.
@@ -1061,21 +1086,16 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
}
/**
- * Returns whether debug is enabled on this method.
- *
- * @return <jk>true</jk> if debug is enabled on this method.
- */
- protected Enablement getDebug() {
- return debug;
- }
-
- /**
* @return The REST call logger config for this method.
*/
protected RestCallLoggerConfig getCallLoggerConfig() {
return callLoggerConfig;
}
+ Enablement getDebug() {
+ return debug;
+ }
+
@Override /* Object */
public boolean equals(Object o) {
if (! (o instanceof RestMethodContext))
@@ -1119,4 +1139,4 @@ public class RestMethodContext extends BeanContext implements Comparable<RestMet
.append("priority", priority)
);
}
-}
\ No newline at end of file
+}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 3310a30..ba287a0 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -209,7 +209,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
.maxInput(rjm.maxInput);
if (isDebug()) {
- setDebug();
+ inner = CachingHttpServletRequest.wrap(inner);
}
String stylesheet = getQuery().getString("stylesheet");
@@ -1314,14 +1314,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
*/
public boolean isDebug() {
Boolean b = ObjectUtils.castOrNull(getAttribute("Debug"), Boolean.class);
- if (b != null)
- return b;
- Enablement e = restJavaMethod != null ? restJavaMethod.getDebug() : context.getDebug();
- if (e == TRUE)
- return true;
- if (e == FALSE)
- return false;
- return "true".equalsIgnoreCase(getHeader("X-Debug"));
+ return b == null ? false : b;
}
/**
@@ -1802,7 +1795,7 @@ public final class RestRequest extends HttpServletRequestWrapper {
public RestCallLoggerConfig getCallLoggerConfig() {
if (restJavaMethod != null)
return restJavaMethod.getCallLoggerConfig();
- return RestCallLoggerConfig.DEFAULT;
+ return RestCallLoggerConfig.DEFAULT_NOOP;
}
void close() {
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
index f1eee7d..58e14c8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/StatusStats.java
@@ -21,6 +21,7 @@ import org.apache.juneau.annotation.*;
*
*/
@Bean(bpi="resource,methods")
+@SuppressWarnings("javadoc")
public class StatusStats implements Comparable<StatusStats> {
private final Class<?> resource;
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
index 43d1149..fd03bff 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
@@ -169,9 +169,22 @@ public @interface Rest {
* <li>
* The default call handler if not specified is {@link BasicRestCallHandler}.
* <li>
+ * The resource class itself will be used if it implements the {@link RestCallHandler} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
* The {@link RestServlet} class itself implements the {@link RestCallHandler} interface with the same
* functionality as {@link BasicRestCallHandler} that gets used if not overridden by this annotation.
* <br>Subclasses can also alter the behavior by overriding these methods.
+ * <li>
+ * The implementation must have one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestContext)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
+ * </ul>
+ * <li>
+ * Inner classes of the REST resource class are allowed.
* </ul>
*
* <ul class='seealso'>
@@ -202,9 +215,22 @@ public @interface Rest {
* <li>
* The default resource finder if not specified is {@link ClasspathResourceFinderBasic}.
* <li>
+ * The resource class itself will be used if it implements the {@link ClasspathResourceFinder} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
* The {@link RestServlet} class itself implements the {@link ClasspathResourceFinder} interface with the same
* functionality as {@link ClasspathResourceFinderBasic} that gets used if not overridden by this annotation.
* <br>Subclasses can also alter the behavior by overriding this method.
+ * <li>
+ * The implementation must have one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestContext)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
+ * </ul>
+ * <li>
+ * Inner classes of the REST resource class are allowed.
* </ul>
*
* <ul class='seealso'>
@@ -405,9 +431,22 @@ public @interface Rest {
* <li>
* The default info provider if not specified is {@link BasicRestInfoProvider}.
* <li>
+ * The resource class itself will be used if it implements the {@link RestInfoProvider} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
* The {@link RestServlet} class itself implements the {@link RestInfoProvider} interface with the same
* functionality as {@link BasicRestInfoProvider} that gets used if not overridden by this annotation.
* <br>Subclasses can also alter the behavior by overriding these methods.
+ * <li>
+ * The implementation must have one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestContext)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
+ * </ul>
+ * <li>
+ * Inner classes of the REST resource class are allowed.
* </ul>
*
* <ul class='seealso'>
@@ -423,9 +462,22 @@ public @interface Rest {
* <li>
* The default call logger if not specified is {@link BasicRestCallLogger}.
* <li>
+ * The resource class itself will be used if it implements the {@link RestCallLogger} interface and not
+ * explicitly overridden via this annotation.
+ * <li>
* The {@link RestServlet} class itself implements the {@link RestCallLogger} interface with the same
* functionality as {@link BasicRestCallLogger} that gets used if not overridden by this annotation.
* <br>Subclasses can also alter the behavior by overriding this method.
+ * <li>
+ * The implementation must have one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestContext)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
+ * </ul>
+ * <li>
+ * Inner classes of the REST resource class are allowed.
* </ul>
*
* <ul class='seealso'>
@@ -574,7 +626,7 @@ public @interface Rest {
* The typical usage is to define a path to a child resource relative to the parent resource.
*
* <h5 class='figure'>Example:</h5>
- * <p class='bpcode'>
+ * <p class='bcode w800'>
* <ja>@Rest</ja>(
* children={ChildResource.<jk>class</jk>}
* )
@@ -612,7 +664,7 @@ public @interface Rest {
* Spring Boot initializer class:
*
* <h5 class='figure'>Example:</h5>
- * <p class='bpcode'>
+ * <p class='bcode'>
* <ja>@SpringBootApplication</ja>
* <ja>@Controller</ja>
* <jk>public class</jk> App {
@@ -647,7 +699,7 @@ public @interface Rest {
* or access through the {@link RestRequest#getPathMatch()} method.
*
* <h5 class='figure'>Example:</h5>
- * <p class='bpcode'>
+ * <p class='bcode'>
* <ja>@Rest</ja>(
* path=<js>"/myResource/{foo}/{bar}"</js>
* )
@@ -790,6 +842,21 @@ public @interface Rest {
* <p>
* The resolver used for resolving child resources.
*
+ * <ul class='notes'>
+ * <li>
+ * Unless overridden, resource resolvers are inherited from ascendant resources.
+ * <li>
+ * The implementation must have one of the following constructors:
+ * <ul>
+ * <li><code><jk>public</jk> T(RestContext)</code>
+ * <li><code><jk>public</jk> T()</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>(RestContext)</code>
+ * <li><code><jk>public static</jk> <jsm>create</jsm>()</code>
+ * </ul>
+ * <li>
+ * Inner classes of the REST resource class are allowed.
+ * </ul>
+ *
* <ul class='seealso'>
* <li class='jf'>{@link RestContext#REST_resourceResolver}
* </ul>
@@ -1287,6 +1354,8 @@ public @interface Rest {
* <ul class='spaced-list'>
* <li>
* HTTP request/response bodies are cached in memory for logging purposes.
+ * <li>
+ * HTTP requests/responses are logged to the registered {@link RestCallLogger}.
* </ul>
*
* <p>
@@ -1301,6 +1370,9 @@ public @interface Rest {
* <li>
* Supports {@doc DefaultRestSvlVariables}
* (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <li>
+ * These debug settings can be overridden by the {@link Rest#debugOn()} annotation or at runtime by directly
+ * calling {@link RestRequest#setDebug()}.
* </ul>
*
* <ul class='seealso'>
@@ -1308,4 +1380,121 @@ public @interface Rest {
* </ul>
*/
String debug() default "";
+
+ /**
+ * Enable debug mode on specified classes/methods.
+ *
+ * <p>
+ * Enables the following:
+ * <ul class='spaced-list'>
+ * <li>
+ * HTTP request/response bodies are cached in memory for logging purposes on matching classes and methods.
+ * <li>
+ * HTTP requests/responses are logged to the registered {@link RestCallLogger}.
+ * </ul>
+ *
+ * <p>
+ * Consists of a comma-delimited list of strings of the following forms:
+ * <ul>
+ * <li><js>"class-identifier"</js> - Enable debug on the specified class.
+ * <li><js>"class-identifier=[true|false|per-request]"</js> - Explicitly enable debug on the specified class.
+ * <li><js>"method-identifier"</js> - Enable debug on the specified class.
+ * <li><js>"method-identifier=[true|false|per-request]"</js> - Explicitly enable debug on the specified class.
+ * </ul>
+ *
+ * <p>
+ * Class identifiers can be any of the following forms:
+ * <ul>
+ * <li>Fully qualified:
+ * <ul>
+ * <li><js>"com.foo.MyClass"</js>
+ * </ul>
+ * <li>Fully qualified inner class:
+ * <ul>
+ * <li><js>"com.foo.MyClass$Inner1$Inner2"</js>
+ * </ul>
+ * <li>Simple:
+ * <ul>
+ * <li><js>"MyClass"</js>
+ * </ul>
+ * <li>Simple inner:
+ * <ul>
+ * <li><js>"MyClass$Inner1$Inner2"</js>
+ * <li><js>"Inner1$Inner2"</js>
+ * <li><js>"Inner2"</js>
+ * </ul>
+ * </ul>
+ *
+ * <p>
+ * Method identifiers can be any of the following forms:
+ * <ul>
+ * <li>Fully qualified with args:
+ * <ul>
+ * <li><js>"com.foo.MyClass.myMethod(String,int)"</js>
+ * <li><js>"com.foo.MyClass.myMethod(java.lang.String,int)"</js>
+ * <li><js>"com.foo.MyClass.myMethod()"</js>
+ * </ul>
+ * <li>Fully qualified:
+ * <ul>
+ * <li><js>"com.foo.MyClass.myMethod"</js>
+ * </ul>
+ * <li>Simple with args:
+ * <ul>
+ * <li><js>"MyClass.myMethod(String,int)"</js>
+ * <li><js>"MyClass.myMethod(java.lang.String,int)"</js>
+ * <li><js>"MyClass.myMethod()"</js>
+ * </ul>
+ * <li>Simple:
+ * <ul>
+ * <li><js>"MyClass.myMethod"</js>
+ * </ul>
+ * <li>Simple inner class:
+ * <ul>
+ * <li><js>"MyClass$Inner1$Inner2.myMethod"</js>
+ * <li><js>"Inner1$Inner2.myMethod"</js>
+ * <li><js>"Inner2.myMethod"</js>
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Turn on debug per-request on the class and always on the doX() method</jc>.
+ * <ja>@Rest</ja>(
+ * debugOn=<js>"MyResource=per-request,Mysource.doX=true"</js>
+ * )
+ * <jk>public class</jk> MyResource {
+ *
+ * <ja>@RestMethod</ja>
+ * <jk>public void</jk> String doX() {
+ * ...
+ * }
+ * </p>
+ *
+ * <p>
+ * A more-typical scenario is to pull this setting from an external source such as system property or environment
+ * variable:
+ *
+ * <h5 class='figure'>Example:</h5
+ * <p class='bcode w800'>
+ * <ja>@Rest</ja>(
+ * debugOn=<js>"$E{DEBUG_ON_SETTINGS}"</js>
+ * )
+ * <jk>public class</jk> MyResource {...}
+ * </p>
+ *
+ * <ul class='notes'>
+ * <li>
+ * Supports {@doc DefaultRestSvlVariables}
+ * (e.g. <js>"$L{my.localized.variable}"</js>).
+ * <li>
+ * These debug settings override the settings define via {@link Rest#debug()} and {@link RestMethod#debug()}.
+ * <li>
+ * These debug settings can be overridden at runtime by directly calling {@link RestRequest#setDebug()}.
+ * </ul>
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestContext#REST_debugOn}
+ * </ul>
+ */
+ String debugOn() default "";
}
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java
index 15e72f1..db6ff27 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestConfigApply.java
@@ -290,9 +290,11 @@ public class RestConfigApply extends ConfigApply<Rest> {
if (! a.maxInput().isEmpty())
psb.set(REST_maxInput, string(a.maxInput()));
- if (! a.debug().isEmpty()) {
- psb.set(REST_debug, a.debug());
- }
+ if (! a.debug().isEmpty())
+ psb.set(REST_debug, string(a.debug()));
+
+ if (! a.debugOn().isEmpty())
+ psb.set(REST_debugOn, string(a.debugOn()));
psb.addTo(REST_mimeTypes, strings(a.mimeTypes()));
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 f34ca28..5f9b0f8 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
@@ -174,6 +174,15 @@ public @interface RestMethod {
* Request/response messages are automatically logged.
* </ul>
*
+ * <p>
+ * Possible values (case insensitive):
+ * <ul>
+ * <li><js>"true"</js> - Debug is enabled for all requests.
+ * <li><js>"false"</js> - Debug is disabled for all requests.
+ * <li><js>"per-request"</js> - Debug is enabled only for requests that have a <c class='snippet'>X-Debug: true</c> header.
+ * <li><js>""</js> (or anything else) - Debug mode is inherited from class.
+ * </ul>
+ *
* <ul class='notes'>
* <li>
* Supports {@doc DefaultRestSvlVariables}
@@ -181,7 +190,7 @@ public @interface RestMethod {
* </ul>
*
* <ul class='seealso'>
- * <li class='jf'>{@link RestMethodContext#RESTMETHOD_debug}
+ * <li class='jf'>{@link RestContext#REST_debug}
* </ul>
*/
String debug() default "";
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java
index d466efc..9e5dad6 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestMethodConfigApply.java
@@ -222,16 +222,16 @@ public class RestMethodConfigApply extends ConfigApply<RestMethod> {
}
if (! a.method().isEmpty())
- psb.set(RESTMETHOD_httpMethod, a.method());
+ psb.set(RESTMETHOD_httpMethod, string(a.method()));
if (! a.name().isEmpty())
- psb.set(RESTMETHOD_httpMethod, a.name());
+ psb.set(RESTMETHOD_httpMethod, string(a.name()));
if (a.priority() != 0)
psb.set(RESTMETHOD_priority, a.priority());
if (! a.debug().isEmpty())
- psb.set(RESTMETHOD_debug, a.debug());
+ psb.set(RESTMETHOD_debug, string(a.debug()));
if (! AnnotationUtils.empty(a.logging())) {
Logging al = a.logging();