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: + *
    + *
  1. The first provider with a class name that matches a system property + * gwt.rpc.logging
  2. + *
  3. The first provider for which {@link RpcLoggerProvider#isDefault()} returns + * true
  4. + *
  5. The {@link ServletContextLoggerProvider}
  6. + *
+ * 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: *
    - *
  1. The first provider with a class name that matches a system property + *
  2. The first provider with a fully-qualified class name that matches the system property * gwt.rpc.logging
  3. *
  4. The first provider for which {@link RpcLoggerProvider#isDefault()} returns * true
  5. @@ -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 *
  6. The {@link ServletContextLoggerProvider}
  7. *
- * 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: *
    - *
  1. The first provider with a fully-qualified class name that matches the system property - * gwt.rpc.logging
  2. - *
  3. The first provider for which {@link RpcLoggerProvider#isDefault()} returns - * true
  4. - *
  5. The {@link ServletContextLoggerProvider}
  6. + *
  7. The first provider with a fully-qualified class name that matches the system property with + * the key {@link #PROVIDER_PROPERTY_KEY} (gwt.rpc.logging)
  8. + *
  9. The first provider for which {@link RpcLoggerProvider#isDefault()} returns + * true
  10. + *
  11. The {@link ServletContextLoggerProvider}
  12. *
*/ 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: *

    *
  1. The first provider with a fully-qualified class name that matches the system property with * the key {@link #PROVIDER_PROPERTY_KEY} (gwt.rpc.logging)
  2. @@ -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) {