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.test.jbossmx.compliance.timer;
023:
024: import java.util.ArrayList;
025: import java.util.Date;
026:
027: import javax.management.MBeanServer;
028: import javax.management.MBeanServerFactory;
029: import javax.management.MBeanServerInvocationHandler;
030: import javax.management.Notification;
031: import javax.management.NotificationListener;
032: import javax.management.ObjectName;
033: import javax.management.timer.TimerMBean;
034:
035: import org.jboss.test.jbossmx.compliance.TestCase;
036:
037: /**
038: * Test fixed-delay/fixed-rate timer execution modes.
039: *
040: * Program a JMX timer to produce TIMES notifications, every PERIOD msces,
041: * with the initial notification after PERIOD msecs.
042: *
043: * Introduce a fixed DELAY (<PERIOD) in the reception of the timer notification
044: * to slow it down. Measure the total time in both fixed-rate & fixed-delay
045: * scenarios and compare it with an expected value +/- an allowed percentage
046: * difference.
047: *
048: * In fixed-rate mode the delay does not affect the periodic execution (because
049: * it's less than the period), so the expected total time is the number of repeatitions
050: * times the period, plus the final delay (because that one doesn't overlap with a period).
051: *
052: * In fixed-delay mode things are simpler. The total execution time is prolonged because
053: * the period doesn't overlap with the execution/delay time, so the total time is
054: * period plus delay times the number of repeatitions.
055: *
056: * The choice of numbers below makes sure that even with a 15% allowed difference
057: * there won't be any overlap in the fixed-rate/fixed-delay execution modes
058: * (i.e. one cannot be confused with the other)
059: *
060: * @author Dimitris.Andreadis@jboss.org
061: * @version $Revision: 62061 $
062: */
063: public class PeriodTestCase extends TestCase implements
064: NotificationListener {
065: private final long PERIOD = 300;
066: private final long DELAY = 200;
067: private final long TIMES = 5;
068: private final long FIXED_RATE_TOTAL = PERIOD * TIMES + DELAY;
069: private final long FIXED_DELAY_TOTAL = (PERIOD + DELAY) * TIMES;
070: private final long ALLOWED_DIFFERENCE = 15;
071:
072: /** The object name of the timer service */
073: private ObjectName timerName;
074:
075: /** The MBean server */
076: private MBeanServer server;
077:
078: /** Test start time */
079: private long startTime;
080:
081: /** The received notifications */
082: private ArrayList receivedNotifications = new ArrayList();
083:
084: // Constructor ---------------------------------------------------------------
085:
086: public PeriodTestCase(String s) {
087: super (s);
088: }
089:
090: // Overrides -----------------------------------------------------------------
091:
092: /**
093: * The timer class to test
094: */
095: protected String getTestedTimerClass() {
096: // the standard JMX timer
097: return "javax.management.timer.Timer";
098: }
099:
100: // Tests ---------------------------------------------------------------------
101:
102: /**
103: * Test the (default) fixed-delay timer execution mode
104: */
105: public void testFixedDelay() throws Exception {
106: try {
107: startTimerService();
108: TimerMBean timer = (TimerMBean) MBeanServerInvocationHandler
109: .newProxyInstance(server, timerName,
110: TimerMBean.class, false);
111:
112: // calculate all times from now
113: startTime = System.currentTimeMillis();
114:
115: // This must cause a fixed-delay timer notification production
116: // with TIMES notification produced, spaced at PERIOD msecs, starting in now+PERIOD
117: timer.addNotification("timer.notification", null, null,
118: new Date(startTime + PERIOD), PERIOD, TIMES);
119:
120: long expectedDuration = FIXED_DELAY_TOTAL;
121: waitForNotifications(TIMES, expectedDuration * 2);
122:
123: long testDuration = System.currentTimeMillis() - startTime;
124: checkTimeDifference(expectedDuration, testDuration,
125: ALLOWED_DIFFERENCE);
126: } finally {
127: stopTimerService();
128: }
129: }
130:
131: /**
132: * Test the fixed-rate timer execution mode
133: */
134: public void testFixedRate() throws Exception {
135: try {
136: startTimerService();
137: TimerMBean timer = (TimerMBean) MBeanServerInvocationHandler
138: .newProxyInstance(server, timerName,
139: TimerMBean.class, false);
140:
141: // calculate all times from now
142: startTime = System.currentTimeMillis();
143:
144: // This must cause a fixed-rate timer notification production
145: // with TIMES notification produced, spaced at PERIOD msecs, starting in now+PERIOD
146: timer.addNotification("timer.notification", null, null,
147: new Date(startTime + PERIOD), PERIOD, TIMES, true);
148:
149: long expectedDuration = FIXED_RATE_TOTAL;
150: waitForNotifications(TIMES, expectedDuration * 2);
151:
152: long testDuration = System.currentTimeMillis() - startTime;
153: checkTimeDifference(expectedDuration, testDuration,
154: ALLOWED_DIFFERENCE);
155: } finally {
156: stopTimerService();
157: }
158: }
159:
160: public void handleNotification(Notification notification,
161: Object handback) {
162: try {
163: long time = notification.getTimeStamp() - startTime;
164: long seqNo = notification.getSequenceNumber();
165: log.debug("#" + seqNo + " (" + time + "ms) - "
166: + notification);
167:
168: // cause an artifical delay
169: Thread.sleep(DELAY);
170: } catch (InterruptedException ignore) {
171: }
172:
173: synchronized (receivedNotifications) {
174: receivedNotifications.add(notification);
175:
176: // Notify test completion
177: if (receivedNotifications.size() >= TIMES)
178: receivedNotifications.notifyAll();
179: }
180: }
181:
182: // Support functions ---------------------------------------------------------
183:
184: private void checkTimeDifference(long expected, long actual,
185: long percentage) {
186: long actualDiff = (actual - expected) * 100 / expected;
187: log.debug("Actual time: " + actual + " msec, expected time: "
188: + expected + " msecs");
189: log.debug("Actual difference: " + actualDiff
190: + "%, allowed: +/-" + percentage + "%");
191:
192: long diff = Math.abs(expected - actual);
193: long maxDeviation = expected * percentage / 100;
194:
195: if (diff > maxDeviation)
196: fail("Time difference larger than " + percentage + "%");
197: }
198:
199: private void waitForNotifications(long totalExpected, long wait)
200: throws Exception {
201: synchronized (receivedNotifications) {
202: if (receivedNotifications.size() > totalExpected)
203: fail("too many notifications "
204: + receivedNotifications.size());
205:
206: if (receivedNotifications.size() < totalExpected)
207: receivedNotifications.wait(wait);
208: }
209: assertEquals(totalExpected, receivedNotifications.size());
210: }
211:
212: /**
213: * Get an MBeanServer, install the timer service and a notification
214: * listener.
215: */
216: private void startTimerService() throws Exception {
217: server = MBeanServerFactory.createMBeanServer("Timer");
218: timerName = new ObjectName("Timer:type=TimerService");
219: server.createMBean(getTestedTimerClass(), timerName,
220: new Object[0], new String[0]);
221: server.invoke(timerName, "start", new Object[0], new String[0]);
222: server.addNotificationListener(timerName, this , null, null);
223: receivedNotifications.clear();
224: }
225:
226: /**
227: * Remove everything used by this test.
228: */
229: private void stopTimerService() {
230: try {
231: server.invoke(timerName, "removeAllNotifications",
232: new Object[0], new String[0]);
233: server.invoke(timerName, "stop", new Object[0],
234: new String[0]);
235: server.unregisterMBean(timerName);
236: MBeanServerFactory.releaseMBeanServer(server);
237: receivedNotifications.clear();
238: } catch (Exception ignored) {
239: }
240: }
241:
242: }
|