001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j.examples.mbeans.legacy;
010:
011: import java.lang.reflect.Field;
012: import java.lang.reflect.Method;
013: import javax.management.ListenerNotFoundException;
014: import javax.management.MBeanAttributeInfo;
015: import javax.management.MBeanNotificationInfo;
016: import javax.management.MBeanOperationInfo;
017: import javax.management.MBeanParameterInfo;
018: import javax.management.Notification;
019: import javax.management.NotificationBroadcaster;
020: import javax.management.NotificationBroadcasterSupport;
021: import javax.management.NotificationFilter;
022: import javax.management.NotificationListener;
023:
024: import mx4j.AbstractDynamicMBean;
025:
026: /**
027: * The wrapper DynamicMBean for exposing the legacy service in a non-invasive way. <br>
028: * This MBean emits notifications when the legacy service starts its activity and when it stops it.
029: * Furthermore, when the legacy service is running, it displays the number of threads that the
030: * legacy service is using to perform its activity. <br>
031: * Note how the {@link LegacyService} is completely unaware of JMX, and even if it has private fields
032: * and methods (the legacy service was designed without knowledge of JMX), it is possible to
033: * expose them (via reflection) in JMX. <br>
034: * This MBean is divided in two parts, the implementation one and the JMX one. Note how the JMX
035: * part, thanks to {@link AbstractDynamicMBean}, is very simple even if it is a DynamicMBean.
036: *
037: * @version $Revision: 1.1 $
038: */
039: public class DynamicLegacyService extends AbstractDynamicMBean
040: implements NotificationBroadcaster {
041: //
042: // Implementation Part
043: //
044:
045: private LegacyService service;
046: private Thread statusThread;
047:
048: public DynamicLegacyService(LegacyService service) {
049: this .service = service;
050:
051: statusThread = new Thread(new Runnable() {
052: public void run() {
053: monitorStatus();
054: }
055: });
056: }
057:
058: /**
059: * Starts monitoring the legacy service, and starts as well the legacy service
060: *
061: * @see #isRunning
062: */
063: public void start() {
064: // Start the thread that monitors the status of the service
065: statusThread.start();
066:
067: // We remap the 'start' method as defined by JMX to the 'execute' method of the legacy service
068: service.execute();
069: }
070:
071: /**
072: * Returns whether the legacy service has woken up and it is running or not.
073: */
074: public boolean isRunning() {
075: // The method 'isRunning' is private in the legacy service, so here we use reflection tricks
076: try {
077: Class cls = service.getClass();
078: Method method = cls.getDeclaredMethod("isRunning",
079: new Class[0]);
080: method.setAccessible(true);
081: Boolean result = (Boolean) method.invoke(service,
082: new Object[0]);
083: return result.booleanValue();
084: } catch (Exception ignored) {
085: ignored.printStackTrace();
086: return false;
087: }
088: }
089:
090: /**
091: * Returns the number of threads that the legacy service is using to perform its job when it
092: * wakes up.
093: */
094: public int getThreadCount() {
095: // There is no a direct mapping of the thread count in the legacy service
096: // We use again reflection tricks, calling LegacyService.group.activeCount()
097: try {
098: Class cls = service.getClass();
099: Field field = cls.getDeclaredField("group");
100: field.setAccessible(true);
101: ThreadGroup group = (ThreadGroup) field.get(service);
102: return group.activeCount();
103: } catch (Exception ignored) {
104: ignored.printStackTrace();
105: return 0;
106: }
107: }
108:
109: /**
110: * Monitors the status of the legacy service, every 50 ms, to see if it has woken up
111: * and it is running. <br>
112: * When the legacy service starts and stops its job, a notification is emitted.
113: */
114: private void monitorStatus() {
115: boolean wasRunning = false;
116: while (true) {
117: boolean isRunning = isRunning();
118: if (wasRunning ^ isRunning) {
119: Notification notification = new Notification(
120: "legacy.status.running." + isRunning, this , 0,
121: "Legacy Service Status: " + isRunning);
122: broadcaster.sendNotification(notification);
123: wasRunning = isRunning;
124: } else {
125: if (isRunning)
126: System.out.println("Threads: " + getThreadCount());
127: }
128:
129: // Monitor every 50 ms
130: try {
131: Thread.sleep(50);
132: } catch (InterruptedException ignored) {
133: }
134: }
135: }
136:
137: //
138: // JMX Part
139: //
140:
141: private NotificationBroadcasterSupport broadcaster = new NotificationBroadcasterSupport();
142:
143: protected MBeanAttributeInfo[] createMBeanAttributeInfo() {
144: return new MBeanAttributeInfo[] {
145: new MBeanAttributeInfo("Running", "boolean",
146: "The running status of the Legacy Service",
147: true, false, true),
148: new MBeanAttributeInfo("ThreadCount", "int",
149: "The number of running threads", true, false,
150: false) };
151: }
152:
153: protected MBeanOperationInfo[] createMBeanOperationInfo() {
154: return new MBeanOperationInfo[] { new MBeanOperationInfo(
155: "start", "Start the Legacy Service",
156: new MBeanParameterInfo[0], "void",
157: MBeanOperationInfo.ACTION) };
158: }
159:
160: protected MBeanNotificationInfo[] createMBeanNotificationInfo() {
161: return getNotificationInfo();
162: }
163:
164: public void addNotificationListener(NotificationListener listener,
165: NotificationFilter filter, Object handback) {
166: broadcaster.addNotificationListener(listener, filter, handback);
167: }
168:
169: public MBeanNotificationInfo[] getNotificationInfo() {
170: return new MBeanNotificationInfo[] { new MBeanNotificationInfo(
171: new String[] { "legacy.status.running.true",
172: "legacy.status.running.false" },
173: Notification.class.getName(),
174: "Notifications on the status of the Legacy Service") };
175: }
176:
177: public void removeNotificationListener(NotificationListener listener)
178: throws ListenerNotFoundException {
179: broadcaster.removeNotificationListener(listener);
180: }
181: }
|