001package org.javasimon.jmx; 002 003import org.javasimon.Counter; 004import org.javasimon.Manager; 005import org.javasimon.Simon; 006import org.javasimon.Stopwatch; 007import org.javasimon.callback.CallbackSkeleton; 008 009import java.lang.management.ManagementFactory; 010import java.util.HashSet; 011import java.util.Iterator; 012import java.util.Set; 013 014import javax.management.JMException; 015import javax.management.MBeanServer; 016import javax.management.ObjectName; 017 018/** 019 * Callback that registers MXBeans for Simons after their creation. It is 020 * advisable to register the callback as soon as possible otherwise MX Beans 021 * for some Simons may not be created. Class can be extended in order to 022 * override {@link #constructObjectName(Simon)}. 023 * 024 * @author <a href="mailto:virgo47@gmail.com">Richard "Virgo" Richter</a> 025 */ 026public class JmxRegisterCallback extends CallbackSkeleton { 027 028 /** Domain part of the JMX object name - protected for subclasses. */ 029 protected String domain; 030 031 /** MBean server instance specified for this callback (or default platform one) - protected for subclasses. */ 032 protected MBeanServer mBeanServer; 033 034 /** Names of all beans registered for separate Simons */ 035 private Set<String> registeredNames = new HashSet<>(); 036 037 /** Whether all existing Simons from Manager should be registered after callback is added to a Manager. */ 038 private boolean registerExisting; 039 040 private Manager manager; 041 042 /** 043 * Default constructor uses default MBeanServer. 044 * 045 * @param domain domain part of the object name 046 */ 047 public JmxRegisterCallback(String domain) { 048 this(ManagementFactory.getPlatformMBeanServer(), domain); 049 } 050 051 /** 052 * Constructor using specific MBeanServer. 053 * 054 * @param mBeanServer specific MBeanServer 055 * @param domain domain part of the object name 056 */ 057 public JmxRegisterCallback(MBeanServer mBeanServer, String domain) { 058 assert domain != null && !(domain.isEmpty()); 059 this.mBeanServer = mBeanServer; 060 this.domain = domain; 061 } 062 063 @Override 064 public synchronized void initialize(Manager manager) { 065 if (this.manager != null) { 066 throw new IllegalStateException("Callback was already initialized"); 067 } 068 this.manager = manager; 069 if (registerExisting) { 070 for (Simon simon : manager.getSimons(null)) { 071 register(simon); 072 } 073 } 074 } 075 076 /** 077 * After Simon is created respective MX bean is registered for it according to its type. 078 * 079 * @param simon created Simon 080 */ 081 @Override 082 public final void onSimonCreated(Simon simon) { 083 if (simon.getName() == null) { 084 return; 085 } 086 register(simon); 087 } 088 089 /** 090 * When the Simon is destroyed, its MX bean is unregistered. 091 * 092 * @param simon destroyed Simon 093 */ 094 @Override 095 public final void onSimonDestroyed(Simon simon) { 096 String name = constructObjectName(simon); 097 unregisterSimon(name); 098 } 099 100 private synchronized void unregisterSimon(String name) { 101 try { 102 ObjectName objectName = new ObjectName(name); 103 mBeanServer.unregisterMBean(objectName); 104 registeredNames.remove(name); 105 onManagerMessage("Unregistered Simon with the name: " + objectName); 106 } catch (JMException e) { 107 onManagerWarning("JMX unregistration failed for: " + name, e); 108 } 109 } 110 111 /** When the manager is cleared, all MX beans for its Simons are unregistered. */ 112 @Override 113 public final void onManagerClear() { 114 unregisterAllSimons(); 115 } 116 117 /** Unregister all previously registered Simons. */ 118 private synchronized void unregisterAllSimons() { 119 Iterator<String> namesIter = registeredNames.iterator(); 120 while (namesIter.hasNext()) { 121 String name = namesIter.next(); 122 try { 123 ObjectName objectName = new ObjectName(name); 124 mBeanServer.unregisterMBean(objectName); 125 // I have to use iterator.remove() - hence no common method for clearManager() and simonDestroyed(simon) 126 namesIter.remove(); 127 onManagerMessage("Unregistered Simon with the name: " + objectName); 128 } catch (JMException e) { 129 onManagerWarning("JMX unregistration failed for: " + name, e); 130 } 131 } 132 } 133 134 /** Stop operation and clear all registered beans. Callback should be removed from Simon Manager. */ 135 public void cleanup() { 136 unregisterAllSimons(); 137 } 138 139 /** 140 * Method registering Simon MX Bean - can not be overridden, but can be used in subclasses. 141 * 142 * @param simon Simon MX Bean to be registered 143 * @since 3.1 144 */ 145 protected final void register(Simon simon) { 146 Object mBean = constructObject(simon); 147 String name = constructObjectName(simon); 148 registerSimonBean(mBean, name); 149 } 150 151 private synchronized void registerSimonBean(Object simonBean, String name) { 152 if (simonBean != null && name != null) { 153 try { 154 ObjectName objectName = new ObjectName(name); 155 if (mBeanServer.isRegistered(objectName)) { 156 mBeanServer.unregisterMBean(objectName); 157 } else { 158 registeredNames.add(name); 159 } 160 mBeanServer.registerMBean(simonBean, objectName); 161 onManagerMessage("Simon registered under the name: " + objectName); 162 } catch (JMException e) { 163 onManagerWarning("JMX registration failed for: " + name, e); 164 registeredNames.remove(name); 165 } 166 } 167 } 168 169 /** 170 * Constructs JMX object from Simon object. Method can be overridden. 171 * 172 * @param simon Simon object 173 * @return JMX object (=MBean) representing the Simon 174 */ 175 protected SimonSuperMXBean constructObject(Simon simon) { 176 SimonSuperMXBean simonMxBean; 177 if (simon instanceof Counter) { 178 simonMxBean = new CounterMXBeanImpl((Counter) simon); 179 } else if (simon instanceof Stopwatch) { 180 simonMxBean = new StopwatchMXBeanImpl((Stopwatch) simon); 181 } else { 182 onManagerWarning("Unknown type of Simon! " + simon, null); 183 simonMxBean = null; 184 } 185 return simonMxBean; 186 } 187 188 /** 189 * Constructs JMX object name from Simon object. Method can be overridden. 190 * 191 * @param simon Simon object 192 * @return object name in String form 193 */ 194 protected String constructObjectName(Simon simon) { 195 return domain + ":type=" + simonType(simon) + ",name=" + simon.getName(); 196 } 197 198 /** 199 * Returns type of the simon as defined in {@link SimonInfo#COUNTER}, 200 * {@link SimonInfo#STOPWATCH} or {@link SimonInfo#UNKNOWN}. 201 * 202 * @param simon Simon object 203 * @return type of the Simon as String 204 */ 205 protected String simonType(Simon simon) { 206 String type = SimonInfo.UNKNOWN; 207 if (simon instanceof Counter) { 208 type = SimonInfo.COUNTER; 209 } else if (simon instanceof Stopwatch) { 210 type = SimonInfo.STOPWATCH; 211 } 212 return type; 213 } 214 215 String getDomain() { 216 return domain; 217 } 218 219 MBeanServer getBeanServer() { 220 return mBeanServer; 221 } 222 223 /** 224 * If set to true before initialization callback registers all Simons already existing in the Manager 225 * during initialization. 226 * 227 * @param registerExisting true if all already existing simons should be registered 228 */ 229 public void setRegisterExisting(boolean registerExisting) { 230 this.registerExisting = registerExisting; 231 } 232}