/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.jbossmx.compliance.timer; import java.util.ArrayList; import java.util.Date; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.timer.TimerNotification; import org.jboss.test.jbossmx.compliance.TestCase; /** * Basic timer test.<p> * * The aim of these tests is to check the most common uses of the timer * service. * * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>. */ public class BasicTestCase extends TestCase implements NotificationListener { // Attributes ---------------------------------------------------------------- /** * The object name of the timer service */ ObjectName timerName; /** * The MBean server */ MBeanServer server; /** * The received notifications */ ArrayList receivedNotifications = new ArrayList(); // Constructor --------------------------------------------------------------- public BasicTestCase(String s) { super(s); } // Tests --------------------------------------------------------------------- /** * Test a single notification works. */ public void testSingleNotification() throws Exception { try { startTimerService(); Integer id = addNotification("test", "hello", "data", calcTime(PERIOD), 0, 1); expectNotifications(1); // TODO // if (getNotificationType(id) != null) // fail("Single notification still registered"); } finally { stopTimerService(); } } /** * Test a repeated notification works. */ public void testRepeatedNotification() throws Exception { try { startTimerService(); Integer id = addNotification("test", "hello", "data", calcTime(PERIOD), PERIOD, REPEATS); expectNotifications(1); expectNotifications(2); // TODO // if (getNotificationType(id) != null) // fail("Repeated notification still registered"); } finally { stopTimerService(); } } /** * Test infinite notification works. */ public void testInfiniteNotification() throws Exception { try { startTimerService(); Integer id = addNotification("test", "hello", "data", calcTime(PERIOD), PERIOD, 0); expectNotifications(1); expectNotifications(2); if (getNotificationType(id) == null) fail("Infinite notification not registered"); } finally { stopTimerService(); } } /** * Test two infinite notification works. */ public void testTwoNotificationProducers() throws Exception { try { startTimerService(); long lTimeOne = 5 * 1000; long lTimeTwo = 12 * 1000; long lWait = 2 * lTimeTwo; long lStart = calcTime( lTimeOne ); Integer lIdOne = addNotification( "test-2", "hello", "data", lStart + lTimeOne, lTimeOne, 0); Integer lIdTwo = addNotification( "test-2", "hello", "data", lStart + lTimeTwo, lTimeTwo, 0); expectNotifications( 1, lWait ); expectNotifications( 2, lWait ); // Check time differences which should be around TIME ONE TimerNotification lNotificationOne = (TimerNotification) receivedNotifications.get( 0 ); TimerNotification lNotificationTwo = (TimerNotification) receivedNotifications.get( 1 ); checkNotificationID( lNotificationOne, lIdOne ); checkNotificationID( lNotificationTwo, lIdOne ); checkTimeDifference( lNotificationOne, lNotificationTwo, lTimeOne ); expectNotifications( 3, lWait ); lNotificationOne = lNotificationTwo; lNotificationTwo = (TimerNotification) receivedNotifications.get( 2 ); checkNotificationID( lNotificationTwo, lIdTwo ); checkTimeDifference( lNotificationOne, lNotificationTwo, ( lTimeTwo - ( 2 * lTimeOne ) ) ); expectNotifications( 4, lWait ); lNotificationOne = lNotificationTwo; lNotificationTwo = (TimerNotification) receivedNotifications.get( 3 ); checkNotificationID( lNotificationTwo, lIdOne ); checkTimeDifference( lNotificationOne, lNotificationTwo, ( ( 3 * lTimeOne ) - lTimeTwo ) ); expectNotifications( 5, lWait ); lNotificationOne = lNotificationTwo; lNotificationTwo = (TimerNotification) receivedNotifications.get( 4 ); checkNotificationID( lNotificationTwo, lIdOne ); checkTimeDifference( lNotificationOne, lNotificationTwo, lTimeOne ); expectNotifications( 6, lWait ); lNotificationOne = lNotificationTwo; lNotificationTwo = (TimerNotification) receivedNotifications.get( 5 ); checkNotificationID( lNotificationTwo, lIdTwo ); checkTimeDifference( lNotificationOne, lNotificationTwo, ( ( 2 * lTimeTwo ) - ( 4 * lTimeOne ) ) ); } finally { stopTimerService(); } } // Overrides ----------------------------------------------------------------- /** * The timer class to test */ protected String getTestedTimerClass() { // the standard JMX timer return "javax.management.timer.Timer"; } // Support functions --------------------------------------------------------- /** * Get an MBeanServer, install the timer service and a notification * listener. */ private void startTimerService() throws Exception { server = MBeanServerFactory.createMBeanServer("Timer"); timerName = new ObjectName("Timer:type=TimerService"); server.createMBean(getTestedTimerClass(), timerName, new Object[0], new String[0]); server.invoke(timerName, "start", new Object[0], new String[0]); receivedNotifications.clear(); server.addNotificationListener(timerName, this, null, null); } /** * Remove everything used by this test. Cannot report failures because * the test might have failed earlier. All notifications are removed, * the RI hangs otherwise. */ private void stopTimerService() { try { server.invoke(timerName, "removeAllNotifications", new Object[0], new String[0]); server.invoke(timerName, "stop", new Object[0], new String[0]); server.unregisterMBean(timerName); MBeanServerFactory.releaseMBeanServer(server); } catch (Exception ignored) {} } /** * Handle a notification, just add it to the list * * @param notification the notification received * @param handback not used */ public void handleNotification(Notification notification, Object handback) { synchronized (receivedNotifications) { receivedNotifications.add(notification); receivedNotifications.notifyAll(); } } /** * Wait for the timer notification and see if we have the correct number * hopefully this should synchronize this test with the timer thread. * * @param expected the number of notifications expected * @throws Exception when the notifications are incorrect */ public void expectNotifications(int expected) throws Exception { expectNotifications( expected, WAIT ); } /** * Wait for the timer notification and see if we have the correct number * hopefully this should synchronize this test with the timer thread. * * @param expected the number of notifications expected * @param wait time in milli seconds to wait for the notification * @throws Exception when the notifications are incorrect */ public void expectNotifications(int expected, long wait) throws Exception { synchronized (receivedNotifications) { if (receivedNotifications.size() > expected) fail("too many notifications"); if (receivedNotifications.size() < expected) { receivedNotifications.wait( wait ); } assertEquals(expected, receivedNotifications.size()); } } /** * Checks if the given Notification ID is the same as the * one of the given Notification * * @param pNotification Notification to be tested * @param pNotificationID Id the Notification should have **/ public void checkNotificationID( TimerNotification pNotification, Integer pNotificationID ) { if( pNotification == null ) { fail( "Notification is null" ); } if( !pNotification.getNotificationID().equals( pNotificationID ) ) { fail( "Wrong Notification ID received: " + pNotification.getNotificationID() + ", expected: " + pNotificationID ); } } /** * Checks if the time between the two Notification is in a * +- 10% limit * * @param pNotificationOne First Notification to be tested * @param pNotificationTwo Second Notification to be tested * @param pTimeDiffernce Expected Time Difference **/ public void checkTimeDifference( TimerNotification pNotificationOne, TimerNotification pNotificationTwo, long pTimeDiffernce ) { long lDiff = pNotificationTwo.getTimeStamp() - pNotificationOne.getTimeStamp(); if( lDiff < ( pTimeDiffernce - ( pTimeDiffernce / 10 ) ) || lDiff > ( pTimeDiffernce + ( pTimeDiffernce / 10 ) ) ) { fail( "Time between first two notification is too small or too big: " + pTimeDiffernce ); } } /** * Add a timer notification * * @param type the type of the notification * @param message the message * @param data the user data * @param time the time of the notification * @param period the period of notification * @param occurs the number of occurances * @return the id of the notfication */ private Integer addNotification(String type, String message, String data, long time, long period, long occurs) throws Exception { return (Integer) server.invoke(timerName, "addNotification", new Object[] { type, message, data, new Date(time), new Long(period), new Long(occurs) }, new String[] { "java.lang.String", "java.lang.String", "java.lang.Object", "java.util.Date", "long", "long" } ); } /** * Get the notification type for an id * * @param id the id of the notification * @return the type of the notification */ private String getNotificationType(Integer id) throws Exception { // This is called after the last expected notification // The timer thread has notified us, but hasn't had time // to remove the notification, give it chance, before // checking for correct behaviour. Thread.yield(); return (String) server.invoke(timerName, "getNotificationType", new Object[] { id }, new String[] { "java.lang.Integer" }); } /** * Calculate the time using an offset from the current time. * @param offset the offset from the current time * @return the calculated time */ private long calcTime(long offset) { return System.currentTimeMillis() + offset; } }