1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.jetspeed.statistics.impl;
19
20 import java.security.Principal;
21 import java.sql.Connection;
22 import java.sql.PreparedStatement;
23 import java.sql.ResultSet;
24 import java.sql.SQLException;
25 import java.sql.Timestamp;
26 import java.text.MessageFormat;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Collections;
31 import java.util.Date;
32 import java.util.GregorianCalendar;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.TreeMap;
37
38 import javax.naming.NamingException;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.sql.DataSource;
41
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44 import org.apache.jetspeed.om.page.ContentPage;
45 import org.apache.jetspeed.request.RequestContext;
46 import org.apache.jetspeed.statistics.AggregateStatistics;
47 import org.apache.jetspeed.statistics.InvalidCriteriaException;
48 import org.apache.jetspeed.statistics.PortalStatistics;
49 import org.apache.jetspeed.statistics.StatisticsQueryCriteria;
50 import org.apache.jetspeed.statistics.UserStats;
51 import org.springframework.orm.ojb.support.PersistenceBrokerDaoSupport;
52
53 /***
54 * <p>
55 * PortalStatisticsImpl
56 * </p>
57 *
58 * @author <a href="mailto:chris@bluesunrise.com">Chris Schaefer </a>
59 * @author <a href="mailto:taylor@apache.org">David Sean Taylor </a>
60 * @version $Id: TestPortletEntityDAO.java,v 1.3 2005/05/24 14:43:19 ate Exp $
61 */
62 public class PortalStatisticsImpl extends PersistenceBrokerDaoSupport implements
63 PortalStatistics
64 {
65
66 protected final static Log logger = LogFactory
67 .getLog(PortalStatisticsImpl.class);
68
69
70 protected BatchedStatistics portletBatch;
71
72
73 protected BatchedStatistics pageBatch;
74
75
76 protected BatchedStatistics userBatch;
77
78
79 protected static final String portletLogFormat = "{0} {1} {2} [{3}] \"{4} {5} {6}\" {7} {8}";
80
81
82 protected static final String pageLogFormat = "{0} {1} {2} [{3}] \"{4} {5}\" {6} {7}";
83
84
85 protected static final String logoutLogFormat = "{0} {1} {2} [{3}] \"{4}\" {5} {6}";
86
87 protected static final int STATUS_LOGGED_IN = 1;
88
89 protected static final int STATUS_LOGGED_OUT = 2;
90
91
92 protected boolean logToCLF = true;
93
94 protected boolean logToDatabase = true;
95
96 protected int maxRecordToFlush_Portlet = 30;
97
98 protected int maxRecordToFlush_User = 30;
99
100 protected int maxRecordToFlush_Page = 30;
101
102 protected long maxTimeMsToFlush_Portlet = 10 * 1000;
103
104 protected long maxTimeMsToFlush_User = 10 * 1000;
105
106 protected long maxTimeMsToFlush_Page = 10 * 1000;
107
108
109
110
111
112 protected DataSource ds;
113
114 protected int currentUserCount = 0;
115
116 protected Map currentUsers;
117
118
119 protected SimpleDateFormat formatter = null;
120
121 /***
122 * <p>
123 * Default constructor.
124 * </p>
125 */
126
127 public PortalStatisticsImpl(boolean logToCLF, boolean logToDatabase,
128 int maxRecordToFlush_Portal, int maxRecordToFlush_User,
129 int maxRecordToFlush_Page, long maxTimeMsToFlush_Portal,
130 long maxTimeMsToFlush_User, long maxTimeMsToFlush_Page,
131 DataSource dataSource)
132
133 {
134
135 this.logToCLF = logToCLF;
136 this.logToDatabase = logToDatabase;
137 this.maxRecordToFlush_Portlet = maxRecordToFlush_Portal;
138 this.maxRecordToFlush_User = maxRecordToFlush_User;
139 this.maxRecordToFlush_Page = maxRecordToFlush_Page;
140 this.maxTimeMsToFlush_Portlet = maxTimeMsToFlush_Portal;
141 this.maxTimeMsToFlush_User = maxTimeMsToFlush_User;
142 this.maxTimeMsToFlush_Page = maxTimeMsToFlush_Page;
143
144 this.ds = dataSource;
145 currentUsers = Collections.synchronizedMap(new TreeMap());
146 }
147
148 public void springInit() throws NamingException
149 {
150 formatter = new SimpleDateFormat("dd/MM/yyyy:hh:mm:ss z");
151 currentUserCount = 0;
152 }
153
154 public DataSource getDataSource()
155 {
156 return ds;
157 }
158
159 public void logPortletAccess(RequestContext request, String portletName,
160 String statusCode, long msElapsedTime)
161 {
162
163 try
164 {
165 HttpServletRequest req = request.getRequest();
166 Principal principal = req.getUserPrincipal();
167 String userName = (principal != null) ? principal.getName()
168 : "guest";
169 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
170 PortletLogRecord record = new PortletLogRecord();
171
172 record.setPortletName(portletName);
173 record.setUserName(userName);
174 if (req.getRemoteAddr() != null)
175 {
176 record.setIpAddress(req.getRemoteAddr());
177 }
178 ContentPage cp = request.getPage();
179 if (cp != null)
180 {
181 if (cp.getPath() != null)
182 {
183 record.setPagePath(cp.getPath());
184 }
185 }
186 record.setStatus(Integer.parseInt(statusCode));
187 record.setTimeStamp(timestamp);
188 record.setMsElapsedTime(msElapsedTime);
189
190 if (logToCLF)
191 {
192 saveAccessToCLF(record);
193 }
194 if (logToDatabase)
195 {
196 storeAccessToStats(record);
197 }
198 } catch (Exception e)
199 {
200 logger.error("Exception", e);
201 }
202 }
203
204 protected void storeAccessToStats(LogRecord record)
205 {
206
207 if (record instanceof PortletLogRecord)
208 {
209 if (portletBatch == null)
210 {
211 portletBatch = new BatchedPortletStatistics(ds,
212 this.maxRecordToFlush_Portlet,
213 this.maxTimeMsToFlush_Portlet, "portletLogBatcher");
214 portletBatch.startThread();
215 }
216 portletBatch.addStatistic(record);
217
218 }
219 if (record instanceof PageLogRecord)
220 {
221 if (pageBatch == null)
222 {
223 pageBatch = new BatchedPageStatistics(ds,
224 this.maxRecordToFlush_Page, this.maxTimeMsToFlush_Page,
225 "pageLogBatcher");
226 pageBatch.startThread();
227 }
228 pageBatch.addStatistic(record);
229
230 }
231 if (record instanceof UserLogRecord)
232 {
233 if (userBatch == null)
234 {
235 userBatch = new BatchedUserStatistics(ds,
236 this.maxRecordToFlush_User, this.maxTimeMsToFlush_User,
237 "userLogBatcher");
238 userBatch.startThread();
239 }
240 userBatch.addStatistic(record);
241
242 }
243 }
244
245 protected void saveAccessToCLF(LogRecord record)
246 {
247 Object[] args = {""};
248 String logMessage = "";
249 if (record instanceof PortletLogRecord)
250 {
251 PortletLogRecord rec = (PortletLogRecord) record;
252 Object[] args1 =
253 { rec.getIpAddress(), "-", rec.getUserName(), rec.getTimeStamp(),
254 rec.getLogType(), formatter.format(rec.getTimeStamp()),
255 rec.getPortletName(),
256 new Integer(rec.getStatus()).toString(),
257 new Long(rec.getMsElapsedTime())};
258 args = args1;
259 logMessage = MessageFormat.format(portletLogFormat, args)
260 .toString();
261 }
262 if (record instanceof PageLogRecord)
263 {
264 PageLogRecord rec = (PageLogRecord) record;
265 Object[] args1 =
266 { rec.getIpAddress(), "-", rec.getUserName(), rec.getTimeStamp(),
267 rec.getLogType(), formatter.format(rec.getTimeStamp()),
268 new Integer(rec.getStatus()).toString(),
269 new Long(rec.getMsElapsedTime())};
270 args = args1;
271 logMessage = MessageFormat.format(pageLogFormat, args).toString();
272 }
273 if (record instanceof UserLogRecord)
274 {
275 UserLogRecord rec = (UserLogRecord) record;
276 Object[] args1 =
277 { rec.getIpAddress(), "-", rec.getUserName(), rec.getTimeStamp(),
278 rec.getLogType(), formatter.format(rec.getTimeStamp()),
279 new Integer(rec.getStatus()).toString(),
280 new Long(rec.getMsElapsedTime())};
281 args = args1;
282 logMessage = MessageFormat.format(logoutLogFormat, args).toString();
283 }
284 logger.info(logMessage);
285 }
286
287 public void logPageAccess(RequestContext request, String statusCode,
288 long msElapsedTime)
289 {
290 try
291 {
292 HttpServletRequest req = request.getRequest();
293 Principal principal = req.getUserPrincipal();
294 String userName = (principal != null) ? principal.getName()
295 : "guest";
296 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
297 PageLogRecord record = new PageLogRecord();
298
299 record.setUserName(userName);
300 record.setIpAddress(req.getRemoteAddr());
301 ContentPage cp = request.getPage();
302 if (cp != null)
303 {
304 if (cp.getPath() != null)
305 {
306 record.setPagePath(cp.getPath());
307 }
308 }
309 record.setStatus(Integer.parseInt(statusCode));
310 record.setTimeStamp(timestamp);
311 record.setMsElapsedTime(msElapsedTime);
312
313 if (logToCLF)
314 {
315 saveAccessToCLF(record);
316 }
317 if (logToDatabase)
318 {
319 storeAccessToStats(record);
320 }
321
322 } catch (Exception e)
323 {
324 logger.error("Exception", e);
325 }
326 }
327
328 public void logUserLogout(String ipAddress, String userName,
329 long msSessionLength)
330 {
331 try
332 {
333
334 if (userName == null)
335 {
336 userName = "guest";
337 }
338
339 if (!"guest".equals(userName))
340 {
341 synchronized (currentUsers)
342 {
343 UserStats userStats = null;
344
345 Map users = (Map)currentUsers.get(userName);
346 if(users != null && users.size() > 0)
347 {
348 userStats = (UserStats) users.get(ipAddress);
349 }
350
351 if(userStats != null)
352 {
353
354 currentUserCount = currentUserCount - 1;
355
356 userStats.setNumberOfSession(userStats
357 .getNumberOfSessions() - 1);
358 if (userStats.getNumberOfSessions() <= 0)
359 {
360 users.remove(ipAddress);
361 currentUsers.put(userName, users);
362 }
363 }
364 }
365 }
366
367 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
368 UserLogRecord record = new UserLogRecord();
369
370 record.setUserName(userName);
371 record.setIpAddress(ipAddress);
372 record.setStatus(STATUS_LOGGED_OUT);
373 record.setTimeStamp(timestamp);
374 record.setMsElapsedTime(msSessionLength);
375
376 if (logToCLF)
377 {
378 saveAccessToCLF(record);
379 }
380 if (logToDatabase)
381 {
382 storeAccessToStats(record);
383 }
384
385 } catch (Exception e)
386 {
387 logger.error("Exception", e);
388 }
389 }
390
391
392
393
394
395
396
397 public void logUserLogin(RequestContext request, long msElapsedLoginTime)
398 {
399 try
400 {
401 HttpServletRequest req = request.getRequest();
402 Principal principal = req.getUserPrincipal();
403 String userName = (principal != null) ? principal.getName()
404 : "guest";
405 String ipAddress = req.getRemoteAddr();
406 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
407 UserLogRecord record = new UserLogRecord();
408
409 if (!"guest".equals(userName))
410 {
411 currentUserCount = currentUserCount + 1;
412
413 synchronized (currentUsers)
414 {
415
416 UserStats userStats = null;
417
418 Map users = (Map)currentUsers.get(userName);
419 if(users != null && users.size() > 0)
420 {
421 userStats = (UserStats) users.get(ipAddress);
422 }
423 else
424 {
425 users = new TreeMap();
426 }
427
428 if(userStats == null)
429 {
430 userStats = new UserStatsImpl();
431 userStats.setNumberOfSession(0);
432 userStats.setUsername(userName);
433 userStats.setInetAddressFromIp(ipAddress);
434 }
435
436 userStats.setNumberOfSession(userStats
437 .getNumberOfSessions() + 1);
438 users.put(ipAddress, userStats);
439 currentUsers.put(userName, users);
440 }
441 }
442
443 record.setUserName(userName);
444 record.setIpAddress(ipAddress);
445 record.setStatus(STATUS_LOGGED_IN);
446 record.setTimeStamp(timestamp);
447 record.setMsElapsedTime(msElapsedLoginTime);
448
449 if (logToCLF)
450 {
451 saveAccessToCLF(record);
452 }
453 if (logToDatabase)
454 {
455 storeAccessToStats(record);
456 }
457
458 } catch (Exception e)
459 {
460 logger.error("Exception", e);
461 }
462
463 }
464
465 /***
466 * @see org.springframework.beans.factory.DisposableBean#destroy()
467 */
468 public void springDestroy()
469 {
470 if (portletBatch != null)
471 {
472 portletBatch.tellThreadToStop();
473 synchronized (portletBatch.thread)
474 {
475 portletBatch.thread.notify();
476 }
477
478 }
479 if (userBatch != null)
480 {
481 userBatch.tellThreadToStop();
482 synchronized (userBatch.thread)
483 {
484 userBatch.thread.notify();
485 }
486 }
487 if (pageBatch != null)
488 {
489 pageBatch.tellThreadToStop();
490 synchronized (pageBatch.thread)
491 {
492 pageBatch.thread.notify();
493 }
494 }
495
496 if ((this.currentUserCount != 0) && logger.isDebugEnabled())
497 {
498 logger.debug("destroying while users are logged in");
499 }
500 boolean done = false;
501 while (!done)
502 {
503 done = true;
504 if (portletBatch != null)
505 {
506 if (!portletBatch.isDone())
507 {
508 done = false;
509 }
510 }
511 if (userBatch != null)
512 {
513 if (!userBatch.isDone())
514 {
515 done = false;
516 }
517 }
518 if (pageBatch != null)
519 {
520 if (!pageBatch.isDone())
521 {
522 done = false;
523 }
524 }
525
526 try
527 {
528 Thread.sleep(2);
529 } catch (InterruptedException ie)
530 {
531 }
532 }
533
534 }
535
536 /***
537 * @see org.apache.jetspeed.statistics.PortalStatistics#getNumberOfCurrentUsers()
538 */
539 public int getNumberOfCurrentUsers()
540 {
541 return currentUserCount;
542 }
543
544 protected Date getStartDateFromPeriod(String period, Date end)
545 {
546 GregorianCalendar gcEnd = new GregorianCalendar();
547 gcEnd.setTime(end);
548 if (period != null)
549 {
550 if (period.endsWith("m"))
551 {
552
553 String p = period.substring(0, period.length() - 1);
554 int ret = Integer.parseInt(p);
555 gcEnd.add(Calendar.MONTH, (ret * -1));
556 } else if (period.endsWith("d"))
557 {
558
559 String p = period.substring(0, period.length() - 1);
560 int ret = Integer.parseInt(p);
561 gcEnd.add(Calendar.HOUR, (ret * 24 * -1));
562 } else if (period.endsWith("h"))
563 {
564
565 String p = period.substring(0, period.length() - 1);
566 int ret = Integer.parseInt(p);
567 gcEnd.add(Calendar.HOUR, (ret * -1));
568 } else if (period.equals("all"))
569 {
570 gcEnd = new GregorianCalendar();
571 gcEnd.set(1968, 07, 15);
572 } else
573 {
574
575 int ret = Integer.parseInt(period);
576 gcEnd.add(Calendar.MINUTE, (ret * -1));
577 }
578 } else
579 {
580 gcEnd = new GregorianCalendar();
581 gcEnd.set(1968, 07, 15);
582
583 }
584 return gcEnd.getTime();
585 }
586
587
588
589
590
591 public StatisticsQueryCriteria createStatisticsQueryCriteria()
592 {
593 return new StatisticsQueryCriteriaImpl();
594 }
595
596
597
598
599 public AggregateStatistics getDefaultEmptyAggregateStatistics()
600 {
601 return new AggregateStatisticsImpl();
602 }
603
604 /***
605 * @see org.apache.jetspeed.statistics.PortalStatistics#queryStatistics(org.apache.jetspeed.statistics.StatisticsQueryCriteria)
606 */
607 public AggregateStatistics queryStatistics(StatisticsQueryCriteria criteria)
608 throws InvalidCriteriaException
609 {
610 AggregateStatistics as = new AggregateStatisticsImpl();
611 String query;
612 String query2;
613
614 String tableName;
615 String groupColumn;
616
617 Date end = new Date();
618 Date start = getStartDateFromPeriod(criteria.getTimePeriod(), end);
619
620 String queryType = criteria.getQueryType();
621
622
623 if (PortalStatistics.QUERY_TYPE_USER.equals(queryType))
624 {
625 tableName = "USER_STATISTICS";
626 groupColumn = "USER_NAME";
627 } else if (PortalStatistics.QUERY_TYPE_PORTLET.equals(queryType))
628 {
629 tableName = "PORTLET_STATISTICS";
630 groupColumn = "PORTLET";
631 } else if (PortalStatistics.QUERY_TYPE_PAGE.equals(queryType))
632 {
633 tableName = "PAGE_STATISTICS";
634 groupColumn = "PAGE";
635 } else
636 {
637 throw new InvalidCriteriaException(
638 " invalid queryType passed to queryStatistics");
639 }
640 String orderColumn = "itemcount";
641
642 String ascDesc = "DESC";
643
644 if (!PortalStatistics.QUERY_TYPE_USER.equals(queryType))
645 {
646 query = "select count(*) as itemcount , MIN(ELAPSED_TIME) as amin ,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax from "
647 + tableName + " where time_stamp > ? and time_stamp < ?";
648 query2 = "select count(*) as itemcount ,"
649 + groupColumn
650 + ", MIN(ELAPSED_TIME) as amin ,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax "
651 + "from " + tableName
652 + " where time_stamp > ? and time_stamp < ? group by "
653 + groupColumn + " order by " + orderColumn + " " + ascDesc;
654 } else
655 {
656 query = "select count(*) as itemcount , MIN(ELAPSED_TIME) as amin,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax from "
657 + tableName
658 + " where time_stamp > ? and time_stamp < ? and status = 2";
659 query2 = "select count(*) as itemcount ,"
660 + groupColumn
661 + ", MIN(ELAPSED_TIME) as amin ,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax "
662 + "from "
663 + tableName
664 + " where time_stamp > ? and time_stamp < ? and status = 2 group by "
665 + groupColumn + " order by " + orderColumn + " " + ascDesc;
666 }
667 Connection con = null;
668 try
669 {
670 con = ds.getConnection();
671 PreparedStatement pstmt = con.prepareStatement(query);
672 pstmt.setTimestamp(1, new Timestamp(start.getTime()));
673 pstmt.setTimestamp(2, new Timestamp(end.getTime()));
674 ResultSet rs = pstmt.executeQuery();
675 float denominator = 1.0f;
676 if (PortalStatistics.QUERY_TYPE_USER.equals(queryType))
677 {
678 denominator = 1000f * 60f;
679
680 }
681 if (rs.next())
682 {
683 as.setHitCount(rs.getInt("itemcount"));
684
685 as.setMinProcessingTime(rs.getFloat("amin") / denominator);
686 as.setAvgProcessingTime(rs.getFloat("aavg") / denominator);
687 as.setMaxProcessingTime(rs.getFloat("amax") / denominator);
688
689 }
690 PreparedStatement pstmt2 = con.prepareStatement(query2);
691 pstmt2.setTimestamp(1, new Timestamp(start.getTime()));
692 pstmt2.setTimestamp(2, new Timestamp(end.getTime()));
693 ResultSet rs2 = pstmt2.executeQuery();
694
695 int rowCount = 0;
696 int totalRows = 5;
697 String listsizeStr = criteria.getListsize();
698 int temp = -1;
699 try
700 {
701 temp = Integer.parseInt(listsizeStr);
702 }
703 catch (NumberFormatException e)
704 {
705 }
706 if(temp != -1) {
707 totalRows = temp;
708 }
709
710 while ((rs2.next()) && (rowCount < totalRows))
711 {
712 Map row = new HashMap();
713 row.put("count", "" + rs2.getInt("itemcount"));
714 String col = rs2.getString(groupColumn);
715 int maxColLen = 35;
716 if (col != null)
717 {
718
719 if (col.length() > maxColLen)
720 {
721 col = col.substring(0, maxColLen);
722 }
723 }
724
725 row.put("groupColumn", col);
726 row.put("min", ""
727 + floatFormatter(rs2.getFloat("amin") / denominator));
728 row.put("avg", ""
729 + floatFormatter(rs2.getFloat("aavg") / denominator));
730 row.put("max", ""
731 + floatFormatter(rs2.getFloat("amax") / denominator));
732 as.addRow(row);
733 rowCount++;
734 }
735
736 }
737 catch (SQLException e)
738 {
739 throw new InvalidCriteriaException(e.toString());
740 }
741 finally
742 {
743 try
744 {
745 if(con != null)
746 {
747 con.close();
748 }
749 }
750 catch (SQLException e)
751 {
752 logger.error("error releasing the connection",e);
753 }
754 }
755
756 return as;
757 }
758
759 protected String floatFormatter(float f)
760 {
761
762 int f2 = new Float(f).intValue();
763 return Integer.toString(f2);
764 }
765
766
767
768
769
770
771 public List getListOfLoggedInUsers()
772 {
773 List list = new ArrayList();
774
775 synchronized (currentUsers)
776 {
777 list.addAll(currentUsers.values());
778 }
779 return list;
780 }
781
782
783
784
785
786
787 public int getNumberOfLoggedInUsers()
788 {
789 return this.currentUserCount;
790 }
791
792
793 /***
794 * @see org.apache.jetspeed.statistics.PortalStatistics#forceFlush()
795 */
796 public void forceFlush()
797 {
798 if (pageBatch != null)
799 {
800 this.pageBatch.flush();
801 }
802 if (portletBatch != null)
803 {
804 this.portletBatch.flush();
805 }
806 if (userBatch != null)
807 {
808 this.userBatch.flush();
809 }
810 }
811
812 }