git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@423854 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/pom.xml b/org.apache.felix.mosgi.jmx.rmiconnector/pom.xml
new file mode 100644
index 0000000..f1983d6
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/pom.xml
@@ -0,0 +1,75 @@
+<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 rmiconnector</name>
+  <artifactId>org.apache.felix.mosgi.jmx.rmiconnector</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.mosgi.jmx.agent</artifactId>
+      <version>${pom.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>${pom.groupId}</groupId>
+      <artifactId>org.apache.felix.mosgi.jmx.registry</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 RMI Connector</bundleName>
+            <bundleDescription>MOSGi JMX-MX4J RMI Connector</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>
+	      org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider.rmi;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.registry.mx4j.tools.naming;specification-version="1.0.0",
+	      org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider.rmi;specification-version="1.0.0",
+	      javax.management;specification-version="1.0.0",
+	      javax.management.remote;specification-version="1.0.0"
+	    </importPackage>
+          </osgiManifest>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/RmiConnectorActivator.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/RmiConnectorActivator.java
new file mode 100644
index 0000000..694c109
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/RmiConnectorActivator.java
@@ -0,0 +1,200 @@
+/*
+ *   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.rmiconnector;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceEvent;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.framework.cache.BundleCache;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+import org.apache.felix.mosgi.jmx.registry.mx4j.tools.naming.NamingServiceIfc;  
+
+import java.net.InetAddress;
+
+public class RmiConnectorActivator implements BundleActivator, ServiceListener{
+  public static BundleContext bc;
+
+  private ObjectName connectorServerName=new ObjectName("RmiConnector:name=RMIConnector");
+
+  private JMXConnectorServer connectorServer;
+  private MBeanServer mbs;
+  private NamingServiceIfc nsi;
+  private ServiceReference mBeanServerSR, namingServiceIfcSR;
+
+  private String version=null;
+
+  public RmiConnectorActivator() throws javax.management.MalformedObjectNameException {}
+  
+  ////////////////////////////////////////////////////
+  //          BundleActivator                       //
+  ////////////////////////////////////////////////////
+  public void start(BundleContext bc) throws Exception{
+		this.version=(String)bc.getBundle().getHeaders().get(Constants.BUNDLE_VERSION);
+    RmiConnectorActivator.bc=bc;
+    this.mBeanServerSR=bc.getServiceReference(MBeanServer.class.getName());
+    this.namingServiceIfcSR=bc.getServiceReference(NamingServiceIfc.class.getName());
+    RmiConnectorActivator. bc.addServiceListener(this, "(|(objectClass="+NamingServiceIfc.class.getName()+")"+"(objectClass="+MBeanServer.class.getName()+"))");
+    if (this.mBeanServerSR!=null && this.namingServiceIfcSR!=null){
+      this.startRmiConnector();
+    } else {
+      if (this.mBeanServerSR == null){
+        RmiConnectorActivator.log(LogService.LOG_WARNING,"No JMX Agent found",null);
+      }else if (this.namingServiceIfcSR==null){
+        RmiConnectorActivator.log(LogService.LOG_WARNING,"No RMI Registry found", null);
+      }
+    }
+  }
+
+  public void stop(BundleContext bc) throws Exception {
+    this.stopRmiConnector();
+    RmiConnectorActivator.bc=null;
+  }
+
+  //////////////////////////////////////////////////////
+  //          ServiceListener                        //
+  //////////////////////////////////////////////////////
+  public void serviceChanged(ServiceEvent serviceevent) {
+    ServiceReference servicereference= serviceevent.getServiceReference();
+    String [] ast=(String[])(servicereference.getProperty("objectClass"));
+    String as=ast[0];
+    switch (serviceevent.getType()) {
+      case ServiceEvent.REGISTERED :
+        if (as.equals(NamingServiceIfc.class.getName())){
+          this.namingServiceIfcSR=servicereference;
+        }else if (as.equals(MBeanServer.class.getName())){
+          this.mBeanServerSR=servicereference;
+        }
+        if (this.namingServiceIfcSR!=null && this.mBeanServerSR!=null){
+          try{
+            this.startRmiConnector();
+          }catch (Exception e){
+            e.printStackTrace();
+          }
+        }
+        break;
+      case ServiceEvent.UNREGISTERING :
+        try{
+          this.stopRmiConnector();
+        }catch (Exception e){
+          e.printStackTrace();
+        }
+        break;
+    }
+  }
+
+  public static void log(int prio, String message, Throwable t){
+    if (RmiConnectorActivator.bc!=null){
+      ServiceReference logSR=RmiConnectorActivator.bc.getServiceReference(LogService.class.getName());
+      if (logSR!=null){
+        ((LogService)RmiConnectorActivator.bc.getService(logSR)).log(prio, message, t);
+      }else{
+        System.out.println("No Log Service");
+      }
+    }else{
+      System.out.println(RmiConnectorActivator.class.getName()+": No bundleContext");
+    }
+  }
+
+  private void startRmiConnector() throws Exception{
+    String profile=bc.getProperty(BundleCache.CACHE_PROFILE_PROP);
+    if (profile==null){
+      profile=System.getProperty(BundleCache.CACHE_PROFILE_PROP);
+		}
+    String rmiPort=bc.getProperty("insa.jmxconsole.rmiport."+profile);
+    if (rmiPort==null){
+      rmiPort="1099";
+    }
+    String url="service:jmx:rmi:///jndi/rmi://"+ InetAddress.getLocalHost().getHostAddress()+":"+rmiPort+"/"+profile;
+    RmiConnectorActivator.log(LogService.LOG_INFO, "insa.jmx.rmiconnector.url ==> "+url, null);
+    
+    RmiConnectorActivator.log(LogService.LOG_INFO, "Starting JMX Rmi Connector "+version,null);
+    this.mbs=(MBeanServer)bc.getService(this.mBeanServerSR);
+    this.nsi=(NamingServiceIfc)bc.getService(this.namingServiceIfcSR);
+    JMXServiceURL address=new JMXServiceURL(url);
+/*
+    Map environment = new HashMap();
+    environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
+    environment.put(Context.PROVIDER_URL, "rmi://localhost:"+rmiPort);
+    environment.put(JMXConnectorServerFactory.PROTOCOL_PROVIDER_CLASS_LOADER,this.getClass().getClassLoader());
+  */
+
+/* Loggin 
+Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+java.util.logging.ConsoleHandler ch=new java.util.logging.ConsoleHandler();
+ch.setLevel(java.util.logging.Level.FINEST);
+java.util.logging.Logger.getLogger("javax.management.remote.misc").setLevel(java.util.logging.Level.FINEST);
+java.util.logging.Logger.getLogger("javax.management.remote.rmi").setLevel(java.util.logging.Level.FINEST);
+java.util.logging.Logger.getLogger("javax.management.remote.rmi").addHandler(ch);
+java.util.logging.Logger.getLogger("javax.management.remote.misc").addHandler(ch);
+*/
+
+		/*
+		java.util.Map env = new java.util.HashMap();
+		env.put(JMXConnectorServerFactory.PROTOCOL_PROVIDER_CLASS_LOADER, this.getClass().getClassLoader());
+		env.put("jmx.remote.protocol.provider.pkgs", "mx4j.remote.provider");
+		*/
+
+    this.connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, null, this.mbs);
+
+    RmiConnectorActivator.log(LogService.LOG_DEBUG, "===> "+this.connectorServer, null);
+    RmiConnectorActivator.log(LogService.LOG_DEBUG, "======> "+this.connectorServer.getMBeanServer(), null);
+
+//    this.mbs.registerMBean(this.connectorServer, this.connectorServerName);
+//    this.connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(address, null, java.lang.management.ManagementFactory.getPlatformMBeanServer());
+    this.connectorServer.start();
+
+    RmiConnectorActivator.log(LogService.LOG_INFO, "JMX Rmi Connector started "+version,null);
+  }
+
+  private void stopRmiConnector() throws Exception {
+    RmiConnectorActivator.log(LogService.LOG_INFO, "Stopping JMX Rmi connector "+version,null);
+    if (this.connectorServer!=null){
+      this.connectorServer.stop();
+      this.connectorServer=null;
+    }
+
+    if (this.mbs!=null){
+//SFR   this.mbs.unregisterMBean(this.connectorServerName);
+      this.mbs=null;
+    }
+
+    this.nsi=null;
+
+    if (this.mBeanServerSR!=null){
+      RmiConnectorActivator.bc.ungetService(this.mBeanServerSR);
+      this.mBeanServerSR=null;
+    }
+    if (this.namingServiceIfcSR!=null){
+      RmiConnectorActivator.bc.ungetService(this.namingServiceIfcSR);
+      this.namingServiceIfcSR=null;
+    }
+    this.connectorServerName=null;
+    RmiConnectorActivator.log(LogService.LOG_INFO, "Rmi Connector stopped "+version,null);
+  }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/AbstractHeartBeat.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/AbstractHeartBeat.java
new file mode 100644
index 0000000..ff5edd3
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/AbstractHeartBeat.java
@@ -0,0 +1,118 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public abstract class AbstractHeartBeat implements HeartBeat, Runnable
+{
+   private final ConnectionNotificationEmitter emitter;
+   private long pulsePeriod;
+   private int maxRetries;
+   private Thread thread;
+   private volatile boolean stopped;
+
+   protected AbstractHeartBeat(ConnectionNotificationEmitter emitter, Map environment)
+   {
+      this.emitter = emitter;
+      if (environment != null)
+      {
+         try
+         {
+            pulsePeriod = ((Long)environment.get(MX4JRemoteConstants.CONNECTION_HEARTBEAT_PERIOD)).longValue();
+         }
+         catch (Exception ignored)
+         {
+         }
+         try
+         {
+            maxRetries = ((Integer)environment.get(MX4JRemoteConstants.CONNECTION_HEARTBEAT_RETRIES)).intValue();
+         }
+         catch (Exception ignored)
+         {
+         }
+      }
+      if (pulsePeriod <= 0) pulsePeriod = 5000;
+      if (maxRetries <= 0) maxRetries = 3;
+   }
+
+   protected abstract void pulse() throws IOException;
+
+   public void start() throws IOException
+   {
+      thread = new Thread(this, "Connection HeartBeat");
+      thread.setDaemon(true);
+      thread.start();
+   }
+
+   public void stop() throws IOException
+   {
+      if (stopped) return;
+      stopped = true;
+      thread.interrupt();
+   }
+
+   public void run()
+   {
+      int retries = 0;
+      while (!stopped && !thread.isInterrupted())
+      {
+         try
+         {
+            Thread.sleep(pulsePeriod);
+
+            try
+            {
+               pulse();
+               retries = 0;
+            }
+            catch (IOException x)
+            {
+               if (retries++ == maxRetries)
+               {
+                  // The connection has died
+                  sendConnectionNotificationFailed();
+                  // And go on
+               }
+            }
+         }
+         catch (InterruptedException x)
+         {
+            Thread.currentThread().interrupt();
+            return;
+         }
+      }
+   }
+
+   protected void sendConnectionNotificationFailed()
+   {
+      emitter.sendConnectionNotificationFailed();
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ClientProxy.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ClientProxy.java
new file mode 100644
index 0000000..08a8887
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ClientProxy.java
@@ -0,0 +1,56 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.management.MBeanServerConnection;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ClientProxy implements InvocationHandler
+{
+   private final MBeanServerConnection target;
+
+   protected ClientProxy(MBeanServerConnection target)
+   {
+      this.target = target;
+   }
+
+   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+   {
+      try
+      {
+         return method.invoke(target, args);
+      }
+      catch (InvocationTargetException x)
+      {
+         throw x.getTargetException();
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ConnectionNotificationEmitter.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ConnectionNotificationEmitter.java
new file mode 100644
index 0000000..ced5560
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ConnectionNotificationEmitter.java
@@ -0,0 +1,92 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.io.IOException;
+
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.rmi.RMIConnector;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ConnectionNotificationEmitter extends NotificationBroadcasterSupport
+{
+   private static long sequenceNumber;
+
+   private JMXConnector connector;
+
+   public ConnectionNotificationEmitter(JMXConnector connector)
+   {
+      this.connector = connector;
+   }
+
+   private long getNextNotificationNumber()
+   {
+      synchronized (RMIConnector.class)
+      {
+         return sequenceNumber++;
+      }
+   }
+
+   private String getConnectionId()
+   {
+      try
+      {
+         return connector.getConnectionId();
+      }
+      catch (IOException x)
+      {
+         return null;
+      }
+   }
+
+   public void sendConnectionNotificationOpened()
+   {
+      JMXConnectionNotification notification = new JMXConnectionNotification(JMXConnectionNotification.OPENED, connector, getConnectionId(), getNextNotificationNumber(), "Connection opened", null);
+      sendNotification(notification);
+   }
+
+   public void sendConnectionNotificationClosed()
+   {
+      JMXConnectionNotification notification = new JMXConnectionNotification(JMXConnectionNotification.CLOSED, connector, getConnectionId(), getNextNotificationNumber(), "Connection closed", null);
+      sendNotification(notification);
+   }
+
+   public void sendConnectionNotificationFailed()
+   {
+      JMXConnectionNotification notification = new JMXConnectionNotification(JMXConnectionNotification.FAILED, connector, getConnectionId(), getNextNotificationNumber(), "Connection failed", null);
+      sendNotification(notification);
+   }
+
+   public void sendConnectionNotificationLost(long howMany)
+   {
+      JMXConnectionNotification notification = new JMXConnectionNotification(JMXConnectionNotification.NOTIFS_LOST, connector, getConnectionId(), getNextNotificationNumber(), "Some notification (" + howMany + ") was lost", null);
+      sendNotification(notification);
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ConnectionResolver.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ConnectionResolver.java
new file mode 100644
index 0000000..6d73a80
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ConnectionResolver.java
@@ -0,0 +1,149 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.management.remote.JMXServiceURL;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.MX4JRemoteConstants;
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ProviderHelper;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+/**
+ * ConnectionResolver handles the details of creating connections for different protocols.
+ * Subclasses for the specific protocol are found using a mechanism very similar to the
+ * one specified by {@link javax.management.remote.JMXConnectorFactory}. Here a subclass
+ * has a fully qualified name specified like this:
+ * <package>.resolver.<protocol>.<PROTOCOL>Resolver, for example
+ * {@link mx4j.remote.resolver.rmi.RMIResolver}
+ * This class is used from both the client and the server.
+ * The former uses it to lookup stubs or connections to the server side; the latter uses it
+ * to create server instances and make them availale to clients, for example via JNDI.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public abstract class ConnectionResolver extends ProviderHelper
+{
+   /**
+    * Returns a subclass of ConnectionResolver for the specified protocol.
+    */
+   public static ConnectionResolver getInstance(String proto) {
+      String protocol = normalizeProtocol(proto);
+      String resolverPackages = findResolverPackageList();
+//sfr      return loadResolver(resolverPackages, protocol, Thread.currentThread().getContextClassLoader());
+      return loadResolver(resolverPackages, protocol, ConnectionResolver.class.getClassLoader());
+   }
+
+   private static String findResolverPackageList() {
+      String packages = findSystemPackageList(MX4JRemoteConstants.PROTOCOL_RESOLVER_PACKAGES);
+      if (packages == null)
+         packages = MX4JRemoteConstants.RESOLVER_PACKAGES;
+      else
+         packages += MX4JRemoteConstants.RESOLVER_PACKAGES_SEPARATOR + MX4JRemoteConstants.RESOLVER_PACKAGES;
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Resolver packages list is: " + packages, null);
+      return packages;
+   }
+
+   private static ConnectionResolver loadResolver(String packages, String protocol, ClassLoader loader) {
+      StringTokenizer tokenizer = new StringTokenizer(packages, MX4JRemoteConstants.RESOLVER_PACKAGES_SEPARATOR);
+      while (tokenizer.hasMoreTokens()) {
+         String pkg = tokenizer.nextToken().trim();
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Resolver package: " + pkg,null);
+         if (pkg.length() == 0) continue;
+
+         String className = protocol.toUpperCase() + MX4JRemoteConstants.RESOLVER_CLASS;
+         String resolverClassName = constructClassName(pkg, protocol, className);
+
+         Class resolverClass = null;
+         try
+         {
+            resolverClass = loadClass(resolverClassName, loader);
+         }
+         catch (ClassNotFoundException x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_DEBUG,"Resolver class " + resolverClassName + " not found, continuing with next package",null);
+            continue;
+         }
+         catch (Exception x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_WARNING, "Cannot load resolver class " + resolverClassName, x);
+            return null;
+         }
+
+         try
+         {
+            return (ConnectionResolver)resolverClass.newInstance();
+         }
+         catch (Exception x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_WARNING,"Cannot instantiate resolver class " + resolverClassName, x);
+            return null;
+         }
+      }
+
+      // Nothing found
+       RmiConnectorActivator.log(LogService.LOG_DEBUG,"Could not find resolver for protocol " + protocol + " in package list '" + packages + "'", null);
+      return null;
+   }
+
+   /**
+    * Looks up a connection to the server side as specified in the given JMXServiceURL.
+    * This method is used by {@link javax.management.remote.JMXConnector}s.
+    */
+   public abstract Object lookupClient(JMXServiceURL url, Map environment) throws IOException;
+
+   /**
+    * Connects the client returned by {@link #lookupClient} to the server side
+    */
+   public abstract Object bindClient(Object client, Map environment) throws IOException;
+
+   /**
+    * Creates an instance of the server as specified in the given JMXServiceURL.
+    * It is only a factory method, it should just return a fresh instance of the server;
+    * other methods are responsible to make it available to clients (for example exporting it).
+    * This method is used by {@link javax.management.remote.JMXConnectorServer}s.
+    * @see #bindServer
+    */
+   public abstract Object createServer(JMXServiceURL url, Map environment) throws IOException;
+
+   /**
+    * Binds the server created by {@link #createServer} to a place specified in the JMXServiceURL.
+    * @return a new JMXServiceURL that specifies where the server has been bound to.
+    * @see #unbindServer
+    */
+   public abstract JMXServiceURL bindServer(Object server, JMXServiceURL url, Map environment) throws IOException;
+
+   /**
+    * Unbinds the server created by {@link #createServer} from the place specified in the JMXServiceURL.
+    * @see #bindServer
+    */
+   public abstract void unbindServer(Object server, JMXServiceURL address, Map environment) throws IOException;
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/DefaultRemoteNotificationServerHandler.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/DefaultRemoteNotificationServerHandler.java
new file mode 100644
index 0000000..2c7943c
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/DefaultRemoteNotificationServerHandler.java
@@ -0,0 +1,339 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.NotificationFilter;
+import javax.management.remote.NotificationResult;
+import javax.management.remote.TargetedNotification;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class DefaultRemoteNotificationServerHandler implements RemoteNotificationServerHandler
+{
+   private static int listenerID;
+
+   private final NotificationListener listener;
+   private final Map tuples = new HashMap();
+   private final NotificationBuffer buffer;
+
+   public DefaultRemoteNotificationServerHandler(Map environment)
+   {
+      listener = new ServerListener();
+      buffer = new NotificationBuffer(environment);
+   }
+
+   public Integer generateListenerID(ObjectName name, NotificationFilter filter)
+   {
+      synchronized (DefaultRemoteNotificationServerHandler.class)
+      {
+         return new Integer(++listenerID);
+      }
+   }
+
+   public NotificationListener getServerNotificationListener()
+   {
+      return listener;
+   }
+
+   public void addNotificationListener(Integer id, NotificationTuple tuple)
+   {
+      synchronized (tuples)
+      {
+         tuples.put(id, tuple);
+      }
+   }
+
+   public void removeNotificationListener(Integer id)
+   {
+      synchronized (tuples)
+      {
+         tuples.remove(id);
+      }
+   }
+
+   public NotificationTuple getNotificationListener(Integer id)
+   {
+      synchronized (tuples)
+      {
+         return (NotificationTuple)tuples.get(id);
+      }
+   }
+
+   public NotificationResult fetchNotifications(long sequenceNumber, int maxNotifications, long timeout)
+   {
+      return buffer.getNotifications(sequenceNumber, maxNotifications, timeout);
+   }
+
+   /**
+    * Called when there are no notifications to send to the client.
+    * It is guaranteed that no notification can be added before this method waits on the given lock.
+    * It should wait on the given lock for the specified timeout, and return true
+    * to send notifications (if no notifications arrived, an empty notification array
+    * will be returned to the client), or false if no notifications should be sent to
+    * the client.
+    * @param lock The object on which {@link #wait} should be called
+    * @param timeout The amount of time to wait (guaranteed to be strictly greater than 0)
+    */
+   protected boolean waitForNotifications(Object lock, long timeout)
+   {
+      synchronized (lock)
+      {
+         try
+         {
+            lock.wait(timeout);
+         }
+         catch (InterruptedException x)
+         {
+            Thread.currentThread().interrupt();
+         }
+      }
+      return true;
+   }
+
+   /**
+    * This method filters the given notification array and returns a possibly smaller array containing
+    * only notifications that passed successfully the filtering.
+    * Default behavior is no filtering, but subclasses may choose to change this bahavior.
+    * For example, for RMI, one can assure that all notifications are truly serializable, and log those
+    * that are not.
+    */
+   protected TargetedNotification[] filterNotifications(TargetedNotification[] notifications)
+   {
+      return notifications;
+   }
+
+   private void addNotification(Integer id, Notification notification)
+   {
+      buffer.add(new TargetedNotification(notification, id));
+   }
+
+   public class ServerListener implements NotificationListener
+   {
+      public void handleNotification(Notification notification, Object handback)
+      {
+         Integer id = (Integer)handback;
+         addNotification(id, notification);
+      }
+   }
+
+   public class NotificationBuffer
+   {
+      private final List buffer = new LinkedList();
+      private int maxCapacity;
+      private int purgeDistance;
+      private long firstSequence;
+      private long lastSequence;
+      private long lowestExpectedSequence;
+
+      public NotificationBuffer(Map environment)
+      {
+         if (environment != null)
+         {
+            try
+            {
+               maxCapacity = ((Integer)environment.get(MX4JRemoteConstants.NOTIFICATION_BUFFER_CAPACITY)).intValue();
+            }
+            catch (Exception ignored)
+            {
+            }
+
+            try
+            {
+               purgeDistance = ((Integer)environment.get(MX4JRemoteConstants.NOTIFICATION_PURGE_DISTANCE)).intValue();
+            }
+            catch (Exception ignored)
+            {
+            }
+         }
+         if (maxCapacity <= 0) maxCapacity = 1024;
+         if (purgeDistance <= 0) purgeDistance = 128;
+      }
+
+      public int getSize()
+      {
+         synchronized (buffer)
+         {
+            return buffer.size();
+         }
+      }
+
+      public void add(TargetedNotification notification)
+      {
+         synchronized (buffer)
+         {
+            if (buffer.size() == maxCapacity)
+            {
+               RmiConnectorActivator.log(LogService.LOG_DEBUG, "Notification buffer full: " + this, null);
+               removeRange(0, 1);
+            }
+            buffer.add(notification);
+            ++lastSequence;
+            RmiConnectorActivator.log(LogService.LOG_DEBUG,"Notification added to buffer: " + this, null);
+            buffer.notifyAll();
+         }
+      }
+
+      private void removeRange(int start, int end)
+      {
+         synchronized (buffer)
+         {
+            buffer.subList(start, end).clear();
+            firstSequence += end - start;
+         }
+      }
+
+      private long getFirstSequenceNumber()
+      {
+         synchronized (buffer)
+         {
+            return firstSequence;
+         }
+      }
+
+      private long getLastSequenceNumber()
+      {
+         synchronized (buffer)
+         {
+            return lastSequence;
+         }
+      }
+
+      public NotificationResult getNotifications(long sequenceNumber, int maxNotifications, long timeout)
+      {
+         synchronized (buffer)
+         {
+            NotificationResult result = null;
+            int size = 0;
+            if (sequenceNumber < 0)
+            {
+               // We loose the notifications between addNotificationListener() and fetchNotifications(), but c'est la vie.
+               long sequence = getLastSequenceNumber();
+               size = new Long(sequence + 1).intValue();
+               result = new NotificationResult(getFirstSequenceNumber(), sequence, new TargetedNotification[0]);
+                RmiConnectorActivator.log(LogService.LOG_DEBUG,"First fetchNotification call: " + this + ", returning " + result, null);
+            }
+            else
+            {
+               int start = new Long(sequenceNumber - getFirstSequenceNumber()).intValue();
+
+               List sublist = null;
+               boolean send = false;
+               while (size == 0)
+               {
+                  int end = buffer.size();
+                  if (end - start > maxNotifications) end = start + maxNotifications;
+
+                  sublist = buffer.subList(start, end);
+                  size = sublist.size();
+
+                  if (send) break;
+
+                  if (size == 0)
+                  {
+                     if (timeout <= 0) break;
+                      RmiConnectorActivator.log(LogService.LOG_DEBUG,"No notifications to send, waiting " + timeout + " ms", null);
+
+                     // We wait for notifications to arrive. Since we release the lock on the buffer
+                     // other threads can modify it. To avoid ConcurrentModificationException we compute
+                     // again the sublist
+                     send = waitForNotifications(buffer, timeout);
+                  }
+               }
+
+               TargetedNotification[] notifications = (TargetedNotification[])sublist.toArray(new TargetedNotification[size]);
+               notifications = filterNotifications(notifications);
+               result = new NotificationResult(getFirstSequenceNumber(), sequenceNumber + size, notifications);
+                RmiConnectorActivator.log(LogService.LOG_DEBUG,"Non-first fetchNotification call: " + this + ", returning " + result, null);
+
+               purgeNotifications(sequenceNumber, size);
+               RmiConnectorActivator.log(LogService.LOG_DEBUG,"Purged Notifications: " + this, null);
+            }
+            return result;
+         }
+      }
+
+      private void purgeNotifications(long sequenceNumber, int size)
+      {
+         // Record the lowest expected sequence number sent by the client.
+         // New clients will always have an initial big sequence number
+         // (they're initialized with getLastSequenceNumber()), while old
+         // clients can have lesser sequence numbers.
+         // Here we record the lesser of these sequence numbers, that is the
+         // sequence number of the oldest notification any client may ever ask.
+         // This way we can purge old notifications that have already been
+         // delivered to clients.
+
+         // The worst case is when a client has a long interval between fetchNotifications()
+         // calls, and another client has a short interval. The lowestExpectedSequence will
+         // grow with the second client, until a purge happens, so the first client can
+         // loose notifications. By tuning appropriately the purgeDistance and the interval
+         // between fetchNotifications() calls, it should never happen.
+
+         synchronized (buffer)
+         {
+            if (sequenceNumber <= lowestExpectedSequence)
+            {
+               long lowest = Math.min(lowestExpectedSequence, sequenceNumber);
+
+               if (lowest - getFirstSequenceNumber() > purgeDistance)
+               {
+                  // Purge only half of the old notifications, for safety
+                  int purgeSize = purgeDistance >> 1;
+                  removeRange(0, purgeSize);
+               }
+
+               lowestExpectedSequence = sequenceNumber + size;
+            }
+         }
+      }
+
+      public String toString()
+      {
+         StringBuffer buffer = new StringBuffer("NotificationBuffer@");
+         buffer.append(Integer.toHexString(hashCode())).append("[");
+         buffer.append("first=").append(getFirstSequenceNumber()).append(", ");
+         buffer.append("last=").append(getLastSequenceNumber()).append(", ");
+         buffer.append("size=").append(getSize()).append(", ");
+         buffer.append("lowest expected=").append(lowestExpectedSequence).append(", ");
+         buffer.append("maxCapacity=").append(maxCapacity).append(", ");
+         buffer.append("purgeDistance=").append(purgeDistance).append("]");
+         return buffer.toString();
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/HeartBeat.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/HeartBeat.java
new file mode 100644
index 0000000..23f2a22
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/HeartBeat.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.rmiconnector.mx4j.remote;
+
+import java.io.IOException;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface HeartBeat
+{
+   public void start() throws IOException;
+   public void stop() throws IOException;
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/MX4JRemoteConstants.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/MX4JRemoteConstants.java
new file mode 100644
index 0000000..ae42d2b
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/MX4JRemoteConstants.java
@@ -0,0 +1,112 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface MX4JRemoteConstants
+{
+   /**
+    * A vertical bar '|' as mandated by the spec
+    */
+   public static final String PROVIDER_PACKAGES_SEPARATOR = "|";
+   /**
+    * MX4J provider packages list for JMXConnector and JMXConnectorServer factories
+    */
+   public static final String PROVIDER_PACKAGES = "org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider" + PROVIDER_PACKAGES_SEPARATOR + "org.apache.felix.mosgi.jmx.rmiconnector.mx4j.tools.remote.provider";
+   /**
+    * The string 'ClientProvider' as mandated by the spec
+    */
+   public static final String CLIENT_PROVIDER_CLASS = "ClientProvider";
+   /**
+    * The string 'ServerProvider' as mandated by the spec
+    */
+   public static final String SERVER_PROVIDER_CLASS = "ServerProvider";
+
+
+   /**
+    * The key that specifies resolver packages, very much like
+    * {@link javax.management.remote.JMXConnectorFactory#PROTOCOL_PROVIDER_PACKAGES}
+    */
+   public static final String PROTOCOL_RESOLVER_PACKAGES = "mx4j.remote.resolver.pkgs";
+   /**
+    * A vertical bar '|'
+    */
+   public static final String RESOLVER_PACKAGES_SEPARATOR = PROVIDER_PACKAGES_SEPARATOR;
+   /**
+    * MX4J provider packages list for {@link mx4j.remote.ConnectionResolver} subclasses
+    */
+   public static final String RESOLVER_PACKAGES = "org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.resolver" + RESOLVER_PACKAGES_SEPARATOR + "org.apache.felix.mosgi.jmx.rmiconnector.mx4j.tools.remote.resolver";
+   /**
+    * The string 'Resolver'
+    */
+   public static final String RESOLVER_CLASS = "Resolver";
+
+   /**
+    * The reference implementation uses this property to specify the notification fetch timeout (in ms).
+    * MX4J will use the same for compatibility. DO NOT CHANGE IT unless the reference implementation changes it.
+    */
+   public static final String FETCH_NOTIFICATIONS_TIMEOUT = "jmx.remote.x.client.fetch.timeout";
+   /**
+    * The reference implementation uses this property to specify the maximum number of notification to fetch.
+    * MX4J will use the same for compatibility. DO NOT CHANGE IT unless the reference implementation changes it.
+    */
+   public static final String FETCH_NOTIFICATIONS_MAX_NUMBER = "jmx.remote.x.client.max.notifications";
+   /**
+    * The reference implementation uses this property to specify the notification buffer size.
+    * MX4J will use the same for compatibility. DO NOT CHANGE IT unless the reference implementation changes it.
+    */
+   public static final String NOTIFICATION_BUFFER_CAPACITY = "jmx.remote.x.buffer.size";
+   /**
+    * MX4J's implementation uses this property to specify the distance between the lowest expected notification
+    * sequence number (sent by the client via fetchNotifications()) and the minimum sequence number of the
+    * notification buffer. When this difference is greater than the value of this property, old notifications
+    * are eliminated from the notification buffer
+    */
+   public static final String NOTIFICATION_PURGE_DISTANCE = "jmx.remote.x.notification.purge.distance";
+   /**
+    * MX4J's implementation uses this property to specify the amount of time (in ms) the client should sleep
+    * between notification fetches. A value of 0 means there will be no sleep (fetches will be done one
+    * after the other).
+    */
+   public static final String FETCH_NOTIFICATIONS_SLEEP = "jmx.remote.x.notification.fetch.sleep";
+
+   /**
+    * MX4J's implementation uses this property to specify the period (in ms) of the heartbeat pulse for
+    * {@link javax.management.remote.JMXConnector JMXConnectors} that use heartbeat to check if the
+    * connection with {@link javax.management.remote.JMXConnectorServer JMXConnectorServers} is still alive.
+    * @see #CONNECTION_HEARTBEAT_RETRIES
+    */
+   public static final String CONNECTION_HEARTBEAT_PERIOD = "jmx.remote.x.connection.heartbeat.period";
+   /**
+    * MX4J's implementation uses this property to specify the number of retries of heartbeat pulses before
+    * declaring the connection between a {@link javax.management.remote.JMXConnector JMXConnector} and a
+    * {@link javax.management.remote.JMXConnectorServer JMXConnectorServer} failed, at which a
+    * {@link javax.management.remote.JMXConnectionNotification notification failed} is emitted.
+    * @see #CONNECTION_HEARTBEAT_PERIOD
+    */
+   public static final String CONNECTION_HEARTBEAT_RETRIES = "jmx.remote.x.connection.heartbeat.retries";
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/MX4JRemoteUtils.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/MX4JRemoteUtils.java
new file mode 100644
index 0000000..396a307
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/MX4JRemoteUtils.java
@@ -0,0 +1,344 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.DomainCombiner;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import javax.security.auth.AuthPermission;
+import javax.security.auth.Policy;
+import javax.security.auth.Subject;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.remote.SubjectDelegationPermission;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.2 $
+ */
+public class MX4JRemoteUtils
+{
+   private static int connectionNumber;
+
+   /**
+    * Returns a copy of the given Map that does not contain non-serializable entries
+    */
+   public static Map removeNonSerializableEntries(Map map)
+   {
+      Map newMap = new HashMap(map.size());
+      for (Iterator i = map.entrySet().iterator(); i.hasNext();)
+      {
+         Map.Entry entry = (Map.Entry)i.next();
+         if (isSerializable(entry)) newMap.put(entry.getKey(), entry.getValue());
+      }
+      return newMap;
+   }
+
+   private static boolean isSerializable(Object object)
+   {
+      if (object instanceof Map.Entry) return isSerializable(((Map.Entry)object).getKey()) && isSerializable(((Map.Entry)object).getValue());
+      if (object == null) return true;
+      if (object instanceof String) return true;
+      if (object instanceof Number) return true;
+      if (!(object instanceof Serializable)) return false;
+
+      return isTrulySerializable(object);
+   }
+
+   public static boolean isTrulySerializable(Object object)
+   {
+      // Give up and serialize the object
+      try
+      {
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         ObjectOutputStream oos = new ObjectOutputStream(baos);
+         oos.writeObject(object);
+         oos.close();
+         return true;
+      }
+      catch (IOException ignored)
+      {
+      }
+      return false;
+   }
+
+   public static String createConnectionID(String protocol, String callerAddress, int callerPort, Subject subject)
+   {
+      // See JSR 160 specification at javax/management/remote/package-summary.html
+
+      StringBuffer buffer = new StringBuffer(protocol);
+      buffer.append(':');
+      if (callerAddress != null) buffer.append("//").append(callerAddress);
+      if (callerPort >= 0) buffer.append(':').append(callerPort);
+      buffer.append(' ');
+
+      if (subject != null)
+      {
+         Set principals = subject.getPrincipals();
+         for (Iterator i = principals.iterator(); i.hasNext();)
+         {
+            Principal principal = (Principal)i.next();
+            String name = principal.getName();
+            name = name.replace(' ', '_');
+            buffer.append(name);
+            if (i.hasNext()) buffer.append(';');
+         }
+      }
+      buffer.append(' ');
+
+      buffer.append("0x").append(Integer.toHexString(getNextConnectionNumber()));
+
+      return buffer.toString();
+   }
+
+   private static synchronized int getNextConnectionNumber()
+   {
+      return ++connectionNumber;
+   }
+
+   public static Object subjectInvoke(Subject subject, Subject delegate, AccessControlContext context, PrivilegedExceptionAction action) throws Exception
+   {
+      if (delegate != null)
+      {
+         if (subject == null) throw new SecurityException("There is no authenticated subject to delegate to");
+         checkSubjectDelegationPermission(delegate, getSubjectContext(subject, context));
+      }
+
+      if (subject == null)
+      {
+         if (context == null) return action.run();
+         try
+         {
+            return AccessController.doPrivileged(action, context);
+         }
+         catch (PrivilegedActionException x)
+         {
+            throw x.getException();
+         }
+      }
+
+      try
+      {
+         AccessControlContext subjectContext = getSubjectContext(subject, context);
+         return Subject.doAsPrivileged(subject, action, subjectContext);
+      }
+      catch (PrivilegedActionException x)
+      {
+         throw x.getException();
+      }
+   }
+
+   private static void checkSubjectDelegationPermission(final Subject delegate, AccessControlContext context) throws SecurityException
+   {
+      final SecurityManager sm = System.getSecurityManager();
+      if (sm != null)
+      {
+         AccessController.doPrivileged(new PrivilegedAction()
+         {
+            public Object run()
+            {
+               StringBuffer buffer = new StringBuffer();
+               Set principals = delegate.getPrincipals();
+               for (Iterator i = principals.iterator(); i.hasNext();)
+               {
+                  Principal principal = (Principal)i.next();
+                  buffer.setLength(0);
+                  String permission = buffer.append(principal.getClass().getName()).append(".").append(principal.getName()).toString();
+                  sm.checkPermission(new SubjectDelegationPermission(permission));
+               }
+               return null;
+            }
+         }, context);
+      }
+   }
+
+   /**
+    * Returns a suitable AccessControlContext that restricts access in a {@link Subject#doAsPrivileged} call
+    * based on the current JAAS authorization policy, and combined with the given context.
+    *
+    * This is needed because the server stack frames in a call to a JMXConnectorServer are,
+    * for example for RMI, like this:
+    * <pre>
+    * java.lang.Thread.run()
+    *   [rmi runtime classes]
+    *     javax.management.remote.rmi.RMIConnectionImpl
+    *       [mx4j JSR 160 implementation code]
+    *         javax.security.auth.Subject.doAsPrivileged()
+    *           [mx4j JSR 160 implementation code]
+    *             [mx4j JSR 3 implementation code]
+    * </pre>
+    * All protection domains in this stack frames have AllPermission, normally, and the Subject.doAsPrivileged()
+    * call stops the checks very early. <br>
+    *
+    * So we need a restricting context (created at the start() of the connector server), and furthermore we need
+    * to combine the restricting context with a "special" context that does not have the same location as the
+    * JSR 3 and 160 classes and implementation (in particular will have a null location). <br>
+    * The "injection" of this synthetic ProtectionDomain allows to give AllPermission to the JSR 3 and 160 classes
+    * and implementation, but still have the possibility to specify a JAAS policy with MBeanPermissions in this way:
+    * <pre>
+    * grant principal javax.management.remote.JMXPrincipal "mx4j"
+    * {
+    *    permission javax.management.MBeanPermission "*", "getAttribute";
+    * };
+    * </pre>
+    */
+   private static AccessControlContext getSubjectContext(final Subject subject, final AccessControlContext context)
+   {
+      final SecurityManager sm = System.getSecurityManager();
+      if (sm == null)
+      {
+         return context;
+      }
+      else
+      {
+         return (AccessControlContext)AccessController.doPrivileged(new PrivilegedAction()
+         {
+            public Object run()
+            {
+               InjectingDomainCombiner combiner = new InjectingDomainCombiner(subject);
+               AccessControlContext acc = new AccessControlContext(context, combiner);
+               AccessController.doPrivileged(new PrivilegedAction()
+               {
+                  public Object run()
+                  {
+                     // Check this permission, that is required anyway, to combine the domains
+                     sm.checkPermission(new AuthPermission("doAsPrivileged"));
+                     return null;
+                  }
+               }, acc);
+               ProtectionDomain[] combined = combiner.getCombinedDomains();
+               return new AccessControlContext(combined);
+            }
+         });
+      }
+   }
+
+   private static class InjectingDomainCombiner implements DomainCombiner
+   {
+      private static Constructor domainConstructor;
+
+      static
+      {
+         try
+         {
+            domainConstructor = ProtectionDomain.class.getConstructor(new Class[]{CodeSource.class, PermissionCollection.class, ClassLoader.class, Principal[].class});
+         }
+         catch (Exception x)
+         {
+         }
+      }
+
+      private ProtectionDomain domain;
+      private ProtectionDomain[] combined;
+
+      public InjectingDomainCombiner(Subject subject)
+      {
+         if (domainConstructor != null)
+         {
+            Principal[] principals = (Principal[])subject.getPrincipals().toArray(new Principal[0]);
+            try
+            {
+               domain = (ProtectionDomain)domainConstructor.newInstance(new Object[]{new CodeSource(null, (java.security.cert.Certificate[])null), null, null, principals});
+            }
+            catch (Exception x)
+            {
+            }
+         }
+
+         if (domain == null)
+         {
+            // This is done for JDK 1.3 compatibility.
+            domain = new SubjectProtectionDomain(new CodeSource(null, (java.security.cert.Certificate[])null), subject);
+         }
+      }
+
+      public ProtectionDomain[] combine(ProtectionDomain[] current, ProtectionDomain[] assigned)
+      {
+         int length = current.length;
+         ProtectionDomain[] result = null;
+         if (assigned == null || assigned.length == 0)
+         {
+            result = new ProtectionDomain[length + 1];
+            System.arraycopy(current, 0, result, 0, length);
+         }
+         else
+         {
+            result = new ProtectionDomain[length + assigned.length + 1];
+            System.arraycopy(current, 0, result, 0, length);
+            System.arraycopy(assigned, 0, result, length, assigned.length);
+         }
+         result[result.length - 1] = domain;
+         this.combined = result;
+         return result;
+      }
+
+      public ProtectionDomain[] getCombinedDomains()
+      {
+         return combined;
+      }
+
+      private static class SubjectProtectionDomain extends ProtectionDomain
+      {
+         private final Subject subject;
+
+         public SubjectProtectionDomain(CodeSource codesource, Subject subject)
+         {
+            super(codesource, null);
+            this.subject = subject;
+         }
+
+         public boolean implies(Permission permission)
+         {
+            Policy policy = (Policy)AccessController.doPrivileged(new PrivilegedAction()
+            {
+               public Object run()
+               {
+                  return Policy.getPolicy();
+               }
+            });
+            PermissionCollection permissions = policy.getPermissions(subject, getCodeSource());
+            return permissions.implies(permission);
+         }
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/NotificationTuple.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/NotificationTuple.java
new file mode 100644
index 0000000..2998dbe
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/NotificationTuple.java
@@ -0,0 +1,147 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import javax.management.NotificationFilter;
+import javax.management.Notification;
+import javax.management.ObjectName;
+import javax.management.NotificationListener;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class NotificationTuple
+{
+   private static final NotificationFilter NO_FILTER = new NotificationFilter()
+   {
+      public boolean isNotificationEnabled(Notification notification)
+      {
+         return true;
+      }
+
+      public String toString()
+      {
+         return "no filter";
+      }
+   };
+   private static final Object NO_HANDBACK = new Object()
+   {
+      public String toString()
+      {
+         return "no handback";
+      }
+   };
+
+   private final ObjectName observed;
+   private final NotificationListener listener;
+   private final NotificationFilter filter;
+   private final Object handback;
+   private boolean invokeFilter;
+
+   public NotificationTuple(ObjectName observed, NotificationListener listener)
+   {
+      this(observed, listener, NO_FILTER, NO_HANDBACK);
+   }
+
+   public NotificationTuple(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback)
+   {
+      this.observed = observed;
+      this.listener = listener;
+      this.filter = filter;
+      this.handback = handback;
+      this.invokeFilter = false;
+   }
+
+   public NotificationListener getNotificationListener()
+   {
+      return listener;
+   }
+
+   public Object getHandback()
+   {
+      if (handback == NO_HANDBACK) return null;
+      return handback;
+   }
+
+   public NotificationFilter getNotificationFilter()
+   {
+      if (filter == NO_FILTER) return null;
+      return filter;
+   }
+
+   public void setInvokeFilter(boolean invoke)
+   {
+      this.invokeFilter = invoke;
+   }
+
+   public boolean getInvokeFilter()
+   {
+      if (!invokeFilter) return false;
+      NotificationFilter filter = getNotificationFilter();
+      if (filter == null) return false;
+      return true;
+   }
+
+   public boolean equals(Object obj)
+   {
+      if (this == obj) return true;
+      if (!(obj instanceof NotificationTuple)) return false;
+
+      final NotificationTuple other = (NotificationTuple)obj;
+
+      if (!observed.equals(other.observed)) return false;
+      if (!listener.equals(other.listener)) return false;
+
+      // Special treatment for special filter
+      if (filter == NO_FILTER) return true;
+      if (other.filter == NO_FILTER) return true;
+
+      if (filter != null ? !filter.equals(other.filter) : other.filter != null) return false;
+      if (handback != null ? !handback.equals(other.handback) : other.handback != null) return false;
+
+      return true;
+   }
+
+   public int hashCode()
+   {
+      int result;
+      result = observed.hashCode();
+      result = 29 * result + listener.hashCode();
+      result = 29 * result + (filter != null ? filter.hashCode() : 0);
+      result = 29 * result + (handback != null ? handback.hashCode() : 0);
+      return result;
+   }
+
+   public String toString()
+   {
+      StringBuffer buffer = new StringBuffer("NotificationTuple [");
+      buffer.append(observed).append(", ");
+      buffer.append(listener).append(", ");
+      buffer.append(filter).append(", ");
+      buffer.append(handback).append("]");
+      return buffer.toString();
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ProviderFactory.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ProviderFactory.java
new file mode 100644
index 0000000..78cce6b
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ProviderFactory.java
@@ -0,0 +1,180 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorProvider;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXConnectorServerProvider;
+import javax.management.remote.JMXProviderException;
+import javax.management.remote.JMXServiceURL;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.MX4JRemoteConstants;
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ProviderHelper;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.2 $
+ */
+public class ProviderFactory extends ProviderHelper
+{
+   public static JMXConnectorProvider newJMXConnectorProvider(JMXServiceURL url, Map env) throws IOException
+   {
+      // Yes, throw NPE if url is null (spec compliant)
+      String protocol = normalizeProtocol(url.getProtocol());
+      String providerPackages = findProviderPackageList(env, JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES);
+      ClassLoader classLoader = findProviderClassLoader(env, JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER);
+      JMXConnectorProvider provider = (JMXConnectorProvider)loadProvider(providerPackages, protocol, MX4JRemoteConstants.CLIENT_PROVIDER_CLASS, classLoader);
+      return provider;
+   }
+
+   public static JMXConnectorServerProvider newJMXConnectorServerProvider(JMXServiceURL url, Map env) throws IOException
+   {
+      // Yes, throw NPE if url is null (spec compliant)
+      String protocol = normalizeProtocol(url.getProtocol());
+      String providerPackages = findProviderPackageList(env, JMXConnectorServerFactory.PROTOCOL_PROVIDER_PACKAGES);
+      ClassLoader classLoader = findProviderClassLoader(env, JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER);
+      JMXConnectorServerProvider provider = (JMXConnectorServerProvider)loadProvider(providerPackages, protocol, MX4JRemoteConstants.SERVER_PROVIDER_CLASS, classLoader);
+      return provider;
+   }
+
+   private static String findEnvironmentProviderPackageList(Map environment, String key) throws JMXProviderException
+   {
+      String providerPackages = null;
+      if (environment != null)
+      {
+         Object pkgs = environment.get(key);
+         RmiConnectorActivator.log(LogService.LOG_DEBUG, "Provider packages in the environment: " + pkgs, null);
+         if (pkgs != null && !(pkgs instanceof String)) throw new JMXProviderException("Provider package list must be a string");
+         providerPackages = (String)pkgs;
+      }
+      return providerPackages;
+   }
+
+   private static String findProviderPackageList(Map environment, final String providerPkgsKey) throws JMXProviderException
+   {
+      // 1. Look in the environment
+      // 2. Look for system property
+      // 3. Use implementation's provider
+
+      String providerPackages = findEnvironmentProviderPackageList(environment, providerPkgsKey);
+
+      if (providerPackages == null)
+      {
+         providerPackages = findSystemPackageList(providerPkgsKey);
+      }
+
+      if (providerPackages != null && providerPackages.trim().length() == 0) throw new JMXProviderException("Provider package list cannot be an empty string");
+
+      if (providerPackages == null)
+         providerPackages = MX4JRemoteConstants.PROVIDER_PACKAGES;
+      else
+         providerPackages += MX4JRemoteConstants.PROVIDER_PACKAGES_SEPARATOR + MX4JRemoteConstants.PROVIDER_PACKAGES;
+
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Provider packages list is: " + providerPackages,null);
+
+      return providerPackages;
+   }
+
+   private static ClassLoader findProviderClassLoader(Map environment, String providerLoaderKey)
+   {
+
+      ClassLoader classLoader = null;
+      if (environment != null)
+      {
+         Object loader = environment.get(providerLoaderKey);
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Provider classloader in the environment: " + loader, null);
+         if (loader != null && !(loader instanceof ClassLoader)) throw new IllegalArgumentException("Provider classloader is not a ClassLoader");
+         classLoader = (ClassLoader)loader;
+      }
+
+      if (classLoader == null)
+      {
+         //classLoader = Thread.currentThread().getContextClassLoader();
+         classLoader = ProviderFactory.class.getClassLoader();
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Provider classloader (was null) in the environment: " + classLoader, null);
+      }
+
+      // Add the classloader as required by the spec
+      environment.put(JMXConnectorFactory.PROTOCOL_PROVIDER_CLASS_LOADER, classLoader);
+      RmiConnectorActivator.log(LogService.LOG_WARNING,"Provider classloader added to the environment", null);
+
+      return classLoader;
+   }
+
+   private static Object loadProvider(String packages, String protocol, String className, ClassLoader loader) throws JMXProviderException, MalformedURLException
+   {
+      StringTokenizer tokenizer = new StringTokenizer(packages, MX4JRemoteConstants.PROVIDER_PACKAGES_SEPARATOR);
+      while (tokenizer.hasMoreTokens())
+      {
+         String pkg = tokenizer.nextToken().trim();
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Provider package: " + pkg, null);
+
+         // The spec states the package cannot be empty
+         if (pkg.length() == 0) throw new JMXProviderException("Empty package list not allowed: " + packages);
+
+         String providerClassName = constructClassName(pkg, protocol, className);
+
+         Class providerClass = null;
+         try
+         {
+            providerClass = loadClass(providerClassName, loader);
+         }
+         catch (ClassNotFoundException x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_DEBUG,"Provider class " + providerClassName + " not found, continuing with next package",null);
+            continue;
+         }
+         catch (Exception x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_WARNING,"Cannot load provider class " + providerClassName, x);
+            throw new JMXProviderException("Cannot load provider class " + providerClassName, x);
+         }
+
+         try
+         {
+            return providerClass.newInstance();
+         }
+         catch (Exception x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_WARNING,"Cannot instantiate provider class " + providerClassName, x);
+            throw new JMXProviderException("Cannot instantiate provider class " + providerClassName, x);
+         }
+      }
+
+      // Nothing found
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Could not find provider for protocol " + protocol + " in package list '" + packages + "'", null);
+      throw new MalformedURLException("Could not find provider for protocol " + protocol + " in package list '" + packages + "'");
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ProviderHelper.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ProviderHelper.java
new file mode 100644
index 0000000..ab27a96
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/ProviderHelper.java
@@ -0,0 +1,71 @@
+/*
+ *   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.rmiconnector.mx4j.remote;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+/**
+ *
+ * @version $Revision: 1.2 $
+ */
+public abstract class ProviderHelper
+{
+   protected static String normalizeProtocol(String protocol)
+   {
+      // Replace special chars as required by the spec
+      String normalized = protocol.replace('+', '.');
+      normalized = normalized.replace('-', '_');
+      RmiConnectorActivator.log(LogService.LOG_INFO, "Normalizing protocol: " + protocol + " --> " + normalized, null);
+      return normalized;
+   }
+
+   protected static String findSystemPackageList(final String key)
+   {
+      String providerPackages = (String)AccessController.doPrivileged(new PrivilegedAction()
+      {
+         public Object run()
+         {
+            return System.getProperty(key);
+         }
+      });
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Packages in the system property '" + key + "': " + providerPackages,null);
+      return providerPackages;
+   }
+
+   protected static Class loadClass(String className, ClassLoader loader) throws ClassNotFoundException
+   {
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Loading class: " + className +" From "+loader,null);
+      if (loader == null){
+         loader= ProviderHelper.class.getClassLoader();
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"a new loader "+loader,null);
+         
+         	//Thread.currentThread().getContextClassLoader();
+      }
+      return loader.loadClass(className);
+   }
+
+   protected static String constructClassName(String packageName, String protocol, String className)
+   {
+      return new StringBuffer(packageName).append(".").append(protocol).append(".").append(className).toString();
+   }
+
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/RemoteNotificationClientHandler.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/RemoteNotificationClientHandler.java
new file mode 100644
index 0000000..5e29b63
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/RemoteNotificationClientHandler.java
@@ -0,0 +1,400 @@
+
+/*
+ *   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.rmiconnector.mx4j.remote;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.NotificationFilter;
+import javax.management.remote.NotificationResult;
+import javax.management.remote.TargetedNotification;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+public class RemoteNotificationClientHandler {
+   private static int fetcherID;
+   private static int delivererID;
+
+   public interface NotificationHandler
+   {
+      public NotificationResult fetchNotifications(long sequenceNumber, int maxNumber, long timeout) throws IOException;
+      public void sendNotificationsLost(long howMany);
+   }
+
+   private final Map tuples = new HashMap();
+   private NotificationFetcherThread fetcherThread;
+   private NotificationDelivererThread delivererThread;
+
+   public RemoteNotificationClientHandler(NotificationHandler fetcher, Map environment)
+   {
+      this.fetcherThread = new NotificationFetcherThread(fetcher, environment);
+      this.delivererThread = new NotificationDelivererThread();
+   }
+
+   private boolean isActive()
+   {
+      return fetcherThread.isActive();
+   }
+
+   private void start()
+   {
+      delivererThread.start();
+      fetcherThread.start();
+   }
+
+   private void stop()
+   {
+      fetcherThread.stop();
+      delivererThread.stop();
+   }
+
+   private synchronized static int getFetcherID()
+   {
+      return ++fetcherID;
+   }
+
+   private synchronized static int getDelivererID()
+   {
+      return ++delivererID;
+   }
+
+   public boolean contains(NotificationTuple tuple)
+   {
+      synchronized (tuples)
+      {
+         return tuples.containsValue(tuple);
+      }
+   }
+
+   public void addNotificationListener(Integer id, NotificationTuple tuple)
+   {
+      if (!isActive()) start();
+
+      synchronized (tuples)
+      {
+         tuples.put(id, tuple);
+      }
+
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Adding remote NotificationListener " + tuple,null);
+   }
+
+   public Integer[] getNotificationListeners(NotificationTuple tuple)
+   {
+      synchronized (tuples)
+      {
+         ArrayList ids = new ArrayList();
+         for (Iterator i = tuples.entrySet().iterator(); i.hasNext();)
+         {
+            Map.Entry entry = (Map.Entry)i.next();
+            if (entry.getValue().equals(tuple)) ids.add(entry.getKey());
+         }
+         if (ids.size() > 0) return (Integer[])ids.toArray(new Integer[ids.size()]);
+      }
+      return null;
+   }
+
+   public Integer getNotificationListener(NotificationTuple tuple)
+   {
+      synchronized (tuples)
+      {
+         for (Iterator i = tuples.entrySet().iterator(); i.hasNext();)
+         {
+            Map.Entry entry = (Map.Entry)i.next();
+            if (entry.getValue().equals(tuple)) return (Integer)entry.getKey();
+         }
+      }
+      return null;
+   }
+
+   public void removeNotificationListeners(Integer[] ids)
+   {
+      NotificationTuple tuple = null;
+      boolean stop = false;
+      synchronized (tuples)
+      {
+         for (int i = 0; i < ids.length; ++i)
+         {
+            Integer id = ids[i];
+            tuple = (NotificationTuple)tuples.remove(id);
+         }
+         stop = tuples.size() == 0;
+      }
+      if (stop) stop();
+
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Removing remote NotificationListener " + tuple,null);
+   }
+
+   private void deliverNotifications(TargetedNotification[] notifications)
+   {
+      delivererThread.addNotifications(notifications);
+   }
+
+   private void sendNotification(TargetedNotification notification)
+   {
+      NotificationTuple tuple = null;
+      synchronized (tuples)
+      {
+         tuple = (NotificationTuple)tuples.get(notification.getListenerID());
+      }
+
+      // It may be possible that a notification arrived after the client already removed the listener
+      if (tuple == null) return;
+
+      Notification notif = notification.getNotification();
+
+
+      if (tuple.getInvokeFilter())
+      {
+         // Invoke the filter on client side
+         NotificationFilter filter = tuple.getNotificationFilter();
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Filtering notification " + notif + ", filter = " + filter,null);
+         if (filter != null)
+         {
+            try
+            {
+               boolean deliver = filter.isNotificationEnabled(notif);
+               if (!deliver) return;
+            }
+            catch (RuntimeException x)
+            {
+               RmiConnectorActivator.log(LogService.LOG_WARNING,"RuntimeException caught from isNotificationEnabled, filter = " + filter, x);
+               // And go on quietly
+            }
+         }
+      }
+
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Sending Notification " + notif + ", listener info is " + tuple,null);
+
+      NotificationListener listener = tuple.getNotificationListener();
+
+      try
+      {
+         listener.handleNotification(notif, tuple.getHandback());
+      }
+      catch (RuntimeException x)
+      {
+         RmiConnectorActivator.log(LogService.LOG_WARNING,"RuntimeException caught from handleNotification, listener = " + listener, x);
+         // And return quietly
+      }
+   }
+
+   private class NotificationFetcherThread implements Runnable {
+      private final NotificationHandler handler;
+      private long sequenceNumber;
+      private volatile boolean active;
+      private Thread thread;
+      private long timeout;
+      private int maxNumber;
+      private long sleep;
+
+      public NotificationFetcherThread(NotificationHandler fetcher, Map environment)
+      {
+         this.handler = fetcher;
+
+         // Default server timeout is one minute
+         timeout = 60 * 1000;
+         // At most 25 notifications at time
+         maxNumber = 25;
+         // By default we don't sleep and we call the server again.
+         sleep = 0;
+         if (environment != null)
+         {
+            try
+            {
+               timeout = ((Long)environment.get(MX4JRemoteConstants.FETCH_NOTIFICATIONS_TIMEOUT)).longValue();
+            }
+            catch (Exception ignored)
+            {
+            }
+            try
+            {
+               maxNumber = ((Integer)environment.get(MX4JRemoteConstants.FETCH_NOTIFICATIONS_MAX_NUMBER)).intValue();
+            }
+            catch (Exception ignored)
+            {
+            }
+            try
+            {
+               sleep = ((Integer)environment.get(MX4JRemoteConstants.FETCH_NOTIFICATIONS_SLEEP)).intValue();
+            }
+            catch (Exception ignored)
+            {
+            }
+         }
+      }
+
+      private synchronized long getSequenceNumber()
+      {
+         return sequenceNumber;
+      }
+
+      private synchronized void setSequenceNumber(long sequenceNumber)
+      {
+         this.sequenceNumber = sequenceNumber;
+      }
+
+      public boolean isActive()
+      {
+         return active;
+      }
+
+      public synchronized void start()
+      {
+         active = true;
+         // Initialized to a negative value for the first fetchNotification call
+         sequenceNumber = -1;
+         thread = new Thread(this, "Notification Fetcher #" + getFetcherID());
+         thread.setDaemon(true);
+         thread.start();
+      }
+
+      public synchronized void stop()
+      {
+         active = false;
+         thread.interrupt();
+      }
+
+      public void run()
+      {
+
+         while (isActive() && !thread.isInterrupted())
+         {
+            try
+            {
+               long sequence = getSequenceNumber();
+               NotificationResult result = handler.fetchNotifications(sequence, maxNumber, timeout);
+               RmiConnectorActivator.log(LogService.LOG_WARNING,"Fetched Notifications: " + result, null);
+
+               long sleepTime = sleep;
+               if (result != null)
+               {
+                  long nextSequence = result.getNextSequenceNumber();
+                  TargetedNotification[] targeted = result.getTargetedNotifications();
+                  int targetedLength = targeted == null ? 0 : targeted.length;
+                  boolean notifsFilteredByServer = nextSequence - sequence != targetedLength;
+                  boolean notifsLostByServer = sequence >= 0 && result.getEarliestSequenceNumber() > sequence;
+                  if (notifsFilteredByServer)
+                  {
+                     // We lost some notification
+                     handler.sendNotificationsLost(nextSequence - sequence - targetedLength);
+                  }
+                  if (notifsLostByServer)
+                  {
+                     // We lost some notification
+                     handler.sendNotificationsLost(result.getEarliestSequenceNumber() - sequence);
+                  }
+
+                  setSequenceNumber(nextSequence);
+                  deliverNotifications(targeted);
+
+                  // If we got a maxNumber of notifications, probably the server has more to send, don't sleep
+                  if (targeted != null && targeted.length == maxNumber) sleepTime = 0;
+               }
+
+               if (sleepTime > 0) Thread.sleep(sleepTime);
+            }
+            catch (IOException x)
+            {
+               RmiConnectorActivator.log(LogService.LOG_INFO,"Caught IOException from fetchNotifications", x);
+               // And try again
+            }
+            catch (InterruptedException x)
+            {
+               active = false;
+               Thread.currentThread().interrupt();
+               break;
+            }
+         }
+      }
+   }
+
+   private class NotificationDelivererThread implements Runnable
+   {
+      private final List notificationQueue = new LinkedList();
+      private volatile boolean active;
+      private Thread thread;
+
+      public void addNotifications(TargetedNotification[] notifications)
+      {
+         if (notifications == null || notifications.length == 0) return;
+
+         List notifs = Arrays.asList(notifications);
+
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Enqueuing notifications for delivery: " + notifs,null);
+
+         synchronized (notificationQueue)
+         {
+            notificationQueue.addAll(notifs);
+            notificationQueue.notifyAll();
+         }
+      }
+
+      public boolean isActive()
+      {
+         return active;
+      }
+
+      public synchronized void start()
+      {
+         active = true;
+         notificationQueue.clear();
+         thread = new Thread(this, "Notification Deliverer #" + getDelivererID());
+         thread.setDaemon(true);
+         thread.start();
+      }
+
+      public synchronized void stop()
+      {
+         active = false;
+         thread.interrupt();
+      }
+
+      public void run()
+      {
+         while (isActive() && !thread.isInterrupted())
+         {
+            try
+            {
+               TargetedNotification notification = null;
+               synchronized (notificationQueue)
+               {
+                  while (notificationQueue.isEmpty()) notificationQueue.wait();
+                  notification = (TargetedNotification)notificationQueue.remove(0);
+               }
+               sendNotification(notification);
+            }
+            catch (InterruptedException x)
+            {
+               active = false;
+               Thread.currentThread().interrupt();
+               break;
+            }
+         }
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/RemoteNotificationServerHandler.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/RemoteNotificationServerHandler.java
new file mode 100644
index 0000000..effff75
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/RemoteNotificationServerHandler.java
@@ -0,0 +1,48 @@
+/*
+ * 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.rmiconnector.mx4j.remote;
+
+import javax.management.ObjectName;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.remote.NotificationResult;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public interface RemoteNotificationServerHandler
+{
+   public Integer generateListenerID(ObjectName name, NotificationFilter filter);
+
+   public NotificationListener getServerNotificationListener();
+
+   public void addNotificationListener(Integer id, NotificationTuple tuple);
+
+   public void removeNotificationListener(Integer id);
+
+   public NotificationTuple getNotificationListener(Integer id);
+
+   public NotificationResult fetchNotifications(long sequenceNumber, int maxNotifications, long timeout);
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/RMIClientProvider.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/RMIClientProvider.java
new file mode 100644
index 0000000..ec2e800
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/RMIClientProvider.java
@@ -0,0 +1,44 @@
+/*
+ * 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.rmiconnector.mx4j.remote.provider;
+
+import java.util.Map;
+import java.io.IOException;
+
+import javax.management.remote.JMXConnectorProvider;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnector;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public abstract class RMIClientProvider implements JMXConnectorProvider
+{
+   public JMXConnector newJMXConnector(JMXServiceURL url, Map environment) throws IOException
+   {
+      return new RMIConnector(url, environment);
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/RMIServerProvider.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/RMIServerProvider.java
new file mode 100644
index 0000000..15d4d8a
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/RMIServerProvider.java
@@ -0,0 +1,46 @@
+/*
+ * 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.rmiconnector.mx4j.remote.provider;
+
+import java.util.Map;
+import java.io.IOException;
+
+import javax.management.remote.JMXConnectorServerProvider;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
+import javax.management.MBeanServer;
+
+/**
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public abstract class RMIServerProvider implements JMXConnectorServerProvider
+{
+   public JMXConnectorServer newJMXConnectorServer(JMXServiceURL url, Map environment, MBeanServer server) throws IOException
+   {
+      return new RMIConnectorServer(url, environment, server);
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/rmi/ServerProvider.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/rmi/ServerProvider.java
new file mode 100644
index 0000000..d1a20cb
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/provider/rmi/ServerProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.rmiconnector.mx4j.remote.provider.rmi;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider.RMIServerProvider;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ServerProvider extends RMIServerProvider
+{
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/resolver/rmi/RMIResolver.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/resolver/rmi/RMIResolver.java
new file mode 100644
index 0000000..cfcbbea
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/resolver/rmi/RMIResolver.java
@@ -0,0 +1,308 @@
+/*
+ * 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.rmiconnector.mx4j.remote.resolver.rmi;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ObjectInputStream;
+import java.net.MalformedURLException;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.Remote;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.rmi.RMIConnectorServer;
+import javax.management.remote.rmi.RMIJRMPServerImpl;
+import javax.management.remote.rmi.RMIServer;
+import javax.management.remote.rmi.RMIServerImpl;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.osgi.service.log.LogService;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ConnectionResolver;
+import org.apache.felix.mosgi.jmx.agent.mx4j.util.Base64Codec;
+
+/**
+ * Resolver for RMI/JRMP protocol.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RMIResolver extends ConnectionResolver
+{
+   private static final String JNDI_CONTEXT = "/jndi/";
+   private static final String STUB_CONTEXT = "/stub/";
+
+
+//********************************************************************************************************************//
+// CLIENT METHODS
+
+
+   public Object lookupClient(JMXServiceURL url, Map environment) throws IOException
+   {
+      return lookupRMIServerStub(url, environment);
+   }
+
+   public Object bindClient(Object client, Map environment) throws IOException
+   {
+      // JRMP does not need anything special
+      return client;
+   }
+
+   protected RMIServer lookupRMIServerStub(JMXServiceURL url, Map environment) throws IOException
+   {
+      String path = url.getURLPath();
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL for lookup is: '" + url + "'", null);
+
+      if (path != null)
+      {
+         if (path.startsWith(JNDI_CONTEXT))
+         {
+            return lookupStubInJNDI(url, environment);
+         }
+
+         return decodeStub(url, environment);
+      }
+
+      throw new MalformedURLException("Unsupported lookup " + url);
+   }
+
+   private RMIServer lookupStubInJNDI(JMXServiceURL url, Map environment) throws IOException
+   {
+
+      String path = url.getURLPath();
+      String name = path.substring(JNDI_CONTEXT.length());
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"Looking up RMI stub in JNDI under name " + name, null);
+
+      InitialContext ctx = null;
+      try
+      {
+         ctx = new InitialContext(new Hashtable(environment));
+         Object stub = ctx.lookup(name);
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Found RMI stub in JNDI " + stub, null);
+         return narrowRMIServerStub(stub);
+      }
+      catch (NamingException x)
+      {
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot lookup RMI stub in JNDI", x);
+         throw new IOException(x.toString());
+      }
+      finally
+      {
+         try
+         {
+            if (ctx != null) ctx.close();
+         }
+         catch (NamingException x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot close InitialContext", x);
+         }
+      }
+   }
+
+   protected RMIServer narrowRMIServerStub(Object stub)
+   {
+      return (RMIServer)stub;
+   }
+
+   protected RMIServer decodeStub(JMXServiceURL url, Map environment) throws IOException
+   {
+      String path = url.getURLPath();
+      if (path.startsWith(STUB_CONTEXT))
+      {
+         byte[] encoded = path.substring(STUB_CONTEXT.length()).getBytes();
+         if (!Base64Codec.isArrayByteBase64(encoded)) throw new IOException("Encoded stub form is not a valid Base64 sequence: " + url);
+         byte[] decoded = Base64Codec.decodeBase64(encoded);
+         ByteArrayInputStream bais = new ByteArrayInputStream(decoded);
+         ObjectInputStream ois = null;
+         try
+         {
+            ois = new ObjectInputStream(bais);
+            return (RMIServer)ois.readObject();
+         }
+         catch (ClassNotFoundException x)
+         {
+            throw new IOException("Cannot decode stub from " + url + ": " + x);
+         }
+         finally
+         {
+            if (ois != null) ois.close();
+         }
+      }
+      throw new MalformedURLException("Unsupported binding: " + url);
+   }
+
+
+//********************************************************************************************************************//
+// SERVER METHODS
+
+
+   public Object createServer(JMXServiceURL url, Map environment) throws IOException
+   {
+      return createRMIServer(url, environment);
+   }
+
+   protected RMIServerImpl createRMIServer(JMXServiceURL url, Map environment) throws IOException
+   {
+      int port = url.getPort();
+      RMIClientSocketFactory clientFactory = (RMIClientSocketFactory)environment.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE);
+      RMIServerSocketFactory serverFactory = (RMIServerSocketFactory)environment.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE);
+      return new RMIJRMPServerImpl(port, clientFactory, serverFactory, environment);
+   }
+
+   public JMXServiceURL bindServer(Object server, JMXServiceURL url, Map environment) throws IOException
+   {
+      // See javax/management/remote/rmi/package-summary.html
+
+      RMIServerImpl rmiServer = (RMIServerImpl)server;
+
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL for binding is: '" + url + "'",null);
+
+      if (isEncodedForm(url))
+      {
+         String path = encodeStub(rmiServer, environment);
+         return new JMXServiceURL(url.getProtocol(), url.getHost(), url.getPort(), path);
+      }
+
+      String jndiURL = parseJNDIForm(url);
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL path for binding is: '" + jndiURL + "'", null);
+
+      InitialContext ctx = null;
+      try
+      {
+         ctx = new InitialContext(new Hashtable(environment));
+         boolean rebind = Boolean.valueOf((String)environment.get(RMIConnectorServer.JNDI_REBIND_ATTRIBUTE)).booleanValue();
+         if (rebind)
+            ctx.rebind(jndiURL, rmiServer.toStub());
+         else
+            ctx.bind(jndiURL, rmiServer.toStub());
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Bound " + rmiServer + " to " + jndiURL, null);
+         return url;
+      }
+      catch (NamingException x)
+      {
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot bind server " + rmiServer + " to " + jndiURL, x);
+         throw new IOException(x.toString());
+      }
+      finally
+      {
+         try
+         {
+            if (ctx != null) ctx.close();
+         }
+         catch (NamingException x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot close InitialContext", x);
+         }
+      }
+   }
+
+   protected String encodeStub(RMIServerImpl rmiServer, Map environment) throws IOException
+   {
+      Remote stub = rmiServer.toStub();
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      ObjectOutputStream oos = null;
+      try
+      {
+         oos = new ObjectOutputStream(baos);
+         oos.writeObject(stub);
+      }
+      finally
+      {
+         if (oos != null) oos.close();
+      }
+      byte[] bytes = baos.toByteArray();
+      byte[] encoded = Base64Codec.encodeBase64(bytes);
+      // Since the bytes are base 64 bytes, the encoding in creating the string is not important: any will work
+      return STUB_CONTEXT + new String(encoded);
+   }
+
+   protected boolean isEncodedForm(JMXServiceURL url)
+   {
+      String path = url.getURLPath();
+      if (path == null || path.length() == 0 || path.equals("/") || path.startsWith(STUB_CONTEXT)) return true;
+      return false;
+   }
+
+   private String parseJNDIForm(JMXServiceURL url) throws MalformedURLException
+   {
+      String path = url.getURLPath();
+      if (path.startsWith(JNDI_CONTEXT))
+      {
+         String jndiURL = path.substring(JNDI_CONTEXT.length());
+         if (jndiURL == null || jndiURL.length() == 0) throw new MalformedURLException("No JNDI URL specified: " + url);
+         return jndiURL;
+      }
+      throw new MalformedURLException("Unsupported binding: " + url);
+   }
+
+   public void unbindServer(Object server, JMXServiceURL url, Map environment) throws IOException
+   {
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL for unbinding is: '" + url + "'", null);
+      // The server was not bound to JNDI (the stub was encoded), just return
+      if (isEncodedForm(url))
+      {
+         destroyServer(server, environment);
+         return;
+      }
+
+      String jndiURL = parseJNDIForm(url);
+      RmiConnectorActivator.log(LogService.LOG_DEBUG,"JMXServiceURL path for binding is: '" + jndiURL + "'",null);
+
+      InitialContext ctx = null;
+      try
+      {
+         ctx = new InitialContext(new Hashtable(environment));
+         ctx.unbind(jndiURL);
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Unbound " + server + " from " + jndiURL, null);
+      }
+      catch (NamingException x)
+      {
+         RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot unbind server " + server + " to " + jndiURL, x);
+         throw new IOException(x.toString());
+      }
+      finally
+      {
+         try
+         {
+            if (ctx != null) ctx.close();
+         }
+         catch (NamingException x)
+         {
+            RmiConnectorActivator.log(LogService.LOG_DEBUG,"Cannot close InitialContext", x);
+         }
+      }
+   }
+
+   protected void destroyServer(Object server, Map environment) throws IOException
+   {
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientExceptionCatcher.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientExceptionCatcher.java
new file mode 100644
index 0000000..d908b4c
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientExceptionCatcher.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.rmiconnector.mx4j.remote.rmi;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.rmi.NoSuchObjectException;
+import java.io.IOException;
+
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXServerErrorException;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ClientProxy;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ClientExceptionCatcher extends ClientProxy
+{
+   private ClientExceptionCatcher(MBeanServerConnection target)
+   {
+      super(target);
+   }
+
+   public static MBeanServerConnection newInstance(MBeanServerConnection target)
+   {
+      ClientExceptionCatcher handler = new ClientExceptionCatcher(target);
+      return (MBeanServerConnection)Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{MBeanServerConnection.class}, handler);
+   }
+
+   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+   {
+      try
+      {
+         return super.invoke(proxy, method, args);
+      }
+      catch (NoSuchObjectException x)
+      {
+         // The connection has been already closed by the server
+         throw new IOException("Connection closed by the server");
+      }
+      catch (Exception x)
+      {
+         throw x;
+      }
+      catch (Error x)
+      {
+         throw new JMXServerErrorException("Error thrown during invocation", x);
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientInvoker.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientInvoker.java
new file mode 100644
index 0000000..ae40401
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientInvoker.java
@@ -0,0 +1,260 @@
+/*
+ * 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.rmiconnector.mx4j.remote.rmi;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.rmi.MarshalledObject;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServerConnection;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.remote.rmi.RMIConnection;
+import javax.security.auth.Subject;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.NotificationTuple;
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.RemoteNotificationClientHandler;
+
+/**
+ * An MBeanServerConnection that "converts" the MBeanServerConnection calls to {@link RMIConnection} calls,
+ * performing wrapping of parameters and/or the needed actions.
+ *
+ * @see mx4j.remote.rmi.RMIConnectionInvoker
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Brian Scully</a>
+ * @version $Revision: 1.2 $
+ */
+public class ClientInvoker implements MBeanServerConnection
+{
+   private final RMIConnection connection;
+   private final Subject delegate;
+   private final RemoteNotificationClientHandler notificationHandler;
+
+   public ClientInvoker(RMIConnection rmiConnection, RemoteNotificationClientHandler notificationHandler, Subject delegate, Map environment)
+   {
+      this.connection = rmiConnection;
+      this.delegate = delegate;
+      this.notificationHandler = notificationHandler;
+   }
+
+   public void addNotificationListener(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback)
+           throws InstanceNotFoundException, IOException
+   {
+      NotificationTuple tuple = new NotificationTuple(observed, listener, filter, handback);
+      if (notificationHandler.contains(tuple)) return;
+
+      MarshalledObject f = null;
+      try
+      {
+         f = RMIMarshaller.marshal(filter);
+      }
+      catch (NotSerializableException x)
+      {
+         // Invoke the filter on client side
+         tuple.setInvokeFilter(true);
+      }
+      Integer[] ids = connection.addNotificationListeners(new ObjectName[] {observed}, new MarshalledObject[] {f}, new Subject[] {delegate});
+      notificationHandler.addNotificationListener(ids[0], tuple);
+   }
+
+   public void removeNotificationListener(ObjectName observed, NotificationListener listener)
+           throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      Integer[] ids = notificationHandler.getNotificationListeners(new NotificationTuple(observed, listener));
+      if (ids == null) throw new ListenerNotFoundException("Could not find listener " + listener);
+      connection.removeNotificationListeners(observed, ids, delegate);
+      notificationHandler.removeNotificationListeners(ids);
+   }
+
+   public void removeNotificationListener(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback)
+           throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      Integer id = notificationHandler.getNotificationListener(new NotificationTuple(observed, listener, filter, handback));
+      if (id == null) throw new ListenerNotFoundException("Could not find listener " + listener + " with filter " + filter + " and handback " + handback);
+      Integer[] ids = new Integer[] {id};
+      connection.removeNotificationListeners(observed, ids, delegate);
+      notificationHandler.removeNotificationListeners(ids);
+   }
+
+   public void addNotificationListener(ObjectName observed, ObjectName listener, NotificationFilter filter, Object handback)
+           throws InstanceNotFoundException, IOException
+   {
+      MarshalledObject f = RMIMarshaller.marshal(filter);
+      MarshalledObject h = RMIMarshaller.marshal(handback);
+      connection.addNotificationListener(observed, listener, f, h, delegate);
+   }
+
+   public void removeNotificationListener(ObjectName observed, ObjectName listener)
+           throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      connection.removeNotificationListener(observed, listener, delegate);
+   }
+
+   public void removeNotificationListener(ObjectName observed, ObjectName listener, NotificationFilter filter, Object handback)
+           throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      MarshalledObject f = RMIMarshaller.marshal(filter);
+      MarshalledObject h = RMIMarshaller.marshal(handback);
+      connection.removeNotificationListener(observed, listener, f, h, delegate);
+   }
+
+   public MBeanInfo getMBeanInfo(ObjectName objectName)
+           throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException
+   {
+      return connection.getMBeanInfo(objectName, delegate);
+   }
+
+   public boolean isInstanceOf(ObjectName objectName, String className)
+           throws InstanceNotFoundException, IOException
+   {
+      return connection.isInstanceOf(objectName, className, delegate);
+   }
+
+   public String[] getDomains()
+           throws IOException
+   {
+      return connection.getDomains(delegate);
+   }
+
+   public String getDefaultDomain()
+           throws IOException
+   {
+      return connection.getDefaultDomain(delegate);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName objectName)
+           throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException
+   {
+      return connection.createMBean(className, objectName, delegate);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName objectName, Object[] args, String[] parameters)
+           throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException
+   {
+      MarshalledObject arguments = RMIMarshaller.marshal(args);
+      return connection.createMBean(className, objectName, arguments, parameters, delegate);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName objectName, ObjectName loaderName)
+           throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException
+   {
+      return connection.createMBean(className, objectName, loaderName, delegate);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName objectName, ObjectName loaderName, Object[] args, String[] parameters)
+           throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException
+   {
+      MarshalledObject arguments = RMIMarshaller.marshal(args);
+      return connection.createMBean(className, objectName, loaderName, arguments, parameters, delegate);
+   }
+
+   public void unregisterMBean(ObjectName objectName)
+           throws InstanceNotFoundException, MBeanRegistrationException, IOException
+   {
+      connection.unregisterMBean(objectName, delegate);
+   }
+
+   public Object getAttribute(ObjectName objectName, String attribute)
+           throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException
+   {
+      return connection.getAttribute(objectName, attribute, delegate);
+   }
+
+   public void setAttribute(ObjectName objectName, Attribute attribute)
+           throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException, IOException
+   {
+      MarshalledObject attrib = RMIMarshaller.marshal(attribute);
+      connection.setAttribute(objectName, attrib, delegate);
+   }
+
+   public AttributeList getAttributes(ObjectName objectName, String[] attributes)
+           throws InstanceNotFoundException, ReflectionException, IOException
+   {
+      return connection.getAttributes(objectName, attributes, delegate);
+   }
+
+   public AttributeList setAttributes(ObjectName objectName, AttributeList attributes)
+           throws InstanceNotFoundException, ReflectionException, IOException
+   {
+      MarshalledObject attribs = RMIMarshaller.marshal(attributes);
+      return connection.setAttributes(objectName, attribs, delegate);
+   }
+
+   public Object invoke(ObjectName objectName, String methodName, Object[] args, String[] parameters)
+           throws InstanceNotFoundException, MBeanException, ReflectionException, IOException
+   {
+      MarshalledObject arguments = RMIMarshaller.marshal(args);
+      return connection.invoke(objectName, methodName, arguments, parameters, delegate);
+   }
+
+   public Integer getMBeanCount()
+           throws IOException
+   {
+      return connection.getMBeanCount(delegate);
+   }
+
+   public boolean isRegistered(ObjectName objectName)
+           throws IOException
+   {
+      return connection.isRegistered(objectName, delegate);
+   }
+
+   public ObjectInstance getObjectInstance(ObjectName objectName)
+           throws InstanceNotFoundException, IOException
+   {
+      return connection.getObjectInstance(objectName, delegate);
+   }
+
+   public Set queryMBeans(ObjectName patternName, QueryExp filter)
+           throws IOException
+   {
+      MarshalledObject query = RMIMarshaller.marshal(filter);
+      return connection.queryMBeans(patternName, query, delegate);
+   }
+
+   public Set queryNames(ObjectName patternName, QueryExp filter)
+           throws IOException
+   {
+      MarshalledObject query = RMIMarshaller.marshal(filter);
+      return connection.queryNames(patternName, query, delegate);
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientUnmarshaller.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientUnmarshaller.java
new file mode 100644
index 0000000..c18ef3a
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/ClientUnmarshaller.java
@@ -0,0 +1,96 @@
+/*
+ * 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.rmiconnector.mx4j.remote.rmi;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.management.MBeanServerConnection;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ClientProxy;
+
+/**
+ * An MBeanServerConnection proxy that performs the setting of the appropriate context classloader
+ * to allow classloading of classes sent by the server but not known to the client, in methods like
+ * {@link MBeanServerConnection#getAttribute}, {@link MBeanServerConnection#invoke} and so on.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class ClientUnmarshaller extends ClientProxy
+{
+   private final ClassLoader classLoader;
+
+   private ClientUnmarshaller(MBeanServerConnection target, ClassLoader loader)
+   {
+      super(target);
+      this.classLoader = loader;
+   }
+
+   public static MBeanServerConnection newInstance(MBeanServerConnection target, ClassLoader loader)
+   {
+      ClientUnmarshaller handler = new ClientUnmarshaller(target, loader);
+      return (MBeanServerConnection)Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{MBeanServerConnection.class}, handler);
+   }
+
+   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+   {
+      if (classLoader == null)
+      {
+         return chain(proxy, method, args);
+      }
+      else
+      {
+         ClassLoader old = Thread.currentThread().getContextClassLoader();
+         try
+         {
+            setContextClassLoader(classLoader);
+            return chain(proxy, method, args);
+         }
+         finally
+         {
+            setContextClassLoader(old);
+         }
+      }
+   }
+
+   private Object chain(Object proxy, Method method, Object[] args) throws Throwable
+   {
+      return super.invoke(proxy, method, args);
+   }
+
+   private void setContextClassLoader(final ClassLoader loader)
+   {
+      AccessController.doPrivileged(new PrivilegedAction()
+      {
+         public Object run()
+         {
+            Thread.currentThread().setContextClassLoader(loader);
+            return null;
+         }
+      });
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionInvoker.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionInvoker.java
new file mode 100644
index 0000000..d6f9ebd
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionInvoker.java
@@ -0,0 +1,309 @@
+/*
+ * 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.rmiconnector.mx4j.remote.rmi;
+
+import java.io.IOException;
+import java.rmi.MarshalledObject;
+import java.security.SecureClassLoader;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.loading.ClassLoaderRepository;
+import javax.management.remote.NotificationResult;
+import javax.management.remote.rmi.RMIConnection;
+import javax.security.auth.Subject;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.NotificationTuple;
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.RemoteNotificationServerHandler;
+
+/**
+ * An RMIConnection that "converts" remote calls to {@link MBeanServer} calls,
+ * performing unwrapping of parameters and/or the needed actions.
+ *
+ * @see mx4j.remote.rmi.ClientInvoker
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @author <a href="mailto:btscully@users.sourceforge.net">Brian Scully</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RMIConnectionInvoker implements RMIConnection
+{
+   private final MBeanServer server;
+   private final ClassLoader defaultLoader;
+   private final RemoteNotificationServerHandler notificationHandler;
+
+   public RMIConnectionInvoker(MBeanServer server, ClassLoader defaultLoader, Map environment)
+   {
+      this.server = server;
+      this.defaultLoader = defaultLoader;
+      // TODO: here we hardcoded the handler for notifications. Maybe worth to make it pluggable ?
+      this.notificationHandler = new RMIRemoteNotificationServerHandler(environment);
+   }
+
+   public String getConnectionId() throws IOException
+   {
+      throw new Error("getConnectionId() must not be propagated along the invocation chain");
+   }
+
+   public void close() throws IOException
+   {
+      throw new Error("close() must not be propagated along the invocation chain");
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName name, Subject delegate)
+           throws ReflectionException,
+           InstanceAlreadyExistsException,
+           MBeanRegistrationException,
+           MBeanException,
+           NotCompliantMBeanException,
+           IOException
+   {
+      return server.createMBean(className, name);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Subject delegate)
+           throws ReflectionException,
+           InstanceAlreadyExistsException,
+           MBeanRegistrationException,
+           MBeanException,
+           NotCompliantMBeanException,
+           InstanceNotFoundException,
+           IOException
+   {
+      return server.createMBean(className, name, loaderName);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName name, MarshalledObject params, String[] signature, Subject delegate)
+           throws ReflectionException,
+           InstanceAlreadyExistsException,
+           MBeanRegistrationException,
+           MBeanException,
+           NotCompliantMBeanException,
+           IOException
+   {
+      Object[] args = (Object[])RMIMarshaller.unmarshal(params, new RepositoryClassLoader(server.getClassLoaderRepository()), defaultLoader);
+      return server.createMBean(className, name, args, signature);
+   }
+
+   public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, MarshalledObject params, String[] signature, Subject delegate)
+           throws ReflectionException,
+           InstanceAlreadyExistsException,
+           MBeanRegistrationException,
+           MBeanException,
+           NotCompliantMBeanException,
+           InstanceNotFoundException,
+           IOException
+   {
+      Object[] args = (Object[])RMIMarshaller.unmarshal(params, server.getClassLoader(loaderName), defaultLoader);
+      return server.createMBean(className, name, loaderName, args, signature);
+   }
+
+   public void unregisterMBean(ObjectName name, Subject delegate) throws InstanceNotFoundException, MBeanRegistrationException, IOException
+   {
+      server.unregisterMBean(name);
+   }
+
+   public ObjectInstance getObjectInstance(ObjectName name, Subject delegate) throws InstanceNotFoundException, IOException
+   {
+      return server.getObjectInstance(name);
+   }
+
+   public Set queryMBeans(ObjectName name, MarshalledObject query, Subject delegate) throws IOException
+   {
+      QueryExp filter = (QueryExp)RMIMarshaller.unmarshal(query, null, defaultLoader);
+      return server.queryMBeans(name, filter);
+   }
+
+   public Set queryNames(ObjectName name, MarshalledObject query, Subject delegate) throws IOException
+   {
+      QueryExp filter = (QueryExp)RMIMarshaller.unmarshal(query, null, defaultLoader);
+      return server.queryNames(name, filter);
+   }
+
+   public boolean isRegistered(ObjectName name, Subject delegate) throws IOException
+   {
+      return server.isRegistered(name);
+   }
+
+   public Integer getMBeanCount(Subject delegate) throws IOException
+   {
+      return server.getMBeanCount();
+   }
+
+   public Object getAttribute(ObjectName name, String attribute, Subject delegate)
+           throws MBeanException,
+           AttributeNotFoundException,
+           InstanceNotFoundException,
+           ReflectionException,
+           IOException
+   {
+      return server.getAttribute(name, attribute);
+   }
+
+   public AttributeList getAttributes(ObjectName name, String[] attributes, Subject delegate)
+           throws InstanceNotFoundException, ReflectionException, IOException
+   {
+      return server.getAttributes(name, attributes);
+   }
+
+   public void setAttribute(ObjectName name, MarshalledObject attribute, Subject delegate)
+           throws InstanceNotFoundException,
+           AttributeNotFoundException,
+           InvalidAttributeValueException,
+           MBeanException,
+           ReflectionException,
+           IOException
+   {
+      Attribute attrib = (Attribute)RMIMarshaller.unmarshal(attribute, server.getClassLoaderFor(name), defaultLoader);
+      server.setAttribute(name, attrib);
+   }
+
+   public AttributeList setAttributes(ObjectName name, MarshalledObject attributes, Subject delegate)
+           throws InstanceNotFoundException,
+           ReflectionException,
+           IOException
+   {
+      AttributeList attribs = (AttributeList)RMIMarshaller.unmarshal(attributes, server.getClassLoaderFor(name), defaultLoader);
+      return server.setAttributes(name, attribs);
+   }
+
+   public Object invoke(ObjectName name, String operationName, MarshalledObject params, String[] signature, Subject delegate)
+           throws InstanceNotFoundException,
+           MBeanException,
+           ReflectionException,
+           IOException
+   {
+      Object[] args = (Object[])RMIMarshaller.unmarshal(params, server.getClassLoaderFor(name), defaultLoader);
+      return server.invoke(name, operationName, args, signature);
+   }
+
+   public String getDefaultDomain(Subject delegate) throws IOException
+   {
+      return server.getDefaultDomain();
+   }
+
+   public String[] getDomains(Subject delegate) throws IOException
+   {
+      return server.getDomains();
+   }
+
+   public MBeanInfo getMBeanInfo(ObjectName name, Subject delegate) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException
+   {
+      return server.getMBeanInfo(name);
+   }
+
+   public boolean isInstanceOf(ObjectName name, String className, Subject delegate) throws InstanceNotFoundException, IOException
+   {
+      return server.isInstanceOf(name, className);
+   }
+
+   public void addNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegate)
+           throws InstanceNotFoundException, IOException
+   {
+      NotificationFilter f = (NotificationFilter)RMIMarshaller.unmarshal(filter, server.getClassLoaderFor(name), defaultLoader);
+      Object h = RMIMarshaller.unmarshal(handback, server.getClassLoaderFor(name), defaultLoader);
+      server.addNotificationListener(name, listener, f, h);
+   }
+
+   public void removeNotificationListener(ObjectName name, ObjectName listener, Subject delegate)
+           throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      server.removeNotificationListener(name, listener);
+   }
+
+   public void removeNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegate)
+           throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      NotificationFilter f = (NotificationFilter)RMIMarshaller.unmarshal(filter, server.getClassLoaderFor(name), defaultLoader);
+      Object h = RMIMarshaller.unmarshal(handback, server.getClassLoaderFor(name), defaultLoader);
+      server.removeNotificationListener(name, listener, f, h);
+   }
+
+   public Integer[] addNotificationListeners(ObjectName[] names, MarshalledObject[] filters, Subject[] delegates) throws InstanceNotFoundException, IOException
+   {
+      ArrayList ids = new ArrayList();
+      for (int i = 0; i < names.length; ++i)
+      {
+         ObjectName name = names[i];
+         MarshalledObject filter = filters[i];
+         NotificationFilter f = (NotificationFilter)RMIMarshaller.unmarshal(filter, server.getClassLoaderFor(name), defaultLoader);
+         Integer id = notificationHandler.generateListenerID(name, f);
+         NotificationListener listener = notificationHandler.getServerNotificationListener();
+         server.addNotificationListener(name, listener, f, id);
+         notificationHandler.addNotificationListener(id, new NotificationTuple(name, listener, f, id));
+         ids.add(id);
+      }
+      return (Integer[])ids.toArray(new Integer[ids.size()]);
+   }
+
+   public void removeNotificationListeners(ObjectName name, Integer[] listenerIDs, Subject delegate) throws InstanceNotFoundException, ListenerNotFoundException, IOException
+   {
+      for (int i = 0; i < listenerIDs.length; ++i)
+      {
+         Integer id = listenerIDs[i];
+         NotificationTuple tuple = notificationHandler.getNotificationListener(id);
+         server.removeNotificationListener(name, tuple.getNotificationListener(), tuple.getNotificationFilter(), tuple.getHandback());
+         notificationHandler.removeNotificationListener(id);
+      }
+   }
+
+   public NotificationResult fetchNotifications(long clientSequenceNumber, int maxNotifications, long timeout) throws IOException
+   {
+      return notificationHandler.fetchNotifications(clientSequenceNumber, maxNotifications, timeout);
+   }
+
+   private static class RepositoryClassLoader extends SecureClassLoader
+   {
+      private final ClassLoaderRepository repository;
+
+      private RepositoryClassLoader(ClassLoaderRepository repository)
+      {
+         this.repository = repository;
+      }
+
+      public Class loadClass(String name) throws ClassNotFoundException
+      {
+         return repository.loadClass(name);
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionProxy.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionProxy.java
new file mode 100644
index 0000000..a130876
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionProxy.java
@@ -0,0 +1,58 @@
+/*
+ * 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.rmiconnector.mx4j.remote.rmi;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.management.remote.rmi.RMIConnection;
+
+/**
+ * Base class for RMIConnection dynamic proxies.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RMIConnectionProxy implements InvocationHandler
+{
+   private RMIConnection nested;
+
+   protected RMIConnectionProxy(RMIConnection nested)
+   {
+      this.nested = nested;
+   }
+
+   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+   {
+      try
+      {
+         return method.invoke(nested, args);
+      }
+      catch (InvocationTargetException x)
+      {
+         throw x.getTargetException();
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionSubjectInvoker.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionSubjectInvoker.java
new file mode 100644
index 0000000..d4d85f9
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIConnectionSubjectInvoker.java
@@ -0,0 +1,120 @@
+/*
+ * 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.rmiconnector.mx4j.remote.rmi;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.rmi.MarshalledObject;
+import java.security.PrivilegedExceptionAction;
+import java.security.AccessControlContext;
+import java.util.ArrayList;
+
+import javax.management.ObjectName;
+import javax.management.remote.JMXServerErrorException;
+import javax.management.remote.rmi.RMIConnection;
+import javax.security.auth.Subject;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.MX4JRemoteUtils;
+
+/**
+ * An RMIConnection proxy that wraps the call into a {@link Subject#doAsPrivileged} invocation,
+ * in order to execute the code under subject-based security, and to perform subject delegation.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RMIConnectionSubjectInvoker extends RMIConnectionProxy
+{
+   public static RMIConnection newInstance(RMIConnection nested, Subject subject, AccessControlContext context)
+   {
+      RMIConnectionSubjectInvoker handler = new RMIConnectionSubjectInvoker(nested, subject, context);
+      return (RMIConnection)Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[] {RMIConnection.class}, handler);
+   }
+
+   private final Subject subject;
+   private final AccessControlContext context;
+
+   private RMIConnectionSubjectInvoker(RMIConnection nested, Subject subject, AccessControlContext context)
+   {
+      super(nested);
+      this.subject = subject;
+      this.context = context;
+   }
+
+   public Object invoke(final Object proxy, final Method method, final Object[] args)
+           throws Throwable
+   {
+      String methodName = method.getName();
+      if ("fetchNotifications".equals(methodName)) return chain(proxy, method, args);
+
+      if ("addNotificationListeners".equals(methodName))
+      {
+         Subject[] delegates = (Subject[])args[args.length - 1];
+         if (delegates == null || delegates.length == 0) return chain(proxy, method, args);
+
+         if (delegates.length == 1) return subjectInvoke(proxy, method, args, delegates[0]);
+
+         ArrayList ids = new ArrayList();
+         for (int i = 0; i < delegates.length; ++i)
+         {
+            ObjectName name = ((ObjectName[])args[0])[i];
+            MarshalledObject filter = ((MarshalledObject[])args[1])[i];
+            Subject delegate = delegates[i];
+            Object[] newArgs = new Object[] {new ObjectName[] {name}, new MarshalledObject[] {filter}, new Subject[] {delegate}};
+            Integer id = ((Integer[])subjectInvoke(proxy, method, newArgs, delegate))[0];
+            ids.add(id);
+         }
+         return (Integer[])ids.toArray(new Integer[ids.size()]);
+      }
+
+      // For all other methods, the subject is always the last argument
+      Subject delegate = (Subject)args[args.length - 1];
+
+      return subjectInvoke(proxy, method, args, delegate);
+   }
+
+   private Object subjectInvoke(final Object proxy, final Method method, final Object[] args, Subject delegate) throws Exception
+   {
+      return MX4JRemoteUtils.subjectInvoke(subject, delegate, context, new PrivilegedExceptionAction()
+      {
+         public Object run() throws Exception
+         {
+            return chain(proxy, method, args);
+         }
+      });
+   }
+
+   private Object chain(Object proxy, Method method, Object[] args) throws Exception
+   {
+      try
+      {
+         return super.invoke(proxy, method, args);
+      }
+      catch (Throwable x)
+      {
+         if (x instanceof Exception) throw (Exception)x;
+         throw new JMXServerErrorException("Error thrown during invocation", (Error)x);
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIHeartBeat.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIHeartBeat.java
new file mode 100644
index 0000000..9d539ad
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIHeartBeat.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.rmiconnector.mx4j.remote.rmi;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.management.remote.rmi.RMIConnection;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.AbstractHeartBeat;
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ConnectionNotificationEmitter;
+
+/**
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+public class RMIHeartBeat extends AbstractHeartBeat
+{
+   private final RMIConnection connection;
+
+   public RMIHeartBeat(RMIConnection connection, ConnectionNotificationEmitter emitter, Map environment)
+   {
+      super(emitter, environment);
+      this.connection = connection;
+   }
+
+   protected void pulse() throws IOException
+   {
+      connection.getDefaultDomain(null);
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIMarshaller.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIMarshaller.java
new file mode 100644
index 0000000..7cbf5b1
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIMarshaller.java
@@ -0,0 +1,186 @@
+/*
+ * 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.rmiconnector.mx4j.remote.rmi;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.rmi.MarshalledObject;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * Marshaller/Unmarshaller for RMI's MarshalledObjects.
+ *
+ * @author <a href="mailto:biorn_steedom@users.sourceforge.net">Simone Bordet</a>
+ * @version $Revision: 1.1.1.1 $
+ */
+class RMIMarshaller
+{
+   private static Method unmarshal;
+
+   /**
+    * MarshalledObject.get() loads the object it contains by using the first user-defined classloader it can find
+    * in the stack frames of the call.
+    * In a normal usage of JSR 160, this classloader is the one that loaded this class, most probably
+    * the system classloader.
+    * If the class cannot be found with that loader, then the RMI semantic is tried: first the thread context
+    * classloader, then dynamic code download (if there is a security manager).
+    * Here we load the Marshaller class using an URLClassLoader that is only able to load classes from the URL
+    * where it loaded this class, thus it cannot see other classes in the system classloader.
+    * This URLClassLoader then becomes the first user-defined classloader in the stack frames, but it will fail
+    * to load anything else, thus allowing MarshalledObject.get() to use the thread context classloader.
+    */
+   static
+   {
+      try
+      {
+         AccessController.doPrivileged(new PrivilegedExceptionAction()
+         {
+            public Object run() throws Exception
+            {
+               URL url = RMIMarshaller.class.getProtectionDomain().getCodeSource().getLocation();
+               // TODO: is it enough to use the parent, or maybe better use null as parent classloader ?
+               URLClassLoader loader = new URLClassLoader(new URL[] {url}, RMIMarshaller.class.getClassLoader().getParent());
+               Class marshaller = loader.loadClass(Marshaller.class.getName());
+               unmarshal = marshaller.getMethod("unmarshal", new Class[] {MarshalledObject.class});
+               return null;
+            }
+         });
+      }
+      catch (PrivilegedActionException x)
+      {
+         throw new Error(x.toString());
+      }
+   }
+
+   /**
+    * Returns a MarshalledObject obtained by marshalling the given object.
+    */
+   public static MarshalledObject marshal(Object object) throws IOException
+   {
+      if (object == null) return null;
+      return new MarshalledObject(object);
+   }
+
+   /**
+    * Returns the unmarshalled object obtained unmarshalling the given MarshalledObject,
+    * using as context classloader first the given mbeanLoader, if not null, then with the given defaultLoader.
+    */
+   public static Object unmarshal(MarshalledObject object, ClassLoader mbeanLoader, ClassLoader defaultLoader) throws IOException
+   {
+      if (object == null) return null;
+      if (mbeanLoader == null) return unmarshal(object, defaultLoader);
+      return unmarshal(object, new MarshallerClassLoader(mbeanLoader, defaultLoader));
+   }
+
+   private static Object unmarshal(MarshalledObject object, ClassLoader loader) throws IOException
+   {
+      if (loader != null)
+      {
+         ClassLoader old = Thread.currentThread().getContextClassLoader();
+         try
+         {
+            setContextClassLoader(loader);
+            return unmarshal(object);
+         }
+         catch (IOException x)
+         {
+            throw x;
+         }
+         catch (ClassNotFoundException ignored)
+         {
+         }
+         finally
+         {
+            setContextClassLoader(old);
+         }
+      }
+      throw new IOException("Cannot unmarshal " + object);
+   }
+
+   private static Object unmarshal(MarshalledObject marshalled) throws IOException, ClassNotFoundException
+   {
+      try
+      {
+         return unmarshal.invoke(null, new Object[]{marshalled});
+      }
+      catch (InvocationTargetException x)
+      {
+         Throwable t = x.getTargetException();
+         if (t instanceof IOException) throw (IOException)t;
+         if (t instanceof ClassNotFoundException) throw (ClassNotFoundException)t;
+         throw new IOException(t.toString());
+      }
+      catch (Exception x)
+      {
+         throw new IOException(x.toString());
+      }
+   }
+
+   private static void setContextClassLoader(final ClassLoader loader)
+   {
+      AccessController.doPrivileged(new PrivilegedAction()
+      {
+         public Object run()
+         {
+            Thread.currentThread().setContextClassLoader(loader);
+            return null;
+         }
+      });
+   }
+
+   public static class Marshaller
+   {
+      public static Object unmarshal(MarshalledObject obj) throws IOException, ClassNotFoundException
+      {
+         return obj.get();
+      }
+   }
+
+   private static class MarshallerClassLoader extends ClassLoader
+   {
+      private final ClassLoader defaultLoader;
+
+      private MarshallerClassLoader(ClassLoader mbeanLoader, ClassLoader defaultLoader)
+      {
+         super(mbeanLoader);
+         this.defaultLoader = defaultLoader;
+      }
+
+      protected Class findClass(String name) throws ClassNotFoundException
+      {
+         return defaultLoader.loadClass(name);
+      }
+
+      protected URL findResource(String name)
+      {
+         return defaultLoader.getResource(name);
+      }
+   }
+}
diff --git a/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIRemoteNotificationServerHandler.java b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIRemoteNotificationServerHandler.java
new file mode 100644
index 0000000..8d68bbc
--- /dev/null
+++ b/org.apache.felix.mosgi.jmx.rmiconnector/src/main/java/org/apache/felix/mosgi/jmx/rmiconnector/mx4j/remote/rmi/RMIRemoteNotificationServerHandler.java
@@ -0,0 +1,58 @@
+/*
+ *   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.rmiconnector.mx4j.remote.rmi;
+
+import java.util.Map;
+import java.util.ArrayList;
+
+import javax.management.remote.TargetedNotification;
+
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.DefaultRemoteNotificationServerHandler;
+import org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.MX4JRemoteUtils;
+
+import org.osgi.service.log.LogService;
+import org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator;
+
+/**
+ *
+ * @version $Revision: 1.1.1.1 $
+ */
+class RMIRemoteNotificationServerHandler extends DefaultRemoteNotificationServerHandler
+{
+   RMIRemoteNotificationServerHandler(Map environment)
+   {
+      super(environment);
+   }
+
+   protected TargetedNotification[] filterNotifications(TargetedNotification[] notifications)
+   {
+      ArrayList list = new ArrayList();
+      for (int i = 0; i < notifications.length; ++i)
+      {
+         TargetedNotification notification = notifications[i];
+         if (MX4JRemoteUtils.isTrulySerializable(notification))
+         {
+            list.add(notification);
+         }
+         else
+         {
+            RmiConnectorActivator.log(LogService.LOG_INFO,"Cannot send notification " + notification + " to the client: it is not serializable", null);
+         }
+      }
+      return (TargetedNotification[])list.toArray(new TargetedNotification[list.size()]);
+   }
+}