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    
015    package org.apache.hivemind.management.impl;
016    
017    import java.lang.reflect.InvocationTargetException;
018    import java.lang.reflect.Method;
019    import java.util.ArrayList;
020    import java.util.Iterator;
021    import java.util.List;
022    
023    import javax.management.DynamicMBean;
024    import javax.management.InstanceAlreadyExistsException;
025    import javax.management.InstanceNotFoundException;
026    import javax.management.JMException;
027    import javax.management.MBeanRegistrationException;
028    import javax.management.MBeanServer;
029    import javax.management.NotCompliantMBeanException;
030    import javax.management.ObjectInstance;
031    import javax.management.ObjectName;
032    import javax.management.StandardMBean;
033    
034    import org.apache.commons.logging.Log;
035    import org.apache.hivemind.ErrorHandler;
036    import org.apache.hivemind.events.RegistryShutdownListener;
037    import org.apache.hivemind.internal.ServicePoint;
038    import org.apache.hivemind.management.MBeanRegistry;
039    import org.apache.hivemind.management.ManagementMessages;
040    import 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     */
052    public 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    }