git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@422696 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.mosgi.jmx.agent/pom.xml b/org.apache.felix.mosgi.jmx.agent/pom.xml
new file mode 100644
index 0000000..6414390
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/pom.xml
@@ -0,0 +1,65 @@
+<project>
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix</artifactId>
+ <version>0.8.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>osgi-bundle</packaging>
+ <name>MOSGi JMX agent</name>
+ <artifactId>org.apache.felix.mosgi.jmx.agent</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>${pom.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>${pom.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>${pom.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix.plugins</groupId>
+ <artifactId>maven-osgi-plugin</artifactId>
+ <version>${pom.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <osgiManifest>
+ <bundleName>MOSGi JMX-MX4J Agent Service</bundleName>
+ <bundleDescription>MOSGi JMX-MX4J Agent Service</bundleDescription>
+ <bundleActivator>auto-detect</bundleActivator>
+ <bundleDocUrl>http://oscar-osgi.sf.net/obr2/${pom.artifactId}/</bundleDocUrl>
+ <bundleUrl>http://oscar-osgi.sf.net/obr2/${pom.artifactId}/${pom.artifactId}-${pom.version}.jar</bundleUrl>
+ <bundleSource>http://oscar-osgi.sf.net/obr2/${pom.artifactId}/${pom.artifactId}-${pom.version}-src.jar</bundleSource>
+ <bundleSymbolicName>${pom.artifactId}</bundleSymbolicName>
+ <exportPackage>
+ ${pom.artifactId};specification-version="1.0.0",
+ org.apache.felix.mosgi.jmx.agent.mx4j.server;specification-version="1.0.0",
+ org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor;specification-version="1.0.0"
+ </exportPackage>
+ <importPackage>
+ org.osgi.service.log;specification-version="1.0.0",
+ org.osgi.framework;specification-version="1.0.0",
+ org.apache.felix.mosgi.jmx.agent.mx4j.server;specification-version="1.0.0",
+ org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor;specification-version="1.0.0",
+ javax.management;specification-version="1.0.0",
+ javax.management.loading;specification-version="1.0.0"
+ </importPackage>
+ </osgiManifest>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/AgentActivator.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/AgentActivator.java
new file mode 100644
index 0000000..86c3e0b
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/AgentActivator.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2003 Stéphane Frénot
+ * Copyright (C) 2003 INSA Lyon Laboratoire CITI, France
+ * Copyright (C) 2003 INRIA projet ARES, France
+ *
+ * This program is licensed under the Apache Software License
+ * version 1.1; refer to the ASL-LICENSE.txt file included with
+ * this program for details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Contact: Stéphane Frénot stephane.frenot@insa-lyon.fr
+ * Contributor(s): Didier Donsez, didier.donsez@imag.fr
+ *
+ * $Id: AgentActivator.java,v 1.6 2005/09/12 09:52:13 sfrenot Exp $
+ *
+ * TODO : profile should only call bc.getProperty
+**/
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent;
+
+import java.util.StringTokenizer;
+
+import java.lang.management.ManagementFactory;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.Constants;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.InvalidSyntaxException;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+import javax.management.MBeanServerFactory;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanRegistrationException;
+import javax.management.NotCompliantMBeanException;
+
+import org.osgi.service.log.LogService;
+import org.apache.felix.framework.cache.BundleCache;
+
+public class AgentActivator implements BundleActivator, ServiceListener {
+ private MBeanServer server;
+ private ServiceRegistration serverRegistration;
+ static private BundleContext bc;
+ private String version = null;
+
+ //BundleActivator interface
+
+ public void start(BundleContext context) throws Exception {
+ AgentActivator.bc=context;
+ this.version=(String)bc.getBundle().getHeaders().get(Constants.BUNDLE_VERSION);
+ AgentActivator.log(LogService.LOG_INFO, "Starting JMX Agent "+version,null);
+ String profile=bc.getProperty(BundleCache.CACHE_PROFILE_PROP);
+ if (profile==null){
+ profile=System.getProperty(BundleCache.CACHE_PROFILE_PROP);
+ }
+ String virtual=bc.getProperty("insa.jmxconsole.core."+profile);
+ StringTokenizer st=new StringTokenizer(System.getProperty("java.version"), ".");
+ st.nextToken();
+ int minor=Integer.parseInt(st.nextToken());
+
+ System.out.println("VIRTUAL OOOO "+virtual+" : "+profile);
+ this.startAgent(virtual, minor);
+ this.registerExistingMBeans();
+ bc.addServiceListener(this);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ AgentActivator.log(LogService.LOG_INFO, "JMX Agent stopping "+version,null);
+// MBeanServerFactory.releaseMBeanServer(this.server);
+ this.unregisterExistingMBeans();
+ this.serverRegistration.unregister();
+ AgentActivator.log(LogService.LOG_INFO, "JMX Agent stopped "+version,null);
+ AgentActivator.bc=null;
+ this.serverRegistration=null;
+ this.server=null;
+ }
+
+ // Service Listener Interface
+ public void serviceChanged(ServiceEvent serviceEvent) {
+ ServiceReference serviceReference = serviceEvent.getServiceReference();
+ if (isMBean(serviceReference)) {
+ if (serviceEvent.getType() == ServiceEvent.REGISTERED) {
+ this.register(serviceReference);
+ } else if (serviceEvent.getType() == ServiceEvent.UNREGISTERING) {
+ this.unregister(serviceReference);
+ } else if (serviceEvent.getType() == ServiceEvent.MODIFIED) {
+ this.unregister(serviceReference);
+ this.register(serviceReference);
+ }
+ }
+ }
+
+ private static void log(int prio, String message, Throwable t){
+ if (AgentActivator.bc!=null){
+ ServiceReference logSR=AgentActivator.bc.getServiceReference(LogService.class.getName());
+ if (logSR!=null){
+ ((LogService)AgentActivator.bc.getService(logSR)).log(prio, message, t);
+ }else{
+ System.out.println("No Log Service");
+ }
+ }else{
+ System.out.println(AgentActivator.class.getName()+": No bundleContext");
+ }
+ }
+
+ private void startAgent(String virtual, int minor){
+ if (virtual==null && minor >=5){
+ Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+ this.server= ManagementFactory.getPlatformMBeanServer();
+ AgentActivator.log(LogService.LOG_DEBUG, "A jdk1.5 agent started "+this.server,null);
+ }else {
+ Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+ this.server = MBeanServerFactory.createMBeanServer();
+ AgentActivator.log(LogService.LOG_DEBUG, "A lightweight agent started "+this.server,null);
+ }
+ this.serverRegistration=bc.registerService(MBeanServer.class.getName(), this.server, null);
+ AgentActivator.log(LogService.LOG_INFO, "JMX Agent started "+version,null);
+ }
+
+ private boolean isMBean(ServiceReference serviceReference) {
+ String[] objectClasses = (String[]) serviceReference .getProperty(Constants.OBJECTCLASS);
+ if (objectClasses == null){
+ return false;
+ }
+ int i = 0;
+ for (; i < objectClasses.length; i++) {
+ if (objectClasses[i].endsWith("MBean")){
+ break;
+ }
+ // "Static MBean interfaces" ends by "MBean"
+ if (objectClasses[i].equals(DynamicMBean.class.getName())){
+ break;
+ }
+ }
+ if (i == objectClasses.length){
+ return false;
+ }
+ return true;
+ }
+
+ private void registerExistingMBeans() {
+ ServiceReference[] serviceReferences = null;
+ try {
+ serviceReferences = bc.getServiceReferences(null, null);
+ } catch (InvalidSyntaxException e) {
+ // Never Thrown
+ }
+ if (serviceReferences == null){
+ return;
+ }
+ for (int i = 0; i < serviceReferences.length; i++) {
+ if (isMBean(serviceReferences[i])){
+ this.register(serviceReferences[i]);
+ }
+ }
+ }
+
+ private void unregisterExistingMBeans() {
+ ServiceReference[] serviceReferences = null;
+ try {
+ serviceReferences = bc.getServiceReferences(null, null);
+ } catch (InvalidSyntaxException e) {
+ // never thrown
+ }
+ if (serviceReferences == null){
+ return;
+ }
+ for (int i = 0; i < serviceReferences.length; i++) {
+ if (isMBean(serviceReferences[i])){
+ unregister(serviceReferences[i]);
+ }
+ }
+ }
+
+ private void register(ServiceReference serviceReference) {
+ String name = this.getObjectNameString(serviceReference);
+
+ Object mbean = bc.getService(serviceReference);
+ ObjectName objectName = null;
+ try {
+ // Unique identification of MBeans
+ objectName = new ObjectName(name);
+ } catch (MalformedObjectNameException e) {
+ e.printStackTrace();
+ } catch (NullPointerException e) {
+ e.printStackTrace();
+ }
+ try {
+ // Uniquely identify the MBean and register it with the MBeanServer
+ server.registerMBean(mbean, objectName);
+ } catch (InstanceAlreadyExistsException e1) {
+ e1.printStackTrace();
+ } catch (MBeanRegistrationException e1) {
+ e1.printStackTrace();
+ } catch (NotCompliantMBeanException e1) {
+ e1.printStackTrace();
+ }
+ }
+
+ private void unregister(ServiceReference serviceReference) {
+ String name = getObjectNameString(serviceReference);
+ try {
+ // TODO check if unregisterMBean occurs really
+ server.unregisterMBean(new ObjectName(name));
+ } catch (InstanceNotFoundException e) {
+ // do nothing;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private String getObjectNameString(ServiceReference serviceReference) {
+ String objectName = (String) serviceReference.getProperty(org.apache.felix.mosgi.jmx.agent.Constants.OBJECTNAME);
+ if (objectName != null){
+ // If objectName starts with the colon character (:), the domain part of the object name is the domain of the agent.
+ if(objectName.startsWith(":")){
+ // invokes the getDefaultDomain() method of the Framework class to obtain this information.
+ objectName=server.getDefaultDomain()+objectName;
+ }
+ return objectName;
+ }
+
+System.out.println("No "+org.apache.felix.mosgi.jmx.agent.Constants.OBJECTNAME+" constant for "+serviceReference+" MBean. Trying to build it");
+
+ String[] objectClasses = (String[]) serviceReference.getProperty(Constants.OBJECTCLASS);
+ if (objectClasses == null){
+ return null;
+ }
+ int i = 0;
+ for (; i < objectClasses.length; i++) {
+ if (objectClasses[i].endsWith("MBean")){
+ break;
+ }
+ }
+ // TODO do nothing if there is several MBean inplemented interfaces
+ if (i == objectClasses.length){
+ return null;
+ }
+
+ StringBuffer sb=new StringBuffer(server.getDefaultDomain());
+ sb.append(":");
+ sb.append("BundleId=");
+ sb.append(serviceReference.getBundle().getBundleId());
+ sb.append(", ServiceId=");
+ sb.append(serviceReference.getProperty(Constants.SERVICE_ID));
+ sb.append(", ObjectClass=");
+ sb.append(objectClasses[i]);
+ Object servicePID=serviceReference.getProperty(Constants.SERVICE_PID);
+ if (servicePID==null){
+ sb.append(", servicePID=NA");
+ }else{
+ sb.append(", servicePID=");
+ sb.append(servicePID);
+ }
+System.out.println("==>"+sb.toString());
+ return sb.toString();
+
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/Constants.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/Constants.java
new file mode 100644
index 0000000..0cc9a93
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/Constants.java
@@ -0,0 +1,4 @@
+package org.apache.felix.mosgi.jmx.agent;
+public interface Constants {
+ public final static String OBJECTNAME="jmxagent.objectname";
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/AbstractDynamicMBean.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/AbstractDynamicMBean.java
new file mode 100644
index 0000000..137945c
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/AbstractDynamicMBean.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+*/
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+
+import javax.management.DynamicMBean;
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import javax.management.AttributeList;
+import javax.management.MBeanInfo;
+import javax.management.Attribute;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.RuntimeErrorException;
+import javax.management.MBeanParameterInfo;
+import javax.management.RuntimeMBeanException;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.Utils;
+
+/**
+ * Utility class that allow the user to easily write DynamicMBeans. <br>
+ * By extending this class, the developer does not have to implement the methods of the DynamicMBean interface, but
+ * has instead to provide only the metadata (by overriding few methods) and the implementation (by implementing
+ * the methods) of the MBean itself. <br>
+ * The methods to override that provides metadata information are usually the following:
+ * <ul>
+ * <li> <code>createMBeanAttributeInfo</code>, if the MBeans has manageable attributes </li>
+ * <li> <code>createMBeanOperationInfo</code>, if the MBeans has manageable operations </li>
+ * <li> <code>createMBeanNotificationInfo</code>, if the MBeans has manageable notifications </li>
+ * <li> <code>createMBeanConstructorInfo</code>, if the MBeans has manageable constructors </li>
+ * <li> <code>getMBeanDescription</code> </li>
+ * </ul>
+ * For example, the following MBean only has one manageable attribute:
+ * <pre>
+ * public class SimpleDynamic extends AbstractDynamicMBean
+ * {
+ * protected MBeanAttributeInfo[] createMBeanAttributeInfo()
+ * {
+ * return new MBeanAttributeInfo[]
+ * {
+ * new MBeanAttributeInfo("Name", String.class.getName(), "The name", true, true, false)
+ * };
+ * }
+ *
+ * protected String getMBeanDescription()
+ * {
+ * return "A simple DynamicMBean";
+ * }
+ *
+ * public String getName() { ... }
+ *
+ * public void setName(String name) { ... }
+ * }
+ * </pre>
+ * It is responsibility of the developer to specify the metadata <b>and</b> implement the methods specified by the
+ * metadata, that will be invoked via reflection by the AbstractDynamicMBean class. For this reason, the methods
+ * belonging to the MBean implementation (in the case above <code>getName()</code> and <code>setName(...)</code>)
+ * must be public.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public abstract class AbstractDynamicMBean implements DynamicMBean
+{
+ private MBeanInfo info;
+ private Object resource;
+
+ /**
+ * Only subclasses can create a new instance of an AbstractDynamicMBean.
+ * @see #createMBeanConstructorInfo
+ */
+ protected AbstractDynamicMBean()
+ {
+ }
+
+ /**
+ * Returns the value of the manageable attribute, as specified by the DynamicMBean interface.
+ * @see #createMBeanAttributeInfo
+ */
+ public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException
+ {
+ if (attribute == null) throw new AttributeNotFoundException("Attribute " + attribute + " not found");
+
+ Object resource = null;
+ MBeanInfo info = null;
+ synchronized (this)
+ {
+ resource = getResourceOrThis();
+ info = getMBeanInfo();
+ }
+
+ MBeanAttributeInfo[] attrs = info.getAttributes();
+ if (attrs == null || attrs.length == 0) throw new AttributeNotFoundException("No attributes defined for this MBean");
+
+ for (int i = 0; i < attrs.length; ++i)
+ {
+ MBeanAttributeInfo attr = attrs[i];
+ if (attr == null) continue;
+
+ if (attribute.equals(attr.getName()))
+ {
+ if (!attr.isReadable()) throw new ReflectionException(new NoSuchMethodException("No getter defined for attribute: " + attribute));
+
+ // Found, invoke via reflection
+ String prefix = null;
+ if (attr.isIs())
+ prefix = "is";
+ else
+ prefix = "get";
+
+ try
+ {
+ return invoke(resource, prefix + attr.getName(), new Class[0], new Object[0]);
+ }
+ catch (InvalidAttributeValueException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+ }
+
+ throw new AttributeNotFoundException("Attribute " + attribute + " not found");
+ }
+
+ /**
+ * Returns the manageable attributes, as specified by the DynamicMBean interface.
+ */
+ public AttributeList getAttributes(String[] attributes)
+ {
+ AttributeList list = new AttributeList();
+
+ if (attributes != null)
+ {
+ for (int i = 0; i < attributes.length; ++i)
+ {
+ String attribute = attributes[i];
+ try
+ {
+ Object result = getAttribute(attribute);
+ list.add(new Attribute(attribute, result));
+ }
+ catch (AttributeNotFoundException ignored)
+ {
+ }
+ catch (MBeanException ignored)
+ {
+ }
+ catch (ReflectionException ignored)
+ {
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Returns the MBeaInfo, as specified by the DynamicMBean interface; the default implementation caches the value
+ * returned by {@link #createMBeanInfo} (that is thus called only once).
+ * @see #createMBeanInfo
+ * @see #setMBeanInfo
+ */
+ public synchronized MBeanInfo getMBeanInfo()
+ {
+ if (info == null) setMBeanInfo(createMBeanInfo());
+ return info;
+ }
+
+ /**
+ * Returns the value of the manageable operation as specified by the DynamicMBean interface
+ * @see #createMBeanOperationInfo
+ */
+ public Object invoke(String method, Object[] arguments, String[] params) throws MBeanException, ReflectionException
+ {
+ if (method == null) throw new IllegalArgumentException("Method name cannot be null");
+ if (arguments == null) arguments = new Object[0];
+ if (params == null) params = new String[0];
+
+ Object resource = null;
+ MBeanInfo info = null;
+ synchronized (this)
+ {
+ resource = getResourceOrThis();
+ info = getMBeanInfo();
+ }
+
+ MBeanOperationInfo[] opers = info.getOperations();
+ if (opers == null || opers.length == 0) throw new ReflectionException(new NoSuchMethodException("No operations defined for this MBean"));
+
+ for (int i = 0; i < opers.length; ++i)
+ {
+ MBeanOperationInfo oper = opers[i];
+ if (oper == null) continue;
+
+ if (method.equals(oper.getName()))
+ {
+ MBeanParameterInfo[] parameters = oper.getSignature();
+ if (params.length != parameters.length) continue;
+
+ String[] signature = new String[parameters.length];
+ for (int j = 0; j < signature.length; ++j)
+ {
+ MBeanParameterInfo param = parameters[j];
+ if (param == null)
+ signature[j] = null;
+ else
+ signature[j] = param.getType();
+ }
+
+ if (Utils.arrayEquals(params, signature))
+ {
+ // Found the right operation
+ try
+ {
+ Class[] classes = Utils.loadClasses(resource.getClass().getClassLoader(), signature);
+ return invoke(resource, method, classes, arguments);
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (InvalidAttributeValueException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+ }
+ }
+
+ throw new ReflectionException(new NoSuchMethodException("Operation " + method + " with signature " + Arrays.asList(params) + " is not defined for this MBean"));
+ }
+
+ /**
+ * Sets the value of the manageable attribute, as specified by the DynamicMBean interface.
+ * @see #createMBeanAttributeInfo
+ */
+ public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
+ {
+ if (attribute == null) throw new AttributeNotFoundException("Attribute " + attribute + " not found");
+
+ Object resource = null;
+ MBeanInfo info = null;
+ synchronized (this)
+ {
+ resource = getResourceOrThis();
+ info = getMBeanInfo();
+ }
+
+ MBeanAttributeInfo[] attrs = info.getAttributes();
+ if (attrs == null || attrs.length == 0) throw new AttributeNotFoundException("No attributes defined for this MBean");
+
+ for (int i = 0; i < attrs.length; ++i)
+ {
+ MBeanAttributeInfo attr = attrs[i];
+ if (attr == null) continue;
+
+ if (attribute.getName().equals(attr.getName()))
+ {
+ if (!attr.isWritable()) throw new ReflectionException(new NoSuchMethodException("No setter defined for attribute: " + attribute));
+
+ try
+ {
+ String signature = attr.getType();
+ Class cls = Utils.loadClass(resource.getClass().getClassLoader(), signature);
+ invoke(resource, "set" + attr.getName(), new Class[]{cls}, new Object[]{attribute.getValue()});
+ return;
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+ }
+
+ throw new AttributeNotFoundException("Attribute " + attribute + " not found");
+ }
+
+ /**
+ * Sets the manageable attributes, as specified by the DynamicMBean interface.
+ */
+ public AttributeList setAttributes(AttributeList attributes)
+ {
+ AttributeList list = new AttributeList();
+
+ if (attributes != null)
+ {
+ for (int i = 0; i < attributes.size(); ++i)
+ {
+ Attribute attribute = (Attribute)attributes.get(i);
+ try
+ {
+ setAttribute(attribute);
+ list.add(attribute);
+ }
+ catch (AttributeNotFoundException ignored)
+ {
+ }
+ catch (InvalidAttributeValueException ignored)
+ {
+ }
+ catch (MBeanException ignored)
+ {
+ }
+ catch (ReflectionException ignored)
+ {
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * @deprecated Replaced by {@link #invoke(Object,String,Class[],Object[])}. <br>
+ * The resource passed is the resource as set by {@link #setResource} or - if it is null - 'this' instance. <br>
+ * This method is deprecated because it is not thread safe.
+ */
+ protected Object invoke(String name, Class[] params, Object[] args) throws InvalidAttributeValueException, MBeanException, ReflectionException
+ {
+ Object resource = getResourceOrThis();
+ return invoke(resource, name, params, args);
+ }
+
+ /**
+ * Looks up the method to call on given resource and invokes it.
+ * The default implementation requires that the methods that implement attribute and operation behavior
+ * on the resource object are public, but it is possible to override this behavior, and call
+ * also private methods.
+ * @see #findMethod
+ * @see #invokeMethod
+ */
+ protected Object invoke(Object resource, String name, Class[] params, Object[] args) throws InvalidAttributeValueException, MBeanException, ReflectionException
+ {
+ try
+ {
+ Class cls = resource.getClass();
+ Method method = findMethod(cls, name, params);
+ return invokeMethod(method, resource, args);
+ }
+ catch (NoSuchMethodException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (IllegalAccessException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (IllegalArgumentException x)
+ {
+ throw new InvalidAttributeValueException(x.toString());
+ }
+ catch (InvocationTargetException x)
+ {
+ Throwable t = x.getTargetException();
+ if (t instanceof RuntimeException)
+ throw new RuntimeMBeanException((RuntimeException)t);
+ else if (t instanceof Exception) throw new MBeanException((Exception)t);
+ throw new RuntimeErrorException((Error)t);
+ }
+ }
+
+ /**
+ * Returns the (public) method with the given name and signature on the given class. <br>
+ * Override to return non-public methods, or to map methods to other classes, or to map methods with
+ * different signatures
+ * @see #invoke(String, Class[], Object[])
+ * @see #invokeMethod
+ */
+ protected Method findMethod(Class cls, String name, Class[] params) throws NoSuchMethodException
+ {
+ return cls.getMethod(name, params);
+ }
+
+ /**
+ * Invokes the given method on the given resource object with the given arguments. <br>
+ * Override to map methods to other objects, or to map methods with different arguments
+ * @see #invoke(String, Class[], Object[])
+ * @see #findMethod
+ */
+ protected Object invokeMethod(Method method, Object resource, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
+ {
+ return method.invoke(resource, args);
+ }
+
+ private Object getResourceOrThis()
+ {
+ Object resource = getResource();
+ if (resource == null) resource = this;
+ return resource;
+ }
+
+ /**
+ * Returns the resource object on which invoke attribute's getters, attribute's setters and operation's methods
+ * @see #setResource
+ */
+ protected synchronized Object getResource()
+ {
+ return resource;
+ }
+
+ /**
+ * Specifies the resource object on which invoke attribute's getters, attribute's setters and operation's methods.
+ * @see #getResource
+ */
+ public synchronized void setResource(Object resource)
+ {
+ this.resource = resource;
+ }
+
+ /**
+ * Sets the MBeanInfo object cached by this instance. <br>
+ * The given MBeanInfo is not cloned.
+ * @see #getMBeanInfo
+ */
+ protected synchronized void setMBeanInfo(MBeanInfo info)
+ {
+ this.info = info;
+ }
+
+ /**
+ * Creates the MBeanInfo for this instance, calling in succession factory methods that the user can override.
+ * Information to create MBeanInfo are taken calling the following methods:
+ * <ul>
+ * <li><code>{@link #createMBeanAttributeInfo}</code></li>
+ * <li><code>{@link #createMBeanConstructorInfo}</code></li>
+ * <li><code>{@link #createMBeanOperationInfo}</code></li>
+ * <li><code>{@link #createMBeanNotificationInfo}</code></li>
+ * <li><code>{@link #getMBeanClassName}</code></li>
+ * <li><code>{@link #getMBeanDescription}</code></li>
+ * </ul>
+ */
+ protected MBeanInfo createMBeanInfo()
+ {
+ MBeanAttributeInfo[] attrs = createMBeanAttributeInfo();
+ MBeanConstructorInfo[] ctors = createMBeanConstructorInfo();
+ MBeanOperationInfo[] opers = createMBeanOperationInfo();
+ MBeanNotificationInfo[] notifs = createMBeanNotificationInfo();
+ String className = getMBeanClassName();
+ String description = getMBeanDescription();
+ return new MBeanInfo(className, description, attrs, ctors, opers, notifs);
+ }
+
+ /**
+ * To be overridden to return metadata information about manageable attributes.
+ */
+ protected MBeanAttributeInfo[] createMBeanAttributeInfo()
+ {
+ return new MBeanAttributeInfo[0];
+ }
+
+ /**
+ * To be overridden to return metadata information about manageable constructors.
+ */
+ protected MBeanConstructorInfo[] createMBeanConstructorInfo()
+ {
+ return new MBeanConstructorInfo[0];
+ }
+
+ /**
+ * To be overridden to return metadata information about manageable operations.
+ */
+ protected MBeanOperationInfo[] createMBeanOperationInfo()
+ {
+ return new MBeanOperationInfo[0];
+ }
+
+ /**
+ * To be overridden to return metadata information about manageable notifications.
+ */
+ protected MBeanNotificationInfo[] createMBeanNotificationInfo()
+ {
+ return new MBeanNotificationInfo[0];
+ }
+
+ /**
+ * To be overridden to return metadata information about the class name of this MBean;
+ * by default returns this class' name.
+ */
+ protected String getMBeanClassName()
+ {
+ return getClass().getName();
+ }
+
+ /**
+ * To be overridden to return metadata information about the description of this MBean.
+ */
+ protected String getMBeanDescription()
+ {
+ return null;
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/ImplementationException.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/ImplementationException.java
new file mode 100644
index 0000000..9761cbb
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/ImplementationException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j;
+
+/**
+ * Thrown when an internal error in the MX4J implementation is detected.
+ * Contact the MX4J mailing list for support when this exception is thrown in your programs.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ImplementationException extends RuntimeException
+{
+ public ImplementationException() {}
+ public ImplementationException(String message) {super(message);}
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MBeanDescription.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MBeanDescription.java
new file mode 100644
index 0000000..bcfc687
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MBeanDescription.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Implement this inteface to give descriptions to standard MBean. <p>
+ * The MX4J implementation will look, for every standard MBean, for a class with name composed by
+ * the fully qualified MBean class name + "MBeanDescription".
+ * If such a class is found, the MX4J implementation will call its methods to retrieve description
+ * information about the MBean itself.
+ * MBean descriptions are built-in in DynamicMBean, but not in standard MBeans.
+ * The <a href="http://xdoclet.sourceforge.net">XDoclet</a> tool is used to automate the process of
+ * generating the MBeanDescription classes for a given MBean, along with the MBean interface.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface MBeanDescription
+{
+ /**
+ * Should return the description of the MBean.
+ * For example: "This MBean is the rmiregistry service"
+ */
+ public String getMBeanDescription();
+ /**
+ * Should return the description for the given constructor of the MBean.
+ * For example: "Creates an rmiregistry instance on the specified port"
+ */
+ public String getConstructorDescription(Constructor ctor);
+ /**
+ * Should return the name of the constructor's parameter for the given constructor and parameter index.
+ * For example: "port"
+ */
+ public String getConstructorParameterName(Constructor ctor, int index);
+ /**
+ * Should return the description for the constructor's parameter for the given constructor and parameter index.
+ * For example: "The port on which the rmiregistry will wait on for client requests"
+ */
+ public String getConstructorParameterDescription(Constructor ctor, int index);
+ /**
+ * Should return the description for the specified attribute.
+ * For example: "The port on which the rmiregistry will wait on for client requests"
+ */
+ public String getAttributeDescription(String attribute);
+ /**
+ * Should return the description for the specified operation.
+ * For example: "Binds the given object to the given name"
+ */
+ public String getOperationDescription(Method operation);
+ /**
+ * Should return the name of the operation's parameter for the given operation and parameter index.
+ * For example: "bindName"
+ */
+ public String getOperationParameterName(Method method, int index);
+ /**
+ * Should return the description for the operations's parameter for the given operation and parameter index.
+ * For example: "The name to which the object will be bound to"
+ */
+ public String getOperationParameterDescription(Method method, int index);
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MBeanDescriptionAdapter.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MBeanDescriptionAdapter.java
new file mode 100644
index 0000000..4d11042
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MBeanDescriptionAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j;
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Default implementation for the MBeanDescription interface.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MBeanDescriptionAdapter implements MBeanDescription
+{
+ public String getMBeanDescription()
+ {
+ return "Manageable Bean";
+ }
+
+ public String getConstructorDescription(Constructor ctor)
+ {
+ return "Constructor exposed for management";
+ }
+
+ public String getConstructorParameterName(Constructor ctor, int index)
+ {
+ return "param" + (index + 1);
+ }
+
+ public String getConstructorParameterDescription(Constructor ctor, int index)
+ {
+ return "Constructor's parameter n. " + (index + 1);
+ }
+
+ public String getAttributeDescription(String attribute)
+ {
+ return "Attribute exposed for management";
+ }
+
+ public String getOperationDescription(Method operation)
+ {
+ return "Operation exposed for management";
+ }
+
+ public String getOperationParameterName(Method method, int index)
+ {
+ return "param" + (index + 1);
+ }
+
+ public String getOperationParameterDescription(Method method, int index)
+ {
+ return "Operation's parameter n. " + (index + 1);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MX4JSystemKeys.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MX4JSystemKeys.java
new file mode 100644
index 0000000..35262d9
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/MX4JSystemKeys.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j;
+
+/**
+ * This class holds the system property keys that the MX4J implementation uses to plugin
+ * custom components. <br>
+ * The naming convention is that, for a defined constant, the corrispondent system property
+ * is obtained by converting the constant name to lowercase and by replacing the underscores
+ * with dots so that, for example, the constant <code>MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY</code>
+ * correspond to the system property key <code>mx4j.mbeanserver.classloader.repository</code>
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public final class MX4JSystemKeys
+{
+ /**
+ * Specifies a full qualified class name of a class implementing the {@link mx4j.server.MBeanRepository}
+ * interface, that will be used by the MBeanServer to store information about registered MBeans.
+ */
+ public static final String MX4J_MBEANSERVER_REPOSITORY = "mx4j.mbeanserver.repository";
+
+ /**
+ * Specifies a full qualified class name of a class extending the {@link mx4j.server.ModifiableClassLoaderRepository}
+ * class, that will be used by the MBeanServer to store ClassLoader MBeans that wants to be registered in
+ * the MBeanServer's ClassLoaderRepository.
+ */
+ public static final String MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY = "mx4j.mbeanserver.classloader.repository";
+
+ /**
+ * Specifies the level of logging performed by the MX4J JMX implementation.
+ * Possible value are (case insensitive), from most verbose to least verbose:
+ * <ul>
+ * <li>trace</li>
+ * <li>debug</li>
+ * <li>info</li>
+ * <li>warn</li>
+ * <li>error</li>
+ * <li>fatal</li>
+ * </ul>
+ */
+ public static final String MX4J_LOG_PRIORITY = "mx4j.log.priority";
+
+ /**
+ * Specifies a full qualified class name of a class extending the {@link mx4j.log.Logger} class, that
+ * will be used as prototype for new loggers created.
+ */
+ public static final String MX4J_LOG_PROTOTYPE = "mx4j.log.prototype";
+
+ /**
+ * When this property is set to false (as specified by {@link Boolean#valueOf(String)}), the MX4J
+ * JMX implementation will accept as MBean interfaces of standard MBeans also interfaces defined in
+ * different packages or as nested classes of the MBean class.
+ * So for example, will be possible for a com.foo.Service to have a management interface called
+ * com.bar.ServiceMBean.
+ * If not defined, or if set to true, only MBean interfaces of the same package of the MBean class
+ * are considered valid management interfaces.
+ */
+ public static final String MX4J_STRICT_MBEAN_INTERFACE = "mx4j.strict.mbean.interface";
+
+ /**
+ * Specifies a full qualified class name of a class implementing the {@link mx4j.server.MBeanInvoker} interface,
+ * that will be used as invoker for standard MBeans.
+ * Two classes are provided by the MX4J JMX implementation: {@link mx4j.server.BCELMBeanInvoker} and
+ * {@link mx4j.server.ReflectedMBeanInvoker}.
+ * The first one will use BCEL classes (if present) to speed up invocations on standard MBeans, while the second
+ * uses reflection.
+ * If, for any reason, the BCEL invocation fails, then the reflected invoker is used.
+ */
+ public static final String MX4J_MBEAN_INVOKER = "mx4j.mbean.invoker";
+
+ /**
+ * From JMX 1.2, names for attributes and operations, as well as their (return) types, must be valid
+ * Java identifiers, as specified by {@link Character#isJavaIdentifierStart} and {@link Character#isJavaIdentifierPart}.
+ * When set to true, (as specified by {@link Boolean#valueOf(String)}), this property turnes off this check.
+ */
+ public static String MX4J_UNCHECKED_IDENTIFIERS = "jmx.unchecked.identifiers";
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/ClassLoaderObjectInputStream.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/ClassLoaderObjectInputStream.java
new file mode 100644
index 0000000..f12a201
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/ClassLoaderObjectInputStream.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.loading;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+import java.lang.reflect.Proxy;
+
+/**
+ * ObjectInputStream that can read serialized java Objects using a supplied classloader
+ * to find the object's classes.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ClassLoaderObjectInputStream extends ObjectInputStream
+{
+ private ClassLoader classLoader;
+
+ /**
+ * Creates a new ClassLoaderObjectInputStream
+ * @param stream The decorated stream
+ * @param classLoader The ClassLoader used to load classes
+ */
+ public ClassLoaderObjectInputStream(InputStream stream, ClassLoader classLoader) throws IOException, StreamCorruptedException
+ {
+ super(stream);
+ if (classLoader == null) throw new IllegalArgumentException("Classloader cannot be null");
+ this.classLoader = classLoader;
+ }
+
+ protected Class resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException
+ {
+ String name = osc.getName();
+ return loadClass(name);
+ }
+
+ protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException
+ {
+ Class[] classes = new Class[interfaces.length];
+ for (int i = 0; i < interfaces.length; ++i) classes[i] = loadClass(interfaces[i]);
+
+ return Proxy.getProxyClass(classLoader, classes);
+ }
+
+ private Class loadClass(String name) throws ClassNotFoundException
+ {
+ return classLoader.loadClass(name);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetParseException.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetParseException.java
new file mode 100644
index 0000000..713b2e4
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetParseException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.loading;
+/**
+ * Thrown when a problem parsing MLet files is encountered
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MLetParseException extends Exception
+{
+ public MLetParseException()
+ {
+ }
+
+ public MLetParseException(String message)
+ {
+ super(message);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetParser.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetParser.java
new file mode 100644
index 0000000..bc2f955
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetParser.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.loading;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.loading.MLet;
+
+/**
+ * The parser for MLet files, as specified in the JMX documentation.
+ * This parser is case insensitive regards to the MLet tags: MLET is equal to mlet and to MLet.
+ * This parser also supports XML-style comments in the file.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MLetParser
+{
+ public static final String OPEN_COMMENT = "<!--";
+ public static final String CLOSE_COMMENT = "-->";
+
+ public static final String OPEN_BRACKET = "<";
+ public static final String CLOSE_BRACKET = ">";
+
+ public static final String MLET_TAG = "MLET";
+ public static final String CODE_ATTR = "CODE";
+ public static final String OBJECT_ATTR = "OBJECT";
+ public static final String ARCHIVE_ATTR = "ARCHIVE";
+ public static final String CODEBASE_ATTR = "CODEBASE";
+ public static final String NAME_ATTR = "NAME";
+ public static final String VERSION_ATTR = "VERSION";
+
+ public static final String ARG_TAG = "ARG";
+ public static final String TYPE_ATTR = "TYPE";
+ public static final String VALUE_ATTR = "VALUE";
+
+ private MLet mlet;
+
+ /**
+ * Creates a new MLetParser
+ */
+ public MLetParser()
+ {
+ }
+
+ /**
+ * Creates a new MLetParser
+ * @param mlet The MLet used to resolve classes specified in the ARG tags.
+ */
+ public MLetParser(MLet mlet)
+ {
+ this.mlet = mlet;
+ }
+
+ /**
+ * Parses the given content, that must contains a valid MLet file.
+ *
+ * @param content The content to parse
+ * @return A list of {@link MLetTag}s
+ * @throws MLetParseException If the content is not a valid MLet file
+ */
+ public List parse(String content) throws MLetParseException
+ {
+ if (content == null) throw new MLetParseException("MLet file content cannot be null");
+
+ // Strip comments
+ content = stripComments(content.trim());
+ content = convertToUpperCase(content);
+
+ ArrayList mlets = parseMLets(content);
+ if (mlets.size() < 1) throw new MLetParseException("MLet file is empty");
+
+ ArrayList mletTags = new ArrayList();
+ for (int i = 0; i < mlets.size(); ++i)
+ {
+ String mletTag = (String)mlets.get(i);
+
+ MLetTag tag = parseMLet(mletTag);
+ mletTags.add(tag);
+ }
+
+ return mletTags;
+ }
+
+ private MLetTag parseMLet(String content) throws MLetParseException
+ {
+ MLetTag tag = new MLetTag();
+ parseMLetAttributes(tag, content);
+ parseMLetArguments(tag, content);
+ return tag;
+ }
+
+ private ArrayList parseMLets(String content) throws MLetParseException
+ {
+ ArrayList list = new ArrayList();
+ int start = 0;
+ int current = -1;
+ while ((current = findOpenTag(content, start, MLET_TAG)) >= 0)
+ {
+ int end = findCloseTag(content, current + 1, MLET_TAG, true);
+ if (end < 0) throw new MLetParseException("MLET tag not closed at index: " + current);
+
+ String mlet = content.substring(current, end);
+ list.add(mlet);
+
+ start = end + 1;
+ }
+ return list;
+ }
+
+ private void parseMLetArguments(MLetTag tag, String content) throws MLetParseException
+ {
+ int start = 0;
+ int current = -1;
+ while ((current = findOpenTag(content, start, ARG_TAG)) >= 0)
+ {
+ int end = findCloseTag(content, current + 1, ARG_TAG, false);
+ if (end < 0) throw new MLetParseException("ARG tag not closed");
+
+ String arg = content.substring(current, end);
+
+ int type = arg.indexOf(TYPE_ATTR);
+ if (type < 0) throw new MLetParseException("Missing TYPE attribute");
+
+ int value = arg.indexOf(VALUE_ATTR);
+ if (value < 0) throw new MLetParseException("Missing VALUE attribute");
+
+ String className = findAttributeValue(arg, type, TYPE_ATTR);
+ tag.addArg(className, convertToObject(className, findAttributeValue(arg, value, VALUE_ATTR)));
+
+ start = end + 1;
+ }
+ }
+
+ private void parseMLetAttributes(MLetTag tag, String content) throws MLetParseException
+ {
+ int end = content.indexOf(CLOSE_BRACKET);
+ String attributes = content.substring(0, end);
+
+ // Find mandatory attributes
+ int archive = -1;
+ int object = -1;
+ int code = -1;
+
+ archive = attributes.indexOf(ARCHIVE_ATTR);
+ if (archive < 0) throw new MLetParseException("Missing ARCHIVE attribute");
+
+ code = attributes.indexOf(CODE_ATTR);
+ object = attributes.indexOf(OBJECT_ATTR);
+ if (code < 0 && object < 0) throw new MLetParseException("Missing CODE or OBJECT attribute");
+ if (code > 0 && object > 0) throw new MLetParseException("CODE and OBJECT attributes cannot be both present");
+
+ if (code >= 0)
+ tag.setCode(findAttributeValue(attributes, code, CODE_ATTR));
+ else
+ tag.setObject(findAttributeValue(attributes, object, OBJECT_ATTR));
+
+ tag.setArchive(findAttributeValue(attributes, archive, ARCHIVE_ATTR));
+
+ // Look for optional attributes
+ int codebase = attributes.indexOf(CODEBASE_ATTR);
+ if (codebase >= 0) tag.setCodeBase(findAttributeValue(attributes, codebase, CODEBASE_ATTR));
+
+ int name = attributes.indexOf(NAME_ATTR);
+ if (name >= 0)
+ {
+ String objectName = findAttributeValue(attributes, name, NAME_ATTR);
+ try
+ {
+ tag.setName(new ObjectName(objectName));
+ }
+ catch (MalformedObjectNameException x)
+ {
+ throw new MLetParseException("Invalid ObjectName: " + objectName);
+ }
+ }
+
+ int version = attributes.indexOf(VERSION_ATTR);
+ if (version >= 0) tag.setVersion(findAttributeValue(attributes, version, VERSION_ATTR));
+ }
+
+ private String findAttributeValue(String content, int start, String attribute) throws MLetParseException
+ {
+ int equal = content.indexOf('=', start);
+ if (equal < 0) throw new MLetParseException("Missing '=' for attribute");
+
+ // Ensure no garbage
+ if (!attribute.equals(content.substring(start, equal).trim())) throw new MLetParseException("Invalid attribute");
+
+ int begin = content.indexOf('"', equal + 1);
+ if (begin < 0) throw new MLetParseException("Missing quotes for attribute value");
+
+ // Ensure no garbage
+ if (content.substring(equal + 1, begin).trim().length() != 0) throw new MLetParseException("Invalid attribute value");
+
+ int end = content.indexOf('"', begin + 1);
+ if (end < 0) throw new MLetParseException("Missing quote for attribute value");
+
+ return content.substring(begin + 1, end).trim();
+ }
+
+ private int findOpenTag(String content, int start, String tag)
+ {
+ String opening = new StringBuffer(OPEN_BRACKET).append(tag).toString();
+ return content.indexOf(opening, start);
+ }
+
+ private int findCloseTag(String content, int start, String tag, boolean strictSyntax)
+ {
+ int count = 1;
+
+ do
+ {
+ int close = content.indexOf(CLOSE_BRACKET, start);
+ if (close < 0)
+ {
+ return -1;
+ }
+ int open = content.indexOf(OPEN_BRACKET, start);
+ if (open >= 0 && close > open)
+ {
+ ++count;
+ }
+ else
+ {
+ --count;
+ if (count == 0)
+ {
+ // Either I found the closing bracket of the open tag,
+ // or the closing tag
+ if (!strictSyntax || (strictSyntax && content.charAt(close - 1) == '/'))
+ {
+ // Found the closing tag
+ return close + 1;
+ }
+ else
+ {
+ // Found the closing bracket of the open tag, go for the full closing tag
+ String closing = new StringBuffer(OPEN_BRACKET).append("/").append(tag).append(CLOSE_BRACKET).toString();
+ close = content.indexOf(closing, start);
+ if (close < 0)
+ return -1;
+ else
+ return close + closing.length();
+ }
+ }
+ }
+
+ start = close + 1;
+ }
+ while (true);
+ }
+
+ private String stripComments(String content) throws MLetParseException
+ {
+ StringBuffer buffer = new StringBuffer();
+ int start = 0;
+ int current = -1;
+ while ((current = content.indexOf(OPEN_COMMENT, start)) >= 0)
+ {
+ int end = content.indexOf(CLOSE_COMMENT, current + 1);
+
+ if (end < 0) throw new MLetParseException("Missing close comment tag at index: " + current);
+
+ String stripped = content.substring(start, current);
+ buffer.append(stripped);
+ start = end + CLOSE_COMMENT.length();
+ }
+ String stripped = content.substring(start, content.length());
+ buffer.append(stripped);
+ return buffer.toString();
+ }
+
+ private String convertToUpperCase(String content) throws MLetParseException
+ {
+ StringBuffer buffer = new StringBuffer();
+ int start = 0;
+ int current = -1;
+ while ((current = content.indexOf("\"", start)) >= 0)
+ {
+ int end = content.indexOf("\"", current + 1);
+
+ if (end < 0) throw new MLetParseException("Missing closing quote at index: " + current);
+
+ String converted = content.substring(start, current).toUpperCase();
+ buffer.append(converted);
+ String quoted = content.substring(current, end + 1);
+ buffer.append(quoted);
+ start = end + 1;
+ }
+ String converted = content.substring(start, content.length()).toUpperCase();
+ buffer.append(converted);
+ return buffer.toString();
+ }
+
+ private Object convertToObject(String clsName, String value) throws MLetParseException
+ {
+ try
+ {
+ if (clsName.equals("boolean") || clsName.equals("java.lang.Boolean"))
+ return Boolean.valueOf(value);
+ else if (clsName.equals("byte") || clsName.equals("java.lang.Byte"))
+ return Byte.valueOf(value);
+ else if (clsName.equals("char") || clsName.equals("java.lang.Character"))
+ {
+ char ch = 0;
+ if (value.length() > 0) ch = value.charAt(0);
+ return new Character(ch);
+ }
+ else if (clsName.equals("short") || clsName.equals("java.lang.Short"))
+ return Short.valueOf(value);
+ else if (clsName.equals("int") || clsName.equals("java.lang.Integer"))
+ return Integer.valueOf(value);
+ else if (clsName.equals("long") || clsName.equals("java.lang.Long"))
+ return Long.valueOf(value);
+ else if (clsName.equals("float") || clsName.equals("java.lang.Float"))
+ return Float.valueOf(value);
+ else if (clsName.equals("double") || clsName.equals("java.lang.Double"))
+ return Double.valueOf(value);
+ else if (clsName.equals("java.lang.String"))
+ return value;
+ else if (mlet != null)
+ {
+ try
+ {
+ Class cls = mlet.loadClass(clsName);
+ Constructor ctor = cls.getConstructor(new Class[]{String.class});
+ return ctor.newInstance(new Object[]{value});
+ }
+ catch (Exception ignored)
+ {
+ }
+ }
+ }
+ catch (NumberFormatException x)
+ {
+ throw new MLetParseException("Invalid value: " + value);
+ }
+ return null;
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetTag.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetTag.java
new file mode 100644
index 0000000..107f215
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/loading/MLetTag.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.loading;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+import javax.management.ObjectName;
+
+/**
+ * Represents an MLET tag, as documented in the JMX specification.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MLetTag
+{
+ private String code;
+ private String object;
+ private String archive;
+ private String codebase;
+ private ObjectName objectName;
+ private String version;
+ private ArrayList signature = new ArrayList();
+ private ArrayList arguments = new ArrayList();
+
+ /**
+ * Normalizes the codebase held by this MLetTag (specified in the MLet file) using the
+ * URL of the MLet file as default.
+ * This means that if the codebase in the MLet file is not provided or it is relative, then
+ * the URL of the MLet file will be taken as base for computing the normalized codebase;
+ * otherwise, if a full URL has been specified as codebase in the MLet file, that URL is taken
+ * and the URL of the MLet file is discarded.
+ *
+ * @param mletFileURL The URL of the MLet file
+ * @return The normalized codebase
+ */
+ public URL normalizeCodeBase(URL mletFileURL)
+ {
+ // If the codebase specified in the MLet file is relative, or null,
+ // then the codebase is the one of the mletFileURL, otherwise
+ // the given codebase must be used.
+
+ URL codebaseURL = null;
+ String codebase = getCodeBase();
+ if (codebase != null)
+ {
+ // Try to see if it's a URL
+ try
+ {
+ codebaseURL = new URL(codebase);
+ }
+ catch (MalformedURLException ignored)
+ {
+ // Not a complete URL, use the mletFileURL as a base
+ try
+ {
+ codebaseURL = new URL(mletFileURL, codebase);
+ }
+ catch (MalformedURLException alsoIgnored)
+ {
+ }
+ }
+ }
+
+ // Either codebase not provided or failed to be created, use the mletFileURL
+ if (codebaseURL == null)
+ {
+ String path = mletFileURL.getPath();
+ int index = path.lastIndexOf('/');
+
+ try
+ {
+ codebaseURL = new URL(mletFileURL, path.substring(0, index + 1));
+ }
+ catch (MalformedURLException ignored)
+ {
+ // Cannot fail, we just remove the mlet file name from the path
+ // leaving the directory where it resides as a codebase
+ }
+ }
+ return codebaseURL;
+ }
+
+ /**
+ * Returns the jars file names specified in the ARCHIVE attribute of the MLet tag.
+ */
+ public String[] parseArchive()
+ {
+ String archive = getArchive();
+ ArrayList archives = new ArrayList();
+ StringTokenizer tokenizer = new StringTokenizer(archive, ",");
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken().trim();
+ if (token.length() > 0)
+ {
+ token.replace('\\', '/');
+ archives.add(token);
+ }
+ }
+ return (String[])archives.toArray(new String[0]);
+ }
+
+ /**
+ * Returns the URL for the given archive file name using the provided URL as a codebase,
+ * or null if the URL cannot be created.
+ */
+ public URL createArchiveURL(URL codebase, String archive)
+ {
+ try
+ {
+ return new URL(codebase, archive);
+ }
+ catch (MalformedURLException ignored)
+ {
+ }
+ return null;
+ }
+
+ public String getVersion()
+ {
+ return version;
+ }
+
+ public String getCodeBase()
+ {
+ return codebase;
+ }
+
+ public String getArchive()
+ {
+ return archive;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public ObjectName getObjectName()
+ {
+ return objectName;
+ }
+
+ public String getObject()
+ {
+ return object;
+ }
+
+ public String[] getSignature()
+ {
+ return signature == null ? new String[0] : (String[])signature.toArray(new String[signature.size()]);
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments == null ? new Object[0] : (Object[])arguments.toArray(new Object[arguments.size()]);
+ }
+
+ //
+ // Setters, called by MLetParser
+ //
+
+ void setArchive(String archive)
+ {
+ this.archive = archive;
+ }
+
+ void setCode(String code)
+ {
+ this.code = code;
+ }
+
+ void setCodeBase(String codebase)
+ {
+ // Important that the codebase ends with a slash, see usages of getCodeBase()
+
+ codebase = codebase.replace('\\', '/');
+ if (!codebase.endsWith("/")) codebase += "/";
+ this.codebase = codebase;
+ }
+
+ void setName(ObjectName name)
+ {
+ objectName = name;
+ }
+
+ void setObject(String object)
+ {
+ this.object = object;
+ }
+
+ void setVersion(String version)
+ {
+ this.version = version;
+ }
+
+ void addArg(String type, Object value)
+ {
+ signature.add(type);
+ arguments.add(value);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/log/Log.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/log/Log.java
new file mode 100644
index 0000000..6347e1b
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/log/Log.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.log;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+import java.util.HashMap;
+
+import javax.management.RuntimeOperationsException;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.MX4JSystemKeys;
+
+/**
+ * Main class for the log service. <p>
+ * The system property 'mx4j.log.priority' controls the priority of the standard logging, and defaults to 'warn'.
+ * Possible values are, from least to greatest priority, the following (case insensitive):
+ * <ul>
+ * <li>trace
+ * <li>debug
+ * <li>info
+ * <li>warn
+ * <li>error
+ * <li>fatal
+ * </ul>
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class Log
+{
+ private static Logger m_prototype;
+ private static Map m_prototypeMap = new HashMap();
+ private static Map m_loggerCache = new HashMap();
+ private static int m_defaultPriority;
+
+ static
+ {
+ // Do not require callers up in the stack to have this permission
+ String priority = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(MX4JSystemKeys.MX4J_LOG_PRIORITY);
+ }
+ });
+ if ("trace".equalsIgnoreCase(priority)) {m_defaultPriority = Logger.TRACE;}
+ else if ("debug".equalsIgnoreCase(priority)) {m_defaultPriority = Logger.DEBUG;}
+ else if ("info".equalsIgnoreCase(priority)) {m_defaultPriority = Logger.INFO;}
+ else if ("warn".equalsIgnoreCase(priority)) {m_defaultPriority = Logger.WARN;}
+ else if ("error".equalsIgnoreCase(priority)) {m_defaultPriority = Logger.ERROR;}
+ else if ("fatal".equalsIgnoreCase(priority)) {m_defaultPriority = Logger.FATAL;}
+ else {m_defaultPriority = Logger.INFO;}
+
+ String prototype = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(MX4JSystemKeys.MX4J_LOG_PROTOTYPE);
+ }
+ });
+ if (prototype != null && prototype.trim().length() > 0)
+ {
+ try
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Class cls = cl.loadClass(prototype);
+ redirectTo((Logger)cls.newInstance());
+ }
+ catch (Exception x)
+ {
+ x.printStackTrace();
+ // Do nothing else: the user will see the exception trace
+ // and understand that the property was wrong
+ }
+ }
+ }
+
+ private Log() {}
+
+ /**
+ * Sets the default priority for all loggers.
+ * @see #setDefaultPriority
+ */
+ public static void setDefaultPriority(int priority)
+ {
+ switch (priority)
+ {
+ case Logger.TRACE: m_defaultPriority = Logger.TRACE; break;
+ case Logger.DEBUG: m_defaultPriority = Logger.DEBUG; break;
+ case Logger.INFO: m_defaultPriority = Logger.INFO; break;
+ case Logger.WARN: m_defaultPriority = Logger.WARN; break;
+ case Logger.ERROR: m_defaultPriority = Logger.ERROR; break;
+ case Logger.FATAL: m_defaultPriority = Logger.FATAL; break;
+ default: m_defaultPriority = Logger.WARN; break;
+ }
+ }
+
+ /**
+ * Returns the default priority.
+ * @see #setDefaultPriority
+ */
+ public static int getDefaultPriority()
+ {
+ return m_defaultPriority;
+ }
+
+ /**
+ * Returns a new instance of a Logger associated with the given <code>category</code>;
+ * if {@link #redirectTo} has been called then a new instance of the prototype Logger, associated with the given
+ * <code>category<code>, is returned. This requires the prototype Logger class to have a public parameterless
+ * constructor.
+ */
+ public static Logger getLogger(String category)
+ {
+ if (category == null) {throw new RuntimeOperationsException(new IllegalArgumentException("Category cannot be null"));}
+
+ synchronized (m_loggerCache)
+ {
+ Logger logger = (Logger)m_loggerCache.get(category);
+ if (logger == null)
+ {
+ // Try to see if a delegate for this category overrides other settings
+ Logger prototype = null;
+ synchronized (m_prototypeMap)
+ {
+ prototype = (Logger)m_prototypeMap.get(category);
+ }
+ if (prototype == null)
+ {
+ // Try to see if a prototype for all categories has been set
+ if (m_prototype != null)
+ {
+ logger = createLogger(m_prototype, category);
+ }
+ else
+ {
+ logger = createLogger(null, category);
+ }
+ }
+ else
+ {
+ logger = createLogger(prototype, category);
+ }
+
+ // cache it
+ m_loggerCache.put(category, logger);
+ }
+ return logger;
+ }
+ }
+
+ private static Logger createLogger(Logger prototype, String category)
+ {
+ Logger logger = null;
+ try
+ {
+ logger = prototype == null ? new Logger() : (Logger)prototype.getClass().newInstance();
+ }
+ catch (Exception x)
+ {
+ x.printStackTrace();
+ logger = new Logger();
+ }
+ logger.setCategory(category);
+ logger.setPriority(m_defaultPriority);
+ return logger;
+ }
+
+ /**
+ * Tells to the log service to use the given <code>delegate</code> Logger to perform logging. <br>
+ * Use a null delegate to remove redirection.
+ * @see #getLogger
+ */
+ public static void redirectTo(Logger prototype)
+ {
+ m_prototype = prototype;
+
+ // Clear the cache, as we want requests for new loggers to be generated again.
+ synchronized (m_loggerCache)
+ {
+ m_loggerCache.clear();
+ }
+ }
+
+ /**
+ * Tells to the log service to use the given <code>delegate</code> Logger to perform logging for the given
+ * category (that cannot be null). <br>
+ * Settings made using this method overrides the ones made with {@link #redirectTo(Logger) redirectTo}, meaning
+ * that it is possible to redirect all the log to a certain delegate but certain categories.
+ * Use a null delegate to remove redirection for the specified category.
+ * @see #getLogger
+ */
+ public static void redirectTo(Logger prototype, String category)
+ {
+ if (category == null) {throw new RuntimeOperationsException(new IllegalArgumentException("Category cannot be null"));}
+
+ if (prototype == null)
+ {
+ // Remove the redirection
+ synchronized (m_prototypeMap)
+ {
+ m_prototypeMap.remove(category);
+ }
+
+ // Clear the cache for this category
+ synchronized (m_loggerCache)
+ {
+ m_loggerCache.remove(category);
+ }
+ }
+ else
+ {
+ // Put or replace
+ synchronized (m_prototypeMap)
+ {
+ m_prototypeMap.put(category, prototype);
+ }
+
+ // Clear the cache for this category
+ synchronized (m_loggerCache)
+ {
+ m_loggerCache.remove(category);
+ }
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/log/Logger.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/log/Logger.java
new file mode 100644
index 0000000..3eff2fb
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/log/Logger.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.log;
+
+/**
+ * Base class for logging objects.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class Logger
+{
+ public static final int TRACE = 0;
+ public static final int DEBUG = TRACE + 10;
+ public static final int INFO = DEBUG + 10;
+ public static final int WARN = INFO + 10;
+ public static final int ERROR = WARN + 10;
+ public static final int FATAL = ERROR + 10;
+
+ private int m_priority = WARN;
+ private String m_category;
+
+ protected Logger() {}
+
+ public void setPriority(int priority) {m_priority = priority;}
+ public int getPriority() {return m_priority;}
+
+ public String getCategory() {return m_category;}
+ protected void setCategory(String category) {m_category = category;}
+
+ public final boolean isEnabledFor(int priority)
+ {
+ return priority >= getPriority();
+ }
+
+ public final void fatal(Object message) {log(FATAL, message, null);}
+ public final void fatal(Object message, Throwable t) {log(FATAL, message, t);}
+ public final void error(Object message) {log(ERROR, message, null);}
+ public final void error(Object message, Throwable t) {log(ERROR, message, t);}
+ public final void warn(Object message) {log(WARN, message, null);}
+ public final void warn(Object message, Throwable t) {log(WARN, message, t);}
+ public final void info(Object message) {log(INFO, message, null);}
+ public final void info(Object message, Throwable t) {log(INFO, message, t);}
+ public final void debug(Object message) {log(DEBUG, message, null);}
+ public final void debug(Object message, Throwable t) {log(DEBUG, message, t);}
+ public final void trace(Object message) {log(TRACE, message, null);}
+ public final void trace(Object message, Throwable t) {log(TRACE, message, t);}
+
+ protected void log(int priority, Object message, Throwable t)
+ {
+ if (isEnabledFor(priority))
+ {
+ System.out.println(message);
+ if (t != null)
+ {
+ t.printStackTrace(System.out);
+ }
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/DefaultClassLoaderRepository.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/DefaultClassLoaderRepository.java
new file mode 100644
index 0000000..5746f31
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/DefaultClassLoaderRepository.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import java.util.ArrayList;
+
+import javax.management.loading.MLet;
+
+/**
+ * Default implementation of a ClassLoaderRepository
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.2 $
+ */
+public class DefaultClassLoaderRepository extends ModifiableClassLoaderRepository implements java.io.Serializable
+{
+ private static final int WITHOUT = 1;
+ private static final int BEFORE = 2;
+
+ private ArrayList classLoaders = new ArrayList();
+
+ public Class loadClass(String className) throws ClassNotFoundException
+ {
+ return loadClassWithout(null, className);
+ }
+
+ public Class loadClassWithout(ClassLoader loader, String className) throws ClassNotFoundException
+ {
+ return loadClassFromRepository(loader, className, WITHOUT);
+ }
+
+ public Class loadClassBefore(ClassLoader loader, String className) throws ClassNotFoundException
+ {
+ return loadClassFromRepository(loader, className, BEFORE);
+ }
+
+ protected void addClassLoader(ClassLoader cl)
+ {
+ if (cl == null) return;
+
+ ArrayList loaders = getClassLoaders();
+ synchronized (loaders)
+ {
+ if (!loaders.contains(cl)) loaders.add(cl);
+ }
+ }
+
+ protected void removeClassLoader(ClassLoader cl)
+ {
+ if (cl == null) return;
+
+ ArrayList loaders = getClassLoaders();
+ synchronized (loaders)
+ {
+ loaders.remove(cl);
+ }
+ }
+
+ protected ArrayList cloneClassLoaders()
+ {
+ ArrayList loaders = getClassLoaders();
+ synchronized (loaders)
+ {
+ return (ArrayList)loaders.clone();
+ }
+ }
+
+ protected ArrayList getClassLoaders()
+ {
+ return classLoaders;
+ }
+
+ private Class loadClassFromRepository(ClassLoader loader, String className, int algorithm) throws ClassNotFoundException
+ {
+ ArrayList copy = cloneClassLoaders();
+ for (int i = 0; i < copy.size(); ++i)
+ {
+ try
+ {
+ ClassLoader cl = (ClassLoader)copy.get(i);
+ if (cl.equals(loader))
+ {
+ if (algorithm == BEFORE) break;
+ else continue;
+ }
+
+ return loadClass(cl, className);
+ }
+ catch (ClassNotFoundException ignored)
+ {
+ }
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ private Class loadClass(ClassLoader loader, String className) throws ClassNotFoundException
+ {
+ // This is an optimization: if the classloader is an MLet (and not a subclass)
+ // then the method MLet.loadClass(String, ClassLoaderRepository) is used.
+ if (loader.getClass() == MLet.class) return ((MLet)loader).loadClass(className, null);
+ return loader.loadClass(className);
+ }
+
+ private int getSize()
+ {
+ ArrayList loaders = getClassLoaders();
+ synchronized (loaders)
+ {
+ return loaders.size();
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/DefaultMBeanRepository.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/DefaultMBeanRepository.java
new file mode 100644
index 0000000..441f270
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/DefaultMBeanRepository.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.management.ObjectName;
+
+/**
+ * Default implementation of the MBeanRepository interface.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+class DefaultMBeanRepository implements MBeanRepository
+{
+ private HashMap m_map = new HashMap();
+
+ public MBeanMetaData get(ObjectName name)
+ {
+ return (MBeanMetaData)m_map.get(name);
+ }
+
+ public void put(ObjectName name, MBeanMetaData metadata)
+ {
+ m_map.put(name, metadata);
+ }
+
+ public void remove(ObjectName name)
+ {
+ m_map.remove(name);
+ }
+
+ public int size()
+ {
+ return m_map.size();
+ }
+
+ public Iterator iterator()
+ {
+ return m_map.values().iterator();
+ }
+
+ public Object clone()
+ {
+ try
+ {
+ DefaultMBeanRepository repository = (DefaultMBeanRepository)super.clone();
+ repository.m_map = (HashMap)m_map.clone();
+ return repository;
+ }
+ catch (CloneNotSupportedException ignored)
+ {
+ return null;
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanIntrospector.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanIntrospector.java
new file mode 100644
index 0000000..1e31850
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanIntrospector.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.management.DynamicMBean;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.NotificationBroadcaster;
+import javax.management.loading.MLet;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.MBeanDescription;
+import org.apache.felix.mosgi.jmx.agent.mx4j.MBeanDescriptionAdapter;
+import org.apache.felix.mosgi.jmx.agent.mx4j.MX4JSystemKeys;
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Log;
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Logger;
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.Utils;
+
+/**
+ * Introspector for MBeans. <p>
+ * Main purposes of this class are:
+ * <ul>
+ * <li> Given an mbean, gather all information regarding it into a {@link MBeanMetaData} instance, see {@link #introspect}
+ * <li> Given an introspected MBeanMetaData, decide if the MBean is compliant or not.
+ * <li> Act as a factory for {@link MBeanInvoker}s
+ * </ul>
+ *
+ * The following system properties are used to control this class' behavior:
+ * <ul>
+ * <li> mx4j.strict.mbean.interface, if set to 'no' then are treated as standard MBeans also classes that implement
+ * management interfaces beloging to different packages or that are inner classes; otherwise are treated as MBeans
+ * only classes that implement interfaces whose name if the fully qualified name of the MBean class + "MBean"
+ * <li> mx4j.mbean.invoker, if set to the qualified name of an implementation of the {@link MBeanInvoker} interface,
+ * then an instance of the class will be used to invoke methods on standard MBeans. By default the generated-on-the-fly
+ * MBeanInvoker is used; to revert to the version that uses reflection, for example,
+ * use mx4j.mbean.invoker = {@link ReflectedMBeanInvoker mx4j.server.ReflectedMBeanInvoker}
+ * </ul>
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MBeanIntrospector
+{
+ private static MBeanDescriptionAdapter DEFAULT_DESCRIPTION = new MBeanDescriptionAdapter();
+
+ private boolean m_useExtendedMBeanInterfaces = false;
+ private boolean m_bcelClassesAvailable = false;
+ private final String m_customMBeanInvoker;
+
+ public MBeanIntrospector()
+ {
+ String strict = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(MX4JSystemKeys.MX4J_STRICT_MBEAN_INTERFACE);
+ }
+ });
+ if (strict != null && !Boolean.valueOf(strict).booleanValue())
+ {
+ m_useExtendedMBeanInterfaces = true;
+ }
+
+ // Try to see if BCEL classes are present
+ /* SFR : removed BCEL management
+ try
+ {
+ getClass().getClassLoader().loadClass("org.apache.bcel.generic.Type");
+ m_bcelClassesAvailable = true;
+ }
+ catch (Throwable ignored)
+ {
+ }
+ */
+
+ // See if someone specified which MBean invoker to use
+ m_customMBeanInvoker = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(MX4JSystemKeys.MX4J_MBEAN_INVOKER);
+ }
+ });
+ }
+
+ /**
+ * Introspect the given mbean, storing the results in the given metadata.
+ * It expects that the mbean field and the classloader field are not null
+ * @see #isMBeanCompliant
+ */
+ public void introspect(MBeanMetaData metadata)
+ {
+ introspectType(metadata);
+ introspectMBeanInfo(metadata);
+ }
+
+ /**
+ * Returns whether the given already introspected metadata is compliant.
+ * Must be called after {@link #introspect}
+ */
+ public boolean isMBeanCompliant(MBeanMetaData metadata)
+ {
+ return isMBeanClassCompliant(metadata) && isMBeanTypeCompliant(metadata) && isMBeanInfoCompliant(metadata);
+ }
+
+ /**
+ * Used by the test cases, invoked via reflection, keep it private.
+ * Introspect the mbean and returns if it's compliant
+ */
+ private boolean testCompliance(MBeanMetaData metadata)
+ {
+ introspect(metadata);
+ return isMBeanCompliant(metadata);
+ }
+
+ private boolean isMBeanClassCompliant(MBeanMetaData metadata)
+ {
+ // From JMX 1.1: no requirements (can be abstract, non public and no accessible constructors)
+ return true;
+ }
+
+ private boolean isMBeanTypeCompliant(MBeanMetaData metadata)
+ {
+ Logger logger = getLogger();
+
+ if (metadata.standard && metadata.dynamic)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is both standard and dynamic");
+ return false;
+ }
+ if (!metadata.standard && !metadata.dynamic)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not standard nor dynamic");
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isMBeanInfoCompliant(MBeanMetaData metadata)
+ {
+ Logger logger = getLogger();
+
+ if (metadata.info == null)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo is null");
+ return false;
+ }
+ return true;
+ }
+
+ private void introspectType(MBeanMetaData metadata)
+ {
+ // Some information is already provided (StandardMBean)
+ if (metadata.standard)
+ {
+ introspectStandardMBean(metadata);
+ return;
+ }
+
+ if (metadata.mbean instanceof DynamicMBean)
+ {
+ metadata.dynamic = true;
+ return;
+ }
+ else
+ {
+ metadata.dynamic = false;
+ // Continue and see if it's a plain standard MBean
+ }
+
+ // We have a plain standard MBean, introspect it
+ introspectStandardMBean(metadata);
+ }
+
+ private void introspectStandardMBean(MBeanMetaData metadata)
+ {
+ if (metadata.management != null)
+ {
+ // Be sure the MBean implements the management interface
+ if (metadata.management.isInstance(metadata.mbean))
+ {
+ if (metadata.invoker == null) metadata.invoker = createInvoker(metadata);
+ return;
+ }
+ else
+ {
+ // Not compliant, reset the values
+ metadata.standard = false;
+ metadata.management = null;
+ metadata.invoker = null;
+ return;
+ }
+ }
+ else
+ {
+ Class cls = metadata.mbean.getClass();
+ for (Class c = cls; c != null; c = c.getSuperclass())
+ {
+ Class[] intfs = c.getInterfaces();
+ for (int i = 0; i < intfs.length; ++i)
+ {
+ Class intf = intfs[i];
+
+ if (implementsMBean(c.getName(), intf.getName()))
+ {
+ // OK, found the MBean interface for this class
+ metadata.standard = true;
+ metadata.management = intf;
+ metadata.invoker = createInvoker(metadata);
+ return;
+ }
+ }
+ }
+
+ // Management interface not found, it's not compliant, reset the values
+ metadata.standard = false;
+ metadata.management = null;
+ metadata.invoker = null;
+ }
+ }
+
+ private void introspectMBeanInfo(MBeanMetaData metadata)
+ {
+ if (metadata.dynamic)
+ {
+ metadata.info = getDynamicMBeanInfo(metadata);
+ }
+ else if (metadata.standard)
+ {
+ metadata.info = createStandardMBeanInfo(metadata);
+ }
+ else
+ {
+ // Not a valid MBean, reset the MBeanInfo: this will cause an exception later
+ metadata.info = null;
+ }
+ }
+
+ private MBeanInfo getDynamicMBeanInfo(MBeanMetaData metadata)
+ {
+ Logger logger = getLogger();
+
+ MBeanInfo info = null;
+
+ try {
+ info = ((DynamicMBean) metadata.mbean).getMBeanInfo();
+ } catch (Exception x) {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("getMBeanInfo threw: " + x.toString());
+ }
+
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Dynamic MBeanInfo is: " + info);
+
+ if (info == null)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBeanInfo cannot be null");
+ return null;
+ }
+
+ return info;
+ }
+
+ private MBeanInfo createStandardMBeanInfo(MBeanMetaData metadata)
+ {
+ // This is a non-standard extension: description for standard MBeans
+ MBeanDescription description = createMBeanDescription(metadata);
+
+ MBeanConstructorInfo[] ctors = createMBeanConstructorInfo(metadata, description);
+ if (ctors == null) return null;
+ MBeanAttributeInfo[] attrs = createMBeanAttributeInfo(metadata, description);
+ if (attrs == null) return null;
+ MBeanOperationInfo[] opers = createMBeanOperationInfo(metadata, description);
+ if (opers == null) return null;
+ MBeanNotificationInfo[] notifs = createMBeanNotificationInfo(metadata);
+ if (notifs == null) return null;
+
+ return new MBeanInfo(metadata.mbean.getClass().getName(), description.getMBeanDescription(), attrs, ctors, opers, notifs);
+ }
+
+ private MBeanDescription createMBeanDescription(MBeanMetaData metadata)
+ {
+ // This is a non-standard extension
+
+ Logger logger = getLogger();
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Looking for standard MBean description...");
+
+ Class mbeanClass = metadata.mbean.getClass();
+
+ for (Class cls = mbeanClass; cls != null; cls = cls.getSuperclass())
+ {
+ String clsName = cls.getName();
+ if (clsName.startsWith("java.")) break;
+
+ // Use full qualified name only
+ String descrClassName = clsName + "MBeanDescription";
+ // Try to load the class
+ try
+ {
+ Class descrClass = null;
+ ClassLoader loader = metadata.classloader;
+ if (loader != null)
+ {
+ // Optimize lookup of the description class in case of MLets: we lookup the description class
+ // only in the classloader of the mbean, not in the whole CLR (since MLets delegates to the CLR)
+ if (loader.getClass() == MLet.class)
+ descrClass = ((MLet)loader).loadClass(descrClassName, null);
+ else
+ descrClass = loader.loadClass(descrClassName);
+ }
+ else
+ {
+ descrClass = Class.forName(descrClassName, false, null);
+ }
+
+ Object descrInstance = descrClass.newInstance();
+ if (descrInstance instanceof MBeanDescription)
+ {
+ MBeanDescription description = (MBeanDescription)descrInstance;
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Found provided standard MBean description: " + description);
+ return description;
+ }
+ }
+ catch (ClassNotFoundException ignored)
+ {
+ }
+ catch (InstantiationException ignored)
+ {
+ }
+ catch (IllegalAccessException ignored)
+ {
+ }
+ }
+
+ MBeanDescription description = DEFAULT_DESCRIPTION;
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Cannot find standard MBean description, using default: " + description);
+ return description;
+ }
+
+ private MBeanOperationInfo[] createMBeanOperationInfo(MBeanMetaData metadata, MBeanDescription description)
+ {
+ ArrayList operations = new ArrayList();
+
+ Method[] methods = metadata.management.getMethods();
+ for (int j = 0; j < methods.length; ++j)
+ {
+ Method method = methods[j];
+ if (!Utils.isAttributeGetter(method) && !Utils.isAttributeSetter(method))
+ {
+ String descr = description == null ? null : description.getOperationDescription(method);
+ Class[] params = method.getParameterTypes();
+ MBeanParameterInfo[] paramsInfo = new MBeanParameterInfo[params.length];
+ for (int k = 0; k < params.length; ++k)
+ {
+ Class param = params[k];
+ String paramName = description == null ? null : description.getOperationParameterName(method, k);
+ String paramDescr = description == null ? null : description.getOperationParameterDescription(method, k);
+ paramsInfo[k] = new MBeanParameterInfo(paramName, param.getName(), paramDescr);
+ }
+ MBeanOperationInfo info = new MBeanOperationInfo(method.getName(), descr, paramsInfo, method.getReturnType().getName(), MBeanOperationInfo.UNKNOWN);
+ operations.add(info);
+ }
+ }
+
+ return (MBeanOperationInfo[])operations.toArray(new MBeanOperationInfo[operations.size()]);
+ }
+
+ private MBeanAttributeInfo[] createMBeanAttributeInfo(MBeanMetaData metadata, MBeanDescription description)
+ {
+ Logger logger = getLogger();
+
+ HashMap attributes = new HashMap();
+ HashMap getterNames = new HashMap();
+
+ Method[] methods = metadata.management.getMethods();
+ for (int j = 0; j < methods.length; ++j)
+ {
+ Method method = methods[j];
+ if (Utils.isAttributeGetter(method))
+ {
+ String name = method.getName();
+ boolean isIs = name.startsWith("is");
+
+ String attribute = null;
+ if (isIs)
+ attribute = name.substring(2);
+ else
+ attribute = name.substring(3);
+
+ String descr = description == null ? null : description.getAttributeDescription(attribute);
+
+ MBeanAttributeInfo info = (MBeanAttributeInfo)attributes.get(attribute);
+
+ if (info != null)
+ {
+ // JMX spec does not allow overloading attributes.
+ // If an attribute with the same name already exists the MBean is not compliant
+ if (!info.getType().equals(method.getReturnType().getName()))
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not compliant: has overloaded attribute " + attribute);
+ return null;
+ }
+ else
+ {
+ // They return the same value,
+ if (getterNames.get(name) != null)
+ {
+ // This is the case of an attribute being present in multiple interfaces
+ // Ignore all but the first, since they resolve to the same method anyways
+ continue;
+ }
+
+ // there is a chance that one is a get-getter and one is a is-getter
+ // for a boolean attribute. In this case, the MBean is not compliant.
+ if (info.isReadable())
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not compliant: has overloaded attribute " + attribute);
+ return null;
+ }
+
+ // MBeanAttributeInfo is already present due to a setter method, just update its readability
+ info = new MBeanAttributeInfo(attribute, info.getType(), info.getDescription(), true, info.isWritable(), isIs);
+ }
+ }
+ else
+ {
+ info = new MBeanAttributeInfo(attribute, method.getReturnType().getName(), descr, true, false, isIs);
+ }
+
+ // Replace if exists
+ attributes.put(attribute, info);
+ getterNames.put(name,method);
+ }
+ else if (Utils.isAttributeSetter(method))
+ {
+ String name = method.getName();
+ String attribute = name.substring(3);
+
+ String descr = description == null ? null : description.getAttributeDescription(attribute);
+
+ MBeanAttributeInfo info = (MBeanAttributeInfo)attributes.get(attribute);
+
+ if (info != null)
+ {
+ // JMX spec does not allow overloading attributes.
+ // If an attribute with the same name already exists the MBean is not compliant
+ if (!info.getType().equals(method.getParameterTypes()[0].getName()))
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean is not compliant: has overloaded attribute " + attribute);
+ return null;
+ }
+ else
+ {
+ // MBeanAttributeInfo is already present due to a getter method, just update its writability
+ info = new MBeanAttributeInfo(info.getName(), info.getType(), info.getDescription(), info.isReadable(), true, info.isIs());
+ }
+ }
+ else
+ {
+ info = new MBeanAttributeInfo(attribute, method.getParameterTypes()[0].getName(), descr, false, true, false);
+ }
+
+ // Replace if exists
+ attributes.put(attribute, info);
+ }
+ }
+
+ return (MBeanAttributeInfo[])attributes.values().toArray(new MBeanAttributeInfo[attributes.size()]);
+ }
+
+ private MBeanNotificationInfo[] createMBeanNotificationInfo(MBeanMetaData metadata)
+ {
+ MBeanNotificationInfo[] notifs = null;
+ if (metadata.mbean instanceof NotificationBroadcaster)
+ {
+ notifs = ((NotificationBroadcaster)metadata.mbean).getNotificationInfo();
+ }
+ if (notifs == null) notifs = new MBeanNotificationInfo[0];
+ return notifs;
+ }
+
+ private MBeanConstructorInfo[] createMBeanConstructorInfo(MBeanMetaData metadata, MBeanDescription descrs)
+ {
+ Class mbeanClass = metadata.mbean.getClass();
+
+ Constructor[] ctors = mbeanClass.getConstructors();
+ MBeanConstructorInfo[] constructors = new MBeanConstructorInfo[ctors.length];
+ for (int i = 0; i < ctors.length; ++i)
+ {
+ Constructor constructor = ctors[i];
+ String descr = descrs == null ? null : descrs.getConstructorDescription(constructor);
+ Class[] params = constructor.getParameterTypes();
+ MBeanParameterInfo[] paramsInfo = new MBeanParameterInfo[params.length];
+ for (int j = 0; j < params.length; ++j)
+ {
+ Class param = params[j];
+ String paramName = descrs == null ? null : descrs.getConstructorParameterName(constructor, j);
+ String paramDescr = descrs == null ? null : descrs.getConstructorParameterDescription(constructor, j);
+ paramsInfo[j] = new MBeanParameterInfo(paramName, param.getName(), paramDescr);
+ }
+
+ String ctorName = constructor.getName();
+ MBeanConstructorInfo info = new MBeanConstructorInfo(ctorName.substring(ctorName.lastIndexOf('.') + 1), descr, paramsInfo);
+ constructors[i] = info;
+ }
+ return constructors;
+ }
+
+ private boolean implementsMBean(String clsName, String intfName)
+ {
+ if (intfName.equals(clsName + "MBean")) return true;
+
+ if (m_useExtendedMBeanInterfaces)
+ {
+ // Check also that the may be in different packages and/or inner classes
+
+ // Trim packages
+ int clsDot = clsName.lastIndexOf('.');
+ if (clsDot > 0) clsName = clsName.substring(clsDot + 1);
+ int intfDot = intfName.lastIndexOf('.');
+ if (intfDot > 0) intfName = intfName.substring(intfDot + 1);
+ // Try again
+ if (intfName.equals(clsName + "MBean")) return true;
+
+ // Trim inner classes
+ int clsDollar = clsName.lastIndexOf('$');
+ if (clsDollar > 0) clsName = clsName.substring(clsDollar + 1);
+ int intfDollar = intfName.lastIndexOf('$');
+ if (intfDollar > 0) intfName = intfName.substring(intfDollar + 1);
+ // Try again
+ if (intfName.equals(clsName + "MBean")) return true;
+ }
+
+ // Give up
+ return false;
+ }
+
+ private MBeanInvoker createInvoker(MBeanMetaData metadata)
+ {
+ Logger logger = getLogger();
+
+ if (m_customMBeanInvoker != null)
+ {
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Custom MBeanInvoker class is: " + m_customMBeanInvoker);
+ try
+ {
+ MBeanInvoker mbeanInvoker = (MBeanInvoker)Thread.currentThread().getContextClassLoader().loadClass(m_customMBeanInvoker).newInstance();
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using custom MBeanInvoker: " + mbeanInvoker);
+ return mbeanInvoker;
+ }
+ catch (Exception x)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Cannot instantiate custom MBeanInvoker, using default", x);
+ }
+ }
+
+/* SFR
+ if (m_bcelClassesAvailable)
+ {
+ MBeanInvoker mbeanInvoker = BCELMBeanInvoker.create(metadata);
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using default BCEL MBeanInvoker for MBean " + metadata.name + ", " + mbeanInvoker);
+ return mbeanInvoker;
+ }
+ else
+ {
+*/
+ MBeanInvoker mbeanInvoker = new ReflectedMBeanInvoker();
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Using default Reflection MBeanInvoker for MBean " + metadata.name + ", " + mbeanInvoker);
+ return mbeanInvoker;
+/* SFR } */
+ }
+
+ private Logger getLogger()
+ {
+ return Log.getLogger(getClass().getName());
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanInvoker.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanInvoker.java
new file mode 100644
index 0000000..9713bfd
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanInvoker.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+
+/**
+ * Invokes methods on standard MBeans. <p>
+ * Actually two implementations are available: one that uses reflection and one that generates on-the-fly a customized
+ * MBeanInvoker per each particular MBean and that is implemented with direct calls. <br>
+ * The default is the direct call version, that uses the <a href="http://jakarta.apache.org/bcel">BCEL</a> to generate
+ * the required bytecode on-the-fly. <br>
+ * In the future may be the starting point for MBean interceptors.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface MBeanInvoker
+{
+ /**
+ * Invokes the specified operation on the MBean instance
+ */
+ public Object invoke(MBeanMetaData metadata, String method, String[] signature, Object[] args) throws MBeanException, ReflectionException;
+ /**
+ * Returns the value of the specified attribute.
+ */
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException;
+ /**
+ * Sets the value of the specified attribute.
+ */
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException;
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanMetaData.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanMetaData.java
new file mode 100644
index 0000000..2454fd9
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanMetaData.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import javax.management.MBeanInfo;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+/**
+ * Objects of this class hold metadata information about MBeans.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MBeanMetaData
+{
+ /**
+ * The MBean instance.
+ */
+ public Object mbean;
+
+ /**
+ * The classloader of the MBean
+ */
+ public ClassLoader classloader;
+
+ /**
+ * The ObjectInstance of the MBean
+ */
+ public ObjectInstance instance;
+
+ /**
+ * The ObjectName of the MBean
+ */
+ public ObjectName name;
+
+ /**
+ * The MBeanInfo of the MBean
+ */
+ public MBeanInfo info;
+
+ /**
+ * True if the MBean is dynamic
+ */
+ public boolean dynamic;
+
+ /**
+ * True if the MBean is standard
+ */
+ public boolean standard;
+
+ /**
+ * The management interface of the MBean, if it is a standard MBean
+ */
+ public Class management;
+
+ /**
+ * The invoker for the MBean, if it is a standard MBean
+ */
+ public MBeanInvoker invoker;
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanRepository.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanRepository.java
new file mode 100644
index 0000000..744a7e9
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MBeanRepository.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import java.util.Iterator;
+import javax.management.ObjectName;
+
+/**
+ * The MBeanServer implementation delegates to implementations of this interface the storage of registered MBeans. <p>
+ * All necessary synchronization code is taken care by the MBeanServer, so implementations can be coded without caring
+ * of synchronization issues.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface MBeanRepository extends Cloneable
+{
+ /**
+ * Returns the metadata information associated with the given object name.
+ * @see #put
+ */
+ public MBeanMetaData get(ObjectName name);
+
+ /**
+ * Inserts the given metadata associated with the given object name into this repository.
+ * @see #get
+ */
+ public void put(ObjectName name, MBeanMetaData metadata);
+
+ /**
+ * Removes the metadata associated with the given object name from this repository.
+ */
+ public void remove(ObjectName name);
+
+ /**
+ * Returns the size of this repository.
+ */
+ public int size();
+
+ /**
+ * Returns an iterator on the metadata stored in this repository.
+ */
+ public Iterator iterator();
+
+ /**
+ * Clones this MBean repository
+ */
+ public Object clone();
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServer.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServer.java
new file mode 100644
index 0000000..5954f09
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServer.java
@@ -0,0 +1,1489 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.BadAttributeValueExpException;
+import javax.management.BadBinaryOpValueExpException;
+import javax.management.BadStringOperationException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidApplicationException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMRuntimeException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanPermission;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.MBeanServerPermission;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.OperationsException;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.RuntimeErrorException;
+import javax.management.RuntimeOperationsException;
+import javax.management.StandardMBean;
+import javax.management.loading.ClassLoaderRepository;
+import javax.management.loading.PrivateClassLoader;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.ImplementationException;
+import org.apache.felix.mosgi.jmx.agent.mx4j.MX4JSystemKeys;
+import org.apache.felix.mosgi.jmx.agent.mx4j.loading.ClassLoaderObjectInputStream;
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Log;
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Logger;
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.InvokerMBeanServerInterceptor;
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.MBeanServerInterceptor;
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.MBeanServerInterceptorConfigurator;
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.Utils;
+
+/**
+ * The MX4J MBeanServer implementation. <br>
+ * The MBeanServer accomplishes these roles:
+ * <ul>
+ * <li> Returns information about the Agent
+ * <li> Acts as a repository for MBeans
+ * <li> Acts as an invoker, on behalf of the user, on MBeans
+ * </ul>
+ * <br>
+ * The repository function is delegated to instances of {@link MBeanRepository} classes.
+ * This class acts as a factory for MBeanRepository instances, that can be controlled via the system property
+ * {@link mx4j.MX4JSystemKeys#MX4J_MBEANSERVER_REPOSITORY} to the qualified name of the implementation class. <br>
+ *
+ * This class also acts as an invoker on MBeans. The architecture is interceptor-based, that is whenever you call
+ * from a client an MBeanServer method that will end up to call the MBean instance, the call is dispatched to
+ * the interceptor chain and eventually to the MBean. <br>
+ * The interceptors are configurable via the MBean {@link MBeanServerInterceptorConfigurator}.
+ * When the call is about to arrive to the MBean instance, the last interceptor dispatches the call depending on
+ * the MBean type: if the MBean is a dynamic MBean, the call is dispatched directly; if the MBean is a standard
+ * MBean an {@link MBeanInvoker} is delegated to invoke on the MBean instance.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.3 $
+ */
+public class MX4JMBeanServer implements MBeanServer, java.io.Serializable
+{
+ private String defaultDomain;
+ private MBeanRepository mbeanRepository;
+ private MBeanServerDelegate delegate;
+ private ObjectName delegateName;
+ private MBeanIntrospector introspector;
+ private MBeanServerInterceptorConfigurator invoker;
+ private static long notifications;
+ private ModifiableClassLoaderRepository classLoaderRepository;
+ private Map domains = new HashMap();
+
+ private static final String[] EMPTY_PARAMS = new String[0];
+ private static final Object[] EMPTY_ARGS = new Object[0];
+
+ /**
+ * Create a new MBeanServer implementation with the specified default domain.
+ * If the default domain is null, then the empty string is assumed.
+ *
+ * @param defaultDomain The default domain to be used
+ * @throws SecurityException if access is not granted to create an MBeanServer instance
+ */
+ public MX4JMBeanServer(String defaultDomain, MBeanServer outer, MBeanServerDelegate delegate)
+ {
+ Logger logger = getLogger();
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Creating MBeanServer instance...");
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Checking permission to create MBeanServer...");
+ sm.checkPermission(new MBeanServerPermission("newMBeanServer"));
+ }
+
+ if (defaultDomain == null) defaultDomain = "";
+ this.defaultDomain = defaultDomain;
+
+ if (delegate==null) throw new JMRuntimeException("Delegate can't be null");
+ this.delegate = delegate;
+
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("MBeanServer default domain is: '" + this.defaultDomain + "'");
+
+ mbeanRepository = createMBeanRepository();
+
+ classLoaderRepository = createClassLoaderRepository();
+ // JMX 1.2 requires the CLR to have as first entry the classloader of this class
+ classLoaderRepository.addClassLoader(getClass().getClassLoader());
+
+ introspector = new MBeanIntrospector();
+
+ // This is the official name of the delegate, it is used as a source for MBeanServerNotifications
+ try
+ {
+ delegateName = new ObjectName("JMImplementation", "type", "MBeanServerDelegate");
+ }
+ catch (MalformedObjectNameException ignored)
+ {
+ }
+
+ try
+ {
+ ObjectName invokerName = new ObjectName(MBeanServerInterceptorConfigurator.OBJECT_NAME);
+ invoker = new MBeanServerInterceptorConfigurator(this);
+
+// ContextClassLoaderMBeanServerInterceptor ccl = new ContextClassLoaderMBeanServerInterceptor();
+// NotificationListenerMBeanServerInterceptor notif = new NotificationListenerMBeanServerInterceptor();
+// SecurityMBeanServerInterceptor sec = new SecurityMBeanServerInterceptor();
+ InvokerMBeanServerInterceptor inv = new InvokerMBeanServerInterceptor(outer==null ? this : outer);
+
+// invoker.addPreInterceptor(ccl);
+// invoker.addPreInterceptor(notif);
+// invoker.addPostInterceptor(sec);
+ invoker.addPostInterceptor(inv);
+
+ invoker.start();
+
+ // The interceptor stack is in place, register the configurator and all interceptors
+ privilegedRegisterMBean(invoker, invokerName);
+
+// ObjectName cclName = new ObjectName("JMImplementation", "interceptor", "contextclassloader");
+// ObjectName notifName = new ObjectName("JMImplementation", "interceptor", "notificationwrapper");
+// ObjectName secName = new ObjectName("JMImplementation", "interceptor", "security");
+ ObjectName invName = new ObjectName("JMImplementation", "interceptor", "invoker");
+
+// privilegedRegisterMBean(ccl, cclName);
+// privilegedRegisterMBean(notif, notifName);
+// privilegedRegisterMBean(sec, secName);
+ privilegedRegisterMBean(inv, invName);
+ }
+ catch (Exception x)
+ {
+ logger.error("MBeanServerInterceptorConfigurator cannot be registered", x);
+ throw new ImplementationException();
+ }
+
+ // Now register the delegate
+ try
+ {
+ privilegedRegisterMBean(delegate, delegateName);
+ }
+ catch (Exception x)
+ {
+ logger.error("MBeanServerDelegate cannot be registered", x);
+ throw new ImplementationException(x.toString());
+ }
+
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("MBeanServer instance created successfully");
+ }
+
+ /**
+ * Returns the ClassLoaderRepository for this MBeanServer.
+ * When first the ClassLoaderRepository is created in the constructor, the system property
+ * {@link mx4j.MX4JSystemKeys#MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY} is tested;
+ * if it is non-null and defines a subclass of
+ * {@link ModifiableClassLoaderRepository}, then that class is used instead of the default one.
+ */
+ public ClassLoaderRepository getClassLoaderRepository()
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ sm.checkPermission(new MBeanPermission("-#-[-]", "getClassLoaderRepository"));
+ }
+
+ return getModifiableClassLoaderRepository();
+ }
+
+ private ModifiableClassLoaderRepository getModifiableClassLoaderRepository()
+ {
+ return classLoaderRepository;
+ }
+
+ public ClassLoader getClassLoader(ObjectName name) throws InstanceNotFoundException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ name = secureObjectName(name);
+
+ if (name == null)
+ {
+ sm.checkPermission(new MBeanPermission("-#-[-]", "getClassLoader"));
+ }
+ else
+ {
+ MBeanMetaData metadata = findMBeanMetaData(name);
+ sm.checkPermission(new MBeanPermission(metadata.info.getClassName(), "-", name, "getClassLoader"));
+ }
+ }
+
+ return getClassLoaderImpl(name);
+ }
+
+ public ClassLoader getClassLoaderFor(ObjectName name) throws InstanceNotFoundException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ name = secureObjectName(name);
+ }
+
+ // If name is null, I get InstanceNotFoundException
+ MBeanMetaData metadata = findMBeanMetaData(name);
+
+ if (sm != null)
+ {
+ sm.checkPermission(new MBeanPermission(metadata.info.getClassName(), "-", name, "getClassLoaderFor"));
+ }
+
+ return metadata.mbean.getClass().getClassLoader();
+ }
+
+ /**
+ * Returns the MBean classloader corrispondent to the given ObjectName.
+ * If <code>name</code> is null, the classloader of this class is returned.
+ */
+ private ClassLoader getClassLoaderImpl(ObjectName name) throws InstanceNotFoundException
+ {
+ if (name == null)
+ {
+ return getClass().getClassLoader();
+ }
+ else
+ {
+ MBeanMetaData metadata = findMBeanMetaData(name);
+ if (metadata.mbean instanceof ClassLoader)
+ {
+ return (ClassLoader)metadata.mbean;
+ }
+ else
+ {
+ throw new InstanceNotFoundException(name.getCanonicalName());
+ }
+ }
+ }
+
+ public ObjectInputStream deserialize(String className, ObjectName loaderName, byte[] bytes)
+ throws InstanceNotFoundException, OperationsException, ReflectionException
+ {
+ if (className == null || className.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid class name '" + className + "'"));
+ }
+
+ ClassLoader cl = getClassLoader(loaderName);
+
+ try
+ {
+ Class cls = cl.loadClass(className);
+ return deserializeImpl(cls.getClassLoader(), bytes);
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+
+ public ObjectInputStream deserialize(String className, byte[] bytes)
+ throws OperationsException, ReflectionException
+ {
+ if (className == null || className.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid class name '" + className + "'"));
+ }
+
+ // Find the classloader that can load the given className using the ClassLoaderRepository
+ try
+ {
+ Class cls = getClassLoaderRepository().loadClass(className);
+ return deserializeImpl(cls.getClassLoader(), bytes);
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+
+ public ObjectInputStream deserialize(ObjectName objectName, byte[] bytes)
+ throws InstanceNotFoundException, OperationsException
+ {
+ ClassLoader cl = getClassLoaderFor(objectName);
+ return deserializeImpl(cl, bytes);
+ }
+
+ /**
+ * Deserializes the given bytes using the specified classloader.
+ */
+ private ObjectInputStream deserializeImpl(ClassLoader classloader, byte[] bytes) throws OperationsException
+ {
+ if (bytes == null || bytes.length == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid byte array " + bytes));
+ }
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ try
+ {
+ return new ClassLoaderObjectInputStream(bais, classloader);
+ }
+ catch (IOException x)
+ {
+ throw new OperationsException(x.toString());
+ }
+ }
+
+ private MBeanServerInterceptor getHeadInterceptor()
+ {
+ MBeanServerInterceptor head = invoker.getHeadInterceptor();
+
+ if (head == null) throw new IllegalStateException("No MBeanServer interceptor, probably the configurator has been stopped");
+
+ return head;
+ }
+
+ private Logger getLogger()
+ {
+ return Log.getLogger(getClass().getName());
+ }
+
+ /**
+ * Creates a new repository for MBeans.
+ * The system property {@link mx4j.MX4JSystemKeys#MX4J_MBEANSERVER_REPOSITORY} is tested
+ * for a full qualified name of a class implementing the {@link MBeanRepository} interface.
+ * In case the system property is not defined or the class is not loadable or instantiable, a default
+ * implementation is returned.
+ */
+ private MBeanRepository createMBeanRepository()
+ {
+ Logger logger = getLogger();
+
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Checking for system property " + MX4JSystemKeys.MX4J_MBEANSERVER_REPOSITORY);
+
+ String value = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(MX4JSystemKeys.MX4J_MBEANSERVER_REPOSITORY);
+ }
+ });
+
+ if (value != null)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Property found for custom MBeanServer registry; class is: " + value);
+
+ try
+ {
+ MBeanRepository registry = (MBeanRepository)Thread.currentThread().getContextClassLoader().loadClass(value).newInstance();
+ if (logger.isEnabledFor(Logger.TRACE))
+ {
+ logger.trace("Custom MBeanServer registry created successfully");
+ }
+ return registry;
+ }
+ catch (Exception x)
+ {
+ if (logger.isEnabledFor(Logger.TRACE))
+ {
+ logger.trace("Custom MBeanServer registry could not be created", x);
+ }
+ }
+ }
+
+ return new DefaultMBeanRepository();
+ }
+
+ /**
+ * Creates a new ClassLoaderRepository for ClassLoader MBeans.
+ * The system property {@link mx4j.MX4JSystemKeys#MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY}
+ * is tested for a full qualified name of a class
+ * extending the {@link ModifiableClassLoaderRepository} class.
+ * In case the system property is not defined or the class is not loadable or instantiable, a default
+ * implementation is returned.
+ */
+ private ModifiableClassLoaderRepository createClassLoaderRepository()
+ {
+ Logger logger = getLogger();
+
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Checking for system property " + MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY);
+
+ String value = (String)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return System.getProperty(MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY);
+ }
+ });
+
+ if (value != null)
+ {
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Property found for custom ClassLoaderRepository; class is: " + value);
+
+ try
+ {
+ ModifiableClassLoaderRepository repository = (ModifiableClassLoaderRepository)Thread.currentThread().getContextClassLoader().loadClass(value).newInstance();
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Custom ClassLoaderRepository created successfully " + repository);
+ return repository;
+ }
+ catch (Exception x)
+ {
+ if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Custom ClassLoaderRepository could not be created", x);
+ }
+ }
+ return new DefaultClassLoaderRepository();
+ }
+
+ /**
+ * Returns the repository of MBeans for this MBeanServer
+ */
+ private MBeanRepository getMBeanRepository()
+ {
+ return mbeanRepository;
+ }
+
+ /**
+ * Looks up the metadata associated with the given ObjectName.
+ * @throws InstanceNotFoundException if the given ObjectName is not a registered MBean
+ */
+ private MBeanMetaData findMBeanMetaData(ObjectName objectName) throws InstanceNotFoundException
+ {
+ MBeanMetaData metadata = null;
+ if (objectName != null)
+ {
+ objectName = normalizeObjectName(objectName);
+
+ MBeanRepository repository = getMBeanRepository();
+ synchronized (repository)
+ {
+ metadata = repository.get(objectName);
+ }
+ }
+ if (metadata == null)
+ {
+ throw new InstanceNotFoundException("MBeanServer cannot find MBean with ObjectName " + objectName);
+ }
+ return metadata;
+ }
+
+ public void addNotificationListener(ObjectName observed, ObjectName listener, NotificationFilter filter, Object handback)
+ throws InstanceNotFoundException
+ {
+ listener = secureObjectName(listener);
+
+ Object mbean = findMBeanMetaData(listener).mbean;
+ if (!(mbean instanceof NotificationListener))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean " + listener + " is not a NotificationListener"));
+ }
+ addNotificationListener(observed, (NotificationListener)mbean, filter, handback);
+ }
+
+ public void addNotificationListener(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback)
+ throws InstanceNotFoundException
+ {
+ if (listener == null)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("NotificationListener cannot be null"));
+ }
+
+ observed = secureObjectName(observed);
+
+ MBeanMetaData metadata = findMBeanMetaData(observed);
+
+ Object mbean = metadata.mbean;
+
+ if (!(mbean instanceof NotificationBroadcaster))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean " + observed + " is not a NotificationBroadcaster"));
+ }
+
+ addNotificationListenerImpl(metadata, listener, filter, handback);
+ }
+
+ private void addNotificationListenerImpl(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ {
+ getHeadInterceptor().addNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void removeNotificationListener(ObjectName observed, ObjectName listener)
+ throws InstanceNotFoundException, ListenerNotFoundException
+ {
+ listener = secureObjectName(listener);
+
+ Object mbean = findMBeanMetaData(listener).mbean;
+ if (!(mbean instanceof NotificationListener))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean " + listener + " is not a NotificationListener"));
+ }
+ removeNotificationListener(observed, (NotificationListener)mbean);
+ }
+
+ public void removeNotificationListener(ObjectName observed, NotificationListener listener)
+ throws InstanceNotFoundException, ListenerNotFoundException
+ {
+ if (listener == null)
+ {
+ throw new ListenerNotFoundException("NotificationListener cannot be null");
+ }
+
+ observed = secureObjectName(observed);
+
+ MBeanMetaData metadata = findMBeanMetaData(observed);
+ Object mbean = metadata.mbean;
+
+ if (!(mbean instanceof NotificationBroadcaster))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean " + observed + " is not a NotificationBroadcaster"));
+ }
+
+ removeNotificationListenerImpl(metadata, listener);
+ }
+
+ public void removeNotificationListener(ObjectName observed, ObjectName listener, NotificationFilter filter, Object handback)
+ throws InstanceNotFoundException, ListenerNotFoundException
+ {
+ listener = secureObjectName(listener);
+
+ Object mbean = findMBeanMetaData(listener).mbean;
+ if (!(mbean instanceof NotificationListener))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean " + listener + " is not a NotificationListener"));
+ }
+ removeNotificationListener(observed, (NotificationListener)mbean, filter, handback);
+ }
+
+ public void removeNotificationListener(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback)
+ throws InstanceNotFoundException, ListenerNotFoundException
+ {
+ if (listener == null)
+ {
+ throw new ListenerNotFoundException("NotificationListener cannot be null");
+ }
+
+ observed = secureObjectName(observed);
+
+ MBeanMetaData metadata = findMBeanMetaData(observed);
+ Object mbean = metadata.mbean;
+
+ if (!(mbean instanceof NotificationEmitter))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean " + observed + " is not a NotificationEmitter"));
+ }
+
+ removeNotificationListenerImpl(metadata, listener, filter, handback);
+ }
+
+ private void removeNotificationListenerImpl(MBeanMetaData metadata, NotificationListener listener)
+ throws ListenerNotFoundException
+ {
+ getHeadInterceptor().removeNotificationListener(metadata, listener);
+ }
+
+ private void removeNotificationListenerImpl(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ throws ListenerNotFoundException
+ {
+ getHeadInterceptor().removeNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public Object instantiate(String className)
+ throws ReflectionException, MBeanException
+ {
+ return instantiate(className, null, null);
+ }
+
+ public Object instantiate(String className, Object[] args, String[] parameters)
+ throws ReflectionException, MBeanException
+ {
+ if (className == null || className.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Class name cannot be null or empty"));
+ }
+
+ try
+ {
+ Class cls = getModifiableClassLoaderRepository().loadClass(className);
+ return instantiateImpl(className, cls.getClassLoader(), null, parameters, args).mbean;
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+
+ public Object instantiate(String className, ObjectName loaderName)
+ throws ReflectionException, MBeanException, InstanceNotFoundException
+ {
+ return instantiate(className, loaderName, null, null);
+ }
+
+ public Object instantiate(String className, ObjectName loaderName, Object[] args, String[] parameters)
+ throws ReflectionException, MBeanException, InstanceNotFoundException
+ {
+ if (className == null || className.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Class name cannot be null or empty"));
+ }
+
+ // loaderName can be null: means using this class' ClassLoader
+
+ loaderName = secureObjectName(loaderName);
+ if (loaderName != null && loaderName.isPattern())
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("ObjectName for the ClassLoader cannot be a pattern ObjectName: " + loaderName));
+ }
+
+ ClassLoader cl = getClassLoaderImpl(loaderName);
+ return instantiateImpl(className, cl, null, parameters, args).mbean;
+ }
+
+ private MBeanMetaData instantiateImpl(String className, ClassLoader classloader, ObjectName name, String[] params, Object[] args)
+ throws ReflectionException, MBeanException
+ {
+ if (params == null) params = EMPTY_PARAMS;
+ if (args == null) args = EMPTY_ARGS;
+
+ MBeanMetaData metadata = createMBeanMetaData();
+ metadata.classloader = classloader;
+ metadata.name = secureObjectName(name);
+
+ getHeadInterceptor().instantiate(metadata, className, params, args);
+
+ return metadata;
+ }
+
+ public ObjectInstance createMBean(String className, ObjectName objectName)
+ throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException
+ {
+ return createMBean(className, objectName, null, null);
+ }
+
+ public ObjectInstance createMBean(String className, ObjectName objectName, Object[] args, String[] parameters)
+ throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException
+ {
+ try
+ {
+ Class cls = getModifiableClassLoaderRepository().loadClass(className);
+ MBeanMetaData metadata = instantiateImpl(className, cls.getClassLoader(), objectName, parameters, args);
+
+ registerImpl(metadata, false);
+
+ return metadata.instance;
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+
+ public ObjectInstance createMBean(String className, ObjectName objectName, ObjectName loaderName)
+ throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException
+ {
+ return createMBean(className, objectName, loaderName, null, null);
+ }
+
+ public ObjectInstance createMBean(String className, ObjectName objectName, ObjectName loaderName, Object[] args, String[] parameters)
+ throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException
+ {
+ loaderName = secureObjectName(loaderName);
+
+ ClassLoader cl = getClassLoaderImpl(loaderName);
+
+ MBeanMetaData metadata = instantiateImpl(className, cl, objectName, parameters, args);
+
+ registerImpl(metadata, false);
+
+ return metadata.instance;
+ }
+
+ public ObjectInstance registerMBean(Object mbean, ObjectName objectName)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException
+ {
+ return registerMBeanImpl(mbean, objectName, false);
+ }
+
+ private ObjectInstance registerMBeanImpl(Object mbean, ObjectName objectName, boolean privileged)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException
+ {
+ if (mbean == null)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("MBean instance cannot be null"));
+ }
+
+ MBeanMetaData metadata = createMBeanMetaData();
+ metadata.mbean = mbean;
+ metadata.classloader = mbean.getClass().getClassLoader();
+ metadata.name = secureObjectName(objectName);
+
+ registerImpl(metadata, privileged);
+
+ return metadata.instance;
+ }
+
+ /**
+ * Returns a new instance of the metadata class used to store MBean information.
+ */
+ private MBeanMetaData createMBeanMetaData()
+ {
+ return new MBeanMetaData();
+ }
+
+ /**
+ * This method is called only to register implementation MBeans from the constructor.
+ * Since to create an instance of this class already requires a permission, here we hide the registration
+ * of implementation MBeans to the client that thus need no further permissions.
+ */
+ private ObjectInstance privilegedRegisterMBean(final Object mbean, final ObjectName name)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException
+ {
+ try
+ {
+ return (ObjectInstance)AccessController.doPrivileged(new PrivilegedExceptionAction()
+ {
+ public Object run() throws Exception
+ {
+ return registerMBeanImpl(mbean, name, true);
+ }
+ });
+ }
+ catch (PrivilegedActionException x)
+ {
+ Exception xx = x.getException();
+ if (xx instanceof InstanceAlreadyExistsException)
+ throw (InstanceAlreadyExistsException)xx;
+ else if (xx instanceof MBeanRegistrationException)
+ throw (MBeanRegistrationException)xx;
+ else if (xx instanceof NotCompliantMBeanException)
+ throw (NotCompliantMBeanException)xx;
+ else
+ throw new MBeanRegistrationException(xx);
+ }
+ }
+
+ private void registerImpl(MBeanMetaData metadata, boolean privileged) throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException
+ {
+ introspector.introspect(metadata);
+
+ if (!introspector.isMBeanCompliant(metadata)) throw new NotCompliantMBeanException("MBean is not compliant");
+
+ MBeanServerInterceptor head = getHeadInterceptor();
+
+ try
+ {
+ // With this call, the MBean implementor can replace the ObjectName with a subclass that is not secure, secure it again
+ head.registration(metadata, MBeanServerInterceptor.PRE_REGISTER);
+ metadata.name = secureObjectName(metadata.name);
+
+ metadata.instance = new ObjectInstance(metadata.name, metadata.info.getClassName());
+
+ register(metadata, privileged);
+
+ head.registration(metadata, MBeanServerInterceptor.POST_REGISTER_TRUE);
+ }
+ catch (Throwable x)
+ {
+ try
+ {
+ head.registration(metadata, MBeanServerInterceptor.POST_REGISTER_FALSE);
+ }
+ catch (MBeanRegistrationException ignored)
+ {/* Ignore this one to rethrow the other one */
+ }
+
+ if (x instanceof SecurityException)
+ {
+ throw (SecurityException)x;
+ }
+ else if (x instanceof InstanceAlreadyExistsException)
+ {
+ throw (InstanceAlreadyExistsException)x;
+ }
+ else if (x instanceof MBeanRegistrationException)
+ {
+ throw (MBeanRegistrationException)x;
+ }
+ else if (x instanceof RuntimeOperationsException)
+ {
+ throw (RuntimeOperationsException)x;
+ }
+ else if (x instanceof JMRuntimeException)
+ {
+ throw (JMRuntimeException)x;
+ }
+ else if (x instanceof Exception)
+ {
+ throw new MBeanRegistrationException((Exception)x);
+ }
+ else if (x instanceof Error)
+ {
+ throw new MBeanRegistrationException(new RuntimeErrorException((Error)x));
+ }
+ else
+ {
+ throw new ImplementationException();
+ }
+ }
+
+ if (metadata.mbean instanceof ClassLoader && !(metadata.mbean instanceof PrivateClassLoader))
+ {
+ ClassLoader cl = (ClassLoader)metadata.mbean;
+ getModifiableClassLoaderRepository().addClassLoader(cl);
+ }
+ }
+
+ private void register(MBeanMetaData metadata, boolean privileged) throws InstanceAlreadyExistsException
+ {
+ metadata.name = normalizeObjectName(metadata.name);
+
+ ObjectName objectName = metadata.name;
+ if (objectName == null || objectName.isPattern())
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("ObjectName cannot be null or a pattern ObjectName"));
+ }
+ if (objectName.getDomain().equals("JMImplementation") && !privileged)
+ {
+ throw new JMRuntimeException("Domain 'JMImplementation' is reserved for the JMX Agent");
+ }
+
+ MBeanRepository repository = getMBeanRepository();
+ synchronized (repository)
+ {
+ if (repository.get(objectName) != null) throw new InstanceAlreadyExistsException(objectName.toString());
+
+ repository.put(objectName, metadata);
+ }
+ addDomain(objectName.getDomain());
+
+ notify(objectName, MBeanServerNotification.REGISTRATION_NOTIFICATION);
+ }
+
+ private void notify(ObjectName objectName, String notificationType)
+ {
+ long sequenceNumber = 0;
+ synchronized (MX4JMBeanServer.class)
+ {
+ sequenceNumber = notifications;
+ ++notifications;
+ }
+
+ delegate.sendNotification(new MBeanServerNotification(notificationType, delegateName, sequenceNumber, objectName));
+ }
+
+ private void addDomain(String domain)
+ {
+ synchronized (domains)
+ {
+ Integer count = (Integer)domains.get(domain);
+ if (count == null)
+ domains.put(domain, new Integer(1));
+ else
+ domains.put(domain, new Integer(count.intValue() + 1));
+ }
+ }
+
+ private void removeDomain(String domain)
+ {
+ synchronized (domains)
+ {
+ Integer count = (Integer)domains.get(domain);
+ if (count == null) throw new ImplementationException();
+ if (count.intValue() < 2)
+ domains.remove(domain);
+ else
+ domains.put(domain, new Integer(count.intValue() - 1));
+ }
+ }
+
+ public void unregisterMBean(ObjectName objectName)
+ throws InstanceNotFoundException, MBeanRegistrationException
+ {
+ objectName = secureObjectName(objectName);
+
+ if (objectName == null || objectName.isPattern())
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("ObjectName cannot be null or a pattern ObjectName"));
+ }
+
+ if (objectName.getDomain().equals("JMImplementation"))
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Domain 'JMImplementation' is reserved for the JMX Agent"));
+ }
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ try
+ {
+ MBeanServerInterceptor head = getHeadInterceptor();
+ head.registration(metadata, MBeanServerInterceptor.PRE_DEREGISTER);
+
+ unregister(metadata);
+
+ getHeadInterceptor().registration(metadata, MBeanServerInterceptor.POST_DEREGISTER);
+
+ if (metadata.mbean instanceof ClassLoader && !(metadata.mbean instanceof PrivateClassLoader))
+ {
+ getModifiableClassLoaderRepository().removeClassLoader((ClassLoader)metadata.mbean);
+ }
+ }
+ catch (MBeanRegistrationException x)
+ {
+ throw x;
+ }
+ catch (SecurityException x)
+ {
+ throw x;
+ }
+ catch (Exception x)
+ {
+ throw new MBeanRegistrationException(x);
+ }
+ catch (Error x)
+ {
+ throw new MBeanRegistrationException(new RuntimeErrorException(x));
+ }
+ }
+
+ private void unregister(MBeanMetaData metadata)
+ {
+ ObjectName objectName = metadata.name;
+
+ MBeanRepository repository = getMBeanRepository();
+ synchronized (repository)
+ {
+ repository.remove(objectName);
+ }
+ removeDomain(objectName.getDomain());
+
+ notify(objectName, MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+ }
+
+ public Object getAttribute(ObjectName objectName, String attribute)
+ throws InstanceNotFoundException, MBeanException, AttributeNotFoundException, ReflectionException
+ {
+ if (attribute == null || attribute.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid attribute"));
+ }
+
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ return getHeadInterceptor().getAttribute(metadata, attribute);
+ }
+
+
+ public void setAttribute(ObjectName objectName, Attribute attribute)
+ throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
+ {
+ if (attribute == null || attribute.getName().trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid attribute"));
+ }
+
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ getHeadInterceptor().setAttribute(metadata, attribute);
+ }
+
+ public AttributeList getAttributes(ObjectName objectName, String[] attributes)
+ throws InstanceNotFoundException, ReflectionException
+ {
+ if (attributes == null || attributes.length == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid attribute list"));
+ }
+
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ // Must check if the user has the right to call this method, regardless of the attributes
+ sm.checkPermission(new MBeanPermission(metadata.info.getClassName(), "-", objectName, "getAttribute"));
+ }
+
+ return getHeadInterceptor().getAttributes(metadata, attributes);
+ }
+
+ public AttributeList setAttributes(ObjectName objectName, AttributeList attributes)
+ throws InstanceNotFoundException, ReflectionException
+ {
+ if (attributes == null)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid attribute list"));
+ }
+
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ // Must check if the user has the right to call this method, regardless of the attributes
+ sm.checkPermission(new MBeanPermission(metadata.info.getClassName(), "-", objectName, "setAttribute"));
+ }
+
+ return getHeadInterceptor().setAttributes(metadata, attributes);
+ }
+
+ public Object invoke(ObjectName objectName, String methodName, Object[] args, String[] parameters)
+ throws InstanceNotFoundException, MBeanException, ReflectionException
+ {
+ if (methodName == null || methodName.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid operation name '" + methodName + "'"));
+ }
+
+ if (args == null) args = EMPTY_ARGS;
+ if (parameters == null) parameters = EMPTY_PARAMS;
+
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ return getHeadInterceptor().invoke(metadata, methodName, parameters, args);
+ }
+
+ public String getDefaultDomain()
+ {
+ return defaultDomain;
+ }
+
+ public String[] getDomains()
+ {
+ synchronized (domains)
+ {
+ Set keys = domains.keySet();
+ return (String[])keys.toArray(new String[keys.size()]);
+ }
+ }
+
+ public Integer getMBeanCount()
+ {
+ MBeanRepository repository = getMBeanRepository();
+ synchronized (repository)
+ {
+ return new Integer(repository.size());
+ }
+ }
+
+ public boolean isRegistered(ObjectName objectName)
+ {
+ try
+ {
+ return findMBeanMetaData(objectName) != null;
+ }
+ catch (InstanceNotFoundException x)
+ {
+ return false;
+ }
+ }
+
+ public MBeanInfo getMBeanInfo(ObjectName objectName)
+ throws InstanceNotFoundException, IntrospectionException, ReflectionException
+ {
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ MBeanInfo info = getHeadInterceptor().getMBeanInfo(metadata);
+ if (info == null) throw new JMRuntimeException("MBeanInfo returned for MBean " + objectName + " is null");
+ return info;
+ }
+
+ public ObjectInstance getObjectInstance(ObjectName objectName)
+ throws InstanceNotFoundException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ objectName = secureObjectName(objectName);
+ }
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ if (sm != null)
+ {
+ sm.checkPermission(new MBeanPermission(metadata.info.getClassName(), "-", objectName, "getObjectInstance"));
+ }
+
+ return metadata.instance;
+ }
+
+ public boolean isInstanceOf(ObjectName objectName, String className)
+ throws InstanceNotFoundException
+ {
+ if (className == null || className.trim().length() == 0)
+ {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Invalid class name"));
+ }
+
+ objectName = secureObjectName(objectName);
+
+ MBeanMetaData metadata = findMBeanMetaData(objectName);
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ sm.checkPermission(new MBeanPermission(metadata.info.getClassName(), "-", objectName, "isInstanceOf"));
+ }
+
+ try
+ {
+ ClassLoader loader = metadata.classloader;
+ Class cls = null;
+ if (loader != null)
+ cls = loader.loadClass(className);
+ else
+ cls = Class.forName(className, false, null);
+
+ if (metadata.mbean instanceof StandardMBean)
+ {
+ Object impl = ((StandardMBean) metadata.mbean).getImplementation();
+ return cls.isInstance(impl);
+ }
+ else
+ {
+ return cls.isInstance(metadata.mbean);
+ }
+ }
+ catch (ClassNotFoundException x)
+ {
+ return false;
+ }
+ }
+
+ public Set queryMBeans(ObjectName patternName, QueryExp filter)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ patternName = secureObjectName(patternName);
+ // Must check if the user has the right to call this method,
+ // no matter which ObjectName has been passed.
+ sm.checkPermission(new MBeanPermission("-#-[-]", "queryMBeans"));
+ }
+
+ Set match = queryObjectNames(patternName, filter, true);
+
+ Set set = new HashSet();
+ for (Iterator i = match.iterator(); i.hasNext();)
+ {
+ ObjectName name = (ObjectName)i.next();
+ try
+ {
+ MBeanMetaData metadata = findMBeanMetaData(name);
+ set.add(metadata.instance);
+ }
+ catch (InstanceNotFoundException ignored)
+ {
+ // A concurrent thread removed the MBean after queryNames, ignore
+ }
+ }
+ return set;
+ }
+
+ public Set queryNames(ObjectName patternName, QueryExp filter)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ patternName = secureObjectName(patternName);
+ // Must check if the user has the right to call this method,
+ // no matter which ObjectName has been passed.
+ sm.checkPermission(new MBeanPermission("-#-[-]", "queryNames"));
+ }
+
+ return queryObjectNames(patternName, filter, false);
+ }
+
+ /**
+ * Utility method for queryNames and queryMBeans that returns a set of ObjectNames.
+ * It does 3 things:
+ * 1) filter the MBeans following the given ObjectName pattern
+ * 2) filter the MBeans following the permissions that client code has
+ * 3) filter the MBeans following the given QueryExp
+ * It is important that these 3 operations are done in this order
+ */
+ private Set queryObjectNames(ObjectName patternName, QueryExp filter, boolean instances)
+ {
+ // First, retrieve the scope of the query: all mbeans matching the patternName
+ Set scope = findMBeansByPattern(patternName);
+
+ // Second, filter the scope by checking the caller's permissions
+ Set secureScope = filterMBeansBySecurity(scope, instances);
+
+ // Third, filter the scope using the given QueryExp
+ Set match = filterMBeansByQuery(secureScope, filter);
+
+ return match;
+ }
+
+ /**
+ * Returns a set of ObjectNames of the registered MBeans that match the given ObjectName pattern
+ */
+ private Set findMBeansByPattern(ObjectName pattern)
+ {
+ if (pattern == null)
+ {
+ try
+ {
+ pattern = new ObjectName("*:*");
+ }
+ catch (MalformedObjectNameException ignored)
+ {
+ }
+ }
+
+ pattern = normalizeObjectName(pattern);
+
+ String patternDomain = pattern.getDomain();
+ Hashtable patternProps = pattern.getKeyPropertyList();
+
+ Set set = new HashSet();
+
+ // Clone the repository, we are faster than holding the lock while iterating
+ MBeanRepository repository = (MBeanRepository)getMBeanRepository().clone();
+
+ for (Iterator i = repository.iterator(); i.hasNext();)
+ {
+ MBeanMetaData metadata = (MBeanMetaData)i.next();
+ ObjectName name = metadata.name;
+ Hashtable props = name.getKeyPropertyList();
+
+ String domain = name.getDomain();
+ if (Utils.wildcardMatch(patternDomain, domain))
+ {
+ // Domain matches, now check properties
+ if (pattern.isPropertyPattern())
+ {
+ // A property pattern with no entries, can only be '*'
+ if (patternProps.size() == 0)
+ {
+ // User wants all properties
+ set.add(name);
+ }
+ else
+ {
+ // Loop on the properties of the pattern.
+ // If one is not found then the current ObjectName does not match
+ boolean found = true;
+ for (Iterator j = patternProps.entrySet().iterator(); j.hasNext();)
+ {
+ Map.Entry entry = (Map.Entry)j.next();
+ Object patternKey = entry.getKey();
+ Object patternValue = entry.getValue();
+ if (patternKey.equals("*"))
+ {
+ continue;
+ }
+
+ // Try to see if the current ObjectName contains this entry
+ if (!props.containsKey(patternKey))
+ {
+ // Not even the key is present
+ found = false;
+ break;
+ }
+ else
+ {
+ // The key is present, let's check if the values are equal
+ Object value = props.get(patternKey);
+ if (value == null && patternValue == null)
+ {
+ // Values are equal, go on with next pattern entry
+ continue;
+ }
+ if (value != null && value.equals(patternValue))
+ {
+ // Values are equal, go on with next pattern entry
+ continue;
+ }
+ // Here values are different
+ found = false;
+ break;
+ }
+ }
+ if (found) set.add(name);
+ }
+ }
+ else
+ {
+ if (props.entrySet().equals(patternProps.entrySet())) set.add(name);
+ }
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Filters the given set of ObjectNames following the permission that client code has granted.
+ * Returns a set containing the allowed ObjectNames.
+ */
+ private Set filterMBeansBySecurity(Set mbeans, boolean instances)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null) return mbeans;
+
+ HashSet set = new HashSet();
+ for (Iterator i = mbeans.iterator(); i.hasNext();)
+ {
+ ObjectName name = (ObjectName)i.next();
+ try
+ {
+ MBeanMetaData metadata = findMBeanMetaData(name);
+ String className = metadata.info.getClassName();
+ sm.checkPermission(new MBeanPermission(className, "-", name, instances ? "queryMBeans" : "queryNames"));
+ set.add(name);
+ }
+ catch (InstanceNotFoundException ignored)
+ {
+ // A concurrent thread removed this MBean, continue
+ continue;
+ }
+ catch (SecurityException ignored)
+ {
+ // Don't add the name to the list, and go on.
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Filters the given set of ObjectNames following the given QueryExp.
+ * Returns a set of ObjectNames that match the given QueryExp.
+ */
+ private Set filterMBeansByQuery(Set scope, QueryExp filter)
+ {
+ if (filter == null) return scope;
+
+ Set set = new HashSet();
+ for (Iterator i = scope.iterator(); i.hasNext();)
+ {
+ ObjectName name = (ObjectName)i.next();
+ filter.setMBeanServer(this);
+ try
+ {
+ if (filter.apply(name)) set.add(name);
+ }
+ catch (BadStringOperationException ignored)
+ {
+ }
+ catch (BadBinaryOpValueExpException ignored)
+ {
+ }
+ catch (BadAttributeValueExpException x)
+ {
+ }
+ catch (InvalidApplicationException x)
+ {
+ }
+ catch (SecurityException x)
+ {
+ }
+ catch (Exception x)
+ {
+ // The 1.2 spec says Exceptions must not be propagated
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Returns a normalized ObjectName from the given one.
+ * If an ObjectName is specified with the abbreviated notation for the default domain, that is ':key=value'
+ * this method returns an ObjectName whose domain is the default domain of this MBeanServer and with the same
+ * properties.
+ */
+ private ObjectName normalizeObjectName(ObjectName name)
+ {
+ if (name == null) return null;
+
+ String defaultDomain = getDefaultDomain();
+ String domain = name.getDomain();
+
+ if (domain.length() == 0 && defaultDomain.length() > 0)
+ {
+ // The given object name specifies the abbreviated form to indicate the default domain,
+ // ie ':key=value', the empty string as domain. I must convert this abbreviated form
+ // to the full one, if the default domain of this mbeanserver is not the empty string as well
+ StringBuffer buffer = new StringBuffer(defaultDomain).append(":").append(name.getKeyPropertyListString());
+ if (name.isPropertyPattern())
+ {
+ if (name.getKeyPropertyList().size() > 0)
+ buffer.append(",*");
+ else
+ buffer.append("*");
+ }
+ try
+ {
+ name = new ObjectName(buffer.toString());
+ }
+ catch (MalformedObjectNameException ignored)
+ {
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Returns an ObjectName instance even if the provided ObjectName is a subclass.
+ * This is done to avoid security holes: a nasty ObjectName subclass can bypass security checks.
+ */
+ private ObjectName secureObjectName(ObjectName name)
+ {
+ // I cannot trust ObjectName, since a malicious user can send a subclass that overrides equals and hashcode
+ // to match another ObjectName for which it does not have permission, or returns different results from
+ // ObjectName.getCanonicalName() for different calls, so that passes the security checks but in fact will
+ // later refer to a different ObjectName for which it does not have permission.
+ if (name == null) return null;
+ return ObjectName.getInstance(name);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServerBuilder.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServerBuilder.java
new file mode 100644
index 0000000..49e4ad7
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServerBuilder.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerBuilder;
+import javax.management.MBeanServerDelegate;
+
+/**
+ * <p>This class is responsible for creating new instances of {@link MBeanServerDelegate}
+ * and {@link MBeanServer}. It creates instances from the implementation in the
+ * <code>mx4j.server</code> package.</p>
+ *
+ * <p>The {@link javax.management.MBeanServerFactory} first creates the delegate, then it
+ * creates the MBeanServer and provides a reference to the created delegate to it.
+ * Note that the delegate passed to the MBeanServer might not be the instance returned
+ * by this builder; for example, it could be a wrapper around it.</p>
+ *
+ * @see MBeanServer
+ * @see javax.management.MBeanServerFactory
+ *
+ * @author <a href="mailto:oreinert@users.sourceforge.net">Olav Reinert</a>
+ * @version $Revision: 1.1.1.1 $
+ **/
+
+public class MX4JMBeanServerBuilder extends MBeanServerBuilder
+{
+ /**
+ * Returns a new {@link MX4JMBeanServerDelegate} instance for a new MBeanServer.
+ * @return a new {@link MX4JMBeanServerDelegate} instance for a new MBeanServer.
+ **/
+ public MBeanServerDelegate newMBeanServerDelegate()
+ {
+ return new MX4JMBeanServerDelegate();
+ }
+
+ /**
+ * Returns a new {@link MX4JMBeanServer} instance.
+ * @param defaultDomain the default domain name for the new server.
+ * @param outer the {@link MBeanServer} that is passed in calls to
+ * {@link javax.management.MBeanRegistration#preRegister(javax.management.MBeanServer, javax.management.ObjectName)}.
+ * @param delegate the {@link MBeanServerDelegate} instance for the new server.
+ * @return a new {@link MX4JMBeanServer} instance.
+ **/
+ public MBeanServer newMBeanServer(String defaultDomain, MBeanServer outer, MBeanServerDelegate delegate)
+ {
+ return new MX4JMBeanServer(defaultDomain, outer, delegate);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServerDelegate.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServerDelegate.java
new file mode 100644
index 0000000..66dd3a9
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/MX4JMBeanServerDelegate.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+
+import javax.management.MBeanServerDelegate;
+
+/**
+ * The MBeanServerDelegate subclass typical of the MX4J implementation.
+ *
+ * @see javax.management.MBeanServerBuilder
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MX4JMBeanServerDelegate extends MBeanServerDelegate
+{
+ public String getImplementationName()
+ {
+ return "MX4J";
+ }
+
+ public String getImplementationVendor()
+ {
+ return "The MX4J Team";
+ }
+
+ public String getImplementationVersion()
+ {
+ return "2.0-beta-1";
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/ModifiableClassLoaderRepository.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/ModifiableClassLoaderRepository.java
new file mode 100644
index 0000000..6c4833b
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/ModifiableClassLoaderRepository.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+import javax.management.loading.ClassLoaderRepository;
+
+/**
+ * Base class to extend to create custom ClassLoaderRepositories.
+ * MX4J's MBeanServer can use a custom ClassLoaderRepository instead of the default one
+ * by simply specifying a suitable system property, see {@link mx4j.MX4JSystemKeys}.
+ * It must be a class, otherwise it opens up a security hole, as anyone can cast the MBeanServer's
+ * ClassLoaderRepository down to this class and call addClassLoader or removeClassLoader
+ * since, if this class is an interface, they must be public.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public abstract class ModifiableClassLoaderRepository implements ClassLoaderRepository
+{
+ /**
+ * Adds, if does not already exist, the specified ClassLoader to this repository.
+ * @param cl The classloader to add
+ * @see #removeClassLoader
+ */
+ protected abstract void addClassLoader(ClassLoader cl);
+
+ /**
+ * Removes, if exists, the specified ClassLoader from this repository.
+ * @param cl The classloader to remove
+ * @see #addClassLoader
+ */
+ protected abstract void removeClassLoader(ClassLoader cl);
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/ReflectedMBeanInvoker.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/ReflectedMBeanInvoker.java
new file mode 100644
index 0000000..f46fd76
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/ReflectedMBeanInvoker.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMRuntimeException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.ReflectionException;
+import javax.management.RuntimeErrorException;
+import javax.management.RuntimeMBeanException;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.ImplementationException;
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.Utils;
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.MethodTernaryTree;
+
+/**
+ * MBeanInvoker that uses reflection to invoke on MBean instances.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ReflectedMBeanInvoker implements MBeanInvoker
+{
+ private static final String[] EMPTY_PARAMS = new String[0];
+ private static final Object[] EMPTY_ARGS = new Object[0];
+
+ private final Map attributes = new HashMap();
+ private final Map attributeNames = new HashMap();
+ private final MethodTernaryTree operations = new MethodTernaryTree();
+ private final MethodTernaryTree methods = new MethodTernaryTree();
+
+ public Object invoke(MBeanMetaData metadata, String method, String[] params, Object[] args) throws MBeanException, ReflectionException
+ {
+ MBeanOperationInfo oper = getStandardOperationInfo(metadata, method, params);
+ if (oper != null)
+ {
+ try
+ {
+ return invokeImpl(metadata, method, params, args);
+ }
+ catch (IllegalArgumentException x)
+ {
+ throw new RuntimeOperationsException(x);
+ }
+ }
+ else
+ {
+ throw new ReflectionException(new NoSuchMethodException("Operation " + method + " does not belong to the management interface"));
+ }
+ }
+
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException
+ {
+ MBeanAttributeInfo attr = getStandardAttributeInfo(metadata, attribute, false);
+ if (attr != null)
+ {
+ String attributeName = getAttributeName(attr, true);
+ try
+ {
+ return invokeImpl(metadata, attributeName, EMPTY_PARAMS, EMPTY_ARGS);
+ }
+ catch (IllegalArgumentException x)
+ {
+ // Never thrown, since there are no arguments
+ throw new ImplementationException();
+ }
+ }
+ else
+ {
+ throw new AttributeNotFoundException(attribute);
+ }
+ }
+
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException
+ {
+ String name = attribute.getName();
+ MBeanAttributeInfo attr = getStandardAttributeInfo(metadata, name, true);
+ if (attr != null)
+ {
+ String attributeName = getAttributeName(attr, false);
+ try
+ {
+ invokeImpl(metadata, attributeName, new String[]{attr.getType()}, new Object[]{attribute.getValue()});
+ }
+ catch (IllegalArgumentException x)
+ {
+ throw new InvalidAttributeValueException("Invalid value for attribute " + name + ": " + attribute.getValue());
+ }
+ }
+ else
+ {
+ throw new AttributeNotFoundException(name);
+ }
+ }
+
+ protected Object invokeImpl(MBeanMetaData metadata, String method, String[] signature, Object[] args) throws ReflectionException, MBeanException, IllegalArgumentException
+ {
+ Method m = getStandardManagementMethod(metadata, method, signature);
+
+ try
+ {
+ return m.invoke(metadata.mbean, args);
+ }
+ catch (IllegalAccessException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (InvocationTargetException x)
+ {
+ Throwable t = x.getTargetException();
+ if (t instanceof Error) throw new RuntimeErrorException((Error)t);
+ if (t instanceof JMRuntimeException) throw (JMRuntimeException)t;
+ if (t instanceof RuntimeException) throw new RuntimeMBeanException((RuntimeException)t);
+ throw new MBeanException((Exception)t);
+ }
+ }
+
+ private MBeanAttributeInfo getStandardAttributeInfo(MBeanMetaData metadata, String attribute, boolean isWritable)
+ {
+ MBeanAttributeInfo attr = null;
+ synchronized (attributes)
+ {
+ attr = (MBeanAttributeInfo)attributes.get(attribute);
+ }
+ if (attr != null)
+ {
+ if (isWritable && attr.isWritable()) return attr;
+ if (!isWritable && attr.isReadable()) return attr;
+ }
+ else
+ {
+ MBeanAttributeInfo[] attrs = metadata.info.getAttributes();
+ if (attrs != null)
+ {
+ for (int i = 0; i < attrs.length; ++i)
+ {
+ attr = attrs[i];
+ String name = attr.getName();
+ if (attribute.equals(name))
+ {
+ synchronized (attributes)
+ {
+ attributes.put(attribute, attr);
+ }
+ if (isWritable && attr.isWritable()) return attr;
+ if (!isWritable && attr.isReadable()) return attr;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private MBeanOperationInfo getStandardOperationInfo(MBeanMetaData metadata, String method, String[] signature)
+ {
+ MBeanOperationInfo oper = null;
+
+ synchronized (operations)
+ {
+ oper = (MBeanOperationInfo)operations.get(method, signature);
+ }
+
+ if (oper != null) return oper;
+
+ // The MBeanOperationInfo is not in the cache, look it up
+ MBeanInfo info = metadata.info;
+ MBeanOperationInfo[] opers = info.getOperations();
+ if (opers != null)
+ {
+ for (int i = 0; i < opers.length; ++i)
+ {
+ oper = opers[i];
+ String name = oper.getName();
+ if (method.equals(name))
+ {
+ // Same method name, check number of parameters
+ MBeanParameterInfo[] params = oper.getSignature();
+ if (signature.length == params.length)
+ {
+ boolean match = true;
+ for (int j = 0; j < params.length; ++j)
+ {
+ MBeanParameterInfo param = params[j];
+ if (!signature[j].equals(param.getType()))
+ {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ {
+ synchronized (operations)
+ {
+ operations.put(method, signature, oper);
+ }
+ return oper;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private Method getStandardManagementMethod(MBeanMetaData metadata, String name, String[] signature) throws ReflectionException
+ {
+ Method method = null;
+ synchronized (methods)
+ {
+ method = (Method)methods.get(name, signature);
+ }
+ if (method != null) return method;
+
+ // Method is not in cache, look it up
+ try
+ {
+ Class[] params = Utils.loadClasses(metadata.classloader, signature);
+ method = metadata.mbean.getClass().getMethod(name, params);
+ synchronized (methods)
+ {
+ methods.put(name, signature, method);
+ }
+ return method;
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (NoSuchMethodException x)
+ {
+ throw new ReflectionException(x);
+ }
+ }
+
+ private String getAttributeName(MBeanAttributeInfo attribute, boolean getter)
+ {
+ AttributeName attributeName = null;
+ String name = attribute.getName();
+ synchronized (attributeNames)
+ {
+ attributeName = (AttributeName)attributeNames.get(name);
+ }
+ if (attributeName == null)
+ {
+ String prefix = attribute.isIs() ? "is" : "get";
+ attributeName = new AttributeName(prefix + name, "set" + name);
+ synchronized (attributeNames)
+ {
+ attributeNames.put(name, attributeName);
+ }
+ }
+
+ if (getter) return attributeName.getter;
+ return attributeName.setter;
+ }
+
+ private static class AttributeName
+ {
+ private final String getter;
+ private final String setter;
+
+ public AttributeName(String getter, String setter)
+ {
+ this.getter = getter;
+ this.setter = setter;
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/ContextClassLoaderMBeanServerInterceptor.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/ContextClassLoaderMBeanServerInterceptor.java
new file mode 100644
index 0000000..899acb8
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/ContextClassLoaderMBeanServerInterceptor.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+import java.security.PrivilegedAction;
+import java.security.AccessController;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ReflectionException;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData;
+
+/**
+ * This interceptor sets the context class loader to the proper value for incoming calls.
+ * It saves the current context class loader, set the context class loader to be the MBean's class loader for
+ * the current call, and on return re-set the context class loader to the previous value
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ContextClassLoaderMBeanServerInterceptor extends DefaultMBeanServerInterceptor
+{
+ public ContextClassLoaderMBeanServerInterceptor()
+ {
+ // Disabled by default
+ setEnabled(false);
+ }
+
+ public String getType()
+ {
+ return "contextclassloader";
+ }
+
+ public void addNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ super.addNotificationListener(metadata, listener, filter, handback);
+ return;
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ super.addNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener) throws ListenerNotFoundException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ super.removeNotificationListener(metadata, listener);
+ return;
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ super.removeNotificationListener(metadata, listener);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ super.removeNotificationListener(metadata, listener, filter, handback);
+ return;
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ super.removeNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void instantiate(MBeanMetaData metadata, String className, String[] params, Object[] args) throws ReflectionException, MBeanException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ super.instantiate(metadata, className, params, args);
+ return;
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ super.instantiate(metadata, className, params, args);
+ }
+
+ public void registration(MBeanMetaData metadata, int operation) throws MBeanRegistrationException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ super.registration(metadata, operation);
+ return;
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ super.registration(metadata, operation);
+ }
+
+ public MBeanInfo getMBeanInfo(MBeanMetaData metadata)
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ return super.getMBeanInfo(metadata);
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ return super.getMBeanInfo(metadata);
+ }
+
+ public Object invoke(MBeanMetaData metadata, String method, String[] params, Object[] args) throws MBeanException, ReflectionException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ return super.invoke(metadata, method, params, args);
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ return super.invoke(metadata, method, params, args);
+ }
+
+ public AttributeList getAttributes(MBeanMetaData metadata, String[] attributes)
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ return super.getAttributes(metadata, attributes);
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ return super.getAttributes(metadata, attributes);
+ }
+
+ public AttributeList setAttributes(MBeanMetaData metadata, AttributeList attributes)
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ return super.setAttributes(metadata, attributes);
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ return super.setAttributes(metadata, attributes);
+ }
+
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ return super.getAttribute(metadata, attribute);
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ return super.getAttribute(metadata, attribute);
+ }
+
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException
+ {
+ if (isEnabled())
+ {
+ ClassLoader context = getContextClassLoader();
+ if (metadata.classloader != context)
+ {
+ try
+ {
+ setContextClassLoader(metadata.classloader);
+ super.setAttribute(metadata, attribute);
+ return;
+ }
+ finally
+ {
+ setContextClassLoader(context);
+ }
+ }
+ }
+
+ super.setAttribute(metadata, attribute);
+ }
+
+ private ClassLoader getContextClassLoader()
+ {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ private void setContextClassLoader(final ClassLoader cl)
+ {
+ AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ Thread.currentThread().setContextClassLoader(cl);
+ return null;
+ }
+ });
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/DefaultMBeanServerInterceptor.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/DefaultMBeanServerInterceptor.java
new file mode 100644
index 0000000..5112eea
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/DefaultMBeanServerInterceptor.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+import java.util.List;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ReflectionException;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Log;
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Logger;
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData;
+
+/**
+ * Base class for MBeanServer --> MBean interceptors.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.2 $
+ */
+public abstract class DefaultMBeanServerInterceptor implements MBeanServerInterceptor, DefaultMBeanServerInterceptorMBean
+{
+ private boolean enabled = true;
+ private String logCategory;
+ private List chain;
+
+ protected DefaultMBeanServerInterceptor()
+ {
+ // It's amazing how setting up here this string dramatically reduces the times to get the Logger instance
+ logCategory = getClass().getName() + "." + getType();
+ }
+
+ /**
+ * Returns whether this interceptor is enabled
+ * @see #setEnabled
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * Enables or disables this interceptor
+ * @see #isEnabled
+ */
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Returns the type of this interceptor
+ */
+ public abstract String getType();
+
+ protected synchronized MBeanServerInterceptor getNext()
+ {
+ int index = chain.indexOf(this);
+ MBeanServerInterceptor next = (MBeanServerInterceptor)chain.get(index + 1);
+ next.setChain(chain);
+ return next;
+ }
+
+ public synchronized void setChain(List chain)
+ {
+ this.chain = chain;
+ }
+
+ protected Logger getLogger()
+ {
+ return Log.getLogger(logCategory);
+ }
+
+ public void addNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ {
+ getNext().addNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener) throws ListenerNotFoundException
+ {
+ getNext().removeNotificationListener(metadata, listener);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException
+ {
+ getNext().removeNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void instantiate(MBeanMetaData metadata, String className, String[] params, Object[] args) throws ReflectionException, MBeanException
+ {
+ getNext().instantiate(metadata, className, params, args);
+ }
+
+ public void registration(MBeanMetaData metadata, int operation) throws MBeanRegistrationException
+ {
+ getNext().registration(metadata, operation);
+ }
+
+ public MBeanInfo getMBeanInfo(MBeanMetaData metadata)
+ {
+ return getNext().getMBeanInfo(metadata);
+ }
+
+ public Object invoke(MBeanMetaData metadata, String method, String[] params, Object[] args) throws MBeanException, ReflectionException
+ {
+ return getNext().invoke(metadata, method, params, args);
+ }
+
+ public AttributeList getAttributes(MBeanMetaData metadata, String[] attributes)
+ {
+ return getNext().getAttributes(metadata, attributes);
+ }
+
+ public AttributeList setAttributes(MBeanMetaData metadata, AttributeList attributes)
+ {
+ return getNext().setAttributes(metadata, attributes);
+ }
+
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException
+ {
+ return getNext().getAttribute(metadata, attribute);
+ }
+
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException
+ {
+ getNext().setAttribute(metadata, attribute);
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/DefaultMBeanServerInterceptorMBean.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/DefaultMBeanServerInterceptorMBean.java
new file mode 100644
index 0000000..68a8d31
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/DefaultMBeanServerInterceptorMBean.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+/**
+ * Management interface for the DefaultMBeanServerInterceptor MBean
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface DefaultMBeanServerInterceptorMBean
+{
+ /**
+ * Returns whether this interceptor is enabled
+ * @see #setEnabled
+ */
+ public boolean isEnabled();
+
+ /**
+ * Enables or disables this interceptor
+ * @see #isEnabled
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * Returns the type of this interceptor
+ */
+ public String getType();
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/InvokerMBeanServerInterceptor.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/InvokerMBeanServerInterceptor.java
new file mode 100644
index 0000000..bcb8ba4
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/InvokerMBeanServerInterceptor.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.DynamicMBean;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMRuntimeException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeErrorException;
+import javax.management.RuntimeMBeanException;
+import javax.management.NotificationEmitter;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.ImplementationException;
+import org.apache.felix.mosgi.jmx.agent.mx4j.log.Logger;
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData;
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.Utils;
+
+/**
+ * The last MBeanServer --$gt; MBean interceptor in the chain.
+ * It calls the MBean instance; if the MBean is a dynamic MBean, the call is direct, otherwise the call is delegated
+ * to an {@link mx4j.server.MBeanInvoker MBeanInvoker}.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class InvokerMBeanServerInterceptor extends DefaultMBeanServerInterceptor implements InvokerMBeanServerInterceptorMBean
+{
+ private MBeanServer outerServer;
+
+ /**
+ * Instantiates a new interceptor instance.
+ * @param outerServer the {@link MBeanServer} instance that is passed to
+ * {@link MBeanRegistration#preRegister(MBeanServer, ObjectName)}.
+ */
+ public InvokerMBeanServerInterceptor(MBeanServer outerServer)
+ {
+ this.outerServer = outerServer;
+ }
+
+ /**
+ * Returns the type of this interceptor
+ */
+ public String getType()
+ {
+ return "invoker";
+ }
+
+ /**
+ * This interceptor is always enabled
+ */
+ public boolean isEnabled()
+ {
+ return true;
+ }
+
+ public void addNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ {
+ ((NotificationBroadcaster)metadata.mbean).addNotificationListener(listener, filter, handback);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener) throws ListenerNotFoundException
+ {
+ ((NotificationBroadcaster)metadata.mbean).removeNotificationListener(listener);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ throws ListenerNotFoundException
+ {
+ ((NotificationEmitter)metadata.mbean).removeNotificationListener(listener, filter, handback);
+ }
+
+ public void instantiate(MBeanMetaData metadata, String className, String[] params, Object[] args) throws ReflectionException, MBeanException
+ {
+ try
+ {
+ ClassLoader loader = metadata.classloader;
+ Class cls = null;
+ if (loader != null)
+ cls = loader.loadClass(className);
+ else
+ cls = Class.forName(className, false, null);
+
+ Class[] signature = Utils.loadClasses(loader, params);
+
+ Constructor ctor = cls.getConstructor(signature);
+
+ metadata.mbean = ctor.newInstance(args);
+ }
+ catch (ClassNotFoundException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (NoSuchMethodException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (InstantiationException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (IllegalAccessException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (IllegalArgumentException x)
+ {
+ throw new ReflectionException(x);
+ }
+ catch (InvocationTargetException x)
+ {
+ Throwable t = x.getTargetException();
+ if (t instanceof Error)
+ {
+ throw new RuntimeErrorException((Error)t);
+ }
+ else if (t instanceof RuntimeException)
+ {
+ throw new RuntimeMBeanException((RuntimeException)t);
+ }
+ else
+ {
+ throw new MBeanException((Exception)t);
+ }
+ }
+ }
+
+ public void registration(MBeanMetaData metadata, int operation) throws MBeanRegistrationException
+ {
+ if (!(metadata.mbean instanceof MBeanRegistration)) return;
+
+ MBeanRegistration registrable = (MBeanRegistration)metadata.mbean;
+
+ try
+ {
+ switch (operation)
+ {
+ case PRE_REGISTER:
+ ObjectName objName = registrable.preRegister(outerServer, metadata.name);
+ metadata.name = objName;
+ break;
+ case POST_REGISTER_TRUE:
+ registrable.postRegister(Boolean.TRUE);
+ break;
+ case POST_REGISTER_FALSE:
+ registrable.postRegister(Boolean.FALSE);
+ break;
+ case PRE_DEREGISTER:
+ registrable.preDeregister();
+ break;
+ case POST_DEREGISTER:
+ registrable.postDeregister();
+ break;
+ default:
+ throw new ImplementationException();
+ }
+ }
+ catch (RuntimeException x) {
+ throw new RuntimeMBeanException(x);
+ }
+ catch (Exception x)
+ {
+ if (x instanceof MBeanRegistrationException)
+ {
+ throw (MBeanRegistrationException)x;
+ }
+ throw new MBeanRegistrationException(x);
+ }
+ }
+
+ public MBeanInfo getMBeanInfo(MBeanMetaData metadata)
+ {
+ if (metadata.dynamic)
+ {
+ // From JMX 1.1 the MBeanInfo may be dynamically changed at every time, let's refresh it
+ MBeanInfo info = null;
+ try {
+ info = ((DynamicMBean)metadata.mbean).getMBeanInfo();
+ } catch (RuntimeException x) {
+ throw new RuntimeMBeanException(x);
+ }
+ if (info == null) return null;
+ metadata.info = info;
+
+ // Refresh also ObjectInstance.getClassName(), if it's the case
+ String className = info.getClassName();
+ if (!metadata.instance.getClassName().equals(className))
+ {
+ metadata.instance = new ObjectInstance(metadata.name, className);
+ }
+ }
+
+ return (MBeanInfo)metadata.info.clone();
+ }
+
+ public Object invoke(MBeanMetaData metadata, String method, String[] params, Object[] args) throws MBeanException, ReflectionException
+ {
+ if (metadata.dynamic)
+ {
+ try
+ {
+ return ((DynamicMBean)metadata.mbean).invoke(method, args, params);
+ }
+ catch (JMRuntimeException x)
+ {
+ throw x;
+ }
+ catch (RuntimeException x)
+ {
+ throw new RuntimeMBeanException(x);
+ }
+ catch (Error x)
+ {
+ throw new RuntimeErrorException(x);
+ }
+ }
+ else
+ {
+ return metadata.invoker.invoke(metadata, method, params, args);
+ }
+ }
+
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException
+ {
+ if (metadata.dynamic)
+ {
+ try
+ {
+ return ((DynamicMBean)metadata.mbean).getAttribute(attribute);
+ }
+ catch (JMRuntimeException x)
+ {
+ throw x;
+ }
+ catch (RuntimeException x)
+ {
+ throw new RuntimeMBeanException(x);
+ }
+ catch (Error x)
+ {
+ throw new RuntimeErrorException(x);
+ }
+ }
+ else
+ {
+ return metadata.invoker.getAttribute(metadata, attribute);
+ }
+ }
+
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException
+ {
+ if (metadata.dynamic)
+ {
+ try
+ {
+ ((DynamicMBean)metadata.mbean).setAttribute(attribute);
+ }
+ catch (JMRuntimeException x)
+ {
+ throw x;
+ }
+ catch (RuntimeException x)
+ {
+ throw new RuntimeMBeanException(x);
+ }
+ catch (Error x)
+ {
+ throw new RuntimeErrorException(x);
+ }
+ }
+ else
+ {
+ metadata.invoker.setAttribute(metadata, attribute);
+ }
+ }
+
+ public AttributeList getAttributes(MBeanMetaData metadata, String[] attributes)
+ {
+ if (metadata.dynamic)
+ {
+ try
+ {
+ return ((DynamicMBean)metadata.mbean).getAttributes(attributes);
+ }
+ catch (JMRuntimeException x)
+ {
+ throw x;
+ }
+ catch (RuntimeException x)
+ {
+ throw new RuntimeMBeanException(x);
+ }
+ catch (Error x)
+ {
+ throw new RuntimeErrorException(x);
+ }
+ }
+ else
+ {
+ AttributeList list = new AttributeList();
+ for (int i = 0; i < attributes.length; ++i)
+ {
+ String name = attributes[i];
+ try
+ {
+ Object value = getAttribute(metadata, name);
+ Attribute attr = new Attribute(name, value);
+ list.add(attr);
+ }
+ catch (Exception ignored)
+ {
+ Logger logger = getLogger();
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Exception caught from getAttributes(), ignoring attribute " + name);
+ }
+ }
+ return list;
+ }
+ }
+
+ public AttributeList setAttributes(MBeanMetaData metadata, AttributeList attributes)
+ {
+ if (metadata.dynamic)
+ {
+ try
+ {
+ return ((DynamicMBean)metadata.mbean).setAttributes(attributes);
+ }
+ catch (JMRuntimeException x)
+ {
+ throw x;
+ }
+ catch (RuntimeException x)
+ {
+ throw new RuntimeMBeanException(x);
+ }
+ catch (Error x)
+ {
+ throw new RuntimeErrorException(x);
+ }
+ }
+ else
+ {
+ AttributeList list = new AttributeList();
+ for (int i = 0; i < attributes.size(); ++i)
+ {
+ Attribute attr = (Attribute)attributes.get(i);
+ try
+ {
+ setAttribute(metadata, attr);
+ list.add(attr);
+ }
+ catch (Exception ignored)
+ {
+ Logger logger = getLogger();
+ if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Exception caught from setAttributes(), ignoring attribute " + attr, ignored);
+ }
+ }
+ return list;
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/InvokerMBeanServerInterceptorMBean.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/InvokerMBeanServerInterceptorMBean.java
new file mode 100644
index 0000000..217677e
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/InvokerMBeanServerInterceptorMBean.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+/**
+ * Management interface for the InvokerMBeanServerInterceptor MBean
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface InvokerMBeanServerInterceptorMBean
+{
+ /**
+ * Returns the type of this interceptor
+ */
+ public String getType();
+
+ /**
+ * This interceptor is always enabled
+ */
+ public boolean isEnabled();
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptor.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptor.java
new file mode 100644
index 0000000..09f61a2
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+
+import java.util.List;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ReflectionException;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData;
+
+/**
+ * MBeanServer --> MBean interceptor.
+ * These interceptors are used internally to implement MBeanServer functionality prior to call
+ * MBeans, and can be used to customize MBeanServer implementation by users.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface MBeanServerInterceptor
+{
+ /**
+ * Constant used to specify the status of the MBean registration in {@link #registration}
+ */
+ public static final int PRE_REGISTER = 1;
+ /**
+ * Constant used to specify the status of the MBean registration in {@link #registration}
+ */
+ public static final int POST_REGISTER_TRUE = 2;
+ /**
+ * Constant used to specify the status of the MBean registration in {@link #registration}
+ */
+ public static final int POST_REGISTER_FALSE = 3;
+ /**
+ * Constant used to specify the status of the MBean registration in {@link #registration}
+ */
+ public static final int PRE_DEREGISTER = 4;
+ /**
+ * Constant used to specify the status of the MBean registration in {@link #registration}
+ */
+ public static final int POST_DEREGISTER = 5;
+
+ /**
+ * A concise string that tells the type of this interceptor
+ */
+ public String getType();
+
+ /**
+ * Sets the chain of interceptors on this interceptor. This interceptor will use this list to
+ * find the interceptor in the chain after itself
+ * @param interceptors The list of interceptors
+ */
+ public void setChain(List interceptors);
+
+ /**
+ * Adds the given notification listener to the MBean, along with the given filter and handback
+ */
+ public void addNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback);
+
+ /**
+ * Removes the given notification listener from the MBean.
+ */
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener) throws ListenerNotFoundException;
+ /**
+ * Removes the given notification listener from the MBean, specified by the given filter and handback.
+ */
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException;
+
+ /**
+ * Instantiate the given className passing the given arguments to the constructor with the given signature
+ */
+ public void instantiate(MBeanMetaData metadata, String className, String[] params, Object[] args) throws ReflectionException, MBeanException;
+
+ /**
+ * Calls the specified {@link javax.management.MBeanRegistration} method on the MBean instance.
+ */
+ public void registration(MBeanMetaData metadata, int operation) throws MBeanRegistrationException;
+
+ /**
+ * Calls getMBeanInfo on the MBean instance (only on DynamicMBeans).
+ */
+ public MBeanInfo getMBeanInfo(MBeanMetaData metadata);
+
+ /**
+ * Invokes the specified MBean operation on the MBean instance
+ */
+ public Object invoke(MBeanMetaData metadata, String method, String[] params, Object[] args) throws MBeanException, ReflectionException;
+
+ /**
+ * Gets the specified attributes values from the MBean instance.
+ */
+ public AttributeList getAttributes(MBeanMetaData metadata, String[] attributes);
+
+ /**
+ * Sets the specified attributes values on the MBean instance.
+ */
+ public AttributeList setAttributes(MBeanMetaData metadata, AttributeList attributes);
+
+ /**
+ * Gets the specified attribute value from the MBean instance.
+ */
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException;
+
+ /**
+ * Sets the specified attribute value on the MBean instance.
+ */
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException;
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptorConfigurator.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptorConfigurator.java
new file mode 100644
index 0000000..4db025a
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptorConfigurator.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+import java.util.ArrayList;
+
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.ImplementationException;
+
+/**
+ * MBean that configures the MBeanServer --> MBean interceptor chain.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MBeanServerInterceptorConfigurator implements MBeanServerInterceptorConfiguratorMBean
+{
+ public static final String OBJECT_NAME = "JMImplementation:type=MBeanServerInterceptorConfigurator";
+
+ private final MBeanServer server;
+ private final ArrayList preInterceptors = new ArrayList();
+ private final ArrayList postInterceptors = new ArrayList();
+ private final ArrayList clientInterceptors = new ArrayList();
+ private volatile boolean running;
+ private boolean chainModified;
+ private MBeanServerInterceptor head;
+
+ /**
+ * Creates an instance of this configurator, for the given MBeanServer
+ */
+ public MBeanServerInterceptorConfigurator(MBeanServer server)
+ {
+ this.server = server;
+ chainModified = true;
+ }
+
+ /**
+ * Appends the given interceptor, provided by the client, to the existing interceptor chain.
+ * @see #registerInterceptor
+ */
+ public void addInterceptor(MBeanServerInterceptor interceptor)
+ {
+ synchronized (clientInterceptors)
+ {
+ clientInterceptors.add(interceptor);
+ chainModified = true;
+ }
+ }
+
+ /**
+ * Appends the given interceptor, provided by the client, to the existing interceptor chain and registers it as MBean.
+ * @see #addInterceptor
+ */
+ public void registerInterceptor(MBeanServerInterceptor interceptor, ObjectName name) throws MBeanException
+ {
+ // First, try register this interceptor. The call will use the old interceptor chain
+ try
+ {
+ server.registerMBean(interceptor, name);
+ addInterceptor(interceptor);
+ }
+ catch (Exception x)
+ {
+ throw new MBeanException(x, "Could not register interceptor with name " + name);
+ }
+ }
+
+ /**
+ * Removes all the interceptors added via {@link #addInterceptor(MBeanServerInterceptor interceptor)}.
+ * @see #addInterceptor
+ */
+ public void clearInterceptors()
+ {
+ synchronized (clientInterceptors)
+ {
+ clientInterceptors.clear();
+ chainModified = true;
+ }
+ }
+
+ /**
+ * Adds the given interceptor at the beginning of the interceptor chain, before the custom interceptors that may be added
+ * via {@link #addInterceptor}.
+ * This method is called by the MBeanServer during initialization, to configure the interceptors needed to work properly.
+ */
+ public void addPreInterceptor(MBeanServerInterceptor interceptor)
+ {
+ if (isRunning()) throw new ImplementationException();
+ preInterceptors.add(interceptor);
+ }
+
+ /**
+ * Adds the given interceptor at the end of the interceptor chain, after the custom interceptors that may be added
+ * via {@link #addInterceptor}.
+ * This method is called by the MBeanServer during initialization, to configure the interceptors needed to work properly.
+ */
+ public void addPostInterceptor(MBeanServerInterceptor interceptor)
+ {
+ if (isRunning()) throw new ImplementationException();
+ postInterceptors.add(interceptor);
+ }
+
+ /**
+ * Returns the head interceptor of the interceptor chain.
+ * The head interceptor is always present.
+ */
+ public MBeanServerInterceptor getHeadInterceptor()
+ {
+ if (!isRunning()) return null;
+
+ if (chainModified) setupChain();
+
+ return head;
+ }
+
+ private void setupChain()
+ {
+ chainModified = false;
+
+ int size = clientInterceptors.size();
+ ArrayList chain = new ArrayList(preInterceptors.size() + size + postInterceptors.size());
+ chain.addAll(preInterceptors);
+ if (size > 0)
+ {
+ synchronized (clientInterceptors)
+ {
+ chain.addAll(clientInterceptors);
+ }
+ }
+ chain.addAll(postInterceptors);
+
+ // Set the chain on the first interceptor
+ MBeanServerInterceptor first = (MBeanServerInterceptor)chain.get(0);
+ first.setChain(chain);
+
+ head = first;
+ }
+
+ /**
+ * Starts this configurator, so that the MBeanServer is now able to accept incoming calls.
+ * @see #stop
+ * @see #isRunning
+ */
+ public void start()
+ {
+ if (!isRunning())
+ {
+ running = true;
+ }
+ }
+
+ /**
+ * Stops this configurator, so that the MBeanServer is not able to accept incoming calls.
+ * @see #start
+ */
+ public void stop()
+ {
+ if (isRunning())
+ {
+ running = false;
+ }
+ }
+
+ /**
+ * Returns whether this configurator is running and thus if the MBeanServer can accept incoming calls
+ * @see #start
+ */
+ public boolean isRunning()
+ {
+ return running;
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptorConfiguratorMBean.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptorConfiguratorMBean.java
new file mode 100644
index 0000000..6bb7c5d
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/MBeanServerInterceptorConfiguratorMBean.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+
+import javax.management.MBeanException;
+import javax.management.ObjectName;
+
+/**
+ * Management interface for the MBeanServerInterceptorConfigurator MBean.
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface MBeanServerInterceptorConfiguratorMBean
+{
+ /**
+ * Appends the given interceptor, provided by the client, to the existing interceptor chain.
+ * @see #registerInterceptor
+ */
+ public void addInterceptor(MBeanServerInterceptor interceptor);
+
+ /**
+ * Appends the given interceptor, provided by the client, to the existing interceptor chain and registers it as MBean.
+ * @see #addInterceptor
+ */
+ public void registerInterceptor(MBeanServerInterceptor interceptor, ObjectName name) throws MBeanException;
+
+ /**
+ * Removes all the interceptors added via {@link #addInterceptor(MBeanServerInterceptor interceptor)}.
+ * @see #addInterceptor
+ */
+ public void clearInterceptors();
+
+ /**
+ * Starts this configurator, so that the MBeanServer is now able to accept incoming calls.
+ * @see #stop
+ * @see #isRunning
+ */
+ public void start();
+
+ /**
+ * Stops this configurator, so that the MBeanServer is not able to accept incoming calls.
+ * @see #start
+ */
+ public void stop();
+
+ /**
+ * Returns whether this configurator is running and thus if the MBeanServer can accept incoming calls
+ * @see #start
+ */
+ public boolean isRunning();
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/NotificationListenerMBeanServerInterceptor.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/NotificationListenerMBeanServerInterceptor.java
new file mode 100644
index 0000000..da71b6c
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/NotificationListenerMBeanServerInterceptor.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData;
+
+/**
+ * Interceptor that takes care of replacing the source of Notifications to the
+ * ObjectName of the NotificationBroadcaster that emitted it.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class NotificationListenerMBeanServerInterceptor extends DefaultMBeanServerInterceptor
+{
+ private static class ListenerWrapper implements NotificationListener
+ {
+ private final NotificationListener listener;
+ private final ObjectName objectName;
+
+ private ListenerWrapper(NotificationListener listener, ObjectName name)
+ {
+ this.listener = listener;
+ this.objectName = name;
+ }
+
+ public void handleNotification(Notification notification, Object handback)
+ {
+ // The JMX spec does not specify how to change the source to be the ObjectName
+ // of the broadcaster. If we serialize the calls to the listeners, then it's
+ // possible to change the source and restore it back to the old value before
+ // calling the next listener; but if we want to support concurrent calls
+ // to the listeners, this is not possible. Here I chose to support concurrent
+ // calls so I change the value once and I never restore it.
+ Object src = notification.getSource();
+ if (!(src instanceof ObjectName))
+ {
+ // Change the source to be the ObjectName of the notification broadcaster
+ // if we are not already an ObjectName (compliant with RI behaviour)
+ notification.setSource(objectName);
+ }
+
+ // Notify the real listener
+ NotificationListener listener = getTargetListener();
+ listener.handleNotification(notification, handback);
+ }
+
+ private NotificationListener getTargetListener()
+ {
+ return listener;
+ }
+
+ public int hashCode()
+ {
+ return getTargetListener().hashCode();
+ }
+
+ public boolean equals(Object obj)
+ {
+ if (obj == null) return false;
+ if (obj == this) return true;
+
+ try
+ {
+ ListenerWrapper other = (ListenerWrapper)obj;
+ return getTargetListener().equals(other.getTargetListener());
+ }
+ catch (ClassCastException ignored)
+ {
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return getTargetListener().toString();
+ }
+ }
+
+ public String getType()
+ {
+ return "notificationlistener";
+ }
+
+ public void addNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ {
+ if (isEnabled())
+ {
+ ListenerWrapper wrapper = new ListenerWrapper(listener, metadata.name);
+ super.addNotificationListener(metadata, wrapper, filter, handback);
+ }
+ else
+ {
+ super.addNotificationListener(metadata, listener, filter, handback);
+ }
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener) throws ListenerNotFoundException
+ {
+ if (isEnabled())
+ {
+ ListenerWrapper wrapper = new ListenerWrapper(listener, metadata.name);
+ super.removeNotificationListener(metadata, wrapper);
+ }
+ else
+ {
+ super.removeNotificationListener(metadata, listener);
+ }
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException
+ {
+ if (isEnabled())
+ {
+ ListenerWrapper wrapper = new ListenerWrapper(listener, metadata.name);
+ super.removeNotificationListener(metadata, wrapper, filter, handback);
+ }
+ else
+ {
+ super.removeNotificationListener(metadata, listener, filter, handback);
+ }
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/SecurityMBeanServerInterceptor.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/SecurityMBeanServerInterceptor.java
new file mode 100644
index 0000000..814bc18
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/SecurityMBeanServerInterceptor.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.MBeanPermission;
+import javax.management.MBeanTrustPermission;
+
+import org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData;
+
+/**
+ * Interceptor that takes care of performing security checks (in case the SecurityManager is installed) for
+ * MBeanServer to MBean calls.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class SecurityMBeanServerInterceptor extends DefaultMBeanServerInterceptor implements SecurityMBeanServerInterceptorMBean
+{
+ public String getType()
+ {
+ return "security";
+ }
+
+ public boolean isEnabled()
+ {
+ return true;
+ }
+
+ public void addNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback)
+ {
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "addNotificationListener");
+ super.addNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener) throws ListenerNotFoundException
+ {
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "removeNotificationListener");
+ super.removeNotificationListener(metadata, listener);
+ }
+
+ public void removeNotificationListener(MBeanMetaData metadata, NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException
+ {
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "removeNotificationListener");
+ super.removeNotificationListener(metadata, listener, filter, handback);
+ }
+
+ public void instantiate(MBeanMetaData metadata, String className, String[] params, Object[] args) throws ReflectionException, MBeanException
+ {
+ checkPermission(className, null, metadata.name, "instantiate");
+ super.instantiate(metadata, className, params, args);
+ }
+
+ public MBeanInfo getMBeanInfo(MBeanMetaData metadata)
+ {
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "getMBeanInfo");
+ return super.getMBeanInfo(metadata);
+ }
+
+ public Object invoke(MBeanMetaData metadata, String method, String[] params, Object[] args) throws MBeanException, ReflectionException
+ {
+ checkPermission(metadata.info.getClassName(), method, metadata.name, "invoke");
+ return super.invoke(metadata, method, params, args);
+ }
+
+ public AttributeList getAttributes(MBeanMetaData metadata, String[] attributes)
+ {
+ Object[] secured = filterAttributes(metadata.info.getClassName(), metadata.name, attributes, true);
+ String[] array = new String[secured.length];
+ for (int i = 0; i < array.length; ++i) array[i] = (String)secured[i];
+ return super.getAttributes(metadata, array);
+ }
+
+ public AttributeList setAttributes(MBeanMetaData metadata, AttributeList attributes)
+ {
+ Object[] secured = filterAttributes(metadata.info.getClassName(), metadata.name, attributes.toArray(), false);
+ AttributeList list = new AttributeList();
+ for (int i = 0; i < secured.length; ++i) list.add(secured[i]);
+ return super.setAttributes(metadata, list);
+ }
+
+ public Object getAttribute(MBeanMetaData metadata, String attribute) throws MBeanException, AttributeNotFoundException, ReflectionException
+ {
+ checkPermission(metadata.info.getClassName(), attribute, metadata.name, "getAttribute");
+ return super.getAttribute(metadata, attribute);
+ }
+
+ public void setAttribute(MBeanMetaData metadata, Attribute attribute) throws MBeanException, AttributeNotFoundException, InvalidAttributeValueException, ReflectionException
+ {
+ checkPermission(metadata.info.getClassName(), attribute.getName(), metadata.name, "setAttribute");
+ super.setAttribute(metadata, attribute);
+ }
+
+ public void registration(MBeanMetaData metadata, int operation) throws MBeanRegistrationException
+ {
+ switch (operation)
+ {
+ case PRE_REGISTER:
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "registerMBean");
+ checkTrustRegistration(metadata.mbean.getClass());
+ break;
+ case POST_REGISTER_TRUE:
+ // The MBean can implement MBeanRegistration and change the ObjectName
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "registerMBean");
+ break;
+ case PRE_DEREGISTER:
+ checkPermission(metadata.info.getClassName(), null, metadata.name, "unregisterMBean");
+ break;
+ default:
+ break;
+ }
+ super.registration(metadata, operation);
+ }
+
+ private void checkPermission(String className, String methodName, ObjectName objectname, String action)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ sm.checkPermission(new MBeanPermission(className, methodName, objectname, action));
+ }
+ }
+
+ private void checkTrustRegistration(final Class cls)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ {
+ ProtectionDomain domain = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ return cls.getProtectionDomain();
+ }
+ });
+
+ MBeanTrustPermission permission = new MBeanTrustPermission("register");
+ if (!domain.implies(permission))
+ {
+ throw new AccessControlException("Access denied " + permission + ": MBean class " + cls.getName() + " is not trusted for registration");
+ }
+ }
+ }
+
+ private Object[] filterAttributes(String className, ObjectName objectName, Object[] attributes, boolean isGet)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm == null) return attributes;
+
+ ArrayList list = new ArrayList();
+
+ for (int i = 0; i < attributes.length; ++i)
+ {
+ Object attribute = attributes[i];
+ String name = isGet ? (String)attribute : ((Attribute)attribute).getName();
+
+ try
+ {
+ checkPermission(className, name, objectName, isGet ? "getAttribute" : "setAttribute");
+ list.add(attribute);
+ }
+ catch (SecurityException ignore)
+ {
+ // This is ok. We just don't add this attribute to the list
+ }
+ }
+
+ return list.toArray();
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/SecurityMBeanServerInterceptorMBean.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/SecurityMBeanServerInterceptorMBean.java
new file mode 100644
index 0000000..5be5a6f
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/server/interceptor/SecurityMBeanServerInterceptorMBean.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.server.interceptor;
+
+
+/**
+ * Management interface for the SecurityMBeanServerInterceptor MBean
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface SecurityMBeanServerInterceptorMBean
+{
+ /**
+ * Returns the type of this interceptor
+ */
+ public String getType();
+
+ /**
+ * This interceptor is always enabled
+ */
+ public boolean isEnabled();
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/Base64Codec.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/Base64Codec.java
new file mode 100644
index 0000000..803d47c
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/Base64Codec.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.util;
+
+
+/**
+ * This class is copy/paste of Jakarta's Commons-Codec v1.1 <code>org.apache.commons.codec.binary.Base64</code>
+ * implementation.
+ * It is reproduced here because we don't want to require a new jar just to perform Base64 code/decoding.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class Base64Codec
+{
+ static final int CHUNK_SIZE = 76;
+ static final byte[] CHUNK_SEPARATOR = "\n".getBytes();
+
+ static final int BASELENGTH = 255;
+ static final int LOOKUPLENGTH = 64;
+ static final int TWENTYFOURBITGROUP = 24;
+ static final int EIGHTBIT = 8;
+ static final int SIXTEENBIT = 16;
+ static final int SIXBIT = 6;
+ static final int FOURBYTE = 4;
+ static final int SIGN = -128;
+ static final byte PAD = (byte)'=';
+
+ private static byte[] base64Alphabet = new byte[BASELENGTH];
+ private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+ static
+ {
+ for (int i = 0; i < BASELENGTH; i++)
+ {
+ base64Alphabet[i] = (byte)-1;
+ }
+ for (int i = 'Z'; i >= 'A'; i--)
+ {
+ base64Alphabet[i] = (byte)(i - 'A');
+ }
+ for (int i = 'z'; i >= 'a'; i--)
+ {
+ base64Alphabet[i] = (byte)(i - 'a' + 26);
+ }
+ for (int i = '9'; i >= '0'; i--)
+ {
+ base64Alphabet[i] = (byte)(i - '0' + 52);
+ }
+
+ base64Alphabet['+'] = 62;
+ base64Alphabet['/'] = 63;
+
+ for (int i = 0; i <= 25; i++)
+ {
+ lookUpBase64Alphabet[i] = (byte)('A' + i);
+ }
+
+ for (int i = 26, j = 0; i <= 51; i++, j++)
+ {
+ lookUpBase64Alphabet[i] = (byte)('a' + j);
+ }
+
+ for (int i = 52, j = 0; i <= 61; i++, j++)
+ {
+ lookUpBase64Alphabet[i] = (byte)('0' + j);
+ }
+
+ lookUpBase64Alphabet[62] = (byte)'+';
+ lookUpBase64Alphabet[63] = (byte)'/';
+ }
+
+ private Base64Codec()
+ {
+ }
+
+ public static boolean isArrayByteBase64(byte[] arrayOctect)
+ {
+ arrayOctect = discardWhitespace(arrayOctect);
+
+ int length = arrayOctect.length;
+ if (length == 0)
+ {
+ return true;
+ }
+ for (int i = 0; i < length; i++)
+ {
+ if (!isBase64(arrayOctect[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static byte[] encodeBase64(byte[] binaryData)
+ {
+ return (encodeBase64(binaryData, false));
+ }
+
+ public static byte[] decodeBase64(byte[] base64Data)
+ {
+ // RFC 2045 suggests line wrapping at (no more than) 76
+ // characters -- we may have embedded whitespace.
+ base64Data = discardWhitespace(base64Data);
+
+ // handle the edge case, so we don't have to worry about it later
+ if (base64Data.length == 0)
+ {
+ return new byte[0];
+ }
+
+ int numberQuadruple = base64Data.length / FOURBYTE;
+ byte decodedData[] = null;
+ byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+ // Throw away anything not in base64Data
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ {
+ // this sizes the output array properly - rlw
+ int lastData = base64Data.length;
+ // ignore the '=' padding
+ while (base64Data[lastData - 1] == PAD)
+ {
+ if (--lastData == 0)
+ {
+ return new byte[0];
+ }
+ }
+ decodedData = new byte[lastData - numberQuadruple];
+ }
+
+ for (int i = 0; i < numberQuadruple; i++)
+ {
+ dataIndex = i * 4;
+ marker0 = base64Data[dataIndex + 2];
+ marker1 = base64Data[dataIndex + 3];
+
+ b1 = base64Alphabet[base64Data[dataIndex]];
+ b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+ if (marker0 != PAD && marker1 != PAD)
+ {
+ //No PAD e.g 3cQl
+ b3 = base64Alphabet[marker0];
+ b4 = base64Alphabet[marker1];
+
+ decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ decodedData[encodedIndex + 2] = (byte)(b3 << 6 | b4);
+ }
+ else if (marker0 == PAD)
+ {
+ //Two PAD e.g. 3c[Pad][Pad]
+ decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
+ }
+ else if (marker1 == PAD)
+ {
+ //One PAD e.g. 3cQ[Pad]
+ b3 = base64Alphabet[marker0];
+
+ decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ }
+ encodedIndex += 3;
+ }
+ return decodedData;
+ }
+
+ private static byte[] encodeBase64Chunked(byte[] binaryData)
+ {
+ return (encodeBase64(binaryData, true));
+ }
+
+ private static boolean isBase64(byte octect)
+ {
+ if (octect == PAD)
+ {
+ return true;
+ }
+ else if (base64Alphabet[octect] == -1)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ private static byte[] encodeBase64(byte[] binaryData, boolean isChunked)
+ {
+ int lengthDataBits = binaryData.length * EIGHTBIT;
+ int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+ int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+ byte encodedData[] = null;
+ int encodedDataLength = 0;
+ int nbrChunks = 0;
+
+ if (fewerThan24bits != 0)
+ {
+ //data not divisible by 24 bit
+ encodedDataLength = (numberTriplets + 1) * 4;
+ }
+ else
+ {
+ // 16 or 8 bit
+ encodedDataLength = numberTriplets * 4;
+ }
+
+ // If the output is to be "chunked" into 76 character sections,
+ // for compliance with RFC 2045 MIME, then it is important to
+ // allow for extra length to account for the separator(s)
+ if (isChunked)
+ {
+
+ nbrChunks =
+ (CHUNK_SEPARATOR.length == 0
+ ? 0
+ : (int)Math.ceil((float)encodedDataLength / CHUNK_SIZE));
+ encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
+ }
+
+ encodedData = new byte[encodedDataLength];
+
+ byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ int i = 0;
+ int nextSeparatorIndex = CHUNK_SIZE;
+ int chunksSoFar = 0;
+
+ //log.debug("number of triplets = " + numberTriplets);
+ for (i = 0; i < numberTriplets; i++)
+ {
+ dataIndex = i * 3;
+ b1 = binaryData[dataIndex];
+ b2 = binaryData[dataIndex + 1];
+ b3 = binaryData[dataIndex + 2];
+
+ //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);
+
+ l = (byte)(b2 & 0x0f);
+ k = (byte)(b1 & 0x03);
+
+ byte val1 =
+ ((b1 & SIGN) == 0)
+ ? (byte)(b1 >> 2)
+ : (byte)((b1) >> 2 ^ 0xc0);
+ byte val2 =
+ ((b2 & SIGN) == 0)
+ ? (byte)(b2 >> 4)
+ : (byte)((b2) >> 4 ^ 0xf0);
+ byte val3 =
+ ((b3 & SIGN) == 0)
+ ? (byte)(b3 >> 6)
+ : (byte)((b3) >> 6 ^ 0xfc);
+
+ encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+ //log.debug( "val2 = " + val2 );
+ //log.debug( "k4 = " + (k<<4) );
+ //log.debug( "vak = " + (val2 | (k<<4)) );
+ encodedData[encodedIndex + 1] =
+ lookUpBase64Alphabet[val2 | (k << 4)];
+ encodedData[encodedIndex + 2] =
+ lookUpBase64Alphabet[(l << 2) | val3];
+ encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
+
+ encodedIndex += 4;
+
+ // If we are chunking, let's put a chunk separator down.
+ if (isChunked)
+ {
+ // this assumes that CHUNK_SIZE % 4 == 0
+ if (encodedIndex == nextSeparatorIndex)
+ {
+ System.arraycopy(
+ CHUNK_SEPARATOR,
+ 0,
+ encodedData,
+ encodedIndex,
+ CHUNK_SEPARATOR.length);
+ chunksSoFar++;
+ nextSeparatorIndex =
+ (CHUNK_SIZE * (chunksSoFar + 1))
+ + (chunksSoFar * CHUNK_SEPARATOR.length);
+ encodedIndex += CHUNK_SEPARATOR.length;
+ }
+ }
+ }
+
+ // form integral number of 6-bit groups
+ dataIndex = i * 3;
+
+ if (fewerThan24bits == EIGHTBIT)
+ {
+ b1 = binaryData[dataIndex];
+ k = (byte)(b1 & 0x03);
+ //log.debug("b1=" + b1);
+ //log.debug("b1<<2 = " + (b1>>2) );
+ byte val1 =
+ ((b1 & SIGN) == 0)
+ ? (byte)(b1 >> 2)
+ : (byte)((b1) >> 2 ^ 0xc0);
+ encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+ encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
+ encodedData[encodedIndex + 2] = PAD;
+ encodedData[encodedIndex + 3] = PAD;
+ }
+ else if (fewerThan24bits == SIXTEENBIT)
+ {
+
+ b1 = binaryData[dataIndex];
+ b2 = binaryData[dataIndex + 1];
+ l = (byte)(b2 & 0x0f);
+ k = (byte)(b1 & 0x03);
+
+ byte val1 =
+ ((b1 & SIGN) == 0)
+ ? (byte)(b1 >> 2)
+ : (byte)((b1) >> 2 ^ 0xc0);
+ byte val2 =
+ ((b2 & SIGN) == 0)
+ ? (byte)(b2 >> 4)
+ : (byte)((b2) >> 4 ^ 0xf0);
+
+ encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
+ encodedData[encodedIndex + 1] =
+ lookUpBase64Alphabet[val2 | (k << 4)];
+ encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
+ encodedData[encodedIndex + 3] = PAD;
+ }
+
+ if (isChunked)
+ {
+ // we also add a separator to the end of the final chunk.
+ if (chunksSoFar < nbrChunks)
+ {
+ System.arraycopy(
+ CHUNK_SEPARATOR,
+ 0,
+ encodedData,
+ encodedDataLength - CHUNK_SEPARATOR.length,
+ CHUNK_SEPARATOR.length);
+ }
+ }
+
+ return encodedData;
+ }
+
+ private static byte[] discardWhitespace(byte[] data)
+ {
+ byte groomedData[] = new byte[data.length];
+ int bytesCopied = 0;
+
+ for (int i = 0; i < data.length; i++)
+ {
+ switch (data[i])
+ {
+ case (byte)' ':
+ case (byte)'\n':
+ case (byte)'\r':
+ case (byte)'\t':
+ break;
+ default:
+ groomedData[bytesCopied++] = data[i];
+ }
+ }
+
+ byte packedData[] = new byte[bytesCopied];
+
+ System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+ return packedData;
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/MethodTernaryTree.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/MethodTernaryTree.java
new file mode 100644
index 0000000..06489a2
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/MethodTernaryTree.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.util;
+
+
+/**
+ * Specialized ternary tree for method metadata information. <p>
+ * In JMX methods are referred to with the method name and the String[] representing the signature.
+ * One can decide to cache method information using as key a concatenation of method name + signature,
+ * but the cost of concatenation is very high, while hashmap access is fast.
+ * Ternary trees avoid string concatenation, and result to be 10x faster than concatenation + hashmap.
+ * However, the signature of a standard TernaryTree would be <code>Object get(Object[] key)</code> and
+ * <code>void put(Object[] key, Object value)</code>. Unfortunately normalizing method name + signature
+ * into a single array is also very expensive. <br>
+ * This version leaves method name and signature separated to have the fastest access possible to
+ * method information.
+ * See <a href="http://www.ddj.com/documents/s=921/ddj9804a/9804a.htm">here</a> for further information
+ * on TernaryTrees.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class MethodTernaryTree
+{
+ private Node m_root;
+
+ /**
+ * Returns the method information given the method name and its signature.
+ * @see #put
+ */
+ public Object get(String methodName, String[] signature)
+ {
+ if (signature == null) {throw new IllegalArgumentException();}
+ return search(methodName, signature);
+ }
+
+ /**
+ * Inserts in this TernaryTree the given method information, using as key the method name and its signature
+ * @see #get
+ */
+ public void put(String methodName, String[] signature, Object information)
+ {
+ if (signature == null) {throw new IllegalArgumentException();}
+ m_root = insert(m_root, methodName, signature, signature.length, information);
+ }
+
+ private Object search(String methodName, String[] signature)
+ {
+ Node node = m_root;
+ int index = 0;
+ while (node != null)
+ {
+ Object key = index == 0 ? methodName : signature[index - 1];
+ if (key == null) {throw new IllegalArgumentException();}
+
+ int split = splitFunction(key);
+ if (split < node.splitValue)
+ {
+ node = node.left;
+ }
+ else if (split == node.splitValue)
+ {
+ if (index == signature.length)
+ {
+ // Two objects may return the same split, because the splitFunction is not perfect
+ // (ie does not always yield different values for different objects, eg hash functions)
+ if (node.keys == null) {return null;}
+ for (int i = 0; i < node.keys.length; ++i)
+ {
+ if (node.keys[i].equals(key))
+ {
+ return node.values[i];
+ }
+ }
+ return null;
+ }
+ else
+ {
+ ++index;
+ node = node.middle;
+ }
+ }
+ else
+ {
+ node = node.right;
+ }
+ }
+ return null;
+ }
+
+ private Node insert(Node node, String methodName, String[] signature, int length, Object value)
+ {
+ Object key = methodName;
+ if (key == null) {throw new IllegalArgumentException();}
+
+ int split = splitFunction(key);
+ if (node == null)
+ {
+ node = new Node();
+ node.splitValue = split;
+ }
+
+ if (split < node.splitValue)
+ {
+ node.left = insert(node.left, methodName, signature, length, value);
+ }
+ else if (split == node.splitValue)
+ {
+ // Two objects may return the same split, because the splitFunction is not perfect
+ // (ie does not always yield different values for different objects, eg hash functions)
+ if (length == 0)
+ {
+ if (node.keys == null)
+ {
+ node.keys = new Object[1];
+ node.values = new Object[1];
+ node.keys[0] = key;
+ node.values[0] = value;
+ }
+ else
+ {
+ // Loop to see if the key already exists
+ boolean found = false;
+ for (int i = 0; i < node.keys.length; ++i)
+ {
+ if (node.keys[i].equals(key))
+ {
+ // Already present, replace the value
+ node.keys[i] = key;
+ node.values[i] = value;
+ found = true;
+ break;
+ }
+ }
+ // Not present, add it
+ if (!found)
+ {
+ int len = node.keys.length;
+ Object[] olds = node.keys;
+ node.keys = new Object[len + 1];
+ System.arraycopy(olds, 0, node.keys, 0, len);
+ node.keys[len] = key;
+
+ olds = node.values;
+ node.values = new Object[len + 1];
+ System.arraycopy(olds, 0, node.values, 0, len);
+ node.values[len] = value;
+ }
+ }
+ }
+ else
+ {
+ node.middle = insert(node.middle, signature[signature.length - length], signature, length - 1, value);
+ }
+ }
+ else
+ {
+ node.right = insert(node.right, methodName, signature, length, value);
+ }
+
+ return node;
+ }
+
+ protected int splitFunction(Object obj)
+ {
+ return obj.hashCode();
+ }
+
+ private class Node
+ {
+ private int splitValue;
+ private Node right;
+ private Node middle;
+ private Node left;
+ private Object[] keys;
+ private Object[] values;
+ }
+}
diff --git a/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/Utils.java b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/Utils.java
new file mode 100644
index 0000000..359b75c
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.agent/src/main/java/org/apache/felix/mosgi/jmx/agent/mx4j/util/Utils.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) MX4J.
+ * All rights reserved.
+ *
+ * This software is distributed under the terms of the MX4J License version 1.0.
+ * See the terms of the MX4J License in the documentation provided with this software.
+ */
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.mosgi.jmx.agent.mx4j.util;
+
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+
+/**
+ * Several utility functions for the JMX implementation
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class Utils
+{
+ /**
+ * This methods load a class given the classloader and the name of the class, and work for
+ * extended names of primitive types. <p>
+ * If you try to do ClassLoader.loadClass("boolean") it barfs it cannot find the class,
+ * so this method cope with this problem.
+ */
+ public static Class loadClass(ClassLoader loader, String name) throws ClassNotFoundException
+ {
+ if (name == null) throw new ClassNotFoundException("null");
+
+ name = name.trim();
+ if (name.equals("boolean")) return boolean.class;
+ else if (name.equals("byte")) return byte.class;
+ else if (name.equals("char")) return char.class;
+ else if (name.equals("short")) return short.class;
+ else if (name.equals("int")) return int.class;
+ else if (name.equals("long")) return long.class;
+ else if (name.equals("float")) return float.class;
+ else if (name.equals("double")) return double.class;
+ else if (name.equals("java.lang.String")) return String.class;
+ else if (name.equals("java.lang.Object")) return Object.class;
+ else if (name.startsWith("["))
+ {
+ // It's an array, figure out how many dimensions
+ int dimension = 0;
+ while (name.charAt(dimension) == '[')
+ {
+ ++dimension;
+ }
+ char type = name.charAt(dimension);
+ Class cls = null;
+ switch (type)
+ {
+ case 'Z':
+ cls = boolean.class;
+ break;
+ case 'B':
+ cls = byte.class;
+ break;
+ case 'C':
+ cls = char.class;
+ break;
+ case 'S':
+ cls = short.class;
+ break;
+ case 'I':
+ cls = int.class;
+ break;
+ case 'J':
+ cls = long.class;
+ break;
+ case 'F':
+ cls = float.class;
+ break;
+ case 'D':
+ cls = double.class;
+ break;
+ case 'L':
+ // Strip the semicolon at the end
+ String n = name.substring(dimension + 1, name.length() - 1);
+ cls = loadClass(loader, n);
+ break;
+ }
+
+ if (cls == null)
+ {
+ throw new ClassNotFoundException(name);
+ }
+ else
+ {
+ int[] dim = new int[dimension];
+ return Array.newInstance(cls, dim).getClass();
+ }
+ }
+ else
+ {
+ if (loader != null)
+ return loader.loadClass(name);
+ else
+ return Class.forName(name, false, null);
+ }
+ }
+
+ /**
+ * Returns the classes whose names are specified by the <code>names</code> argument, loaded with the
+ * specified classloader.
+ */
+ public static Class[] loadClasses(ClassLoader loader, String[] names) throws ClassNotFoundException
+ {
+ int n = names.length;
+ Class[] cls = new Class[n];
+ for (int i = 0; i < n; ++i)
+ {
+ String name = names[i];
+ cls[i] = loadClass(loader, name);
+ }
+ return cls;
+ }
+
+ /**
+ * Returns true is the given method is a JMX attribute getter method
+ */
+ public static boolean isAttributeGetter(Method m)
+ {
+ if (m == null) return false;
+
+ String name = m.getName();
+ Class retType = m.getReturnType();
+ Class[] params = m.getParameterTypes();
+ if (retType != Void.TYPE && params.length == 0)
+ {
+ if (name.startsWith("get") && name.length() > 3) return true;
+ else if (name.startsWith("is") && retType == Boolean.TYPE) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the method is a JMX attribute setter method
+ */
+ public static boolean isAttributeSetter(Method m)
+ {
+ if (m == null) return false;
+
+ String name = m.getName();
+ Class retType = m.getReturnType();
+ Class[] params = m.getParameterTypes();
+ if (retType == Void.TYPE && params.length == 1 && name.startsWith("set") && name.length() > 3)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean wildcardMatch(String pattern, String string)
+ {
+ int stringLength = string.length();
+ int stringIndex = 0;
+ for (int patternIndex = 0; patternIndex < pattern.length(); ++patternIndex)
+ {
+ char c = pattern.charAt(patternIndex);
+ if (c == '*')
+ {
+ // Recurse with the pattern without this '*' and the actual string, until
+ // match is found or we inspected the whole string
+ while (stringIndex < stringLength)
+ {
+ if (wildcardMatch(pattern.substring(patternIndex + 1), string.substring(stringIndex)))
+ {
+ return true;
+ }
+ // No match found, try a shorter string, since we are matching '*'
+ ++stringIndex;
+ }
+ }
+ else if (c == '?')
+ {
+ // Increment the string index, since '?' match a single char in the string
+ ++stringIndex;
+ if (stringIndex > stringLength)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // A normal character in the pattern, must match the one in the string
+ if (stringIndex >= stringLength || c != string.charAt(stringIndex))
+ {
+ return false;
+ }
+ ++stringIndex;
+ }
+ }
+
+ // I've inspected the whole pattern, but not the whole string
+ return stringIndex == stringLength;
+ }
+
+ public static boolean arrayEquals(Object[] arr1, Object[] arr2)
+ {
+ if (arr1 == null && arr2 == null) return true;
+ if (arr1 == null ^ arr2 == null) return false;
+ if (!arr1.getClass().equals(arr2.getClass())) return false;
+ if (arr1.length != arr2.length) return false;
+
+ for (int i = 0; i < arr1.length; ++i)
+ {
+ Object obj1 = arr1[i];
+ Object obj2 = arr2[i];
+ if (obj1 == null ^ obj2 == null) return false;
+ if (obj1 != null && !obj1.equals(obj2)) return false;
+ }
+ return true;
+ }
+
+ public static boolean arrayEquals(byte[] arr1, byte[] arr2)
+ {
+ if (arr1 == null && arr2 == null) return true;
+ if (arr1 == null ^ arr2 == null) return false;
+ if (!arr1.getClass().equals(arr2.getClass())) return false;
+ if (arr1.length != arr2.length) return false;
+
+ for (int i = 0; i < arr1.length; ++i)
+ {
+ byte b1 = arr1[i];
+ byte b2 = arr2[i];
+ if (b1 != b2) return false;
+ }
+ return true;
+ }
+
+ public static int arrayHashCode(Object[] arr)
+ {
+ int hash = 0;
+ if (arr != null)
+ {
+ // Avoid that 2 arrays of length 0 but different classes return same hash
+ hash ^= arr.getClass().hashCode();
+ for (int i = 0; i < arr.length; ++i)
+ {
+ hash ^= arr[i] == null ? 0 : arr[i].hashCode();
+ }
+ }
+ return hash;
+ }
+
+ public static int arrayHashCode(byte[] arr)
+ {
+ int hash = 0;
+ if (arr != null)
+ {
+ // Avoid that 2 arrays of length 0 but different classes return same hash
+ hash ^= arr.getClass().hashCode();
+ for (int i = 0; i < arr.length; ++i)
+ {
+ hash ^= arr[i];
+ }
+ }
+ return hash;
+ }
+
+ public static char[] arrayCopy(char[] chars)
+ {
+ if (chars == null) return null;
+ char[] copy = new char[chars.length];
+ System.arraycopy(chars, 0, copy, 0, chars.length);
+ return copy;
+ }
+}