001: /*
002: * $Id$
003: * $Revision$
004: * $Date$
005: *
006: * ==============================================================================
007: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
008: * use this file except in compliance with the License. You may obtain a copy of
009: * the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
015: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
016: * License for the specific language governing permissions and limitations under
017: * the License.
018: */
019: /*
020: * $Id$
021: * $Revision$
022: * $Date$
023: *
024: * ==============================================================================
025: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
026: * use this file except in compliance with the License. You may obtain a copy of
027: * the License at
028: *
029: * http://www.apache.org/licenses/LICENSE-2.0
030: *
031: * Unless required by applicable law or agreed to in writing, software
032: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
033: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
034: * License for the specific language governing permissions and limitations under
035: * the License.
036: */
037: package wicket.protocol.http;
038:
039: import java.io.Serializable;
040: import java.util.ArrayList;
041: import java.util.Collection;
042: import java.util.Date;
043: import java.util.LinkedList;
044: import java.util.List;
045: import java.util.Map;
046:
047: import wicket.IRequestTarget;
048: import wicket.Page;
049: import wicket.PageMap;
050: import wicket.Session;
051: import wicket.request.target.component.IBookmarkablePageRequestTarget;
052: import wicket.request.target.component.IPageRequestTarget;
053: import wicket.request.target.component.listener.IListenerInterfaceRequestTarget;
054: import wicket.request.target.resource.ISharedResourceRequestTarget;
055: import wicket.session.ISessionStore;
056: import wicket.util.concurrent.ConcurrentHashMap;
057: import wicket.util.lang.Classes;
058: import wicket.util.string.AppendingStringBuffer;
059:
060: /**
061: * This is the logger class that can be set in the {@link WebApplication#setRequestLogger(RequestLogger)}
062: * method. If this class is set all request and live sessions will be recorded and displayed
063: * From the total created sessions, to the peak session count and the current livesessions.
064: * For the livesessions the request logger will record what request are happening
065: * what kind of {@link IRequestTarget} was the event target and what {@link IRequestTarget}
066: * was the response target. It also records what session data was touched for this and
067: * how long the request did take.
068: *
069: * To view this information live see the {@link InspectorBug} that shows the {@link InspectorPage}
070: * with the {@link LiveSessionsPage}
071: *
072: * This class is still a bit experimental for the 1.2 release. Will improve further in 2.0
073: *
074: * @author jcompagner
075: *
076: * @since 1.2
077: */
078: public class RequestLogger {
079: // TODO post 1.2 for this class: saving to a log file, only holding a small part in mem.
080:
081: private int totalCreatedSessions;
082:
083: private int peakSessions;
084:
085: private Map liveSessions;
086:
087: /**
088: * Construct.
089: */
090: public RequestLogger() {
091: liveSessions = new ConcurrentHashMap();
092: }
093:
094: /**
095: * @return The total created sessions counter
096: */
097: public int getTotalCreatedSessions() {
098: return totalCreatedSessions;
099: }
100:
101: /**
102: * @return The peak sessions counter
103: */
104: public int getPeakSessions() {
105: return peakSessions;
106: }
107:
108: /**
109: * @return Collection of live Sessions
110: */
111: public Collection getLiveSessions() {
112: return liveSessions.values();
113: }
114:
115: /**
116: * Method used to cleanup a livesession when the session was
117: * invalidated by the webcontainer
118: *
119: * @param sessionId
120: */
121: public void sessionDestroyed(String sessionId) {
122: liveSessions.remove(sessionId);
123: }
124:
125: /**
126: * This method is called when the request is over this will
127: * set the total time a request takes and cleans up the current
128: * request data.
129: *
130: * @param timeTaken
131: */
132: public void requestTime(long timeTaken) {
133: SessionData sd = getSessionData();
134: sd.endRequest(timeTaken);
135: }
136:
137: /**
138: * Called to monitor removals of objects out of the {@link ISessionStore}
139: *
140: * @param value
141: */
142: public void objectRemoved(Object value) {
143: SessionData sd = getSessionData();
144: if (value instanceof Page) {
145: sd.pageRemoved((Page) value);
146: } else if (value instanceof PageMap) {
147: sd.pageMapRemoved((PageMap) value);
148: } else if (value instanceof WebSession) {
149: sd.webSessionRemoved((WebSession) value);
150: } else {
151: // unknown object/custom object?
152: }
153: }
154:
155: /**
156: * Called to monitor updates of objects in the {@link ISessionStore}
157: *
158: * @param value
159: */
160: public void objectUpdated(Object value) {
161: SessionData sd = getSessionData();
162: if (value instanceof Page) {
163: sd.pageUpdated((Page) value);
164: } else if (value instanceof PageMap) {
165: sd.pageMapUpdated((PageMap) value);
166: } else if (value instanceof WebSession) {
167: sd.webSessionUpdated((WebSession) value);
168: } else {
169: // unknown object/custom object?
170: }
171:
172: }
173:
174: /**
175: * Called to monitor additions of objects in the {@link ISessionStore}
176: *
177: * @param value
178: */
179: public void objectCreated(Object value) {
180: SessionData sd = null;
181:
182: //ignore the creation of sessions itself.
183: // Because not much is set then (Session.get()/ RequestCycle.get())
184: if (!(value instanceof Session)) {
185: sd = getSessionData();
186: }
187: if (value instanceof Page) {
188: sd.pageCreated((Page) value);
189: } else if (value instanceof PageMap) {
190: sd.pageMapCreated((PageMap) value);
191: } else {
192: // unknown object/custom object?
193: }
194:
195: }
196:
197: /**
198: * Sets the target that was the response target for the current request
199: *
200: * @param target
201: */
202: public void logResponseTarget(IRequestTarget target) {
203: getSessionData().logResponseTarget(target);
204: }
205:
206: /**
207: * Sets the target that was the event target for the current request
208: *
209: * @param target
210: */
211: public void logEventTarget(IRequestTarget target) {
212: getSessionData().logEventTarget(target);
213: }
214:
215: private SessionData getSessionData() {
216: Session session = Session.get();
217: // TODO when delayed sessions, do make sure this doesn't cause a http session creation.
218: SessionData sessionData = (SessionData) liveSessions
219: .get(session.getId());
220: if (sessionData == null) {
221: sessionData = createSessionData(session);
222: }
223: return sessionData;
224: }
225:
226: /**
227: * @param session
228: * @return The SessionData object
229: */
230: private SessionData createSessionData(Session session) {
231: SessionData sessionData = new SessionData(session);
232: liveSessions.put(session.getId(), sessionData);
233: totalCreatedSessions++;
234: if (peakSessions < liveSessions.size()) {
235: peakSessions = liveSessions.size();
236: }
237: return sessionData;
238: }
239:
240: /**
241: * This class hols the information one sessions has
242: *
243: * @author jcompagner
244: */
245: public static class SessionData implements Serializable {
246: private static final long serialVersionUID = 1L;
247:
248: private final Session session;
249:
250: private LinkedList requests;
251:
252: private RequestData currentRequest;
253:
254: private double totalRequestsTime;
255:
256: private long lastRequestTime = System.currentTimeMillis();
257:
258: /**
259: * Construct.
260: * @param session
261: */
262: public SessionData(Session session) {
263: this .session = session;
264: this .requests = new LinkedList();
265: }
266:
267: /**
268: * @return The session id
269: */
270: public String getId() {
271: return session.getId();
272: }
273:
274: /**
275: * @return The session
276: */
277: public Session getSession() {
278: return session;
279: }
280:
281: /**
282: * Gets lastRequestTime.
283: * @return lastRequestTime
284: */
285: public long getLastRequestTime() {
286: return lastRequestTime;
287: }
288:
289: /**
290: * @return The request list of this session
291: */
292: public List getRequests() {
293: return requests;
294: }
295:
296: /**
297: * @return The total session size
298: */
299: public long getSessionSize() {
300: return session.getSizeInBytes();
301: }
302:
303: /**
304: * @return The total time in seconds all request did take
305: */
306: public Double getRequestsTime() {
307: return new Double(totalRequestsTime / 1000);
308: }
309:
310: /**
311: * @param target
312: */
313: public void logEventTarget(IRequestTarget target) {
314: getCurrentRequest().addEventTarget(
315: getRequestTargetString(target));
316: }
317:
318: /**
319: * @param target
320: */
321: public void logResponseTarget(IRequestTarget target) {
322: getCurrentRequest().addResponseTarget(
323: getRequestTargetString(target));
324: }
325:
326: /**
327: * @param target
328: * @return The request target nice display string
329: */
330: private String getRequestTargetString(IRequestTarget target) {
331: AppendingStringBuffer sb = new AppendingStringBuffer(128);
332: if (target instanceof IListenerInterfaceRequestTarget) {
333: IListenerInterfaceRequestTarget listener = (IListenerInterfaceRequestTarget) target;
334: sb.append("Interface call [target:");
335: sb.append(Classes.simpleName(listener.getTarget()
336: .getClass()));
337: sb.append("(");
338: sb.append(listener.getTarget().getId());
339: sb.append("), page: ");
340: sb.append(Classes.simpleName(listener.getPage()
341: .getClass()));
342: sb.append("(");
343: sb.append(listener.getPage().getId());
344: sb.append("), interface: ");
345: sb.append(listener.getRequestListenerInterface()
346: .getName());
347: sb.append(".");
348: sb.append(listener.getRequestListenerInterface()
349: .getMethod().getName());
350: sb.append("]");
351: } else if (target instanceof IPageRequestTarget) {
352: IPageRequestTarget pageRequestTarget = (IPageRequestTarget) target;
353: sb.append("PageRequest call [page: ");
354: sb.append(Classes.simpleName(pageRequestTarget
355: .getPage().getClass()));
356: sb.append("(");
357: sb.append(pageRequestTarget.getPage().getId());
358: sb.append(")]");
359: } else if (target instanceof IBookmarkablePageRequestTarget) {
360: IBookmarkablePageRequestTarget pageRequestTarget = (IBookmarkablePageRequestTarget) target;
361: sb.append("BookmarkablePage call [page: ");
362: sb.append(Classes.simpleName(pageRequestTarget
363: .getPageClass()));
364: sb.append("]");
365: } else if (target instanceof ISharedResourceRequestTarget) {
366: ISharedResourceRequestTarget sharedResourceTarget = (ISharedResourceRequestTarget) target;
367: sb.append("Shared Resource call [resourcekey: ");
368: sb.append(sharedResourceTarget.getResourceKey());
369: sb.append("]");
370: } else {
371: sb.append(target.toString());
372: }
373: return sb.toString();
374: }
375:
376: /**
377: * @param page
378: */
379: public void pageCreated(Page page) {
380: getCurrentRequest().addEntry(
381: "Page created, id: " + page.getId() + ", class:"
382: + page.getClass());
383: }
384:
385: /**
386: * @param map
387: */
388: public void pageMapCreated(PageMap map) {
389: getCurrentRequest().addEntry(
390: "PageMap created, name: "
391: + (map.getName() == null ? "DEFAULT" : map
392: .getName()));
393: }
394:
395: /**
396: * @param session
397: */
398: public void webSessionCreated(WebSession session) {
399: getCurrentRequest().addEntry("WebSession created");
400: }
401:
402: /**
403: * @param session
404: */
405: public void webSessionUpdated(WebSession session) {
406: getCurrentRequest().addEntry("WebSession updated");
407: }
408:
409: /**
410: * @param map
411: */
412: public void pageMapUpdated(PageMap map) {
413: getCurrentRequest().addEntry(
414: "PageMap updated, name: "
415: + (map.getName() == null ? "DEFAULT" : map
416: .getName()));
417: }
418:
419: /**
420: * @param page
421: */
422: public void pageUpdated(Page page) {
423: getCurrentRequest().addEntry(
424: "Page updated, id: " + page.getId() + ", class:"
425: + page.getClass());
426: }
427:
428: /**
429: * @param session
430: */
431: public void webSessionRemoved(WebSession session) {
432: getCurrentRequest().addEntry("WebSession removed");
433: }
434:
435: /**
436: * @param map
437: */
438: public void pageMapRemoved(PageMap map) {
439: getCurrentRequest().addEntry(
440: "PageMap removed, name: "
441: + (map.getName() == null ? "DEFAULT" : map
442: .getName()));
443: }
444:
445: /**
446: * @param page
447: */
448: public void pageRemoved(Page page) {
449: getCurrentRequest().addEntry(
450: "Page removed, id: " + page.getId() + ", class:"
451: + page.getClass());
452: }
453:
454: /**
455: * @param timeTaken
456: */
457: public void endRequest(long timeTaken) {
458: RequestData rd = getCurrentRequest();
459: rd.setTimeTaken(timeTaken);
460: totalRequestsTime += timeTaken;
461: currentRequest = null;
462:
463: lastRequestTime = new Date().getTime() - timeTaken;
464: }
465:
466: private RequestData getCurrentRequest() {
467: if (currentRequest == null) {
468: currentRequest = new RequestData();
469: requests.addFirst(currentRequest);
470: if (requests.size() > 1000) {
471: requests.removeLast();
472: }
473: }
474: return currentRequest;
475: }
476:
477: }
478:
479: /**
480: * This class hold the information one request of a session has.
481: *
482: * @author jcompagner
483: */
484: public static class RequestData implements Serializable {
485: private static final long serialVersionUID = 1L;
486:
487: private Date startDate;
488: private long timeTaken;
489: private List entries = new ArrayList(5);
490: private String eventTarget;
491: private String responseTarget;
492:
493: /**
494: * @return The time taken for this request
495: */
496: public Long getTimeTaken() {
497: return new Long(timeTaken);
498: }
499:
500: /**
501: * @return The time taken for this request
502: */
503: public Date getStartDate() {
504: return startDate;
505: }
506:
507: /**
508: * @return The event target string
509: */
510: public String getEventTargert() {
511: return eventTarget;
512: }
513:
514: /**
515: * @return The response target string
516: */
517: public String getResponseTarget() {
518: return responseTarget;
519: }
520:
521: /**
522: * @param target
523: */
524: public void addResponseTarget(String target) {
525: this .responseTarget = target;
526: }
527:
528: /**
529: * @param target
530: */
531: public void addEventTarget(String target) {
532: this .eventTarget = target;
533: }
534:
535: /**
536: * @param timeTaken
537: */
538: public void setTimeTaken(long timeTaken) {
539: this .timeTaken = timeTaken;
540: this .startDate = new Date(System.currentTimeMillis()
541: - timeTaken);
542: }
543:
544: /**
545: * @param string
546: */
547: public void addEntry(String string) {
548: entries.add(string);
549: }
550:
551: /**
552: * @return All entries of the objects that are created/updated or removed in this request
553: */
554: public String getAlteredObjects() {
555: AppendingStringBuffer sb = new AppendingStringBuffer();
556: for (int i = 0; i < entries.size(); i++) {
557: String element = (String) entries.get(i);
558: sb.append(element);
559: if (entries.size() != i - 1) {
560: sb.append("<br/>");
561: }
562: }
563: return sb.toString();
564: }
565:
566: }
567: }
|