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); }