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 40fa637e06 TODO-365: rename commons Logger to RichLogger
40fa637e06 is described below
commit 40fa637e062b62b349f4dc5dbaeec14da44b7d1a
Author: James Bognar <[email protected]>
AuthorDate: Sat Aug 15 13:35:19 2026 -0400
TODO-365: rename commons Logger to RichLogger
Add pluggable MessageGenerator (printf default, MessageFormat, custom via
builder), canonical-vs-view identity model with a weak-valued registry, unified
log(LogRecord) capture interception with parent-chain listener propagation,
capture ergonomics on LogRecordCapture, and fix the parent==null
unregistered-logger dead-output bug. Sweep production + example consumers off
the old commons Logger. Docs updated in juneau-docs. Also resolves Eclipse
resource/deprecation warnings surfaced by [...]
---
.../apache/juneau/bean/openapi3/ui/OpenApiUI.java | 2 +-
.../apache/juneau/bean/swagger/ui/SwaggerUI.java | 2 +-
.../juneau/commons/inject/BasicBeanStore.java | 5 +-
.../juneau/commons/inject/BeanInstantiator.java | 2 +-
.../apache/juneau/commons/logging/LogRecord.java | 48 +++-
.../juneau/commons/logging/LogRecordCapture.java | 72 ++++-
.../juneau/commons/logging/LogRecordListener.java | 6 +-
...ogRecordListener.java => MessageGenerator.java} | 32 ++-
.../logging/{Logger.java => RichLogger.java} | 269 ++++++++++++++++--
.../juneau/commons/logging/package-info.java | 2 +-
.../org/apache/juneau/commons/utils/IoUtils.java | 2 +-
.../apache/juneau/commons/utils/SystemUtils.java | 2 +-
.../juneau/commons/utils/ThrowableUtils.java | 2 +-
.../commons/inject/BeanInstantiator_Test.java | 10 +-
.../commons/logging/LogRecordCapture_Test.java | 94 ++++++-
.../juneau/commons/logging/LogRecord_Test.java | 60 ++++
.../{Logger_Test.java => RichLogger_Test.java} | 312 +++++++++++++++++++--
.../marshall/serializer/WriterSerializer.java | 2 +-
.../juneau/examples/bean/atom/AtomHtmlExample.java | 2 +-
.../examples/core/html/HtmlComplexExample.java | 2 +-
.../examples/core/html/HtmlSimpleExample.java | 4 +-
.../examples/core/json/JsonComplexExample.java | 2 +-
.../core/json/JsonConfigurationExample.java | 6 +-
.../examples/core/json/JsonSimpleExample.java | 4 +-
.../juneau/examples/core/oapi/OapiExample.java | 2 +-
.../juneau/examples/core/svl/SvlExample.java | 14 +-
.../examples/core/uon/UonComplexExample.java | 2 +-
.../juneau/examples/core/uon/UonExample.java | 2 +-
.../examples/core/xml/XmlComplexExample.java | 2 +-
.../examples/core/xml/XmlConfigurationExample.java | 10 +-
.../juneau/examples/core/xml/XmlSimpleExample.java | 2 +-
.../org/apache/juneau/rest/server/RestContext.java | 20 +-
.../apache/juneau/rest/server/RestResponse.java | 1 -
.../juneau/rest/server/servlet/RestResource.java | 4 +-
.../juneau/rest/server/servlet/RestServlet.java | 4 +-
.../swagger/BasicSwaggerProviderSession.java | 2 +-
.../server/util/CachingHttpServletRequest.java | 3 +
.../server/config/repository/GitControl.java | 4 +-
.../server/config/repository/GitControl_Test.java | 29 +-
39 files changed, 889 insertions(+), 156 deletions(-)
diff --git
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
index ce0435c710..3f32923abf 100644
---
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
+++
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/ui/OpenApiUI.java
@@ -75,7 +75,7 @@ public class OpenApiUI extends ObjectSwap<OpenApi,Div> {
private static final Set<String> STANDARD_METHODS = set("get", "put",
"post", "delete", "options", "head", "patch", "trace");
- private static final Logger LOG = Logger.getLogger(OpenApiUI.class);
+ private static final RichLogger LOG =
RichLogger.getLogger(OpenApiUI.class);
@SuppressWarnings({
"null" // Null analysis not applicable for optional values
diff --git
a/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
b/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
index ecb8ab50e7..f980135c5c 100644
---
a/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
+++
b/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/ui/SwaggerUI.java
@@ -73,7 +73,7 @@ public class SwaggerUI extends ObjectSwap<Swagger,Div> {
private static final Set<String> STANDARD_METHODS = set("get", "put",
"post", "delete", "options");
- private static final Logger LOG = Logger.getLogger(SwaggerUI.class);
+ private static final RichLogger LOG =
RichLogger.getLogger(SwaggerUI.class);
private static Div examples(Session s, ParameterInfo pi) {
// @formatter:off
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
index 089ee8e882..eef842adef 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BasicBeanStore.java
@@ -25,9 +25,8 @@ import static org.apache.juneau.commons.utils.StringUtils.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
-import java.util.logging.*;
-
import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.commons.logging.*;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.settings.*;
@@ -69,7 +68,7 @@ import org.apache.juneau.commons.settings.*;
"resource" // BeanStore is a fluent AutoCloseable; self-returns and
owned/sentinel stores are not new resources to close.
})
public class BasicBeanStore implements WritableBeanStore {
- private static final Logger LOGGER =
Logger.getLogger(BasicBeanStore.class.getName());
+ private static final RichLogger LOGGER =
RichLogger.getLogger(BasicBeanStore.class.getName());
/**
* Static reusable empty instance.
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
index 6da72f45f1..9ddfd7ae5c 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/inject/BeanInstantiator.java
@@ -214,7 +214,7 @@ public class BeanInstantiator<T> {
/** Default builder class names for auto-detection. */
protected static final Set<String> DEFAULT_BUILDER_CLASS_NAMES =
u(set("Builder"));
- private static final Logger logger =
Logger.getLogger(BeanInstantiator.class);
+ private static final RichLogger logger =
RichLogger.getLogger(BeanInstantiator.class);
/**
* Lifecycle scope for the bean being created.
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
index ff87597079..2424e0c0dd 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecord.java
@@ -37,14 +37,14 @@ import java.util.logging.*;
*
* <h5 class='section'>Usage:</h5>
* <p>
- * This class is used internally by {@link Logger} when logging formatted
messages.
+ * This class is used internally by {@link RichLogger} when logging formatted
messages.
* The formatted message is only computed when the LogRecord's message is
actually
* accessed (e.g., by a Handler or Formatter).
* </p>
*
* <h5 class='section'>Formatting contract:</h5>
* <p>
- * Juneau {@link Logger} patterns are <b>printf-style</b> (rendered here via
{@link org.apache.juneau.commons.utils.StringUtils#format(String, Object...)}).
+ * Juneau {@link RichLogger} patterns are <b>printf-style</b> (rendered here
via {@link org.apache.juneau.commons.utils.StringUtils#format(String,
Object...)}).
* Because this override returns the already-substituted message, downstream
{@link java.util.logging.Handler Handlers} and
* {@link java.util.logging.Formatter Formatters} that call {@link
#getMessage()} see fully-rendered text and never re-apply
* formatting — so the printf-vs-MessageFormat choice is purely internal. The
JDK's {@link java.text.MessageFormat}
@@ -54,7 +54,7 @@ import java.util.logging.*;
* </p>
*
* <h5 class='section'>See Also:</h5><ul>
- * <li class='jc'>{@link Logger}
+ * <li class='jc'>{@link RichLogger}
* <li class='jc'>{@link
org.apache.juneau.commons.utils.StringUtils#format(String, Object...)}
* <li class='jc'>{@link java.util.logging.LogRecord}
* </ul>
@@ -69,6 +69,7 @@ import java.util.logging.*;
public class LogRecord extends java.util.logging.LogRecord {
private static final long serialVersionUID = 1L;
+ private transient MessageGenerator generator;
// Key constants for format placeholders
private static final String KEY_date = "date";
@@ -91,10 +92,25 @@ public class LogRecord extends java.util.logging.LogRecord {
* @param throwable The throwable, or <jk>null</jk> if none.
*/
public LogRecord(String loggerName, Level level, String msg, Object[]
parameters, Throwable throwable) {
+ this(loggerName, level, msg, parameters, throwable,
MessageGenerator.PRINTF);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param loggerName The logger name. Can be <jk>null</jk>.
+ * @param level The log level. Must not be <jk>null</jk>.
+ * @param msg The message pattern (will be used as-is if args is null
or empty).
+ * @param parameters The format arguments, or <jk>null</jk> if none.
+ * @param throwable The throwable, or <jk>null</jk> if none.
+ * @param generator The message generator.
+ */
+ public LogRecord(String loggerName, Level level, String msg, Object[]
parameters, Throwable throwable, MessageGenerator generator) {
super(level, msg);
setLoggerName(loggerName);
setParameters(isNotEmptyArray(parameters) ? parameters : null);
setThrown(throwable);
+ this.generator = generator == null ? MessageGenerator.PRINTF :
generator;
}
/**
@@ -110,7 +126,10 @@ public class LogRecord extends java.util.logging.LogRecord
{
public String getMessage() {
var m = super.getMessage();
var p = getParameters();
- return isEmptyArray(p) ? m : f(m, p);
+ if (isEmptyArray(p))
+ return m;
+ var messageGenerator = generator == null ?
MessageGenerator.PRINTF : generator;
+ return safeOptCatch(() -> messageGenerator.format(m, p), x ->
m).orElse(m);
}
/**
@@ -202,11 +221,22 @@ public class LogRecord extends
java.util.logging.LogRecord {
* @see SimpleFormatter
* @see Formatter
*/
+ public String formatted(String format) {
+ return formatted(this, format);
+ }
+
+ /**
+ * Formats a JUL log record with the same placeholders as {@link
#formatted(String)}.
+ *
+ * @param record The record to format.
+ * @param format The format string.
+ * @return The formatted string.
+ */
@SuppressWarnings({
"deprecation" // Date constructor is deprecated but needed for
compatibility
})
- public String formatted(String format) {
- var date = new Date(getMillis());
+ public static String formatted(java.util.logging.LogRecord record,
String format) {
+ var date = new Date(record.getMillis());
Function<String,Object> resolver = key -> switch (key) {
case KEY_date -> "%1$s";
@@ -215,11 +245,11 @@ public class LogRecord extends
java.util.logging.LogRecord {
case KEY_msg -> "%4$s";
case KEY_thrown -> "%5$s";
case KEY_timestamp -> new
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(date);
- case KEY_thread, KEY_threadid -> s(getThreadID());
- case KEY_exception -> o(getThrown()).map(x ->
x.getMessage()).orElse("");
+ case KEY_thread, KEY_threadid ->
s(record.getThreadID());
+ case KEY_exception -> o(record.getThrown()).map(x ->
x.getMessage()).orElse("");
default -> "";
};
- return safeOptCatch(()->f(formatNamed(format, resolver), date,
getLoggerName(), getLevel(), getMessage(), getThrown()), x ->
x.getLocalizedMessage()).orElse(null);
+ return safeOptCatch(() -> f(formatNamed(format, resolver),
date, record.getLoggerName(), record.getLevel(), record.getMessage(),
record.getThrown()), x -> x.getLocalizedMessage()).orElse(null);
}
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
index 9829527e5d..932d0fee94 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordCapture.java
@@ -18,6 +18,8 @@ package org.apache.juneau.commons.logging;
import java.io.*;
import java.util.*;
+import java.util.function.*;
+import java.util.regex.*;
/**
* Captures log records for testing purposes.
@@ -30,7 +32,7 @@ import java.util.*;
* <h5 class='section'>Usage:</h5>
* <p class='bjava'>
* <jc>// Capture records using try-with-resources</jc>
- * <jk>try</jk> (LogRecordCapture <jv>capture</jv> =
Logger.getLogger(MyClass.<jk>class</jk>).captureEvents()) {
+ * <jk>try</jk> (LogRecordCapture <jv>capture</jv> =
RichLogger.getLogger(MyClass.<jk>class</jk>).captureEvents()) {
* <jv>logger</jv>.info(<js>"Test message"</js>);
* <jv>logger</jv>.warning(<js>"Warning message"</js>);
*
@@ -50,22 +52,31 @@ import java.util.*;
* </p>
*
* <h5 class='section'>See Also:</h5><ul>
- * <li class='jc'>{@link Logger#captureEvents()}
+ * <li class='jc'>{@link RichLogger#captureEvents()}
* <li class='jc'>{@link LogRecord#formatted(String)}
* </ul>
*/
public class LogRecordCapture implements LogRecordListener, Closeable {
- private final Logger logger;
- private final List<LogRecord> records =
Collections.synchronizedList(new ArrayList<>());
+ private final RichLogger logger;
+ private final List<RichLogger> pinnedLoggers = new ArrayList<>();
+ private final List<java.util.logging.LogRecord> records =
Collections.synchronizedList(new ArrayList<>());
+ private final Predicate<java.util.logging.LogRecord> filter;
/**
* Constructor.
*
* @param logger The logger to capture records from.
*/
- LogRecordCapture(Logger logger) {
+ LogRecordCapture(RichLogger logger) {
+ this(logger, x -> true);
+ }
+
+ LogRecordCapture(RichLogger logger,
Predicate<java.util.logging.LogRecord> filter) {
this.logger = logger;
+ this.filter = filter == null ? x -> true : filter;
+ pinnedLoggers.add(logger);
+ RichLogger.forEachLiveAncestor(logger.getName(),
pinnedLoggers::add);
logger.addLogRecordListener(this);
}
@@ -75,8 +86,9 @@ public class LogRecordCapture implements LogRecordListener,
Closeable {
* @param rec The log record that was logged.
*/
@Override
- public void onLogRecord(LogRecord rec) {
- records.add(rec);
+ public void onLogRecord(java.util.logging.LogRecord rec) {
+ if (filter.test(rec))
+ records.add(rec);
}
/**
@@ -84,7 +96,7 @@ public class LogRecordCapture implements LogRecordListener,
Closeable {
*
* @return An unmodifiable list of captured LogRecords.
*/
- public List<LogRecord> getRecords() {
+ public List<java.util.logging.LogRecord> getRecords() {
synchronized (records) {
return List.copyOf(records);
}
@@ -102,8 +114,7 @@ public class LogRecordCapture implements LogRecordListener,
Closeable {
public List<String> getRecords(String format) {
synchronized (records) {
return records.stream()
- .map(LogRecord.class::cast)
- .map(x -> x.formatted(format))
+ .map(x -> LogRecord.formatted(x, format))
.toList();
}
}
@@ -133,11 +144,52 @@ public class LogRecordCapture implements
LogRecordListener, Closeable {
return records.isEmpty();
}
+ public List<String> messages() {
+ synchronized (records) {
+ return
records.stream().map(java.util.logging.LogRecord::getMessage).toList();
+ }
+ }
+
+ public java.util.logging.LogRecord last() {
+ synchronized (records) {
+ return records.isEmpty() ? null :
records.get(records.size() - 1);
+ }
+ }
+
+ public List<java.util.logging.LogRecord>
byLevel(java.util.logging.Level level) {
+ synchronized (records) {
+ return records.stream().filter(x ->
x.getLevel().equals(level)).toList();
+ }
+ }
+
+ public List<java.util.logging.LogRecord> matching(String regex) {
+ var p = Pattern.compile(regex);
+ synchronized (records) {
+ return records.stream().filter(x ->
p.matcher(x.getMessage()).find()).toList();
+ }
+ }
+
+ public String assertMessage() {
+ var r = last();
+ return r == null ? null : r.getMessage();
+ }
+
+ public java.util.logging.Level assertLevel() {
+ var r = last();
+ return r == null ? null : r.getLevel();
+ }
+
+ public Throwable assertThrown() {
+ var r = last();
+ return r == null ? null : r.getThrown();
+ }
+
/**
* Closes this capture and removes it from the logger's listeners.
*/
@Override
public void close() {
logger.removeLogRecordListener(this);
+ pinnedLoggers.clear();
}
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
index d70ea1e133..981606ff78 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
@@ -20,12 +20,12 @@ package org.apache.juneau.commons.logging;
* Interface for listening to log records.
*
* <p>
- * Implementations of this interface can be registered with a {@link Logger}
to receive
+ * Implementations of this interface can be registered with a {@link
RichLogger} to receive
* notifications when log records are logged.
*
* <h5 class='section'>See Also:</h5><ul>
* <li class='jc'>{@link LogRecordCapture}
- * <li class='jc'>{@link Logger}
+ * <li class='jc'>{@link RichLogger}
* </ul>
*/
public interface LogRecordListener {
@@ -35,5 +35,5 @@ public interface LogRecordListener {
*
* @param rec The log record that was logged.
*/
- void onLogRecord(LogRecord rec);
+ void onLogRecord(java.util.logging.LogRecord rec);
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/MessageGenerator.java
similarity index 52%
copy from
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
copy to
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/MessageGenerator.java
index d70ea1e133..7003e50414 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/LogRecordListener.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/MessageGenerator.java
@@ -17,23 +17,27 @@
package org.apache.juneau.commons.logging;
/**
- * Interface for listening to log records.
- *
- * <p>
- * Implementations of this interface can be registered with a {@link Logger}
to receive
- * notifications when log records are logged.
- *
- * <h5 class='section'>See Also:</h5><ul>
- * <li class='jc'>{@link LogRecordCapture}
- * <li class='jc'>{@link Logger}
- * </ul>
+ * Strategy interface for rendering log message patterns.
*/
-public interface LogRecordListener {
+@FunctionalInterface
+public interface MessageGenerator {
+
+ /**
+ * Printf-style message generator backed by {@link #format(String,
Object...)}.
+ */
+ MessageGenerator PRINTF = (pattern, args) ->
org.apache.juneau.commons.utils.StringUtils.format(pattern, args);
+
+ /**
+ * MessageFormat-style message generator backed by {@link
org.apache.juneau.commons.utils.StringUtils#mformat(String, Object...)}.
+ */
+ MessageGenerator MESSAGE_FORMAT = (pattern, args) ->
org.apache.juneau.commons.utils.StringUtils.mformat(pattern, args);
/**
- * Called when a log record is logged.
+ * Renders a message pattern with arguments.
*
- * @param rec The log record that was logged.
+ * @param pattern The message pattern.
+ * @param args The message arguments.
+ * @return The rendered message.
*/
- void onLogRecord(LogRecord rec);
+ String format(String pattern, Object...args);
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/Logger.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/RichLogger.java
similarity index 63%
rename from
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/Logger.java
rename to
juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/RichLogger.java
index 1675efa936..6b4209d8e0 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/Logger.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/RichLogger.java
@@ -19,8 +19,10 @@ package org.apache.juneau.commons.logging;
import static java.util.logging.Level.*;
import static org.apache.juneau.commons.utils.Shorts.*;
+import java.lang.ref.*;
import java.util.*;
import java.util.concurrent.*;
+import java.util.function.*;
import java.util.logging.*;
/**
@@ -31,7 +33,7 @@ import java.util.logging.*;
* an underlying {@link java.util.logging.Logger} instance. This design
provides:
* </p>
* <ul>
- * <li>Type compatibility - extends Logger for use anywhere a Logger is
expected
+ * <li>Type compatibility - extends RichLogger for use anywhere a
RichLogger is expected
* <li>Flexibility - can wrap existing logger instances
* <li>Convenience methods for formatted logging using {@link
org.apache.juneau.commons.utils.Shorts#fs(String, Object...)}
* </ul>
@@ -55,7 +57,7 @@ import java.util.logging.*;
* </ul>
*
* <p>
- * Juneau {@link Logger} patterns are printf-style. Because {@link
LogRecord#getMessage()} returns the
+ * Juneau {@link RichLogger} patterns are printf-style. Because {@link
LogRecord#getMessage()} returns the
* already-substituted message, the {@link java.text.MessageFormat} parameter
contract of the underlying
* {@link java.util.logging.LogRecord} only surfaces if a record is serialized
and consumed as a plain
* {@link java.util.logging.LogRecord} by a non-Juneau handler.
@@ -63,7 +65,7 @@ import java.util.logging.*;
*
* <h5 class='section'>Example:</h5>
* <p class='bjava'>
- * Logger <jv>logger</jv> =
Logger.getLogger(MyClass.<jk>class</jk>.getName());
+ * RichLogger <jv>logger</jv> =
RichLogger.getLogger(MyClass.<jk>class</jk>.getName());
*
* <jc>// Formatted logging at different levels (printf-style
placeholders)</jc>
* <jv>logger</jv>.severe(<js>"Error processing user %s: %s"</js>, userId,
error);
@@ -83,56 +85,91 @@ import java.util.logging.*;
"java:S1192", // String literals intentionally duplicated for clarity
"java:S2176" // Class name intentionally matches
java.util.logging.Logger; extends it to provide Juneau-specific logging
enhancements
})
-public class Logger extends java.util.logging.Logger {
+public class RichLogger extends java.util.logging.Logger {
/**
* Permanent registry of logger instances by name.
*
* <p>
* Uses an unbounded {@link ConcurrentHashMap} so every name maps to
exactly one
- * {@link Logger} instance for the JVM lifetime. An evictable cache
(e.g. one with a
+ * {@link RichLogger} instance for the JVM lifetime. An evictable
cache (e.g. one with a
* {@code maxSize} that clears the whole map) would cause a class-loaded
- * {@code static final Logger} field to refer to an instance that is no
longer in the
+ * {@code static final RichLogger} field to refer to an instance that
is no longer in the
* registry; a subsequent call to {@link #getLogger(String)} would then
return a new,
* different instance, breaking {@link LogRecordCapture} and any other
code that
* assumes one name == one identity.
*/
- private static final ConcurrentHashMap<String,Logger> loggers = new
ConcurrentHashMap<>();
+ private static final ConcurrentHashMap<String,LoggerRef> loggers = new
ConcurrentHashMap<>();
+ private static final ReferenceQueue<RichLogger> loggerQueue = new
ReferenceQueue<>();
+
+ private static final class LoggerRef extends WeakReference<RichLogger> {
+ final String name;
+
+ LoggerRef(String name, RichLogger referent,
ReferenceQueue<RichLogger> q) {
+ super(referent, q);
+ this.name = name;
+ }
+ }
/**
* The underlying logger instance that we delegate to.
*/
private final java.util.logging.Logger delegate;
+ private final MessageGenerator generator;
+ private final RichLogger canonical;
+
/**
* List of log record listeners.
*/
- private final List<LogRecordListener> listeners =
Collections.synchronizedList(new ArrayList<>());
+ private final List<LogRecordListener> listeners;
+ private volatile boolean useParentListeners = true;
/**
* Protected constructor - wraps an existing logger instance.
*
* @param delegate The underlying logger to delegate to. Must not be
<jk>null</jk>.
*/
- protected Logger(java.util.logging.Logger delegate) {
+ protected RichLogger(java.util.logging.Logger delegate) {
+ this(delegate, MessageGenerator.PRINTF, null);
+ }
+
+ private RichLogger(java.util.logging.Logger delegate, MessageGenerator
generator, RichLogger canonical) {
super(delegate.getName(), null);
this.delegate = delegate;
+ this.generator = generator == null ? MessageGenerator.PRINTF :
generator;
+ this.canonical = canonical == null ? this : canonical;
+ this.listeners = canonical == null ?
Collections.synchronizedList(new ArrayList<>()) : null;
}
/**
* Creates a logger for the specified name.
*
* <p>
- * This method returns the same Logger instance for a given name,
ensuring that
+ * This method returns the same RichLogger instance for a given name,
ensuring that
* listeners attached to a logger persist across multiple calls to
{@link #getLogger(String)}.
- * Logger instances are cached and automatically created using the
underlying
+ * RichLogger instances are cached and automatically created using the
underlying
* {@link java.util.logging.Logger#getLogger(String)}.
*
* @param name The logger name. Must not be <jk>null</jk>.
* @return A logger instance (cached and reused for the same name).
*/
- public static Logger getLogger(String name) {
- return loggers.computeIfAbsent(name, k -> new
Logger(java.util.logging.Logger.getLogger(k)));
+ public static RichLogger getLogger(String name) {
+ drainCollectedLoggers();
+ while (true) {
+ var currentRef = loggers.get(name);
+ var current = currentRef == null ? null :
currentRef.get();
+ if (current != null)
+ return current;
+ var created = new
RichLogger(java.util.logging.Logger.getLogger(name));
+ var createdRef = new LoggerRef(name, created,
loggerQueue);
+ if (currentRef == null) {
+ if (loggers.putIfAbsent(name, createdRef) ==
null)
+ return created;
+ } else if (loggers.replace(name, currentRef,
createdRef))
+ return created;
+ drainCollectedLoggers();
+ }
}
/**
@@ -141,10 +178,96 @@ public class Logger extends java.util.logging.Logger {
* @param clazz The class. Must not be <jk>null</jk>.
* @return A logger instance.
*/
- public static Logger getLogger(Class<?> clazz) {
+ public static RichLogger getLogger(Class<?> clazz) {
return getLogger(cn(clazz));
}
+ static RichLogger findLogger(String name) {
+ drainCollectedLoggers();
+ var ref = loggers.get(name);
+ if (ref == null)
+ return null;
+ var logger = ref.get();
+ if (logger == null)
+ loggers.remove(name, ref);
+ return logger;
+ }
+
+ static void forEachLiveAncestor(String name, Consumer<RichLogger>
action) {
+ var ancestorName = parentName(name);
+ while (ancestorName != null) {
+ var ancestor = findLogger(ancestorName);
+ if (ancestor != null)
+ action.accept(ancestor);
+ ancestorName = parentName(ancestorName);
+ }
+ }
+
+ private static String parentName(String name) {
+ if (name == null || name.isEmpty())
+ return null;
+ var i = name.lastIndexOf('.');
+ return i == -1 ? "" : name.substring(0, i);
+ }
+
+ private static void drainCollectedLoggers() {
+ LoggerRef ref;
+ while ((ref = (LoggerRef)loggerQueue.poll()) != null)
+ loggers.remove(ref.name, ref);
+ }
+
+ /**
+ * Creates a builder for the specified logger name.
+ *
+ * @param name The logger name.
+ * @return A new builder.
+ */
+ public static Builder builder(String name) {
+ return new Builder(name);
+ }
+
+ /**
+ * Creates a builder for the specified class logger name.
+ *
+ * @param clazz The class.
+ * @return A new builder.
+ */
+ public static Builder builder(Class<?> clazz) {
+ return builder(cn(clazz));
+ }
+
+ /**
+ * Builder for creating configured {@link RichLogger} views.
+ */
+ public static class Builder {
+ private final String name;
+ private MessageGenerator generator = MessageGenerator.PRINTF;
+
+ Builder(String name) {
+ this.name = name;
+ }
+
+ public Builder printf() {
+ generator = MessageGenerator.PRINTF;
+ return this;
+ }
+
+ public Builder messageFormat() {
+ generator = MessageGenerator.MESSAGE_FORMAT;
+ return this;
+ }
+
+ public Builder generator(MessageGenerator value) {
+ generator = value == null ? MessageGenerator.PRINTF :
value;
+ return this;
+ }
+
+ public RichLogger build() {
+ var canonical = getLogger(name);
+ return new RichLogger(canonical.delegate, generator,
canonical);
+ }
+ }
+
/**
* Creates a log record capture for testing.
*
@@ -158,7 +281,21 @@ public class Logger extends java.util.logging.Logger {
"resource" // Caller takes ownership of the returned
LogRecordCapture
})
public LogRecordCapture captureEvents() {
- return new LogRecordCapture(this);
+ return new LogRecordCapture(canonical);
+ }
+
+ @SuppressWarnings({
+ "resource" // Caller takes ownership of the returned
LogRecordCapture
+ })
+ public LogRecordCapture captureEvents(Level min) {
+ return new LogRecordCapture(canonical, x ->
x.getLevel().intValue() >= min.intValue());
+ }
+
+ @SuppressWarnings({
+ "resource" // Caller takes ownership of the returned
LogRecordCapture
+ })
+ public LogRecordCapture
captureEvents(Predicate<java.util.logging.LogRecord> filter) {
+ return new LogRecordCapture(canonical, filter);
}
/**
@@ -167,7 +304,7 @@ public class Logger extends java.util.logging.Logger {
* @param listener The listener to add.
*/
void addLogRecordListener(LogRecordListener listener) {
- listeners.add(listener);
+ canonical.listeners.add(listener);
}
/**
@@ -176,7 +313,46 @@ public class Logger extends java.util.logging.Logger {
* @param listener The listener to remove.
*/
void removeLogRecordListener(LogRecordListener listener) {
- listeners.remove(listener);
+ canonical.listeners.remove(listener);
+ }
+
+ public void setUseParentListeners(boolean useParentListeners) {
+ canonical.useParentListeners = useParentListeners;
+ }
+
+ public boolean isUseParentListeners() {
+ return canonical.useParentListeners;
+ }
+
+ @Override
+ public void log(java.util.logging.LogRecord record) {
+ canonical.listeners.forEach(x -> x.onLogRecord(record));
+ if (canonical.useParentListeners) {
+ var ancestors = new ArrayList<RichLogger>();
+ forEachLiveAncestor(canonical.getName(),
ancestors::add);
+ for (var ancestor : ancestors) {
+ ancestor.listeners.forEach(x ->
x.onLogRecord(record));
+ if (!ancestor.useParentListeners)
+ break;
+ }
+ }
+ delegate.log(record);
+ }
+
+ private static boolean hasAnyListenerInChain(RichLogger logger) {
+ if (!logger.listeners.isEmpty())
+ return true;
+ if (!logger.useParentListeners)
+ return false;
+ var ancestors = new ArrayList<RichLogger>();
+ forEachLiveAncestor(logger.getName(), ancestors::add);
+ for (var ancestor : ancestors) {
+ if (!ancestor.listeners.isEmpty())
+ return true;
+ if (!ancestor.useParentListeners)
+ break;
+ }
+ return false;
}
/**
@@ -196,62 +372,105 @@ public class Logger extends java.util.logging.Logger {
* @param thrown The throwable, or <jk>null</jk> if none.
*/
private void doLog(Level level, String msg, Object[] args, Throwable
thrown) {
- if (!isLoggable(level) && listeners.isEmpty())
+ if (!isLoggable(level) && !hasAnyListenerInChain(canonical))
return;
// Create LogRecord with lazy formatting support
- var rec = new LogRecord(getName(), level, msg, args, thrown);
-
- // Notify all listeners
- listeners.forEach(x -> x.onLogRecord(rec));
+ var rec = new LogRecord(getName(), level, msg, args, thrown,
generator);
+ this.log(rec);
+ }
- // Delegate to underlying logger (LogRecord extends
java.util.logging.LogRecord)
- delegate.log(rec);
+ private void logSupplier(Level level, Supplier<String> msgSupplier,
Throwable thrown) {
+ if (!isLoggable(level) && !hasAnyListenerInChain(canonical))
+ return;
+ var msg = msgSupplier == null ? null : msgSupplier.get();
+ var rec = new LogRecord(getName(), level, msg, null, thrown,
generator);
+ this.log(rec);
}
// Convenience methods with formatted strings
- // Standard Logger methods - feed through central doLog method
+ // Standard RichLogger methods - feed through central doLog method
@Override
public void severe(String msg) {
doLog(SEVERE, msg, null, null);
}
+ @Override
+ public void severe(Supplier<String> msgSupplier) {
+ logSupplier(SEVERE, msgSupplier, null);
+ }
+
@Override
public void warning(String msg) {
doLog(WARNING, msg, null, null);
}
+ @Override
+ public void warning(Supplier<String> msgSupplier) {
+ logSupplier(WARNING, msgSupplier, null);
+ }
+
@Override
public void info(String msg) {
doLog(INFO, msg, null, null);
}
+ @Override
+ public void info(Supplier<String> msgSupplier) {
+ logSupplier(INFO, msgSupplier, null);
+ }
+
@Override
public void config(String msg) {
doLog(CONFIG, msg, null, null);
}
+ @Override
+ public void config(Supplier<String> msgSupplier) {
+ logSupplier(CONFIG, msgSupplier, null);
+ }
+
@Override
public void fine(String msg) {
doLog(FINE, msg, null, null);
}
+ @Override
+ public void fine(Supplier<String> msgSupplier) {
+ logSupplier(FINE, msgSupplier, null);
+ }
+
@Override
public void finer(String msg) {
doLog(FINER, msg, null, null);
}
+ @Override
+ public void finer(Supplier<String> msgSupplier) {
+ logSupplier(FINER, msgSupplier, null);
+ }
+
@Override
public void finest(String msg) {
doLog(FINEST, msg, null, null);
}
+ @Override
+ public void finest(Supplier<String> msgSupplier) {
+ logSupplier(FINEST, msgSupplier, null);
+ }
+
@Override
public void log(Level level, String msg) {
doLog(level, msg, null, null);
}
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier) {
+ logSupplier(level, msgSupplier, null);
+ }
+
@Override
public void log(Level level, String msg, Object param1) {
doLog(level, msg, new Object[]{param1}, null);
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
index 190f0828d5..dece8ce766 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/logging/package-info.java
@@ -19,7 +19,7 @@
* Lightweight logging utilities built on top of {@code java.util.logging}.
*
* <p>
- * Provides an extended {@link org.apache.juneau.commons.logging.Logger} with
formatted-message
+ * Provides an extended {@link org.apache.juneau.commons.logging.RichLogger}
with formatted-message
* convenience methods, a lazy-formatting {@link
org.apache.juneau.commons.logging.LogRecord}, and
* facilities for {@link org.apache.juneau.commons.logging.LogRecordCapture
capturing} and
* {@link org.apache.juneau.commons.logging.LogRecordListener listening to}
log records.
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
index 3a09490e7c..ff4cf5a127 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/IoUtils.java
@@ -1031,7 +1031,7 @@ public class IoUtils {
* @param lines The lines to print. Must not be <jk>null</jk>
(individual <jk>null</jk> entries render as <js>"null"</js>).
*/
public static final void printLines(String[] lines) {
- Logger log = Logger.getLogger(IoUtils.class);
+ RichLogger log = RichLogger.getLogger(IoUtils.class);
for (var i = 0; i < lines.length; i++)
log.info(String.format("%4s:%s", i + 1, lines[i]));
}
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
index f1e9d9fc83..0269d13224 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/SystemUtils.java
@@ -30,7 +30,7 @@ import org.apache.juneau.commons.settings.*;
*/
public class SystemUtils {
- private static final Logger LOG = Logger.getLogger(SystemUtils.class);
+ private static final RichLogger LOG =
RichLogger.getLogger(SystemUtils.class);
/**
* Prevents instantiation.
diff --git
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
index 1b89279d4d..9e31eac292 100644
---
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
+++
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/ThrowableUtils.java
@@ -58,7 +58,7 @@ import org.apache.juneau.commons.settings.*;
*/
public class ThrowableUtils {
- private static final Logger LOG =
Logger.getLogger(ThrowableUtils.class);
+ private static final RichLogger LOG =
RichLogger.getLogger(ThrowableUtils.class);
static final Setting<Boolean> VERBOSE =
Settings.get().get("juneau.enableVerboseExceptions").asBoolean();
diff --git
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
index b8369fd248..6ad9fd402c 100644
---
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
+++
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/inject/BeanInstantiator_Test.java
@@ -26,7 +26,7 @@ import java.util.logging.*;
import org.apache.juneau.commons.*;
import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.utils.*;
import org.junit.jupiter.api.*;
@@ -3842,7 +3842,7 @@ class BeanInstantiator_Test extends TestBase {
void q01_loggingOnSimpleBeanCreation() {
// Get the logger instance using the same method as
BeanInstantiator static field
// This ensures we get the same cached instance
- var logger = Logger.getLogger(BeanInstantiator.class);
+ var logger =
RichLogger.getLogger(BeanInstantiator.class);
logger.setLevel(Level.FINE); // Enable FINE level
logging
try (var capture = logger.captureEvents()) {
@@ -3871,7 +3871,7 @@ class BeanInstantiator_Test extends TestBase {
*/
@Test
void q02_loggingOnBuilderBeanCreation() {
- var logger = Logger.getLogger(BeanInstantiator.class);
+ var logger =
RichLogger.getLogger(BeanInstantiator.class);
logger.setLevel(Level.FINE); // Enable FINE level
logging
try (var capture = logger.captureEvents()) {
var bean = bc(Q02_BeanWithBuilder.class).run();
@@ -3893,7 +3893,7 @@ class BeanInstantiator_Test extends TestBase {
*/
@Test
void q03_logMessagesIncludeBeanTypePrefix() {
- var logger = Logger.getLogger(BeanInstantiator.class);
+ var logger =
RichLogger.getLogger(BeanInstantiator.class);
logger.setLevel(Level.FINE); // Enable FINE level
logging
try (var capture = logger.captureEvents()) {
var bean = bc(SimpleBean.class).run();
@@ -3918,7 +3918,7 @@ class BeanInstantiator_Test extends TestBase {
*/
@Test
void q04_logMessagesWithFormatArguments() {
- var logger = Logger.getLogger(BeanInstantiator.class);
+ var logger =
RichLogger.getLogger(BeanInstantiator.class);
try (var capture = logger.captureEvents()) {
// Create a bean that will trigger logging with
format arguments
var bean = bc(Q02_BeanWithBuilder.class).run();
diff --git
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
index 2efef2beff..f1a62bc1e5 100644
---
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
+++
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecordCapture_Test.java
@@ -19,6 +19,7 @@ package org.apache.juneau.commons.logging;
import static org.apache.juneau.commons.TestAssertions.*;
import static org.junit.jupiter.api.Assertions.*;
+import java.util.*;
import java.util.logging.*;
import org.apache.juneau.commons.*;
@@ -29,8 +30,8 @@ import org.junit.jupiter.api.*;
*/
class LogRecordCapture_Test extends TestBase {
- private static Logger getLogger(String name) {
- var l = Logger.getLogger(name);
+ private static RichLogger getLogger(String name) {
+ var l = RichLogger.getLogger(name);
l.setLevel(Level.OFF);
return l;
}
@@ -273,7 +274,7 @@ class LogRecordCapture_Test extends TestBase {
}
//====================================================================================================
- // Integration with Logger
+ // Integration with RichLogger
//====================================================================================================
@Test void h01_capturesAllLevels() {
@@ -323,4 +324,91 @@ class LogRecordCapture_Test extends TestBase {
assertSame(exception, records.get(0).getThrown());
}
}
+
+ @Test void i01_capture_acceptsPlainJulRecords() {
+ var logger = RichLogger.getLogger("i01");
+ try (var capture = logger.captureEvents()) {
+ var rec = new java.util.logging.LogRecord(Level.INFO,
"plain");
+ rec.setLoggerName("i01");
+ logger.log(rec);
+
+ var records = capture.getRecords();
+ assertSize(1, records);
+ assertEquals("plain", records.get(0).getMessage());
+ assertFalse(records.get(0) instanceof LogRecord);
+ }
+ }
+
+ @Test void i02_getRecordsFormatted_handlesPlainJulRecords() {
+ var logger = RichLogger.getLogger("i02");
+ try (var capture = logger.captureEvents()) {
+ var rec = new
java.util.logging.LogRecord(Level.WARNING, "plain-format");
+ rec.setLoggerName("i02");
+ logger.log(rec);
+
+ var formatted = capture.getRecords("{level}:{msg}");
+ assertSize(1, formatted);
+ assertEquals("WARNING:plain-format", formatted.get(0));
+ }
+ }
+
+ @Test void j01_captureEvents_levelThreshold_usesJulDirection() {
+ var logger = RichLogger.getLogger("j01");
+ try (var capture = logger.captureEvents(Level.FINE)) {
+ logger.finer("finer");
+ logger.fine("fine");
+ logger.info("info");
+ logger.severe("severe");
+
+ var messages = capture.messages();
+ assertEquals(List.of("fine", "info", "severe"),
messages);
+ }
+ }
+
+ @Test void j02_captureEvents_predicate_filtersAtCaptureTime() {
+ var logger = RichLogger.getLogger("j02");
+ try (var capture = logger.captureEvents(x ->
x.getMessage().contains("keep"))) {
+ logger.info("keep-1");
+ logger.info("drop");
+ logger.info("keep-2");
+
+ assertEquals(List.of("keep-1", "keep-2"),
capture.messages());
+ assertEquals(2, capture.size());
+ }
+ }
+
+ @Test void j03_lastAndAssertHelpers_returnRawValues() {
+ var logger = RichLogger.getLogger("j03");
+ var ex = new RuntimeException("x");
+ try (var capture = logger.captureEvents()) {
+ logger.info("a");
+ logger.warning(ex, "b");
+
+ assertEquals("b", capture.assertMessage());
+ assertEquals(Level.WARNING, capture.assertLevel());
+ assertSame(ex, capture.assertThrown());
+ }
+ }
+
+ @Test void j04_last_returnsNullWhenEmpty() {
+ var logger = RichLogger.getLogger("j04");
+ try (var capture = logger.captureEvents()) {
+ assertNull(capture.last());
+ assertNull(capture.assertMessage());
+ assertNull(capture.assertLevel());
+ assertNull(capture.assertThrown());
+ }
+ }
+
+ @Test void j05_byLevelAndMatching_areQueryTimeFilters() {
+ var logger = RichLogger.getLogger("j05");
+ try (var capture = logger.captureEvents()) {
+ logger.info("abc-1");
+ logger.warning("abc-2");
+ logger.warning("xyz");
+
+ assertEquals(2, capture.byLevel(Level.WARNING).size());
+ assertEquals(2, capture.matching("abc").size());
+ }
+ }
}
diff --git
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
index a68fe91aff..1caf63b2eb 100644
---
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
+++
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/LogRecord_Test.java
@@ -18,6 +18,7 @@ package org.apache.juneau.commons.logging;
import static org.junit.jupiter.api.Assertions.*;
+import java.io.*;
import java.util.logging.*;
import java.util.stream.*;
@@ -102,6 +103,45 @@ class LogRecord_Test extends TestBase {
assertNull(rec.getParameters());
}
+ @Test void c01_getMessage_defaultGenerator_usesPrintf() {
+ var rec = new LogRecord("test.logger", Level.INFO, "%s + %s =
%s", new Object[]{1, 2, 3}, null);
+
+ assertEquals("1 + 2 = 3", rec.getMessage());
+ }
+
+ @Test void c02_getMessage_messageFormatGenerator() {
+ var rec = new LogRecord("test.logger", Level.INFO, "{0} + {1} =
{2}", new Object[]{1, 2, 3}, null, MessageGenerator.MESSAGE_FORMAT);
+
+ assertEquals("1 + 2 = 3", rec.getMessage());
+ }
+
+ @Test void c03_getMessage_customGenerator() {
+ var rec = new LogRecord("test.logger", Level.INFO, "ignored",
new Object[]{"a", "b"}, null, (pattern, args) -> "custom:" + args.length);
+
+ assertEquals("custom:2", rec.getMessage());
+ }
+
+ @Test void c04_getMessage_generatorFailureFallsBackToPattern() {
+ var rec = new LogRecord("test.logger", Level.INFO, null, new
Object[]{"a"}, null, MessageGenerator.MESSAGE_FORMAT);
+
+ assertNull(rec.getMessage());
+ }
+
+ @Test void c05_constructorCompatibility_usesPrintfGenerator() {
+ var rec1 = new LogRecord("test.logger", Level.INFO, "Value:
%s", new Object[]{42}, null);
+ var rec2 = new LogRecord("test.logger", Level.INFO, "Value:
%s", new Object[]{42}, null, MessageGenerator.PRINTF);
+
+ assertEquals(rec1.getMessage(), rec2.getMessage());
+ }
+
+ @Test void c06_deserializedRecordFallsBackToPrintfGenerator() throws
Exception {
+ var rec = new LogRecord("test.logger", Level.INFO, "{0}", new
Object[]{"value"}, null, MessageGenerator.MESSAGE_FORMAT);
+ assertEquals("value", rec.getMessage());
+
+ var rec2 = roundTrip(rec);
+ assertEquals("{0}", rec2.getMessage());
+ }
+
//====================================================================================================
// formatted() method - named placeholders
//====================================================================================================
@@ -257,4 +297,24 @@ class LogRecord_Test extends TestBase {
assertTrue(formatted.contains("Error"));
assertTrue(formatted.contains("\n"));
}
+
+ @Test void f03_formatted_static_handlesPlainJulRecord() {
+ var rec = new java.util.logging.LogRecord(Level.INFO,
"plain-jul");
+ rec.setLoggerName("test.logger");
+
+ var formatted = LogRecord.formatted(rec,
"{logger}:{level}:{msg}");
+ assertEquals("test.logger:INFO:plain-jul", formatted);
+ }
+
+ private static LogRecord roundTrip(LogRecord record) throws Exception {
+ byte[] data;
+ try (var baos = new ByteArrayOutputStream(); var oos = new
ObjectOutputStream(baos)) {
+ oos.writeObject(record);
+ oos.flush();
+ data = baos.toByteArray();
+ }
+ try (var bais = new ByteArrayInputStream(data); var ois = new
ObjectInputStream(bais)) {
+ return (LogRecord)ois.readObject();
+ }
+ }
}
diff --git
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/Logger_Test.java
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/RichLogger_Test.java
similarity index 69%
rename from
juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/Logger_Test.java
rename to
juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/RichLogger_Test.java
index f1f1acbff6..ab0d6eff14 100644
---
a/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/Logger_Test.java
+++
b/juneau-core/juneau-commons/src/test/java/org/apache/juneau/commons/logging/RichLogger_Test.java
@@ -19,28 +19,30 @@ package org.apache.juneau.commons.logging;
import static org.apache.juneau.commons.TestAssertions.*;
import static org.junit.jupiter.api.Assertions.*;
+import java.lang.ref.*;
+import java.util.concurrent.atomic.*;
import java.util.logging.*;
import org.apache.juneau.commons.*;
import org.junit.jupiter.api.*;
/**
- * Tests for {@link Logger}.
+ * Tests for {@link RichLogger}.
*/
@SuppressWarnings({
"java:S117", // Local variable name intentional for test readability.
"resource" // g01 only asserts the returned capture's initial state;
the listener is a test fixture, not a held resource
})
-class Logger_Test extends TestBase {
+class RichLogger_Test extends TestBase {
- private static Logger getLogger(String name) {
- var l = Logger.getLogger(name);
+ private static RichLogger getLogger(String name) {
+ var l = RichLogger.getLogger(name);
l.setLevel(Level.OFF);
return l;
}
- private static Logger getLogger(Class<?> class_) {
- var l = Logger.getLogger(class_);
+ private static RichLogger getLogger(Class<?> class_) {
+ var l = RichLogger.getLogger(class_);
l.setLevel(Level.OFF);
return l;
}
@@ -59,12 +61,12 @@ class Logger_Test extends TestBase {
}
@Test void a02_getLogger_byClass() {
- var logger1 = getLogger(Logger_Test.class);
- var logger2 = getLogger(Logger_Test.class);
+ var logger1 = getLogger(RichLogger_Test.class);
+ var logger2 = getLogger(RichLogger_Test.class);
assertNotNull(logger1);
assertSame(logger1, logger2); // Should return same instance
- assertEquals(Logger_Test.class.getName(), logger1.getName());
+ assertEquals(RichLogger_Test.class.getName(),
logger1.getName());
}
@Test void a03_differentNames_returnDifferentInstances() {
@@ -576,15 +578,15 @@ class Logger_Test extends TestBase {
}
//====================================================================================================
- // Logger delegation
+ // RichLogger delegation
//====================================================================================================
- @Test void f01_delegatesToUnderlyingLogger() {
+ @Test void f01_delegatesToUnderlyingRichLogger() {
var logger = getLogger("f01");
- var underlyingLogger =
java.util.logging.Logger.getLogger("f01");
+ var underlyingRichLogger =
java.util.logging.Logger.getLogger("f01");
- assertEquals(underlyingLogger.getName(), logger.getName());
- assertEquals(underlyingLogger.getLevel(), logger.getLevel());
+ assertEquals(underlyingRichLogger.getName(), logger.getName());
+ assertEquals(underlyingRichLogger.getLevel(),
logger.getLevel());
}
@Test void f02_setLevel_delegates() {
@@ -595,7 +597,7 @@ class Logger_Test extends TestBase {
}
@Test void f03_isLoggable_delegates() {
- var logger = Logger.getLogger("f03");
+ var logger = RichLogger.getLogger("f03");
logger.setLevel(Level.INFO);
assertTrue(logger.isLoggable(Level.INFO));
assertTrue(logger.isLoggable(Level.SEVERE));
@@ -629,21 +631,287 @@ class Logger_Test extends TestBase {
//====================================================================================================
// Registry stability under high logger-name volume
- // Regression for the Java-25 Q_logging failure: a Cache-backed
registry cleared itself once
- // 1000+ distinct names existed, causing getLogger(X) to return a new
instance for a name
- // whose class had pinned the old instance in a static final field.
+ // Regression guard for weak-valued registry behavior: a
strongly-referenced logger
+ // must remain identity-stable despite high distinct-name volume.
//====================================================================================================
@Test void h01_registryStable_afterManyDistinctNames() {
// Pin the instance before flooding the registry.
- var pinned = Logger.getLogger("h01.pinned");
+ var pinned = RichLogger.getLogger("h01.pinned");
// Create enough distinct logger names to exceed the old Cache
maxSize threshold (1000).
for (int i = 0; i < 1500; i++)
- Logger.getLogger("h01.flood." + i);
+ RichLogger.getLogger("h01.flood." + i);
// The registry must still return the exact same object for the
original name.
- assertSame(pinned, Logger.getLogger("h01.pinned"),
- "Logger registry must be stable: getLogger(name) must
always return the same instance");
+ assertSame(pinned, RichLogger.getLogger("h01.pinned"),
+ "RichLogger registry must be stable: getLogger(name)
must always return the same instance");
+ }
+
+
//====================================================================================================
+ // Builder views and canonical identity
+
//====================================================================================================
+
+ @Test void i01_builderBuild_returnsDistinctViews() {
+ var a = RichLogger.builder("i01").build();
+ var b = RichLogger.builder("i01").build();
+ var c = RichLogger.getLogger("i01");
+
+ assertNotSame(a, b);
+ assertNotSame(a, c);
+ assertNotSame(b, c);
+ }
+
+ @Test void i02_builderAndCanonical_shareCapture() {
+ var canonical = RichLogger.getLogger("i02");
+ var view = RichLogger.builder("i02").messageFormat().build();
+ try (var capture = canonical.captureEvents()) {
+ view.info("{0}", "value");
+
+ var records = capture.getRecords();
+ assertSize(1, records);
+ assertEquals("value", records.get(0).getMessage());
+ }
+ }
+
+ @Test void i03_builderDefault_usesPrintf() {
+ var view = RichLogger.builder("i03").build();
+ try (var capture = RichLogger.getLogger("i03").captureEvents())
{
+ view.info("%s + %s = %s", 1, 2, 3);
+
+ var records = capture.getRecords();
+ assertSize(1, records);
+ assertEquals("1 + 2 = 3", records.get(0).getMessage());
+ }
+ }
+
+ @Test void i04_builderViews_useIndependentGenerators() {
+ var printfView = RichLogger.builder("i04").printf().build();
+ var messageFormatView =
RichLogger.builder("i04").messageFormat().build();
+ var customView = RichLogger.builder("i04").generator((pattern,
args) -> "custom").build();
+ try (var capture = RichLogger.getLogger("i04").captureEvents())
{
+ printfView.info("printf %s", "x");
+ messageFormatView.info("message {0}", "x");
+ customView.info("ignored %s", "x");
+
+ var records = capture.getRecords();
+ assertSize(3, records);
+ assertEquals("printf x", records.get(0).getMessage());
+ assertEquals("message x", records.get(1).getMessage());
+ assertEquals("custom", records.get(2).getMessage());
+ }
+ }
+
+ @Test void j01_findLogger_nonCreatingLookup() {
+ var name = "j01.noncreating." + System.nanoTime();
+ assertNull(RichLogger.findLogger(name));
+ var logger = RichLogger.getLogger(name);
+ assertSame(logger, RichLogger.findLogger(name));
+ }
+
+ @Test void j02_capturePinsAncestors_whileOpen() {
+ var ancestor = RichLogger.getLogger("j02");
+ var parent = RichLogger.getLogger("j02.child");
+ var child = RichLogger.getLogger("j02.child.grandchild");
+ var ancestorRef = new WeakReference<>(ancestor);
+ var parentRef = new WeakReference<>(parent);
+ ancestor = null;
+ parent = null;
+
+ try (var capture = child.captureEvents()) {
+ assertNotNull(ancestorRef.get());
+ assertNotNull(parentRef.get());
+ }
+ }
+
+ @Test void j03_unreferencedCanonical_eventuallyCollectable() throws
Exception {
+ var name = "j03.collectable." + System.nanoTime();
+ var logger = RichLogger.getLogger(name);
+ var ref = new WeakReference<>(logger);
+ logger = null;
+
+ assertTrue(awaitCollected(ref), "Expected unreferenced
canonical to become collectable");
+ }
+
+ @Test void k01_logOverride_capturesAndPublishesOnce() {
+ var logger = RichLogger.getLogger("k01");
+ var delegate = java.util.logging.Logger.getLogger("k01");
+ var published = new AtomicInteger();
+ var h = new Handler() {
+ @Override
+ public void publish(java.util.logging.LogRecord record)
{
+ published.incrementAndGet();
+ }
+ @Override public void flush() {}
+ @Override public void close() {}
+ };
+ delegate.addHandler(h);
+ try (var capture = logger.captureEvents()) {
+ var rec = new java.util.logging.LogRecord(Level.INFO,
"plain");
+ rec.setLoggerName("k01");
+ logger.log(rec);
+ logger.info("wrapped");
+
+ var records = capture.getRecords();
+ assertSize(2, records);
+ assertEquals("plain", records.get(0).getMessage());
+ assertEquals("wrapped", records.get(1).getMessage());
+ assertEquals(2, published.get());
+ } finally {
+ delegate.removeHandler(h);
+ }
+ }
+
+ @Test void l01_parentCapture_observesDescendantLogs() {
+ var parent = RichLogger.getLogger("l01");
+ var child = RichLogger.getLogger("l01.child.leaf");
+ child.setLevel(Level.OFF);
+ try (var capture = parent.captureEvents()) {
+ child.info("from-child");
+ assertSize(1, capture.getRecords());
+ assertEquals("from-child",
capture.getRecords().get(0).getMessage());
+ }
+ }
+
+ @Test void l02_unrelatedAncestors_notNotified() {
+ var a = RichLogger.getLogger("l02.a");
+ var b = RichLogger.getLogger("l02.b.child");
+ try (var capture = a.captureEvents()) {
+ b.info("unrelated");
+ assertTrue(capture.isEmpty());
+ }
+ }
+
+ @Test void l03_emitterCanDisableParentPropagation() {
+ var parent = RichLogger.getLogger("l03");
+ var child = RichLogger.getLogger("l03.child");
+ child.setUseParentListeners(false);
+ try (var capture = parent.captureEvents()) {
+ child.info("blocked");
+ assertTrue(capture.isEmpty());
+ } finally {
+ child.setUseParentListeners(true);
+ }
+ }
+
+ @Test void l04_intermediateDisable_stopsNotifyAndGuardWalk() {
+ var far = RichLogger.getLogger("l04");
+ var mid = RichLogger.getLogger("l04.mid");
+ var leaf = RichLogger.getLogger("l04.mid.leaf");
+ mid.setUseParentListeners(false);
+ leaf.setLevel(Level.OFF);
+ try (var capture = far.captureEvents()) {
+ leaf.info("should-not-arrive");
+ assertTrue(capture.isEmpty());
+ } finally {
+ mid.setUseParentListeners(true);
+ }
+ }
+
+ @Test void l05_propagation_doesNotCreateAncestorCanonicals() {
+ var name = "l05.root." + System.nanoTime();
+ var leaf = RichLogger.getLogger(name + ".leaf");
+ assertNull(RichLogger.findLogger(name));
+ leaf.info("no-listeners");
+ assertNull(RichLogger.findLogger(name));
+ }
+
+ @Test void m01_supplierNotEvaluated_whenNoLoggingOrListeners() {
+ var logger = RichLogger.getLogger("m01");
+ logger.setLevel(Level.OFF);
+ var calls = new AtomicInteger();
+
+ logger.info(() -> {
+ calls.incrementAndGet();
+ return "value";
+ });
+
+ assertEquals(0, calls.get());
+ }
+
+ @Test void m02_supplierEvaluated_whenAncestorCaptureExists() {
+ var parent = RichLogger.getLogger("m02");
+ var child = RichLogger.getLogger("m02.child");
+ child.setLevel(Level.OFF);
+ var calls = new AtomicInteger();
+
+ try (var capture = parent.captureEvents()) {
+ child.info(() -> {
+ calls.incrementAndGet();
+ return "value";
+ });
+
+ assertEquals(1, calls.get());
+ assertSize(1, capture.getRecords());
+ assertEquals("value",
capture.getRecords().get(0).getMessage());
+ }
+ }
+
+ @Test void m03_inheritedThrowableSupplier_remainsLevelGated() {
+ var logger = RichLogger.getLogger("m03");
+ logger.setLevel(Level.OFF);
+ var calls = new AtomicInteger();
+
+ try (var capture = logger.captureEvents()) {
+ logger.log(Level.INFO, new RuntimeException("ignored"),
() -> {
+ calls.incrementAndGet();
+ return "value";
+ });
+
+ assertEquals(0, calls.get());
+ assertTrue(capture.isEmpty());
+ }
+ }
+
+ @Test void m04_delegateAccessorsStillForwardToDelegate() {
+ var logger = RichLogger.getLogger("m04");
+ var delegate = java.util.logging.Logger.getLogger("m04");
+
+ logger.setLevel(Level.WARNING);
+ assertEquals(Level.WARNING, logger.getLevel());
+ assertEquals(Level.WARNING, delegate.getLevel());
+ assertFalse(logger.isLoggable(Level.INFO));
+ assertTrue(logger.isLoggable(Level.SEVERE));
+ }
+
+ @Test void n01_delegatePublication_reachesRootParentHandler() {
+ var name = "n01." + System.nanoTime();
+ var logger = RichLogger.getLogger(name);
+ var root = LogManager.getLogManager().getLogger("");
+ var published = new AtomicInteger();
+ var h = new Handler() {
+ @Override
+ public void publish(java.util.logging.LogRecord record)
{
+ if (name.equals(record.getLoggerName()))
+ published.incrementAndGet();
+ }
+ @Override public void flush() {}
+ @Override public void close() {}
+ };
+ // Before delegate.log(record) interception existed, wrapper
publication ended on the
+ // unregistered wrapper and never reached root handlers.
+ root.addHandler(h);
+ try (var capture = logger.captureEvents()) {
+ var rec = new
java.util.logging.LogRecord(Level.WARNING, "raw");
+ rec.setLoggerName(name);
+ logger.log(rec);
+ logger.info("commons");
+ logger.log(Level.WARNING, new
RuntimeException("ignored"), () -> "supplier");
+
+ assertSize(3, capture.getRecords());
+ assertEquals(3, published.get());
+ } finally {
+ root.removeHandler(h);
+ }
+ }
+
+ private static boolean awaitCollected(WeakReference<?> ref) throws
Exception {
+ for (int i = 0; i < 20; i++) {
+ System.gc();
+ Thread.sleep(20);
+ if (ref.get() == null)
+ return true;
+ }
+ return false;
}
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
index cb9246349c..735bf505fa 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/WriterSerializer.java
@@ -446,7 +446,7 @@ public class WriterSerializer extends Serializer implements
ThrowingFunction<Obj
* @return This object.
*/
public final WriterSerializer println(Object o) {
- Logger.getLogger(WriterSerializer.class).info(toString(o)); //
NOT DEBUG
+ RichLogger.getLogger(WriterSerializer.class).info(toString(o));
// NOT DEBUG
return this;
}
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
index a612003885..37a107eeaa 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/bean/atom/AtomHtmlExample.java
@@ -231,6 +231,6 @@ public class AtomHtmlExample {
* </tr>
* </table>
*/
- Logger.getLogger(AtomHtmlExample.class).info(s.write(feed));
+ RichLogger.getLogger(AtomHtmlExample.class).info(s.write(feed));
}
}
\ No newline at end of file
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
index 53aeb91bb3..02f03c75a4 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlComplexExample.java
@@ -69,7 +69,7 @@ public class HtmlComplexExample {
var flat = htmlSerializer.write(pojoc);
// Print out the created POJO in JSON format.
- Logger.getLogger(HtmlComplexExample.class).info(flat);
+ RichLogger.getLogger(HtmlComplexExample.class).info(flat);
var parse = htmlParser.read(flat, PojoComplex.class);
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
index f6f34368fd..b2cae26141 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/html/HtmlSimpleExample.java
@@ -46,7 +46,7 @@ public class HtmlSimpleExample {
var flat = htmlSerializer.write(pojo);
// Print out the created POJO in JSON format.
- Logger.getLogger(HtmlSimpleExample.class).info(flat);
+ RichLogger.getLogger(HtmlSimpleExample.class).info(flat);
var parse = htmlParser.read(flat, Pojo.class);
@@ -60,7 +60,7 @@ public class HtmlSimpleExample {
* </table></div></div></article></section></body></html>
*/
var docSerialized = HtmlDocSerializer.DEFAULT.write(pojo);
- Logger.getLogger(HtmlSimpleExample.class).info(docSerialized);
+
RichLogger.getLogger(HtmlSimpleExample.class).info(docSerialized);
// The object above can be parsed thanks to the
@BeanCtor(properties = id,name) annotation on Pojo
// Using this approach, you can keep your POJOs immutable, and
still serialize and deserialize them.
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
index 04e083edf3..9a4fd2c12a 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonComplexExample.java
@@ -65,7 +65,7 @@ public class JsonComplexExample {
var flat = jsonSerializer.write(pojoc);
// Print out the created POJO in JSON format.
- Logger.getLogger(JsonComplexExample.class).info(flat);
+ RichLogger.getLogger(JsonComplexExample.class).info(flat);
var parse = jsonParser.read(flat, PojoComplex.class);
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
index 2bb59bec60..a2ef0fe367 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonConfigurationExample.java
@@ -49,7 +49,7 @@ public class JsonConfigurationExample {
*/
var withWhitespace =
JsonSerializer.create().ws().build().write(aPojo);
// the output will be padded with spaces after format characters
-
Logger.getLogger(JsonConfigurationExample.class).info(withWhitespace);
+
RichLogger.getLogger(JsonConfigurationExample.class).info(withWhitespace);
/**
* Produces
@@ -57,7 +57,7 @@ public class JsonConfigurationExample {
*/
var escaped =
JsonSerializer.create().escapeSolidus().build().write(aPojo);
// the output will have escaped /
- Logger.getLogger(JsonConfigurationExample.class).info(escaped);
+
RichLogger.getLogger(JsonConfigurationExample.class).info(escaped);
/**
* Produces
@@ -73,7 +73,7 @@ public class JsonConfigurationExample {
.build()
.write(aPojo); // Create a Json5Serializer
-
Logger.getLogger(JsonConfigurationExample.class).info(configurableJson);
+
RichLogger.getLogger(JsonConfigurationExample.class).info(configurableJson);
}
}
\ No newline at end of file
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
index fbf509d20b..dd989590d1 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/json/JsonSimpleExample.java
@@ -53,7 +53,7 @@ public class JsonSimpleExample {
var flat = jsonSerializer.write(pojo);
// Print out the created POJO in JSON format.
- Logger.getLogger(JsonSimpleExample.class).info(flat);
+ RichLogger.getLogger(JsonSimpleExample.class).info(flat);
var parse = jsonParser.read(flat, Pojo.class);
@@ -61,7 +61,7 @@ public class JsonSimpleExample {
assert parse.getName().equals(pojo.getName());
var json5 = Json5Serializer.DEFAULT.write(pojo);
- Logger.getLogger(JsonSimpleExample.class).info(json5);
+ RichLogger.getLogger(JsonSimpleExample.class).info(json5);
// Parse a JSON object (creates a generic JsonMap).
var json = "{name:'John Smith',age:21}";
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
index bde17bcd08..7534c0def1 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/oapi/OapiExample.java
@@ -74,7 +74,7 @@ public class OapiExample {
// Convert POJO to BASE64-encoded string.
var s = OpenApiSerializer.DEFAULT;
var httpPart = s.getPartSession().write(HEADER, schemab, pojo);
- Logger.getLogger(OapiExample.class).info(httpPart);
+ RichLogger.getLogger(OapiExample.class).info(httpPart);
// Convert BASE64-encoded string back into a POJO.
var p = OpenApiParser.DEFAULT;
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
index 5bc4f81ca4..f0a6a11c77 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/svl/SvlExample.java
@@ -41,29 +41,29 @@ public class SvlExample {
System.out.println(vr.resolve("JAVA_HOME=$E{JAVA_HOME, not
defined}"));
// $S{key[,default]} for getting system properties (uses
System.getProperty() )
-
Logger.getLogger(SvlExample.class).info(vr.resolve("os.name=$S{os.name, not
defined}"));
+
RichLogger.getLogger(SvlExample.class).info(vr.resolve("os.name=$S{os.name, not
defined}"));
// #{if(cond, then, else)} general if or if-else condition //
NOSONAR
// #{notEmpty(s)} returns true if not empty
- Logger.getLogger(SvlExample.class).info(vr.resolve("TEST_VAR is
#{if(#{notEmpty($E{TEST_VAR})}, not empty, empty)}"));
+
RichLogger.getLogger(SvlExample.class).info(vr.resolve("TEST_VAR is
#{if(#{notEmpty($E{TEST_VAR})}, not empty, empty)}"));
// #{switch(value, pattern1, val1, ..., default)} glob-pattern
switch-case // NOSONAR
System.out.println(vr.resolve("#{switch(Carrot, *Ap*, Fruit,
*Car*, Veg, *, N/A)}"));
// #{replaceRegex(s, regex, replacement)} pattern replace
- Logger.getLogger(SvlExample.class).info(vr.resolve("Java
version=#{replaceRegex($S{java.version}, \"(_([0-9]+))\", \" build=$2\")}"));
+ RichLogger.getLogger(SvlExample.class).info(vr.resolve("Java
version=#{replaceRegex($S{java.version}, \"(_([0-9]+))\", \" build=$2\")}"));
// #{upper(s)} / #{lower(s)} case conversion
-
Logger.getLogger(SvlExample.class).info(vr.resolve("#{lower(JAVA_HOME)}
#{upper($E{JAVA_HOME})}"));
+
RichLogger.getLogger(SvlExample.class).info(vr.resolve("#{lower(JAVA_HOME)}
#{upper($E{JAVA_HOME})}"));
// #{len(s[,delimiter])} length / part count
- Logger.getLogger(SvlExample.class).info(vr.resolve("parts =
#{len($S{os.version}, \".\")}, charcount = #{len($S{os.version})}"));
+ RichLogger.getLogger(SvlExample.class).info(vr.resolve("parts =
#{len($S{os.version}, \".\")}, charcount = #{len($S{os.version})}"));
// #{substring(s, start[, end])} substring extraction
- Logger.getLogger(SvlExample.class).info(vr.resolve("version =
#{substring($S{java.version}, 0, 3)}"));
+ RichLogger.getLogger(SvlExample.class).info(vr.resolve("version
= #{substring($S{java.version}, 0, 3)}"));
// #{extract(s, regex[, group])} regex group extraction
- Logger.getLogger(SvlExample.class).info(vr.resolve("update =
#{extract($S{java.version}, \"_([0-9]+)\", 1)}"));
+ RichLogger.getLogger(SvlExample.class).info(vr.resolve("update
= #{extract($S{java.version}, \"_([0-9]+)\", 1)}"));
/*
* See all supported variable types at,
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
index 458bdace7e..40ad5ea864 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonComplexExample.java
@@ -58,7 +58,7 @@ public class UonComplexExample {
*/
var uonSerializer = UonSerializer.DEFAULT;
// This will show the final output from the bean
-
Logger.getLogger(UonComplexExample.class).info(uonSerializer.write(pojoc));
+
RichLogger.getLogger(UonComplexExample.class).info(uonSerializer.write(pojoc));
var obj = UonParser.DEFAULT.read(uonSerializer.write(pojoc),
PojoComplex.class);
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
index 2e2b51c13d..8431bece61 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/uon/UonExample.java
@@ -43,7 +43,7 @@ public class UonExample {
* (name=name,id=id)
*/
var serial = UonSerializer.DEFAULT.write(pojo);
- Logger.getLogger(UonExample.class).info(serial);
+ RichLogger.getLogger(UonExample.class).info(serial);
// Deserialize back to Pojo instance
var obj = UonParser.DEFAULT.read(serial, Pojo.class);
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
index a8d49cc776..41deb413b1 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlComplexExample.java
@@ -53,7 +53,7 @@ public class XmlComplexExample {
// Serialize to human readable XML and print
var serial = XmlSerializer.DEFAULT_SQ_READABLE.write(pojoc);
- Logger.getLogger(XmlComplexExample.class).info(serial);
+ RichLogger.getLogger(XmlComplexExample.class).info(serial);
// Deserialize back to PojoComplex instance
var obj = XmlParser.DEFAULT.read(serial, PojoComplex.class);
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
index 4c81c08ca5..4a469c44d9 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlConfigurationExample.java
@@ -51,7 +51,7 @@ public class XmlConfigurationExample {
*/
var withWhitespace =
XmlSerializer.create().ws().build().write(aPojo);
// the output will be padded with spaces after format
characters.
-
Logger.getLogger(XmlConfigurationExample.class).info(withWhitespace);
+
RichLogger.getLogger(XmlConfigurationExample.class).info(withWhitespace);
Map<String,List<Pojo>> values = m();
var pojoc = new PojoComplex("pojo", new Pojo("1.0", "name0"),
values);
@@ -60,13 +60,13 @@ public class XmlConfigurationExample {
//<object><innerPojo><name>name0</name><id>1.0</id></innerPojo><id>pojo</id></object>
var mapescaped =
XmlSerializer.create().trimEmptyMaps().build().write(pojoc);
// the output will have trimmed Empty maps.
-
Logger.getLogger(XmlConfigurationExample.class).info(mapescaped);
+
RichLogger.getLogger(XmlConfigurationExample.class).info(mapescaped);
//Produces
//<object
xmlns="http://www.apache.org/2013/Juneau"><name><pojo></name><id>a</id></object>
var nspaceToRoot =
XmlSerializer.create().ns().addNamespaceUrisToRoot().build().write(aPojo);
// the output will add default name space to the xml document
root.
-
Logger.getLogger(XmlConfigurationExample.class).info(nspaceToRoot);
+
RichLogger.getLogger(XmlConfigurationExample.class).info(nspaceToRoot);
var nPojo = new Pojo("a", null);
@@ -74,14 +74,14 @@ public class XmlConfigurationExample {
//<object><id>a</id></object>
var nullescaped = XmlSerializer.create().build().write(nPojo);
// the output will have trimmed null properties.
-
Logger.getLogger(XmlConfigurationExample.class).info(nullescaped);
+
RichLogger.getLogger(XmlConfigurationExample.class).info(nullescaped);
//Produces
//<object
xmlns="http://www.pierobon.org/iis/review1.htm.html#one"><name><pojo></name><id>a</id></object>
var dNamsSpace =
XmlSerializer.create().enableNamespaces().defaultNamespace(Namespace.create("http://www.pierobon.org"
+ "/iis/review1.htm.html#one")).addNamespaceUrisToRoot().build()
.write(aPojo);
// the output will have new default namespace added.
-
Logger.getLogger(XmlConfigurationExample.class).info(dNamsSpace);
+
RichLogger.getLogger(XmlConfigurationExample.class).info(dNamsSpace);
}
}
\ No newline at end of file
diff --git
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
index c8a93fde22..b2f3f89830 100644
---
a/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
+++
b/juneau-examples/juneau-examples-core/src/main/java/org/apache/juneau/examples/core/xml/XmlSimpleExample.java
@@ -46,7 +46,7 @@ public class XmlSimpleExample {
* </object>
*/
var serial = XmlSerializer.DEFAULT_SQ_READABLE.write(pojo);
- Logger.getLogger(XmlSimpleExample.class).info(serial);
+ RichLogger.getLogger(XmlSimpleExample.class).info(serial);
// Deserialize back to Pojo instance
var obj = XmlParser.DEFAULT.read(serial, Pojo.class);
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
index 3cb2142f66..1fc65f3ca9 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestContext.java
@@ -57,7 +57,7 @@ import org.apache.juneau.commons.function.*;
import org.apache.juneau.commons.http.MediaType;
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.commons.lang.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.reflect.ParameterInfo;
import org.apache.juneau.commons.settings.*;
@@ -153,7 +153,7 @@ import jakarta.servlet.http.*;
})
public class RestContext extends Context {
- private static final Logger LOG = Logger.getLogger(RestContext.class);
+ private static final RichLogger LOG =
RichLogger.getLogger(RestContext.class);
// Property name constants
private static final String PROP_allowContentParam =
"allowContentParam";
@@ -1043,7 +1043,7 @@ public class RestContext extends Context {
bs.addDefaultSupplier(EncoderSet.class, encoders::get);
bs.addDefaultSupplier(SerializerSet.class, serializers::get);
bs.addDefaultSupplier(ParserSet.class, parsers::get);
- bs.addDefaultSupplier(Logger.class, logger::get);
+ bs.addDefaultSupplier(RichLogger.class, logger::get);
bs.addDefaultSupplier(java.util.logging.Logger.class,
logger::get);
bs.addDefaultSupplier(ThrownStore.class, thrownStore::get);
bs.addDefaultSupplier(MethodExecStore.class,
methodExecStore::get);
@@ -1573,15 +1573,15 @@ public class RestContext extends Context {
private final Memoizer<JsonSchemaGenerator> jsonSchemaGenerator =
memoizer(() -> jsonSchemaGeneratorBuilder.get().build());
/**
- * The {@link Logger} for this resource.
+ * The {@link RichLogger} for this resource.
*
* <p>
- * Defaults to {@code Logger.getLogger(resourceClass.getName())}. A
bean-store override or
+ * Defaults to {@code RichLogger.getLogger(resourceClass.getName())}. A
bean-store override or
* {@code @Bean} factory method REPLACES the default.
*/
- private final Memoizer<Logger> logger = memoizer(() -> {
- var v = Holder.of(Logger.getLogger(cn(resourceClass())));
- beanStore().createBeanFromMethod(Logger.class,
resource().get(), RestContext::isBeanMethod, v.get()).ifPresent(v::set);
+ private final Memoizer<RichLogger> logger = memoizer(() -> {
+ var v = Holder.of(RichLogger.getLogger(cn(resourceClass())));
+ beanStore().createBeanFromMethod(RichLogger.class,
resource().get(), RestContext::isBeanMethod, v.get()).ifPresent(v::set);
return v.get();
});
@@ -3252,7 +3252,7 @@ public class RestContext extends Context {
});
/** Logger for async / virtual-thread setup events. Used at memoizer
init before {@link #getLogger()} may be wired up. */
- private static final Logger ASYNC_LOG =
Logger.getLogger(RestContext.class.getName() + ".async");
+ private static final RichLogger ASYNC_LOG =
RichLogger.getLogger(RestContext.class.getName() + ".async");
/**
* Whether framework memoizers and operation/child contexts should be
force-initialized during constructor execution;
@@ -4079,7 +4079,7 @@ public class RestContext extends Context {
* The logger for this resource.
* <br>Never <jk>null</jk>.
*/
- public Logger getLogger() { return
beanStore.getBean(Logger.class).orElse(null); }
+ public RichLogger getLogger() { return
beanStore.getBean(RichLogger.class).orElse(null); }
/**
* Returns the resource bundle used by this resource.
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
index e1daf7bd7d..57dc292f08 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
@@ -20,7 +20,6 @@ import static java.time.format.DateTimeFormatter.*;
import static java.time.temporal.ChronoUnit.*;
import static org.apache.juneau.commons.httppart.HttpPartType.*;
import static org.apache.juneau.commons.utils.AssertionUtils.*;
-import static org.apache.juneau.commons.utils.ObjectUtils.*;
import static org.apache.juneau.commons.utils.Shorts.*;
import static org.apache.juneau.commons.utils.StringUtils.*;
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
index 3c8fd0bc6e..e0c43b2d21 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestResource.java
@@ -22,7 +22,7 @@ import java.util.concurrent.atomic.*;
import java.util.function.*;
import java.util.logging.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
import org.apache.juneau.http.response.*;
import org.apache.juneau.rest.server.*;
@@ -150,7 +150,7 @@ public abstract class RestResource {
var c = context.get();
var logger = c == null ? null : c.getLogger();
if (logger == null)
- logger = Logger.getLogger(cn(this));
+ logger = RichLogger.getLogger(cn(this));
logger.log(level, cause, msg);
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
index 9e16199f2b..3b414d8953 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/servlet/RestServlet.java
@@ -28,7 +28,7 @@ import java.util.function.*;
import java.util.logging.*;
import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.commons.logging.Logger;
+import org.apache.juneau.commons.logging.RichLogger;
import org.apache.juneau.commons.reflect.*;
import org.apache.juneau.commons.utils.*;
import org.apache.juneau.http.response.*;
@@ -366,7 +366,7 @@ public abstract class RestServlet extends HttpServlet {
var c = context.get();
var logger = c == null ? null : c.getLogger();
if (logger == null)
- logger = Logger.getLogger(cn(this));
+ logger = RichLogger.getLogger(cn(this));
logger.log(level, cause, msg);
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
index 4877c6d7b8..c57d11b820 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
@@ -72,7 +72,7 @@ import jakarta.servlet.*;
})
public class BasicSwaggerProviderSession {
- private static final Logger LOG =
Logger.getLogger(BasicSwaggerProviderSession.class);
+ private static final RichLogger LOG =
RichLogger.getLogger(BasicSwaggerProviderSession.class);
// Swagger JSON property name constants
private static final String SWAGGER_allOf = "allOf";
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
index b5aed4b270..50719b7688 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/util/CachingHttpServletRequest.java
@@ -30,6 +30,9 @@ import jakarta.servlet.http.*;
* be rendered. Memory stays bounded even for large uploads.
*
*/
+@SuppressWarnings({
+ "resource" // The tee'd request stream is owned by the underlying
request/servlet container, which closes it when the request completes; closing
it here would break body-caching. Eclipse JDT @Owning warning is by design.
+})
public class CachingHttpServletRequest extends HttpServletRequestWrapper {
/** Default body capture cap, in bytes (8 KB). */
diff --git
a/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
b/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
index a7df635de2..bc99998870 100644
---
a/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
+++
b/juneau-sc/juneau-sc-server/src/main/java/org/apache/juneau/server/config/repository/GitControl.java
@@ -243,10 +243,10 @@ public class GitControl implements AutoCloseable {
try {
var it = pc.call().iterator();
if (it.hasNext()) {
- Logger.getLogger(GitControl.class).info("{}",
it.next().toString());
+
RichLogger.getLogger(GitControl.class).info("{}", it.next().toString());
}
} catch (InvalidRemoteException e) {
- Logger.getLogger(GitControl.class).warning(e, "Error
pushing to remote repository.");
+ RichLogger.getLogger(GitControl.class).warning(e,
"Error pushing to remote repository.");
}
}
}
\ No newline at end of file
diff --git
a/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
b/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
index a7bb48426e..a9db75ae8f 100644
---
a/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
+++
b/juneau-sc/juneau-sc-server/src/test/java/org/apache/juneau/server/config/repository/GitControl_Test.java
@@ -47,17 +47,23 @@ class GitControl_Test {
//
-----------------------------------------------------------------------------------------------------------------
@Test void a01_nullUsernameYieldsAnonymousTransport() {
- assertNull(GitControl.findCredentialsProvider(null,
"GIT_TOKEN", new BasicBeanStore()));
+ try (var beanStore = new BasicBeanStore()) {
+ assertNull(GitControl.findCredentialsProvider(null,
"GIT_TOKEN", beanStore));
+ }
}
+ @SuppressWarnings({
+ "resource" // new BasicBeanStore().addBean(...) is a fluent
self-return; the try-with-resources on 'beanStore' already closes the same
instance the constructor produced.
+ })
@Test void a02_contributedStoreResolvesSecret() throws Exception {
var store = new InMemorySecretStore();
store.store("GIT_TOKEN", "hunter2".toCharArray());
- var beanStore = new BasicBeanStore().addBean(SecretStore.class,
store);
- var cp = GitControl.findCredentialsProvider("svc-account",
"GIT_TOKEN", beanStore);
- assertInstanceOf(UsernamePasswordCredentialsProvider.class, cp);
- assertArrayEquals("hunter2".toCharArray(), passwordOf(cp));
+ try (var beanStore = new
BasicBeanStore().addBean(SecretStore.class, store)) {
+ var cp =
GitControl.findCredentialsProvider("svc-account", "GIT_TOKEN", beanStore);
+
assertInstanceOf(UsernamePasswordCredentialsProvider.class, cp);
+ assertArrayEquals("hunter2".toCharArray(),
passwordOf(cp));
+ }
}
@Test void a03_absentSecretYieldsEmptyPassword() throws Exception {
@@ -68,20 +74,25 @@ class GitControl_Test {
}
@Test void a04_emptyBeanStoreFallsBackToInMemoryDefault() throws
Exception {
- var cp = GitControl.findCredentialsProvider("svc-account",
"GIT_TOKEN", new BasicBeanStore());
- assertArrayEquals(new char[0], passwordOf(cp));
+ try (var beanStore = new BasicBeanStore()) {
+ var cp =
GitControl.findCredentialsProvider("svc-account", "GIT_TOKEN", beanStore);
+ assertArrayEquals(new char[0], passwordOf(cp));
+ }
}
//
-----------------------------------------------------------------------------------------------------------------
// Constructor integration.
//
-----------------------------------------------------------------------------------------------------------------
+ @SuppressWarnings({
+ "resource" // new BasicBeanStore().addBean(...) is a fluent
self-return; the try-with-resources on 'beanStore' already closes the same
instance the constructor produced.
+ })
@Test void
b01_secretStoreConstructorWiresCredentialsAndClosesCleanly(@TempDir File dir)
throws IOException {
var store = new InMemorySecretStore();
store.store("GIT_TOKEN", "hunter2".toCharArray());
- var beanStore = new BasicBeanStore().addBean(SecretStore.class,
store);
- try (var gitControl = new GitControl(dir.getAbsolutePath(),
"https://example.com/repo.git", "svc-account", "GIT_TOKEN", beanStore, false)) {
+ try (var beanStore = new
BasicBeanStore().addBean(SecretStore.class, store);
+ var gitControl = new
GitControl(dir.getAbsolutePath(), "https://example.com/repo.git",
"svc-account", "GIT_TOKEN", beanStore, false)) {
assertNotNull(gitControl);
}
}