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/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..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;
@@ -37,6 +40,8 @@
*/
public class RPCServletUtils {
+ private static final Logger logger = LogManager.getLogger(RPCServletUtils.class);
+
public static final String CHARSET_UTF8_NAME = "UTF-8";
/**
@@ -319,12 +324,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 +344,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 +375,7 @@ public static void writeResponse(ServletContext servletContext,
}
if (caught != null) {
- servletContext.log("Unable to compress response", caught);
+ logger.error("Unable to compress response", caught);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
@@ -378,18 +390,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.error("Exception while dispatching incoming RPC call", failure);
// Send GENERIC_FAILURE_MSG with 500 status.
//
@@ -403,7 +420,7 @@ public static void writeResponseForUnexpectedFailure(
response.getWriter().write(GENERIC_FAILURE_MSG);
}
} catch (IOException ex) {
- servletContext.log(
+ 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 1bd73e142ef..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;
@@ -45,6 +47,8 @@
public class RemoteServiceServlet extends AbstractRemoteServiceServlet
implements SerializationPolicyProvider {
+ private static final Logger logger = LogManager.getLogger(RemoteServiceServlet.class);
+
/**
* 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.error("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.error(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.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 {
- servlet.log("ERROR: Service deserializes enhanced JPA/JDO classes, which is " +
+ 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 " +
@@ -111,17 +117,19 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
}
}
} catch (ParseException e) {
- servlet.log("ERROR: Failed to parse the policy file '"
+ logger.error(
+ "Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
- servlet.log("ERROR: Could not read the policy file '"
+ logger.error(
+ "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.error(message);
}
} finally {
if (is != null) {
@@ -182,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.
*
@@ -260,8 +281,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.warn(
+ "Failed to get the SerializationPolicy '"
+ strongName
+ "' for module '"
+ moduleBaseURL
@@ -311,7 +332,7 @@ public String processCall(String payload) throws SerializationException {
try {
rpcRequest = RPC.decodeRequest(payload, delegate.getClass(), this);
} catch (IncompatibleRemoteServiceException ex) {
- log(
+ logger.error(
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailedRequest(null, ex);
@@ -350,12 +371,13 @@ public String processCall(RPCRequest rpcRequest) throws SerializationException {
rpcRequest.getParameters(), rpcRequest.getSerializationPolicy(),
rpcRequest.getFlags());
} catch (IncompatibleRemoteServiceException ex) {
- log(
+ logger.error(
"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.error(
+ "An RpcTokenException was thrown while processing this call.",
tokenException);
return RPC.encodeResponseForFailedRequest(rpcRequest, tokenException);
}
@@ -364,9 +386,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
@@ -465,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) {
- RemoteServiceServlet.this.log(message);
- }
-
- @Override
- public void logError(String message, Throwable throwable) {
- RemoteServiceServlet.this.log(message, throwable);
- }
- };
- return CODE_SERVER_CLIENT.loadPolicy(url, adapter);
+ return CODE_SERVER_CLIENT.loadPolicy(url);
}
/**
@@ -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/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 0cd88ac5454..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() {
@@ -118,7 +118,7 @@ public String getContextPath() {
}
public String getInitParameter(String arg0) {
- throw new UnsupportedOperationException();
+ return null;
}
public Enumeration getInitParameterNames() {
@@ -353,7 +353,6 @@ public void testDoGetSerializationPolicy_ModuleInSeparateServlet()
mockRequest.contextPath = "/foo";
SerializationPolicy serializationPolicy = rss.doGetSerializationPolicy(
mockRequest, "http://www.google.com/MyModule", "");
- assertNotNull(mockContext.messageLogged);
assertNull(serializationPolicy);
}