summaryrefslogtreecommitdiff
path: root/java/management/client/src/main/java/org/apache/qpid/management/wsdm/capabilities/MBeanCapabilityBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/management/client/src/main/java/org/apache/qpid/management/wsdm/capabilities/MBeanCapabilityBuilder.java')
-rw-r--r--java/management/client/src/main/java/org/apache/qpid/management/wsdm/capabilities/MBeanCapabilityBuilder.java549
1 files changed, 549 insertions, 0 deletions
diff --git a/java/management/client/src/main/java/org/apache/qpid/management/wsdm/capabilities/MBeanCapabilityBuilder.java b/java/management/client/src/main/java/org/apache/qpid/management/wsdm/capabilities/MBeanCapabilityBuilder.java
new file mode 100644
index 0000000000..ea67bdf9e1
--- /dev/null
+++ b/java/management/client/src/main/java/org/apache/qpid/management/wsdm/capabilities/MBeanCapabilityBuilder.java
@@ -0,0 +1,549 @@
+/*
+ *
+ * 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.qpid.management.wsdm.capabilities;
+
+import javassist.CannotCompileException;
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.CtNewMethod;
+
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.ObjectName;
+import javax.xml.namespace.QName;
+
+import org.apache.muse.core.Environment;
+import org.apache.qpid.management.Messages;
+import org.apache.qpid.management.Names;
+import org.apache.qpid.management.wsdm.common.EntityInstanceNotFoundFault;
+import org.apache.qpid.management.wsdm.common.MethodInvocationFault;
+import org.apache.qpid.management.wsdm.common.NoSuchAttributeFault;
+import org.apache.qpid.management.wsdm.common.QManFault;
+import org.apache.qpid.transport.util.Logger;
+
+/**
+ * Builder for capability class that will implements the interface
+ * and the behaviour of the underlying JMX Entity.
+ * The product of this builder (capability class) will be used for create a new instance
+ * of the corresponding capability. It will be the "adapter" between WS-Resource and
+ * JMX MBean.
+ *
+ * @author Andrea Gazzarini
+ */
+public class MBeanCapabilityBuilder implements IArtifactBuilder{
+
+ private final static String GET_PROPERTY_NAMES_METHOD_COMMON_PART = "public QName[] getPropertyNames() { return ";
+ private final static String GET_PROPERTY_NAMES_METHOD_WITH_ARRAY = GET_PROPERTY_NAMES_METHOD_COMMON_PART+" PROPERTIES;}";
+ private final static String GET_PROPERTY_NAMES_METHOD_WITH_EMPTY_ARRAY = GET_PROPERTY_NAMES_METHOD_COMMON_PART+" new QName[0];}";
+ private final static Logger LOGGER = Logger.get(MBeanCapabilityBuilder.class);
+
+ /**
+ * Handler interface definining operation needed to be
+ * peformed (by a concrete implementor) when the "endAttributes"
+ * director callback happens.
+ *
+ * @author Andrea Gazzarini
+ */
+ interface EndAttributesHandler {
+
+ /**
+ * Concrete implementor must define in this method what
+ * needs to be done when the corresponding director callback
+ * happens (@see {@link MBeanCapabilityBuilder#endAttributes()}
+ *
+ * @throws BuilderException when a failure is raised inside the concrete implementation.
+ */
+ void endAttributes() throws BuilderException;
+ };
+
+ /**
+ * This is the concrete implementation of the internal interface EndAttributesHandler
+ * that is activated when this builder detects the presence of at least one property on the
+ * capability class.
+ */
+ final EndAttributesHandler _atLeastThereIsOneProperty = new EndAttributesHandler() {
+
+ /**
+ * Creates the QName array instance member and the corresponding
+ * accessor getPropertyNames().
+ *
+ * @throws BuilderException when the member above cannot be added to the capability class.
+ */
+ public void endAttributes() throws BuilderException
+ {
+ try
+ {
+ _properties.deleteCharAt(_properties.length()-1);
+ _properties.append("};");
+
+ CtField properties = CtField.make(_properties.toString(), _capabilityClassDefinition);
+
+ _capabilityClassDefinition.addField(properties);
+
+ CtMethod getPropertyNames = CtNewMethod.make(
+ GET_PROPERTY_NAMES_METHOD_WITH_ARRAY,
+ _capabilityClassDefinition);
+ _capabilityClassDefinition.addMethod(getPropertyNames);
+ } catch(Exception exception)
+ {
+ throw new BuilderException(exception);
+ }
+ }
+ };
+
+ /**
+ * This is the concrete implementation of the internal interface EndAttributesHandler
+ * that is activated when this builder detects that there are no properties defined for
+ * the capability class.
+ */
+ final EndAttributesHandler _noPropertyHasBeenDefined= new EndAttributesHandler()
+ {
+ /**
+ * Creates the getPropertyNames() that simply returns an empty QName array.
+ *
+ * @throws BuilderException when the member above cannot be added to the capability class.
+ */
+ public void endAttributes() throws BuilderException
+ {
+ try
+ {
+ CtMethod getPropertyNames = CtNewMethod.make(
+ GET_PROPERTY_NAMES_METHOD_WITH_EMPTY_ARRAY,
+ _capabilityClassDefinition);
+ _capabilityClassDefinition.addMethod(getPropertyNames);
+ } catch(Exception exception)
+ {
+ throw new BuilderException(exception);
+ }
+ }
+ };
+
+ /**
+ * This is the active state for this builder when the requested class has never been
+ * built.
+ */
+ IArtifactBuilder _classNotAvailable = new IArtifactBuilder()
+ {
+
+ /**
+ * Build process begins.
+ * The given object name is used to build a minimal definition of the product class.
+ *
+ * @param objectName the name of the JMX entity.
+ * @throws BuilderException when the initial definiton of the capability cannot be created.
+ */
+ public void begin(ObjectName objectName) throws BuilderException
+ {
+ String className = objectName.getKeyProperty(Names.CLASS);
+ ClassPool pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(MBeanCapabilityBuilder.class));
+ pool.importPackage(QName.class.getPackage().getName());
+ pool.importPackage(ObjectName.class.getPackage().getName());
+ pool.importPackage(QManFault.class.getPackage().getName());
+ pool.importPackage(Names.class.getPackage().getName());
+ pool.importPackage(Result.class.getPackage().getName());
+ pool.importPackage(NoSuchAttributeFault.class.getPackage().getName());
+ pool.importPackage(EntityInstanceNotFoundFault.class.getPackage().getName());
+ pool.importPackage(MethodInvocationFault.class.getPackage().getName());
+
+ _capabilityClassDefinition = pool.makeClass("org.apache.qpid.management.wsdm.capabilities."+className);
+ try
+ {
+ _capabilityClassDefinition.setSuperclass(pool.get(MBeanCapability.class.getName()));
+ } catch(Exception exception)
+ {
+ throw new BuilderException(exception);
+ }
+ }
+
+ /**
+ * Director callback.
+ * All attributes have been notified.
+ *
+ * This builder is using this callback in order to create the initial
+ * properties QNames declaration.
+ *
+ */
+ public void endAttributes() throws BuilderException
+ {
+ _endAttributeHandler.endAttributes();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void endOperations() throws BuilderException
+ {
+ try
+ {
+ _capabilityClass = _capabilityClassDefinition.toClass(
+ QManAdapterCapability.class.getClassLoader(),
+ QManAdapterCapability.class.getProtectionDomain());
+ } catch (Exception exception)
+ {
+ throw new BuilderException(exception);
+ }
+ }
+
+ /**
+ * Director callback.
+ * Attrbute metadata notification. With this callback the director informs this builder that the
+ * currently processed MBean has an attribute with the given metadata.
+ * This builder uses this information in order to add a property and the corresponding accessors
+ * to the capability class that is going to be built.
+ *
+ * @throws BuilderException bytecode manipulation / creation failure.
+ */
+ public void onAttribute(MBeanAttributeInfo attribute) throws BuilderException
+ {
+ String name = attribute.getName();
+ String type = attribute.getType();
+
+ try
+ {
+ type = Class.forName(type).getCanonicalName();
+
+ addPropertyMemberInstance(type, name);
+
+ String nameForAccessors = getNameForAccessors(name);
+
+ if (attribute.isReadable())
+ {
+ String accessor = generateGetter(type, nameForAccessors,name);
+ CtMethod getter = CtNewMethod.make(accessor,_capabilityClassDefinition);
+ _capabilityClassDefinition.addMethod(getter);
+ appendToPropertiesArray(name);
+
+ LOGGER.debug(
+ Messages.QMAN_200043_GENERATED_ACCESSOR_METHOD,
+ _objectName,
+ accessor);
+ }
+
+ if (attribute.isWritable())
+ {
+ String accessor = generateSetter(type, nameForAccessors,name);
+ CtMethod setter = CtNewMethod.make(accessor,_capabilityClassDefinition);
+ _capabilityClassDefinition.addMethod(setter);
+
+ LOGGER.debug(
+ Messages.QMAN_200043_GENERATED_ACCESSOR_METHOD,
+ _objectName,
+ accessor);
+ }
+ } catch(Exception exception)
+ {
+ throw new BuilderException(exception);
+ }
+ }
+
+ public void onOperation(MBeanOperationInfo operation) throws BuilderException
+ {
+ StringBuilder method = new StringBuilder();
+ try
+ {
+ method
+ .append("public Result ")
+ .append(operation.getName())
+ .append("( ");
+
+ for (MBeanParameterInfo parameter: operation.getSignature())
+ {
+ method
+ .append(Class.forName(parameter.getType()).getCanonicalName())
+ .append(' ')
+ .append(parameter.getName())
+ .append(',');
+ }
+
+ method.deleteCharAt(method.length()-1);
+ method.append(") throws EntityInstanceNotFoundFault, MethodInvocationFault,QManFault { return invoke(")
+ .append("\"").append(operation.getName()).append("\"")
+ .append(", new Object[]{ ");
+
+ for (MBeanParameterInfo parameter: operation.getSignature())
+ {
+ method.append(parameter.getName())
+ .append(',');
+ }
+
+ method.deleteCharAt(method.length()-1);
+ method.append("}, new String[]{ ");
+
+ for (MBeanParameterInfo parameter: operation.getSignature())
+ {
+ method
+ .append("\"")
+ .append(parameter.getType())
+ .append("\",");
+ }
+ method.deleteCharAt(method.length()-1);
+ method.append("}); }");
+
+ String methodAsString = method.toString();
+ methodAsString = methodAsString.replace("new Object[]{}","null");
+ methodAsString = methodAsString.replace("new String[]{}","null");
+
+ CtMethod definition = CtNewMethod.make(methodAsString,_capabilityClassDefinition);
+ _capabilityClassDefinition.addMethod(definition);
+ } catch(Exception exception)
+ {
+ throw new BuilderException(exception);
+ } finally {
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug(
+ Messages.QMAN_200044_GENERATED_METHOD,
+ _objectName,
+ method.toString());
+ }
+ }
+ }
+
+ public void setEnvironment(Environment environment)
+ {
+ // Nothing to do here...
+ }
+ };
+
+ StringBuilder _properties = new StringBuilder("private static final QName[] PROPERTIES = new QName[]{ ");
+ private Class<MBeanCapability> _capabilityClass;
+ CtClass _capabilityClassDefinition;
+ EndAttributesHandler _endAttributeHandler = _noPropertyHasBeenDefined;
+
+ private ObjectName _objectName;
+
+ IArtifactBuilder _state;
+
+ /**
+ * Director callback.
+ * Attrbute metadata notification. With this callback the director informs this builder that the
+ * currently processed MBean has an attribute with the given metadata.
+ * This builder uses this information in order to add a property and the corresponding accessors
+ * to the capability class that is going to be built.
+ *
+ * @throws BuilderException bytecode manipulation / creation failure.
+ */
+ public void onAttribute(MBeanAttributeInfo attribute) throws BuilderException
+ {
+ _state.onAttribute(attribute);
+ }
+
+ /**
+ * First callback : this method is called at the begin of the director process.
+ * Contains builder initialization code.
+ *
+ * @param objectName the name of the target JMX entity of this capability.
+ * @throws BuilderException when the initialization fails.
+ */
+ @SuppressWarnings("unchecked")
+ public void begin(ObjectName objectName) throws BuilderException
+ {
+ try
+ {
+ this._objectName = objectName;
+ String className = objectName.getKeyProperty(Names.CLASS);
+ _capabilityClass = (Class<MBeanCapability>) Class.forName("org.apache.qpid.management.wsdm.capabilities."+className);
+ _state = new DummyCapabilityBuilder();
+ } catch (ClassNotFoundException exception)
+ {
+ _state = _classNotAvailable;
+ }
+
+ _state.begin(objectName);
+ }
+
+ /**
+ * Director callback.
+ * Operation metadata notification. With this callback the director informs this builder that the
+ * currently processed MBean has an operation with the given metadata.
+ * This builder uses this information in order to add a method to the capability class that is
+ * going to be built.
+ *
+ * For example, let's suppose that an operation like that is detected on the MBean :
+ *
+ * public void purge(int request)
+ *
+ * then the capability will be enrichied with the following method :
+ *
+ * public void purge(int request) throws QManFault {
+ * invoke(
+ * "purge",
+ * new Object[]{request},
+ * new String[]{int.class.getName()});
+ * }
+ *
+ * @throws BuilderException bytecode manipulation / creation failure.
+ */
+ public void onOperation(MBeanOperationInfo operation) throws BuilderException
+ {
+ _state.onOperation(operation);
+ }
+
+ /**
+ * Returns the capability class (the product of this builder).
+ *
+ * @return the capability class (the product of this builder).
+ */
+ Class<MBeanCapability> getCapabilityClass()
+ {
+ return _capabilityClass;
+ }
+
+ /**
+ * Determines what needs to be done when all attributes
+ * metadata has been notified to this builder.
+ * Capability class must have an array member with all defined
+ * properties and a getter method that returns it.
+ * In this method those two members are declared (obviously only
+ * if this capability has at least one property).
+ *
+ * @throws BuilderException when something fails during this phase.
+ */
+ public void endAttributes() throws BuilderException
+ {
+ _state.endAttributes();
+ }
+
+ /**
+ * Director callback.
+ * This method is notified when all operations metadata has been
+ * notified to this builder.
+ * This is the place where the capability class is created, defined and loaded by the JVM.
+ *
+ * @throws BuilderException issues on this method are basically class loading related.
+ */
+ @SuppressWarnings("unchecked")
+ public void endOperations() throws BuilderException
+ {
+ _state.endOperations();
+ }
+
+ /**
+ * Injects the module environment on this builder.
+ *
+ * @param environment the module environment.
+ */
+ public void setEnvironment(Environment environment)
+ {
+ // Nothing to do here...
+ }
+
+ /**
+ * Generates the get accessor method for the given property.
+ *
+ * @param type the type of the property.
+ * @param name the name of the property with the first letter capitalized.
+ * @param plainName the plain name of the property.
+ * @return the getter method (as a string).
+ */
+ String generateGetter(String type, String name,String plainName)
+ {
+ return new StringBuilder()
+ .append("public ")
+ .append(type)
+ .append(' ')
+ .append("get")
+ .append(name)
+ .append("() throws NoSuchAttributeFault,EntityInstanceNotFoundFault,QManFault { return (")
+ .append(type)
+ .append(") getAttribute(\"")
+ .append(plainName)
+ .append("\"); }")
+ .toString();
+ }
+
+ /**
+ * Generates the set accessor method for the given property.
+ *
+ * @param type the type of the property.
+ * @param name the name of the property with the first letter capitalized.
+ * @param plainName the plain name of the property.
+ * @return the setter method (as a string).
+ */
+ String generateSetter(String type, String name, String plainName)
+ {
+ return new StringBuilder()
+ .append("public void ")
+ .append("set")
+ .append(name)
+ .append("(")
+ .append(type)
+ .append(" newValue) throws NoSuchAttributeFault,EntityInstanceNotFoundFault,QManFault {")
+ .append(" setAttribute(\"")
+ .append(plainName)
+ .append("\", newValue); }")
+ .toString();
+ }
+
+ /**
+ * Appends the given attribute name to the properties array declared as an
+ * instance member of the capability class.
+ *
+ * @param attributeName the name of the attribute.
+ */
+ private void appendToPropertiesArray(String attributeName)
+ {
+ _properties.append("new QName(Names.NAMESPACE_URI, \"")
+ .append(attributeName)
+ .append("\", Names.PREFIX),");
+
+ _endAttributeHandler = _atLeastThereIsOneProperty;
+ }
+
+ /**
+ * Adds a new property member instance to the capability class.
+ *
+ * @param type the type of the property.
+ * @param name the name of the property.
+ * @throws CannotCompileException when the property cannot be added.
+ */
+ private void addPropertyMemberInstance(String type, String name) throws CannotCompileException
+ {
+ StringBuilder buffer = new StringBuilder()
+ .append("private ")
+ .append(type)
+ .append(' ')
+ .append(name)
+ .append(';');
+
+ CtField field= CtField.make(buffer.toString(),_capabilityClassDefinition);
+ _capabilityClassDefinition.addField(field);
+ }
+
+ /**
+ * Returns a name that will be used in accessor methods.
+ * That name will differ from the given one because the first letter will be capitalized.
+ * For example, if the given name is "name" the return value will be "Name".
+ *
+ * @param name the plain name of the attribute.
+ * @return a capitalized version of the given name to be used in accessors.
+ */
+ String getNameForAccessors(String name)
+ {
+ return
+ Character.toUpperCase(name.charAt(0)) +
+ name.substring(1);
+ }
+
+} \ No newline at end of file