From 1972cb51d135442e30b288eb13d7a0e859fb753e Mon Sep 17 00:00:00 2001
From: Gerard Keiser
Date: Fri, 27 Jun 2025 11:58:53 -0500
Subject: [PATCH 01/23] Replace servlet context logging with JUL logging in
com.google.gwt.user.server.rpc
---
.../rpc/AbstractRemoteServiceServlet.java | 10 ++--
.../gwt/user/server/rpc/RPCServletUtils.java | 32 +++++++++---
.../user/server/rpc/RemoteServiceServlet.java | 50 +++++++++++--------
.../server/rpc/RemoteServiceServletTest.java | 9 ++--
4 files changed, 59 insertions(+), 42 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
index a21e4fb7a3f..bca9fc8d276 100644
--- a/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
@@ -19,7 +19,6 @@
import java.io.IOException;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -41,9 +40,8 @@ public AbstractRemoteServiceServlet() {
* Standard HttpServlet method: handle the POST. Delegates to
* {@link #processPost(HttpServletRequest, HttpServletResponse)}.
*
- * This doPost method swallows ALL exceptions, logs them in the
- * ServletContext, and returns a GENERIC_FAILURE_MSG response with status code
- * 500.
+ * This doPost method swallows ALL exceptions, logs them,
+ * and returns a GENERIC_FAILURE_MSG response with status code 500.
*/
@Override
public final void doPost(HttpServletRequest request,
@@ -106,9 +104,7 @@ protected void doUnexpectedFailure(Throwable e) {
*/
throw new RuntimeException("Unable to report failure", e);
}
- ServletContext servletContext = getServletContext();
- RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
- getThreadLocalResponse(), e);
+ RPCServletUtils.writeResponseForUnexpectedFailure(getThreadLocalResponse(), e);
}
/**
diff --git a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
index 8a2a50fb012..c205a88b66d 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
@@ -24,6 +24,8 @@
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletContext;
@@ -37,6 +39,8 @@
*/
public class RPCServletUtils {
+ private static final Logger logger = Logger.getLogger(RPCServletUtils.class.getName());
+
public static final String CHARSET_UTF8_NAME = "UTF-8";
/**
@@ -319,12 +323,19 @@ public static boolean shouldGzipResponseContent(HttpServletRequest request,
&& exceedsUncompressedContentLengthLimit(responseContent);
}
+ @Deprecated
+ public static void writeResponse(ServletContext servletContext,
+ HttpServletResponse response, String responseContent, boolean gzipResponse)
+ throws IOException {
+ writeResponse(response, responseContent, gzipResponse);
+ }
+
+
/**
* Write the response content into the {@link HttpServletResponse}. If
* gzipResponse is true, the response content will
* be gzipped prior to being written into the response.
*
- * @param servletContext servlet context for this response
* @param response response instance
* @param responseContent a string containing the response content
* @param gzipResponse if true the response content will be gzip
@@ -332,7 +343,7 @@ public static boolean shouldGzipResponseContent(HttpServletRequest request,
* @throws IOException if reading, writing, or closing the response's output
* stream fails
*/
- public static void writeResponse(ServletContext servletContext,
+ public static void writeResponse(
HttpServletResponse response, String responseContent, boolean gzipResponse)
throws IOException {
@@ -363,7 +374,7 @@ public static void writeResponse(ServletContext servletContext,
}
if (caught != null) {
- servletContext.log("Unable to compress response", caught);
+ logger.log(Level.SEVERE, "Unable to compress response", caught);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
@@ -378,18 +389,23 @@ public static void writeResponse(ServletContext servletContext,
response.getOutputStream().write(responseBytes);
}
+ @Deprecated
+ public static void writeResponseForUnexpectedFailure(
+ ServletContext servletContext, HttpServletResponse response,
+ Throwable failure) {
+ writeResponseForUnexpectedFailure(response, failure);
+ }
+
/**
* Called when the servlet itself has a problem, rather than the invoked
* third-party method. It writes a simple 500 message back to the client.
*
- * @param servletContext
* @param response
* @param failure
*/
public static void writeResponseForUnexpectedFailure(
- ServletContext servletContext, HttpServletResponse response,
- Throwable failure) {
- servletContext.log("Exception while dispatching incoming RPC call", failure);
+ HttpServletResponse response, Throwable failure) {
+ logger.log(Level.SEVERE, "Exception while dispatching incoming RPC call", failure);
// Send GENERIC_FAILURE_MSG with 500 status.
//
@@ -403,7 +419,7 @@ public static void writeResponseForUnexpectedFailure(
response.getWriter().write(GENERIC_FAILURE_MSG);
}
} catch (IOException ex) {
- servletContext.log(
+ logger.log(Level.SEVERE,
"respondWithUnexpectedFailure failed while sending the previous failure to the client",
ex);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index 1bd73e142ef..d25733d9df8 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -30,6 +30,8 @@
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -45,6 +47,8 @@
public class RemoteServiceServlet extends AbstractRemoteServiceServlet
implements SerializationPolicyProvider {
+ private static final Logger logger = Logger.getLogger(RemoteServiceServlet.class.getName());
+
/**
* Loads a serialization policy stored as a servlet resource in the same
* ServletContext as this servlet. Returns null if not found.
@@ -62,7 +66,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
- servlet.log("Malformed moduleBaseURL: " + moduleBaseURL, ex);
+ logger.log(Level.SEVERE, "Malformed moduleBaseURL: " + moduleBaseURL, ex);
}
}
@@ -74,12 +78,12 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
* this method.
*/
if (modulePath == null || !modulePath.startsWith(contextPath)) {
- String message = "ERROR: The module path requested, "
+ String message = "The module path requested, "
+ modulePath
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
- servlet.log(message);
+ logger.log(Level.SEVERE, message);
} else {
// Strip off the context path from the module base URL. It should be a
// strict prefix.
@@ -98,11 +102,13 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
null);
if (serializationPolicy.hasClientFields()) {
if (ENABLE_ENHANCED_CLASSES) {
- servlet.log("WARNING: Service deserializes enhanced JPA/JDO classes, which is " +
+ logger.log(Level.WARNING,
+ "Service deserializes enhanced JPA/JDO classes, which is " +
"unsafe. See https://github.com/gwtproject/gwt/issues/9709 for more " +
"detail on the vulnerability that this presents.");
} else {
- servlet.log("ERROR: Service deserializes enhanced JPA/JDO classes, which is " +
+ logger.log(Level.SEVERE,
+ "Service deserializes enhanced JPA/JDO classes, which is " +
"unsafe. Review build logs to see which classes are affected, or set " +
ENABLE_GWT_ENHANCED_CLASSES_PROPERTY + " to true to allow using this " +
"service. See https://github.com/gwtproject/gwt/issues/9709 for more " +
@@ -111,17 +117,19 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
}
}
} catch (ParseException e) {
- servlet.log("ERROR: Failed to parse the policy file '"
+ logger.log(Level.SEVERE,
+ "Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
- servlet.log("ERROR: Could not read the policy file '"
+ logger.log(Level.SEVERE,
+ "Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
} else {
- String message = "ERROR: The serialization policy file '"
+ String message = "The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
- servlet.log(message);
+ logger.log(Level.SEVERE, message);
}
} finally {
if (is != null) {
@@ -260,8 +268,8 @@ public final SerializationPolicy getSerializationPolicy(String moduleBaseURL,
if (serializationPolicy == null) {
// Failed to get the requested serialization policy; use the default
- log(
- "WARNING: Failed to get the SerializationPolicy '"
+ logger.log(Level.WARNING,
+ "Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
+ moduleBaseURL
@@ -311,7 +319,8 @@ public String processCall(String payload) throws SerializationException {
try {
rpcRequest = RPC.decodeRequest(payload, delegate.getClass(), this);
} catch (IncompatibleRemoteServiceException ex) {
- log(
+ logger.log(
+ Level.SEVERE,
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailedRequest(null, ex);
@@ -350,12 +359,13 @@ public String processCall(RPCRequest rpcRequest) throws SerializationException {
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy(),
rpcRequest.getFlags());
} catch (IncompatibleRemoteServiceException ex) {
- log(
+ logger.log(Level.SEVERE,
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailedRequest(rpcRequest, ex);
} catch (RpcTokenException tokenException) {
- log("An RpcTokenException was thrown while processing this call.",
+ logger.log(Level.SEVERE,
+ "An RpcTokenException was thrown while processing this call.",
tokenException);
return RPC.encodeResponseForFailedRequest(rpcRequest, tokenException);
}
@@ -364,9 +374,8 @@ public String processCall(RPCRequest rpcRequest) throws SerializationException {
/**
* Standard HttpServlet method: handle the POST.
*
- * This doPost method swallows ALL exceptions, logs them in the
- * ServletContext, and returns a GENERIC_FAILURE_MSG response with status code
- * 500.
+ * This doPost method swallows ALL exceptions, logs them,
+ * and returns a GENERIC_FAILURE_MSG response with status code 500.
*
* @throws ServletException
* @throws SerializationException
@@ -469,12 +478,12 @@ protected SerializationPolicy loadPolicyFromCodeServer(String url) {
@Override
public void logInfo(String message) {
- RemoteServiceServlet.this.log(message);
+ logger.log(Level.INFO, message);
}
@Override
public void logError(String message, Throwable throwable) {
- RemoteServiceServlet.this.log(message, throwable);
+ logger.log(Level.SEVERE, message, throwable);
}
};
return CODE_SERVER_CLIENT.loadPolicy(url, adapter);
@@ -541,7 +550,6 @@ private void writeResponse(HttpServletRequest request,
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
&& shouldCompressResponse(request, response, responsePayload);
- RPCServletUtils.writeResponse(getServletContext(), response,
- responsePayload, gzipEncode);
+ RPCServletUtils.writeResponse(response, responsePayload, gzipEncode);
}
}
diff --git a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
index 0cd88ac5454..a6d3a6c89ac 100644
--- a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
@@ -96,7 +96,6 @@ public String getServletName() {
}
private class MockServletContext implements ServletContext {
- private String messageLogged;
public MockServletContext() {
}
@@ -190,7 +189,7 @@ public void log(String arg0) {
}
public void log(String arg0, Throwable arg1) {
- messageLogged = arg0;
+ throw new UnsupportedOperationException();
}
public void removeAttribute(String arg0) {
@@ -329,7 +328,6 @@ public InputStream getResourceAsStream(String resource) {
SerializationPolicy serializationPolicy = rss.doGetSerializationPolicy(
mockRequest, "http://www.google.com/MyModule", "12345");
assertNull(serializationPolicy);
- assertNotNull(mockContext.messageLogged);
}
/**
@@ -337,8 +335,8 @@ public InputStream getResourceAsStream(String resource) {
* {@link com.google.gwt.user.server.rpc.RemoteServiceServlet#doGetSerializationPolicy(javax.servlet.http.HttpServletRequest, java.lang.String, java.lang.String)}.
*
* This method tests that if the module path is in a different context than
- * the RemoteServiceServlet which is processing the request, a message will be
- * logged and null is returned for the SerializationPolicy.
+ * the RemoteServiceServlet which is processing the request,
+ * null is returned for the SerializationPolicy.
*/
public void testDoGetSerializationPolicy_ModuleInSeparateServlet()
throws ServletException {
@@ -353,7 +351,6 @@ public void testDoGetSerializationPolicy_ModuleInSeparateServlet()
mockRequest.contextPath = "/foo";
SerializationPolicy serializationPolicy = rss.doGetSerializationPolicy(
mockRequest, "http://www.google.com/MyModule", "");
- assertNotNull(mockContext.messageLogged);
assertNull(serializationPolicy);
}
From 7593341df758a7ce2dd157a5bc29fc45cc13523d Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 25 Sep 2025 15:14:46 -0500
Subject: [PATCH 02/23] Add RPC logging facade backed by service loader
---
maven/lib-gwt.sh | 2 +-
user/build.xml | 19 ++++
...gwt.user.server.rpc.logging.LoggerProvider | 4 +
.../gwt/user/server/rpc/RPCServletUtils.java | 13 +--
.../user/server/rpc/RemoteServiceServlet.java | 56 +++++-----
.../server/rpc/SerializationPolicyClient.java | 37 +++----
.../server/rpc/logging/JulLoggerProvider.java | 43 ++++++++
.../server/rpc/logging/Log4jProvider.java | 25 +++++
.../user/server/rpc/logging/LogManager.java | 102 ++++++++++++++++++
.../gwt/user/server/rpc/logging/Logger.java | 44 ++++++++
.../server/rpc/logging/LoggerDelegate.java | 13 +++
.../server/rpc/logging/LoggerProvider.java | 15 +++
.../rpc/logging/PlatformLoggerProvider.java | 21 ++++
.../server/rpc/logging/ReflectiveLogger.java | 65 +++++++++++
.../rpc/logging/ReflectiveLoggerProvider.java | 39 +++++++
.../logging/ServletContextLoggerProvider.java | 47 ++++++++
.../server/rpc/RemoteServiceServletTest.java | 12 ++-
17 files changed, 497 insertions(+), 60 deletions(-)
create mode 100644 user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/LogManager.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/Logger.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
diff --git a/maven/lib-gwt.sh b/maven/lib-gwt.sh
index 24b1386b4b6..113f1ed13a4 100644
--- a/maven/lib-gwt.sh
+++ b/maven/lib-gwt.sh
@@ -86,7 +86,7 @@ function maven-gwt() {
zip -q $GWT_EXTRACT_DIR/gwt-user.jar --copy --out $GWT_EXTRACT_DIR/gwt-user-trimmed.jar \
"com/google/gwt/*" "com/google/web/bindery/*" "javaemul/*" \
"javax/validation/*" "org/hibernate/validator/*" \
- "org/w3c/flute/*"
+ "org/w3c/flute/*" "META-INF/services/*"
mv $GWT_EXTRACT_DIR/gwt-user-trimmed.jar $GWT_EXTRACT_DIR/gwt-user.jar
for i in $gwtLibs
diff --git a/user/build.xml b/user/build.xml
index 2af156012ff..9a6c3ba1b94 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -163,6 +163,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -178,6 +196,7 @@
+
diff --git a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider
new file mode 100644
index 00000000000..aecd0eb215d
--- /dev/null
+++ b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider
@@ -0,0 +1,4 @@
+com.google.gwt.user.server.rpc.logging.JulLoggerProvider
+com.google.gwt.user.server.rpc.logging.Log4jProvider
+com.google.gwt.user.server.rpc.logging.PlatformLoggerProvider
+# No no-args constructor: com.google.gwt.user.server.rpc.logging.ServletContextLoggerProvider
diff --git a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
index c205a88b66d..cf514e39d6c 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
@@ -16,6 +16,9 @@
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.server.rpc.logging.LogManager;
+import com.google.gwt.user.server.rpc.logging.Logger;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -24,8 +27,6 @@
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletContext;
@@ -39,7 +40,7 @@
*/
public class RPCServletUtils {
- private static final Logger logger = Logger.getLogger(RPCServletUtils.class.getName());
+ private static final Logger logger = LogManager.getLogger(RPCServletUtils.class);
public static final String CHARSET_UTF8_NAME = "UTF-8";
@@ -374,7 +375,7 @@ public static void writeResponse(
}
if (caught != null) {
- logger.log(Level.SEVERE, "Unable to compress response", caught);
+ logger.error("Unable to compress response", caught);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
@@ -405,7 +406,7 @@ public static void writeResponseForUnexpectedFailure(
*/
public static void writeResponseForUnexpectedFailure(
HttpServletResponse response, Throwable failure) {
- logger.log(Level.SEVERE, "Exception while dispatching incoming RPC call", failure);
+ logger.error("Exception while dispatching incoming RPC call", failure);
// Send GENERIC_FAILURE_MSG with 500 status.
//
@@ -419,7 +420,7 @@ public static void writeResponseForUnexpectedFailure(
response.getWriter().write(GENERIC_FAILURE_MSG);
}
} catch (IOException ex) {
- logger.log(Level.SEVERE,
+ logger.error(
"respondWithUnexpectedFailure failed while sending the previous failure to the client",
ex);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index d25733d9df8..ba2b6e459d9 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -22,6 +22,8 @@
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RpcTokenException;
import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.server.rpc.logging.LogManager;
+import com.google.gwt.user.server.rpc.logging.Logger;
import java.io.IOException;
import java.io.InputStream;
@@ -30,8 +32,6 @@
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -47,7 +47,7 @@
public class RemoteServiceServlet extends AbstractRemoteServiceServlet
implements SerializationPolicyProvider {
- private static final Logger logger = Logger.getLogger(RemoteServiceServlet.class.getName());
+ private static final Logger logger = LogManager.getLogger(RemoteServiceServlet.class);
/**
* Loads a serialization policy stored as a servlet resource in the same
@@ -66,7 +66,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
- logger.log(Level.SEVERE, "Malformed moduleBaseURL: " + moduleBaseURL, ex);
+ logger.error("Malformed moduleBaseURL: " + moduleBaseURL, ex);
}
}
@@ -83,7 +83,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
+ ", is not in the same web application as this servlet, "
+ contextPath
+ ". Your module may not be properly configured or your client and server code maybe out of date.";
- logger.log(Level.SEVERE, message);
+ logger.error(message);
} else {
// Strip off the context path from the module base URL. It should be a
// strict prefix.
@@ -102,12 +102,12 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
null);
if (serializationPolicy.hasClientFields()) {
if (ENABLE_ENHANCED_CLASSES) {
- logger.log(Level.WARNING,
+ logger.warn(
"Service deserializes enhanced JPA/JDO classes, which is " +
"unsafe. See https://github.com/gwtproject/gwt/issues/9709 for more " +
"detail on the vulnerability that this presents.");
} else {
- logger.log(Level.SEVERE,
+ logger.error(
"Service deserializes enhanced JPA/JDO classes, which is " +
"unsafe. Review build logs to see which classes are affected, or set " +
ENABLE_GWT_ENHANCED_CLASSES_PROPERTY + " to true to allow using this " +
@@ -117,11 +117,11 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
}
}
} catch (ParseException e) {
- logger.log(Level.SEVERE,
+ logger.error(
"Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
- logger.log(Level.SEVERE,
+ logger.error(
"Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
@@ -129,7 +129,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
String message = "The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
- logger.log(Level.SEVERE, message);
+ logger.error(message);
}
} finally {
if (is != null) {
@@ -190,9 +190,22 @@ public RemoteServiceServlet(Object delegate) {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
+ String providerName = getProviderName(config);
+ LogManager.initialize(providerName, config.getServletContext());
codeServerPort = getCodeServerPort();
}
+ private String getProviderName(ServletConfig config) {
+ String parameterName = "gwt.rpc.logging";
+ if (System.getProperty(parameterName) != null) {
+ return System.getProperty(parameterName);
+ } else if (config.getInitParameter(parameterName) != null) {
+ return config.getInitParameter(parameterName);
+ } else {
+ return config.getServletContext().getInitParameter(parameterName);
+ }
+ }
+
/**
* Returns the value of the gwt.codeserver.port system property, or zero if not defined.
*
@@ -268,7 +281,7 @@ public final SerializationPolicy getSerializationPolicy(String moduleBaseURL,
if (serializationPolicy == null) {
// Failed to get the requested serialization policy; use the default
- logger.log(Level.WARNING,
+ logger.warn(
"Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
@@ -319,8 +332,7 @@ public String processCall(String payload) throws SerializationException {
try {
rpcRequest = RPC.decodeRequest(payload, delegate.getClass(), this);
} catch (IncompatibleRemoteServiceException ex) {
- logger.log(
- Level.SEVERE,
+ logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailedRequest(null, ex);
@@ -359,12 +371,12 @@ public String processCall(RPCRequest rpcRequest) throws SerializationException {
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy(),
rpcRequest.getFlags());
} catch (IncompatibleRemoteServiceException ex) {
- logger.log(Level.SEVERE,
+ logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailedRequest(rpcRequest, ex);
} catch (RpcTokenException tokenException) {
- logger.log(Level.SEVERE,
+ logger.error(
"An RpcTokenException was thrown while processing this call.",
tokenException);
return RPC.encodeResponseForFailedRequest(rpcRequest, tokenException);
@@ -474,19 +486,7 @@ protected String getCodeServerPolicyUrl(String strongName) {
* no authentication. It should only be used during development.
*/
protected SerializationPolicy loadPolicyFromCodeServer(String url) {
- SerializationPolicyClient.Logger adapter = new SerializationPolicyClient.Logger() {
-
- @Override
- public void logInfo(String message) {
- logger.log(Level.INFO, message);
- }
-
- @Override
- public void logError(String message, Throwable throwable) {
- logger.log(Level.SEVERE, message, throwable);
- }
- };
- return CODE_SERVER_CLIENT.loadPolicy(url, adapter);
+ return CODE_SERVER_CLIENT.loadPolicy(url);
}
/**
diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
index dffc45a8841..f3a0c312fad 100644
--- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
+++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
@@ -15,6 +15,9 @@
*/
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.server.rpc.logging.LogManager;
+import com.google.gwt.user.server.rpc.logging.Logger;
+
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
@@ -30,6 +33,8 @@
* (Intended only for development.)
*/
class SerializationPolicyClient {
+
+ private static final Logger logger = LogManager.getLogger(SerializationPolicyClient.class);
private final int connectTimeout;
private final int readTimeout;
@@ -43,12 +48,12 @@ class SerializationPolicyClient {
this.readTimeout = readTimeoutMs;
}
- SerializationPolicy loadPolicy(String url, Logger logger) {
+ SerializationPolicy loadPolicy(String url) {
URL urlObj;
try {
urlObj = new URL(url);
} catch (MalformedURLException e) {
- logger.logError("Can't parse serialization policy URL: " + url, e);
+ logger.error("Can't parse serialization policy URL: " + url, e);
return null;
}
@@ -66,11 +71,11 @@ SerializationPolicy loadPolicy(String url, Logger logger) {
conn.connect();
in = conn.getInputStream();
} catch (IOException e) {
- logger.logError("Can't open serialization policy URL: " + url, e);
+ logger.error("Can't open serialization policy URL: " + url, e);
return null;
}
- return readPolicy(in, url, logger);
+ return readPolicy(in, url);
}
/**
@@ -79,34 +84,33 @@ SerializationPolicy loadPolicy(String url, Logger logger) {
* @param sourceName names the source of the input stream for log messages.
* @return the policy or null if unavailable.
*/
- private static SerializationPolicy readPolicy(InputStream in, String sourceName,
- Logger logger) {
+ private static SerializationPolicy readPolicy(InputStream in, String sourceName) {
try {
List errs = new ArrayList();
SerializationPolicy policy = SerializationPolicyLoader.loadFromStream(in, errs);
- logger.logInfo("Downloaded serialization policy from " + sourceName);
+ logger.error("Downloaded serialization policy from " + sourceName);
if (!errs.isEmpty()) {
- logMissingClasses(logger, errs);
+ logMissingClasses(errs);
}
return policy;
} catch (ParseException e) {
- logger.logError("Can't parse serialization policy from " + sourceName, e);
+ logger.error("Can't parse serialization policy from " + sourceName, e);
return null;
} catch (IOException e) {
- logger.logError("Can't read serialization policy from " + sourceName, e);
+ logger.error("Can't read serialization policy from " + sourceName, e);
return null;
} finally {
try {
in.close();
} catch (IOException e) {
- logger.logError("Can't close serialization policy url: " + sourceName, e);
+ logger.error("Can't close serialization policy url: " + sourceName, e);
}
}
}
- private static void logMissingClasses(Logger logger, List errs) {
+ private static void logMissingClasses(List errs) {
StringBuilder message = new StringBuilder();
message.append("Unable to load server-side classes used by policy:\n");
@@ -118,14 +122,7 @@ private static void logMissingClasses(Logger logger, List 0) {
message.append(" (omitted " + omitted + " more classes)\n");
}
- logger.logInfo(message.toString());
+ logger.info(message.toString());
}
- /**
- * Destination for the loader's log messages.
- */
- interface Logger {
- void logInfo(String message);
- void logError(String message, Throwable throwable);
- }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
new file mode 100644
index 00000000000..27e3ae64f15
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
@@ -0,0 +1,43 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import java.util.logging.Level;
+
+public class JulLoggerProvider implements LoggerProvider {
+
+ public JulLoggerProvider() { }
+
+ @Override
+ public LoggerDelegate createLogger(String name) {
+ return new JulLogger(java.util.logging.Logger.getLogger(name));
+ }
+
+ private static final class JulLogger implements LoggerDelegate {
+ private final java.util.logging.Logger logger;
+
+ public JulLogger(java.util.logging.Logger logger) {
+ this.logger = logger;
+ }
+
+ @Override
+ public void info(String message) {
+ logger.log(Level.INFO, message);
+ }
+
+ @Override
+ public void warn(String message) {
+ logger.log(Level.WARNING, message);
+ }
+
+ @Override
+ public void error(String message) {
+ logger.log(Level.SEVERE, message);
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ logger.log(Level.SEVERE, message, throwable);
+ }
+
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
new file mode 100644
index 00000000000..78c9d1dec7a
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
@@ -0,0 +1,25 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import java.lang.reflect.Method;
+
+public class Log4jProvider extends ReflectiveLoggerProvider implements LoggerProvider {
+
+ public Log4jProvider() {
+ try {
+ Class> factory = Class.forName("org.apache.logging.log4j.LogManager");
+ createLogger = factory.getMethod("getLogger", String.class);
+ Class> levelClass = Class.forName("org.apache.logging.log4j.Level");
+ Method levelFinder = levelClass.getMethod("valueOf", String.class);
+ infoLevel = levelFinder.invoke(null, "INFO");
+ warnLevel = levelFinder.invoke(null, "WARN");
+ errorLevel = levelFinder.invoke(null, "ERROR");
+ Class> loggerClass = Class.forName("org.apache.logging.log4j.Logger");
+ logMessage = loggerClass.getMethod("log", levelClass, String.class);
+ logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
+ available = true;
+ } catch (Exception e) {
+ available = false;
+ }
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/LogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/LogManager.java
new file mode 100644
index 00000000000..b1130429379
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/LogManager.java
@@ -0,0 +1,102 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import javax.servlet.ServletContext;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class LogManager {
+
+ private static volatile List providers = null;
+ private static volatile ServletContext servletContext = null;
+ private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
+ private static final AtomicReference loggerProvider = new AtomicReference<>();
+ private static final Object initLock = new Object();
+
+ public static Logger getLogger(Class> clazz) {
+ return loggers.computeIfAbsent(clazz.getName(), Logger::new);
+ }
+
+ static LoggerProvider getLoggerProvider() {
+ loggerProvider.compareAndSet(null, new JulLoggerProvider());
+ LoggerProvider provider = loggerProvider.get();
+ if (provider == null) {
+ synchronized (initLock) {
+ provider = loggerProvider.get();
+ if (provider == null) {
+ initialize();
+ provider = loggerProvider.get();
+ }
+ }
+ }
+ return provider;
+ }
+
+ public static void initialize() {
+ initialize(null, null);
+ }
+
+ public static void initialize(String providerName) {
+ initialize(providerName, null);
+ }
+
+ public static void initialize(String providerName, ServletContext servletContext) {
+ synchronized (initLock) {
+ if (servletContext != null) {
+ LogManager.servletContext = servletContext;
+ }
+ LoggerProvider fallback = servletContext != null ?
+ new ServletContextLoggerProvider(LogManager.servletContext) :
+ new JulLoggerProvider();
+ LoggerProvider provider = chooseProvider(providerName, fallback);
+ setupProvider(provider);
+ }
+ }
+
+ private static LoggerProvider chooseProvider(String name, LoggerProvider fallback) {
+ if (providers == null) {
+ providers = loadAllProviders();
+ }
+ for (LoggerProvider provider : providers) {
+ if (provider.isDefault() || provider.getClass().getName().equals(name)) {
+ return provider;
+ }
+ }
+ return fallback;
+ }
+
+ private static void setupProvider(LoggerProvider provider) {
+ LoggerProvider currentProvider = loggerProvider.get();
+ String currentProviderName = currentProvider != null ? currentProvider.getClass().getName() : null;
+ if (!provider.getClass().getName().equals(currentProviderName)) {
+ loggerProvider.set(provider);
+ loggers.clear();
+ }
+ }
+
+ private static List loadAllProviders() {
+ List providers = new ArrayList<>();
+ Set loaded = new HashSet<>();
+ ClassLoader[] loaders = new ClassLoader[] {
+ Thread.currentThread().getContextClassLoader(),
+ LogManager.class.getClassLoader(),
+ ClassLoader.getSystemClassLoader()
+ };
+
+ for (ClassLoader loader : loaders) {
+ if (loader == null) {
+ continue;
+ }
+ ServiceLoader loaderService = ServiceLoader.load(LoggerProvider.class, loader);
+ for (LoggerProvider provider : loaderService) {
+ if (provider.isAvailable() && !loaded.contains(provider.getClass().getName())) {
+ loaded.add(provider.getClass().getName());
+ providers.add(provider);
+ }
+ }
+ }
+
+ return providers;
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/Logger.java b/user/src/com/google/gwt/user/server/rpc/logging/Logger.java
new file mode 100644
index 00000000000..e03714a135c
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/Logger.java
@@ -0,0 +1,44 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+public class Logger {
+
+ private final AtomicReference delegate = new AtomicReference<>();
+ private final String name;
+
+ public Logger(String name) {
+ this.name = name;
+ }
+
+ public void info(String message) {
+ getDelegate().info(message);
+ }
+
+ public void warn(String message) {
+ getDelegate().warn(message);
+ }
+
+ public void error(String message) {
+ getDelegate().error(message);
+ }
+
+ public void error(String message, Throwable throwable) {
+ getDelegate().error(message, throwable);
+ }
+
+ private LoggerDelegate getDelegate() {
+ LoggerDelegate delegate = this.delegate.get();
+ if (delegate == null) {
+ delegate = createDelegate();
+ }
+ return delegate;
+ }
+
+ private LoggerDelegate createDelegate() {
+ LoggerProvider provider = LogManager.getLoggerProvider();
+ LoggerDelegate newDelegate = provider.createLogger(name);
+ return delegate.compareAndSet(null, newDelegate) ? newDelegate : delegate.get();
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java b/user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java
new file mode 100644
index 00000000000..b679b99a6bc
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java
@@ -0,0 +1,13 @@
+package com.google.gwt.user.server.rpc.logging;
+
+public interface LoggerDelegate {
+
+ void info(String message);
+
+ void warn(String message);
+
+ void error(String message);
+
+ void error(String message, Throwable throwable);
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java
new file mode 100644
index 00000000000..5e9e36595ac
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java
@@ -0,0 +1,15 @@
+package com.google.gwt.user.server.rpc.logging;
+
+public interface LoggerProvider {
+
+ LoggerDelegate createLogger(String name);
+
+ default boolean isDefault() {
+ return false;
+ }
+
+ default boolean isAvailable() {
+ return true;
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
new file mode 100644
index 00000000000..9d5df21b8a5
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
@@ -0,0 +1,21 @@
+package com.google.gwt.user.server.rpc.logging;
+
+public class PlatformLoggerProvider extends ReflectiveLoggerProvider implements LoggerProvider {
+
+ public PlatformLoggerProvider() {
+ try {
+ createLogger = System.class.getMethod("getLogger", String.class);
+ Class> levelClass = Class.forName("java.lang.System$Logger$Level");
+ infoLevel = getLogLevel(levelClass, "INFO");
+ warnLevel = getLogLevel(levelClass, "WARNING");
+ errorLevel = getLogLevel(levelClass, "ERROR");
+ Class> loggerClass = Class.forName("java.lang.System$Logger");
+ logMessage = loggerClass.getMethod("log", levelClass, String.class);
+ logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
+ available = true;
+ } catch (Exception e) {
+ available = false;
+ }
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
new file mode 100644
index 00000000000..70d5856a330
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
@@ -0,0 +1,65 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import java.lang.reflect.Method;
+
+class ReflectiveLogger implements LoggerDelegate {
+
+ private final Object logger;
+ private final Object infoLevel;
+ private final Object warnLevel;
+ private final Object errorLevel;
+ private final Method logMessage;
+ private final Method logThrowable;
+
+ public ReflectiveLogger(Object platformLogger, Object infoLevel, Object warnLevel, Object errorLevel, Method logMessage, Method logThrowable) {
+ this.logger = platformLogger;
+ this.infoLevel = infoLevel;
+ this.warnLevel = warnLevel;
+ this.errorLevel = errorLevel;
+ this.logMessage = logMessage;
+ this.logThrowable = logThrowable;
+ }
+
+ @Override
+ public void info(String message) {
+ try {
+ logMessage.invoke(logger, infoLevel, message);
+ } catch (Exception e) {
+ System.err.println("Failed to log message:" + message);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void warn(String message) {
+ try {
+ logMessage.invoke(logger, warnLevel, message);
+ } catch (Exception e) {
+ System.err.println("Failed to log message:" + message);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void error(String message) {
+ try {
+ logMessage.invoke(logger, errorLevel, message);
+ } catch (Exception e) {
+ System.err.println("Failed to log message:" + message);
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ try {
+ logThrowable.invoke(logger, errorLevel, message, throwable);
+ } catch (Exception e) {
+ System.err.println("Failed to log message:" + message);
+ e.printStackTrace();
+ System.err.println("Original exception:");
+ throwable.printStackTrace();
+ }
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
new file mode 100644
index 00000000000..f3566c79cfa
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
@@ -0,0 +1,39 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import java.lang.reflect.Method;
+
+abstract class ReflectiveLoggerProvider implements LoggerProvider {
+
+ protected Method createLogger;
+ protected Object infoLevel;
+ protected Object warnLevel;
+ protected Object errorLevel;
+ protected Method logMessage;
+ protected Method logThrowable;
+ protected boolean available;
+
+ @Override
+ public LoggerDelegate createLogger(String name) {
+ try {
+ Object logger = createLogger.invoke(null, name);
+ return new ReflectiveLogger(logger, infoLevel, warnLevel, errorLevel, logMessage, logThrowable);
+ } catch (Exception e) {
+ throw new IllegalStateException(getClass() + " is not available on this platform", e);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return available;
+ }
+
+ protected Object getLogLevel(Class> levelClass, String levelName) {
+ for (Object level : levelClass.getEnumConstants()) {
+ if (level.toString().equals(levelName)) {
+ return level;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
new file mode 100644
index 00000000000..1be4257454a
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -0,0 +1,47 @@
+package com.google.gwt.user.server.rpc.logging;
+
+import javax.servlet.ServletContext;
+
+public class ServletContextLoggerProvider implements LoggerProvider {
+
+ private final ServletContext servletContext;
+
+ public ServletContextLoggerProvider(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ public LoggerDelegate createLogger(String name) {
+ return new ServletContextLogger(servletContext);
+ }
+
+ private static final class ServletContextLogger implements LoggerDelegate {
+
+ private final ServletContext servletContext;
+
+ public ServletContextLogger(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ public void info(String message) {
+ servletContext.log(message);
+ }
+
+ @Override
+ public void warn(String message) {
+ servletContext.log("WARNING: " + message);
+ }
+
+ @Override public void error(String message) {
+ servletContext.log("ERROR: " + message);
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ servletContext.log(message, throwable);
+ }
+
+ }
+
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
index a6d3a6c89ac..4e015044fa1 100644
--- a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
@@ -79,7 +79,7 @@ public MockServletConfig(ServletContext context) {
}
public String getInitParameter(String arg0) {
- throw new UnsupportedOperationException();
+ return null;
}
public Enumeration getInitParameterNames() {
@@ -96,6 +96,7 @@ public String getServletName() {
}
private class MockServletContext implements ServletContext {
+ private String messageLogged;
public MockServletContext() {
}
@@ -117,7 +118,7 @@ public String getContextPath() {
}
public String getInitParameter(String arg0) {
- throw new UnsupportedOperationException();
+ return null;
}
public Enumeration getInitParameterNames() {
@@ -189,7 +190,7 @@ public void log(String arg0) {
}
public void log(String arg0, Throwable arg1) {
- throw new UnsupportedOperationException();
+ messageLogged = arg0;
}
public void removeAttribute(String arg0) {
@@ -328,6 +329,7 @@ public InputStream getResourceAsStream(String resource) {
SerializationPolicy serializationPolicy = rss.doGetSerializationPolicy(
mockRequest, "http://www.google.com/MyModule", "12345");
assertNull(serializationPolicy);
+ assertNotNull(mockContext.messageLogged);
}
/**
@@ -335,8 +337,8 @@ public InputStream getResourceAsStream(String resource) {
* {@link com.google.gwt.user.server.rpc.RemoteServiceServlet#doGetSerializationPolicy(javax.servlet.http.HttpServletRequest, java.lang.String, java.lang.String)}.
*
* This method tests that if the module path is in a different context than
- * the RemoteServiceServlet which is processing the request,
- * null is returned for the SerializationPolicy.
+ * the RemoteServiceServlet which is processing the request, a message will be
+ * logged and null is returned for the SerializationPolicy.
*/
public void testDoGetSerializationPolicy_ModuleInSeparateServlet()
throws ServletException {
From 752da2256c92963f1a858fc6ce5b4e67709c70da Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Wed, 10 Dec 2025 14:07:59 -0600
Subject: [PATCH 03/23] Add ServletContext parameter to RpcLogger methods,
allow only one initialization of RpcLoggerProvider
Various other improvements as suggested by niloc32
---
user/build.xml | 2 +-
...user.server.rpc.logging.RpcLoggerProvider} | 2 +-
.../rpc/AbstractRemoteServiceServlet.java | 5 +-
.../gwt/user/server/rpc/RPCServletUtils.java | 42 ++---
.../user/server/rpc/RemoteServiceServlet.java | 91 +++++-----
.../server/rpc/SerializationPolicyClient.java | 38 ++--
.../server/rpc/logging/JulLoggerProvider.java | 75 +++++---
.../server/rpc/logging/Log4jProvider.java | 52 ++++--
.../user/server/rpc/logging/LogManager.java | 102 -----------
.../gwt/user/server/rpc/logging/Logger.java | 44 -----
.../server/rpc/logging/LoggerDelegate.java | 13 --
.../server/rpc/logging/LoggerProvider.java | 15 --
.../rpc/logging/PlatformLoggerProvider.java | 50 +++--
.../server/rpc/logging/ReflectiveLogger.java | 120 +++++++-----
.../rpc/logging/ReflectiveLoggerProvider.java | 74 +++++---
.../server/rpc/logging/RpcLogManager.java | 171 ++++++++++++++++++
.../user/server/rpc/logging/RpcLogger.java | 64 +++++++
.../server/rpc/logging/RpcLoggerDelegate.java | 36 ++++
.../server/rpc/logging/RpcLoggerProvider.java | 41 +++++
.../logging/ServletContextLoggerProvider.java | 76 ++++----
20 files changed, 675 insertions(+), 438 deletions(-)
rename user/src/META-INF/services/{com.google.gwt.user.server.rpc.logging.LoggerProvider => com.google.gwt.user.server.rpc.logging.RpcLoggerProvider} (64%)
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/LogManager.java
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/Logger.java
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
create mode 100644 user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
diff --git a/user/build.xml b/user/build.xml
index 9a6c3ba1b94..55b0d0b6a00 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -163,7 +163,7 @@
-
+
diff --git a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
similarity index 64%
rename from user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider
rename to user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
index aecd0eb215d..0eccdd0db68 100644
--- a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.LoggerProvider
+++ b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
@@ -1,4 +1,4 @@
com.google.gwt.user.server.rpc.logging.JulLoggerProvider
com.google.gwt.user.server.rpc.logging.Log4jProvider
com.google.gwt.user.server.rpc.logging.PlatformLoggerProvider
-# No no-args constructor: com.google.gwt.user.server.rpc.logging.ServletContextLoggerProvider
+com.google.gwt.user.server.rpc.logging.ServletContextLoggerProvider
diff --git a/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
index bca9fc8d276..5cddb00faf7 100644
--- a/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
@@ -19,6 +19,7 @@
import java.io.IOException;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -104,7 +105,9 @@ protected void doUnexpectedFailure(Throwable e) {
*/
throw new RuntimeException("Unable to report failure", e);
}
- RPCServletUtils.writeResponseForUnexpectedFailure(getThreadLocalResponse(), e);
+ ServletContext servletContext = getServletContext();
+ RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
+ getThreadLocalResponse(), e);
}
/**
diff --git a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
index cf514e39d6c..e641cd8fc73 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
@@ -15,9 +15,8 @@
*/
package com.google.gwt.user.server.rpc;
-
-import com.google.gwt.user.server.rpc.logging.LogManager;
-import com.google.gwt.user.server.rpc.logging.Logger;
+import com.google.gwt.user.server.rpc.logging.RpcLogManager;
+import com.google.gwt.user.server.rpc.logging.RpcLogger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -40,7 +39,7 @@
*/
public class RPCServletUtils {
- private static final Logger logger = LogManager.getLogger(RPCServletUtils.class);
+ private static final RpcLogger logger = RpcLogManager.getLogger(RPCServletUtils.class);
public static final String CHARSET_UTF8_NAME = "UTF-8";
@@ -67,9 +66,11 @@ public class RPCServletUtils {
private static final String CONTENT_ENCODING_GZIP = "gzip";
- private static final String CONTENT_TYPE_APPLICATION_JSON_UTF8 = "application/json; charset=utf-8";
+ private static final String CONTENT_TYPE_APPLICATION_JSON_UTF8 =
+ "application/json; charset=utf-8";
- private static final String GENERIC_FAILURE_MSG = "The call failed on the server; see server log for details";
+ private static final String GENERIC_FAILURE_MSG =
+ "The call failed on the server; see server log for details";
private static final String GWT_RPC_CONTENT_TYPE = "text/x-gwt-rpc";
@@ -324,14 +325,6 @@ public static boolean shouldGzipResponseContent(HttpServletRequest request,
&& exceedsUncompressedContentLengthLimit(responseContent);
}
- @Deprecated
- public static void writeResponse(ServletContext servletContext,
- HttpServletResponse response, String responseContent, boolean gzipResponse)
- throws IOException {
- writeResponse(response, responseContent, gzipResponse);
- }
-
-
/**
* Write the response content into the {@link HttpServletResponse}. If
* gzipResponse is true, the response content will
@@ -344,7 +337,7 @@ public static void writeResponse(ServletContext servletContext,
* @throws IOException if reading, writing, or closing the response's output
* stream fails
*/
- public static void writeResponse(
+ public static void writeResponse(ServletContext servletContext,
HttpServletResponse response, String responseContent, boolean gzipResponse)
throws IOException {
@@ -375,7 +368,7 @@ public static void writeResponse(
}
if (caught != null) {
- logger.error("Unable to compress response", caught);
+ logger.error("Unable to compress response", caught, servletContext);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
@@ -390,13 +383,6 @@ public static void writeResponse(
response.getOutputStream().write(responseBytes);
}
- @Deprecated
- public static void writeResponseForUnexpectedFailure(
- ServletContext servletContext, HttpServletResponse response,
- Throwable failure) {
- writeResponseForUnexpectedFailure(response, failure);
- }
-
/**
* Called when the servlet itself has a problem, rather than the invoked
* third-party method. It writes a simple 500 message back to the client.
@@ -405,8 +391,9 @@ public static void writeResponseForUnexpectedFailure(
* @param failure
*/
public static void writeResponseForUnexpectedFailure(
- HttpServletResponse response, Throwable failure) {
- logger.error("Exception while dispatching incoming RPC call", failure);
+ ServletContext servletContext, HttpServletResponse response,
+ Throwable failure) {
+ logger.error("Exception while dispatching incoming RPC call", failure, servletContext);
// Send GENERIC_FAILURE_MSG with 500 status.
//
@@ -422,7 +409,7 @@ public static void writeResponseForUnexpectedFailure(
} catch (IOException ex) {
logger.error(
"respondWithUnexpectedFailure failed while sending the previous failure to the client",
- ex);
+ ex, servletContext);
}
}
@@ -448,7 +435,8 @@ private static void checkCharacterEncodingIgnoreCase(
* properly parsed character encoding string if we decide to make this
* change.
*/
- if (characterEncoding.toLowerCase(Locale.ROOT).contains(expectedCharSet.toLowerCase(Locale.ROOT))) {
+ if (characterEncoding.toLowerCase(Locale.ROOT)
+ .contains(expectedCharSet.toLowerCase(Locale.ROOT))) {
encodingOkay = true;
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index ba2b6e459d9..1e2e698bbe6 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -22,8 +22,8 @@
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RpcTokenException;
import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.server.rpc.logging.LogManager;
-import com.google.gwt.user.server.rpc.logging.Logger;
+import com.google.gwt.user.server.rpc.logging.RpcLogManager;
+import com.google.gwt.user.server.rpc.logging.RpcLogger;
import java.io.IOException;
import java.io.InputStream;
@@ -47,7 +47,7 @@
public class RemoteServiceServlet extends AbstractRemoteServiceServlet
implements SerializationPolicyProvider {
- private static final Logger logger = LogManager.getLogger(RemoteServiceServlet.class);
+ private static final RpcLogger logger = RpcLogManager.getLogger(RemoteServiceServlet.class);
/**
* Loads a serialization policy stored as a servlet resource in the same
@@ -66,7 +66,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
- logger.error("Malformed moduleBaseURL: " + moduleBaseURL, ex);
+ logger.error("Malformed moduleBaseURL: " + moduleBaseURL, ex, servlet.getServletContext());
}
}
@@ -82,15 +82,16 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
+ modulePath
+ ", is not in the same web application as this servlet, "
+ contextPath
- + ". Your module may not be properly configured or your client and server code maybe out of date.";
- logger.error(message);
+ + ". Your module may not be properly configured " +
+ "or your client and server code maybe out of date.";
+ logger.error(message, servlet.getServletContext());
} else {
// Strip off the context path from the module base URL. It should be a
// strict prefix.
String contextRelativePath = modulePath.substring(contextPath.length());
- String serializationPolicyFilePath = SerializationPolicyLoader.getSerializationPolicyFileName(contextRelativePath
- + strongName);
+ String serializationPolicyFilePath = SerializationPolicyLoader.getSerializationPolicyFileName(
+ contextRelativePath + strongName);
// Open the RPC resource file and read its contents.
InputStream is = servlet.getServletContext().getResourceAsStream(
@@ -102,34 +103,32 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
null);
if (serializationPolicy.hasClientFields()) {
if (ENABLE_ENHANCED_CLASSES) {
- logger.warn(
- "Service deserializes enhanced JPA/JDO classes, which is " +
- "unsafe. See https://github.com/gwtproject/gwt/issues/9709 for more " +
- "detail on the vulnerability that this presents.");
+ logger.warn("Service deserializes enhanced JPA/JDO classes, which is " +
+ "unsafe. See https://github.com/gwtproject/gwt/issues/9709 for more " +
+ "detail on the vulnerability that this presents.",
+ servlet.getServletContext());
} else {
- logger.error(
- "Service deserializes enhanced JPA/JDO classes, which is " +
- "unsafe. Review build logs to see which classes are affected, or set " +
- ENABLE_GWT_ENHANCED_CLASSES_PROPERTY + " to true to allow using this " +
- "service. See https://github.com/gwtproject/gwt/issues/9709 for more " +
- "detail.");
+ logger.error("Service deserializes enhanced JPA/JDO classes, which is " +
+ "unsafe. Review build logs to see which classes are affected, or set " +
+ ENABLE_GWT_ENHANCED_CLASSES_PROPERTY + " to true to allow using this " +
+ "service. See https://github.com/gwtproject/gwt/issues/9709 for more " +
+ "detail.",
+ servlet.getServletContext());
serializationPolicy = null;
}
}
} catch (ParseException e) {
- logger.error(
- "Failed to parse the policy file '"
- + serializationPolicyFilePath + "'", e);
+ logger.error("Failed to parse the policy file '"
+ + serializationPolicyFilePath + "'", e, servlet.getServletContext());
} catch (IOException e) {
- logger.error(
- "Could not read the policy file '"
- + serializationPolicyFilePath + "'", e);
+ logger.error("Could not read the policy file '"
+ + serializationPolicyFilePath + "'", e, servlet.getServletContext());
}
} else {
String message = "The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
- logger.error(message);
+ logger.error(message, servlet.getServletContext());
}
} finally {
if (is != null) {
@@ -152,7 +151,8 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
* A cache of moduleBaseURL and serialization policy strong name to
* {@link SerializationPolicy}.
*/
- private final Map serializationPolicyCache = new HashMap();
+ private final Map serializationPolicyCache =
+ new HashMap();
/**
* The implementation of the service.
@@ -185,27 +185,17 @@ public RemoteServiceServlet(Object delegate) {
}
/**
- * Overridden to load the gwt.codeserver.port system property.
+ * Overridden to load the gwt.codeserver.port system property and initialize the
+ * {@link RpcLogManager} with a provider name from system properties or the servlet config.
*/
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
- String providerName = getProviderName(config);
- LogManager.initialize(providerName, config.getServletContext());
+ String providerName = RpcLogManager.getProviderName(config);
+ RpcLogManager.initialize(providerName);
codeServerPort = getCodeServerPort();
}
- private String getProviderName(ServletConfig config) {
- String parameterName = "gwt.rpc.logging";
- if (System.getProperty(parameterName) != null) {
- return System.getProperty(parameterName);
- } else if (config.getInitParameter(parameterName) != null) {
- return config.getInitParameter(parameterName);
- } else {
- return config.getServletContext().getInitParameter(parameterName);
- }
- }
-
/**
* Returns the value of the gwt.codeserver.port system property, or zero if not defined.
*
@@ -281,12 +271,13 @@ public final SerializationPolicy getSerializationPolicy(String moduleBaseURL,
if (serializationPolicy == null) {
// Failed to get the requested serialization policy; use the default
- logger.warn(
- "Failed to get the SerializationPolicy '"
+ logger.warn("Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
+ moduleBaseURL
- + "'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.");
+ + "'; a legacy, 1.3.3 compatible, serialization policy will be used. " +
+ "You may experience SerializationExceptions as a result.",
+ getServletContext());
serializationPolicy = RPC.getDefaultSerializationPolicy();
}
@@ -334,7 +325,7 @@ public String processCall(String payload) throws SerializationException {
} catch (IncompatibleRemoteServiceException ex) {
logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
- ex);
+ ex, getServletContext());
return RPC.encodeResponseForFailedRequest(null, ex);
}
return processCall(rpcRequest);
@@ -373,12 +364,12 @@ public String processCall(RPCRequest rpcRequest) throws SerializationException {
} catch (IncompatibleRemoteServiceException ex) {
logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
- ex);
+ ex,
+ getServletContext());
return RPC.encodeResponseForFailedRequest(rpcRequest, ex);
} catch (RpcTokenException tokenException) {
- logger.error(
- "An RpcTokenException was thrown while processing this call.",
- tokenException);
+ logger.error("An RpcTokenException was thrown while processing this call.",
+ tokenException, getServletContext());
return RPC.encodeResponseForFailedRequest(rpcRequest, tokenException);
}
}
@@ -486,7 +477,7 @@ protected String getCodeServerPolicyUrl(String strongName) {
* no authentication. It should only be used during development.
*/
protected SerializationPolicy loadPolicyFromCodeServer(String url) {
- return CODE_SERVER_CLIENT.loadPolicy(url);
+ return CODE_SERVER_CLIENT.loadPolicy(url, getServletContext());
}
/**
@@ -550,6 +541,6 @@ private void writeResponse(HttpServletRequest request,
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
&& shouldCompressResponse(request, response, responsePayload);
- RPCServletUtils.writeResponse(response, responsePayload, gzipEncode);
+ RPCServletUtils.writeResponse(getServletContext(), response, responsePayload, gzipEncode);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
index f3a0c312fad..e24df254ed5 100644
--- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
+++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
@@ -15,8 +15,8 @@
*/
package com.google.gwt.user.server.rpc;
-import com.google.gwt.user.server.rpc.logging.LogManager;
-import com.google.gwt.user.server.rpc.logging.Logger;
+import com.google.gwt.user.server.rpc.logging.RpcLogManager;
+import com.google.gwt.user.server.rpc.logging.RpcLogger;
import java.io.IOException;
import java.io.InputStream;
@@ -28,18 +28,20 @@
import java.util.ArrayList;
import java.util.List;
+import javax.servlet.ServletContext;
+
/**
* A simple and relatively naive client for downloading serialization policies from a URL.
* (Intended only for development.)
*/
class SerializationPolicyClient {
- private static final Logger logger = LogManager.getLogger(SerializationPolicyClient.class);
+ private static final RpcLogger logger = RpcLogManager.getLogger(SerializationPolicyClient.class);
private final int connectTimeout;
private final int readTimeout;
/**
- * Creates an client with the given configuration,
+ * Creates a client with the given configuration.
* @param connectTimeoutMs see {@link URLConnection#setConnectTimeout}
* @param readTimeoutMs see {@link URLConnection#setReadTimeout}
*/
@@ -48,12 +50,12 @@ class SerializationPolicyClient {
this.readTimeout = readTimeoutMs;
}
- SerializationPolicy loadPolicy(String url) {
+ SerializationPolicy loadPolicy(String url, ServletContext servletContext) {
URL urlObj;
try {
urlObj = new URL(url);
} catch (MalformedURLException e) {
- logger.error("Can't parse serialization policy URL: " + url, e);
+ logger.error("Can't parse serialization policy URL: " + url, e, servletContext);
return null;
}
@@ -66,16 +68,16 @@ SerializationPolicy loadPolicy(String url) {
// The code server doesn't redirect. Fail fast if we get a redirect since
// it's likely a configuration error.
if (conn instanceof HttpURLConnection) {
- ((HttpURLConnection)conn).setInstanceFollowRedirects(false);
+ ((HttpURLConnection) conn).setInstanceFollowRedirects(false);
}
conn.connect();
in = conn.getInputStream();
} catch (IOException e) {
- logger.error("Can't open serialization policy URL: " + url, e);
+ logger.error("Can't open serialization policy URL: " + url, e, servletContext);
return null;
}
- return readPolicy(in, url);
+ return readPolicy(in, url, servletContext);
}
/**
@@ -84,33 +86,35 @@ SerializationPolicy loadPolicy(String url) {
* @param sourceName names the source of the input stream for log messages.
* @return the policy or null if unavailable.
*/
- private static SerializationPolicy readPolicy(InputStream in, String sourceName) {
+ private static SerializationPolicy readPolicy(InputStream in, String sourceName,
+ ServletContext servletContext) {
try {
List errs = new ArrayList();
SerializationPolicy policy = SerializationPolicyLoader.loadFromStream(in, errs);
- logger.error("Downloaded serialization policy from " + sourceName);
+ logger.info("Downloaded serialization policy from " + sourceName, servletContext);
if (!errs.isEmpty()) {
- logMissingClasses(errs);
+ logMissingClasses(errs, servletContext);
}
return policy;
} catch (ParseException e) {
- logger.error("Can't parse serialization policy from " + sourceName, e);
+ logger.error("Can't parse serialization policy from " + sourceName, e, servletContext);
return null;
} catch (IOException e) {
- logger.error("Can't read serialization policy from " + sourceName, e);
+ logger.error("Can't read serialization policy from " + sourceName, e, servletContext);
return null;
} finally {
try {
in.close();
} catch (IOException e) {
- logger.error("Can't close serialization policy url: " + sourceName, e);
+ logger.error("Can't close serialization policy url: " + sourceName, e, servletContext);
}
}
}
- private static void logMissingClasses(List errs) {
+ private static void logMissingClasses(List errs,
+ ServletContext servletContext) {
StringBuilder message = new StringBuilder();
message.append("Unable to load server-side classes used by policy:\n");
@@ -122,7 +126,7 @@ private static void logMissingClasses(List errs) {
if (omitted > 0) {
message.append(" (omitted " + omitted + " more classes)\n");
}
- logger.info(message.toString());
+ logger.info(message.toString(), servletContext);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
index 27e3ae64f15..b7b09f37e33 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
@@ -1,43 +1,64 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
import java.util.logging.Level;
+import java.util.logging.Logger;
-public class JulLoggerProvider implements LoggerProvider {
+import javax.servlet.ServletContext;
- public JulLoggerProvider() { }
+/**
+ * A {@link RpcLoggerProvider} that delegates to {@link java.util.logging.Logger}.
+ */
+public class JulLoggerProvider implements RpcLoggerProvider {
- @Override
- public LoggerDelegate createLogger(String name) {
- return new JulLogger(java.util.logging.Logger.getLogger(name));
- }
+ public JulLoggerProvider() {
+ }
- private static final class JulLogger implements LoggerDelegate {
- private final java.util.logging.Logger logger;
+ @Override
+ public RpcLoggerDelegate createLogger(String name) {
+ return new JulLogger(Logger.getLogger(name));
+ }
- public JulLogger(java.util.logging.Logger logger) {
- this.logger = logger;
- }
+ private static final class JulLogger implements RpcLoggerDelegate {
+ private final Logger logger;
- @Override
- public void info(String message) {
- logger.log(Level.INFO, message);
- }
+ JulLogger(Logger logger) {
+ this.logger = logger;
+ }
- @Override
- public void warn(String message) {
- logger.log(Level.WARNING, message);
- }
+ @Override
+ public void info(String message, ServletContext servletContext) {
+ logger.log(Level.INFO, message);
+ }
- @Override
- public void error(String message) {
- logger.log(Level.SEVERE, message);
- }
+ @Override
+ public void warn(String message, ServletContext servletContext) {
+ logger.log(Level.WARNING, message);
+ }
- @Override
- public void error(String message, Throwable throwable) {
- logger.log(Level.SEVERE, message, throwable);
- }
+ @Override
+ public void error(String message, ServletContext servletContext) {
+ logger.log(Level.SEVERE, message);
+ }
+ @Override
+ public void error(String message, Throwable throwable, ServletContext servletContext) {
+ logger.log(Level.SEVERE, message, throwable);
}
+ }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
index 78c9d1dec7a..652e7ba66fa 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
@@ -1,25 +1,43 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
import java.lang.reflect.Method;
-public class Log4jProvider extends ReflectiveLoggerProvider implements LoggerProvider {
+/**
+ * A {@link RpcLoggerProvider} that delegates to Log4j.
+ */
+public class Log4jProvider extends ReflectiveLoggerProvider implements RpcLoggerProvider {
- public Log4jProvider() {
- try {
- Class> factory = Class.forName("org.apache.logging.log4j.LogManager");
- createLogger = factory.getMethod("getLogger", String.class);
- Class> levelClass = Class.forName("org.apache.logging.log4j.Level");
- Method levelFinder = levelClass.getMethod("valueOf", String.class);
- infoLevel = levelFinder.invoke(null, "INFO");
- warnLevel = levelFinder.invoke(null, "WARN");
- errorLevel = levelFinder.invoke(null, "ERROR");
- Class> loggerClass = Class.forName("org.apache.logging.log4j.Logger");
- logMessage = loggerClass.getMethod("log", levelClass, String.class);
- logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
- available = true;
- } catch (Exception e) {
- available = false;
- }
+ public Log4jProvider() {
+ try {
+ Class> factory = Class.forName("org.apache.logging.log4j.LogManager");
+ createLogger = factory.getMethod("getLogger", String.class);
+ Class> levelClass = Class.forName("org.apache.logging.log4j.Level");
+ Method levelFinder = levelClass.getMethod("valueOf", String.class);
+ infoLevel = levelFinder.invoke(null, "INFO");
+ warnLevel = levelFinder.invoke(null, "WARN");
+ errorLevel = levelFinder.invoke(null, "ERROR");
+ Class> loggerClass = Class.forName("org.apache.logging.log4j.Logger");
+ logMessage = loggerClass.getMethod("log", levelClass, String.class);
+ logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
+ available = true;
+ } catch (Exception e) {
+ available = false;
}
+ }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/LogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/LogManager.java
deleted file mode 100644
index b1130429379..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/LogManager.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.google.gwt.user.server.rpc.logging;
-
-import javax.servlet.ServletContext;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class LogManager {
-
- private static volatile List providers = null;
- private static volatile ServletContext servletContext = null;
- private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
- private static final AtomicReference loggerProvider = new AtomicReference<>();
- private static final Object initLock = new Object();
-
- public static Logger getLogger(Class> clazz) {
- return loggers.computeIfAbsent(clazz.getName(), Logger::new);
- }
-
- static LoggerProvider getLoggerProvider() {
- loggerProvider.compareAndSet(null, new JulLoggerProvider());
- LoggerProvider provider = loggerProvider.get();
- if (provider == null) {
- synchronized (initLock) {
- provider = loggerProvider.get();
- if (provider == null) {
- initialize();
- provider = loggerProvider.get();
- }
- }
- }
- return provider;
- }
-
- public static void initialize() {
- initialize(null, null);
- }
-
- public static void initialize(String providerName) {
- initialize(providerName, null);
- }
-
- public static void initialize(String providerName, ServletContext servletContext) {
- synchronized (initLock) {
- if (servletContext != null) {
- LogManager.servletContext = servletContext;
- }
- LoggerProvider fallback = servletContext != null ?
- new ServletContextLoggerProvider(LogManager.servletContext) :
- new JulLoggerProvider();
- LoggerProvider provider = chooseProvider(providerName, fallback);
- setupProvider(provider);
- }
- }
-
- private static LoggerProvider chooseProvider(String name, LoggerProvider fallback) {
- if (providers == null) {
- providers = loadAllProviders();
- }
- for (LoggerProvider provider : providers) {
- if (provider.isDefault() || provider.getClass().getName().equals(name)) {
- return provider;
- }
- }
- return fallback;
- }
-
- private static void setupProvider(LoggerProvider provider) {
- LoggerProvider currentProvider = loggerProvider.get();
- String currentProviderName = currentProvider != null ? currentProvider.getClass().getName() : null;
- if (!provider.getClass().getName().equals(currentProviderName)) {
- loggerProvider.set(provider);
- loggers.clear();
- }
- }
-
- private static List loadAllProviders() {
- List providers = new ArrayList<>();
- Set loaded = new HashSet<>();
- ClassLoader[] loaders = new ClassLoader[] {
- Thread.currentThread().getContextClassLoader(),
- LogManager.class.getClassLoader(),
- ClassLoader.getSystemClassLoader()
- };
-
- for (ClassLoader loader : loaders) {
- if (loader == null) {
- continue;
- }
- ServiceLoader loaderService = ServiceLoader.load(LoggerProvider.class, loader);
- for (LoggerProvider provider : loaderService) {
- if (provider.isAvailable() && !loaded.contains(provider.getClass().getName())) {
- loaded.add(provider.getClass().getName());
- providers.add(provider);
- }
- }
- }
-
- return providers;
- }
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/Logger.java b/user/src/com/google/gwt/user/server/rpc/logging/Logger.java
deleted file mode 100644
index e03714a135c..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/Logger.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.google.gwt.user.server.rpc.logging;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-public class Logger {
-
- private final AtomicReference delegate = new AtomicReference<>();
- private final String name;
-
- public Logger(String name) {
- this.name = name;
- }
-
- public void info(String message) {
- getDelegate().info(message);
- }
-
- public void warn(String message) {
- getDelegate().warn(message);
- }
-
- public void error(String message) {
- getDelegate().error(message);
- }
-
- public void error(String message, Throwable throwable) {
- getDelegate().error(message, throwable);
- }
-
- private LoggerDelegate getDelegate() {
- LoggerDelegate delegate = this.delegate.get();
- if (delegate == null) {
- delegate = createDelegate();
- }
- return delegate;
- }
-
- private LoggerDelegate createDelegate() {
- LoggerProvider provider = LogManager.getLoggerProvider();
- LoggerDelegate newDelegate = provider.createLogger(name);
- return delegate.compareAndSet(null, newDelegate) ? newDelegate : delegate.get();
- }
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java b/user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java
deleted file mode 100644
index b679b99a6bc..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/LoggerDelegate.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.google.gwt.user.server.rpc.logging;
-
-public interface LoggerDelegate {
-
- void info(String message);
-
- void warn(String message);
-
- void error(String message);
-
- void error(String message, Throwable throwable);
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java
deleted file mode 100644
index 5e9e36595ac..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/LoggerProvider.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.google.gwt.user.server.rpc.logging;
-
-public interface LoggerProvider {
-
- LoggerDelegate createLogger(String name);
-
- default boolean isDefault() {
- return false;
- }
-
- default boolean isAvailable() {
- return true;
- }
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
index 9d5df21b8a5..057e4990624 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
@@ -1,21 +1,41 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
-public class PlatformLoggerProvider extends ReflectiveLoggerProvider implements LoggerProvider {
+/**
+ * A {@link RpcLoggerProvider} that delegates to System.Logger
+ *
+ * Once support for Java 8 is dropped, this class can be implemented without reflection.
+ */
+public class PlatformLoggerProvider extends ReflectiveLoggerProvider implements RpcLoggerProvider {
- public PlatformLoggerProvider() {
- try {
- createLogger = System.class.getMethod("getLogger", String.class);
- Class> levelClass = Class.forName("java.lang.System$Logger$Level");
- infoLevel = getLogLevel(levelClass, "INFO");
- warnLevel = getLogLevel(levelClass, "WARNING");
- errorLevel = getLogLevel(levelClass, "ERROR");
- Class> loggerClass = Class.forName("java.lang.System$Logger");
- logMessage = loggerClass.getMethod("log", levelClass, String.class);
- logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
- available = true;
- } catch (Exception e) {
- available = false;
- }
+ public PlatformLoggerProvider() {
+ try {
+ createLogger = System.class.getMethod("getLogger", String.class);
+ Class> levelClass = Class.forName("java.lang.System$Logger$Level");
+ infoLevel = getLogLevel(levelClass, "INFO");
+ warnLevel = getLogLevel(levelClass, "WARNING");
+ errorLevel = getLogLevel(levelClass, "ERROR");
+ Class> loggerClass = Class.forName("java.lang.System$Logger");
+ logMessage = loggerClass.getMethod("log", levelClass, String.class);
+ logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
+ available = true;
+ } catch (Exception e) {
+ available = false;
}
+ }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
index 70d5856a330..497c5260be2 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
@@ -1,65 +1,87 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
import java.lang.reflect.Method;
-class ReflectiveLogger implements LoggerDelegate {
+import javax.servlet.ServletContext;
- private final Object logger;
- private final Object infoLevel;
- private final Object warnLevel;
- private final Object errorLevel;
- private final Method logMessage;
- private final Method logThrowable;
+/**
+ * A logger that uses reflection to call the underlying platform logger.
+ * Compatible with platform logging and Log4j; incompatible with SLF4j.
+ */
+class ReflectiveLogger implements RpcLoggerDelegate {
- public ReflectiveLogger(Object platformLogger, Object infoLevel, Object warnLevel, Object errorLevel, Method logMessage, Method logThrowable) {
- this.logger = platformLogger;
- this.infoLevel = infoLevel;
- this.warnLevel = warnLevel;
- this.errorLevel = errorLevel;
- this.logMessage = logMessage;
- this.logThrowable = logThrowable;
- }
+ private final Object logger;
+ private final Object infoLevel;
+ private final Object warnLevel;
+ private final Object errorLevel;
+ private final Method logMessage;
+ private final Method logThrowable;
+
+ ReflectiveLogger(Object platformLogger, Object infoLevel, Object warnLevel, Object errorLevel,
+ Method logMessage, Method logThrowable) {
+ this.logger = platformLogger;
+ this.infoLevel = infoLevel;
+ this.warnLevel = warnLevel;
+ this.errorLevel = errorLevel;
+ this.logMessage = logMessage;
+ this.logThrowable = logThrowable;
+ }
- @Override
- public void info(String message) {
- try {
- logMessage.invoke(logger, infoLevel, message);
- } catch (Exception e) {
- System.err.println("Failed to log message:" + message);
- e.printStackTrace();
- }
+ @Override
+ public void info(String message, ServletContext servletContext) {
+ try {
+ logMessage.invoke(logger, infoLevel, message);
+ } catch (Exception e) {
+ System.err.println("Failed to log message: " + message);
+ e.printStackTrace();
}
+ }
- @Override
- public void warn(String message) {
- try {
- logMessage.invoke(logger, warnLevel, message);
- } catch (Exception e) {
- System.err.println("Failed to log message:" + message);
- e.printStackTrace();
- }
+ @Override
+ public void warn(String message, ServletContext servletContext) {
+ try {
+ logMessage.invoke(logger, warnLevel, message);
+ } catch (Exception e) {
+ System.err.println("Failed to log message: " + message);
+ e.printStackTrace();
}
+ }
- @Override
- public void error(String message) {
- try {
- logMessage.invoke(logger, errorLevel, message);
- } catch (Exception e) {
- System.err.println("Failed to log message:" + message);
- e.printStackTrace();
- }
+ @Override
+ public void error(String message, ServletContext servletContext) {
+ try {
+ logMessage.invoke(logger, errorLevel, message);
+ } catch (Exception e) {
+ System.err.println("Failed to log message: " + message);
+ e.printStackTrace();
}
+ }
- @Override
- public void error(String message, Throwable throwable) {
- try {
- logThrowable.invoke(logger, errorLevel, message, throwable);
- } catch (Exception e) {
- System.err.println("Failed to log message:" + message);
- e.printStackTrace();
- System.err.println("Original exception:");
- throwable.printStackTrace();
- }
+ @Override
+ public void error(String message, Throwable throwable, ServletContext servletContext) {
+ try {
+ logThrowable.invoke(logger, errorLevel, message, throwable);
+ } catch (Exception e) {
+ System.err.println("Failed to log message: " + message);
+ e.printStackTrace();
+ System.err.println("Original exception:");
+ throwable.printStackTrace();
}
+ }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
index f3566c79cfa..48d9470f374 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
@@ -1,39 +1,59 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
import java.lang.reflect.Method;
-abstract class ReflectiveLoggerProvider implements LoggerProvider {
+/**
+ * Uses reflection to create loggers that may be unavailable in some environments.
+ * Compatible with platform logging and Log4j; incompatible with SLF4j.
+ */
+abstract class ReflectiveLoggerProvider implements RpcLoggerProvider {
- protected Method createLogger;
- protected Object infoLevel;
- protected Object warnLevel;
- protected Object errorLevel;
- protected Method logMessage;
- protected Method logThrowable;
- protected boolean available;
+ protected Method createLogger;
+ protected Object infoLevel;
+ protected Object warnLevel;
+ protected Object errorLevel;
+ protected Method logMessage;
+ protected Method logThrowable;
+ protected boolean available;
- @Override
- public LoggerDelegate createLogger(String name) {
- try {
- Object logger = createLogger.invoke(null, name);
- return new ReflectiveLogger(logger, infoLevel, warnLevel, errorLevel, logMessage, logThrowable);
- } catch (Exception e) {
- throw new IllegalStateException(getClass() + " is not available on this platform", e);
- }
+ @Override
+ public RpcLoggerDelegate createLogger(String name) {
+ try {
+ Object logger = createLogger.invoke(null, name);
+ return new ReflectiveLogger(logger, infoLevel, warnLevel, errorLevel, logMessage,
+ logThrowable);
+ } catch (Exception e) {
+ throw new IllegalStateException(getClass() + " is not available on this platform", e);
}
+ }
- @Override
- public boolean isAvailable() {
- return available;
- }
+ @Override
+ public boolean isAvailable() {
+ return available;
+ }
- protected Object getLogLevel(Class> levelClass, String levelName) {
- for (Object level : levelClass.getEnumConstants()) {
- if (level.toString().equals(levelName)) {
- return level;
- }
- }
- return null;
+ protected Object getLogLevel(Class> levelClass, String levelName) {
+ for (Object level : levelClass.getEnumConstants()) {
+ if (level.toString().equals(levelName)) {
+ return level;
+ }
}
+ return null;
+ }
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
new file mode 100644
index 00000000000..0ba9af41da2
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
+
+import java.util.ServiceLoader;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Logger;
+
+import javax.servlet.ServletConfig;
+
+/**
+ * Handles creation of {@link RpcLogger}s and the initialization of the {@link RpcLoggerProvider},
+ * using {@link ServiceLoader} to discover available providers.
+ *
+ * Can be manually initialized with a specific {@link RpcLoggerProvider} using an object or a fully
+ * qualified class name. If not manually initialized, it will lazily initialize, using a provider
+ * chosen in this order:
+ *
+ * - The first provider with a class name that matches a system property
+ *
gwt.rpc.logging
+ * - The first provider for which {@link RpcLoggerProvider#isDefault()} returns
+ *
true
+ * - The {@link ServletContextLoggerProvider}
+ *
+ * Included providers can be found in
+ * META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
+ */
+public class RpcLogManager {
+
+ private static final Logger logger = Logger.getLogger(RpcLogManager.class.getName());
+
+ public static final String PROVIDER_PARAMETER_KEY = "gwt.rpc.logging";
+ private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
+ private static final AtomicReference loggerProvider = new AtomicReference<>();
+ private static final Object initLock = new Object();
+
+ /**
+ * Creates or retrieves a logger for the fully qualified name of the given class.
+ * @param clazz the class for which to return a logger
+ * @return a logger
+ */
+ public static RpcLogger getLogger(Class> clazz) {
+ return loggers.computeIfAbsent(clazz.getName(), RpcLogger::new);
+ }
+
+ /**
+ * Retrieves the current instance of the {@link RpcLoggerProvider}.
+ *
+ * If the LoggerProvider is not yet initialized, initializes it in a thread-safe manner
+ * and returns the resulting instance.
+ * @return the current instance of the LoggerProvider
+ */
+ static RpcLoggerProvider getLoggerProvider() {
+ RpcLoggerProvider result = loggerProvider.get();
+ if (result == null) {
+ synchronized (initLock) {
+ result = loggerProvider.get();
+ if (result == null) {
+ initialize();
+ result = loggerProvider.get();
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Initializes the {@link RpcLoggerProvider} using the value of the system property.
+ */
+ public static void initialize() {
+ initialize(System.getProperty(PROVIDER_PARAMETER_KEY));
+ }
+
+ /**
+ * Initializes the {@link RpcLoggerProvider} with the given name.
+ *
+ * If no name is supplied, it will use the first loaded provider for which
+ * {@link RpcLoggerProvider#isDefault()} returns true.
+ *
+ * If no such provider is found, it will fall back to {@link ServletContextLoggerProvider}.
+ *
+ * The provider will be initialized only once.
+ * @param providerName the fully qualified class name of the RpcLoggerProvider to use
+ */
+ public static void initialize(String providerName) {
+ if (loggerProvider.get() == null) {
+ synchronized (initLock) {
+ if (loggerProvider.get() == null) {
+ RpcLoggerProvider fallback = new ServletContextLoggerProvider();
+ RpcLoggerProvider provider = loadProvider(providerName, fallback);
+ loggerProvider.set(provider);
+ }
+ }
+ }
+ }
+
+ /**
+ * Initializes the {@link RpcLoggerProvider} with the given provider.
+ *
+ * The provider will be initialized only once.
+ * @param provider the logger provider to use
+ */
+ public static void initialize(RpcLoggerProvider provider) {
+ if (loggerProvider.get() == null) {
+ synchronized (initLock) {
+ if (loggerProvider.get() == null) {
+ loggerProvider.set(provider);
+ }
+ }
+ }
+ }
+
+ /**
+ * Attempts to retrieve a fully qualified class name for a
+ * {@link com.google.gwt.user.server.rpc.logging.RpcLoggerProvider}, checking system properties
+ * and init parameters for gwt.rpc.logging.
+ * @param config the servlet config
+ * @return the fully qualified class name of the provider, or null if none was found.
+ */
+ public static String getProviderName(ServletConfig config) {
+ String parameterName = RpcLogManager.PROVIDER_PARAMETER_KEY;
+ if (System.getProperty(parameterName) != null) {
+ return System.getProperty(parameterName);
+ } else if (config.getInitParameter(parameterName) != null) {
+ return config.getInitParameter(parameterName);
+ } else {
+ return config.getServletContext().getInitParameter(parameterName);
+ }
+ }
+
+ /**
+ * Loads available providers and chooses the first whose class matches the given name, or the
+ * first for which {@link RpcLoggerProvider#isDefault()} returns true.
+ * @param name the fully qualified class name of the {@link RpcLoggerProvider} to use. May be
+ * null.
+ * @param fallback the provider to use in case no named or default provider is found
+ * @return the chosen provider
+ */
+ private static RpcLoggerProvider loadProvider(String name, RpcLoggerProvider fallback) {
+ ServiceLoader loaderService = ServiceLoader.load(RpcLoggerProvider.class);
+ for (RpcLoggerProvider provider : loaderService) {
+ logger.warning(provider.getClass().getName());
+ logger.warning(String.valueOf(provider.isAvailable()));
+ logger.warning(String.valueOf(provider.isDefault()));
+ if (provider.isAvailable() && provider.getClass().getName().equals(name)) {
+ return provider;
+ }
+ }
+ for (RpcLoggerProvider provider : loaderService) {
+ if (provider.isAvailable() && provider.isDefault()) {
+ return provider;
+ }
+ }
+ return fallback;
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
new file mode 100644
index 00000000000..a7e18b5a5d6
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+
+/**
+ * A wrapper that lazily creates a {@link RpcLoggerDelegate} for a given name.
+ */
+public class RpcLogger {
+
+ private final AtomicReference delegate = new AtomicReference<>();
+ private final String name;
+
+ public RpcLogger(String name) {
+ this.name = name;
+ }
+
+ public void info(String message, ServletContext servletContext) {
+ getDelegate().info(message, servletContext);
+ }
+
+ public void warn(String message, ServletContext servletContext) {
+ getDelegate().warn(message, servletContext);
+ }
+
+ public void error(String message, ServletContext servletContext) {
+ getDelegate().error(message, servletContext);
+ }
+
+ public void error(String message, Throwable throwable, ServletContext servletContext) {
+ getDelegate().error(message, throwable, servletContext);
+ }
+
+ /**
+ * Retrieves or creates a logger delegate.
+ * @return the delegate
+ */
+ private RpcLoggerDelegate getDelegate() {
+ RpcLoggerDelegate result = this.delegate.get();
+ if (result == null) {
+ RpcLoggerProvider provider = RpcLogManager.getLoggerProvider();
+ RpcLoggerDelegate newDelegate = provider.createLogger(name);
+ result = delegate.updateAndGet(old -> old == null ? newDelegate : old);
+ }
+ return result;
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
new file mode 100644
index 00000000000..ebefb0c9dfb
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
+
+import javax.servlet.ServletContext;
+
+/**
+ * A very simplified interface for logging RPC events.
+ *
+ * A {@link ServletContext} is passed for compatibility with the fallback
+ * {@link ServletContextLoggerProvider}. It may be ignored for any other implementation.
+ */
+public interface RpcLoggerDelegate {
+
+ void info(String message, ServletContext servletContext);
+
+ void warn(String message, ServletContext servletContext);
+
+ void error(String message, ServletContext servletContext);
+
+ void error(String message, Throwable throwable, ServletContext servletContext);
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
new file mode 100644
index 00000000000..d535b9ff50f
--- /dev/null
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
+
+/**
+ * A factory for {@link RpcLogger} instances.
+ */
+public interface RpcLoggerProvider {
+
+ RpcLoggerDelegate createLogger(String name);
+
+ /**
+ * If no named provider is found, the first provider that returns true will be used.
+ * @return true if this provider should be used when no named provider is found
+ */
+ default boolean isDefault() {
+ return false;
+ }
+
+ /**
+ * If false, this provider will not be used. Useful for reflective implementations.
+ * @return true if this provider can be used
+ */
+ default boolean isAvailable() {
+ return true;
+ }
+
+}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
index 1be4257454a..17198f3da17 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -1,47 +1,59 @@
+/*
+ * Copyright 2025 GWT Project Authors
+ *
+ * Licensed 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 com.google.gwt.user.server.rpc.logging;
import javax.servlet.ServletContext;
-public class ServletContextLoggerProvider implements LoggerProvider {
+/**
+ * A {@link RpcLoggerProvider} that delegates to the servlet context's logging. Used as the fallback
+ * if no other logger provider is found.
+ */
+public class ServletContextLoggerProvider implements RpcLoggerProvider {
- private final ServletContext servletContext;
+ public ServletContextLoggerProvider() {
+ }
- public ServletContextLoggerProvider(ServletContext servletContext) {
- this.servletContext = servletContext;
- }
-
- @Override
- public LoggerDelegate createLogger(String name) {
- return new ServletContextLogger(servletContext);
- }
+ @Override
+ public RpcLoggerDelegate createLogger(String name) {
+ return new ServletContextLogger();
+ }
- private static final class ServletContextLogger implements LoggerDelegate {
+ private static final class ServletContextLogger implements RpcLoggerDelegate {
- private final ServletContext servletContext;
+ ServletContextLogger() { }
- public ServletContextLogger(ServletContext servletContext) {
- this.servletContext = servletContext;
- }
-
- @Override
- public void info(String message) {
- servletContext.log(message);
- }
-
- @Override
- public void warn(String message) {
- servletContext.log("WARNING: " + message);
- }
+ @Override
+ public void info(String message, ServletContext servletContext) {
+ servletContext.log(message);
+ }
- @Override public void error(String message) {
- servletContext.log("ERROR: " + message);
- }
+ @Override
+ public void warn(String message, ServletContext servletContext) {
+ servletContext.log("WARNING: " + message);
+ }
- @Override
- public void error(String message, Throwable throwable) {
- servletContext.log(message, throwable);
- }
+ @Override
+ public void error(String message, ServletContext servletContext) {
+ servletContext.log("ERROR: " + message);
+ }
+ @Override
+ public void error(String message, Throwable throwable, ServletContext servletContext) {
+ servletContext.log(message, throwable);
}
+ }
}
From 84799945f1b9c8dfe19dad627744908fb11f623e Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 11 Dec 2025 09:14:27 -0600
Subject: [PATCH 04/23] Remove unneeded double-checked locking from
RpcLogManager#getLoggerProvider()
---
.../gwt/user/server/rpc/logging/RpcLogManager.java | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 0ba9af41da2..ad3de94a3af 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -67,13 +67,8 @@ public static RpcLogger getLogger(Class> clazz) {
static RpcLoggerProvider getLoggerProvider() {
RpcLoggerProvider result = loggerProvider.get();
if (result == null) {
- synchronized (initLock) {
- result = loggerProvider.get();
- if (result == null) {
- initialize();
- result = loggerProvider.get();
- }
- }
+ initialize();
+ result = loggerProvider.get();
}
return result;
}
From a193b4b72b549197ce482e506e016a499c38da5c Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 11 Dec 2025 09:32:30 -0600
Subject: [PATCH 05/23] Added missing prefix to
ServletContextLogger#error(String, Throwable, ServletContext)
---
.../user/server/rpc/logging/ServletContextLoggerProvider.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
index 17198f3da17..bd0f67e4d10 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -52,7 +52,7 @@ public void error(String message, ServletContext servletContext) {
@Override
public void error(String message, Throwable throwable, ServletContext servletContext) {
- servletContext.log(message, throwable);
+ servletContext.log("ERROR: " + message, throwable);
}
}
From ff5c4a4546698d4a062e8530a9441cbfe03eee72 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Wed, 11 Feb 2026 17:09:03 -0600
Subject: [PATCH 06/23] Remove reflective logger implementations
---
....user.server.rpc.logging.RpcLoggerProvider | 2 -
.../server/rpc/logging/Log4jProvider.java | 43 ---------
.../rpc/logging/PlatformLoggerProvider.java | 41 ---------
.../server/rpc/logging/ReflectiveLogger.java | 87 -------------------
.../rpc/logging/ReflectiveLoggerProvider.java | 59 -------------
5 files changed, 232 deletions(-)
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
diff --git a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
index 0eccdd0db68..b6a1e7847d3 100644
--- a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
+++ b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
@@ -1,4 +1,2 @@
com.google.gwt.user.server.rpc.logging.JulLoggerProvider
-com.google.gwt.user.server.rpc.logging.Log4jProvider
-com.google.gwt.user.server.rpc.logging.PlatformLoggerProvider
com.google.gwt.user.server.rpc.logging.ServletContextLoggerProvider
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
deleted file mode 100644
index 652e7ba66fa..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/Log4jProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2025 GWT Project Authors
- *
- * Licensed 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 com.google.gwt.user.server.rpc.logging;
-
-import java.lang.reflect.Method;
-
-/**
- * A {@link RpcLoggerProvider} that delegates to Log4j.
- */
-public class Log4jProvider extends ReflectiveLoggerProvider implements RpcLoggerProvider {
-
- public Log4jProvider() {
- try {
- Class> factory = Class.forName("org.apache.logging.log4j.LogManager");
- createLogger = factory.getMethod("getLogger", String.class);
- Class> levelClass = Class.forName("org.apache.logging.log4j.Level");
- Method levelFinder = levelClass.getMethod("valueOf", String.class);
- infoLevel = levelFinder.invoke(null, "INFO");
- warnLevel = levelFinder.invoke(null, "WARN");
- errorLevel = levelFinder.invoke(null, "ERROR");
- Class> loggerClass = Class.forName("org.apache.logging.log4j.Logger");
- logMessage = loggerClass.getMethod("log", levelClass, String.class);
- logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
- available = true;
- } catch (Exception e) {
- available = false;
- }
- }
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
deleted file mode 100644
index 057e4990624..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/PlatformLoggerProvider.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2025 GWT Project Authors
- *
- * Licensed 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 com.google.gwt.user.server.rpc.logging;
-
-/**
- * A {@link RpcLoggerProvider} that delegates to System.Logger
- *
- * Once support for Java 8 is dropped, this class can be implemented without reflection.
- */
-public class PlatformLoggerProvider extends ReflectiveLoggerProvider implements RpcLoggerProvider {
-
- public PlatformLoggerProvider() {
- try {
- createLogger = System.class.getMethod("getLogger", String.class);
- Class> levelClass = Class.forName("java.lang.System$Logger$Level");
- infoLevel = getLogLevel(levelClass, "INFO");
- warnLevel = getLogLevel(levelClass, "WARNING");
- errorLevel = getLogLevel(levelClass, "ERROR");
- Class> loggerClass = Class.forName("java.lang.System$Logger");
- logMessage = loggerClass.getMethod("log", levelClass, String.class);
- logThrowable = loggerClass.getMethod("log", levelClass, String.class, Throwable.class);
- available = true;
- } catch (Exception e) {
- available = false;
- }
- }
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
deleted file mode 100644
index 497c5260be2..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLogger.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2025 GWT Project Authors
- *
- * Licensed 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 com.google.gwt.user.server.rpc.logging;
-
-import java.lang.reflect.Method;
-
-import javax.servlet.ServletContext;
-
-/**
- * A logger that uses reflection to call the underlying platform logger.
- * Compatible with platform logging and Log4j; incompatible with SLF4j.
- */
-class ReflectiveLogger implements RpcLoggerDelegate {
-
- private final Object logger;
- private final Object infoLevel;
- private final Object warnLevel;
- private final Object errorLevel;
- private final Method logMessage;
- private final Method logThrowable;
-
- ReflectiveLogger(Object platformLogger, Object infoLevel, Object warnLevel, Object errorLevel,
- Method logMessage, Method logThrowable) {
- this.logger = platformLogger;
- this.infoLevel = infoLevel;
- this.warnLevel = warnLevel;
- this.errorLevel = errorLevel;
- this.logMessage = logMessage;
- this.logThrowable = logThrowable;
- }
-
- @Override
- public void info(String message, ServletContext servletContext) {
- try {
- logMessage.invoke(logger, infoLevel, message);
- } catch (Exception e) {
- System.err.println("Failed to log message: " + message);
- e.printStackTrace();
- }
- }
-
- @Override
- public void warn(String message, ServletContext servletContext) {
- try {
- logMessage.invoke(logger, warnLevel, message);
- } catch (Exception e) {
- System.err.println("Failed to log message: " + message);
- e.printStackTrace();
- }
- }
-
- @Override
- public void error(String message, ServletContext servletContext) {
- try {
- logMessage.invoke(logger, errorLevel, message);
- } catch (Exception e) {
- System.err.println("Failed to log message: " + message);
- e.printStackTrace();
- }
- }
-
- @Override
- public void error(String message, Throwable throwable, ServletContext servletContext) {
- try {
- logThrowable.invoke(logger, errorLevel, message, throwable);
- } catch (Exception e) {
- System.err.println("Failed to log message: " + message);
- e.printStackTrace();
- System.err.println("Original exception:");
- throwable.printStackTrace();
- }
- }
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
deleted file mode 100644
index 48d9470f374..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/ReflectiveLoggerProvider.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2025 GWT Project Authors
- *
- * Licensed 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 com.google.gwt.user.server.rpc.logging;
-
-import java.lang.reflect.Method;
-
-/**
- * Uses reflection to create loggers that may be unavailable in some environments.
- * Compatible with platform logging and Log4j; incompatible with SLF4j.
- */
-abstract class ReflectiveLoggerProvider implements RpcLoggerProvider {
-
- protected Method createLogger;
- protected Object infoLevel;
- protected Object warnLevel;
- protected Object errorLevel;
- protected Method logMessage;
- protected Method logThrowable;
- protected boolean available;
-
- @Override
- public RpcLoggerDelegate createLogger(String name) {
- try {
- Object logger = createLogger.invoke(null, name);
- return new ReflectiveLogger(logger, infoLevel, warnLevel, errorLevel, logMessage,
- logThrowable);
- } catch (Exception e) {
- throw new IllegalStateException(getClass() + " is not available on this platform", e);
- }
- }
-
- @Override
- public boolean isAvailable() {
- return available;
- }
-
- protected Object getLogLevel(Class> levelClass, String levelName) {
- for (Object level : levelClass.getEnumConstants()) {
- if (level.toString().equals(levelName)) {
- return level;
- }
- }
- return null;
- }
-
-}
From 4c02c1d35058b3ae3e83b59394db021a1f853504 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Wed, 11 Feb 2026 17:20:38 -0600
Subject: [PATCH 07/23] Remove accidentally committed logging
---
.../google/gwt/user/server/rpc/logging/RpcLogManager.java | 6 ------
1 file changed, 6 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index ad3de94a3af..323f12db2ba 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -18,7 +18,6 @@
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.logging.Logger;
import javax.servlet.ServletConfig;
@@ -41,8 +40,6 @@
*/
public class RpcLogManager {
- private static final Logger logger = Logger.getLogger(RpcLogManager.class.getName());
-
public static final String PROVIDER_PARAMETER_KEY = "gwt.rpc.logging";
private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
private static final AtomicReference loggerProvider = new AtomicReference<>();
@@ -148,9 +145,6 @@ public static String getProviderName(ServletConfig config) {
private static RpcLoggerProvider loadProvider(String name, RpcLoggerProvider fallback) {
ServiceLoader loaderService = ServiceLoader.load(RpcLoggerProvider.class);
for (RpcLoggerProvider provider : loaderService) {
- logger.warning(provider.getClass().getName());
- logger.warning(String.valueOf(provider.isAvailable()));
- logger.warning(String.valueOf(provider.isDefault()));
if (provider.isAvailable() && provider.getClass().getName().equals(name)) {
return provider;
}
From aed936577d96d4272bb4c71e43f2f64caa154f71 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Wed, 11 Feb 2026 17:23:24 -0600
Subject: [PATCH 08/23] Add real delegate to RpcLogger constructor instead of
trying to lazily initialize the delegate
---
.../server/rpc/logging/RpcLogManager.java | 5 +--
.../user/server/rpc/logging/RpcLogger.java | 33 +++++--------------
2 files changed, 11 insertions(+), 27 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 323f12db2ba..1d863d4c945 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -51,7 +51,8 @@ public class RpcLogManager {
* @return a logger
*/
public static RpcLogger getLogger(Class> clazz) {
- return loggers.computeIfAbsent(clazz.getName(), RpcLogger::new);
+ return loggers.computeIfAbsent(clazz.getName(),
+ name -> new RpcLogger(name, getLoggerProvider()));
}
/**
@@ -61,7 +62,7 @@ public static RpcLogger getLogger(Class> clazz) {
* and returns the resulting instance.
* @return the current instance of the LoggerProvider
*/
- static RpcLoggerProvider getLoggerProvider() {
+ private static RpcLoggerProvider getLoggerProvider() {
RpcLoggerProvider result = loggerProvider.get();
if (result == null) {
initialize();
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
index a7e18b5a5d6..15938331bfe 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
@@ -15,50 +15,33 @@
*/
package com.google.gwt.user.server.rpc.logging;
-import java.util.concurrent.atomic.AtomicReference;
-
import javax.servlet.ServletContext;
/**
* A wrapper that lazily creates a {@link RpcLoggerDelegate} for a given name.
*/
-public class RpcLogger {
+public final class RpcLogger {
- private final AtomicReference delegate = new AtomicReference<>();
- private final String name;
+ private final RpcLoggerDelegate delegate;
- public RpcLogger(String name) {
- this.name = name;
+ RpcLogger(String name, RpcLoggerProvider provider) {
+ delegate = provider.createLogger(name);
}
public void info(String message, ServletContext servletContext) {
- getDelegate().info(message, servletContext);
+ delegate.info(message, servletContext);
}
public void warn(String message, ServletContext servletContext) {
- getDelegate().warn(message, servletContext);
+ delegate.warn(message, servletContext);
}
public void error(String message, ServletContext servletContext) {
- getDelegate().error(message, servletContext);
+ delegate.error(message, servletContext);
}
public void error(String message, Throwable throwable, ServletContext servletContext) {
- getDelegate().error(message, throwable, servletContext);
- }
-
- /**
- * Retrieves or creates a logger delegate.
- * @return the delegate
- */
- private RpcLoggerDelegate getDelegate() {
- RpcLoggerDelegate result = this.delegate.get();
- if (result == null) {
- RpcLoggerProvider provider = RpcLogManager.getLoggerProvider();
- RpcLoggerDelegate newDelegate = provider.createLogger(name);
- result = delegate.updateAndGet(old -> old == null ? newDelegate : old);
- }
- return result;
+ delegate.error(message, throwable, servletContext);
}
}
From 370a386d79b09a88e2b41fc7644c175409afcaf7 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 12 Feb 2026 10:30:52 -0600
Subject: [PATCH 09/23] Remove manual logger initialization mechanisms
---
.../user/server/rpc/RemoteServiceServlet.java | 2 -
.../server/rpc/logging/RpcLogManager.java | 60 +++----------------
.../server/rpc/logging/RpcLoggerProvider.java | 8 ---
3 files changed, 8 insertions(+), 62 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index 1e2e698bbe6..5d80b35b25a 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -191,8 +191,6 @@ public RemoteServiceServlet(Object delegate) {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
- String providerName = RpcLogManager.getProviderName(config);
- RpcLogManager.initialize(providerName);
codeServerPort = getCodeServerPort();
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 1d863d4c945..b4ea1de872b 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -19,17 +19,14 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
-import javax.servlet.ServletConfig;
-
/**
* Handles creation of {@link RpcLogger}s and the initialization of the {@link RpcLoggerProvider},
* using {@link ServiceLoader} to discover available providers.
*
- * Can be manually initialized with a specific {@link RpcLoggerProvider} using an object or a fully
- * qualified class name. If not manually initialized, it will lazily initialize, using a provider
- * chosen in this order:
+ * The provider is initialized the first time a logger is requested. The provider is chosen in this
+ * order:
*
- * - The first provider with a class name that matches a system property
+ *
- The first provider with a fully-qualified class name that matches the system property
*
gwt.rpc.logging
* - The first provider for which {@link RpcLoggerProvider#isDefault()} returns
*
true
@@ -72,14 +69,7 @@ private static RpcLoggerProvider getLoggerProvider() {
}
/**
- * Initializes the {@link RpcLoggerProvider} using the value of the system property.
- */
- public static void initialize() {
- initialize(System.getProperty(PROVIDER_PARAMETER_KEY));
- }
-
- /**
- * Initializes the {@link RpcLoggerProvider} with the given name.
+ * Initializes the {@link RpcLoggerProvider}, attempting to use a name from a system property.
*
* If no name is supplied, it will use the first loaded provider for which
* {@link RpcLoggerProvider#isDefault()} returns true.
@@ -87,9 +77,9 @@ public static void initialize() {
* If no such provider is found, it will fall back to {@link ServletContextLoggerProvider}.
*
* The provider will be initialized only once.
- * @param providerName the fully qualified class name of the RpcLoggerProvider to use
*/
- public static void initialize(String providerName) {
+ private static void initialize() {
+ String providerName = System.getProperty(PROVIDER_PARAMETER_KEY);
if (loggerProvider.get() == null) {
synchronized (initLock) {
if (loggerProvider.get() == null) {
@@ -101,40 +91,6 @@ public static void initialize(String providerName) {
}
}
- /**
- * Initializes the {@link RpcLoggerProvider} with the given provider.
- *
- * The provider will be initialized only once.
- * @param provider the logger provider to use
- */
- public static void initialize(RpcLoggerProvider provider) {
- if (loggerProvider.get() == null) {
- synchronized (initLock) {
- if (loggerProvider.get() == null) {
- loggerProvider.set(provider);
- }
- }
- }
- }
-
- /**
- * Attempts to retrieve a fully qualified class name for a
- * {@link com.google.gwt.user.server.rpc.logging.RpcLoggerProvider}, checking system properties
- * and init parameters for gwt.rpc.logging.
- * @param config the servlet config
- * @return the fully qualified class name of the provider, or null if none was found.
- */
- public static String getProviderName(ServletConfig config) {
- String parameterName = RpcLogManager.PROVIDER_PARAMETER_KEY;
- if (System.getProperty(parameterName) != null) {
- return System.getProperty(parameterName);
- } else if (config.getInitParameter(parameterName) != null) {
- return config.getInitParameter(parameterName);
- } else {
- return config.getServletContext().getInitParameter(parameterName);
- }
- }
-
/**
* Loads available providers and chooses the first whose class matches the given name, or the
* first for which {@link RpcLoggerProvider#isDefault()} returns true.
@@ -146,12 +102,12 @@ public static String getProviderName(ServletConfig config) {
private static RpcLoggerProvider loadProvider(String name, RpcLoggerProvider fallback) {
ServiceLoader loaderService = ServiceLoader.load(RpcLoggerProvider.class);
for (RpcLoggerProvider provider : loaderService) {
- if (provider.isAvailable() && provider.getClass().getName().equals(name)) {
+ if (provider.getClass().getName().equals(name)) {
return provider;
}
}
for (RpcLoggerProvider provider : loaderService) {
- if (provider.isAvailable() && provider.isDefault()) {
+ if (provider.isDefault()) {
return provider;
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
index d535b9ff50f..b07dcb6e5fd 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
@@ -30,12 +30,4 @@ default boolean isDefault() {
return false;
}
- /**
- * If false, this provider will not be used. Useful for reflective implementations.
- * @return true if this provider can be used
- */
- default boolean isAvailable() {
- return true;
- }
-
}
From 6c567288a494042ca29df40cf1f5961387159503 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 12 Feb 2026 10:56:33 -0600
Subject: [PATCH 10/23] Remove redundant atomic for RpcLoggerProvider
---
.../server/rpc/logging/RpcLogManager.java | 22 +++++++------------
1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index b4ea1de872b..692bdac84bb 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -17,7 +17,6 @@
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Handles creation of {@link RpcLogger}s and the initialization of the {@link RpcLoggerProvider},
@@ -39,7 +38,7 @@ public class RpcLogManager {
public static final String PROVIDER_PARAMETER_KEY = "gwt.rpc.logging";
private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
- private static final AtomicReference loggerProvider = new AtomicReference<>();
+ private static volatile RpcLoggerProvider loggerProvider;
private static final Object initLock = new Object();
/**
@@ -60,12 +59,10 @@ public static RpcLogger getLogger(Class> clazz) {
* @return the current instance of the LoggerProvider
*/
private static RpcLoggerProvider getLoggerProvider() {
- RpcLoggerProvider result = loggerProvider.get();
- if (result == null) {
+ if (loggerProvider == null) {
initialize();
- result = loggerProvider.get();
}
- return result;
+ return loggerProvider;
}
/**
@@ -76,17 +73,14 @@ private static RpcLoggerProvider getLoggerProvider() {
*
* If no such provider is found, it will fall back to {@link ServletContextLoggerProvider}.
*
- * The provider will be initialized only once.
+ * If the provider is already initialized, no action is taken.
*/
private static void initialize() {
String providerName = System.getProperty(PROVIDER_PARAMETER_KEY);
- if (loggerProvider.get() == null) {
- synchronized (initLock) {
- if (loggerProvider.get() == null) {
- RpcLoggerProvider fallback = new ServletContextLoggerProvider();
- RpcLoggerProvider provider = loadProvider(providerName, fallback);
- loggerProvider.set(provider);
- }
+ synchronized (initLock) {
+ if (loggerProvider == null) {
+ RpcLoggerProvider fallback = new ServletContextLoggerProvider();
+ loggerProvider = loadProvider(providerName, fallback);
}
}
}
From 949f5ef299b716ae908e7e777ba19298c02beca6 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 12 Feb 2026 11:10:52 -0600
Subject: [PATCH 11/23] Fix link to maven guide
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0438255c30d..1bc9f498621 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@
`$ ant clean dist`
- - To create maven artifacts (after building .jar using ant), use [following guide](./maven/README.txt).
+ - To create maven artifacts (after building .jar using ant), use [following guide](./maven/README.md).
### How to verify GWT code conventions:
From fc0680f518392af68c4f1838b064f6a38346bd31 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 12 Feb 2026 13:55:51 -0600
Subject: [PATCH 12/23] Remove RpcLoggerProvider services file, which is not
being packaged properly and would not work in many environments
---
user/build.xml | 7 -------
...om.google.gwt.user.server.rpc.logging.RpcLoggerProvider | 2 --
.../google/gwt/user/server/rpc/logging/RpcLogManager.java | 2 --
.../com/google/gwt/user/server/rpc/logging/RpcLogger.java | 2 +-
4 files changed, 1 insertion(+), 12 deletions(-)
delete mode 100644 user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
diff --git a/user/build.xml b/user/build.xml
index 55b0d0b6a00..16282cdad22 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -163,13 +163,6 @@
-
-
-
-
-
-
-
diff --git a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider b/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
deleted file mode 100644
index b6a1e7847d3..00000000000
--- a/user/src/META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
+++ /dev/null
@@ -1,2 +0,0 @@
-com.google.gwt.user.server.rpc.logging.JulLoggerProvider
-com.google.gwt.user.server.rpc.logging.ServletContextLoggerProvider
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 692bdac84bb..b61a0e5ccc6 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -31,8 +31,6 @@
* true
* The {@link ServletContextLoggerProvider}
*
- * Included providers can be found in
- * META-INF/services/com.google.gwt.user.server.rpc.logging.RpcLoggerProvider
*/
public class RpcLogManager {
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
index 15938331bfe..7d03e8df568 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
@@ -18,7 +18,7 @@
import javax.servlet.ServletContext;
/**
- * A wrapper that lazily creates a {@link RpcLoggerDelegate} for a given name.
+ * A wrapper that creates a {@link RpcLoggerDelegate} for a given name.
*/
public final class RpcLogger {
From 68a3b9dd4652ff26fd043d0b78bb2cc3f842810b Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 12 Feb 2026 16:11:48 -0600
Subject: [PATCH 13/23] Remove reference to services directory from maven build
script
---
maven/lib-gwt.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/maven/lib-gwt.sh b/maven/lib-gwt.sh
index 113f1ed13a4..24b1386b4b6 100644
--- a/maven/lib-gwt.sh
+++ b/maven/lib-gwt.sh
@@ -86,7 +86,7 @@ function maven-gwt() {
zip -q $GWT_EXTRACT_DIR/gwt-user.jar --copy --out $GWT_EXTRACT_DIR/gwt-user-trimmed.jar \
"com/google/gwt/*" "com/google/web/bindery/*" "javaemul/*" \
"javax/validation/*" "org/hibernate/validator/*" \
- "org/w3c/flute/*" "META-INF/services/*"
+ "org/w3c/flute/*"
mv $GWT_EXTRACT_DIR/gwt-user-trimmed.jar $GWT_EXTRACT_DIR/gwt-user.jar
for i in $gwtLibs
From 5dd9a4f36c4681ecdff84e90b2b4729184788064 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Thu, 12 Feb 2026 16:13:05 -0600
Subject: [PATCH 14/23] Correct continuation indent
---
.../com/google/gwt/user/server/rpc/logging/RpcLogManager.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index b61a0e5ccc6..6a11dae2f46 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -46,7 +46,7 @@ public class RpcLogManager {
*/
public static RpcLogger getLogger(Class> clazz) {
return loggers.computeIfAbsent(clazz.getName(),
- name -> new RpcLogger(name, getLoggerProvider()));
+ name -> new RpcLogger(name, getLoggerProvider()));
}
/**
From 72127bcf188620878d82b54af3d9d167b25f1bbe Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Fri, 13 Feb 2026 16:52:04 -0600
Subject: [PATCH 15/23] Remove ServletContext parameter from RpcLogger methods
by instead setting it on the ServletContextLogger
---
.../rpc/AbstractRemoteServiceServlet.java | 5 +-
.../gwt/user/server/rpc/RPCServletUtils.java | 34 +++++++++---
.../user/server/rpc/RemoteServiceServlet.java | 32 +++++------
.../server/rpc/SerializationPolicyClient.java | 28 +++++-----
.../server/rpc/logging/JulLoggerProvider.java | 10 ++--
.../server/rpc/logging/RpcLogManager.java | 13 +++++
.../user/server/rpc/logging/RpcLogger.java | 18 +++----
.../server/rpc/logging/RpcLoggerDelegate.java | 8 +--
.../logging/ServletContextLoggerProvider.java | 54 +++++++++++++++----
9 files changed, 125 insertions(+), 77 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
index 5cddb00faf7..bca9fc8d276 100644
--- a/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
@@ -19,7 +19,6 @@
import java.io.IOException;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -105,9 +104,7 @@ protected void doUnexpectedFailure(Throwable e) {
*/
throw new RuntimeException("Unable to report failure", e);
}
- ServletContext servletContext = getServletContext();
- RPCServletUtils.writeResponseForUnexpectedFailure(servletContext,
- getThreadLocalResponse(), e);
+ RPCServletUtils.writeResponseForUnexpectedFailure(getThreadLocalResponse(), e);
}
/**
diff --git a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
index e641cd8fc73..30cd3eb5327 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
@@ -325,6 +325,16 @@ public static boolean shouldGzipResponseContent(HttpServletRequest request,
&& exceedsUncompressedContentLengthLimit(responseContent);
}
+ /**
+ * @deprecated Use {@link #writeResponse(HttpServletResponse, String, boolean)} instead; the
+ * servlet context is no longer needed.
+ */
+ @Deprecated
+ public static void writeResponse(ServletContext ignored, HttpServletResponse response,
+ String responseContent, boolean gzipResponse) throws IOException {
+ writeResponse(response, responseContent, gzipResponse);
+ }
+
/**
* Write the response content into the {@link HttpServletResponse}. If
* gzipResponse is true, the response content will
@@ -337,9 +347,8 @@ public static boolean shouldGzipResponseContent(HttpServletRequest request,
* @throws IOException if reading, writing, or closing the response's output
* stream fails
*/
- public static void writeResponse(ServletContext servletContext,
- HttpServletResponse response, String responseContent, boolean gzipResponse)
- throws IOException {
+ public static void writeResponse(HttpServletResponse response, String responseContent,
+ boolean gzipResponse) throws IOException {
byte[] responseBytes = responseContent.getBytes(StandardCharsets.UTF_8);
if (gzipResponse) {
@@ -368,7 +377,7 @@ public static void writeResponse(ServletContext servletContext,
}
if (caught != null) {
- logger.error("Unable to compress response", caught, servletContext);
+ logger.error("Unable to compress response", caught);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
@@ -383,6 +392,16 @@ public static void writeResponse(ServletContext servletContext,
response.getOutputStream().write(responseBytes);
}
+ /**
+ * @deprecated Use {@link #writeResponseForUnexpectedFailure(HttpServletResponse, Throwable)}
+ * instead; the servlet context is no longer needed.
+ */
+ @Deprecated
+ public static void writeResponseForUnexpectedFailure(ServletContext ignored,
+ HttpServletResponse response, Throwable failure) {
+ writeResponseForUnexpectedFailure(response, failure);
+ }
+
/**
* Called when the servlet itself has a problem, rather than the invoked
* third-party method. It writes a simple 500 message back to the client.
@@ -390,10 +409,9 @@ public static void writeResponse(ServletContext servletContext,
* @param response
* @param failure
*/
- public static void writeResponseForUnexpectedFailure(
- ServletContext servletContext, HttpServletResponse response,
+ public static void writeResponseForUnexpectedFailure(HttpServletResponse response,
Throwable failure) {
- logger.error("Exception while dispatching incoming RPC call", failure, servletContext);
+ logger.error("Exception while dispatching incoming RPC call", failure);
// Send GENERIC_FAILURE_MSG with 500 status.
//
@@ -409,7 +427,7 @@ public static void writeResponseForUnexpectedFailure(
} catch (IOException ex) {
logger.error(
"respondWithUnexpectedFailure failed while sending the previous failure to the client",
- ex, servletContext);
+ ex);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index 5d80b35b25a..28ff67d5ea1 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -66,7 +66,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
modulePath = new URL(moduleBaseURL).getPath();
} catch (MalformedURLException ex) {
// log the information, we will default
- logger.error("Malformed moduleBaseURL: " + moduleBaseURL, ex, servlet.getServletContext());
+ logger.error("Malformed moduleBaseURL: " + moduleBaseURL, ex);
}
}
@@ -84,7 +84,7 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
+ contextPath
+ ". Your module may not be properly configured " +
"or your client and server code maybe out of date.";
- logger.error(message, servlet.getServletContext());
+ logger.error(message);
} else {
// Strip off the context path from the module base URL. It should be a
// strict prefix.
@@ -105,30 +105,28 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
if (ENABLE_ENHANCED_CLASSES) {
logger.warn("Service deserializes enhanced JPA/JDO classes, which is " +
"unsafe. See https://github.com/gwtproject/gwt/issues/9709 for more " +
- "detail on the vulnerability that this presents.",
- servlet.getServletContext());
+ "detail on the vulnerability that this presents.");
} else {
logger.error("Service deserializes enhanced JPA/JDO classes, which is " +
"unsafe. Review build logs to see which classes are affected, or set " +
ENABLE_GWT_ENHANCED_CLASSES_PROPERTY + " to true to allow using this " +
"service. See https://github.com/gwtproject/gwt/issues/9709 for more " +
- "detail.",
- servlet.getServletContext());
+ "detail.");
serializationPolicy = null;
}
}
} catch (ParseException e) {
logger.error("Failed to parse the policy file '"
- + serializationPolicyFilePath + "'", e, servlet.getServletContext());
+ + serializationPolicyFilePath + "'", e);
} catch (IOException e) {
logger.error("Could not read the policy file '"
- + serializationPolicyFilePath + "'", e, servlet.getServletContext());
+ + serializationPolicyFilePath + "'", e);
}
} else {
String message = "The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
- logger.error(message, servlet.getServletContext());
+ logger.error(message);
}
} finally {
if (is != null) {
@@ -191,6 +189,7 @@ public RemoteServiceServlet(Object delegate) {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
+ RpcLogManager.setServletContext(config.getServletContext());
codeServerPort = getCodeServerPort();
}
@@ -274,8 +273,7 @@ public final SerializationPolicy getSerializationPolicy(String moduleBaseURL,
+ "' for module '"
+ moduleBaseURL
+ "'; a legacy, 1.3.3 compatible, serialization policy will be used. " +
- "You may experience SerializationExceptions as a result.",
- getServletContext());
+ "You may experience SerializationExceptions as a result.");
serializationPolicy = RPC.getDefaultSerializationPolicy();
}
@@ -323,7 +321,7 @@ public String processCall(String payload) throws SerializationException {
} catch (IncompatibleRemoteServiceException ex) {
logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
- ex, getServletContext());
+ ex);
return RPC.encodeResponseForFailedRequest(null, ex);
}
return processCall(rpcRequest);
@@ -362,12 +360,10 @@ public String processCall(RPCRequest rpcRequest) throws SerializationException {
} catch (IncompatibleRemoteServiceException ex) {
logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
- ex,
- getServletContext());
+ ex);
return RPC.encodeResponseForFailedRequest(rpcRequest, ex);
} catch (RpcTokenException tokenException) {
- logger.error("An RpcTokenException was thrown while processing this call.",
- tokenException, getServletContext());
+ logger.error("An RpcTokenException was thrown while processing this call.");
return RPC.encodeResponseForFailedRequest(rpcRequest, tokenException);
}
}
@@ -475,7 +471,7 @@ protected String getCodeServerPolicyUrl(String strongName) {
* no authentication. It should only be used during development.
*/
protected SerializationPolicy loadPolicyFromCodeServer(String url) {
- return CODE_SERVER_CLIENT.loadPolicy(url, getServletContext());
+ return CODE_SERVER_CLIENT.loadPolicy(url);
}
/**
@@ -539,6 +535,6 @@ private void writeResponse(HttpServletRequest request,
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
&& shouldCompressResponse(request, response, responsePayload);
- RPCServletUtils.writeResponse(getServletContext(), response, responsePayload, gzipEncode);
+ RPCServletUtils.writeResponse(response, responsePayload, gzipEncode);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
index e24df254ed5..dea789c91f7 100644
--- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
+++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
@@ -28,8 +28,6 @@
import java.util.ArrayList;
import java.util.List;
-import javax.servlet.ServletContext;
-
/**
* A simple and relatively naive client for downloading serialization policies from a URL.
* (Intended only for development.)
@@ -50,12 +48,12 @@ class SerializationPolicyClient {
this.readTimeout = readTimeoutMs;
}
- SerializationPolicy loadPolicy(String url, ServletContext servletContext) {
+ SerializationPolicy loadPolicy(String url) {
URL urlObj;
try {
urlObj = new URL(url);
} catch (MalformedURLException e) {
- logger.error("Can't parse serialization policy URL: " + url, e, servletContext);
+ logger.error("Can't parse serialization policy URL: " + url, e);
return null;
}
@@ -73,11 +71,11 @@ SerializationPolicy loadPolicy(String url, ServletContext servletContext) {
conn.connect();
in = conn.getInputStream();
} catch (IOException e) {
- logger.error("Can't open serialization policy URL: " + url, e, servletContext);
+ logger.error("Can't open serialization policy URL: " + url, e);
return null;
}
- return readPolicy(in, url, servletContext);
+ return readPolicy(in, url);
}
/**
@@ -86,35 +84,33 @@ SerializationPolicy loadPolicy(String url, ServletContext servletContext) {
* @param sourceName names the source of the input stream for log messages.
* @return the policy or null if unavailable.
*/
- private static SerializationPolicy readPolicy(InputStream in, String sourceName,
- ServletContext servletContext) {
+ private static SerializationPolicy readPolicy(InputStream in, String sourceName) {
try {
List errs = new ArrayList();
SerializationPolicy policy = SerializationPolicyLoader.loadFromStream(in, errs);
- logger.info("Downloaded serialization policy from " + sourceName, servletContext);
+ logger.info("Downloaded serialization policy from " + sourceName);
if (!errs.isEmpty()) {
- logMissingClasses(errs, servletContext);
+ logMissingClasses(errs);
}
return policy;
} catch (ParseException e) {
- logger.error("Can't parse serialization policy from " + sourceName, e, servletContext);
+ logger.error("Can't parse serialization policy from " + sourceName, e);
return null;
} catch (IOException e) {
- logger.error("Can't read serialization policy from " + sourceName, e, servletContext);
+ logger.error("Can't read serialization policy from " + sourceName, e);
return null;
} finally {
try {
in.close();
} catch (IOException e) {
- logger.error("Can't close serialization policy url: " + sourceName, e, servletContext);
+ logger.error("Can't close serialization policy url: " + sourceName, e);
}
}
}
- private static void logMissingClasses(List errs,
- ServletContext servletContext) {
+ private static void logMissingClasses(List errs) {
StringBuilder message = new StringBuilder();
message.append("Unable to load server-side classes used by policy:\n");
@@ -126,7 +122,7 @@ private static void logMissingClasses(List errs,
if (omitted > 0) {
message.append(" (omitted " + omitted + " more classes)\n");
}
- logger.info(message.toString(), servletContext);
+ logger.info(message.toString());
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
index b7b09f37e33..a639fc215e9 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
@@ -18,8 +18,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.servlet.ServletContext;
-
/**
* A {@link RpcLoggerProvider} that delegates to {@link java.util.logging.Logger}.
*/
@@ -41,22 +39,22 @@ private static final class JulLogger implements RpcLoggerDelegate {
}
@Override
- public void info(String message, ServletContext servletContext) {
+ public void info(String message) {
logger.log(Level.INFO, message);
}
@Override
- public void warn(String message, ServletContext servletContext) {
+ public void warn(String message) {
logger.log(Level.WARNING, message);
}
@Override
- public void error(String message, ServletContext servletContext) {
+ public void error(String message) {
logger.log(Level.SEVERE, message);
}
@Override
- public void error(String message, Throwable throwable, ServletContext servletContext) {
+ public void error(String message, Throwable throwable) {
logger.log(Level.SEVERE, message, throwable);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 6a11dae2f46..d1c05d2fd81 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -18,6 +18,8 @@
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
+import javax.servlet.ServletContext;
+
/**
* Handles creation of {@link RpcLogger}s and the initialization of the {@link RpcLoggerProvider},
* using {@link ServiceLoader} to discover available providers.
@@ -49,6 +51,17 @@ public static RpcLogger getLogger(Class> clazz) {
name -> new RpcLogger(name, getLoggerProvider()));
}
+ /**
+ * Sets the servlet context of the {@link ServletContextLoggerProvider} to use for logging. Has no
+ * effect if the provider is not a {@link ServletContextLoggerProvider}.
+ * @param servletContext the servlet context to use
+ */
+ public static void setServletContext(ServletContext servletContext) {
+ if (loggerProvider instanceof ServletContextLoggerProvider) {
+ ((ServletContextLoggerProvider) loggerProvider).setServletContext(servletContext);
+ }
+ }
+
/**
* Retrieves the current instance of the {@link RpcLoggerProvider}.
*
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
index 7d03e8df568..52d28a3e6d6 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
@@ -15,8 +15,6 @@
*/
package com.google.gwt.user.server.rpc.logging;
-import javax.servlet.ServletContext;
-
/**
* A wrapper that creates a {@link RpcLoggerDelegate} for a given name.
*/
@@ -28,20 +26,20 @@ public final class RpcLogger {
delegate = provider.createLogger(name);
}
- public void info(String message, ServletContext servletContext) {
- delegate.info(message, servletContext);
+ public void info(String message) {
+ delegate.info(message);
}
- public void warn(String message, ServletContext servletContext) {
- delegate.warn(message, servletContext);
+ public void warn(String message) {
+ delegate.warn(message);
}
- public void error(String message, ServletContext servletContext) {
- delegate.error(message, servletContext);
+ public void error(String message) {
+ delegate.error(message);
}
- public void error(String message, Throwable throwable, ServletContext servletContext) {
- delegate.error(message, throwable, servletContext);
+ public void error(String message, Throwable throwable) {
+ delegate.error(message, throwable);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
index ebefb0c9dfb..03e59fc0d16 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
@@ -25,12 +25,12 @@
*/
public interface RpcLoggerDelegate {
- void info(String message, ServletContext servletContext);
+ void info(String message);
- void warn(String message, ServletContext servletContext);
+ void warn(String message);
- void error(String message, ServletContext servletContext);
+ void error(String message);
- void error(String message, Throwable throwable, ServletContext servletContext);
+ void error(String message, Throwable throwable);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
index bd0f67e4d10..5674605650c 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -20,39 +20,71 @@
/**
* A {@link RpcLoggerProvider} that delegates to the servlet context's logging. Used as the fallback
* if no other logger provider is found.
+ *
+ * Servlet context logging does not support separate named loggers, so this reuses the same logger
+ * instance. This reuse allows the servlet context to be easily set after the provider is
+ * initialized, and normally the provider will be initialized before the servlet context is even
+ * available.
*/
public class ServletContextLoggerProvider implements RpcLoggerProvider {
+ private final ServletContextLogger logger = new ServletContextLogger();
+
public ServletContextLoggerProvider() {
}
+ void setServletContext(ServletContext servletContext) {
+ if (servletContext != null) {
+ logger.servletContext = servletContext;
+ }
+ }
+
@Override
- public RpcLoggerDelegate createLogger(String name) {
- return new ServletContextLogger();
+ public RpcLoggerDelegate createLogger(String ignored) {
+ return logger;
}
private static final class ServletContextLogger implements RpcLoggerDelegate {
- ServletContextLogger() { }
+ private volatile ServletContext servletContext;
+
+ private ServletContextLogger() { }
@Override
- public void info(String message, ServletContext servletContext) {
- servletContext.log(message);
+ public void info(String message) {
+ if (servletContext != null) {
+ servletContext.log("INFO: " + message);
+ } else {
+ System.out.println("INFO: " + message);
+ }
}
@Override
- public void warn(String message, ServletContext servletContext) {
- servletContext.log("WARNING: " + message);
+ public void warn(String message) {
+ if (servletContext != null) {
+ servletContext.log("WARNING: " + message);
+ } else {
+ System.out.println("WARNING: " + message);
+ }
}
@Override
- public void error(String message, ServletContext servletContext) {
- servletContext.log("ERROR: " + message);
+ public void error(String message) {
+ if (servletContext != null) {
+ servletContext.log("ERROR: " + message);
+ } else {
+ System.err.println("ERROR: " + message);
+ }
}
@Override
- public void error(String message, Throwable throwable, ServletContext servletContext) {
- servletContext.log("ERROR: " + message, throwable);
+ public void error(String message, Throwable throwable) {
+ if (servletContext != null) {
+ servletContext.log("ERROR: " + message, throwable);
+ } else {
+ System.err.println("ERROR: " + message);
+ throwable.printStackTrace();
+ }
}
}
From 0d0936886ed977e0d3c0edf16919657d2fff9274 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Fri, 13 Feb 2026 17:04:28 -0600
Subject: [PATCH 16/23] Improve javadoc comment
---
.../google/gwt/user/server/rpc/SerializationPolicyClient.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
index dea789c91f7..e228d5598a2 100644
--- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
+++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyClient.java
@@ -39,7 +39,8 @@ class SerializationPolicyClient {
private final int readTimeout;
/**
- * Creates a client with the given configuration.
+ * Creates a client to load serialization policies from a dev mode server, with the given
+ * timeouts.
* @param connectTimeoutMs see {@link URLConnection#setConnectTimeout}
* @param readTimeoutMs see {@link URLConnection#setReadTimeout}
*/
From 2091f1ca5ffc11880789c3110fd00ba084cfffb9 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Fri, 13 Feb 2026 17:12:45 -0600
Subject: [PATCH 17/23] Update javadoc
---
.../gwt/user/server/rpc/logging/RpcLoggerDelegate.java | 5 -----
1 file changed, 5 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
index 03e59fc0d16..db42d992575 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
@@ -15,13 +15,8 @@
*/
package com.google.gwt.user.server.rpc.logging;
-import javax.servlet.ServletContext;
-
/**
* A very simplified interface for logging RPC events.
- *
- * A {@link ServletContext} is passed for compatibility with the fallback
- * {@link ServletContextLoggerProvider}. It may be ignored for any other implementation.
*/
public interface RpcLoggerDelegate {
From fd4e2667c4df3b1ba1005e220646266327dacbee Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Wed, 25 Feb 2026 15:02:14 -0600
Subject: [PATCH 18/23] Merge initialize and get provider methods in
RpcLogManager
---
.../server/rpc/logging/RpcLogManager.java | 33 +++++--------------
1 file changed, 9 insertions(+), 24 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index d1c05d2fd81..5b432967ce7 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -39,7 +39,6 @@ public class RpcLogManager {
public static final String PROVIDER_PARAMETER_KEY = "gwt.rpc.logging";
private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
private static volatile RpcLoggerProvider loggerProvider;
- private static final Object initLock = new Object();
/**
* Creates or retrieves a logger for the fully qualified name of the given class.
@@ -57,8 +56,8 @@ public static RpcLogger getLogger(Class> clazz) {
* @param servletContext the servlet context to use
*/
public static void setServletContext(ServletContext servletContext) {
- if (loggerProvider instanceof ServletContextLoggerProvider) {
- ((ServletContextLoggerProvider) loggerProvider).setServletContext(servletContext);
+ if (getLoggerProvider() instanceof ServletContextLoggerProvider) {
+ ((ServletContextLoggerProvider) getLoggerProvider()).setServletContext(servletContext);
}
}
@@ -71,29 +70,15 @@ public static void setServletContext(ServletContext servletContext) {
*/
private static RpcLoggerProvider getLoggerProvider() {
if (loggerProvider == null) {
- initialize();
- }
- return loggerProvider;
- }
-
- /**
- * Initializes the {@link RpcLoggerProvider}, attempting to use a name from a system property.
- *
- * If no name is supplied, it will use the first loaded provider for which
- * {@link RpcLoggerProvider#isDefault()} returns true.
- *
- * If no such provider is found, it will fall back to {@link ServletContextLoggerProvider}.
- *
- * If the provider is already initialized, no action is taken.
- */
- private static void initialize() {
- String providerName = System.getProperty(PROVIDER_PARAMETER_KEY);
- synchronized (initLock) {
- if (loggerProvider == null) {
- RpcLoggerProvider fallback = new ServletContextLoggerProvider();
- loggerProvider = loadProvider(providerName, fallback);
+ synchronized (RpcLogManager.class) {
+ if (loggerProvider == null) {
+ RpcLoggerProvider fallback = new ServletContextLoggerProvider();
+ String providerName = System.getProperty(PROVIDER_PARAMETER_KEY);
+ loggerProvider = loadProvider(providerName, fallback);
+ }
}
}
+ return loggerProvider;
}
/**
From ece5acf17d130f5213d575b4f4c7113c49642f65 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Wed, 25 Feb 2026 15:02:40 -0600
Subject: [PATCH 19/23] Revert changes to RemoteServiceServletTest
---
.../google/gwt/user/server/rpc/RemoteServiceServletTest.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
index 4e015044fa1..0cd88ac5454 100644
--- a/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RemoteServiceServletTest.java
@@ -79,7 +79,7 @@ public MockServletConfig(ServletContext context) {
}
public String getInitParameter(String arg0) {
- return null;
+ throw new UnsupportedOperationException();
}
public Enumeration getInitParameterNames() {
@@ -118,7 +118,7 @@ public String getContextPath() {
}
public String getInitParameter(String arg0) {
- return null;
+ throw new UnsupportedOperationException();
}
public Enumeration getInitParameterNames() {
@@ -353,6 +353,7 @@ public void testDoGetSerializationPolicy_ModuleInSeparateServlet()
mockRequest.contextPath = "/foo";
SerializationPolicy serializationPolicy = rss.doGetSerializationPolicy(
mockRequest, "http://www.google.com/MyModule", "");
+ assertNotNull(mockContext.messageLogged);
assertNull(serializationPolicy);
}
From cecfc97bd0dc60af52187ff3918c26c41cc1ca09 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Mon, 27 Apr 2026 08:44:24 -0500
Subject: [PATCH 20/23] Remove unneeded layer of indirection by combining
RpcLogger and RpcLoggerDelegate
---
.../server/rpc/logging/JulLoggerProvider.java | 4 +--
.../server/rpc/logging/RpcLogManager.java | 2 +-
.../user/server/rpc/logging/RpcLogger.java | 26 ++++------------
.../server/rpc/logging/RpcLoggerDelegate.java | 31 -------------------
.../server/rpc/logging/RpcLoggerProvider.java | 2 +-
.../logging/ServletContextLoggerProvider.java | 4 +--
6 files changed, 12 insertions(+), 57 deletions(-)
delete mode 100644 user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
index a639fc215e9..b82d2654d36 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
@@ -27,11 +27,11 @@ public JulLoggerProvider() {
}
@Override
- public RpcLoggerDelegate createLogger(String name) {
+ public RpcLogger createLogger(String name) {
return new JulLogger(Logger.getLogger(name));
}
- private static final class JulLogger implements RpcLoggerDelegate {
+ private static final class JulLogger implements RpcLogger {
private final Logger logger;
JulLogger(Logger logger) {
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 5b432967ce7..7dece5e048d 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -47,7 +47,7 @@ public class RpcLogManager {
*/
public static RpcLogger getLogger(Class> clazz) {
return loggers.computeIfAbsent(clazz.getName(),
- name -> new RpcLogger(name, getLoggerProvider()));
+ name -> getLoggerProvider().createLogger(name));
}
/**
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
index 52d28a3e6d6..a69284573d5 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
@@ -16,30 +16,16 @@
package com.google.gwt.user.server.rpc.logging;
/**
- * A wrapper that creates a {@link RpcLoggerDelegate} for a given name.
+ * A very simplified interface for logging RPC events.
*/
-public final class RpcLogger {
+public interface RpcLogger {
- private final RpcLoggerDelegate delegate;
+ void info(String message);
- RpcLogger(String name, RpcLoggerProvider provider) {
- delegate = provider.createLogger(name);
- }
+ void warn(String message);
- public void info(String message) {
- delegate.info(message);
- }
+ void error(String message);
- public void warn(String message) {
- delegate.warn(message);
- }
-
- public void error(String message) {
- delegate.error(message);
- }
-
- public void error(String message, Throwable throwable) {
- delegate.error(message, throwable);
- }
+ void error(String message, Throwable throwable);
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
deleted file mode 100644
index db42d992575..00000000000
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerDelegate.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2025 GWT Project Authors
- *
- * Licensed 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 com.google.gwt.user.server.rpc.logging;
-
-/**
- * A very simplified interface for logging RPC events.
- */
-public interface RpcLoggerDelegate {
-
- void info(String message);
-
- void warn(String message);
-
- void error(String message);
-
- void error(String message, Throwable throwable);
-
-}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
index b07dcb6e5fd..6f100555f81 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
@@ -20,7 +20,7 @@
*/
public interface RpcLoggerProvider {
- RpcLoggerDelegate createLogger(String name);
+ RpcLogger createLogger(String name);
/**
* If no named provider is found, the first provider that returns true will be used.
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
index 5674605650c..03134f3574d 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -40,11 +40,11 @@ void setServletContext(ServletContext servletContext) {
}
@Override
- public RpcLoggerDelegate createLogger(String ignored) {
+ public RpcLogger createLogger(String ignored) {
return logger;
}
- private static final class ServletContextLogger implements RpcLoggerDelegate {
+ private static final class ServletContextLogger implements RpcLogger {
private volatile ServletContext servletContext;
From a0010c1c72c7665d963b77630836ce6b2981e7bb Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Mon, 27 Apr 2026 15:53:50 -0500
Subject: [PATCH 21/23] Remove unneeded locking from logger provider
initialization
---
.../server/rpc/logging/RpcLogManager.java | 68 ++++++++-----------
1 file changed, 27 insertions(+), 41 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 7dece5e048d..6669d48969a 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -27,27 +27,29 @@
* The provider is initialized the first time a logger is requested. The provider is chosen in this
* order:
*
- * - The first provider with a fully-qualified class name that matches the system property
- *
gwt.rpc.logging
- * - The first provider for which {@link RpcLoggerProvider#isDefault()} returns
- *
true
- * - The {@link ServletContextLoggerProvider}
+ * - The first provider with a fully-qualified class name that matches the system property with
+ * the key {@link #PROVIDER_PROPERTY_KEY} (
gwt.rpc.logging)
+ * - The first provider for which {@link RpcLoggerProvider#isDefault()} returns
+ *
true
+ * - The {@link ServletContextLoggerProvider}
*
*/
public class RpcLogManager {
- public static final String PROVIDER_PARAMETER_KEY = "gwt.rpc.logging";
+ /**
+ * System property key for selecting an {@link RpcLoggerProvider} by fully-qualified class name.
+ */
+ public static final String PROVIDER_PROPERTY_KEY = "gwt.rpc.logging";
private static final ConcurrentHashMap loggers = new ConcurrentHashMap<>();
- private static volatile RpcLoggerProvider loggerProvider;
+ private static final RpcLoggerProvider loggerProvider = loadProvider();
/**
- * Creates or retrieves a logger for the fully qualified name of the given class.
+ * Creates or retrieves a logger for the fully-qualified name of the given class.
* @param clazz the class for which to return a logger
* @return a logger
*/
public static RpcLogger getLogger(Class> clazz) {
- return loggers.computeIfAbsent(clazz.getName(),
- name -> getLoggerProvider().createLogger(name));
+ return loggers.computeIfAbsent(clazz.getName(), loggerProvider::createLogger);
}
/**
@@ -56,43 +58,23 @@ public static RpcLogger getLogger(Class> clazz) {
* @param servletContext the servlet context to use
*/
public static void setServletContext(ServletContext servletContext) {
- if (getLoggerProvider() instanceof ServletContextLoggerProvider) {
- ((ServletContextLoggerProvider) getLoggerProvider()).setServletContext(servletContext);
- }
- }
-
- /**
- * Retrieves the current instance of the {@link RpcLoggerProvider}.
- *
- * If the LoggerProvider is not yet initialized, initializes it in a thread-safe manner
- * and returns the resulting instance.
- * @return the current instance of the LoggerProvider
- */
- private static RpcLoggerProvider getLoggerProvider() {
- if (loggerProvider == null) {
- synchronized (RpcLogManager.class) {
- if (loggerProvider == null) {
- RpcLoggerProvider fallback = new ServletContextLoggerProvider();
- String providerName = System.getProperty(PROVIDER_PARAMETER_KEY);
- loggerProvider = loadProvider(providerName, fallback);
- }
- }
+ if (loggerProvider instanceof ServletContextLoggerProvider) {
+ ((ServletContextLoggerProvider) loggerProvider).setServletContext(servletContext);
}
- return loggerProvider;
}
/**
- * Loads available providers and chooses the first whose class matches the given name, or the
- * first for which {@link RpcLoggerProvider#isDefault()} returns true.
- * @param name the fully qualified class name of the {@link RpcLoggerProvider} to use. May be
- * null.
- * @param fallback the provider to use in case no named or default provider is found
- * @return the chosen provider
+ * Loads available providers and chooses the first whose class matches the name given with the
+ * {@link #PROVIDER_PROPERTY_KEY} system property, or, failing that, the first for which
+ * {@link RpcLoggerProvider#isDefault()} returns true. If none found, returns a
+ * {@link ServletContextLoggerProvider} as fallback.
+ * @return a logger provider
*/
- private static RpcLoggerProvider loadProvider(String name, RpcLoggerProvider fallback) {
+ private static RpcLoggerProvider loadProvider() {
+ String providerClassName = System.getProperty(PROVIDER_PROPERTY_KEY);
ServiceLoader loaderService = ServiceLoader.load(RpcLoggerProvider.class);
for (RpcLoggerProvider provider : loaderService) {
- if (provider.getClass().getName().equals(name)) {
+ if (provider.getClass().getName().equals(providerClassName)) {
return provider;
}
}
@@ -101,7 +83,11 @@ private static RpcLoggerProvider loadProvider(String name, RpcLoggerProvider fal
return provider;
}
}
- return fallback;
+ return new ServletContextLoggerProvider();
+ }
+
+ private RpcLogManager() {
+ // Not instantiable
}
}
From 219dabaeffee37680d1437ad1558fbc16ab80943 Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Mon, 27 Apr 2026 17:32:33 -0500
Subject: [PATCH 22/23] Improve documentation of RPC logging package
---
.../server/rpc/logging/JulLoggerProvider.java | 7 ++++++-
.../user/server/rpc/logging/RpcLogManager.java | 14 ++++++++------
.../gwt/user/server/rpc/logging/RpcLogger.java | 8 +++++++-
.../server/rpc/logging/RpcLoggerProvider.java | 17 +++++++++++++++--
.../logging/ServletContextLoggerProvider.java | 18 ++++++++++++++----
5 files changed, 50 insertions(+), 14 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
index b82d2654d36..7e8cc14beab 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/JulLoggerProvider.java
@@ -19,10 +19,15 @@
import java.util.logging.Logger;
/**
- * A {@link RpcLoggerProvider} that delegates to {@link java.util.logging.Logger}.
+ * An {@link RpcLoggerProvider} that delegates to {@link java.util.logging.Logger}.
+ *
+ * @see RpcLogManager
*/
public class JulLoggerProvider implements RpcLoggerProvider {
+ /**
+ * Public for {@link java.util.ServiceLoader}; not intended for direct use outside this package.
+ */
public JulLoggerProvider() {
}
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
index 6669d48969a..0b749816b3b 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogManager.java
@@ -21,11 +21,10 @@
import javax.servlet.ServletContext;
/**
- * Handles creation of {@link RpcLogger}s and the initialization of the {@link RpcLoggerProvider},
- * using {@link ServiceLoader} to discover available providers.
- *
- * The provider is initialized the first time a logger is requested. The provider is chosen in this
- * order:
+ * Entry point for obtaining {@link RpcLogger}s. Initializes the {@link RpcLoggerProvider} on class
+ * load using {@link ServiceLoader} to discover available implementations.
+ *
+ * The provider is chosen in this order:
*
* - The first provider with a fully-qualified class name that matches the system property with
* the key {@link #PROVIDER_PROPERTY_KEY} (
gwt.rpc.logging)
@@ -45,6 +44,7 @@ public class RpcLogManager {
/**
* Creates or retrieves a logger for the fully-qualified name of the given class.
+ *
* @param clazz the class for which to return a logger
* @return a logger
*/
@@ -55,6 +55,7 @@ public static RpcLogger getLogger(Class> clazz) {
/**
* Sets the servlet context of the {@link ServletContextLoggerProvider} to use for logging. Has no
* effect if the provider is not a {@link ServletContextLoggerProvider}.
+ *
* @param servletContext the servlet context to use
*/
public static void setServletContext(ServletContext servletContext) {
@@ -67,7 +68,8 @@ public static void setServletContext(ServletContext servletContext) {
* Loads available providers and chooses the first whose class matches the name given with the
* {@link #PROVIDER_PROPERTY_KEY} system property, or, failing that, the first for which
* {@link RpcLoggerProvider#isDefault()} returns true. If none found, returns a
- * {@link ServletContextLoggerProvider} as fallback.
+ * {@link ServletContextLoggerProvider} as a fallback.
+ *
* @return a logger provider
*/
private static RpcLoggerProvider loadProvider() {
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
index a69284573d5..7958d8842d8 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLogger.java
@@ -16,7 +16,13 @@
package com.google.gwt.user.server.rpc.logging;
/**
- * A very simplified interface for logging RPC events.
+ * A minimal logging facade used by GWT's server-side RPC package.
+ *
+ * Instances can be obtained from {@link RpcLogManager#getLogger(Class)}. The logging system that
+ * this delegates to is selected by {@link RpcLogManager} at class-initialization.
+ *
+ * @see RpcLogManager
+ * @see RpcLoggerProvider
*/
public interface RpcLogger {
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
index 6f100555f81..a7a55ea04aa 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
@@ -16,14 +16,27 @@
package com.google.gwt.user.server.rpc.logging;
/**
- * A factory for {@link RpcLogger} instances.
+ * Service provider interface for obtaining {@link RpcLogger} instances.
+ *
+ * A concrete instance can be obtained with {@link RpcLogManager#getLogger(Class)}, which discovers
+ * implementations with a service loader
+ *
+ * @see RpcLogManager
*/
public interface RpcLoggerProvider {
+ /**
+ * Creates or retrieves a logger with the given name.
+ *
+ * @param name the name of the logger to create or retrieve
+ * @return the created or retrieved logger
+ */
RpcLogger createLogger(String name);
/**
- * If no named provider is found, the first provider that returns true will be used.
+ * Indicates whether this provider should be used as the default in the absence of a provider
+ * explicitly selected with the {@link RpcLogManager#PROVIDER_PROPERTY_KEY} system property.
+ *
* @return true if this provider should be used when no named provider is found
*/
default boolean isDefault() {
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
index 03134f3574d..c26657482ba 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -18,21 +18,31 @@
import javax.servlet.ServletContext;
/**
- * A {@link RpcLoggerProvider} that delegates to the servlet context's logging. Used as the fallback
- * if no other logger provider is found.
- *
+ * An {@link RpcLoggerProvider} that delegates to the servlet context's logging. Used as the
+ * fallback if no other logger provider is found.
+ *
* Servlet context logging does not support separate named loggers, so this reuses the same logger
* instance. This reuse allows the servlet context to be easily set after the provider is
* initialized, and normally the provider will be initialized before the servlet context is even
- * available.
+ * available. Logs are written to {@link System#out} and {@link System#err} until the servlet
+ * context is set.
+ *
+ * @see RpcLogManager
*/
public class ServletContextLoggerProvider implements RpcLoggerProvider {
private final ServletContextLogger logger = new ServletContextLogger();
+ /**
+ * Public for {@link java.util.ServiceLoader}; not intended for direct use outside this package.
+ */
public ServletContextLoggerProvider() {
}
+ /**
+ * Sets the {@link ServletContext} to which log messages will be written.
+ * @param servletContext the servlet context to use
+ */
void setServletContext(ServletContext servletContext) {
if (servletContext != null) {
logger.servletContext = servletContext;
From 19969ef38267006063690c6c1db5a5f84a5fff5e Mon Sep 17 00:00:00 2001
From: gkeiser
Date: Tue, 28 Apr 2026 08:40:53 -0500
Subject: [PATCH 23/23] Correct an inaccurate javadoc
---
.../google/gwt/user/server/rpc/logging/RpcLoggerProvider.java | 4 ++--
.../user/server/rpc/logging/ServletContextLoggerProvider.java | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
index a7a55ea04aa..51c9de5ac0e 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/RpcLoggerProvider.java
@@ -18,8 +18,8 @@
/**
* Service provider interface for obtaining {@link RpcLogger} instances.
*
- * A concrete instance can be obtained with {@link RpcLogManager#getLogger(Class)}, which discovers
- * implementations with a service loader
+ * This is not intended to be used directly, but through {@link RpcLogManager#getLogger(Class)},
+ * which discovers implementations with a service loader
*
* @see RpcLogManager
*/
diff --git a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
index c26657482ba..8fee64ddce0 100644
--- a/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
+++ b/user/src/com/google/gwt/user/server/rpc/logging/ServletContextLoggerProvider.java
@@ -41,6 +41,7 @@ public ServletContextLoggerProvider() {
/**
* Sets the {@link ServletContext} to which log messages will be written.
+ *
* @param servletContext the servlet context to use
*/
void setServletContext(ServletContext servletContext) {