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 synchronized (this)
212 {
213 if (portletBatch == null)
214 {
215 portletBatch = new BatchedPortletStatistics(ds,
216 this.maxRecordToFlush_Portlet,
217 this.maxTimeMsToFlush_Portlet, "portletLogBatcher");
218 portletBatch.startThread();
219 }
220 }
221 }
222 portletBatch.addStatistic(record);
223
224 }
225 if (record instanceof PageLogRecord)
226 {
227 if (pageBatch == null)
228 {
229 synchronized (this)
230 {
231 if (pageBatch == null)
232 {
233 pageBatch = new BatchedPageStatistics(ds,
234 this.maxRecordToFlush_Page, this.maxTimeMsToFlush_Page,
235 "pageLogBatcher");
236 pageBatch.startThread();
237 }
238 }
239 }
240 pageBatch.addStatistic(record);
241
242 }
243 if (record instanceof UserLogRecord)
244 {
245 if (userBatch == null)
246 {
247 synchronized (this)
248 {
249 if (userBatch == null)
250 {
251 userBatch = new BatchedUserStatistics(ds,
252 this.maxRecordToFlush_User, this.maxTimeMsToFlush_User,
253 "userLogBatcher");
254 userBatch.startThread();
255 }
256 }
257 }
258 userBatch.addStatistic(record);
259
260 }
261 }
262
263 protected void saveAccessToCLF(LogRecord record)
264 {
265 Object[] args = {""};
266 String logMessage = "";
267 if (record instanceof PortletLogRecord)
268 {
269 PortletLogRecord rec = (PortletLogRecord) record;
270 Object[] args1 =
271 { rec.getIpAddress(), "-", rec.getUserName(), rec.getTimeStamp(),
272 rec.getLogType(), formatter.format(rec.getTimeStamp()),
273 rec.getPortletName(),
274 new Integer(rec.getStatus()).toString(),
275 new Long(rec.getMsElapsedTime())};
276 args = args1;
277 logMessage = MessageFormat.format(portletLogFormat, args)
278 .toString();
279 }
280 if (record instanceof PageLogRecord)
281 {
282 PageLogRecord rec = (PageLogRecord) record;
283 Object[] args1 =
284 { rec.getIpAddress(), "-", rec.getUserName(), rec.getTimeStamp(),
285 rec.getLogType(), formatter.format(rec.getTimeStamp()),
286 new Integer(rec.getStatus()).toString(),
287 new Long(rec.getMsElapsedTime())};
288 args = args1;
289 logMessage = MessageFormat.format(pageLogFormat, args).toString();
290 }
291 if (record instanceof UserLogRecord)
292 {
293 UserLogRecord rec = (UserLogRecord) record;
294 Object[] args1 =
295 { rec.getIpAddress(), "-", rec.getUserName(), rec.getTimeStamp(),
296 rec.getLogType(), formatter.format(rec.getTimeStamp()),
297 new Integer(rec.getStatus()).toString(),
298 new Long(rec.getMsElapsedTime())};
299 args = args1;
300 logMessage = MessageFormat.format(logoutLogFormat, args).toString();
301 }
302 logger.info(logMessage);
303 }
304
305 public void logPageAccess(RequestContext request, String statusCode,
306 long msElapsedTime)
307 {
308 try
309 {
310 HttpServletRequest req = request.getRequest();
311 Principal principal = req.getUserPrincipal();
312 String userName = (principal != null) ? principal.getName()
313 : "guest";
314 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
315 PageLogRecord record = new PageLogRecord();
316
317 record.setUserName(userName);
318 record.setIpAddress(req.getRemoteAddr());
319 ContentPage cp = request.getPage();
320 if (cp != null)
321 {
322 if (cp.getPath() != null)
323 {
324 record.setPagePath(cp.getPath());
325 }
326 }
327 record.setStatus(Integer.parseInt(statusCode));
328 record.setTimeStamp(timestamp);
329 record.setMsElapsedTime(msElapsedTime);
330
331 if (logToCLF)
332 {
333 saveAccessToCLF(record);
334 }
335 if (logToDatabase)
336 {
337 storeAccessToStats(record);
338 }
339
340 } catch (Exception e)
341 {
342 logger.error("Exception", e);
343 }
344 }
345
346 public void logUserLogout(String ipAddress, String userName,
347 long msSessionLength)
348 {
349 try
350 {
351
352 if (userName == null)
353 {
354 userName = "guest";
355 }
356
357 if (!"guest".equals(userName))
358 {
359 synchronized (currentUsers)
360 {
361 UserStats userStats = null;
362
363 Map users = (Map)currentUsers.get(userName);
364 if(users != null && users.size() > 0)
365 {
366 userStats = (UserStats) users.get(ipAddress);
367 }
368
369 if(userStats != null)
370 {
371
372 currentUserCount = currentUserCount - 1;
373
374 userStats.setNumberOfSession(userStats
375 .getNumberOfSessions() - 1);
376 if (userStats.getNumberOfSessions() <= 0)
377 {
378 users.remove(ipAddress);
379 currentUsers.put(userName, users);
380 }
381 }
382 }
383 }
384
385 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
386 UserLogRecord record = new UserLogRecord();
387
388 record.setUserName(userName);
389 record.setIpAddress(ipAddress);
390 record.setStatus(STATUS_LOGGED_OUT);
391 record.setTimeStamp(timestamp);
392 record.setMsElapsedTime(msSessionLength);
393
394 if (logToCLF)
395 {
396 saveAccessToCLF(record);
397 }
398 if (logToDatabase)
399 {
400 storeAccessToStats(record);
401 }
402
403 } catch (Exception e)
404 {
405 logger.error("Exception", e);
406 }
407 }
408
409
410
411
412
413
414
415 public void logUserLogin(RequestContext request, long msElapsedLoginTime)
416 {
417 try
418 {
419 HttpServletRequest req = request.getRequest();
420 Principal principal = req.getUserPrincipal();
421 String userName = (principal != null) ? principal.getName()
422 : "guest";
423 String ipAddress = req.getRemoteAddr();
424 Timestamp timestamp = new Timestamp(System.currentTimeMillis());
425 UserLogRecord record = new UserLogRecord();
426
427 if (!"guest".equals(userName))
428 {
429 currentUserCount = currentUserCount + 1;
430
431 synchronized (currentUsers)
432 {
433
434 UserStats userStats = null;
435
436 Map users = (Map)currentUsers.get(userName);
437 if(users != null && users.size() > 0)
438 {
439 userStats = (UserStats) users.get(ipAddress);
440 }
441 else
442 {
443 users = new TreeMap();
444 }
445
446 if(userStats == null)
447 {
448 userStats = new UserStatsImpl();
449 userStats.setNumberOfSession(0);
450 userStats.setUsername(userName);
451 userStats.setInetAddressFromIp(ipAddress);
452 }
453
454 userStats.setNumberOfSession(userStats
455 .getNumberOfSessions() + 1);
456 users.put(ipAddress, userStats);
457 currentUsers.put(userName, users);
458 }
459 }
460
461 record.setUserName(userName);
462 record.setIpAddress(ipAddress);
463 record.setStatus(STATUS_LOGGED_IN);
464 record.setTimeStamp(timestamp);
465 record.setMsElapsedTime(msElapsedLoginTime);
466
467 if (logToCLF)
468 {
469 saveAccessToCLF(record);
470 }
471 if (logToDatabase)
472 {
473 storeAccessToStats(record);
474 }
475
476 } catch (Exception e)
477 {
478 logger.error("Exception", e);
479 }
480
481 }
482
483 /***
484 * @see org.springframework.beans.factory.DisposableBean#destroy()
485 */
486 public void springDestroy()
487 {
488 if (portletBatch != null)
489 {
490 portletBatch.tellThreadToStop();
491 synchronized (portletBatch.thread)
492 {
493 portletBatch.thread.notify();
494 }
495
496 }
497 if (userBatch != null)
498 {
499 userBatch.tellThreadToStop();
500 synchronized (userBatch.thread)
501 {
502 userBatch.thread.notify();
503 }
504 }
505 if (pageBatch != null)
506 {
507 pageBatch.tellThreadToStop();
508 synchronized (pageBatch.thread)
509 {
510 pageBatch.thread.notify();
511 }
512 }
513
514 if ((this.currentUserCount != 0) && logger.isDebugEnabled())
515 {
516 logger.debug("destroying while users are logged in");
517 }
518 boolean done = false;
519 while (!done)
520 {
521 done = true;
522 if (portletBatch != null)
523 {
524 if (!portletBatch.isDone())
525 {
526 done = false;
527 }
528 }
529 if (userBatch != null)
530 {
531 if (!userBatch.isDone())
532 {
533 done = false;
534 }
535 }
536 if (pageBatch != null)
537 {
538 if (!pageBatch.isDone())
539 {
540 done = false;
541 }
542 }
543
544 try
545 {
546 Thread.sleep(2);
547 } catch (InterruptedException ie)
548 {
549 }
550 }
551
552 }
553
554 /***
555 * @see org.apache.jetspeed.statistics.PortalStatistics#getNumberOfCurrentUsers()
556 */
557 public int getNumberOfCurrentUsers()
558 {
559 return currentUserCount;
560 }
561
562 protected Date getStartDateFromPeriod(String period, Date end)
563 {
564 GregorianCalendar gcEnd = new GregorianCalendar();
565 gcEnd.setTime(end);
566 if (period != null)
567 {
568 if (period.endsWith("m"))
569 {
570
571 String p = period.substring(0, period.length() - 1);
572 int ret = Integer.parseInt(p);
573 gcEnd.add(Calendar.MONTH, (ret * -1));
574 } else if (period.endsWith("d"))
575 {
576
577 String p = period.substring(0, period.length() - 1);
578 int ret = Integer.parseInt(p);
579 gcEnd.add(Calendar.HOUR, (ret * 24 * -1));
580 } else if (period.endsWith("h"))
581 {
582
583 String p = period.substring(0, period.length() - 1);
584 int ret = Integer.parseInt(p);
585 gcEnd.add(Calendar.HOUR, (ret * -1));
586 } else if (period.equals("all"))
587 {
588 gcEnd = new GregorianCalendar();
589 gcEnd.set(1968, 07, 15);
590 } else
591 {
592
593 int ret = Integer.parseInt(period);
594 gcEnd.add(Calendar.MINUTE, (ret * -1));
595 }
596 } else
597 {
598 gcEnd = new GregorianCalendar();
599 gcEnd.set(1968, 07, 15);
600
601 }
602 return gcEnd.getTime();
603 }
604
605
606
607
608
609 public StatisticsQueryCriteria createStatisticsQueryCriteria()
610 {
611 return new StatisticsQueryCriteriaImpl();
612 }
613
614
615
616
617 public AggregateStatistics getDefaultEmptyAggregateStatistics()
618 {
619 return new AggregateStatisticsImpl();
620 }
621
622 /***
623 * @see org.apache.jetspeed.statistics.PortalStatistics#queryStatistics(org.apache.jetspeed.statistics.StatisticsQueryCriteria)
624 */
625 public AggregateStatistics queryStatistics(StatisticsQueryCriteria criteria)
626 throws InvalidCriteriaException
627 {
628 AggregateStatistics as = new AggregateStatisticsImpl();
629 String query;
630 String query2;
631
632 String tableName;
633 String groupColumn;
634
635 Date end = new Date();
636 Date start = getStartDateFromPeriod(criteria.getTimePeriod(), end);
637
638 String queryType = criteria.getQueryType();
639
640
641 if (PortalStatistics.QUERY_TYPE_USER.equals(queryType))
642 {
643 tableName = "USER_STATISTICS";
644 groupColumn = "USER_NAME";
645 } else if (PortalStatistics.QUERY_TYPE_PORTLET.equals(queryType))
646 {
647 tableName = "PORTLET_STATISTICS";
648 groupColumn = "PORTLET";
649 } else if (PortalStatistics.QUERY_TYPE_PAGE.equals(queryType))
650 {
651 tableName = "PAGE_STATISTICS";
652 groupColumn = "PAGE";
653 } else
654 {
655 throw new InvalidCriteriaException(
656 " invalid queryType passed to queryStatistics");
657 }
658 String orderColumn = "itemcount";
659
660 String ascDesc = "DESC";
661
662 if (!PortalStatistics.QUERY_TYPE_USER.equals(queryType))
663 {
664 query = "select count(*) as itemcount , MIN(ELAPSED_TIME) as amin ,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax from "
665 + tableName + " where time_stamp > ? and time_stamp < ?";
666 query2 = "select count(*) as itemcount ,"
667 + groupColumn
668 + ", MIN(ELAPSED_TIME) as amin ,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax "
669 + "from " + tableName
670 + " where time_stamp > ? and time_stamp < ? group by "
671 + groupColumn + " order by " + orderColumn + " " + ascDesc;
672 } else
673 {
674 query = "select count(*) as itemcount , MIN(ELAPSED_TIME) as amin,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax from "
675 + tableName
676 + " where time_stamp > ? and time_stamp < ? and status = 2";
677 query2 = "select count(*) as itemcount ,"
678 + groupColumn
679 + ", MIN(ELAPSED_TIME) as amin ,AVG(ELAPSED_TIME) as aavg ,MAX(ELAPSED_TIME) as amax "
680 + "from "
681 + tableName
682 + " where time_stamp > ? and time_stamp < ? and status = 2 group by "
683 + groupColumn + " order by " + orderColumn + " " + ascDesc;
684 }
685 Connection con = null;
686 try
687 {
688 con = ds.getConnection();
689 PreparedStatement pstmt = con.prepareStatement(query);
690 pstmt.setTimestamp(1, new Timestamp(start.getTime()));
691 pstmt.setTimestamp(2, new Timestamp(end.getTime()));
692 ResultSet rs = pstmt.executeQuery();
693 float denominator = 1.0f;
694 if (PortalStatistics.QUERY_TYPE_USER.equals(queryType))
695 {
696 denominator = 1000f * 60f;
697
698 }
699 if (rs.next())
700 {
701 as.setHitCount(rs.getInt("itemcount"));
702
703 as.setMinProcessingTime(rs.getFloat("amin") / denominator);
704 as.setAvgProcessingTime(rs.getFloat("aavg") / denominator);
705 as.setMaxProcessingTime(rs.getFloat("amax") / denominator);
706
707 }
708 PreparedStatement pstmt2 = con.prepareStatement(query2);
709 pstmt2.setTimestamp(1, new Timestamp(start.getTime()));
710 pstmt2.setTimestamp(2, new Timestamp(end.getTime()));
711 ResultSet rs2 = pstmt2.executeQuery();
712
713 int rowCount = 0;
714 int totalRows = 5;
715 String listsizeStr = criteria.getListsize();
716 int temp = -1;
717 try
718 {
719 temp = Integer.parseInt(listsizeStr);
720 }
721 catch (NumberFormatException e)
722 {
723 }
724 if(temp != -1) {
725 totalRows = temp;
726 }
727
728 while ((rs2.next()) && (rowCount < totalRows))
729 {
730 Map row = new HashMap();
731 row.put("count", "" + rs2.getInt("itemcount"));
732 String col = rs2.getString(groupColumn);
733 int maxColLen = 35;
734 if (col != null)
735 {
736
737 if (col.length() > maxColLen)
738 {
739 col = col.substring(0, maxColLen);
740 }
741 }
742
743 row.put("groupColumn", col);
744 row.put("min", ""
745 + floatFormatter(rs2.getFloat("amin") / denominator));
746 row.put("avg", ""
747 + floatFormatter(rs2.getFloat("aavg") / denominator));
748 row.put("max", ""
749 + floatFormatter(rs2.getFloat("amax") / denominator));
750 as.addRow(row);
751 rowCount++;
752 }
753
754 }
755 catch (SQLException e)
756 {
757 throw new InvalidCriteriaException(e.toString());
758 }
759 finally
760 {
761 try
762 {
763 if(con != null)
764 {
765 con.close();
766 }
767 }
768 catch (SQLException e)
769 {
770 logger.error("error releasing the connection",e);
771 }
772 }
773
774 return as;
775 }
776
777 protected String floatFormatter(float f)
778 {
779
780 int f2 = new Float(f).intValue();
781 return Integer.toString(f2);
782 }
783
784
785
786
787
788
789 public List getListOfLoggedInUsers()
790 {
791 List list = new ArrayList();
792
793 synchronized (currentUsers)
794 {
795 list.addAll(currentUsers.values());
796 }
797 return list;
798 }
799
800
801
802
803
804
805 public int getNumberOfLoggedInUsers()
806 {
807 return this.currentUserCount;
808 }
809
810
811 /***
812 * @see org.apache.jetspeed.statistics.PortalStatistics#forceFlush()
813 */
814 public void forceFlush()
815 {
816 if (pageBatch != null)
817 {
818 this.pageBatch.flush();
819 }
820 if (portletBatch != null)
821 {
822 this.portletBatch.flush();
823 }
824 if (userBatch != null)
825 {
826 this.userBatch.flush();
827 }
828 }
829
830 }