001/* 002 * Copyright 2002-2012 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.io.IOException; 020import java.net.MalformedURLException; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.Properties; 024import javax.management.MBeanServerConnection; 025import javax.management.remote.JMXConnector; 026import javax.management.remote.JMXConnectorFactory; 027import javax.management.remote.JMXServiceURL; 028 029import org.springframework.aop.TargetSource; 030import org.springframework.aop.framework.ProxyFactory; 031import org.springframework.aop.target.AbstractLazyCreationTargetSource; 032import org.springframework.beans.factory.BeanClassLoaderAware; 033import org.springframework.beans.factory.DisposableBean; 034import org.springframework.beans.factory.FactoryBean; 035import org.springframework.beans.factory.InitializingBean; 036import org.springframework.util.ClassUtils; 037import org.springframework.util.CollectionUtils; 038 039/** 040 * {@link FactoryBean} that creates a JMX 1.2 {@code MBeanServerConnection} 041 * to a remote {@code MBeanServer} exposed via a {@code JMXServerConnector}. 042 * Exposes the {@code MBeanServer} for bean references. 043 * 044 * @author Rob Harrop 045 * @author Juergen Hoeller 046 * @since 1.2 047 * @see MBeanServerFactoryBean 048 * @see ConnectorServerFactoryBean 049 * @see org.springframework.jmx.access.MBeanClientInterceptor#setServer 050 * @see org.springframework.jmx.access.NotificationListenerRegistrar#setServer 051 */ 052public class MBeanServerConnectionFactoryBean 053 implements FactoryBean<MBeanServerConnection>, BeanClassLoaderAware, InitializingBean, DisposableBean { 054 055 private JMXServiceURL serviceUrl; 056 057 private Map<String, Object> environment = new HashMap<String, Object>(); 058 059 private boolean connectOnStartup = true; 060 061 private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 062 063 private JMXConnector connector; 064 065 private MBeanServerConnection connection; 066 067 private JMXConnectorLazyInitTargetSource connectorTargetSource; 068 069 070 /** 071 * Set the service URL of the remote {@code MBeanServer}. 072 */ 073 public void setServiceUrl(String url) throws MalformedURLException { 074 this.serviceUrl = new JMXServiceURL(url); 075 } 076 077 /** 078 * Set the environment properties used to construct the {@code JMXConnector} 079 * as {@code java.util.Properties} (String key/value pairs). 080 */ 081 public void setEnvironment(Properties environment) { 082 CollectionUtils.mergePropertiesIntoMap(environment, this.environment); 083 } 084 085 /** 086 * Set the environment properties used to construct the {@code JMXConnector} 087 * as a {@code Map} of String keys and arbitrary Object values. 088 */ 089 public void setEnvironmentMap(Map<String, ?> environment) { 090 if (environment != null) { 091 this.environment.putAll(environment); 092 } 093 } 094 095 /** 096 * Set whether to connect to the server on startup. Default is "true". 097 * <p>Can be turned off to allow for late start of the JMX server. 098 * In this case, the JMX connector will be fetched on first access. 099 */ 100 public void setConnectOnStartup(boolean connectOnStartup) { 101 this.connectOnStartup = connectOnStartup; 102 } 103 104 @Override 105 public void setBeanClassLoader(ClassLoader classLoader) { 106 this.beanClassLoader = classLoader; 107 } 108 109 110 /** 111 * Creates a {@code JMXConnector} for the given settings 112 * and exposes the associated {@code MBeanServerConnection}. 113 */ 114 @Override 115 public void afterPropertiesSet() throws IOException { 116 if (this.serviceUrl == null) { 117 throw new IllegalArgumentException("Property 'serviceUrl' is required"); 118 } 119 120 if (this.connectOnStartup) { 121 connect(); 122 } 123 else { 124 createLazyConnection(); 125 } 126 } 127 128 /** 129 * Connects to the remote {@code MBeanServer} using the configured service URL and 130 * environment properties. 131 */ 132 private void connect() throws IOException { 133 this.connector = JMXConnectorFactory.connect(this.serviceUrl, this.environment); 134 this.connection = this.connector.getMBeanServerConnection(); 135 } 136 137 /** 138 * Creates lazy proxies for the {@code JMXConnector} and {@code MBeanServerConnection} 139 */ 140 private void createLazyConnection() { 141 this.connectorTargetSource = new JMXConnectorLazyInitTargetSource(); 142 TargetSource connectionTargetSource = new MBeanServerConnectionLazyInitTargetSource(); 143 144 this.connector = (JMXConnector) 145 new ProxyFactory(JMXConnector.class, this.connectorTargetSource).getProxy(this.beanClassLoader); 146 this.connection = (MBeanServerConnection) 147 new ProxyFactory(MBeanServerConnection.class, connectionTargetSource).getProxy(this.beanClassLoader); 148 } 149 150 151 @Override 152 public MBeanServerConnection getObject() { 153 return this.connection; 154 } 155 156 @Override 157 public Class<? extends MBeanServerConnection> getObjectType() { 158 return (this.connection != null ? this.connection.getClass() : MBeanServerConnection.class); 159 } 160 161 @Override 162 public boolean isSingleton() { 163 return true; 164 } 165 166 167 /** 168 * Closes the underlying {@code JMXConnector}. 169 */ 170 @Override 171 public void destroy() throws IOException { 172 if (this.connectorTargetSource == null || this.connectorTargetSource.isInitialized()) { 173 this.connector.close(); 174 } 175 } 176 177 178 /** 179 * Lazily creates a {@code JMXConnector} using the configured service URL 180 * and environment properties. 181 * @see MBeanServerConnectionFactoryBean#setServiceUrl(String) 182 * @see MBeanServerConnectionFactoryBean#setEnvironment(java.util.Properties) 183 */ 184 private class JMXConnectorLazyInitTargetSource extends AbstractLazyCreationTargetSource { 185 186 @Override 187 protected Object createObject() throws Exception { 188 return JMXConnectorFactory.connect(serviceUrl, environment); 189 } 190 191 @Override 192 public Class<?> getTargetClass() { 193 return JMXConnector.class; 194 } 195 } 196 197 198 /** 199 * Lazily creates an {@code MBeanServerConnection}. 200 */ 201 private class MBeanServerConnectionLazyInitTargetSource extends AbstractLazyCreationTargetSource { 202 203 @Override 204 protected Object createObject() throws Exception { 205 return connector.getMBeanServerConnection(); 206 } 207 208 @Override 209 public Class<?> getTargetClass() { 210 return MBeanServerConnection.class; 211 } 212 } 213 214}