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 2022/07/10 16:10:25 UTC

[juneau] branch jbFixRestNpe updated: Rest bean refactoring.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch jbFixRestNpe
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/jbFixRestNpe by this push:
     new 8361e5498 Rest bean refactoring.
8361e5498 is described below

commit 8361e5498ee84509face84a4e4e87e3cc776b2cf
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Sun Jul 10 12:10:05 2022 -0400

    Rest bean refactoring.
---
 .../org/apache/juneau/rest/ResourceSupplier.java   |  40 ++++++
 .../java/org/apache/juneau/rest/RestContext.java   | 141 +++++----------------
 .../java/org/apache/juneau/rest/RestOpContext.java |   2 +-
 .../org/apache/juneau/rest/annotation/Rest.java    |   4 -
 .../juneau/rest/annotation/RestAnnotation.java     |   2 -
 .../juneau/rest/debug/BasicDebugEnablement.java    | 128 +++++++++++--------
 .../apache/juneau/rest/debug/DebugEnablement.java  |  94 +++++++++++++-
 .../org/apache/juneau/rest/logger/CallLogger.java  |   2 -
 8 files changed, 239 insertions(+), 174 deletions(-)

diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResourceSupplier.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResourceSupplier.java
new file mode 100644
index 000000000..63c836ebb
--- /dev/null
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/ResourceSupplier.java
@@ -0,0 +1,40 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.rest;
+
+import java.util.function.*;
+
+/**
+ * A supplier of a REST resource bean.
+ *
+ * <p>
+ * Pretty much just a normal supplier, but wrapped in a concrete class so that it can be retrieved by class name.
+ */
+public class ResourceSupplier implements Supplier<Object> {
+
+	private final Supplier<?> supplier;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param supplier The supplier of the bean.
+	 */
+	public ResourceSupplier(Supplier<?> supplier) {
+		this.supplier = supplier;
+	}
+
+	@Override
+	public Object get() {
+		return supplier.get();
+	}
+}
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 74bc6a91b..7726c50f2 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
@@ -211,7 +211,7 @@ public class RestContext extends Context {
 
 		private boolean initialized;
 
-		Supplier<?> resource;
+		ResourceSupplier resource;
 		ServletContext servletContext;
 
 		final ServletConfig inner;
@@ -238,7 +238,7 @@ public class RestContext extends Context {
 		private HeaderList.Builder defaultRequestHeaders, defaultResponseHeaders;
 		private NamedAttributeList.Builder defaultRequestAttributes;
 		private RestOpArgList.Builder restOpArgs;
-		private DebugEnablement.Builder debugEnablement;
+		private BeanCreator<DebugEnablement> debugEnablement;
 		private MethodList startCallMethods, endCallMethods, postInitMethods, postInitChildFirstMethods, destroyMethods, preCallMethods, postCallMethods;
 		private RestOperations.Builder restOperations;
 		private RestChildren.Builder restChildren;
@@ -324,13 +324,14 @@ public class RestContext extends Context {
 				return this;
 			initialized = true;
 
-			this.resource = resource;
+			this.resource = new ResourceSupplier(resource);
 			Supplier<?> r = this.resource;
 			Class<?> rc = resourceClass;
 
 			beanStore = createBeanStore(rc, r)
 				.build()
 				.addBean(Builder.class, this)
+				.addBean(ResourceSupplier.class, this.resource)
 				.addBean(ServletConfig.class, inner != null ? inner : this)
 				.addBean(ServletContext.class, (inner != null ? inner : this).getServletContext());
 
@@ -3359,7 +3360,7 @@ public class RestContext extends Context {
 		//-----------------------------------------------------------------------------------------------------------------
 
 		/**
-		 * Returns the debug enablement sub-builder.
+		 * Returns the debug enablement bean creator.
 		 *
 		 * <p>
 		 * Enables the following:
@@ -3372,149 +3373,74 @@ public class RestContext extends Context {
 		 *
 		 * @return The debug enablement sub-builder.
 		 */
-		public DebugEnablement.Builder debugEnablement() {
+		public BeanCreator<DebugEnablement> debugEnablement() {
 			if (debugEnablement == null)
-				debugEnablement = createDebugEnablement(beanStore(), resource());
+				debugEnablement = createDebugEnablement();
 			return debugEnablement;
 		}
 
 		/**
-		 * Applies an operation to the debug enablement sub-builder.
+		 * Specifies the debug enablement class to use for this REST context.
 		 *
-		 * <p>
-		 * Typically used to allow you to execute operations without breaking the fluent flow of the context builder.
-		 *
-		 * <h5 class='section'>Example:</h5>
-		 * <p class='bjava'>
-		 * 	RestContext <jv>context</jv> = RestContext
-		 * 		.<jsm>create</jsm>(<jv>resourceClass</jv>, <jv>parentContext</jv>, <jv>servletConfig</jv>)
-		 * 		.debugEnablement(<jv>x</jv> -&gt; <jv>x</jv>.defaultEnable(<jsf>ALWAYS</jsf>))
-		 * 		.build();
-		 * </p>
-		 *
-		 * @param operation The operation to apply.
-		 * @return This object.
-		 */
-		public Builder debugEnablement(Consumer<DebugEnablement.Builder> operation) {
-			operation.accept(debugEnablement());
-			return this;
-		}
-
-		/**
-		 * Sets the debug default value.
-		 *
-		 * <p>
-		 * The default debug value is the enablement value if not otherwise overridden at the class or method level.
-		 *
-		 * @param value The debug default value.
+		 * @param value The new value for this setting.
 		 * @return This object.
 		 */
-		@FluentSetter
-		public Builder debugDefault(Enablement value) {
-			defaultSettings().set("RestContext.debugDefault", value);
+		public Builder debugEnablement(Class<? extends DebugEnablement> value) {
+			debugEnablement().type(value);
 			return this;
 		}
 
 		/**
-		 * Specifies the debug level on this REST resource.
+		 * Specifies the debug enablement class to use for this REST context.
 		 *
-		 * @param value The value for this setting.
+		 * @param value The new value for this setting.
 		 * @return This object.
 		 */
-		public Builder debug(Enablement value) {
-			debugEnablement().enable(value, this.resourceClass);
+		public Builder debugEnablement(DebugEnablement value) {
+			debugEnablement().impl(value);
 			return this;
 		}
 
 		/**
-		 * 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>
+		 * Sets the debug default value.
 		 *
-		 * <ul class='seealso'>
-		 * 	<li class='ja'>{@link Rest#debugOn}
-		 * </ul>
+		 * <p>
+		 * The default debug value is the enablement value if not otherwise overridden at the class or method level.
 		 *
-		 * @param value The new value for this setting.
+		 * @param value The debug default value.
 		 * @return This object.
 		 */
 		@FluentSetter
-		public Builder debugOn(String value) {
-			for (Map.Entry<String,String> e : splitMap(value != null ? value : "", true).entrySet()) {
-				String k = e.getKey(), v = e.getValue();
-				if (v.isEmpty())
-					v = "ALWAYS";
-				if (! k.isEmpty())
-					debugEnablement().enable(Enablement.fromString(v), k);
-			}
+		public Builder debugDefault(Enablement value) {
+			defaultSettings().set("RestContext.debugDefault", value);
 			return this;
 		}
 
 		/**
-		 * Instantiates the debug enablement sub-builder.
+		 * Instantiates the debug enablement bean creator.
 		 *
-		 * @param beanStore
-		 * 	The factory used for creating beans and retrieving injected beans.
-		 * @param resource
-		 * 	The REST servlet/bean instance that this context is defined against.
-		 * @return A new debug enablement sub-builder.
+		 * @return A new debug enablement bean creator.
 		 */
-		protected DebugEnablement.Builder createDebugEnablement(BeanStore beanStore, Supplier<?> resource) {
-
-			// Default value.
-			Value<DebugEnablement.Builder> v = Value.of(
-				DebugEnablement
-					.create(beanStore)
-			);
-
-			// Default debug enablement if not overridden at class/method level.
-			Enablement debugDefault = defaultSettings.get(Enablement.class, "RestContext.debugDefault").orElse(isDebug() ? Enablement.ALWAYS : Enablement.NEVER);
-			v.get().defaultEnable(debugDefault);
-
-			// Gather @RestOp(debug) settings.
-			Consumer<MethodInfo> consumer = x -> {
-				Value<String> debug = Value.empty();
-				x.getAnnotationList(REST_OP_GROUP).forEachValue(String.class, "debug", NOT_EMPTY, y -> debug.set(y));
-				if (debug.isPresent())
-					v.get().enable(Enablement.fromString(debug.get()), x.getFullName());
-			};
-			ClassInfo.ofProxy(resource.get()).forEachPublicMethod(x -> true, consumer);
-
-			// Replace with bean from bean store.
-			rootBeanStore
-				.getBean(DebugEnablement.class)
-				.ifPresent(x -> v.get().impl(x));
+		protected BeanCreator<DebugEnablement> createDebugEnablement() {
 
-			// Replace with this bean.
-			resourceAs(DebugEnablement.class)
-				.ifPresent(x -> v.get().impl(x));
+			BeanCreator<DebugEnablement> creator = beanStore.createBean(DebugEnablement.class).type(BasicDebugEnablement.class);
 
-			// Specify the implementation class if its set as a default.
+			// Specify the bean type if its set as a default.
 			defaultClasses()
 				.get(DebugEnablement.class)
-				.ifPresent(x -> v.get().type(x));
+				.ifPresent(x -> creator.type(x));
 
-			// Replace with builder from:  public [static] DebugEnablement.Builder createDebugEnablement(<args>)
-			beanStore
-				.createMethodFinder(DebugEnablement.Builder.class)
-				.addBean(DebugEnablement.Builder.class, v.get())
-				.find("createDebugEnablement")
-				.run(x -> v.set(x));
+			rootBeanStore
+				.getBean(DebugEnablement.class)
+				.ifPresent(x -> creator.impl(x));
 
 			// Replace with bean from:  public [static] DebugEnablement createDebugEnablement(<args>)
 			beanStore
 				.createMethodFinder(DebugEnablement.class)
-				.addBean(DebugEnablement.Builder.class, v.get())
 				.find("createDebugEnablement")
-				.run(x -> v.get().impl(x));
+				.run(x -> creator.impl(x));
 
-			return v.get();
+			return creator;
 		}
 
 		//-----------------------------------------------------------------------------------------------------------------
@@ -5871,6 +5797,7 @@ public class RestContext extends Context {
 				.addBean(BeanStore.class, beanStore)
 				.addBean(RestContext.class, this)
 				.addBean(Object.class, resource.get())
+				.addBean(DefaultSettingsMap.class, defaultSettings)
 				.addBean(Builder.class, builder)
 				.addBean(AnnotationWorkList.class, builder.getApplied());
 
@@ -5915,7 +5842,7 @@ public class RestContext extends Context {
 			defaultResponseHeaders = bs.add(HeaderList.class, builder.defaultResponseHeaders().build(), "RestContext.defaultResponseHeaders");
 			defaultRequestAttributes = bs.add(NamedAttributeList.class, builder.defaultRequestAttributes().build(), "RestContext.defaultRequestAttributes");
 			restOpArgs = builder.restOpArgs().build().asArray();
-			debugEnablement = builder.debugEnablement().build();
+			debugEnablement = bs.add(DebugEnablement.class, builder.debugEnablement().orElse(null));
 			startCallMethods = builder.startCallMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new);
 			endCallMethods = builder.endCallMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new);
 			postInitMethods = builder.postInitMethods().stream().map(this::toMethodInvoker).toArray(MethodInvoker[]::new);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
index 8875054ed..b301c738a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
@@ -2238,7 +2238,7 @@ public class RestOpContext extends Context implements Comparable<RestOpContext>
 			if (builder.debug == null)
 				debug = context.getDebugEnablement();
 			else
-				debug = DebugEnablement.create(context.getRootBeanStore()).enable(builder.debug, "*").build();
+				debug = DebugEnablement.create(context.getBeanStore()).enable(builder.debug, "*").build();
 
 			mi = MethodInfo.of(method).accessible();
 			Object r = context.getResource();
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 2d4216476..9550d8c21 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
@@ -523,10 +523,6 @@ public @interface Rest {
 	 * 		These debug settings can be overridden at runtime by directly calling {@link RestRequest#setDebug()}.
 	 * </ul>
 	 *
-	 * <ul class='seealso'>
-	 * 	<li class='jm'>{@link org.apache.juneau.rest.RestContext.Builder#debugOn(String)}
-	 * </ul>
-	 *
 	 * @return The annotation value.
 	 */
 	String debugOn() default "";
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
index f02087f22..e939d7c81 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
@@ -1109,8 +1109,6 @@ public class RestAnnotation {
 			string(a.allowedMethodHeaders()).ifPresent(x -> b.allowedMethodHeaders(x));
 			string(a.allowedMethodParams()).ifPresent(x -> b.allowedMethodParams(x));
 			bool(a.renderResponseStackTraces()).ifPresent(x -> b.renderResponseStackTraces(x));
-			string(a.debug()).map(Enablement::fromString).ifPresent(x -> b.debug(x));
-			string(a.debugOn()).ifPresent(x -> b.debugOn(x));
 		}
 	}
 
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
index acd47cf8d..86a8c4bf8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/BasicDebugEnablement.java
@@ -12,82 +12,104 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest.debug;
 
-import static org.apache.juneau.internal.ObjectUtils.*;
-import static org.apache.juneau.Enablement.*;
-import static org.apache.juneau.collections.JsonMap.*;
+import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.rest.annotation.RestOpAnnotation.*;
 
-import java.lang.reflect.*;
-import java.util.function.*;
-
-import javax.servlet.http.*;
+import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.cp.*;
+import org.apache.juneau.reflect.*;
 import org.apache.juneau.rest.*;
-import org.apache.juneau.utils.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.svl.*;
 
 /**
  * Default implementation of the {@link DebugEnablement} interface.
+ * 
+ * <p>
+ * Enables debug mode based on the following annotations:
+ * <ul>
+ * 	<li class='ja'>{@link Rest#debug()}
+ * 	<li class='ja'>{@link RestOp#debug()}
+ * 	<li class='ja'>{@link Rest#debugOn()}
+ * </ul>
  *
  * <ul class='seealso'>
  * 	<li class='link'>{@doc jrs.LoggingAndDebugging}
  * 	<li class='extlink'>{@source}
  * </ul>
  */
-public class BasicDebugEnablement implements DebugEnablement {
-
-	private final Enablement defaultEnablement;
-	private final ReflectionMap<Enablement> enablementMap;
-	private final Predicate<HttpServletRequest> conditionalPredicate;
+public class BasicDebugEnablement extends DebugEnablement {
 
 	/**
 	 * Constructor.
 	 *
-	 * @param builder The builder containing the settings for this bean.
+	 * @param beanStore The bean store containing injectable beans for this enablement.
 	 */
-	public BasicDebugEnablement(DebugEnablement.Builder builder) {
-		this.defaultEnablement = firstNonNull(builder.defaultEnablement, NEVER);
-		this.enablementMap = builder.mapBuilder.build();
-		this.conditionalPredicate = firstNonNull(builder.conditional, x -> "true".equalsIgnoreCase(x.getHeader("Debug")));
+	public BasicDebugEnablement(BeanStore beanStore) {
+		super(beanStore);
 	}
 
 	@Override
-	public boolean isDebug(RestOpContext context, HttpServletRequest req) {
-		Method m = context.getJavaMethod();
-		Enablement e = enablementMap.find(m).orElse(enablementMap.find(m.getDeclaringClass()).orElse(defaultEnablement));
-		return e == ALWAYS || (e == CONDITIONAL && isConditionallyEnabled(req));
-	}
+	protected Builder init(BeanStore beanStore) {
+		Builder b = super.init(beanStore);
 
-	@Override
-	public boolean isDebug(RestContext context, HttpServletRequest req) {
-		Class<?> c = context.getResourceClass();
-		Enablement e = enablementMap.find(c).orElse(defaultEnablement);
-		return e == ALWAYS || (e == CONDITIONAL && isConditionallyEnabled(req));
-	}
+		DefaultSettingsMap defaultSettings = beanStore.getBean(DefaultSettingsMap.class).get();
+		RestContext.Builder builder = beanStore.getBean(RestContext.Builder.class).get();
+		ResourceSupplier resource = beanStore.getBean(ResourceSupplier.class).get();
+		VarResolver varResolver = beanStore.getBean(VarResolver.class).get();
 
-	/**
-	 * Returns <jk>true</jk> if debugging is conditionally enabled on the specified request.
-	 *
-	 * <p>
-	 * This method only gets called when the enablement value resolves to {@link Enablement#CONDITIONAL CONDITIONAL}.
-	 *
-	 * <p>
-	 * Subclasses can override this method to provide their own implementation.
-	 * The default implementation is provided by {@link DebugEnablement.Builder#conditional(Predicate)}
-	 * which has a default predicate of <c><jv>x</jv> -&gt; <js>"true"</js>.equalsIgnoreCase(<jv>x</jv>.getHeader(<js>"Debug"</js>)</c>.
-	 *
-	 * @param req The incoming HTTP request.
-	 * @return <jk>true</jk> if debugging is conditionally enabled on the specified request.
-	 */
-	protected boolean isConditionallyEnabled(HttpServletRequest req) {
-		return conditionalPredicate.test(req);
-	}
+		// Default debug enablement if not overridden at class/method level.
+		Enablement debugDefault = defaultSettings.get(Enablement.class, "RestContext.debugDefault").orElse(builder.isDebug() ? Enablement.ALWAYS : Enablement.NEVER);
+		b.defaultEnable(debugDefault);
+
+		ClassInfo ci = ClassInfo.ofProxy(resource.get());
+
+		// Gather @Rest(debug) settings.
+		ci.forEachAnnotation(
+			Rest.class,
+			x -> true,
+			x -> {
+				String x2 = varResolver.resolve(x.debug());
+				if (! x2.isEmpty())
+					b.enable(Enablement.fromString(x2), ci.getFullName());
+			}
+		);
+
+		// Gather @RestOp(debug) settings.
+		ci.forEachPublicMethod(
+			x -> true,
+			x -> {
+				x.getAnnotationList(REST_OP_GROUP).forEachValue(
+					String.class,
+					"debug",
+					y -> true,
+					y -> {
+						String y2 = varResolver.resolve(y);
+						if (! y2.isEmpty())
+							b.enable(Enablement.fromString(y2), x.getFullName());
+					}
+				);
+			}
+		);
+
+		// Gather @Rest(debugOn) settings.
+		ci.forEachAnnotation(
+			Rest.class,
+			x -> true,
+			x -> {
+				String x2 = varResolver.resolve(x.debugOn());
+				for (Map.Entry<String,String> e : splitMap(x2, true).entrySet()) {
+					String k = e.getKey(), v = e.getValue();
+					if (v.isEmpty())
+						v = "ALWAYS";
+					if (! k.isEmpty())
+						b.enable(Enablement.fromString(v), k);
+				}
+			}
+		);
 
-	@Override /* Object */
-	public String toString() {
-		return filteredMap()
-			.append("defaultEnablement", defaultEnablement)
-			.append("enablementMap", enablementMap)
-			.append("conditionalPredicate", conditionalPredicate)
-			.asString();
+		return b;
 	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
index 3eb452965..a3cfa5bad 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/debug/DebugEnablement.java
@@ -13,8 +13,11 @@
 package org.apache.juneau.rest.debug;
 
 import static org.apache.juneau.Enablement.*;
+import static org.apache.juneau.collections.JsonMap.*;
+import static org.apache.juneau.internal.ObjectUtils.*;
 import static org.apache.juneau.rest.HttpRuntimeException.*;
 
+import java.lang.reflect.Method;
 import java.util.function.*;
 
 import javax.servlet.http.*;
@@ -34,7 +37,7 @@ import org.apache.juneau.utils.*;
  * 	<li class='extlink'>{@source}
  * </ul>
  */
-public interface DebugEnablement {
+public abstract class DebugEnablement {
 
 	//-----------------------------------------------------------------------------------------------------------------
 	// Static
@@ -43,7 +46,11 @@ public interface DebugEnablement {
 	/**
 	 * Represents no DebugEnablement.
 	 */
-	public abstract class Void implements DebugEnablement {};
+	public abstract class Void extends DebugEnablement {
+		Void(BeanStore beanStore) {
+			super(beanStore);
+		}
+	};
 
 	/**
 	 * Static creator.
@@ -62,7 +69,7 @@ public interface DebugEnablement {
 	/**
 	 * Builder class.
 	 */
-	public class Builder {
+	public static class Builder {
 
 		ReflectionMap.Builder<Enablement> mapBuilder;
 		Enablement defaultEnablement = NEVER;
@@ -214,6 +221,48 @@ public interface DebugEnablement {
 	// Instance
 	//-----------------------------------------------------------------------------------------------------------------
 
+	private final Enablement defaultEnablement;
+	private final ReflectionMap<Enablement> enablementMap;
+	private final Predicate<HttpServletRequest> conditionalPredicate;
+
+	/**
+	 * Constructor.
+	 * <p>
+	 * Subclasses typically override the {@link #init(BeanStore)} method when using this constructor.
+	 *
+	 * @param beanStore The bean store containing injectable beans for this enablement.
+	 */
+	public DebugEnablement(BeanStore beanStore) {
+		Builder builder = init(beanStore);
+		this.defaultEnablement = firstNonNull(builder.defaultEnablement, NEVER);
+		this.enablementMap = builder.mapBuilder.build();
+		this.conditionalPredicate = firstNonNull(builder.conditional, x -> "true".equalsIgnoreCase(x.getHeader("Debug")));
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param builder The builder for this enablement.
+	 */
+	public DebugEnablement(Builder builder) {
+		this.defaultEnablement = firstNonNull(builder.defaultEnablement, NEVER);
+		this.enablementMap = builder.mapBuilder.build();
+		this.conditionalPredicate = firstNonNull(builder.conditional, x -> "true".equalsIgnoreCase(x.getHeader("Debug")));
+
+	}
+
+	/**
+	 * Initializer.
+	 * <p>
+	 * Subclasses should override this method to make modifications to the builder used to create this logger.
+	 *
+	 * @param beanStore The bean store containing injectable beans for this logger.
+	 * @return A new builder object.
+	 */
+	protected Builder init(BeanStore beanStore) {
+		return new Builder(beanStore);
+	}
+
 	/**
 	 * Returns <jk>true</jk> if debug is enabled on the specified class and request.
 	 *
@@ -225,7 +274,11 @@ public interface DebugEnablement {
 	 * @param req The HTTP request.
 	 * @return <jk>true</jk> if debug is enabled on the specified method and request.
 	 */
-	public boolean isDebug(RestContext context, HttpServletRequest req);
+	public boolean isDebug(RestContext context, HttpServletRequest req) {
+		Class<?> c = context.getResourceClass();
+		Enablement e = enablementMap.find(c).orElse(defaultEnablement);
+		return e == ALWAYS || (e == CONDITIONAL && isConditionallyEnabled(req));
+	}
 
 	/**
 	 * Returns <jk>true</jk> if debug is enabled on the specified method and request.
@@ -238,5 +291,36 @@ public interface DebugEnablement {
 	 * @param req The HTTP request.
 	 * @return <jk>true</jk> if debug is enabled on the specified method and request.
 	 */
-	public boolean isDebug(RestOpContext context, HttpServletRequest req);
+	public boolean isDebug(RestOpContext context, HttpServletRequest req) {
+		Method m = context.getJavaMethod();
+		Enablement e = enablementMap.find(m).orElse(enablementMap.find(m.getDeclaringClass()).orElse(defaultEnablement));
+		return e == ALWAYS || (e == CONDITIONAL && isConditionallyEnabled(req));
+	}
+
+	/**
+	 * Returns <jk>true</jk> if debugging is conditionally enabled on the specified request.
+	 *
+	 * <p>
+	 * This method only gets called when the enablement value resolves to {@link Enablement#CONDITIONAL CONDITIONAL}.
+	 *
+	 * <p>
+	 * Subclasses can override this method to provide their own implementation.
+	 * The default implementation is provided by {@link DebugEnablement.Builder#conditional(Predicate)}
+	 * which has a default predicate of <c><jv>x</jv> -&gt; <js>"true"</js>.equalsIgnoreCase(<jv>x</jv>.getHeader(<js>"Debug"</js>)</c>.
+	 *
+	 * @param req The incoming HTTP request.
+	 * @return <jk>true</jk> if debugging is conditionally enabled on the specified request.
+	 */
+	protected boolean isConditionallyEnabled(HttpServletRequest req) {
+		return conditionalPredicate.test(req);
+	}
+
+	@Override /* Object */
+	public String toString() {
+		return filteredMap()
+			.append("defaultEnablement", defaultEnablement)
+			.append("enablementMap", enablementMap)
+			.append("conditionalPredicate", conditionalPredicate)
+			.asString();
+	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
index ffe38a9f8..7a471c8f8 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/logger/CallLogger.java
@@ -84,7 +84,6 @@ import org.apache.juneau.rest.util.*;
  * <ul class='seealso'>
  * 	<li class='jm'>{@link org.apache.juneau.rest.RestContext.Builder#callLogger()}
  * 	<li class='jm'>{@link org.apache.juneau.rest.RestContext.Builder#debugEnablement()}
- * 	<li class='jm'>{@link org.apache.juneau.rest.RestContext.Builder#debugOn(String)}
  * 	<li class='ja'>{@link Rest#debug}
  * 	<li class='ja'>{@link RestOp#debug}
  * 	<li class='link'>{@doc jrs.LoggingAndDebugging}
@@ -714,7 +713,6 @@ public class CallLogger {
 	 * @param req The HTTP request being logged.
 	 * @return <jk>true</jk> if debug is enabled on this request.
 	 * @see org.apache.juneau.rest.RestContext.Builder#debugEnablement()
-	 * @see org.apache.juneau.rest.RestContext.Builder#debugOn(String)
 	 * @see Rest#debug()
 	 * @see RestOp#debug()
 	 */