001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mq.server;
023:
024: import java.text.DateFormat;
025: import java.util.ArrayList;
026: import java.util.Calendar;
027: import java.util.Date;
028: import java.util.GregorianCalendar;
029:
030: import org.jboss.mq.MessageStatistics;
031:
032: /**
033: * This class stores message count informations for a given queue
034: *
035: * @todo The date processing has been reported as a bottleneck
036: * @author Ulf Schroeter (u.schroeter@mobilcom.de)
037: * @author Stephan Steinbacher (s.steinbacher@mobilcom.de)
038: * @version $Revision: 57198 $
039: */
040: public class MessageCounter {
041: // destination related information
042: String destName;
043: String destSubscription;
044: boolean destTopic;
045: boolean destDurable;
046:
047: // destination queue
048: BasicQueue destQueue;
049:
050: // counter
051: int countTotal;
052: int countTotalLast;
053: int depthLast;
054: long timeLastUpdate;
055:
056: // per hour day counter history
057: int dayCounterMax;
058: ArrayList dayCounter;
059:
060: /**
061: * Get an array of message statistics from an array of message counters
062: *
063: * @param counter the message counters
064: * @return the message statistics
065: * @throws Exception for any error
066: */
067: public static MessageStatistics[] getMessageStatistics(
068: MessageCounter[] counter) throws Exception {
069: MessageStatistics[] stats = new MessageStatistics[counter.length];
070: for (int i = 0; i < counter.length; ++i) {
071: stats[i] = new MessageStatistics();
072: stats[i].setName(counter[i].getDestinationName());
073: stats[i].setSubscriptionID(counter[i]
074: .getDestinationSubscription());
075: stats[i].setTopic(counter[i].getDestinationTopic());
076: stats[i].setDurable(counter[i].getDestinationDurable());
077: stats[i].setCount(counter[i].getCount());
078: stats[i].setCountDelta(counter[i].getCountDelta());
079: stats[i].setDepth(counter[i].getDepth());
080: stats[i].setDepthDelta(counter[i].getDepthDelta());
081: stats[i].setTimeLastUpdate(counter[i].getLastUpdate());
082: }
083: return stats;
084: }
085:
086: /**
087: * Constructor
088: *
089: * @param name destination name
090: * @param subscription subscription name
091: * @param queue internal queue object
092: * @param topic topic destination flag
093: * @param durable durable subsciption flag
094: * @param daycountmax max message history day count
095: */
096: public MessageCounter(String name, String subscription,
097: BasicQueue queue, boolean topic, boolean durable,
098: int daycountmax) {
099: // store destination related information
100: destName = name;
101: destSubscription = subscription;
102: destTopic = topic;
103: destDurable = durable;
104: destQueue = queue;
105:
106: // initialize counter
107: resetCounter();
108:
109: // initialize message history
110: dayCounter = new ArrayList();
111:
112: setHistoryLimit(daycountmax);
113: }
114:
115: /**
116: * Get string representation
117: */
118: public String toString() {
119: return getCounterAsString();
120: }
121:
122: /**
123: * Increment message counter and update message history
124: */
125: public void incrementCounter() {
126: // update message counter
127: countTotal++;
128:
129: // update timestamp
130: timeLastUpdate = System.currentTimeMillis();
131:
132: // update message history
133: updateHistory(true);
134: }
135:
136: /**
137: * Gets the related destination name
138: *
139: * @return String destination name
140: */
141: public String getDestinationName() {
142: return destName;
143: }
144:
145: /**
146: * Gets the related destination subscription
147: *
148: * @return String destination name
149: */
150: public String getDestinationSubscription() {
151: return destSubscription;
152: }
153:
154: /**
155: * Gets the related destination topic flag
156: *
157: * @return boolean true: topic destination, false: queue destination
158: */
159: public boolean getDestinationTopic() {
160: return destTopic;
161: }
162:
163: /**
164: * Gets the related destination durable subscription flag
165: *
166: * @return boolean true : durable subscription,
167: * false: non-durable subscription
168: */
169: public boolean getDestinationDurable() {
170: return destDurable;
171: }
172:
173: /**
174: * Gets the total message count since startup or
175: * last counter reset
176: *
177: * @return int message count
178: */
179: public int getCount() {
180: return countTotal;
181: }
182:
183: /**
184: * Gets the message count delta since last method call
185: *
186: * @return int message count delta
187: */
188: public int getCountDelta() {
189: int delta = countTotal - countTotalLast;
190:
191: countTotalLast = countTotal;
192:
193: return delta;
194: }
195:
196: /**
197: * Gets the current message count of pending messages
198: * within the destination waiting for dispatch
199: *
200: * @return int message queue depth
201: */
202: public int getDepth() {
203: return destQueue.getQueueDepth();
204: }
205:
206: /**
207: * Gets the message count delta of pending messages
208: * since last method call. Therefore
209: *
210: * @return int message queue depth delta
211: */
212: public int getDepthDelta() {
213: int current = destQueue.getQueueDepth();
214: int delta = current - depthLast;
215:
216: depthLast = current;
217:
218: return delta;
219: }
220:
221: /**
222: * Gets the timestamp of the last message add
223: *
224: * @return long system time
225: */
226: public long getLastUpdate() {
227: return timeLastUpdate;
228: }
229:
230: /**
231: * Reset message counter values
232: */
233: public void resetCounter() {
234: countTotal = 0;
235: countTotalLast = 0;
236: depthLast = 0;
237: timeLastUpdate = 0;
238: }
239:
240: /**
241: * Get message counter data as string in format
242: *
243: * "Topic/Queue, Name, Subscription, Durable, Count, CountDelta,
244: * Depth, DepthDelta, Timestamp Last Increment"
245: *
246: * @return String message counter data string
247: */
248: public String getCounterAsString() {
249: String ret;
250:
251: // Topic/Queue
252: if (destTopic)
253: ret = "Topic,";
254: else
255: ret = "Queue,";
256:
257: // name
258: ret += destName + ",";
259:
260: // subscription
261: if (destSubscription != null)
262: ret += destSubscription + ",";
263: else
264: ret += "-,";
265:
266: // Durable subscription
267: if (destTopic) {
268: // Topic
269: if (destDurable)
270: ret += "true,";
271: else
272: ret += "false,";
273: } else {
274: // Queue
275: ret += "-,";
276: }
277:
278: // counter values
279: ret += getCount() + "," + getCountDelta() + "," + getDepth()
280: + "," + getDepthDelta() + ",";
281:
282: // timestamp last counter update
283: if (timeLastUpdate > 0) {
284: DateFormat dateFormat = DateFormat.getDateTimeInstance(
285: DateFormat.SHORT, DateFormat.MEDIUM);
286:
287: ret += dateFormat.format(new Date(timeLastUpdate));
288: } else {
289: ret += "-";
290: }
291:
292: return ret;
293: }
294:
295: /**
296: * Get message counter history day count limit
297: *
298: * <0: unlimited, 0: history disabled, >0: day count
299: */
300: public int getHistoryLimit() {
301: return dayCounterMax;
302: }
303:
304: /**
305: * Set message counter history day count limit
306: *
307: * <0: unlimited, 0: history disabled, >0: day count
308: */
309: public void setHistoryLimit(int daycountmax) {
310: boolean bInitialize = false;
311:
312: // store new maximum day count
313: dayCounterMax = daycountmax;
314:
315: // update day counter array
316: synchronized (dayCounter) {
317: if (dayCounterMax > 0) {
318: // limit day history to specified day count
319: int delta = dayCounter.size() - dayCounterMax;
320:
321: for (int i = 0; i < delta; i++) {
322: // reduce array size to requested size by dropping
323: // oldest day counters
324: dayCounter.remove(0);
325: }
326:
327: // create initial day counter when empty
328: bInitialize = dayCounter.isEmpty();
329: } else if (dayCounterMax == 0) {
330: // disable history
331: dayCounter.clear();
332: } else {
333: // unlimited day history
334:
335: // create initial day counter when empty
336: bInitialize = dayCounter.isEmpty();
337: }
338:
339: // optionally initialize first day counter entry
340: if (bInitialize) {
341: dayCounter.add(new DayCounter(new GregorianCalendar(),
342: true));
343: }
344: }
345: }
346:
347: /**
348: * Update message counter history
349: */
350: private void updateHistory(boolean incrementCounter) {
351: // check history activation
352: if (dayCounter.isEmpty()) {
353: return;
354: }
355:
356: // calculate day difference between current date and date of last day counter entry
357: synchronized (dayCounter) {
358: DayCounter counterLast = (DayCounter) dayCounter
359: .get(dayCounter.size() - 1);
360:
361: GregorianCalendar calNow = new GregorianCalendar();
362: GregorianCalendar calLast = counterLast.getDate();
363:
364: // clip day time part for day delta calulation
365: calNow.clear(Calendar.AM_PM);
366: calNow.clear(Calendar.HOUR);
367: calNow.clear(Calendar.HOUR_OF_DAY);
368: calNow.clear(Calendar.MINUTE);
369: calNow.clear(Calendar.SECOND);
370: calNow.clear(Calendar.MILLISECOND);
371:
372: calLast.clear(Calendar.AM_PM);
373: calLast.clear(Calendar.HOUR);
374: calLast.clear(Calendar.HOUR_OF_DAY);
375: calLast.clear(Calendar.MINUTE);
376: calLast.clear(Calendar.SECOND);
377: calLast.clear(Calendar.MILLISECOND);
378:
379: long millisPerDay = 86400000; // 24 * 60 * 60 * 1000
380: long millisDelta = calNow.getTime().getTime()
381: - calLast.getTime().getTime();
382:
383: int dayDelta = (int) (millisDelta / millisPerDay);
384:
385: if (dayDelta > 0) {
386: // finalize last day counter
387: counterLast.finalizeDayCounter();
388:
389: // add new intermediate empty day counter entries
390: DayCounter counterNew;
391:
392: for (int i = 1; i < dayDelta; i++) {
393: // increment date
394: calLast.add(Calendar.DAY_OF_YEAR, 1);
395:
396: counterNew = new DayCounter(calLast, false);
397: counterNew.finalizeDayCounter();
398:
399: dayCounter.add(counterNew);
400: }
401:
402: // add new day counter entry for current day
403: counterNew = new DayCounter(calNow, false);
404:
405: dayCounter.add(counterNew);
406:
407: // ensure history day count limit
408: setHistoryLimit(dayCounterMax);
409: }
410:
411: // update last day counter entry
412: counterLast = (DayCounter) dayCounter
413: .get(dayCounter.size() - 1);
414: counterLast.updateDayCounter(incrementCounter);
415: }
416: }
417:
418: /**
419: * Reset message counter history
420: */
421: public void resetHistory() {
422: int max = dayCounterMax;
423:
424: setHistoryLimit(0);
425: setHistoryLimit(max);
426: }
427:
428: /**
429: * Get message counter history data as string in format
430: *
431: * "day count\n
432: * Date 1, hour counter 0, hour counter 1, ..., hour counter 23\n
433: * Date 2, hour counter 0, hour counter 1, ..., hour counter 23\n
434: * .....
435: * .....
436: * Date n, hour counter 0, hour counter 1, ..., hour counter 23\n"
437: *
438: * @return String message history data string
439: */
440: public String getHistoryAsString() {
441: String ret = "";
442:
443: // ensure history counters are up to date
444: updateHistory(false);
445:
446: // compile string
447: synchronized (dayCounter) {
448: // first line: history day count
449: ret += dayCounter.size() + "\n";
450:
451: // following lines: day counter data
452: for (int i = 0; i < dayCounter.size(); i++) {
453: DayCounter counter = (DayCounter) dayCounter.get(i);
454:
455: ret += counter.getDayCounterAsString() + "\n";
456: }
457: }
458:
459: return ret;
460: }
461:
462: /**
463: * Internal day counter class for one day hour based counter history
464: */
465: static class DayCounter {
466: static final int HOURS = 24;
467:
468: GregorianCalendar date = null;
469: int[] counters = new int[HOURS];
470:
471: /**
472: * Constructor
473: *
474: * @param date day counter date
475: * @param isStartDay true first day counter
476: * false follow up day counter
477: */
478: DayCounter(GregorianCalendar date, boolean isStartDay) {
479: // store internal copy of creation date
480: this .date = (GregorianCalendar) date.clone();
481:
482: // initialize the array with '0'- values to current hour (if it is not the
483: // first monitored day) and the rest with default values ('-1')
484: int hour = date.get(Calendar.HOUR_OF_DAY);
485:
486: for (int i = 0; i < HOURS; i++) {
487: if (i < hour) {
488: if (isStartDay)
489: counters[i] = -1;
490: else
491: counters[i] = 0;
492: } else {
493: counters[i] = -1;
494: }
495: }
496:
497: // set the array element of the current hour to '0'
498: counters[hour] = 0;
499: }
500:
501: /**
502: * Gets copy of day counter date
503: *
504: * @return GregorianCalendar day counter date
505: */
506: GregorianCalendar getDate() {
507: return (GregorianCalendar) date.clone();
508: }
509:
510: /**
511: * Update day counter hour array elements
512: *
513: * @param incrementCounter update current hour counter
514: */
515: void updateDayCounter(boolean incrementCounter) {
516: // get the current hour of the day
517: GregorianCalendar cal = new GregorianCalendar();
518:
519: int currentIndex = cal.get(Calendar.HOUR_OF_DAY);
520:
521: // check if the last array update is more than 1 hour ago, if so fill all
522: // array elements between the last index and the current index with '0' values
523: boolean bUpdate = false;
524:
525: for (int i = 0; i <= currentIndex; i++) {
526: if (counters[i] > -1) {
527: // found first initialized hour counter
528: // -> set all following uninitialized
529: // counter values to 0
530: bUpdate = true;
531: }
532:
533: if (bUpdate == true) {
534: if (counters[i] == -1)
535: counters[i] = 0;
536: }
537: }
538:
539: // optionally increment current counter
540: if (incrementCounter) {
541: counters[currentIndex]++;
542: }
543: }
544:
545: /**
546: * Finalize day counter hour array elements
547: */
548: void finalizeDayCounter() {
549: // a new day has began, so fill all array elements from index to end with
550: // '0' values
551: boolean bFinalize = false;
552:
553: for (int i = 0; i < HOURS; i++) {
554: if (counters[i] > -1) {
555: // found first initialized hour counter
556: // -> finalize all following uninitialized
557: // counter values
558: bFinalize = true;
559: }
560:
561: if (bFinalize) {
562: if (counters[i] == -1)
563: counters[i] = 0;
564: }
565: }
566: }
567:
568: /**
569: * Return day counter data as string with format
570: * "Date, hour counter 0, hour counter 1, ..., hour counter 23"
571: *
572: * @return String day counter data
573: */
574: String getDayCounterAsString() {
575: // first element day counter date
576: DateFormat dateFormat = DateFormat
577: .getDateInstance(DateFormat.SHORT);
578:
579: String strData = dateFormat.format(date.getTime());
580:
581: // append 24 comma separated hour counter values
582: for (int i = 0; i < HOURS; i++) {
583: strData += "," + counters[i];
584: }
585:
586: return strData;
587: }
588: }
589: }
|