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.io.IOException;
020: import java.util.Map;
021: import java.util.Properties;
022:
023: import javax.management.JMException;
024: import javax.management.MBeanServer;
025: import javax.management.MalformedObjectNameException;
026: import javax.management.ObjectName;
027: import javax.management.remote.JMXConnectorServer;
028: import javax.management.remote.JMXConnectorServerFactory;
029: import javax.management.remote.JMXServiceURL;
030:
031: import org.springframework.beans.factory.DisposableBean;
032: import org.springframework.beans.factory.FactoryBean;
033: import org.springframework.beans.factory.InitializingBean;
034: import org.springframework.jmx.JmxException;
035:
036: /**
037: * <code>FactoryBean</code> that creates a JSR-160 <code>JMXConnectorServer</code>,
038: * optionally registers it with the <code>MBeanServer</code> and then starts it.
039: *
040: * <p>The <code>JMXConnectorServer</code> can be started in a separate thread by setting the
041: * <code>threaded</code> property to <code>true</code>. You can configure this thread to be a
042: * daemon thread by setting the <code>daemon</code> property to <code>true</code>.
043: *
044: * <p>The <code>JMXConnectorServer</code> is correctly shutdown when an instance of this
045: * class is destroyed on shutdown of the containing <code>ApplicationContext</code>.
046: *
047: * @author Rob Harrop
048: * @author Juergen Hoeller
049: * @since 1.2
050: * @see FactoryBean
051: * @see JMXConnectorServer
052: * @see MBeanServer
053: */
054: public class ConnectorServerFactoryBean extends
055: MBeanRegistrationSupport implements FactoryBean,
056: InitializingBean, DisposableBean {
057:
058: /** The default service URL */
059: public static final String DEFAULT_SERVICE_URL = "service:jmx:jmxmp://localhost:9875";
060:
061: private String serviceUrl = DEFAULT_SERVICE_URL;
062:
063: private Map environment;
064:
065: private ObjectName objectName;
066:
067: private boolean threaded = false;
068:
069: private boolean daemon = false;
070:
071: private JMXConnectorServer connectorServer;
072:
073: /**
074: * Set the service URL for the <code>JMXConnectorServer</code>.
075: */
076: public void setServiceUrl(String serviceUrl) {
077: this .serviceUrl = serviceUrl;
078: }
079:
080: /**
081: * Set the environment properties used to construct the <code>JMXConnectorServer</code>
082: * as <code>java.util.Properties</code> (String key/value pairs).
083: */
084: public void setEnvironment(Properties environment) {
085: this .environment = environment;
086: }
087:
088: /**
089: * Set the environment properties used to construct the <code>JMXConnector</code>
090: * as a <code>Map</code> of String keys and arbitrary Object values.
091: */
092: public void setEnvironmentMap(Map environment) {
093: this .environment = environment;
094: }
095:
096: /**
097: * Set the <code>ObjectName</code> used to register the <code>JMXConnectorServer</code>
098: * itself with the <code>MBeanServer</code>, as <code>ObjectName</code> instance
099: * or as <code>String</code>.
100: * @throws MalformedObjectNameException if the <code>ObjectName</code> is malformed
101: */
102: public void setObjectName(Object objectName)
103: throws MalformedObjectNameException {
104: this .objectName = ObjectNameManager.getInstance(objectName);
105: }
106:
107: /**
108: * Set whether the <code>JMXConnectorServer</code> should be started in a separate thread.
109: */
110: public void setThreaded(boolean threaded) {
111: this .threaded = threaded;
112: }
113:
114: /**
115: * Set whether any threads started for the <code>JMXConnectorServer</code> should be
116: * started as daemon threads.
117: */
118: public void setDaemon(boolean daemon) {
119: this .daemon = daemon;
120: }
121:
122: /**
123: * Start the connector server. If the <code>threaded</code> flag is set to <code>true</code>,
124: * the <code>JMXConnectorServer</code> will be started in a separate thread.
125: * If the <code>daemon</code> flag is set to <code>true</code>, that thread will be
126: * started as a daemon thread.
127: * @throws JMException if a problem occured when registering the connector server
128: * with the <code>MBeanServer</code>
129: * @throws IOException if there is a problem starting the connector server
130: */
131: public void afterPropertiesSet() throws JMException, IOException {
132: if (this .server == null) {
133: this .server = JmxUtils.locateMBeanServer();
134: }
135:
136: // Create the JMX service URL.
137: JMXServiceURL url = new JMXServiceURL(this .serviceUrl);
138:
139: // Create the connector server now.
140: this .connectorServer = JMXConnectorServerFactory
141: .newJMXConnectorServer(url, this .environment,
142: this .server);
143:
144: // Do we want to register the connector with the MBean server?
145: if (this .objectName != null) {
146: doRegister(this .connectorServer, this .objectName);
147: }
148:
149: try {
150: if (this .threaded) {
151: // Start the connector server asynchronously (in a separate thread).
152: Thread connectorThread = new Thread() {
153: public void run() {
154: try {
155: connectorServer.start();
156: } catch (IOException ex) {
157: throw new JmxException(
158: "Could not start JMX connector server after delay",
159: ex);
160: }
161: }
162: };
163:
164: connectorThread.setName("JMX Connector Thread ["
165: + this .serviceUrl + "]");
166: connectorThread.setDaemon(this .daemon);
167: connectorThread.start();
168: } else {
169: // Start the connector server in the same thread.
170: this .connectorServer.start();
171: }
172:
173: if (logger.isInfoEnabled()) {
174: logger.info("JMX connector server started: "
175: + this .connectorServer);
176: }
177: }
178:
179: catch (IOException ex) {
180: // Unregister the connector server if startup failed.
181: unregisterBeans();
182: throw ex;
183: }
184: }
185:
186: public Object getObject() {
187: return this .connectorServer;
188: }
189:
190: public Class getObjectType() {
191: return (this .connectorServer != null ? this .connectorServer
192: .getClass() : JMXConnectorServer.class);
193: }
194:
195: public boolean isSingleton() {
196: return true;
197: }
198:
199: /**
200: * Stop the <code>JMXConnectorServer</code> managed by an instance of this class.
201: * Automatically called on <code>ApplicationContext</code> shutdown.
202: * @throws IOException if there is an error stopping the connector server
203: */
204: public void destroy() throws IOException {
205: if (logger.isInfoEnabled()) {
206: logger.info("Stopping JMX connector server: "
207: + this.connectorServer);
208: }
209: try {
210: this.connectorServer.stop();
211: } finally {
212: unregisterBeans();
213: }
214: }
215:
216: }
|