/**
* Copyright 2014 55 Minutes (http://www.55minutes.com)
*
* 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 fiftyfive.wicket.util;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import fiftyfive.wicket.FoundationApplication;
import org.apache.wicket.Application;
import org.apache.wicket.Session;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.protocol.http.IRequestLogger;
import org.apache.wicket.protocol.http.IRequestLogger.ISessionLogInfo;
import org.apache.wicket.protocol.http.IRequestLogger.SessionData;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.IRequestMapper;
import org.apache.wicket.request.component.IRequestableComponent;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.IComponentRequestHandler;
import org.apache.wicket.request.handler.IPageClassRequestHandler;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.util.lang.Classes;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.util.time.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility methods for logging detailed information about Wicket's internal
* state for troubleshooting purposes. Most of these methods work only when
* called within a Wicket thread.
*
* @since 2.0
*/
public class LoggingUtils
{
private static final Logger LOGGER = LoggerFactory.getLogger(
LoggingUtils.class
);
/** Human-readable descriptions of Wicket's request listener types. */
private static final Map<String,String> LISTENER_DESCRIPTIONS;
static
{
LISTENER_DESCRIPTIONS = new HashMap<String,String>();
LISTENER_DESCRIPTIONS.put(
"IFormSubmitListener",
"Processing Form Submission");
LISTENER_DESCRIPTIONS.put(
"ILinkListener",
"Processing Link Click"
);
LISTENER_DESCRIPTIONS.put(
"IOnChangeListener",
"Processing Form Element OnChange Event"
);
}
/**
* Writes to the logger a best-guess at the most concise description of
* the exception by first unwrapping it (see {@link #unwrap upwrap}), then
* writes a large amount of information regarding the current Wicket state
* (see {@link #dumpWicketState dumpWicketState}), and finally writes the
* full stack traces of the entire exception chain.
* <p>
* Example logger output:
* <pre class="example">
* ParseException: Unparseable date: "1xxx07"
*
* Request:
* URL = wicket/bookmarkable/fiftyfive.wicket.examples.formtest.FormTestPage?0&initialMonth=10.2007&startDate=1xxx07&endDate=11.10.2007
* Handler = RenderPageRequestHandler
* Component = FormTestPage
* Duration = 485 milliseconds
* Session:
* ID = 1sxy938y7qoqq942z4pnuqdqt
* Info = TODO: Your session info goes here
* Size = 716 bytes
* Duration = 310 milliseconds
* Application:
* Active Sessions = 1 (1 peak)
* Memory Usage = 25M used, 40M free, 533M max
* IP Address = 172.16.1.14
* Uptime = 10.9 seconds
* Headers:
* Host = localhost:8080
* User-Agent = Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5
* Accept = application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
* Cache-Control = max-age=0
* Accept-Language = en-us
* Accept-Encoding = gzip, deflate
* Cookie = JSESSIONID=1217fv4qjpnbnv1ewzzmonevh
* Connection = keep-alive
*
* Message: Error calling method: public java.util.Date fiftyfive.wicket.examples.formtest.FormTestPage$1.getStartDate() on object: [ [Component id = calendar-control]]
*
* Root cause:
*
* java.text.ParseException: Unparseable date: "1xxx07"
* at java.text.DateFormat.parse(DateFormat.java:337)
* at fiftyfive.wicket.examples.formtest.FormTestPage$1.getStartDate(FormTestPage.java:82)
* at java.lang.reflect.Method.invoke(Method.java:597)
* at org.apache.wicket.util.lang.PropertyResolver$MethodGetAndSet.getValue(PropertyResolver.java:1112)
* at org.apache.wicket.util.lang.PropertyResolver$ObjectAndGetSetter.getValue(PropertyResolver.java:637)
* at org.apache.wicket.util.lang.PropertyResolver.getValue(PropertyResolver.java:96)
* at org.apache.wicket.model.AbstractPropertyModel.getObject(AbstractPropertyModel.java:122)
* at fiftyfive.wicket.datetime.RestrictedDatePicker.configure(RestrictedDatePicker.java:168)
* at org.apache.wicket.extensions.yui.calendar.DatePicker.renderHead(DatePicker.java:260)
* at org.apache.wicket.Component.renderHead(Component.java:2627)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy$1.component(ParentFirstHeaderRenderStrategy.java:70)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy$1.component(ParentFirstHeaderRenderStrategy.java:66)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:143)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:117)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:193)
* at org.apache.wicket.MarkupContainer.visitChildren(MarkupContainer.java:941)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy.renderChildHeaders(ParentFirstHeaderRenderStrategy.java:64)
* at org.apache.wicket.markup.renderStrategy.AbstractHeaderRenderStrategy.renderHeader(AbstractHeaderRenderStrategy.java:125)
* at org.apache.wicket.markup.html.internal.HtmlHeaderContainer.onComponentTagBody(HtmlHeaderContainer.java:140)
* at org.apache.wicket.Component.renderComponent(Component.java:2518)
* at org.apache.wicket.MarkupContainer.onRender(MarkupContainer.java:1527)
* at org.apache.wicket.Component.render_(Component.java:2380)
* at org.apache.wicket.Component.render(Component.java:2307)
* at org.apache.wicket.MarkupContainer.renderNext(MarkupContainer.java:1466)
* at org.apache.wicket.MarkupContainer.renderAll(MarkupContainer.java:1589)
* at org.apache.wicket.Page.onRender(Page.java:1139)
* at org.apache.wicket.Component.render_(Component.java:2380)
* at org.apache.wicket.Component.render(Component.java:2307)
* at org.apache.wicket.Page.renderPage(Page.java:1289)
* at org.apache.wicket.request.handler.render.WebPageRenderer.renderPage(WebPageRenderer.java:131)
* at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:199)
* at org.apache.wicket.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:149)
* at org.apache.wicket.request.RequestHandlerStack.executeRequestHandler(RequestHandlerStack.java:84)
* at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:206)
* at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:248)
* at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:131)
* at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:184)
* at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
* at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:83)
* at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
* at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
* at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
* at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
* at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
* at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
* at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:440)
* at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
* at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
* at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
* at org.mortbay.jetty.Server.handle(Server.java:326)
* at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
* at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:926)
* at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
* at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
* at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
* at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
* at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
*
*
* Complete stack:
*
* org.apache.wicket.WicketRuntimeException: Error calling method: public java.util.Date fiftyfive.wicket.examples.formtest.FormTestPage$1.getStartDate() on object: [ [Component id = calendar-control]]
* at org.apache.wicket.util.lang.PropertyResolver$MethodGetAndSet.getValue(PropertyResolver.java:1116)
* at org.apache.wicket.util.lang.PropertyResolver$ObjectAndGetSetter.getValue(PropertyResolver.java:637)
* at org.apache.wicket.util.lang.PropertyResolver.getValue(PropertyResolver.java:96)
* at org.apache.wicket.model.AbstractPropertyModel.getObject(AbstractPropertyModel.java:122)
* at fiftyfive.wicket.datetime.RestrictedDatePicker.configure(RestrictedDatePicker.java:168)
* at org.apache.wicket.extensions.yui.calendar.DatePicker.renderHead(DatePicker.java:260)
* at org.apache.wicket.Component.renderHead(Component.java:2627)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy$1.component(ParentFirstHeaderRenderStrategy.java:70)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy$1.component(ParentFirstHeaderRenderStrategy.java:66)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:143)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:117)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:193)
* at org.apache.wicket.MarkupContainer.visitChildren(MarkupContainer.java:941)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy.renderChildHeaders(ParentFirstHeaderRenderStrategy.java:64)
* at org.apache.wicket.markup.renderStrategy.AbstractHeaderRenderStrategy.renderHeader(AbstractHeaderRenderStrategy.java:125)
* at org.apache.wicket.markup.html.internal.HtmlHeaderContainer.onComponentTagBody(HtmlHeaderContainer.java:140)
* at org.apache.wicket.Component.renderComponent(Component.java:2518)
* at org.apache.wicket.MarkupContainer.onRender(MarkupContainer.java:1527)
* at org.apache.wicket.Component.render_(Component.java:2380)
* at org.apache.wicket.Component.render(Component.java:2307)
* at org.apache.wicket.MarkupContainer.renderNext(MarkupContainer.java:1466)
* at org.apache.wicket.MarkupContainer.renderAll(MarkupContainer.java:1589)
* at org.apache.wicket.Page.onRender(Page.java:1139)
* at org.apache.wicket.Component.render_(Component.java:2380)
* at org.apache.wicket.Component.render(Component.java:2307)
* at org.apache.wicket.Page.renderPage(Page.java:1289)
* at org.apache.wicket.request.handler.render.WebPageRenderer.renderPage(WebPageRenderer.java:131)
* at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:199)
* at org.apache.wicket.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:149)
* at org.apache.wicket.request.RequestHandlerStack.executeRequestHandler(RequestHandlerStack.java:84)
* at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:206)
* at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:248)
* at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:131)
*
* java.lang.RuntimeException: java.text.ParseException: Unparseable date: "1xxx07"
* at fiftyfive.wicket.examples.formtest.FormTestPage$1.getStartDate(FormTestPage.java:87)
* at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
* at java.lang.reflect.Method.invoke(Method.java:597)
* at org.apache.wicket.util.lang.PropertyResolver$MethodGetAndSet.getValue(PropertyResolver.java:1112)
* at org.apache.wicket.util.lang.PropertyResolver$ObjectAndGetSetter.getValue(PropertyResolver.java:637)
* at org.apache.wicket.util.lang.PropertyResolver.getValue(PropertyResolver.java:96)
* at org.apache.wicket.model.AbstractPropertyModel.getObject(AbstractPropertyModel.java:122)
* at fiftyfive.wicket.datetime.RestrictedDatePicker.configure(RestrictedDatePicker.java:168)
* at org.apache.wicket.extensions.yui.calendar.DatePicker.renderHead(DatePicker.java:260)
* at org.apache.wicket.Component.renderHead(Component.java:2627)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy$1.component(ParentFirstHeaderRenderStrategy.java:70)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy$1.component(ParentFirstHeaderRenderStrategy.java:66)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:143)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:161)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:117)
* at org.apache.wicket.util.visit.Visits.visitChildren(Visits.java:193)
* at org.apache.wicket.MarkupContainer.visitChildren(MarkupContainer.java:941)
* at org.apache.wicket.markup.renderStrategy.ParentFirstHeaderRenderStrategy.renderChildHeaders(ParentFirstHeaderRenderStrategy.java:64)
* at org.apache.wicket.markup.renderStrategy.AbstractHeaderRenderStrategy.renderHeader(AbstractHeaderRenderStrategy.java:125)
* at org.apache.wicket.markup.html.internal.HtmlHeaderContainer.onComponentTagBody(HtmlHeaderContainer.java:140)
* at org.apache.wicket.Component.renderComponent(Component.java:2518)
* at org.apache.wicket.MarkupContainer.onRender(MarkupContainer.java:1527)
* at org.apache.wicket.Component.render_(Component.java:2380)
* at org.apache.wicket.Component.render(Component.java:2307)
* at org.apache.wicket.MarkupContainer.renderNext(MarkupContainer.java:1466)
* at org.apache.wicket.MarkupContainer.renderAll(MarkupContainer.java:1589)
* at org.apache.wicket.Page.onRender(Page.java:1139)
* at org.apache.wicket.Component.render_(Component.java:2380)
* at org.apache.wicket.Component.render(Component.java:2307)
* at org.apache.wicket.Page.renderPage(Page.java:1289)
* at org.apache.wicket.request.handler.render.WebPageRenderer.renderPage(WebPageRenderer.java:131)
* at org.apache.wicket.request.handler.render.WebPageRenderer.respond(WebPageRenderer.java:199)
* at org.apache.wicket.request.handler.RenderPageRequestHandler.respond(RenderPageRequestHandler.java:149)
* at org.apache.wicket.request.RequestHandlerStack.executeRequestHandler(RequestHandlerStack.java:84)
* at org.apache.wicket.request.cycle.RequestCycle.processRequest(RequestCycle.java:206)
* at org.apache.wicket.request.cycle.RequestCycle.processRequestAndDetach(RequestCycle.java:248)
* at org.apache.wicket.protocol.http.WicketFilter.processRequest(WicketFilter.java:131)</pre>
*/
public static void logException(Logger logger, Exception e)
{
Args.notNull(logger, "logger");
Args.notNull(e, "e");
try
{
Throwable unwrapped = unwrap(e);
logger.error(String.format(
"%s: %s%n%n%s%n%n%s",
Classes.simpleName(unwrapped.getClass()),
unwrapped.getMessage(),
dumpWicketState(),
Strings.toString(e)
));
}
catch(Exception loggingEx)
{
// We should never arrive here, because it means that something
// went terribly wrong in our logging code. Since our code failed,
// fall back to simple logging so that Wicket error handling
// can continue uninterrupted.
LOGGER.error("Unexpected exception during logging", loggingEx);
logger.error("RuntimeException", e);
}
}
/**
* Attempts to find the most meaningful exception in a runtime exception
* chain by stripping away the exceptions commonly used as "wrappers",
* namely: RuntimeException, WicketRuntimeException,
* InvocationTargetException and ExecutionException. These four exception
* types are usually language cruft that don't add much desciptive value.
* <p>
* For example, if the exception chain is:
* <pre class="example">
* WicketRuntimeException
* -> InvocationTargetException
* -> MyBusinessException
* -> SQLException</pre>
* <p>
* Then the unwrapped exception would be {@code MyBusinessException}.
*/
public static Throwable unwrap(Throwable e)
{
Args.notNull(e, "e");
Throwable unwrapped = e;
while(true)
{
Throwable cause = null;
if(unwrapped instanceof WicketRuntimeException ||
unwrapped instanceof InvocationTargetException ||
unwrapped instanceof ExecutionException ||
unwrapped.getClass().equals(RuntimeException.class))
{
cause = unwrapped.getCause();
}
if(null == cause || unwrapped == cause)
{
break;
}
unwrapped = cause;
}
return unwrapped;
}
/**
* Returns a multi-line string that describes the current Wicket request
* cycle in great detail. Example output:
* <pre class="example">
* Request:
* URL = wicket/bookmarkable/fiftyfive.wicket.examples.formtest.FormTestPage?0&initialMonth=10.2007&startDate=1xxx07&endDate=11.10.2007
* Handler = RenderPageRequestHandler
* Component = FormTestPage
* Duration = 485 milliseconds
* Session:
* ID = 1sxy938y7qoqq942z4pnuqdqt
* Info = TODO: Your session info goes here
* Size = 716 bytes
* Duration = 310 milliseconds
* Application:
* Active Sessions = 1 (1 peak)
* Memory Usage = 25M used, 40M free, 533M max
* IP Address = 172.16.1.14
* Uptime = 10.9 seconds
* Headers:
* Host = localhost:8080
* User-Agent = Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5
* Accept = application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
* Cache-Control = max-age=0
* Accept-Language = en-us
* Accept-Encoding = gzip, deflate
* Cookie = JSESSIONID=1217fv4qjpnbnv1ewzzmonevh
* Connection = keep-alive</pre>
* <p>
* Note that session duration and application active sessions are only
* available if Wicket's request logging facility is enabled.
*/
public static String dumpWicketState()
{
return String.format(
"Request:%n%s%nSession:%n%s%nApplication:%n%s%nHeaders:%n%s",
formatMapEntries(getRequestInfo().entrySet(), " "),
formatMapEntries(getSessionInfo().entrySet(), " "),
formatMapEntries(getApplicationInfo().entrySet(), " "),
formatMapEntries(HttpUtils.getHeaders(), " ")
);
}
/**
* Returns a Map with information associated with the following keys:
* <ul>
* <li>{@code URL}</li>
* <li>{@code Handler}</li>
* <li>{@code Component}</li>
* <li>{@code Duration}</li>
* </ul>
*/
public static Map<String,Object> getRequestInfo()
{
Map<String,Object> info = new LinkedHashMap<String,Object>();
info.put("URL", HttpUtils.getRelativeRequestUrl());
info.put("Handler", describeRequestHandler());
info.put("Component", describeRequestComponent());
info.put("Duration", getRequestDuration());
return info;
}
/**
* Returns a Map with information associated with the following keys:
* <ul>
* <li>{@code ID}</li>
* <li>{@code Info} (if session implements {@link ISessionLogInfo})</li>
* <li>{@code Size}</li>
* <li>{@code Duration} (if {@link IRequestLogger} is enabled)</li>
* </ul>
*/
public static Map<String,Object> getSessionInfo()
{
Session sess = Session.get();
Object detail = "--ISessionLogInfo not implemented--";
if(sess instanceof ISessionLogInfo)
{
detail = ((ISessionLogInfo) sess).getSessionInfo();
}
Map<String,Object> info = new LinkedHashMap<String,Object>();
if(sess != null)
{
info.put("ID", sess.getId());
info.put("Info", detail);
info.put("Size", Bytes.bytes(sess.getSizeInBytes()));
Duration dur = getSessionDuration();
if(dur != null)
{
info.put("Duration", getSessionDuration());
}
}
return info;
}
/**
* Returns a Map with information associated with the following keys:
* <ul>
* <li>{@code Active Sessions} (if {@link IRequestLogger} is enabled)</li>
* <li>{@code Memory Usage}</li>
* <li>{@code IP Address}</li>
* <li>{@code Uptime} (if app is a {@link FoundationApplication})</li>
* </ul>
*/
public static Map<String,Object> getApplicationInfo()
{
Map<String,Object> info = new LinkedHashMap<String,Object>();
String active = describeActiveSessions();
if(active != null)
{
info.put("Active Sessions", active);
}
info.put("Memory Usage", describeMemoryUsage());
info.put("IP Address", getHostIpAddress());
Application app = Application.get();
if(app instanceof FoundationApplication)
{
info.put("Uptime", ((FoundationApplication) app).getUptime());
}
return info;
}
/**
* Returns a human-readable description of the original handler of the
* current request. This is simply the name of the
* {@link IRequestHandler} class, like {@code RenderPageRequestHandler}.
*/
public static String describeRequestHandler()
{
IRequestHandler handler = guessOriginalRequestHandler();
// TODO: more descriptive than this?
return handler != null ? Classes.simpleName(handler.getClass()) : null;
}
/**
* Returns a description of component or page that is currently being
* processed by the Wicket framework. If a component is being executed, the
* description will be in the form:
* <pre class="example">
* PageClass > ComponentClass (superclass if component is anonymous) [wicket:id path]</pre>
* <p>
* Example:
* <pre class="example">
* MyPage > MyPanel$1 (Link) [path:to:link]</pre>
* <p>
* If a page is being rendered, the description will be the page class.
*/
public static String describeRequestComponent()
{
IRequestHandler handler = guessOriginalRequestHandler();
Class<? extends IRequestablePage> pageClass = null;
IRequestableComponent component = null;
if(handler instanceof IPageClassRequestHandler)
{
pageClass = ((IPageClassRequestHandler) handler).getPageClass();
}
if(handler instanceof IComponentRequestHandler)
{
component = ((IComponentRequestHandler) handler).getComponent();
}
StringBuffer desc = new StringBuffer();
if(pageClass != null)
{
desc.append(Classes.simpleName(pageClass));
}
if(component != null)
{
String classDesc = null;
if(component.getClass().isAnonymousClass())
{
classDesc = String.format(
"%s (%s)",
Classes.simpleName(component.getClass()),
Classes.simpleName(component.getClass().getSuperclass())
);
}
else
{
classDesc = Classes.simpleName(component.getClass());
}
desc.append(String.format(
" > %s [%s]",
classDesc,
component.getPageRelativePath()
));
}
return desc.length() > 0 ? desc.toString() : null;
}
/**
* Returns the amount of time the current request has taken up until
* this point.
*/
public static Duration getRequestDuration()
{
Time start = Time.millis(RequestCycle.get().getStartTime());
return Duration.elapsed(start);
}
/**
* Returns the amount of time the currently session has been active.
* Depends on Wicket's {@link IRequestLogger} being enabled. If it is not,
* returns {@code null}.
*/
public static Duration getSessionDuration()
{
Date start = null;
Session currSession = Session.get();
IRequestLogger log = Application.get().getRequestLogger();
if(log != null && currSession != null && currSession.getId() != null)
{
String sessionId = currSession.getId();
SessionData[] sessions = log.getLiveSessions();
if(sessions != null)
{
for(SessionData sess : sessions)
{
if(sessionId.equals(sess.getSessionId()))
{
start = sess.getStartDate();
break;
}
}
}
}
return nullSafeElapsed(start);
}
/**
* Returns the IP address hosting this JVM, or {@code null} if it could
* not be determined.
*/
public static String getHostIpAddress()
{
try
{
InetAddress host = InetAddress.getLocalHost();
return host.getHostAddress();
}
catch (Exception ex)
{
// ignore
}
return null;
}
/**
* Returns a string that describes the JVM's memory conditions in this
* format: {@code 36M used, 29M free, 533M max}.
*/
public static String describeMemoryUsage()
{
Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory();
return String.format(
"%sM used, %sM free, %sM max",
(runtime.totalMemory() - free) / 1000000,
free / 1000000,
runtime.maxMemory() / 1000000
);
}
/**
* Returns a string that describes the active sessions in this format:
* {@code 5 (16 peak)}. This information comes from the application's
* {@link IRequestLogger}. If the logger is not enabled, returns
* {@code null}.
*/
public static String describeActiveSessions()
{
IRequestLogger log = Application.get().getRequestLogger();
if(null == log) return null;
SessionData[] sessions = log.getLiveSessions();
if(null == sessions) return null;
return String.format(
"%d (%d peak)",
sessions.length,
log.getPeakSessions()
);
}
/**
* Returns a Duration of time elapsed since the specified date, or
* {@code null} or the date is {@code null}.
*/
private static Duration nullSafeElapsed(Date since)
{
return null == since ? null : Duration.elapsed(Time.valueOf(since));
}
/**
* Formats a Map with on entry per line plus a specified indent.
* Keys are padded and left-aligned so that they are all the same width.
*/
private static String formatMapEntries(Collection entries,
String indent)
{
StringBuffer buf = new StringBuffer();
int width = 1;
for(Map.Entry e : (Collection<Map.Entry>) entries)
{
int length = e.getKey().toString().length();
if(length > width)
{
width = length;
}
}
for(Map.Entry e : (Collection<Map.Entry>) entries)
{
if(buf.length() > 0)
{
buf.append(String.format("%n"));
}
buf.append(String.format(
"%s%-" + width + "s = %s",
indent,
e.getKey(),
e.getValue() == null ? "N/A" : e.getValue()
));
}
return buf.toString();
}
private static IRequestHandler guessOriginalRequestHandler()
{
IRequestMapper mapper = Application.get().getRootRequestMapper();
return mapper.mapRequest(RequestCycle.get().getRequest());
}
/**
* Not meant to be instantiated.
*/
private LoggingUtils()
{
super();
}
}