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.access; 018 019import java.io.IOException; 020import java.net.MalformedURLException; 021import java.util.Arrays; 022import java.util.Map; 023import javax.management.MBeanServerConnection; 024import javax.management.ObjectName; 025import javax.management.remote.JMXServiceURL; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029 030import org.springframework.beans.factory.DisposableBean; 031import org.springframework.beans.factory.InitializingBean; 032import org.springframework.jmx.JmxException; 033import org.springframework.jmx.MBeanServerNotFoundException; 034import org.springframework.jmx.support.NotificationListenerHolder; 035import org.springframework.util.CollectionUtils; 036 037/** 038 * Registrar object that associates a specific {@link javax.management.NotificationListener} 039 * with one or more MBeans in an {@link javax.management.MBeanServer} 040 * (typically via a {@link javax.management.MBeanServerConnection}). 041 * 042 * @author Juergen Hoeller 043 * @since 2.5.2 044 * @see #setServer 045 * @see #setMappedObjectNames 046 * @see #setNotificationListener 047 */ 048public class NotificationListenerRegistrar extends NotificationListenerHolder 049 implements InitializingBean, DisposableBean { 050 051 /** Logger available to subclasses */ 052 protected final Log logger = LogFactory.getLog(getClass()); 053 054 private MBeanServerConnection server; 055 056 private JMXServiceURL serviceUrl; 057 058 private Map<String, ?> environment; 059 060 private String agentId; 061 062 private final ConnectorDelegate connector = new ConnectorDelegate(); 063 064 private ObjectName[] actualObjectNames; 065 066 067 /** 068 * Set the {@code MBeanServerConnection} used to connect to the 069 * MBean which all invocations are routed to. 070 */ 071 public void setServer(MBeanServerConnection server) { 072 this.server = server; 073 } 074 075 /** 076 * Specify the environment for the JMX connector. 077 * @see javax.management.remote.JMXConnectorFactory#connect(javax.management.remote.JMXServiceURL, java.util.Map) 078 */ 079 public void setEnvironment(Map<String, ?> environment) { 080 this.environment = environment; 081 } 082 083 /** 084 * Allow Map access to the environment to be set for the connector, 085 * with the option to add or override specific entries. 086 * <p>Useful for specifying entries directly, for example via 087 * "environment[myKey]". This is particularly useful for 088 * adding or overriding entries in child bean definitions. 089 */ 090 public Map<String, ?> getEnvironment() { 091 return this.environment; 092 } 093 094 /** 095 * Set the service URL of the remote {@code MBeanServer}. 096 */ 097 public void setServiceUrl(String url) throws MalformedURLException { 098 this.serviceUrl = new JMXServiceURL(url); 099 } 100 101 /** 102 * Set the agent id of the {@code MBeanServer} to locate. 103 * <p>Default is none. If specified, this will result in an 104 * attempt being made to locate the attendant MBeanServer, unless 105 * the {@link #setServiceUrl "serviceUrl"} property has been set. 106 * @see javax.management.MBeanServerFactory#findMBeanServer(String) 107 * <p>Specifying the empty String indicates the platform MBeanServer. 108 */ 109 public void setAgentId(String agentId) { 110 this.agentId = agentId; 111 } 112 113 114 @Override 115 public void afterPropertiesSet() { 116 if (getNotificationListener() == null) { 117 throw new IllegalArgumentException("Property 'notificationListener' is required"); 118 } 119 if (CollectionUtils.isEmpty(this.mappedObjectNames)) { 120 throw new IllegalArgumentException("Property 'mappedObjectName' is required"); 121 } 122 prepare(); 123 } 124 125 /** 126 * Registers the specified {@code NotificationListener}. 127 * <p>Ensures that an {@code MBeanServerConnection} is configured and attempts 128 * to detect a local connection if one is not supplied. 129 */ 130 public void prepare() { 131 if (this.server == null) { 132 this.server = this.connector.connect(this.serviceUrl, this.environment, this.agentId); 133 } 134 try { 135 this.actualObjectNames = getResolvedObjectNames(); 136 if (logger.isDebugEnabled()) { 137 logger.debug("Registering NotificationListener for MBeans " + Arrays.asList(this.actualObjectNames)); 138 } 139 for (ObjectName actualObjectName : this.actualObjectNames) { 140 this.server.addNotificationListener( 141 actualObjectName, getNotificationListener(), getNotificationFilter(), getHandback()); 142 } 143 } 144 catch (IOException ex) { 145 throw new MBeanServerNotFoundException( 146 "Could not connect to remote MBeanServer at URL [" + this.serviceUrl + "]", ex); 147 } 148 catch (Exception ex) { 149 throw new JmxException("Unable to register NotificationListener", ex); 150 } 151 } 152 153 /** 154 * Unregisters the specified {@code NotificationListener}. 155 */ 156 @Override 157 public void destroy() { 158 try { 159 if (this.actualObjectNames != null) { 160 for (ObjectName actualObjectName : this.actualObjectNames) { 161 try { 162 this.server.removeNotificationListener( 163 actualObjectName, getNotificationListener(), getNotificationFilter(), getHandback()); 164 } 165 catch (Exception ex) { 166 if (logger.isDebugEnabled()) { 167 logger.debug("Unable to unregister NotificationListener", ex); 168 } 169 } 170 } 171 } 172 } 173 finally { 174 this.connector.close(); 175 } 176 } 177 178}