| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.felix.ipojo.handlers.jmx; |
| |
| import java.lang.management.ManagementFactory; |
| import java.util.*; |
| |
| import javax.management.MBeanRegistration; |
| import javax.management.MBeanServer; |
| import javax.management.ObjectInstance; |
| import javax.management.ObjectName; |
| |
| import org.apache.felix.ipojo.FieldInterceptor; |
| import org.apache.felix.ipojo.InstanceManager; |
| import org.apache.felix.ipojo.PrimitiveHandler; |
| import org.apache.felix.ipojo.architecture.HandlerDescription; |
| import org.apache.felix.ipojo.metadata.Element; |
| import org.apache.felix.ipojo.parser.FieldMetadata; |
| import org.apache.felix.ipojo.parser.MethodMetadata; |
| import org.apache.felix.ipojo.parser.PojoMetadata; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceRegistration; |
| |
| /** |
| * This class implements iPOJO Handler. it builds the dynamic MBean from |
| * metadata.xml and exposes it to the MBean Server. |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public class MBeanHandler extends PrimitiveHandler { |
| |
| /** |
| * The name of the MBeanRegistration postDeregister method. |
| */ |
| public static final String POST_DEREGISTER_METH_NAME = "postDeregister"; |
| |
| /** |
| * The name of the MBeanRegistration preDeregister method. |
| */ |
| public static final String PRE_DEREGISTER_METH_NAME = "preDeregister"; |
| |
| /** |
| * The name of the MBeanRegistration postRegister method. |
| */ |
| public static final String POST_REGISTER_METH_NAME = "postRegister"; |
| |
| /** |
| * The name of the MBeanRegistration preRegister method. |
| */ |
| public static final String PRE_REGISTER_METH_NAME = "preRegister"; |
| |
| /** |
| * The name of the global configuration element. |
| */ |
| private static final String JMX_CONFIG_ELT = "config"; |
| |
| /** |
| * The name of the global configuration element. |
| */ |
| private static final String JMX_CONFIG_ALT_ELT = "JmxBean"; |
| |
| /** |
| * The name of the component object full name attribute. |
| */ |
| private static final String JMX_OBJ_NAME_ELT = "objectName"; |
| |
| /** |
| * The name of the component object name domain attribute. |
| */ |
| private static final String JMX_OBJ_NAME_DOMAIN_ELT = "domain"; |
| |
| /** |
| * The name of the component object name attribute. |
| */ |
| private static final String JMX_OBJ_NAME_WO_DOMAIN_ELT = "name"; |
| |
| /** |
| * The name of the attribute indicating if the handler uses MOSGi MBean server. |
| */ |
| private static final String JMX_USES_MOSGI_ELT = "usesMOSGi"; |
| |
| /** |
| * The name of a method element. |
| */ |
| private static final String JMX_METHOD_ELT = "method"; |
| |
| /** |
| * The alternative name of a method element. |
| */ |
| private static final String JMX_METHOD_ELT_ALT = "JmxMethod"; |
| |
| /** |
| * The name of the property or method name attribute. |
| */ |
| private static final String JMX_NAME_ELT = "name"; |
| |
| /** |
| * The name of a method description attribute. |
| */ |
| private static final String JMX_DESCRIPTION_ELT = "description"; |
| |
| /** |
| * The name of a property element. |
| */ |
| private static final String JMX_PROPERTY_ELT = "property"; |
| |
| /** |
| * The alternative name of a property element. |
| */ |
| private static final String JMX_PROPERTY_ELT_ALT = "JmxProperty"; |
| |
| /** |
| * The name of the field attribute. |
| */ |
| private static final String JMX_FIELD_ELT = "field"; |
| |
| /** |
| * The name of the notification attribute. |
| */ |
| private static final String JMX_NOTIFICATION_ELT = "notification"; |
| |
| /** |
| * The name of the rights attribute. |
| */ |
| private static final String JMX_RIGHTS_ELT = "rights"; |
| |
| /** |
| * The instance manager. Used to store the InstanceManager instance. |
| */ |
| private InstanceManager m_instanceManager; |
| /** |
| * The service registration. Used to register and unregister the Dynamic MBean. |
| */ |
| private ServiceRegistration m_serviceRegistration; |
| /** |
| * Stores data when parsing metadata.xml. |
| */ |
| private JmxConfigFieldMap m_jmxConfigFieldMap; |
| /** |
| * Stores the Dynamic MBean. |
| */ |
| private DynamicMBeanImpl m_MBean; |
| /** |
| * Constant storing the name of the class. |
| */ |
| private String m_namespace = "org.apache.felix.ipojo.handlers.jmx"; |
| /** |
| * The flag used to inform if we use the MOSGi framework. |
| */ |
| private boolean m_usesMOSGi; |
| /** |
| * The ObjectName used to register the MBean. |
| */ |
| private ObjectName m_objectName; |
| /** |
| * The flag used to inform if the MBean is registered. |
| */ |
| private boolean m_registered; |
| /** |
| * The ObjectName specified in handler configuration. It can be null. |
| */ |
| private String m_completeObjNameElt; |
| /** |
| * The ObjectName without domain specified in handler configuration. It can be null. |
| */ |
| private String m_objNameWODomainElt; |
| |
| /** |
| * The ObjectName domain specified in handler configuration. It can be null. |
| */ |
| private String m_domainElt; |
| /** |
| * The flag informing if the POJO implements the MBeanRegistration interface. |
| */ |
| private boolean m_registerCallbacks; |
| /** |
| * The preRegister method of MBeanRegistration interface. It is null if POJO doesn't implement MBeanRegistration interface. |
| */ |
| private MethodMetadata m_preRegisterMeth; |
| /** |
| * The postRegister method of MBeanRegistration interface. It is null if POJO doesn't implement MBeanRegistration interface. |
| */ |
| private MethodMetadata m_postRegisterMeth; |
| /** |
| * The preDeregister method of MBeanRegistration interface. It is null if POJO doesn't implement MBeanRegistration interface. |
| */ |
| private MethodMetadata m_preDeregisterMeth; |
| /** |
| * The postDeregister method of MBeanRegistration interface. It is null if POJO doesn't implement MBeanRegistration interface. |
| */ |
| private MethodMetadata m_postDeregisterMeth; |
| |
| /** |
| * Constructs the structure JmxConfigFieldMap and the Dynamic Mbean. |
| * |
| * @param metadata the component metadata |
| * @param dict the instance configuration |
| */ |
| public void configure(Element metadata, Dictionary dict) { |
| |
| PojoMetadata manipulation = getPojoMetadata(); |
| |
| m_instanceManager = getInstanceManager(); |
| |
| m_jmxConfigFieldMap = new JmxConfigFieldMap(); |
| |
| // Build the hashmap |
| Element[] mbeans = metadata.getElements(JMX_CONFIG_ELT, m_namespace); |
| if (mbeans == null || mbeans.length == 0) { |
| mbeans = metadata.getElements(JMX_CONFIG_ALT_ELT, m_namespace); |
| } |
| |
| if (mbeans.length != 1) { |
| error("A component must have exactly one " + JMX_CONFIG_ELT + " or " + JMX_CONFIG_ALT_ELT + " element."); |
| error("The JMX handler configuration is ignored."); |
| return; |
| } |
| |
| Element mbean = mbeans[0]; |
| |
| // retrieve kind of MBeanServer to use |
| m_usesMOSGi = Boolean.parseBoolean(mbean.getAttribute(JMX_USES_MOSGI_ELT)); |
| |
| // retrieve object name |
| m_completeObjNameElt = mbean.getAttribute(JMX_OBJ_NAME_ELT); |
| m_domainElt = mbean.getAttribute(JMX_OBJ_NAME_DOMAIN_ELT); |
| m_objNameWODomainElt = mbean.getAttribute(JMX_OBJ_NAME_WO_DOMAIN_ELT); |
| |
| // test if Pojo is interested in registration callbacks |
| m_registerCallbacks = manipulation |
| .isInterfaceImplemented(MBeanRegistration.class.getName()); |
| if (m_registerCallbacks) { |
| // don't need to check that methods exist, the pojo implements |
| // MBeanRegistration interface |
| String[] preRegisterParams = { MBeanServer.class.getName(), |
| ObjectName.class.getName() }; |
| m_preRegisterMeth = manipulation.getMethod(PRE_REGISTER_METH_NAME, |
| preRegisterParams); |
| |
| String[] postRegisterParams = { Boolean.class.getName() }; |
| m_postRegisterMeth = manipulation.getMethod( |
| POST_REGISTER_METH_NAME, postRegisterParams); |
| |
| m_preDeregisterMeth = manipulation.getMethod( |
| PRE_DEREGISTER_METH_NAME, new String[0]); |
| |
| m_postDeregisterMeth = manipulation.getMethod( |
| POST_DEREGISTER_METH_NAME, new String[0]); |
| } |
| |
| // set property |
| Element[] attributes = mbean.getElements(JMX_PROPERTY_ELT, m_namespace); |
| Element[] attributesAlt = mbean.getElements(JMX_PROPERTY_ELT_ALT, m_namespace); |
| List<Element> listOfAttributes = new ArrayList<Element>(); |
| if (attributes != null) { |
| listOfAttributes.addAll(Arrays.asList(attributes)); |
| } |
| if (attributesAlt != null) { |
| listOfAttributes.addAll(Arrays.asList(attributesAlt)); |
| } |
| |
| Element[] attributesOld = mbeans[0].getElements(JMX_PROPERTY_ELT); |
| if (attributesOld != null) { |
| warn("The JMX property element should use the '" + m_namespace + "' namespace."); |
| listOfAttributes.addAll(Arrays.asList(attributesOld)); |
| } |
| |
| for (Element attribute : listOfAttributes) { |
| boolean notif = false; |
| String rights; |
| String name; |
| String field = attribute.getAttribute(JMX_FIELD_ELT); |
| |
| if (attribute.containsAttribute(JMX_NAME_ELT)) { |
| name = attribute.getAttribute(JMX_NAME_ELT); |
| } else { |
| name = field; |
| } |
| if (attribute.containsAttribute(JMX_RIGHTS_ELT)) { |
| rights = attribute.getAttribute(JMX_RIGHTS_ELT); |
| } else { |
| rights = "r"; |
| } |
| |
| PropertyField property = new PropertyField(name, field, rights, |
| getTypeFromAttributeField(field, manipulation)); |
| |
| if (attribute.containsAttribute(JMX_NOTIFICATION_ELT)) { |
| notif = Boolean.parseBoolean(attribute |
| .getAttribute(JMX_NOTIFICATION_ELT)); |
| } |
| |
| property.setNotifiable(notif); |
| |
| if (notif) { |
| // add the new notifiable property in structure |
| NotificationField notification = new NotificationField( |
| name, this.getClass().getName() + "." + field, null); |
| m_jmxConfigFieldMap.addNotificationFromName(name, |
| notification); |
| } |
| m_jmxConfigFieldMap.addPropertyFromName(name, property); |
| getInstanceManager().register(manipulation.getField(field), |
| this); |
| info("property exposed:" + name + " " + field + ":" |
| + getTypeFromAttributeField(field, manipulation) + " " |
| + rights + ", Notif=" + notif); |
| } |
| |
| // set methods |
| Element[] methods = mbean.getElements(JMX_METHOD_ELT, m_namespace); |
| Element[] methodsAlt = mbean.getElements(JMX_METHOD_ELT_ALT, m_namespace); |
| List<Element> listOfMethods = new ArrayList<Element>(); |
| if (methods != null) { |
| listOfMethods.addAll(Arrays.asList(methods)); |
| } |
| if (methodsAlt != null) { |
| listOfMethods.addAll(Arrays.asList(methodsAlt)); |
| } |
| |
| Element[] methodsOld = mbeans[0].getElements(JMX_PROPERTY_ELT); |
| if (methodsOld != null) { |
| warn("The JMX method element should use the '" + m_namespace + "' namespace."); |
| listOfMethods.addAll(Arrays.asList(methodsOld)); |
| } |
| |
| for (Element method : listOfMethods) { |
| String name = method.getAttribute(JMX_NAME_ELT); |
| if (name == null) { |
| name = method.getAttribute("method"); |
| } |
| String description = null; |
| if (method.containsAttribute(JMX_DESCRIPTION_ELT)) { |
| description = method.getAttribute(JMX_DESCRIPTION_ELT); |
| } |
| |
| MethodField[] meth = getMethodsFromName(name, manipulation, |
| description); |
| |
| for (int j = 0; j < meth.length; j++) { |
| m_jmxConfigFieldMap.addMethodFromName(name, meth[j]); |
| |
| info("method exposed:" + meth[j].getReturnType() + " " + name); |
| } |
| } |
| |
| } |
| |
| /** |
| * Registers the Dynamic Mbean. |
| */ |
| public void start() { |
| // create the corresponding MBean |
| if (m_registerCallbacks) { |
| m_MBean = new DynamicMBeanWRegisterImpl(m_jmxConfigFieldMap, |
| m_instanceManager, m_preRegisterMeth, m_postRegisterMeth, |
| m_preDeregisterMeth, m_postDeregisterMeth); |
| } else { |
| m_MBean = new DynamicMBeanImpl(m_jmxConfigFieldMap, |
| m_instanceManager); |
| } |
| |
| if (m_usesMOSGi) { |
| // use whiteboard pattern to register MBean |
| |
| if (m_serviceRegistration != null) { |
| m_serviceRegistration.unregister(); |
| } |
| |
| // Register the ManagedService |
| BundleContext bundleContext = m_instanceManager.getContext(); |
| Dictionary<String, String> properties = new Hashtable<String, String>(); |
| try { |
| m_objectName = new ObjectName(getObjectNameString()); |
| |
| properties.put("jmxagent.objectName", m_objectName.toString()); |
| |
| m_serviceRegistration = bundleContext.registerService( |
| javax.management.DynamicMBean.class.getName(), m_MBean, |
| properties); |
| |
| m_registered = true; |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } else { |
| try { |
| m_objectName = new ObjectName(getObjectNameString()); |
| ObjectInstance instance = ManagementFactory |
| .getPlatformMBeanServer().registerMBean(m_MBean, |
| m_objectName); |
| |
| // we must retrieve object name used to register the MBean. |
| // It can have been changed by preRegister method of |
| // MBeanRegistration interface. |
| if (m_registerCallbacks) { |
| m_objectName = instance.getObjectName(); |
| } |
| |
| m_registered = true; |
| } catch (Exception e) { |
| error("Registration of MBean failed.", e); |
| } |
| } |
| } |
| |
| /** |
| * Returns the object name of the exposed component. |
| * |
| * @return the object name of the exposed component. |
| */ |
| private String getObjectNameString() { |
| if (m_completeObjNameElt != null) { |
| return m_completeObjNameElt; |
| } |
| |
| String domain; |
| if (m_domainElt != null) { |
| domain = m_domainElt; |
| } else { |
| domain = getPackageName(m_instanceManager.getClassName()); |
| } |
| |
| String name = "type=" + m_instanceManager.getClassName() + ",instance=" |
| + m_instanceManager.getInstanceName(); |
| if (m_objNameWODomainElt != null) { |
| name = "name=" + m_objNameWODomainElt; |
| } |
| |
| StringBuffer sb = new StringBuffer(); |
| if ((domain != null) && (domain.length() > 0)) { |
| sb.append(domain + ":"); |
| } |
| sb.append(name); |
| |
| info("Computed Objectname: " + sb.toString()); |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Extracts the package name from of given type. |
| * |
| * @param className the type name. |
| * @return the package name of the given type. |
| */ |
| private String getPackageName(String className) { |
| String packageName = ""; |
| |
| int plotIdx = className.lastIndexOf("."); |
| if (plotIdx != -1) { |
| packageName = className.substring(0, plotIdx); |
| } |
| |
| return packageName; |
| } |
| |
| /** |
| * Unregisters the Dynamic Mbean. |
| */ |
| public void stop() { |
| if (m_usesMOSGi) { |
| if (m_serviceRegistration != null) { |
| m_serviceRegistration.unregister(); |
| } |
| } else { |
| if (m_objectName != null) { |
| try { |
| ManagementFactory.getPlatformMBeanServer().unregisterMBean( |
| m_objectName); |
| } catch (Exception e) { |
| error("Unregistration of MBean failed.", e); |
| } |
| m_objectName = null; |
| } |
| } |
| |
| m_MBean = null; |
| m_registered = false; |
| } |
| |
| /** |
| * Called when a POJO member is modified externally. |
| * |
| * @param pojo the modified POJO object |
| * @param fieldName the name of the modified field |
| * @param value the new value of the field |
| * @see FieldInterceptor#onSet(Object, String, Object) |
| */ |
| public void onSet(Object pojo, String fieldName, Object value) { |
| // Check if the field is a configurable property |
| |
| PropertyField propertyField = (PropertyField) m_jmxConfigFieldMap |
| .getPropertyFromField(fieldName); |
| if (propertyField != null) { |
| if (propertyField.isNotifiable()) { |
| // TODO should send notif only when value has changed to a value |
| // different than the last one. |
| m_MBean.sendNotification(propertyField.getName() + " changed", |
| propertyField.getName(), propertyField.getType(), |
| propertyField.getValue(), value); |
| } |
| propertyField.setValue(value); |
| } |
| } |
| |
| /** |
| * Called when a POJO member is read by the MBean. |
| * |
| * @param pojo the read POJO object. |
| * @param fieldName the name of the modified field |
| * @param value the old value of the field |
| * @return the (injected) value of the field |
| * @see FieldInterceptor#onGet(Object, String, Object) |
| */ |
| public Object onGet(Object pojo, String fieldName, Object value) { |
| |
| // Check if the field is a configurable property |
| PropertyField propertyField = (PropertyField) m_jmxConfigFieldMap |
| .getPropertyFromField(fieldName); |
| if (propertyField != null) { |
| // Do we have a value to inject ? |
| Object v = propertyField.getValue(); |
| if (v == null) { |
| String type = propertyField.getType(); |
| if ("boolean".equals(type)) { v = Boolean.FALSE; } |
| else if ("byte".equals(type)) { v = new Byte((byte) 0); } |
| else if ("short".equals(type)) { v = new Short((short) 0); } |
| else if ("int".equals(type)) { v = new Integer(0); } |
| else if ("long".equals(type)) { v = new Long(0); } |
| else if ("float".equals(type)) { v = new Float(0); } |
| else if ("double".equals(type)) { v =new Double(0); } |
| else if ("char".equals(type)) { v = new Character((char) 0); } |
| |
| return v; |
| } |
| m_instanceManager.onSet(pojo, fieldName, propertyField.getValue()); |
| return propertyField.getValue(); |
| } |
| return value; |
| } |
| |
| /** |
| * Gets the type from a field name. |
| * |
| * @param fieldRequire the name of the required field |
| * @param manipulation the metadata extracted from metadata.xml file |
| * @return the type of the field or {@code null} if it wasn't found |
| */ |
| private static String getTypeFromAttributeField(String fieldRequire, |
| PojoMetadata manipulation) { |
| |
| FieldMetadata field = manipulation.getField(fieldRequire); |
| if (field == null) { |
| return null; |
| } else { |
| return FieldMetadata.getReflectionType(field.getFieldType()); |
| } |
| } |
| |
| /** |
| * Gets all the methods available which get this name. |
| * |
| * @param methodName the name of the required methods |
| * @param manipulation the metadata extract from metadata.xml file |
| * @param description the description which appears in JMX console |
| * @return the array of methods with the right name |
| */ |
| private MethodField[] getMethodsFromName(String methodName, |
| PojoMetadata manipulation, String description) { |
| |
| MethodMetadata[] methods = manipulation.getMethods(methodName); |
| if (methods.length == 0) { |
| return null; |
| } |
| |
| MethodField[] ret = new MethodField[methods.length]; |
| |
| if (methods.length == 1) { |
| ret[0] = new MethodField(methods[0], description); |
| return ret; |
| } else { |
| for (int i = 0; i < methods.length; i++) { |
| ret[i] = new MethodField(methods[i], description); |
| } |
| return ret; |
| } |
| } |
| |
| /** |
| * Gets the JMX handler description. |
| * |
| * @return the JMX handler description. |
| * @see org.apache.felix.ipojo.Handler#getDescription() |
| */ |
| public HandlerDescription getDescription() { |
| return new JMXHandlerDescription(this); |
| } |
| |
| /** |
| * Returns the objectName used to register the MBean. If the MBean is not registered, return an empty string. |
| * |
| * @return the objectName used to register the MBean. |
| * @see org.apache.felix.ipojo.Handler#getDescription() |
| */ |
| public String getUsedObjectName() { |
| if (m_objectName != null) { |
| return m_objectName.toString(); |
| } else { |
| return ""; |
| } |
| } |
| |
| /** |
| * Returns true if the MBean is registered. |
| * |
| * @return true if the MBean is registered. |
| */ |
| public boolean isRegistered() { |
| return m_registered; |
| } |
| |
| /** |
| * Returns true if the MBean must be registered thanks to white board pattern of MOSGi. |
| * |
| * @return {@code true} if the MBean must be registered thanks to white board pattern of MOSGi, false otherwise. |
| */ |
| public boolean isUsesMOSGi() { |
| return m_usesMOSGi; |
| } |
| |
| /** |
| * Returns true if the MOSGi framework is present on the OSGi platform. |
| * |
| * @return {@code true} if the MOSGi framework is present on the OSGi platform, false otherwise. |
| */ |
| public boolean isMOSGiExists() { |
| for (Bundle bundle : m_instanceManager.getContext().getBundles()) { |
| String symbolicName = bundle.getSymbolicName(); |
| if ("org.apache.felix.mosgi.jmx.agent".equals(symbolicName)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| } |