001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jmx.support; 018 019import java.util.LinkedHashSet; 020import java.util.Set; 021import javax.management.InstanceAlreadyExistsException; 022import javax.management.InstanceNotFoundException; 023import javax.management.JMException; 024import javax.management.MBeanServer; 025import javax.management.ObjectInstance; 026import javax.management.ObjectName; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import org.springframework.core.Constants; 032import org.springframework.util.Assert; 033 034/** 035 * Provides supporting infrastructure for registering MBeans with an 036 * {@link javax.management.MBeanServer}. The behavior when encountering 037 * an existing MBean at a given {@link ObjectName} is fully configurable 038 * allowing for flexible registration settings. 039 * 040 * <p>All registered MBeans are tracked and can be unregistered by calling 041 * the #{@link #unregisterBeans()} method. 042 * 043 * <p>Sub-classes can receive notifications when an MBean is registered or 044 * unregistered by overriding the {@link #onRegister(ObjectName)} and 045 * {@link #onUnregister(ObjectName)} methods respectively. 046 * 047 * <p>By default, the registration process will fail if attempting to 048 * register an MBean using a {@link javax.management.ObjectName} that is 049 * already used. 050 * 051 * <p>By setting the {@link #setRegistrationPolicy(RegistrationPolicy) registrationPolicy} 052 * property to {@link RegistrationPolicy#IGNORE_EXISTING} the registration process 053 * will simply ignore existing MBeans leaving them registered. This is useful in settings 054 * where multiple applications want to share a common MBean in a shared {@link MBeanServer}. 055 * 056 * <p>Setting {@link #setRegistrationPolicy(RegistrationPolicy) registrationPolicy} property 057 * to {@link RegistrationPolicy#REPLACE_EXISTING} will cause existing MBeans to be replaced 058 * during registration if necessary. This is useful in situations where you can't guarantee 059 * the state of your {@link MBeanServer}. 060 * 061 * @author Rob Harrop 062 * @author Juergen Hoeller 063 * @author Phillip Webb 064 * @since 2.0 065 * @see #setServer 066 * @see #setRegistrationPolicy 067 * @see org.springframework.jmx.export.MBeanExporter 068 */ 069public class MBeanRegistrationSupport { 070 071 /** 072 * Constant indicating that registration should fail when 073 * attempting to register an MBean under a name that already exists. 074 * <p>This is the default registration behavior. 075 * @deprecated since Spring 3.2, in favor of {@link RegistrationPolicy#FAIL_ON_EXISTING} 076 */ 077 @Deprecated 078 public static final int REGISTRATION_FAIL_ON_EXISTING = 0; 079 080 /** 081 * Constant indicating that registration should ignore the affected MBean 082 * when attempting to register an MBean under a name that already exists. 083 * @deprecated since Spring 3.2, in favor of {@link RegistrationPolicy#IGNORE_EXISTING} 084 */ 085 @Deprecated 086 public static final int REGISTRATION_IGNORE_EXISTING = 1; 087 088 /** 089 * Constant indicating that registration should replace the affected MBean 090 * when attempting to register an MBean under a name that already exists. 091 * @deprecated since Spring 3.2, in favor of {@link RegistrationPolicy#REPLACE_EXISTING} 092 */ 093 @Deprecated 094 public static final int REGISTRATION_REPLACE_EXISTING = 2; 095 096 097 /** 098 * Constants for this class. 099 */ 100 private static final Constants constants = new Constants(MBeanRegistrationSupport.class); 101 102 /** 103 * {@code Log} instance for this class. 104 */ 105 protected final Log logger = LogFactory.getLog(getClass()); 106 107 /** 108 * The {@code MBeanServer} instance being used to register beans. 109 */ 110 protected MBeanServer server; 111 112 /** 113 * The beans that have been registered by this exporter. 114 */ 115 private final Set<ObjectName> registeredBeans = new LinkedHashSet<ObjectName>(); 116 117 /** 118 * The policy used when registering an MBean and finding that it already exists. 119 * By default an exception is raised. 120 */ 121 private RegistrationPolicy registrationPolicy = RegistrationPolicy.FAIL_ON_EXISTING; 122 123 124 /** 125 * Specify the {@code MBeanServer} instance with which all beans should 126 * be registered. The {@code MBeanExporter} will attempt to locate an 127 * existing {@code MBeanServer} if none is supplied. 128 */ 129 public void setServer(MBeanServer server) { 130 this.server = server; 131 } 132 133 /** 134 * Return the {@code MBeanServer} that the beans will be registered with. 135 */ 136 public final MBeanServer getServer() { 137 return this.server; 138 } 139 140 /** 141 * Set the registration behavior by the name of the corresponding constant, 142 * e.g. "REGISTRATION_IGNORE_EXISTING". 143 * @see #setRegistrationBehavior 144 * @see #REGISTRATION_FAIL_ON_EXISTING 145 * @see #REGISTRATION_IGNORE_EXISTING 146 * @see #REGISTRATION_REPLACE_EXISTING 147 * @deprecated since Spring 3.2, in favor of {@link #setRegistrationPolicy(RegistrationPolicy)} 148 */ 149 @Deprecated 150 public void setRegistrationBehaviorName(String registrationBehavior) { 151 setRegistrationBehavior(constants.asNumber(registrationBehavior).intValue()); 152 } 153 154 /** 155 * Specify what action should be taken when attempting to register an MBean 156 * under an {@link javax.management.ObjectName} that already exists. 157 * <p>Default is REGISTRATION_FAIL_ON_EXISTING. 158 * @see #setRegistrationBehaviorName(String) 159 * @see #REGISTRATION_FAIL_ON_EXISTING 160 * @see #REGISTRATION_IGNORE_EXISTING 161 * @see #REGISTRATION_REPLACE_EXISTING 162 * @deprecated since Spring 3.2, in favor of {@link #setRegistrationPolicy(RegistrationPolicy)} 163 */ 164 @Deprecated 165 public void setRegistrationBehavior(int registrationBehavior) { 166 setRegistrationPolicy(RegistrationPolicy.valueOf(registrationBehavior)); 167 } 168 169 /** 170 * The policy to use when attempting to register an MBean 171 * under an {@link javax.management.ObjectName} that already exists. 172 * @param registrationPolicy the policy to use 173 * @since 3.2 174 */ 175 public void setRegistrationPolicy(RegistrationPolicy registrationPolicy) { 176 Assert.notNull(registrationPolicy, "RegistrationPolicy must not be null"); 177 this.registrationPolicy = registrationPolicy; 178 } 179 180 181 /** 182 * Actually register the MBean with the server. The behavior when encountering 183 * an existing MBean can be configured using the {@link #setRegistrationBehavior(int)} 184 * and {@link #setRegistrationBehaviorName(String)} methods. 185 * @param mbean the MBean instance 186 * @param objectName the suggested ObjectName for the MBean 187 * @throws JMException if the registration failed 188 */ 189 protected void doRegister(Object mbean, ObjectName objectName) throws JMException { 190 ObjectName actualObjectName; 191 192 synchronized (this.registeredBeans) { 193 ObjectInstance registeredBean = null; 194 try { 195 registeredBean = this.server.registerMBean(mbean, objectName); 196 } 197 catch (InstanceAlreadyExistsException ex) { 198 if (this.registrationPolicy == RegistrationPolicy.IGNORE_EXISTING) { 199 if (logger.isDebugEnabled()) { 200 logger.debug("Ignoring existing MBean at [" + objectName + "]"); 201 } 202 } 203 else if (this.registrationPolicy == RegistrationPolicy.REPLACE_EXISTING) { 204 try { 205 if (logger.isDebugEnabled()) { 206 logger.debug("Replacing existing MBean at [" + objectName + "]"); 207 } 208 this.server.unregisterMBean(objectName); 209 registeredBean = this.server.registerMBean(mbean, objectName); 210 } 211 catch (InstanceNotFoundException ex2) { 212 if (logger.isErrorEnabled()) { 213 logger.error("Unable to replace existing MBean at [" + objectName + "]", ex2); 214 } 215 throw ex; 216 } 217 } 218 else { 219 throw ex; 220 } 221 } 222 223 // Track registration and notify listeners. 224 actualObjectName = (registeredBean != null ? registeredBean.getObjectName() : null); 225 if (actualObjectName == null) { 226 actualObjectName = objectName; 227 } 228 this.registeredBeans.add(actualObjectName); 229 } 230 231 onRegister(actualObjectName, mbean); 232 } 233 234 /** 235 * Unregisters all beans that have been registered by an instance of this class. 236 */ 237 protected void unregisterBeans() { 238 Set<ObjectName> snapshot; 239 synchronized (this.registeredBeans) { 240 snapshot = new LinkedHashSet<ObjectName>(this.registeredBeans); 241 } 242 if (!snapshot.isEmpty()) { 243 logger.info("Unregistering JMX-exposed beans"); 244 for (ObjectName objectName : snapshot) { 245 doUnregister(objectName); 246 } 247 } 248 } 249 250 /** 251 * Actually unregister the specified MBean from the server. 252 * @param objectName the suggested ObjectName for the MBean 253 */ 254 protected void doUnregister(ObjectName objectName) { 255 boolean actuallyUnregistered = false; 256 257 synchronized (this.registeredBeans) { 258 if (this.registeredBeans.remove(objectName)) { 259 try { 260 // MBean might already have been unregistered by an external process 261 if (this.server.isRegistered(objectName)) { 262 this.server.unregisterMBean(objectName); 263 actuallyUnregistered = true; 264 } 265 else { 266 if (logger.isWarnEnabled()) { 267 logger.warn("Could not unregister MBean [" + objectName + "] as said MBean " + 268 "is not registered (perhaps already unregistered by an external process)"); 269 } 270 } 271 } 272 catch (JMException ex) { 273 if (logger.isErrorEnabled()) { 274 logger.error("Could not unregister MBean [" + objectName + "]", ex); 275 } 276 } 277 } 278 } 279 280 if (actuallyUnregistered) { 281 onUnregister(objectName); 282 } 283 } 284 285 /** 286 * Return the {@link ObjectName ObjectNames} of all registered beans. 287 */ 288 protected final ObjectName[] getRegisteredObjectNames() { 289 synchronized (this.registeredBeans) { 290 return this.registeredBeans.toArray(new ObjectName[this.registeredBeans.size()]); 291 } 292 } 293 294 295 /** 296 * Called when an MBean is registered under the given {@link ObjectName}. Allows 297 * subclasses to perform additional processing when an MBean is registered. 298 * <p>The default implementation delegates to {@link #onRegister(ObjectName)}. 299 * @param objectName the actual {@link ObjectName} that the MBean was registered with 300 * @param mbean the registered MBean instance 301 */ 302 protected void onRegister(ObjectName objectName, Object mbean) { 303 onRegister(objectName); 304 } 305 306 /** 307 * Called when an MBean is registered under the given {@link ObjectName}. Allows 308 * subclasses to perform additional processing when an MBean is registered. 309 * <p>The default implementation is empty. Can be overridden in subclasses. 310 * @param objectName the actual {@link ObjectName} that the MBean was registered with 311 */ 312 protected void onRegister(ObjectName objectName) { 313 } 314 315 /** 316 * Called when an MBean is unregistered under the given {@link ObjectName}. Allows 317 * subclasses to perform additional processing when an MBean is unregistered. 318 * <p>The default implementation is empty. Can be overridden in subclasses. 319 * @param objectName the {@link ObjectName} that the MBean was registered with 320 */ 321 protected void onUnregister(ObjectName objectName) { 322 } 323 324}