You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2019/12/25 17:46:26 UTC

[karaf] branch master updated: [KARAF-2925] Add jmxmp support

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/master by this push:
     new 364a980  [KARAF-2925] Add jmxmp support
     new cf4da8b  Merge pull request #527 from jbonofre/KARAF-2925
364a980 is described below

commit 364a98078cb007500af97100008a9a443eb4745b
Author: Jean-Baptiste Onofré <jb...@apache.org>
AuthorDate: Sat Dec 21 13:27:29 2019 +0100

    [KARAF-2925] Add jmxmp support
---
 assemblies/features/base/pom.xml                   | 73 +++++++++++++---------
 .../resources/etc/config.properties                |  4 +-
 assemblies/features/framework/pom.xml              |  6 ++
 .../features/standard/src/main/feature/feature.xml | 33 ++++++++--
 .../karaf/instance/resources/etc/config.properties |  2 +
 .../resources/etc/org.apache.karaf.management.cfg  | 25 ++++++++
 management/server/pom.xml                          | 14 +++++
 .../karaf/management/ConnectorServerFactory.java   | 71 ++++++++++++++++++++-
 .../apache/karaf/management/JaasAuthenticator.java | 55 +++++++++++++++-
 .../karaf/management/internal/Activator.java       | 13 +++-
 .../management/internal/JMXSecurityMBeanImpl.java  |  2 +-
 .../src/main/asciidoc/user-guide/monitoring.adoc   | 27 ++++++++
 12 files changed, 284 insertions(+), 41 deletions(-)

diff --git a/assemblies/features/base/pom.xml b/assemblies/features/base/pom.xml
index 14615d6..701cbcf 100644
--- a/assemblies/features/base/pom.xml
+++ b/assemblies/features/base/pom.xml
@@ -37,32 +37,32 @@
     </properties>
 
     <dependencies>
