Commit the JMX handler allowing to expose an JMX MBean to monitor & control an iPOJO instance remotely.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@586875 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/jmx.handler/pom.xml b/ipojo/jmx.handler/pom.xml
new file mode 100644
index 0000000..37111eb
--- /dev/null
+++ b/ipojo/jmx.handler/pom.xml
@@ -0,0 +1,58 @@
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.handler.jmx</artifactId>
+ <version>0.7.5-SNAPSHOT</version>
+ <name>iPOJO JMX Handler</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>0.9.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.metadata</artifactId>
+ <version>0.7.5-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo</artifactId>
+ <version>0.7.5-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Private-Package>
+ org.apache.felix.ipojo.handlers.jmx
+ </Private-Package>
+ <Bundle-Name>${pom.name}</Bundle-Name>
+ <Bundle-SymbolicName>ipojo.jmx.handler</Bundle-SymbolicName>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/DynamicMBeanImpl.java b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/DynamicMBeanImpl.java
new file mode 100644
index 0000000..0c36ab0
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/DynamicMBeanImpl.java
@@ -0,0 +1,389 @@
+/*
+ * 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.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.management.Attribute;
+import javax.management.AttributeChangeNotification;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.apache.felix.ipojo.util.Callback;
+
+/**
+ * this class implements iPOJO DynamicMBean.
+ * it builds the dynamic MBean
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class DynamicMBeanImpl extends NotificationBroadcasterSupport implements DynamicMBean {
+ /**
+ * JmxConfigDFieldMap : store the data extracted from metadata.xml.
+ */
+ private JmxConfigFieldMap m_configMap;
+ /**
+ * InstanceManager: use to store the InstanceManager instance.
+ */
+ private InstanceManager m_instanceManager;
+ /**
+ * MBeanInfo : class wich store the MBean Informations.
+ */
+ private MBeanInfo m_mBeanInfo;
+ /**
+ * String : constant which store the name of the class.
+ */
+ private String m_className = this.getClass().getName();
+
+ /**
+ * sequenceNumber : use to calculate unique id to notification.
+ */
+ private int m_sequenceNumber = 0;
+
+ /**
+ * DynamicMBeanImpl : constructor.
+ * @param properties : data extracted from metadat.xml file
+ * @param instanceManager : InstanceManager instance
+ */
+ public DynamicMBeanImpl(JmxConfigFieldMap properties, InstanceManager instanceManager) {
+ m_configMap = properties;
+ m_instanceManager = instanceManager;
+ this.buildMBeanInfo();
+ }
+
+ /**
+ * getAttribute implements from JMX.
+ * get the value of the required attribute
+ * @param arg0 name of required attribute
+ * @throws AttributeNotFoundException : if the attribute doesn't exist
+ * @throws MBeanException :
+ * @throws ReflectionException :
+ * @return the object attribute
+ */
+ public Object getAttribute(String arg0) throws AttributeNotFoundException, MBeanException, ReflectionException {
+ PropertyField attribute = m_configMap.getPropertyFromName(arg0);
+
+ if (attribute == null) {
+ throw new AttributeNotFoundException(arg0 + " not found");
+ } else {
+ return attribute.getValue();
+ }
+ }
+ /**
+ * getAttributes : implement from JMX.
+ * get values of reuqired attributes
+ * @param attributeNames : names of the required attributes
+ * @return return the list of the attribute
+ */
+ public AttributeList getAttributes(String[] attributeNames) {
+
+ if (attributeNames == null) {
+ throw new IllegalArgumentException("attributeNames[] cannot be null");
+ }
+
+ AttributeList resultList = new AttributeList();
+ for (int i = 0; i < attributeNames.length; i++) {
+ PropertyField propertyField = (PropertyField) m_configMap.getPropertyFromField((String) attributeNames[i]);
+
+ if (propertyField != null) {
+ resultList.add(new Attribute(attributeNames[i], propertyField.getValue()));
+ }
+ }
+ return resultList;
+ }
+ /**
+ * getMBeanInfo : return the MBean Class builded.
+ * @return return MBeanInfo class constructed by buildMBeanInfo
+ */
+ public MBeanInfo getMBeanInfo() {
+ return m_mBeanInfo;
+ }
+ /**
+ * invoke : invoke the required method on the targeted POJO.
+ * @param operationName : name of the method called
+ * @param params : parameters given to the method
+ * @param signature : determine which method called
+ * @return Object : the object return by the method
+ * @throws MBeanException :
+ * @throws ReflectionException :
+ */
+ public Object invoke(String operationName, Object[] params, String[] signature) throws MBeanException, ReflectionException {
+
+ MethodField method = m_configMap.getMethodFromName(operationName, signature);
+ if (method != null) {
+ MethodMetadata methodCall = method.getMethod();
+ Callback mc = new Callback(methodCall, m_instanceManager);
+ try {
+ return mc.call(params);
+ } catch (NoSuchMethodException e) {
+ System.err.println("No such method!: " + operationName);
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ System.err.println("Illegal Access Exception");
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ System.err.println("Invocation Target Exception");
+ e.printStackTrace();
+ }
+ } else {
+ throw new ReflectionException(new NoSuchMethodException(
+ operationName), "Cannot find the operation "
+ + operationName + " in " + m_className);
+ }
+
+ return null;
+ }
+ /**
+ * setAttribute : change specified attribute value.
+ * @param attribute : attribute with new value to be changed
+ * @throws AttributeNotFoundException : if the requiered attribute was not found
+ * @throws InvalidAttributeValueException : the value is inccorrect type
+ * @throws MBeanException :
+ * @throws ReflectionException :
+ */
+ public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException(
+ "Attribute cannot be null"), "Cannot invoke a setter of "
+ + m_className + " with null attribute");
+ }
+ String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException(
+ "Attribute name cannot be null"),
+ "Cannot invoke the setter of " + m_className
+ + " with null attribute name");
+ }
+ // Check for a recognized attribute name and call the corresponding
+ // setter
+ //
+
+ PropertyField propertyField = (PropertyField) m_configMap.getPropertyFromName(name);
+ if (propertyField == null) {
+ // unrecognized attribute name:
+ throw new AttributeNotFoundException("Attribute " + name
+ + " not found in " + m_className);
+ }
+ if (!propertyField.isWritable()) {
+ throw new InvalidAttributeValueException(
+ "Attribute " + name + " can not be setted");
+ }
+
+ if (value == null) {
+ try {
+ m_instanceManager.setterCallback(propertyField.getField(), null);
+ } catch (Exception e) {
+ throw new InvalidAttributeValueException(
+ "Cannot set attribute " + name + " to null");
+ }
+ } else { // if non null value, make sure it is assignable to the attribute
+ if (true /* TODO type.class.isAssignableFrom(value.getClass())*/) {
+ //propertyField.setValue(value);
+ // setValue(attributeField.getField(),null);
+ m_instanceManager.setterCallback(propertyField.getField(), value);
+ } else {
+ throw new InvalidAttributeValueException(
+ "Cannot set attribute " + name + " to a "
+ + value.getClass().getName()
+ + " object, String expected");
+ }
+ }
+
+
+ }
+ /**
+ * setAttributes : change all the attributes value.
+ * @param attributes : list of attribute value to be changed
+ * @return AttributeList : list of new attribute
+ */
+ public AttributeList setAttributes(AttributeList attributes) {
+
+// Check attributes is not null to avoid NullPointerException later on
+ if (attributes == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException(
+ "AttributeList attributes cannot be null"),
+ "Cannot invoke a setter of " + m_className);
+ }
+ AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, nothing more to do
+ if (attributes.isEmpty()) {
+ return resultList;
+ }
+
+ // for each attribute, try to set it and add to the result list if
+ // successfull
+ for (Iterator i = attributes.iterator(); i.hasNext();) {
+ Attribute attr = (Attribute) i.next();
+ try {
+ setAttribute(attr);
+ String name = attr.getName();
+ Object value = getAttribute(name);
+ resultList.add(new Attribute(name, value));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return resultList;
+ }
+ /**
+ * buildMBeanInfo : buil the MBean information on initilisation.
+ * this value don't change after
+ */
+ private void buildMBeanInfo() {
+ String dDescription = m_configMap.getDecription();
+
+ // generate infos for attributes
+ MBeanAttributeInfo[] dAttributes = null;
+
+ if (m_configMap == null) {
+ return;
+ }
+
+ if (m_configMap.getProperties() != null) {
+ List <MBeanAttributeInfo> lAttributes = null;
+ lAttributes = new ArrayList <MBeanAttributeInfo> ();
+
+ Iterator <PropertyField> iterator = m_configMap.getProperties().iterator();
+ while (iterator.hasNext()) {
+ PropertyField propertyField = (PropertyField) iterator.next();
+ lAttributes.add(new MBeanAttributeInfo(
+ propertyField.getName(),
+ propertyField.getType(),
+ propertyField.getDescription(),
+ propertyField.isReadable(),
+ propertyField.isWritable(),
+ false));
+ }
+ dAttributes = (MBeanAttributeInfo[]) lAttributes.toArray(new MBeanAttributeInfo[ lAttributes.size() ]);
+ }
+
+
+
+ MBeanOperationInfo[] dOperations = null;
+ if (m_configMap.getMethods() != null) {
+
+ List <MBeanOperationInfo> lOperations = new ArrayList <MBeanOperationInfo>();
+
+ Iterator <MethodField[]> iterator = m_configMap.getMethods().iterator();
+ while (iterator.hasNext()) {
+ MethodField[] method = (MethodField[]) iterator.next();
+ for (int i = 0 ; i < method.length ; i++) {
+ lOperations.add(
+ new MBeanOperationInfo(
+ method[i].getName(),
+ method[i].getDescription(),
+ method[i].getParams(),
+ method[i].getReturnType(),
+ MBeanOperationInfo.UNKNOWN
+ )
+ );
+ }
+ dOperations = (MBeanOperationInfo[]) lOperations.toArray(new MBeanOperationInfo[lOperations.size()]);
+ }
+ }
+
+ MBeanNotificationInfo[] dNotification = new MBeanNotificationInfo[0];
+ if (m_configMap.getMethods() != null) {
+
+ List <MBeanNotificationInfo> lNotifications = new ArrayList <MBeanNotificationInfo>();
+
+ Iterator <NotificationField> iterator = m_configMap.getNotifications().iterator();
+ while (iterator.hasNext()) {
+ NotificationField notification = (NotificationField) iterator.next();
+ lNotifications.add(notification.getNotificationInfo());
+ }
+ dNotification = (MBeanNotificationInfo[]) lNotifications.toArray(new MBeanNotificationInfo[lNotifications.size()]);
+ }
+
+ m_mBeanInfo = new MBeanInfo(
+ this.m_className,
+ dDescription,
+ dAttributes,
+ null, // No constructor
+ dOperations,
+ dNotification);
+ }
+
+ /**
+ * getNotificationInfo : get the notification informations (use by JMX).
+ * @return MBeanNotificationInfo[] : structure which describe the notifications
+ */
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ MBeanNotificationInfo[] dNotification = new MBeanNotificationInfo[0];
+ if (m_configMap.getMethods() != null) {
+
+ List < MBeanNotificationInfo> lNotifications = new ArrayList < MBeanNotificationInfo>();
+
+ Iterator <NotificationField> iterator = m_configMap.getNotifications().iterator();
+ while (iterator.hasNext()) {
+ NotificationField notification = (NotificationField) iterator.next();
+ lNotifications.add(notification.getNotificationInfo());
+ }
+ dNotification = (MBeanNotificationInfo[]) lNotifications.toArray(new MBeanNotificationInfo[lNotifications.size()]);
+ }
+ return dNotification;
+ }
+
+ /**
+ * sendNotification : send a notification to a subscriver.
+ * @param msg : msg to send
+ * @param attributeName : name of the attribute
+ * @param attributeType : type of the attribute
+ * @param oldValue : oldvalue of the attribute
+ * @param newValue : new value of the attribute
+ */
+ public void sendNotification(String msg, String attributeName,
+ String attributeType, Object oldValue, Object newValue) {
+
+ long timeStamp = System.currentTimeMillis();
+
+
+ if (newValue.equals(oldValue)) {
+ return;
+ }
+ m_sequenceNumber++;
+ Notification notification = new AttributeChangeNotification(
+ this, m_sequenceNumber, timeStamp,
+ msg, attributeName, attributeType, oldValue, newValue);
+ sendNotification(notification);
+ System.out.println("DEBUG: Notification sent");
+ }
+}
diff --git a/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/JmxConfigFieldMap.java b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/JmxConfigFieldMap.java
new file mode 100644
index 0000000..64a72da
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/JmxConfigFieldMap.java
@@ -0,0 +1,283 @@
+/*
+ * 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.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * JmxConfigFieldMap : use to store the informations needed to build the Dynamic MBean.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class JmxConfigFieldMap {
+
+ /**
+ * m_properties : use to store the attributes exposed.
+ */
+ private Map < String, PropertyField > m_properties = new HashMap < String, PropertyField >();
+ /**
+ * m_methods : use to store the methods exposed.
+ */
+ private Map < String, MethodField[] > m_methods = new HashMap < String, MethodField[] >();
+ /**
+ * m_notification : use to store the notification allowed.
+ */
+ private Map < String, NotificationField > m_notifications = new HashMap < String, NotificationField >();
+ /**
+ * m_description : description of the Mbean.
+ */
+ private String m_description;
+
+
+ /**
+ * JmxConfigFieldMap : constructor.
+ */
+ public JmxConfigFieldMap() {
+
+ }
+
+ /**
+ * getDescription : get the descritpion of the MBean.
+ * @return String : Decription of the MBean
+ */
+ public String getDecription() {
+ return m_description;
+ }
+
+ /**
+ * setDescription : set the descritpion of the MBean.
+ * @param description : String which describe the Mbean
+ */
+ public void setDescription(String description) {
+ this.m_description = description;
+ }
+
+ /**
+ * addPropertyFromName : add a new attribute exposed in the Mbean.
+ * @param name : name of the new property
+ * @param propertyField : Field which describe the property
+ */
+ public void addPropertyFromName(String name, PropertyField propertyField) {
+ m_properties.put(name, propertyField);
+ }
+
+ /**
+ * getProperties : get all of the properties exposed.
+ * @return : collection of all properties
+ */
+ public Collection<PropertyField> getProperties() {
+ if (m_properties != null) {
+ return m_properties.values();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * getPropertyFromName : get the property by the name.
+ * @param name : name of the requiered property
+ * @return PropertyField : the field requiered or null if is not found
+ */
+ public PropertyField getPropertyFromName(String name) {
+ PropertyField prop = m_properties.get(name);
+ return prop;
+ }
+
+ /**
+ * getPropertyFromField : get the property by the field.
+ * @param field : the requiered field
+ * @return PropertyField :
+ */
+ public PropertyField getPropertyFromField(String field) {
+ PropertyField property = null;
+ Iterator<PropertyField> it = m_properties.values().iterator();
+ while (it.hasNext()) {
+ PropertyField p = it.next();
+ if (p.getField().compareTo(field) == 0) {
+ if (property != null) {
+ System.err.println("a field already exist");
+ } else {
+ property = p;
+ }
+ }
+ }
+ return property;
+ }
+
+
+ /**
+ * addMethodFromName : add a new method descriptor from its name.
+ * @param name : name of the method
+ * @param methodField : descritpion of the method
+ */
+ public void addMethodFromName(String name, MethodField methodField) {
+ MethodField[] mf;
+ if (!m_methods.containsKey(name)) {
+ mf = new MethodField[1];
+ mf[0] = methodField;
+ } else {
+ MethodField[] temp = m_methods.get(name);
+ mf = new MethodField[temp.length + 1];
+ for (int i = 0; i < temp.length; i++) {
+ mf[i] = temp[i];
+ }
+ mf[temp.length] = methodField;
+ }
+ m_methods.put(name, mf);
+ }
+
+ /**
+ * addMethodFromName : add new methods descriptors from one name.
+ * (the method muste have the same name but different signature).
+ * @param name : name of the method
+ * @param methodsField : descritpion of the methods
+ */
+ public void addMethodFromName(String name, MethodField[] methodsField) {
+ MethodField[] mf;
+ if (!m_methods.containsKey(name)) {
+ mf = methodsField;
+ } else {
+ MethodField[] temp = m_methods.get(name);
+ mf = new MethodField[temp.length + methodsField.length];
+ for (int i = 0; i < temp.length; i++) {
+ mf[i] = temp[i];
+ }
+ for (int i = 0; i < methodsField.length; i++) {
+ mf[i + temp.length] = methodsField[i];
+ }
+ }
+ m_methods.put(name, mf);
+ }
+
+ /**
+ * DynamicMBeanImpl : add methods from name and erase the older if exist.
+ * @param name : name of the method
+ * @param methodField : method to be added
+ */
+ public void overrideMethodFromName(String name, MethodField methodField) {
+ MethodField[] mf = new MethodField[1];
+ mf[0] = methodField;
+ m_methods.put(name, mf);
+ }
+
+ /**
+ * DynamicMBeanImpl : add methods from name and erase the older if exist.
+ * @param name : name of the method
+ * @param methodsField : array of methods to be added
+ */
+ public void overrideMethodFromName(String name, MethodField[] methodsField) {
+ m_methods.put(name, methodsField);
+ }
+
+ /**
+ * getMethodFromName : return the metod(s) which are similar.
+ * @param name : name of requiered method
+ * @return MethodField[] : list of returned methods
+ */
+ public MethodField[] getMethodFromName(String name) {
+ MethodField[] prop = m_methods.get(name);
+ return prop;
+ }
+
+ /**
+ * getMethodFromName : get the method which the good signature.
+ * @param operationName : name of the method requiered
+ * @param signature : signature requiered
+ * @return MethodField : the method which the same signature or null if not found
+ */
+ public MethodField getMethodFromName(String operationName, String[] signature) {
+ MethodField[] methods = m_methods.get(operationName);
+ for (int i = 0; i < methods.length; i++) {
+ if (isSameSignature(signature, methods[i].getSignature())) {
+ return methods[i];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * isSameSignature : compare two method signature.
+ * @param sig1 : first signature
+ * @param sig2 : second signature
+ * @return boolean : return true if the signature are similar
+ * fale else
+ */
+ private boolean isSameSignature(String[] sig1, String[] sig2) {
+ if (sig1.length != sig2.length) {
+ return false;
+ } else {
+ for (int i = 0; i < sig1.length; i++) {
+ //System.out.println(sig1[i] +" == "+ sig2[i]);
+ if (!sig1[i].equals(sig2[i])) {
+ return false;
+ }
+ }
+
+ }
+ return true;
+ }
+
+ /**
+ * getMethods : return all methods store.
+ * @return Collection : collection of methodField[]
+ */
+ public Collection<MethodField[]> getMethods() {
+ if (m_methods != null) {
+ return m_methods.values();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * addNotificationFromName : add a notification .
+ * @param name :
+ * @param notificationField :
+ */
+ public void addNotificationFromName(String name, NotificationField notificationField) {
+ m_notifications.put(name, notificationField);
+ }
+
+ /**
+ * getNotificationFromName : return the notification with requiered name.
+ * @param name : name requiered
+ * @return NotificationField : return the notification if exist, null else
+ */
+ public NotificationField getNotificationFromName(String name) {
+ NotificationField prop = m_notifications.get(name);
+ return prop;
+ }
+
+ /**
+ * getNotification : get all notifications define.
+ * @return Collection : return collection of NotificationField
+ */
+ public Collection<NotificationField> getNotifications() {
+ if (m_notifications != null) {
+ return m_notifications.values();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/MBeanHandler.java b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/MBeanHandler.java
new file mode 100644
index 0000000..9e04bb0
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/MBeanHandler.java
@@ -0,0 +1,257 @@
+/*
+ * 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.util.Dictionary;
+import java.util.Properties;
+
+import org.apache.felix.ipojo.InstanceManager;
+import org.apache.felix.ipojo.PrimitiveHandler;
+import org.apache.felix.ipojo.metadata.Element;
+import org.apache.felix.ipojo.parser.FieldMetadata;
+import org.apache.felix.ipojo.parser.ManipulationMetadata;
+import org.apache.felix.ipojo.parser.MethodMetadata;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/** this class implements iPOJO Handler.
+ * it builds the dynamic MBean from metadata.xml and expose it to the MBean Server.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MBeanHandler extends PrimitiveHandler {
+ /**
+ * InstanceManager: use to store the InstanceManager instance.
+ */
+ private InstanceManager m_instanceManager;
+ /**
+ * ServiceRegistration : use to register and deregister the Dynamic MBean.
+ */
+ private ServiceRegistration m_serviceRegistration;
+ /**
+ * JmxConfigFieldMap : use to store data when parsing metadata.xml.
+ */
+ private JmxConfigFieldMap m_jmxConfigFieldMap;
+ /**
+ * DynamicMBeanImpl : store the Dynamic MBean.
+ */
+ private DynamicMBeanImpl m_MBean;
+ /**
+ * String : constant which store the name of the class.
+ */
+ private String m_NAMESPACE = this.getClass().getName();
+
+ /**
+ * configure : construct the structure JmxConfigFieldMap.and the Dynamic Mbean.
+ * @param metadata Element
+ * @param dict Dictionnary
+ */
+ public void configure(Element metadata, Dictionary dict) {
+
+ ManipulationMetadata manipulation = new ManipulationMetadata(metadata);
+
+ m_instanceManager = getInstanceManager();
+
+
+ m_jmxConfigFieldMap = new JmxConfigFieldMap();
+
+ // Build the hashmap
+ Element[] mbeans = metadata.getElements("config", m_NAMESPACE);
+
+ if (mbeans.length != 1) { return; }
+
+
+
+ // set property
+ Element[] attributes = mbeans[0].getElements("property");
+ //String[] fields = new String[attributes.length];
+ FieldMetadata[] fields = new FieldMetadata[attributes.length];
+ for (int i = 0 ; i < attributes.length ; i++) {
+ boolean notif = false;
+ String rights;
+ String name;
+ String field = attributes[i].getAttribute("field");
+
+ if (attributes[i].containsAttribute("name")) {
+ name = attributes[i].getAttribute("name");
+ } else {
+ name = field;
+ }
+ if (attributes[i].containsAttribute("rights")) {
+ rights = attributes[i].getAttribute("rights");
+ } else {
+ rights = "w";
+ }
+
+ PropertyField property = new PropertyField(name, field, rights, getTypeFromAttributeField(field, manipulation));
+
+ if (attributes[i].containsAttribute("notification")) {
+ notif = Boolean.parseBoolean(attributes[i].getAttribute("notification"));
+ }
+
+ 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);
+ fields[i] = manipulation.getField(field);
+ System.out.println("DEBUG: property exposed:" + name + " " + field + ":"
+ + getTypeFromAttributeField(field, manipulation) + " " + rights
+ + ", Notif=" + notif);
+ }
+
+ //set methods
+ Element[] methods = mbeans[0].getElements("method");
+ for (int i = 0 ; i < methods.length ; i++) {
+ String name = methods[i].getAttribute("name");
+ String description = null;
+ if (methods[i].containsAttribute("description")) {
+ description = methods[i].getAttribute("description");
+ }
+
+ MethodField[] method = getMethodsFromName(name, manipulation, description);
+
+ for (int j = 0 ; j < method.length ; j++) {
+ m_jmxConfigFieldMap.addMethodFromName(name, method[j]);
+
+ System.out.println("DEBUG: method exposed:" + method[j].getReturnType() + " " + name);
+ }
+ }
+
+ m_instanceManager.register(this, fields, null);
+
+ }
+ /**
+ * start : register the Dynamic Mbean.
+ */
+ public void start() {
+
+// create the corresponding MBean
+ m_MBean = new DynamicMBeanImpl(m_jmxConfigFieldMap, m_instanceManager);
+ if (m_serviceRegistration != null) { m_serviceRegistration.unregister(); }
+
+ // Register the ManagedService
+ BundleContext bundleContext = m_instanceManager.getContext();
+ Properties properties = new Properties();
+ properties.put("jmxagent.objectName", "HandlerJMX:type="
+ + m_instanceManager.getClassName()
+ + ",ServiceId="
+ + m_instanceManager.getInstanceName());
+
+ m_serviceRegistration = bundleContext.registerService(javax.management.DynamicMBean.class.getName(), m_MBean, properties);
+ }
+
+ /**
+ * stop : deregister the Dynamic Mbean.
+ */
+ public void stop() {
+ if (m_serviceRegistration != null) { m_serviceRegistration.unregister(); }
+
+
+ }
+
+
+ /**
+ * setterCallback : call when a POJO member is modified externally.
+ * @param fieldName : name of the modified field
+ * @param value : new value of the field
+ */
+ public void setterCallback(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()) {
+
+ m_MBean.sendNotification(propertyField.getName() + " changed", propertyField.getName(),
+ propertyField.getType(), propertyField.getValue(), value);
+ }
+ propertyField.setValue(value);
+ }
+ }
+
+ /**
+ * getterCallback : call when a POJO member is modified by the MBean.
+ * @param fieldName : name of the modified field
+ * @param value : old value of the field
+ * @return : new value of the field
+ */
+ public Object getterCallback(String fieldName, Object value) {
+
+
+ // Check if the field is a configurable property
+ PropertyField propertyField = (PropertyField) m_jmxConfigFieldMap.getPropertyFromField(fieldName);
+ if (propertyField != null) {
+ m_instanceManager.setterCallback(fieldName, propertyField.getValue());
+ return propertyField.getValue();
+ }
+ m_instanceManager.setterCallback(fieldName, value);
+ return value;
+ }
+
+ /**
+ * getTypeFromAttributeField : get the type from a field name.
+ * @param fieldRequire : name of the requiered field
+ * @param manipulation : metadata extract from metadata.xml file
+ * @return : type of the field or null if it wasn't found
+ */
+ private static String getTypeFromAttributeField(String fieldRequire, ManipulationMetadata manipulation) {
+
+ FieldMetadata field = manipulation.getField(fieldRequire);
+ if (field == null) {
+ return null;
+ } else {
+ return field.getReflectionType();
+ }
+ }
+
+ /**
+ * getMethodsFromName : get all the methods available which get this name.
+ * @param methodName : name of the requiered methods
+ * @param manipulation : metadata extract from metadata.xml file
+ * @param description : description which appears in jmx console
+ * @return : array of methods with the right name
+ */
+ private MethodField[] getMethodsFromName(String methodName, ManipulationMetadata manipulation, String description) {
+
+ MethodMetadata[] fields = manipulation.getMethods(methodName);
+ if (fields.length == 0) {
+ return null;
+ }
+
+ MethodField[] ret = new MethodField[fields.length];
+
+ if (fields.length == 1) {
+ ret[0] = new MethodField(fields[0], description);
+ return ret;
+ } else {
+ for (int i = 0 ; i < fields.length ; i++) {
+ ret[i] = new MethodField(fields[i], description);
+ }
+ return ret;
+ }
+ }
+
+
+}
diff --git a/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/MethodField.java b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/MethodField.java
new file mode 100644
index 0000000..e4346d2
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/MethodField.java
@@ -0,0 +1,86 @@
+/*
+ * 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 javax.management.MBeanParameterInfo;
+
+import org.apache.felix.ipojo.parser.MethodMetadata;
+
+/**
+ * this class build a method JMX description.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class MethodField {
+
+ /**
+ * m_description : store the method descritpion.
+ */
+ private String m_description;
+ /**
+ * m_method : store the method properties.
+ */
+ private MethodMetadata m_method;
+
+ /**
+ * MethodField : constructor.
+ * @param method : the metod properties
+ * @param description : thes method description
+ */
+ public MethodField(MethodMetadata method, String description) {
+ this.m_method = method;
+ this.m_description = description;
+
+ }
+
+
+ public MethodMetadata getMethod() {
+ return m_method;
+ }
+
+ public String getDescription() {
+ return m_description;
+ }
+
+ public String getName() {
+ return m_method.getMethodName();
+ }
+
+ /**
+ * getParams : get the parameter in JMX format.
+ * @return MBeanParameterInfo : return info on JMX format
+ */
+ public MBeanParameterInfo[] getParams() {
+ MBeanParameterInfo[] mbean = new MBeanParameterInfo[m_method.getMethodArguments().length];
+ for (int i = 0; i < m_method.getMethodArguments().length; i++) {
+ mbean[i] = new MBeanParameterInfo("arg" + i, m_method.getMethodArguments()[i], null);
+ }
+ return mbean;
+ }
+
+ public String[] getSignature() {
+ return m_method.getMethodArguments();
+ }
+
+ public String getReturnType() {
+ return m_method.getMethodReturn();
+ }
+
+}
diff --git a/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/NotificationField.java b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/NotificationField.java
new file mode 100644
index 0000000..c6ff225
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/NotificationField.java
@@ -0,0 +1,69 @@
+/*
+ * 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 javax.management.MBeanNotificationInfo;
+
+/**
+ * this calss build the notification descritpion structure.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class NotificationField {
+ /**
+ * m_name : name of the notification.
+ */
+ private String m_name;
+ /**
+ * m_description : description of the notification.
+ */
+ private String m_description;
+ /**
+ * m_description : field of the notification.
+ */
+ private String m_field;
+
+ /**
+ * NotificationField : constructor.
+ * @param name : name of the notification
+ * @param field : field which send a notification when it is modified
+ * @param description : descritpion which appears in jmx console
+ */
+
+ public NotificationField(String name, String field, String description) {
+ this.m_name = name;
+ this.m_field = field;
+ this.m_description = description;
+ }
+
+ /**
+ * getNotificationInfo : return the MBeanNotificationInfo from this class.
+ * @return : type of the field or null if it wasn't found
+ */
+ public MBeanNotificationInfo getNotificationInfo() {
+ String[] notificationTypes = new String[1];
+ notificationTypes[0] = m_field;
+ MBeanNotificationInfo mbni = new MBeanNotificationInfo(
+ notificationTypes,
+ m_name,
+ m_description);
+ return mbni;
+ }
+}
diff --git a/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/PropertyField.java b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/PropertyField.java
new file mode 100644
index 0000000..d692894
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/java/org/apache/felix/ipojo/handlers/jmx/PropertyField.java
@@ -0,0 +1,131 @@
+/*
+ * 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;
+
+/**
+ * this calss build the notification descritpion structure.
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class PropertyField {
+
+ /**
+ * m_name : name of the notification.
+ */
+ private String m_name;
+ /**
+ * m_name : name of the notification.
+ */
+ private String m_field;
+ /**
+ * m_name : name of the notification.
+ */
+ private String m_rights;
+ /**
+ * m_name : name of the notification.
+ */
+ private String m_type;
+ /**
+ * m_name : name of the notification.
+ */
+ private Object m_value;
+ /**
+ * m_name : name of the notification.
+ */
+ private boolean m_notification = false;
+
+ /**
+ * PropertyField : constructor.
+ * @param name : name of the properety
+ * @param field : field which send a notification when it is modified
+ * @param rights : the rights of the attribute (ie: 'r' or 'w')
+ * @param type : the type of the attribute
+ */
+ public PropertyField(String name, String field, String rights, String type) {
+ this.setName(name);
+ this.setField(field);
+ this.m_type = type;
+ if (isRightsValid(rights)) {
+ this.setRights(rights);
+ } else {
+ this.setField("r"); //default rights is read only
+ }
+ }
+
+ public String getField() {
+ return m_field;
+ }
+ public void setField(String field) {
+ this.m_field = field;
+ }
+ public String getName() {
+ return m_name;
+ }
+ public void setName(String name) {
+ this.m_name = name;
+ }
+ public String getRights() {
+ return m_rights;
+ }
+ public void setRights(String rights) {
+ this.m_rights = rights;
+ }
+ public Object getValue() {
+ return m_value;
+ }
+ public void setValue(Object value) {
+ this.m_value = value;
+ }
+
+ public String getType() {
+ return this.m_type;
+ }
+
+ public String getDescription() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isReadable() {
+ return this.getRights().equals("r") || this.getRights().equals("w");
+ }
+
+ public boolean isWritable() {
+ return this.getRights().equals("w");
+ }
+
+ public boolean isNotifiable() {
+ return this.m_notification;
+ }
+
+ public void setNotifiable(boolean value) {
+ this.m_notification = value;
+ }
+
+ /**
+ * isRightsValid : return is the rights is valid or not (ie = 'r' || 'w').
+ * @param rights : string represents the rights
+ * @return boolean : return true if rights = 'r' or 'w'
+ */
+ public static boolean isRightsValid(String rights) {
+ return rights != null && (rights.equals("r") || rights.equals("w"));
+ }
+
+}
diff --git a/ipojo/jmx.handler/src/main/resources/metadata.xml b/ipojo/jmx.handler/src/main/resources/metadata.xml
new file mode 100644
index 0000000..65bdc15
--- /dev/null
+++ b/ipojo/jmx.handler/src/main/resources/metadata.xml
@@ -0,0 +1,5 @@
+<ipojo>
+<!-- Primitives handler -->
+<handler classname="org.apache.felix.ipojo.handlers.jmx.MBeanHandler" name="config" namespace="org.apache.felix.ipojo.handlers.jmx.MBeanHandler">
+</handler>
+</ipojo>
\ No newline at end of file