001: /*
002: * Copyright 2002-2006 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: * http://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:
017: package org.springframework.jmx.support;
018:
019: import java.util.HashSet;
020: import java.util.Iterator;
021: import java.util.Set;
022:
023: import javax.management.InstanceAlreadyExistsException;
024: import javax.management.InstanceNotFoundException;
025: import javax.management.JMException;
026: import javax.management.MBeanServer;
027: import javax.management.ObjectInstance;
028: import javax.management.ObjectName;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import org.springframework.core.Constants;
034:
035: /**
036: * Provides supporting infrastructure for registering MBeans with an
037: * {@link javax.management.MBeanServer}. The behavior when encountering
038: * an existing MBean at a given {@link ObjectName} is fully configurable
039: * allowing for flexible registration settings.
040: *
041: * <p>All registered MBeans are tracked and can be unregistered by calling
042: * the #{@link #unregisterBeans()} method.
043: *
044: * <p>Sub-classes can receive notifications when an MBean is registered or
045: * unregistered by overriding the {@link #onRegister(ObjectName)} and
046: * {@link #onUnregister(ObjectName)} methods respectively.
047: *
048: * <p>By default, the registration process will fail if attempting to
049: * register an MBean using a {@link javax.management.ObjectName} that is
050: * already used.
051: *
052: * <p>By setting the {@link #setRegistrationBehaviorName(String) registrationBehaviorName}
053: * property to <code>REGISTRATION_IGNORE_EXISTING</code> the registration process
054: * will simply ignore existing MBeans leaving them registered. This is useful in settings
055: * where multiple applications want to share a common MBean in a shared {@link MBeanServer}.
056: *
057: * <p>Setting {@link #setRegistrationBehaviorName(String) registrationBehaviorName} property
058: * to <code>REGISTRATION_REPLACE_EXISTING</code> will cause existing MBeans to be replaced
059: * during registration if necessary. This is useful in situations where you can't guarantee
060: * the state of your {@link MBeanServer}.
061: *
062: * @author Rob Harrop
063: * @author Juergen Hoeller
064: * @since 2.0
065: * @see #setServer
066: * @see #setRegistrationBehaviorName
067: * @see org.springframework.jmx.export.MBeanExporter
068: */
069: public 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: */
076: public static final int REGISTRATION_FAIL_ON_EXISTING = 0;
077:
078: /**
079: * Constant indicating that registration should ignore the affected MBean
080: * when attempting to register an MBean under a name that already exists.
081: */
082: public static final int REGISTRATION_IGNORE_EXISTING = 1;
083:
084: /**
085: * Constant indicating that registration should replace the affected MBean
086: * when attempting to register an MBean under a name that already exists.
087: */
088: public static final int REGISTRATION_REPLACE_EXISTING = 2;
089:
090: /**
091: * Constants for this class.
092: */
093: private static final Constants constants = new Constants(
094: MBeanRegistrationSupport.class);
095:
096: /**
097: * <code>Log</code> instance for this class.
098: */
099: protected final Log logger = LogFactory.getLog(getClass());
100:
101: /**
102: * The <code>MBeanServer</code> instance being used to register beans.
103: */
104: protected MBeanServer server;
105:
106: /**
107: * The beans that have been registered by this exporter.
108: */
109: protected Set registeredBeans = new HashSet();
110:
111: /**
112: * The action take when registering an MBean and finding that it already exists.
113: * By default an exception is raised.
114: */
115: private int registrationBehavior = REGISTRATION_FAIL_ON_EXISTING;
116:
117: /**
118: * Specify the <code>MBeanServer</code> instance with which all beans should
119: * be registered. The <code>MBeanExporter</code> will attempt to locate an
120: * existing <code>MBeanServer</code> if none is supplied.
121: */
122: public void setServer(MBeanServer server) {
123: this .server = server;
124: }
125:
126: /**
127: * Set the registration behavior by the name of the corresponding constant,
128: * e.g. "REGISTRATION_IGNORE_EXISTING".
129: * @see #setRegistrationBehavior
130: * @see #REGISTRATION_FAIL_ON_EXISTING
131: * @see #REGISTRATION_IGNORE_EXISTING
132: * @see #REGISTRATION_REPLACE_EXISTING
133: */
134: public void setRegistrationBehaviorName(String registrationBehavior) {
135: setRegistrationBehavior(constants
136: .asNumber(registrationBehavior).intValue());
137: }
138:
139: /**
140: * Specify what action should be taken when attempting to register an MBean
141: * under an {@link javax.management.ObjectName} that already exists.
142: * <p>Default is REGISTRATION_FAIL_ON_EXISTING.
143: * @see #setRegistrationBehaviorName(String)
144: * @see #REGISTRATION_FAIL_ON_EXISTING
145: * @see #REGISTRATION_IGNORE_EXISTING
146: * @see #REGISTRATION_REPLACE_EXISTING
147: */
148: public void setRegistrationBehavior(int registrationBehavior) {
149: this .registrationBehavior = registrationBehavior;
150: }
151:
152: /**
153: * Actually register the MBean with the server. The behavior when encountering
154: * an existing MBean can be configured using the {@link #setRegistrationBehavior(int)}
155: * and {@link #setRegistrationBehaviorName(String)} methods.
156: * @throws JMException if the registration failed
157: */
158: protected void doRegister(Object mbean, ObjectName objectName)
159: throws JMException {
160: ObjectInstance registeredBean = null;
161: try {
162: registeredBean = this .server.registerMBean(mbean,
163: objectName);
164: } catch (InstanceAlreadyExistsException ex) {
165: if (this .registrationBehavior == REGISTRATION_IGNORE_EXISTING) {
166: if (logger.isDebugEnabled()) {
167: logger.debug("Ignoring existing MBean at ["
168: + objectName + "]");
169: }
170: } else if (this .registrationBehavior == REGISTRATION_REPLACE_EXISTING) {
171: try {
172: if (logger.isDebugEnabled()) {
173: logger.debug("Replacing existing MBean at ["
174: + objectName + "]");
175: }
176: this .server.unregisterMBean(objectName);
177: registeredBean = this .server.registerMBean(mbean,
178: objectName);
179: } catch (InstanceNotFoundException ex2) {
180: logger.error(
181: "Unable to replace existing MBean at ["
182: + objectName + "]", ex2);
183: throw ex;
184: }
185: } else {
186: throw ex;
187: }
188: }
189:
190: // Track registration and notify listeners.
191: ObjectName actualObjectName = (registeredBean != null ? registeredBean
192: .getObjectName()
193: : null);
194: if (actualObjectName == null) {
195: actualObjectName = objectName;
196: }
197: this .registeredBeans.add(actualObjectName);
198: onRegister(actualObjectName);
199: }
200:
201: /**
202: * Unregisters all beans that have been registered by an instance of this class.
203: */
204: protected void unregisterBeans() {
205: for (Iterator it = this .registeredBeans.iterator(); it
206: .hasNext();) {
207: ObjectName objectName = (ObjectName) it.next();
208: try {
209: // MBean might already have been unregistered by an external process.
210: if (this .server.isRegistered(objectName)) {
211: this .server.unregisterMBean(objectName);
212: onUnregister(objectName);
213: } else {
214: if (logger.isWarnEnabled()) {
215: logger
216: .warn("Could not unregister MBean ["
217: + objectName
218: + "] as said MBean "
219: + "is not registered (perhaps already unregistered by an external process)");
220: }
221: }
222: } catch (JMException ex) {
223: if (logger.isErrorEnabled()) {
224: logger.error("Could not unregister MBean ["
225: + objectName + "]", ex);
226: }
227: }
228: }
229: this .registeredBeans.clear();
230: }
231:
232: /**
233: * Called when an MBean is registered under the given {@link ObjectName}. Allows
234: * subclasses to perform additional processing when an MBean is registered.
235: * @param objectName the {@link ObjectName} of the MBean that was registered.
236: */
237: protected void onRegister(ObjectName objectName) {
238: }
239:
240: /**
241: * Called when an MBean is unregistered under the given {@link ObjectName}. Allows
242: * subclasses to perform additional processing when an MBean is unregistered.
243: * @param objectName the {@link ObjectName} of the MBean that was unregistered.
244: */
245: protected void onUnregister(ObjectName objectName) {
246: }
247:
248: }
|