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