-                    <dependency>
-                        <groupId>jakarta.xml.bind</groupId>
-                        <artifactId>jakarta.xml.bind-api</artifactId>
-                    </dependency>
-                    <dependency>
-                        <groupId>javax.annotation</groupId>
-                        <artifactId>javax.annotation-api</artifactId>
-                        <version>${javax.annotation.version}</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>com.sun.activation</groupId>
-                        <artifactId>javax.activation</artifactId>
-                        <version>1.2.0</version>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.glassfish.jaxb</groupId>
-                        <artifactId>jaxb-runtime</artifactId>
-                    </dependency>
-                    <dependency>
-                        <groupId>org.glassfish.jaxb</groupId>
-                        <artifactId>txw2</artifactId>
-                    </dependency>
-                    <dependency>
-                        <groupId>com.sun.istack</groupId>
-                        <artifactId>istack-commons-runtime</artifactId>
-                    </dependency>
+        <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>${javax.annotation.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.activation</groupId>
+            <artifactId>javax.activation</artifactId>
+            <version>1.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>txw2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.istack</groupId>
+            <artifactId>istack-commons-runtime</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.karaf.specs</groupId>
             <artifactId>org.apache.karaf.specs.locator</artifactId>
@@ -108,7 +108,12 @@
             <artifactId>org.apache.felix.framework</artifactId>
             <scope>runtime</scope>
         </dependency>
-
+        <dependency>
+            <groupId>org.glassfish.external</groupId>
+            <artifactId>opendmk_jmxremote_optional_jar</artifactId>
+            <version>1.0-b01-ea</version>
+            <scope>runtime</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -195,14 +200,14 @@
                                     <artifactId>jaxb-runtime</artifactId>
                                     <outputDirectory>target/classes/resources/lib/jdk9plus</outputDirectory>
                                 </artifactItem>
- 				<artifactItem>
+                                <artifactItem>
                                     <groupId>org.glassfish.jaxb</groupId>
                                     <artifactId>txw2</artifactId>
                                     <outputDirectory>target/classes/resources/lib/jdk9plus</outputDirectory>
                                 </artifactItem>
                                 <artifactItem>
-     				    <groupId>com.sun.istack</groupId>
-                        	    <artifactId>istack-commons-runtime</artifactId>
+                                    <groupId>com.sun.istack</groupId>
+                                    <artifactId>istack-commons-runtime</artifactId>
                                     <outputDirectory>target/classes/resources/lib/jdk9plus</outputDirectory>
                                 </artifactItem>
                                 <artifactItem>
@@ -221,6 +226,11 @@
                                     <outputDirectory>target/classes/resources/lib/boot</outputDirectory>
                                 </artifactItem>
                                 <artifactItem>
+                                    <groupId>org.glassfish.external</groupId>
+                                    <artifactId>opendmk_jmxremote_optional_jar</artifactId>
+                                    <outputDirectory>target/classes/resources/lib/boot</outputDirectory>
+                                </artifactItem>
+                                <artifactItem>
                                     <groupId>org.apache.karaf</groupId>
                                     <artifactId>org.apache.karaf.client</artifactId>
                                     <outputDirectory>target/classes/resources/system/org/apache/karaf/org.apache.karaf.client/${project.version}</outputDirectory>
@@ -229,7 +239,8 @@
                                     <groupId>org.apache.felix</groupId>
                                     <artifactId>org.apache.felix.framework</artifactId>
                                     <outputDirectory>target/classes/resources/system/org/apache/felix/org.apache.felix.framework/${felix.framework.version}</outputDirectory>
-                                    <destFileName>org.apache.felix.framework-${felix.framework.version}.jar</destFileName>
+                                    <destFileName>org.apache.felix.framework-${felix.framework.version}.jar
+                                    </destFileName>
                                 </artifactItem>
                                 <artifactItem>
                                     <groupId>${equinox.groupId}</groupId>
diff --git a/assemblies/features/base/src/main/filtered-resources/resources/etc/config.properties b/assemblies/features/base/src/main/filtered-resources/resources/etc/config.properties
index fe30b1a..d987fc9 100644
--- a/assemblies/features/base/src/main/filtered-resources/resources/etc/config.properties
+++ b/assemblies/features/base/src/main/filtered-resources/resources/etc/config.properties
@@ -89,7 +89,9 @@ org.osgi.framework.system.packages= \
 #
 org.osgi.framework.system.packages.extra= \
  org.apache.karaf.branding, \
- sun.misc
+ sun.misc, \
+ com.sun.jmx.remote.protocol, \
+ com.sun.jmx.remote.protocol.jmxmp
 
 org.osgi.framework.system.capabilities= \
  ${eecap-${java.specification.version}}, \
diff --git a/assemblies/features/framework/pom.xml b/assemblies/features/framework/pom.xml
index a89663e..6bc683d 100644
--- a/assemblies/features/framework/pom.xml
+++ b/assemblies/features/framework/pom.xml
@@ -74,6 +74,12 @@
             <artifactId>org.apache.felix.framework</artifactId>
             <scope>runtime</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.external</groupId>
+            <artifactId>opendmk_jmxremote_optional_jar</artifactId>
+            <version>1.0-b01-ea</version>
+            <scope>runtime</scope>
+        </dependency>
 
         <!-- Client deps -->
         <dependency>
diff --git a/assemblies/features/standard/src/main/feature/feature.xml b/assemblies/features/standard/src/main/feature/feature.xml
index 16e1ac8..2a59eca 100644
--- a/assemblies/features/standard/src/main/feature/feature.xml
+++ b/assemblies/features/standard/src/main/feature/feature.xml
@@ -1255,12 +1255,12 @@ rmiRegistryPort = 1099
 rmiRegistryHost = 127.0.0.1
 
 #
-# Port number for RMI server connection
+# Port number for RMI connector server connection
 #
 rmiServerPort = 44444
 
 #
-# Host for RMI server
+# Host for RMI connector server
 #
 rmiServerHost = 127.0.0.1
 
@@ -1270,11 +1270,31 @@ rmiServerHost = 127.0.0.1
 jmxRealm = karaf
 
 #
-# The service URL for the JMXConnectorServer
+# The service URL for the JMX RMI connector
 #
 serviceUrl = service:jmx:rmi://${rmiServerHost}:${rmiServerPort}/jndi/rmi://${rmiRegistryHost}:${rmiRegistryPort}/karaf-${karaf.name}
 
 #
+# JMXMP connector enabled
+#
+jmxmpEnabled = false
+
+#
+# JMXMP connector host name
+#
+jmxmpHost = 127.0.0.1
+
+#
+# JMXMP connector port number
+#
+jmxmpPort = 9999
+
+#
+# JMXMP connector service URL
+#
+jmxmpServiceUrl = service:jmx:jmxmp://${jmxmpHost}:${jmxmpPort}
+
+#
 # Whether any threads started for the JMXConnectorServer should be started as daemon threads
 #
 daemon = true
@@ -1285,11 +1305,16 @@ daemon = true
 threaded = true
 
 #
-# The ObjectName used to register the JMXConnectorServer
+# The ObjectName used to register the JMX RMI connector
 #
 objectName = connector:name=rmi
 
 #
+# The ObjectName used to register the JMXMP connector
+#
+jmxmpObjectName = connector:name=jmxmp
+
+#
 # Timeout to lookup for the keystore in case of SSL authentication usage
 #
 #keyStoreAvailabilityTimeout = 5000
diff --git a/instance/src/main/resources/org/apache/karaf/instance/resources/etc/config.properties b/instance/src/main/resources/org/apache/karaf/instance/resources/etc/config.properties
index 0495f45..d8b1f95 100644
--- a/instance/src/main/resources/org/apache/karaf/instance/resources/etc/config.properties
+++ b/instance/src/main/resources/org/apache/karaf/instance/resources/etc/config.properties
@@ -90,6 +90,8 @@ org.osgi.framework.system.packages= \
 org.osgi.framework.system.packages.extra = \
     org.apache.karaf.branding, \
     sun.misc, \
+    com.sun.jmx.remote.protocol, \
+    com.sun.jmx.remote.protocol.jmxmp, \
     org.apache.karaf.diagnostic.core;uses:=org.osgi.framework;version=@@karaf.osgi.version@@, \
     org.apache.karaf.diagnostic.core.common;uses:=org.apache.karaf.diagnostic.core;version=@@karaf.osgi.version@@, \
     org.apache.karaf.jaas.boot;uses:=\"javax.security.auth,javax.security.auth.callback,javax.security.auth.login,javax.security.auth.spi,org.osgi.framework\";version=@@karaf.osgi.version@@, \
diff --git a/instance/src/main/resources/org/apache/karaf/instance/resources/etc/org.apache.karaf.management.cfg b/instance/src/main/resources/org/apache/karaf/instance/resources/etc/org.apache.karaf.management.cfg
index 643739b..d0a9ccc 100644
--- a/instance/src/main/resources/org/apache/karaf/instance/resources/etc/org.apache.karaf.management.cfg
+++ b/instance/src/main/resources/org/apache/karaf/instance/resources/etc/org.apache.karaf.management.cfg
@@ -51,6 +51,26 @@ jmxRealm = karaf
 serviceUrl = service:jmx:rmi://${rmiServerHost}:${rmiServerPort}/jndi/rmi://${rmiRegistryHost}:${rmiRegistryPort}/karaf-${karaf.name}
 
 #
+# JMXMP connector enabled
+#
+jmxmpEnabled = false
+
+#
+# JMXMP connector host name
+#
+jmxmpHost = 127.0.0.1
+
+#
+# JMXMP connector port number
+#
+jmxmpPort = 9999
+
+#
+# JMXMP connector service URL
+#
+jmxmpServiceUrl = service:jmx:jmxmp://${jmxmpHost}:${jmxmpPort}
+
+#
 # Whether any threads started for the JMXConnectorServer should be started as daemon threads
 #
 daemon = true
@@ -66,6 +86,11 @@ threaded = true
 objectName = connector:name=rmi
 
 #
+# The ObjectName used to register the JMXMP connector
+#
+jmxmpObjectName = connector:name=jmxmp
+
+#
 # Timeout to lookup for the keystore in case of SSL authentication usage
 #
 #keyStoreAvailabilityTimeout = 5000
diff --git a/management/server/pom.xml b/management/server/pom.xml
index 5c76cd0..64d4e2b 100644
--- a/management/server/pom.xml
+++ b/management/server/pom.xml
@@ -69,6 +69,18 @@
             <artifactId>org.apache.karaf.util</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.glassfish.external</groupId>
+            <artifactId>opendmk_jmxremote_optional_jar</artifactId>
+            <version>1.0-b01-ea</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.extras</groupId>
+            <artifactId>bean-validator</artifactId>
+            <version>4.0.0.Alpha3</version>
+            <scope>provided</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -114,6 +126,8 @@
                         </Export-Package>
                         <Import-Package>
                             org.osgi.service.event*;resolution:=optional,
+                            com.sun.jmx.remote.protocol;resolution:=optional,
+                            com.sun.jdmk.security.sasl;resolution:=optional,
                             *
                         </Import-Package>
                         <Private-Package>
diff --git a/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java b/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java
index 73fcb6b..204c3b5 100644
--- a/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java
+++ b/management/server/src/main/java/org/apache/karaf/management/ConnectorServerFactory.java
@@ -56,12 +56,17 @@ public class ConnectorServerFactory {
     private MBeanServer server;
     private KarafMBeanServerGuard guard;
     private String serviceUrl;
+    private boolean jmxmpEnabled;
+    private String jmxmpServiceUrl;
     private String rmiServerHost;
     private Map<String, Object> environment;
+    private Map<String, Object> jmxmpEnvironment;
     private ObjectName objectName;
+    private ObjectName jmxmpObjectName;
     private boolean threaded = false;
     private boolean daemon = false;
     private JMXConnectorServer connectorServer;
+    private JMXConnectorServer jmxmpConnectorServer;
 
     private long keyStoreAvailabilityTimeout = 5000;
     private AuthenticatorType authenticatorType = AuthenticatorType.PASSWORD;
@@ -98,6 +103,22 @@ public class ConnectorServerFactory {
         this.serviceUrl = serviceUrl;
     }
 
+    public boolean isJmxmpEnabled() {
+        return this.jmxmpEnabled;
+    }
+
+    public void setJmxmpEnabled(boolean jmxmpEnabled) {
+        this.jmxmpEnabled = jmxmpEnabled;
+    }
+
+    public String getJmxmpServiceUrl() {
+        return jmxmpServiceUrl;
+    }
+
+    public void setJmxmpServiceUrl(String jmxmpServiceUrl) {
+        this.jmxmpServiceUrl = jmxmpServiceUrl;
+    }
+
     public String getRmiServerHost() {
         return this.rmiServerHost;
     }
@@ -114,6 +135,14 @@ public class ConnectorServerFactory {
         this.environment = environment;
     }
 
+    public Map<String, Object> getJmxmpEnvironment() {
+        return this.jmxmpEnvironment;
+    }
+
+    public void setJmxmpEnvironment(Map<String,Object> jmxmpEnvironment) {
+        this.jmxmpEnvironment = jmxmpEnvironment;
+    }
+
     public ObjectName getObjectName() {
         return objectName;
     }
@@ -122,6 +151,14 @@ public class ConnectorServerFactory {
         this.objectName = objectName;
     }
 
+    public ObjectName getJmxmpObjectName() {
+        return this.jmxmpObjectName;
+    }
+
+    public void setJmxmpObjectName(ObjectName jmxmpObjectName) {
+        this.jmxmpObjectName = jmxmpObjectName;
+    }
+
     public boolean isThreaded() {
         return threaded;
     }
@@ -259,12 +296,23 @@ public class ConnectorServerFactory {
             this.server.registerMBean(this.connectorServer, this.objectName);
         }
 
+        if (jmxmpEnabled) {
+            JMXServiceURL jmxmpUrl = new JMXServiceURL(this.jmxmpServiceUrl);
+            this.jmxmpConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxmpUrl, this.jmxmpEnvironment, guardedServer);
+            if (this.jmxmpObjectName != null) {
+                this.server.registerMBean(this.jmxmpConnectorServer, this.jmxmpObjectName);
+            }
+        }
+
         try {
             if (this.threaded) {
                 Thread connectorThread = new Thread(() -> {
                     try {
                         Thread.currentThread().setContextClassLoader(ConnectorServerFactory.class.getClassLoader());
                         connectorServer.start();
+                        if (jmxmpEnabled && jmxmpConnectorServer != null) {
+                            jmxmpConnectorServer.start();
+                        }
                     } catch (IOException ex) {
                         if (ex.getCause() instanceof BindException){
                             // we want just the port message
@@ -285,9 +333,17 @@ public class ConnectorServerFactory {
                 connectorThread.start();
             } else {
                 this.connectorServer.start();
+                if (jmxmpEnabled && jmxmpConnectorServer != null) {
+                    jmxmpConnectorServer.start();
+                }
             }
         } catch (Exception ex) {
-            doUnregister(this.objectName);
+            if (this.objectName != null) {
+                doUnregister(this.objectName);
+            }
+            if (jmxmpEnabled && this.jmxmpObjectName != null) {
+                doUnregister(this.jmxmpObjectName);
+            }
             throw ex;
         }
     }
@@ -297,8 +353,16 @@ public class ConnectorServerFactory {
             if (this.connectorServer != null) {
                 this.connectorServer.stop();
             }
+            if (this.jmxmpEnabled && this.jmxmpConnectorServer != null) {
+                this.jmxmpConnectorServer.stop();
+            }
         } finally {
-            doUnregister(this.objectName);
+            if (this.objectName != null) {
+                doUnregister(this.objectName);
+            }
+            if (this.jmxmpEnabled && this.jmxmpObjectName != null) {
+                doUnregister(this.jmxmpObjectName);
+            }
         }
     }
 
@@ -307,6 +371,9 @@ public class ConnectorServerFactory {
             if (this.objectName != null && this.server.isRegistered(objectName)) {
                 this.server.unregisterMBean(objectName);
             }
+            if (this.jmxmpObjectName != null && this.server.isRegistered(jmxmpObjectName)) {
+                this.server.unregisterMBean(jmxmpObjectName);
+            }
         } catch (JMException ex) {
             // Ignore
         }
diff --git a/management/server/src/main/java/org/apache/karaf/management/JaasAuthenticator.java b/management/server/src/main/java/org/apache/karaf/management/JaasAuthenticator.java
index 2f8aeb7..0ad401d 100644
--- a/management/server/src/main/java/org/apache/karaf/management/JaasAuthenticator.java
+++ b/management/server/src/main/java/org/apache/karaf/management/JaasAuthenticator.java
@@ -17,22 +17,27 @@
 package org.apache.karaf.management;
 
 import javax.security.auth.callback.Callback;
+
+import com.sun.jdmk.security.sasl.AuthenticateCallback;
 import org.apache.karaf.jaas.boot.principal.ClientPrincipal;
 import org.apache.karaf.jaas.boot.principal.RolePrincipal;
 
+import java.io.IOException;
 import java.rmi.server.RemoteServer;
 import java.security.Principal;
 
 import javax.management.remote.JMXAuthenticator;
 import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.NameCallback;
 import javax.security.auth.callback.PasswordCallback;
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.FailedLoginException;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
+import javax.security.sasl.AuthorizeCallback;
 
-public class JaasAuthenticator implements JMXAuthenticator {
+public class JaasAuthenticator implements JMXAuthenticator, CallbackHandler {
 
     private String realm;
 
@@ -90,4 +95,52 @@ public class JaasAuthenticator implements JMXAuthenticator {
             throw new SecurityException("Authentication failed", e);
         }
     }
+
+    @Override
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+        for (int i = 0; i < callbacks.length; i++) {
+            if (callbacks[i] instanceof AuthenticateCallback) {
+                AuthenticateCallback acb = (AuthenticateCallback) callbacks[i];
+                Subject subject = new Subject();
+                try {
+                    subject.getPrincipals().add(new ClientPrincipal("jmx", RemoteServer.getClientHost()));
+                } catch (Throwable t) {
+                    // Ignore
+                }
+                try {
+                    LoginContext loginContext = new LoginContext(realm, subject, cb -> {
+                        for (Callback callback : cb) {
+                            if (callback instanceof NameCallback) {
+                                ((NameCallback) callback).setName(acb.getAuthenticationID());
+                            } else if (callback instanceof PasswordCallback) {
+                                ((PasswordCallback) callback).setPassword((acb.getPassword()));
+                            } else {
+                                throw new UnsupportedCallbackException(callback);
+                            }
+                        }
+                    });
+                    loginContext.login();
+                } catch (Exception e) {
+                    throw new SecurityException("Authentication failed", e);
+                }
+                int roleCount = 0;
+                for (Principal principal : subject.getPrincipals()) {
+                    if (principal instanceof RolePrincipal) {
+                        roleCount++;
+                    }
+                }
+
+                if (roleCount == 0) {
+                    throw new SecurityException("User doesn't have role defined");
+                }
+                acb.setAuthenticated(true);
+            } else if (callbacks[i] instanceof AuthorizeCallback) {
+                AuthorizeCallback acb = (AuthorizeCallback) callbacks[i];
+                acb.setAuthorized(true);
+            } else {
+                throw new UnsupportedCallbackException(callbacks[i]);
+            }
+        }
+    }
+
 }
diff --git a/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java b/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java
index d6ea93d..586c392 100644
--- a/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java
+++ b/management/server/src/main/java/org/apache/karaf/management/internal/Activator.java
@@ -102,10 +102,14 @@ public class Activator extends BaseActivator implements ManagedService {
         String jmxRealm = getString("jmxRealm", "karaf");
         String serviceUrl = getString("serviceUrl",
                 "service:jmx:rmi://" + rmiServerHost + ":" + rmiServerPort + "/jndi/rmi://" + rmiRegistryHost + ":" + rmiRegistryPort + "/karaf-" + System.getProperty("karaf.name"));
-
+        boolean jmxmpEnabled = getBoolean("jmxmpEnabled", false);
+        String jmxmpHost = getString("jmxmpHost", "0.0.0.0");
+        int jmxmpPort = getInt("jmxmpPort", 9999);
+        String jmxmpServiceUrl = getString("jmxmpServiceUrl", "service:jmx:jmxmp://" + jmxmpHost + ":" + jmxmpPort);
         boolean daemon = getBoolean("daemon", true);
         boolean threaded = getBoolean("threaded", true);
         ObjectName objectName = new ObjectName(getString("objectName", "connector:name=rmi"));
+        ObjectName jmxmpObjectName = new ObjectName(getString("jmxmpObjectName", "connector:name=jmxmp"));
         long keyStoreAvailabilityTimeout = getLong("keyStoreAvailabilityTimeout", 5000);
         String authenticatorType = getString("authenticatorType", "password");
         final boolean secured = getBoolean("secured", false);
@@ -149,10 +153,17 @@ public class Activator extends BaseActivator implements ManagedService {
         connectorServerFactory.setDaemon(daemon);
         connectorServerFactory.setThreaded(threaded);
         connectorServerFactory.setObjectName(objectName);
+        connectorServerFactory.setJmxmpEnabled(jmxmpEnabled);
+        connectorServerFactory.setJmxmpServiceUrl(jmxmpServiceUrl);
+        connectorServerFactory.setJmxmpObjectName(jmxmpObjectName);
+        Map<String, Object> jmxmpEnvironment = new HashMap<>();
+        jmxmpEnvironment.put("jmx.remote.profiles", "SASL/PLAIN");
+        jmxmpEnvironment.put("jmx.remote.sasl.callback.handler", jaasAuthenticator);
         Map<String, Object> environment = new HashMap<>();
         environment.put("jmx.remote.authenticator", jaasAuthenticator);
         try {
             connectorServerFactory.setEnvironment(environment);
+            connectorServerFactory.setJmxmpEnvironment(jmxmpEnvironment);
             connectorServerFactory.setKeyStoreAvailabilityTimeout(keyStoreAvailabilityTimeout);
             connectorServerFactory.setAuthenticatorType(authenticatorType);
             connectorServerFactory.setSecured(secured);
diff --git a/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java b/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
index 3c66b9f..59d1b59 100644
--- a/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
+++ b/management/server/src/main/java/org/apache/karaf/management/internal/JMXSecurityMBeanImpl.java
@@ -109,7 +109,7 @@ public class JMXSecurityMBeanImpl extends StandardMBean implements JMXSecurityMB
                         table.put(data);
                     } catch (KeyAlreadyExistsException e) {
                         // KeyAlreadyExistsException can happen only when methods are not empty
-                        LOG.warn("{} (objectName = \"{}\", method = \"{}\")", e, objectName, method);
+                        LOG.warn("{} (objectName = \"{}\", method = \"{}\")", e, new Object[] { objectName, method });
                     }
                 }
             }
diff --git a/manual/src/main/asciidoc/user-guide/monitoring.adoc b/manual/src/main/asciidoc/user-guide/monitoring.adoc
index fc65480..a608f9a 100644
--- a/manual/src/main/asciidoc/user-guide/monitoring.adoc
+++ b/manual/src/main/asciidoc/user-guide/monitoring.adoc
@@ -94,6 +94,26 @@ jmxRealm = karaf
 serviceUrl = service:jmx:rmi://0.0.0.0:${rmiServerPort}/jndi/rmi://0.0.0.0:${rmiRegistryPort}/karaf-${karaf.name}
 
 #
+# JMXMP connector enabled
+#
+jmxmpEnabled = false
+
+#
+# JMXMP connector host name
+#
+jmxmpHost = 127.0.0.1
+
+#
+# JMXMP connector port number
+#
+jmxmpPort = 9999
+
+#
+# JMXMP connector service URL
+#
+jmxmpServiceUrl = service:jmx:jmxmp://${jmxmpHost}:${jmxmpPort}
+
+#
 # Whether any threads started for the JMXConnectorServer should be started as daemon threads
 #
 daemon = true
@@ -109,6 +129,11 @@ threaded = true
 objectName = connector:name=rmi
 
 #
+# The ObjectName used to register the JMXMP connector
+#
+jmxmpObjectName = connector:name=jmxmp
+
+#
 # Role name used for JMX access authorization
 #
 # jmxRole=admin
@@ -118,6 +143,8 @@ objectName = connector:name=rmi
 * `rmiServerPort` property contains the port number of the JMX RMI server. Default is `44444`.
 * `jmxRealm` is the security realm to use as authentication backend. By default it uses the `karaf` realm.
 
+By default, Karaf exposes JMX using RMI. You can also enable JMXMP connector by settings `jmxmpEnabled=true`. You can then configure the JMXMP connector using the corresponding `jmxmp*` properties.
+
 ==== MBeans
 
 Apache Karaf provides a bunch of MBeans.