001// Copyright 2005 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.hivemind.management.impl; 016 017import java.lang.reflect.InvocationTargetException; 018import java.lang.reflect.Method; 019import java.util.ArrayList; 020import java.util.Iterator; 021import java.util.List; 022 023import javax.management.DynamicMBean; 024import javax.management.InstanceAlreadyExistsException; 025import javax.management.InstanceNotFoundException; 026import javax.management.JMException; 027import javax.management.MBeanRegistrationException; 028import javax.management.MBeanServer; 029import javax.management.NotCompliantMBeanException; 030import javax.management.ObjectInstance; 031import javax.management.ObjectName; 032import javax.management.StandardMBean; 033 034import org.apache.commons.logging.Log; 035import org.apache.hivemind.ErrorHandler; 036import org.apache.hivemind.events.RegistryShutdownListener; 037import org.apache.hivemind.internal.ServicePoint; 038import org.apache.hivemind.management.MBeanRegistry; 039import org.apache.hivemind.management.ManagementMessages; 040import org.apache.hivemind.management.ObjectNameBuilder; 041 042/** 043 * Implementation of {@link MBeanRegistry}. Registers MBeans in an standard JMX MBeanServer Supports 044 * calling start methods, after the registration. MBeans can be provided as service references in a 045 * configuration. Standard MBeans must use the primitive service model. Any interceptor destroys JMX 046 * compliance due to naming conventions. Implements shutdown listener to unregisters all MBeans when 047 * the registry is shutdown 048 * 049 * @author Achim Huegen 050 * @since 1.1 051 */ 052public class MBeanRegistryImpl implements MBeanRegistry, RegistryShutdownListener 053{ 054 private ErrorHandler _errorHandler; 055 056 private Log _log; 057 058 private MBeanServer _beanServer; 059 060 private ObjectNameBuilder _objectNameBuilder; 061 062 private List _beans; 063 064 // Holds all registered MBean instances 065 private List _objectInstances = new ArrayList(); 066 067 /** 068 * Creates new instance Registers all MBeans as defined in <code>beans</code> 069 * 070 * @param objectNameBuilder 071 * Service responsible for naming MBeans 072 * @param beans 073 * List with instances of {@link MBeanRegistrationContribution}. The specified 074 * services get registered as MBeans 075 */ 076 public MBeanRegistryImpl(ErrorHandler errorHandler, Log log, MBeanServer beanServer, 077 ObjectNameBuilder objectNameBuilder, List beans) 078 { 079 _errorHandler = errorHandler; 080 _log = log; 081 _beanServer = beanServer; 082 _objectNameBuilder = objectNameBuilder; 083 _beans = beans; 084 if (_beans != null) 085 processContributions(_beans); 086 } 087 088 /** 089 * Registers all services as MBeans, specified in the contribution to this service 090 * 091 * @param beans 092 * List of MBeanRegistrationContribution 093 */ 094 private void processContributions(List beans) 095 { 096 Iterator iter = beans.iterator(); 097 while (iter.hasNext()) 098 { 099 MBeanRegistrationContribution mbeanReg = (MBeanRegistrationContribution) iter.next(); 100 registerServiceAsMBean(mbeanReg.getObjectName(), mbeanReg.getServicePoint(), mbeanReg 101 .getStartMethod()); 102 } 103 } 104 105 /** 106 * Registers a service as MBean. Retrieves an instance of the service by calling 107 * {@link ServicePoint#getService(Class)} 108 * 109 * @param objectName 110 * ObjectName for the MBean, if null the ObjectName is determined by the 111 * {@link ObjectNameBuilder} 112 * @param servicePoint 113 * ServicePoint 114 * @param startMethodName 115 * Name of the start method to call in the servicePoint after registration Can be 116 * null 117 */ 118 private void registerServiceAsMBean(ObjectName objectName, ServicePoint servicePoint, 119 String startMethodName) 120 { 121 // By default the ObjectName is built by ObjectNameBuilder service 122 // but the name can be overriden in the contribution 123 if (objectName == null) 124 objectName = _objectNameBuilder.createServiceObjectName(servicePoint); 125 126 // Register the bean 127 Object mbean; 128 try 129 { 130 Class managementInterface = servicePoint.getServiceInterface(); 131 // TODO: Check if ServiceModel is != pool and threaded 132 mbean = servicePoint.getService(managementInterface); 133 registerMBean(mbean, managementInterface, objectName); 134 } 135 catch (JMException e) 136 { 137 _errorHandler.error( 138 _log, 139 ManagementMessages.errorRegisteringMBean(objectName, e), 140 null, 141 e); 142 return; 143 } 144 // Call the start method if defined 145 try 146 { 147 if (startMethodName != null) 148 invokeStartMethod(mbean, startMethodName); 149 } 150 catch (InvocationTargetException e) 151 { 152 _errorHandler.error(_log, ManagementMessages.errorStartMethodFailed( 153 startMethodName, 154 objectName, 155 e.getTargetException()), null, e); 156 return; 157 } 158 catch (Exception e) 159 { 160 _errorHandler.error(_log, ManagementMessages.errorStartMethodFailed( 161 startMethodName, 162 objectName, 163 e), null, e); 164 return; 165 } 166 } 167 168 /** 169 * @throws InstanceAlreadyExistsException 170 * @throws MBeanRegistrationException 171 * @throws NotCompliantMBeanException 172 * @see MBeanRegistry#registerMBean(Object, Class, ObjectName) 173 */ 174 public ObjectInstance registerMBean(Object obj, Class managementInterface, ObjectName objectName) 175 throws InstanceAlreadyExistsException, MBeanRegistrationException, 176 NotCompliantMBeanException 177 { 178 ObjectInstance instance = null; 179 try 180 { 181 if (_log.isDebugEnabled()) 182 { 183 _log.debug("Trying to register MBean " + objectName); 184 } 185 instance = _beanServer.registerMBean(obj, objectName); 186 } 187 catch (NotCompliantMBeanException e) 188 { 189 if (_log.isDebugEnabled()) 190 { 191 _log.debug("MBean " + objectName + " is not compliant. Registering" 192 + " using StandardMBean"); 193 } 194 if (DynamicMBean.class.isAssignableFrom(obj.getClass()) || managementInterface == null) 195 throw e; 196 // if the object is a Standard MBean that is surrounded by 197 // a proxy or an interceptor it is not compliant since 198 // the naming conventions are not fulfilled. 199 // Now we use the StandardMBean class to adapt the MBean to the 200 // DynamicMBean interface which is not restricted by these 201 // naming conventions 202 StandardMBean standardMBean = new StandardMBean(obj, managementInterface); 203 instance = _beanServer.registerMBean(standardMBean, objectName); 204 } 205 _objectInstances.add(instance); 206 return instance; 207 } 208 209 /** 210 * @see org.apache.hivemind.management.MBeanRegistry#unregisterMBean(javax.management.ObjectName) 211 */ 212 public void unregisterMBean(ObjectName objectName) throws InstanceNotFoundException, 213 MBeanRegistrationException 214 { 215 ObjectInstance instance = _beanServer.getObjectInstance(objectName); 216 _objectInstances.remove(instance); 217 _beanServer.unregisterMBean(objectName); 218 } 219 220 /** 221 * Calls the start method of an mbean 222 */ 223 private void invokeStartMethod(Object mbean, String methodName) throws IllegalAccessException, 224 InvocationTargetException, NoSuchMethodException 225 { 226 Class serviceClass = mbean.getClass(); 227 Method m = serviceClass.getMethod(methodName, null); 228 m.invoke(mbean, null); 229 } 230 231 /** 232 * Unregisters all registered MBeans 233 */ 234 public void registryDidShutdown() 235 { 236 // Unregister objects in reversed order. Otherwise the 237 // Jsr 160 connector gets problems after the namingservice is unregistered 238 for (int i = _objectInstances.size() - 1; i >= 0; i--) 239 { 240 ObjectInstance objectInstance = (ObjectInstance) _objectInstances.get(i); 241 try 242 { 243 _beanServer.unregisterMBean(objectInstance.getObjectName()); 244 } 245 catch (JMException e) 246 { 247 // Uncritical error, just warn 248 _log.warn(ManagementMessages.errorUnregisteringMBean( 249 objectInstance.getObjectName(), 250 e)); 251 } 252 } 253 } 254 255}