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}