1 /* 2 * Copyright 2002-2013 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.jmx.support; 18 19 import java.util.Collections; 20 import java.util.LinkedHashSet; 21 import java.util.Set; 22 import javax.management.InstanceAlreadyExistsException; 23 import javax.management.InstanceNotFoundException; 24 import javax.management.JMException; 25 import javax.management.MBeanServer; 26 import javax.management.ObjectInstance; 27 import javax.management.ObjectName; 28 29 import org.apache.commons.logging.Log; 30 import org.apache.commons.logging.LogFactory; 31 32 import org.springframework.core.Constants; 33 34 /** 35 * Provides supporting infrastructure for registering MBeans with an 36 * {@link javax.management.MBeanServer}. The behavior when encountering 37 * an existing MBean at a given {@link ObjectName} is fully configurable 38 * allowing for flexible registration settings. 39 * 40 * <p>All registered MBeans are tracked and can be unregistered by calling 41 * the #{@link #unregisterBeans()} method. 42 * 43 * <p>Sub-classes can receive notifications when an MBean is registered or 44 * unregistered by overriding the {@link #onRegister(ObjectName)} and 45 * {@link #onUnregister(ObjectName)} methods respectively. 46 * 47 * <p>By default, the registration process will fail if attempting to 48 * register an MBean using a {@link javax.management.ObjectName} that is 49 * already used. 50 * 51 * <p>By setting the {@link #setRegistrationBehaviorName(String) registrationBehaviorName} 52 * property to <code>REGISTRATION_IGNORE_EXISTING</code> the registration process 53 * will simply ignore existing MBeans leaving them registered. This is useful in settings 54 * where multiple applications want to share a common MBean in a shared {@link MBeanServer}. 55 * 56 * <p>Setting {@link #setRegistrationBehaviorName(String) registrationBehaviorName} property 57 * to <code>REGISTRATION_REPLACE_EXISTING</code> will cause existing MBeans to be replaced 58 * during registration if necessary. This is useful in situations where you can't guarantee 59 * the state of your {@link MBeanServer}. 60 * 61 * @author Rob Harrop 62 * @author Juergen Hoeller 63 * @since 2.0 64 * @see #setServer 65 * @see #setRegistrationBehaviorName 66 * @see org.springframework.jmx.export.MBeanExporter 67 */ 68 public class MBeanRegistrationSupport { 69 70 /** 71 * Constant indicating that registration should fail when 72 * attempting to register an MBean under a name that already exists. 73 * <p>This is the default registration behavior. 74 */ 75 public static final int REGISTRATION_FAIL_ON_EXISTING = 0; 76 77 /** 78 * Constant indicating that registration should ignore the affected MBean 79 * when attempting to register an MBean under a name that already exists. 80 */ 81 public static final int REGISTRATION_IGNORE_EXISTING = 1; 82 83 /** 84 * Constant indicating that registration should replace the affected MBean 85 * when attempting to register an MBean under a name that already exists. 86 */ 87 public static final int REGISTRATION_REPLACE_EXISTING = 2; 88 89 90 /** 91 * Constants for this class. 92 */ 93 private static final Constants constants = new Constants(MBeanRegistrationSupport.class); 94 95 /** 96 * {@code Log} instance for this class. 97 */ 98 protected final Log logger = LogFactory.getLog(getClass()); 99 100 /** 101 * The {@code MBeanServer} instance being used to register beans. 102 */ 103 protected MBeanServer server; 104 105 /** 106 * The beans that have been registered by this exporter. 107 */ 108 protected final Set<ObjectName> registeredBeans = Collections.synchronizedSet(new LinkedHashSet<ObjectName>()); 109 110 /** 111 * The action take when registering an MBean and finding that it already exists. 112 * By default an exception is raised. 113 */ 114 private int registrationBehavior = REGISTRATION_FAIL_ON_EXISTING; 115 116 117 /** 118 * Specify the {@code MBeanServer} instance with which all beans should 119 * be registered. The {@code MBeanExporter} will attempt to locate an 120 * existing {@code MBeanServer} if none is supplied. 121 */ setServer(MBeanServer server)122 public void setServer(MBeanServer server) { 123 this.server = server; 124 } 125 126 /** 127 * Return the {@code MBeanServer} that the beans will be registered with. 128 */ getServer()129 public final MBeanServer getServer() { 130 return this.server; 131 } 132 133 /** 134 * Set the registration behavior by the name of the corresponding constant, 135 * e.g. "REGISTRATION_IGNORE_EXISTING". 136 * @see #setRegistrationBehavior 137 * @see #REGISTRATION_FAIL_ON_EXISTING 138 * @see #REGISTRATION_IGNORE_EXISTING 139 * @see #REGISTRATION_REPLACE_EXISTING 140 */ setRegistrationBehaviorName(String registrationBehavior)141 public void setRegistrationBehaviorName(String registrationBehavior) { 142 setRegistrationBehavior(constants.asNumber(registrationBehavior).intValue()); 143 } 144 145 /** 146 * Specify what action should be taken when attempting to register an MBean 147 * under an {@link javax.management.ObjectName} that already exists. 148 * <p>Default is REGISTRATION_FAIL_ON_EXISTING. 149 * @see #setRegistrationBehaviorName(String) 150 * @see #REGISTRATION_FAIL_ON_EXISTING 151 * @see #REGISTRATION_IGNORE_EXISTING 152 * @see #REGISTRATION_REPLACE_EXISTING 153 */ setRegistrationBehavior(int registrationBehavior)154 public void setRegistrationBehavior(int registrationBehavior) { 155 this.registrationBehavior = registrationBehavior; 156 } 157 158 159 /** 160 * Actually register the MBean with the server. The behavior when encountering 161 * an existing MBean can be configured using the {@link #setRegistrationBehavior(int)} 162 * and {@link #setRegistrationBehaviorName(String)} methods. 163 * @param mbean the MBean instance 164 * @param objectName the suggested ObjectName for the MBean 165 * @throws JMException if the registration failed 166 */ doRegister(Object mbean, ObjectName objectName)167 protected void doRegister(Object mbean, ObjectName objectName) throws JMException { 168 ObjectInstance registeredBean = null; 169 try { 170 registeredBean = this.server.registerMBean(mbean, objectName); 171 } 172 catch (InstanceAlreadyExistsException ex) { 173 if (this.registrationBehavior == REGISTRATION_IGNORE_EXISTING) { 174 if (logger.isDebugEnabled()) { 175 logger.debug("Ignoring existing MBean at [" + objectName + "]"); 176 } 177 } 178 else if (this.registrationBehavior == REGISTRATION_REPLACE_EXISTING) { 179 try { 180 if (logger.isDebugEnabled()) { 181 logger.debug("Replacing existing MBean at [" + objectName + "]"); 182 } 183 this.server.unregisterMBean(objectName); 184 registeredBean = this.server.registerMBean(mbean, objectName); 185 } 186 catch (InstanceNotFoundException ex2) { 187 logger.error("Unable to replace existing MBean at [" + objectName + "]", ex2); 188 throw ex; 189 } 190 } 191 else { 192 throw ex; 193 } 194 } 195 196 // Track registration and notify listeners. 197 ObjectName actualObjectName = (registeredBean != null ? registeredBean.getObjectName() : null); 198 if (actualObjectName == null) { 199 actualObjectName = objectName; 200 } 201 this.registeredBeans.add(actualObjectName); 202 onRegister(actualObjectName, mbean); 203 } 204 205 /** 206 * Unregisters all beans that have been registered by an instance of this class. 207 */ unregisterBeans()208 protected void unregisterBeans() { 209 for (ObjectName objectName : new LinkedHashSet<ObjectName>(this.registeredBeans)) { 210 doUnregister(objectName); 211 } 212 } 213 214 /** 215 * Actually unregister the specified MBean from the server. 216 * @param objectName the suggested ObjectName for the MBean 217 */ doUnregister(ObjectName objectName)218 protected void doUnregister(ObjectName objectName) { 219 try { 220 // MBean might already have been unregistered by an external process. 221 if (this.server.isRegistered(objectName)) { 222 this.server.unregisterMBean(objectName); 223 onUnregister(objectName); 224 } 225 else { 226 if (logger.isWarnEnabled()) { 227 logger.warn("Could not unregister MBean [" + objectName + "] as said MBean " + 228 "is not registered (perhaps already unregistered by an external process)"); 229 } 230 } 231 } 232 catch (JMException ex) { 233 if (logger.isErrorEnabled()) { 234 logger.error("Could not unregister MBean [" + objectName + "]", ex); 235 } 236 } 237 this.registeredBeans.remove(objectName); 238 } 239 240 /** 241 * Return the {@link ObjectName ObjectNames} of all registered beans. 242 */ getRegisteredObjectNames()243 protected final ObjectName[] getRegisteredObjectNames() { 244 return this.registeredBeans.toArray(new ObjectName[this.registeredBeans.size()]); 245 } 246 247 248 /** 249 * Called when an MBean is registered under the given {@link ObjectName}. Allows 250 * subclasses to perform additional processing when an MBean is registered. 251 * <p>The default implementation delegates to {@link #onRegister(ObjectName)}. 252 * @param objectName the actual {@link ObjectName} that the MBean was registered with 253 * @param mbean the registered MBean instance 254 */ onRegister(ObjectName objectName, Object mbean)255 protected void onRegister(ObjectName objectName, Object mbean) { 256 onRegister(objectName); 257 } 258 259 /** 260 * Called when an MBean is registered under the given {@link ObjectName}. Allows 261 * subclasses to perform additional processing when an MBean is registered. 262 * <p>The default implementation is empty. Can be overridden in subclasses. 263 * @param objectName the actual {@link ObjectName} that the MBean was registered with 264 */ onRegister(ObjectName objectName)265 protected void onRegister(ObjectName objectName) { 266 } 267 268 /** 269 * Called when an MBean is unregistered under the given {@link ObjectName}. Allows 270 * subclasses to perform additional processing when an MBean is unregistered. 271 * <p>The default implementation is empty. Can be overridden in subclasses. 272 * @param objectName the {@link ObjectName} that the MBean was registered with 273 */ onUnregister(ObjectName objectName)274 protected void onUnregister(ObjectName objectName) { 275 } 276 277 } 278