Repository: activemq-artemis
Updated Branches:
  refs/heads/master d0f3f6719 -> b09ea433b


http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/MBeanServerFactory.java
----------------------------------------------------------------------
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/MBeanServerFactory.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/MBeanServerFactory.java
new file mode 100644
index 0000000..65ca9be
--- /dev/null
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/MBeanServerFactory.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.core.server.management;
+
+import javax.management.MBeanServer;
+import java.lang.management.ManagementFactory;
+import java.util.List;
+
+public class MBeanServerFactory {
+
+   private boolean locateExistingServerIfPossible = false;
+   private String defaultDomain;
+   private boolean registerWithFactory = true;
+   private MBeanServer server;
+   private boolean newlyRegistered = false;
+
+   public boolean isLocateExistingServerIfPossible() {
+      return locateExistingServerIfPossible;
+   }
+
+   public void setLocateExistingServerIfPossible(boolean 
locateExistingServerIfPossible) {
+      this.locateExistingServerIfPossible = locateExistingServerIfPossible;
+   }
+
+   public String getDefaultDomain() {
+      return defaultDomain;
+   }
+
+   public void setDefaultDomain(String defaultDomain) {
+      this.defaultDomain = defaultDomain;
+   }
+
+   public boolean isRegisterWithFactory() {
+      return registerWithFactory;
+   }
+
+   public void setRegisterWithFactory(boolean registerWithFactory) {
+      this.registerWithFactory = registerWithFactory;
+   }
+
+   public boolean isNewlyRegistered() {
+      return newlyRegistered;
+   }
+
+   public void setNewlyRegistered(boolean newlyRegistered) {
+      this.newlyRegistered = newlyRegistered;
+   }
+
+   public MBeanServer getServer() throws Exception {
+      if (this.server == null) {
+         init();
+      }
+      return server;
+   }
+
+   public void init() throws Exception {
+      if (this.locateExistingServerIfPossible) {
+         List servers = 
javax.management.MBeanServerFactory.findMBeanServer(null);
+         if (servers != null && servers.size() > 0) {
+            this.server = (MBeanServer) servers.get(0);
+         }
+         if (this.server == null) {
+            this.server = ManagementFactory.getPlatformMBeanServer();
+         }
+         if (this.server == null) {
+            throw new Exception("Unable to locate MBeanServer");
+         }
+      }
+      if (this.server == null) {
+         if (this.registerWithFactory) {
+            this.server = 
javax.management.MBeanServerFactory.createMBeanServer(this.defaultDomain);
+         } else {
+            this.server = 
javax.management.MBeanServerFactory.newMBeanServer(this.defaultDomain);
+         }
+         this.newlyRegistered = this.registerWithFactory;
+      }
+   }
+
+   public void destroy() throws Exception {
+
+      if (this.newlyRegistered) {
+         javax.management.MBeanServerFactory.releaseMBeanServer(this.server);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
----------------------------------------------------------------------
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
new file mode 100644
index 0000000..d867bd8
--- /dev/null
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.core.server.management;
+
+import org.apache.activemq.artemis.core.config.JMXConnectorConfiguration;
+import org.apache.activemq.artemis.core.server.ActiveMQComponent;
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class ManagementConnector implements ActiveMQComponent {
+
+   private final JMXConnectorConfiguration configuration;
+   private ConnectorServerFactory connectorServerFactory;
+   private RmiRegistryFactory rmiRegistryFactory;
+   private MBeanServerFactory mbeanServerFactory;
+
+   public ManagementConnector(JMXConnectorConfiguration configuration) {
+      this.configuration = configuration;
+   }
+
+   @Override
+   public boolean isStarted() {
+      return rmiRegistryFactory != null;
+   }
+
+   @Override
+   public void start() throws Exception {
+      ArtemisMBeanServerGuard guard = new ArtemisMBeanServerGuard();
+      guard.init();
+
+      rmiRegistryFactory = new RmiRegistryFactory();
+      rmiRegistryFactory.setPort(configuration.getConnectorPort());
+      rmiRegistryFactory.init();
+
+      mbeanServerFactory = new MBeanServerFactory();
+      mbeanServerFactory.setLocateExistingServerIfPossible(true);
+      mbeanServerFactory.init();
+
+      MBeanServer mbeanServer = mbeanServerFactory.getServer();
+
+      JaasAuthenticator jaasAuthenticator = new JaasAuthenticator();
+      jaasAuthenticator.setRealm(configuration.getJmxRealm());
+
+      connectorServerFactory = new ConnectorServerFactory();
+      connectorServerFactory.setServer(mbeanServer);
+      connectorServerFactory.setServiceUrl(configuration.getServiceUrl());
+      
connectorServerFactory.setRmiServerHost(configuration.getConnectorHost());
+      connectorServerFactory.setObjectName(new 
ObjectName(configuration.getObjectName()));
+      Map<String, Object> environment = new HashMap<>();
+      environment.put("jmx.remote.authenticator", jaasAuthenticator);
+      try {
+         connectorServerFactory.setEnvironment(environment);
+         
connectorServerFactory.setAuthenticatorType(configuration.getAuthenticatorType());
+         connectorServerFactory.setSecured(configuration.isSecured());
+         
connectorServerFactory.setKeyStorePath(configuration.getKeyStorePath());
+         
connectorServerFactory.setkeyStoreProvider(configuration.getKeyStoreProvider());
+         
connectorServerFactory.setKeyStorePassword(configuration.getKeyStorePassword());
+         
connectorServerFactory.setTrustStorePath(configuration.getTrustStorePath());
+         
connectorServerFactory.setTrustStoreProvider(configuration.getTrustStoreProvider());
+         
connectorServerFactory.setTrustStorePassword(configuration.getTrustStorePassword());
+         connectorServerFactory.init();
+      } catch (Exception e) {
+         ActiveMQServerLogger.LOGGER.error("Can't init JMXConnectorServer: " + 
e.getMessage());
+      }
+   }
+
+   @Override
+   public void stop() {
+      if (connectorServerFactory != null) {
+         try {
+            connectorServerFactory.destroy();
+         } catch (Exception e) {
+            ActiveMQServerLogger.LOGGER.warn("Error destroying 
ConnectorServerFactory", e);
+         }
+         connectorServerFactory = null;
+      }
+      if (mbeanServerFactory != null) {
+         try {
+            mbeanServerFactory.destroy();
+         } catch (Exception e) {
+            ActiveMQServerLogger.LOGGER.warn("Error destroying 
MBeanServerFactory", e);
+         }
+         mbeanServerFactory = null;
+      }
+      if (rmiRegistryFactory != null) {
+         try {
+            rmiRegistryFactory.destroy();
+         } catch (Exception e) {
+            ActiveMQServerLogger.LOGGER.warn("Error destroying 
RMIRegistryFactory", e);
+         }
+         rmiRegistryFactory = null;
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
----------------------------------------------------------------------
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
new file mode 100644
index 0000000..3ff834e
--- /dev/null
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.core.server.management;
+
+
+import org.apache.activemq.artemis.core.config.JMXConnectorConfiguration;
+import org.apache.activemq.artemis.core.server.ActiveMQComponent;
+
+public class ManagementContext implements ActiveMQComponent {
+   private boolean isStarted = false;
+   private JMXAccessControlList accessControlList;
+   private JMXConnectorConfiguration jmxConnectorConfiguration;
+   private ManagementConnector mBeanServer;
+
+   @Override
+   public void start() throws Exception {
+      if (accessControlList != null) {
+         //if we are configured then assume we want to use the guard so set 
the system property
+         System.setProperty("javax.management.builder.initial", 
ArtemisMBeanServerBuilder.class.getCanonicalName());
+         ArtemisMBeanServerGuard guardHandler = new ArtemisMBeanServerGuard();
+         guardHandler.setJMXAccessControlList(accessControlList);
+         ArtemisMBeanServerBuilder.setGuard(guardHandler);
+      }
+
+      if (jmxConnectorConfiguration != null) {
+         mBeanServer = new ManagementConnector(jmxConnectorConfiguration);
+         mBeanServer.start();
+      }
+      isStarted = true;
+   }
+
+   @Override
+   public void stop() throws Exception {
+      isStarted = false;
+      if (mBeanServer != null) {
+         mBeanServer.stop();
+      }
+   }
+
+   @Override
+   public boolean isStarted() {
+      return isStarted;
+   }
+
+   public void setAccessControlList(JMXAccessControlList accessControlList) {
+      this.accessControlList = accessControlList;
+   }
+
+   public JMXAccessControlList getAccessControlList() {
+      return accessControlList;
+   }
+
+   public void setJmxConnectorConfiguration(JMXConnectorConfiguration 
jmxConnectorConfiguration) {
+      this.jmxConnectorConfiguration = jmxConnectorConfiguration;
+   }
+
+   public JMXConnectorConfiguration getJmxConnectorConfiguration() {
+      return jmxConnectorConfiguration;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/RmiRegistryFactory.java
----------------------------------------------------------------------
diff --git 
a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/RmiRegistryFactory.java
 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/RmiRegistryFactory.java
new file mode 100644
index 0000000..4c5cab2
--- /dev/null
+++ 
b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/RmiRegistryFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.core.server.management;
+
+import java.net.UnknownHostException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+public class RmiRegistryFactory {
+
+   private int port = Registry.REGISTRY_PORT;
+   private Registry registry;
+   /**
+    * @return the port
+    */
+   public int getPort() {
+      return port;
+   }
+
+   /**
+    * @param port the port to set
+    */
+   public void setPort(int port) {
+      this.port = port;
+   }
+
+   public Object getObject() throws Exception {
+      return registry;
+   }
+
+   public void init() throws RemoteException, UnknownHostException {
+      registry = LocateRegistry.createRegistry(port);
+   }
+
+   public void destroy() throws RemoteException {
+      if (registry != null) {
+         UnicastRemoteObject.unexportObject(registry, true);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
----------------------------------------------------------------------
diff --git 
a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
new file mode 100644
index 0000000..d077f44
--- /dev/null
+++ 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/JMXAccessControlListTest.java
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.core.server.management;
+
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import java.util.List;
+
+public class JMXAccessControlListTest {
+
+   @Test
+   public void testBasicDomain() throws MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToWhiteList("org.myDomain", null);
+      controlList.addToWhiteList("org.myDomain.foo", null);
+      Assert.assertTrue(controlList.isInWhiteList(new 
ObjectName("org.myDomain:*")));
+      Assert.assertTrue(controlList.isInWhiteList(new 
ObjectName("org.myDomain.foo:*")));
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain.bar:*")));
+   }
+
+   @Test
+   public void testBasicDomainWithProperty() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToWhiteList("org.myDomain", "type=foo");
+      controlList.addToWhiteList("org.myDomain.foo", "type=bar");
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain:*")));
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain.foo:*")));
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain.bar:*")));
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain:subType=foo")));
+
+      Assert.assertTrue(controlList.isInWhiteList(new 
ObjectName("org.myDomain:type=foo")));
+      Assert.assertTrue(controlList.isInWhiteList(new 
ObjectName("org.myDomain:subType=bar,type=foo")));
+   }
+
+   @Test
+   public void testBasicDomainWithWildCardProperty() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToWhiteList("org.myDomain", "type=*");
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain:*")));
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain.foo:*")));
+      Assert.assertFalse(controlList.isInWhiteList(new 
ObjectName("org.myDomain.bar:*")));
+      Assert.assertTrue(controlList.isInWhiteList(new 
ObjectName("org.myDomain:type=foo")));
+   }
+
+   @Test
+   public void testBasicRole() throws MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", null,"listSomething", 
"admin");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:*"), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testBasicRoleWithKey() throws MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", "type=foo","listSomething", 
"admin");
+      controlList.addToRoleAccess("org.myDomain", null,"listSomething", 
"view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:type=foo"), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testBasicRoleWithKeyContainingQuotes() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", "type=foo","listSomething", 
"admin");
+      controlList.addToRoleAccess("org.myDomain", null,"listSomething", 
"view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:type=\"foo\""), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testBasicRoleWithWildcardKey() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", "type=*","listSomething", 
"admin");
+      controlList.addToRoleAccess("org.myDomain", null,"listSomething", 
"view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:type=foo"), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testMutipleBasicRoles() throws MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", null, "listSomething", 
"admin", "view", "update");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:*"), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin", "view", 
"update"});
+   }
+
+   @Test
+   public void testBasicRoleWithPrefix() throws MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", null,"list*", "admin");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:*"), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testBasicRoleWithBoth() throws MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToRoleAccess("org.myDomain", null,"listSomething", 
"admin");
+      controlList.addToRoleAccess("org.myDomain", null,"list*", "view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain:*"), "listSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+      roles = controlList.getRolesForObject(new ObjectName("org.myDomain:*"), 
"listSomethingMore");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"view"});
+   }
+
+   @Test
+   public void testBasicRoleWithDefaultsPrefix() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToDefaultAccess("setSomething","admin");
+      controlList.addToRoleAccess("org.myDomain", null,"list*", "view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain.foo:*"), "setSomething");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testBasicRoleWithDefaultsWildcardPrefix() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToDefaultAccess("setSomething","admin");
+      controlList.addToDefaultAccess("set*","admin");
+      controlList.addToRoleAccess("org.myDomain", null,"list*", "view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain.foo:*"), "setSomethingMore");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+
+   @Test
+   public void testBasicRoleWithDefaultscatchAllPrefix() throws 
MalformedObjectNameException {
+      JMXAccessControlList controlList = new JMXAccessControlList();
+      controlList.addToDefaultAccess("setSomething","admin");
+      controlList.addToDefaultAccess("*","admin");
+      controlList.addToRoleAccess("org.myDomain", null,"list*", "view");
+      List<String> roles = controlList.getRolesForObject(new 
ObjectName("org.myDomain.foo:*"), "setSomethingMore");
+      Assert.assertArrayEquals(roles.toArray(), new String[]{"admin"});
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/docs/user-manual/en/management.md
----------------------------------------------------------------------
diff --git a/docs/user-manual/en/management.md 
b/docs/user-manual/en/management.md
index b25a88d..9587d05 100644
--- a/docs/user-manual/en/management.md
+++ b/docs/user-manual/en/management.md
@@ -296,16 +296,132 @@ setting `jmx-management-enabled` to `false` in
 
     <!-- false to disable JMX management for Apache ActiveMQ Artemis -->
     <jmx-management-enabled>false</jmx-management-enabled>
+    
+#### Role Based Authentication with JMX
 
-If JMX is enabled, Apache ActiveMQ Artemis can be managed locally using 
`jconsole`.
+Although by default Artemis uses the Java Virtual Machine's `Platform 
MBeanServer` 
+this is guarded using role based authentication that leverages Artemis's JAAS 
plugin support.
+This is configured via the `authorisation` element in the `management.xml` 
configuration file
+and can be used to restrict access to attributes and methods on mbeans.
+
+There are 3 elements within the `authorisation` element, `whitelist`, 
`default-access` and 
+`role-access`, Lets discuss each in turn.
+
+Whitelist contains a list of mBeans that will by pass the authentication, this 
is typically
+used for any mbeans that are needed by the console to run etc. The default 
configuration is:
+
+```xml
+<whitelist>
+   <entry domain="hawtio"/>
+</whitelist>
+```
+This means that any mbean with the domain `hawtio` will be allowed access 
without authorisation. for instance 
+`hawtio:plugin=artemis`. You can also use wildcards for the mBean properties 
so the following would also match.
+
+```xml
+<whitelist>
+   <entry domain="hawtio" key="type=*"/>
+</whitelist>
+```
+
+The `role-access`defines how roles are mapped to particular mBeans and its 
attributes and methods, 
+the default configuration looks like:
+
+```xml 
+<role-access>
+    <match domain="org.apache.activemq.apache">
+       <access method="list*" roles="view,update,amq"/>
+       <access method="get*" roles="view,update,amq"/>
+       <access method="is*" roles="view,update,amq"/>
+       <access method="set*" roles="update,amq"/>
+       <access method="*" roles="amq"/>
+    </match>
+</role-access>
+```
+This contains 1 match and will be applied to any mBean that has the domain 
`org.apache.activemq.apache`.
+Any access to any mBeans that have this domain are controlled by the `access` 
elements which contain a
+method and a set of roles. The method being invoked will be used to pick the 
closest matching method and
+the roles for this will be applied for access. For instance if you try the 
invoke a method called `listMessages` on an mBean 
+with the `org.apache.activemq.apache` domain then this would match the 
`access` with the method of `list*`.
+You could also explicitly configure this by using the full method name, like 
so:
+
+```xml
+<access method="listMessages" roles="view,update,amq"/>
+```
+You can also match specific mBeans within a domain by adding a key attribute 
that is used to match one of the properties
+on the mBean, like:
+
+```xml
+<match domain="org.apache.activemq.apache" key="subcomponent=queues">
+   <access method="list*" roles="view,update,amq"/>
+   <access method="get*" roles="view,update,amq"/>
+   <access method="is*" roles="view,update,amq"/>
+   <access method="set*" roles="update,amq"/>
+   <access method="*" roles="amq"/>
+</match>
+```
+You could also match a specific queue for instance :
+
+`org.apache.activemq.artemis:broker=<brokerName>,component=addresses,address="exampleAddress",subcomponent=queues,routing-type="anycast",queue="exampleQueue"`
+
+by configuring:
+
+```xml
+<match domain="org.apache.activemq.apache" key="queue=exampleQueue">
+   <access method="list*" roles="view,update,amq"/>
+   <access method="get*" roles="view,update,amq"/>
+   <access method="is*" roles="view,update,amq"/>
+   <access method="set*" roles="update,amq"/>
+   <access method="*" roles="amq"/>
+</match>
+```
+
+Access to JMX mBean attributes are converted to method calls so these are 
controlled via the `set*`, `get*` and `is*`. 
+The `*` access is the catch all for everything other method that isnt 
specifically matched.
+
+The `default-access` element is basically the catch all for every method call 
that isn't handled via the `role-access` configuration.
+This has teh same semantics as a `match` element.
+
+> **Note**
+>
+> If JMX is enabled, Apache ActiveMQ Artemis can *not* be managed locally 
using `jconsole` when connecting as a local process,
+> this is because jconsole does not using any authentication when connecting 
this way. If you want to use jconsole you will
+either have to disable authentication, by removing the `authentication` 
element or enable remote access.
+ 
+#### Configuring remote JMX Access
+
+By default remote JMX access to Artemis is disabled for security reasons.
+
+Artemis has a JMX agent which allows access to JMX mBeans remotely. This is 
configured via the `connector` element in the
+`management.xml` configuration file. To enable this you simpl ad the following 
xml:
+
+```xml
+<connector connector-port="1099"/>
+```
+
+This exposes the agent remotely on the port 1099. If you were connecting via 
jconsole you would connect as a remote process
+using the service url `service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi` 
and an appropriate user name and password.
+
+You can also configure the connector using the following:
+
+-  connector-host The host to expose the agent on
+-  connector-port The port to expose the agent on 
+-  jmx-realm The jmx realm to use for authentication, defaults to `activemq` 
to match the JAAS configuration.
+-  object-name The object name to expose the remote connector on, default is 
`connector:name=rmi`
 
 > **Note**
 >
-> Remote connections to JMX are not enabled by default for security
-> reasons. Please refer to [Java Management
-> 
guide](http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html)
-> to configure the server for remote management (system properties must
-> be set in `artemis.profile`).
+> It is important to note that the rmi registry will pick an ip address to 
bind to, If you have a multi IP addresses/NICs  
+> present on the system then you can choose the ip address to use by adding 
the following to artemis.profile 
+> `-Djava.rmi.server.hostname=localhost`
+
+> **Note**
+>
+> Remote connections using the default JVM Agent not enabled by default as 
Artemis exposes the mBean Server via its own configuration. 
+> This is so Artemis can leverage the JAAS authentication layer via JMX. If 
you want to expose this then you will need to 
+> disable both the connector and the authorisation by removing them from the 
`management.xml` configuration.
+> Please refer to [Java Management 
guide](http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html)
+> to configure the server for remote management (system properties must be set 
in `artemis.profile`).
 
 By default, Apache ActiveMQ Artemis server uses the JMX domain 
"org.apache.activemq.artemis".
 To manage several Apache ActiveMQ Artemis servers from the *same* MBeanServer, 
the JMX
@@ -315,12 +431,6 @@ domain can be configured for each individual Apache 
ActiveMQ Artemis server by s
     <!-- use a specific JMX domain for ActiveMQ Artemis MBeans -->
     <jmx-domain>my.org.apache.activemq</jmx-domain>
 
-#### MBeanServer configuration
-
-When Apache ActiveMQ Artemis is run in standalone, it uses the Java Virtual 
Machine's
-`Platform MBeanServer` to register its MBeans. By default, 
[Jolokia](http://www.jolokia.org/)
-is also deployed to allow access to the MBean server via 
[REST](https://en.wikipedia.org/wiki/Representational_state_transfer).
-
 ### Example
 
 See the [Examples](examples.md) chapter for an example which shows how to use 
a remote connection to JMX
@@ -333,12 +443,23 @@ HTTP agent deployed as a Web Application. Jolokia is a 
remote
 JMX-over-HTTP bridge that exposes MBeans. For a full guide as
 to how to use it refer to [Jolokia 
Documentation](http://www.jolokia.org/documentation.html),
 however a simple example to query the broker's version would
-be to use a browser and go to the URL 
[http://localhost:8161/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/Version]().
+be to use a browser and go to the URL 
[http://username:password@localhost:8161/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/Version]().
 
 This would give you back something like the following:
 
     
{"request":{"mbean":"org.apache.activemq.artemis:broker=\"0.0.0.0\"","attribute":"Version","type":"read"},"value":"2.0.0-SNAPSHOT","timestamp":1487017918,"status":200}
 
+### JMX and the Console
+
+The console that ships with Artemis uses Jolokia under the covers which in 
turn uses JMX. This will use the authentication
+configuration in the `management.xml` file as described in the previous 
section. This means that when mBeans are accessed
+via the console the credentials used to log into the console and the roles 
associated with them. By default access to the 
+console is only allow via users with the amq role. This is configured in the 
`artemis.profile` via the system property `-Dhawtio.role=amq`.
+You can configure multiple roles by changing this to 
`-Dhawtio.roles=amq,view,update`.
+
+If a user doesn't have the correct role to invoke a specific operation then 
this will display an authorisation exception 
+in the console. 
+
 ## Using Management Via Apache ActiveMQ Artemis API
 
 The management API in ActiveMQ Artemis is accessed by sending Core Client 
messages

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/examples/features/standard/jmx/pom.xml
----------------------------------------------------------------------
diff --git a/examples/features/standard/jmx/pom.xml 
b/examples/features/standard/jmx/pom.xml
index bb168d6..dae00fd 100644
--- a/examples/features/standard/jmx/pom.xml
+++ b/examples/features/standard/jmx/pom.xml
@@ -72,6 +72,7 @@ under the License.
                      <goal>cli</goal>
                   </goals>
                   <configuration>
+                     <debug>true</debug>
                      <ignore>${noServer}</ignore>
                      <spawn>true</spawn>
                      <testURI>tcp://localhost:61616</testURI>

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/examples/features/standard/jmx/readme.html
----------------------------------------------------------------------
diff --git a/examples/features/standard/jmx/readme.html 
b/examples/features/standard/jmx/readme.html
index 2dfc466..fe981c6 100644
--- a/examples/features/standard/jmx/readme.html
+++ b/examples/features/standard/jmx/readme.html
@@ -34,16 +34,12 @@ under the License.
      <h2>Example configuration</h2>
 
      <p>ActiveMQ Artemis exposes its managed resources by default on the 
platform MBeanServer.</p>
-     <p>To access this MBeanServer remotely, the Java Virtual machine must be 
started with system properties:
+     <p>To access this MBeanServer remotely, add the following to the 
management.xml configuration:
          <pre class="prettyprint">
-             <code>-Dcom.sun.management.jmxremote
-             -Dcom.sun.management.jmxremote.port=3000
-             -Dcom.sun.management.jmxremote.ssl=false
-             -Dcom.sun.management.jmxremote.authenticate=false</code>
-        </pre>
-        <p>These properties are explained in the Java <a 
href="http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html#gdenl";>management
 guide</a>
-            (please note that for this example, we will disable user 
authentication for simplicity sake).</p>
-        <p>With these properties, ActiveMQ Artemis server will be manageable 
remotely using standard JMX URL on port <code>3000</code>.</p>
+             <code><connector connector-port="1099" 
connector-host="127.0.0.1"/></code>
+        </pre>
+        <p>If the example does not work then try changing the host to the ip 
address of the machine running this example on</p>
+        <p>With these properties, ActiveMQ Artemis server will be manageable 
remotely using standard JMX URL on port <code>1099</code>.</p>
      </p>
 
      <h2>Example step-by-step</h2>

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/examples/features/standard/jmx/src/main/java/org/apache/activemq/artemis/jms/example/JMXExample.java
----------------------------------------------------------------------
diff --git 
a/examples/features/standard/jmx/src/main/java/org/apache/activemq/artemis/jms/example/JMXExample.java
 
b/examples/features/standard/jmx/src/main/java/org/apache/activemq/artemis/jms/example/JMXExample.java
index 73658ed..e8beee9 100644
--- 
a/examples/features/standard/jmx/src/main/java/org/apache/activemq/artemis/jms/example/JMXExample.java
+++ 
b/examples/features/standard/jmx/src/main/java/org/apache/activemq/artemis/jms/example/JMXExample.java
@@ -44,7 +44,7 @@ import 
org.apache.activemq.artemis.jms.client.ActiveMQTextMessage;
  */
 public class JMXExample {
 
-   private static final String JMX_URL = 
"service:jmx:rmi:///jndi/rmi://localhost:3000/jmxrmi";
+   private static final String JMX_URL = 
"service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi";
 
    public static void main(final String[] args) throws Exception {
       QueueConnection connection = null;
@@ -79,7 +79,11 @@ public class JMXExample {
          ObjectName on = 
ObjectNameBuilder.DEFAULT.getQueueObjectName(SimpleString.toSimpleString(queue.getQueueName()),
 SimpleString.toSimpleString(queue.getQueueName()), RoutingType.ANYCAST);
 
          // Step 10. Create JMX Connector to connect to the server's 
MBeanServer
-         JMXConnector connector = JMXConnectorFactory.connect(new 
JMXServiceURL(JMXExample.JMX_URL), new HashMap());
+         HashMap env = new HashMap();
+         String[] creds = {"admin", "password"};
+         env.put(JMXConnector.CREDENTIALS, creds);
+
+         JMXConnector connector = JMXConnectorFactory.connect(new 
JMXServiceURL(JMXExample.JMX_URL), env);
 
          // Step 11. Retrieve the MBeanServerConnection
          MBeanServerConnection mbsc = connector.getMBeanServerConnection();

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/examples/features/standard/jmx/src/main/resources/activemq/server0/management.xml
----------------------------------------------------------------------
diff --git 
a/examples/features/standard/jmx/src/main/resources/activemq/server0/management.xml
 
b/examples/features/standard/jmx/src/main/resources/activemq/server0/management.xml
new file mode 100644
index 0000000..cbcd3d8
--- /dev/null
+++ 
b/examples/features/standard/jmx/src/main/resources/activemq/server0/management.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<management-context xmlns="http://activemq.org/schema";>
+   <connector connector-port="1099" connector-host="127.0.0.1"/>
+   <authorisation>
+      <whitelist>
+         <entry domain="hawtio"/>
+      </whitelist>
+      <default-access>
+         <access method="list*" roles="view,update,amq"/>
+         <access method="get*" roles="view,update,amq"/>
+         <access method="is*" roles="view,update,amq"/>
+         <access method="set*" roles="update,amq"/>
+         <access method="*" roles="amq"/>
+      </default-access>
+      <role-access>
+         <match domain="org.apache.activemq.apache">
+            <access method="list*" roles="view,update,amq"/>
+            <access method="get*" roles="view,update,amq"/>
+            <access method="is*" roles="view,update,amq"/>
+            <access method="set*" roles="update,amq"/>
+            <access method="*" roles="amq"/>
+         </match>
+         <!--example of how to configure a specific object-->
+         <!--<match domain="org.apache.activemq.apache" 
key="subcomponent=queues">
+            <access method="list*" roles="view,update,amq"/>
+            <access method="get*" roles="view,update,amq"/>
+            <access method="is*" roles="view,update,amq"/>
+            <access method="set*" roles="update,amq"/>
+            <access method="*" roles="amq"/>
+         </match>-->
+      </role-access>
+   </authorisation>
+</management-context>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/DummyLoginModule.java
----------------------------------------------------------------------
diff --git 
a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/DummyLoginModule.java
 
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/DummyLoginModule.java
new file mode 100644
index 0000000..1ecf824
--- /dev/null
+++ 
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/DummyLoginModule.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.activemq.artemis.tests.integration.management;
+
+
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+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.LoginException;
+import javax.security.auth.spi.LoginModule;
+import java.io.IOException;
+import java.util.Map;
+
+public class DummyLoginModule implements LoginModule {
+   private Subject subject;
+   private CallbackHandler callbackHandler;
+   private Map<String, ?> sharedState;
+   private Map<String, ?> options;
+
+   @Override
+   public void initialize(Subject subject, CallbackHandler callbackHandler, 
Map<String, ?> sharedState, Map<String, ?> options) {
+      this.subject = subject;
+      this.callbackHandler = callbackHandler;
+      this.sharedState = sharedState;
+      this.options = options;
+      System.out.println("DummyLoginModule.initialize");
+   }
+
+   @Override
+   public boolean login() throws LoginException {
+      Callback[] callbacks = new Callback[2];
+
+      callbacks[0] = new NameCallback("Username: ");
+      callbacks[1] = new PasswordCallback("Password: ", false);
+      try {
+         callbackHandler.handle(callbacks);
+      } catch (IOException ioe) {
+         throw new LoginException(ioe.getMessage());
+      } catch (UnsupportedCallbackException uce) {
+         throw new LoginException(uce.getMessage() + " not available to obtain 
information from user");
+      }
+      String user = ((NameCallback) callbacks[0]).getName();
+      char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
+      if (tmpPassword == null) {
+         tmpPassword = new char[0];
+      }
+      if (user == null) {
+         throw new FailedLoginException("User is null");
+      }
+      subject.getPrincipals().add(new RolePrincipal("amq"));
+      // String password = users.getProperty(user);
+
+     /*if (password == null) {
+        throw new FailedLoginException("User does not exist: " + user);
+     }*/
+
+      return true;
+   }
+
+   @Override
+   public boolean commit() throws LoginException {
+      return true;
+   }
+
+   @Override
+   public boolean abort() throws LoginException {
+      return false;
+   }
+
+   @Override
+   public boolean logout() throws LoginException {
+      return false;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/62a2b14d/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementTestBase.java
----------------------------------------------------------------------
diff --git 
a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementTestBase.java
 
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementTestBase.java
index ac09745..8836ba8 100644
--- 
a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementTestBase.java
+++ 
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementTestBase.java
@@ -76,6 +76,10 @@ public abstract class ManagementTestBase extends 
ActiveMQTestBase {
    public void setUp() throws Exception {
       super.setUp();
 
+      createMBeanServer();
+   }
+
+   protected void createMBeanServer() {
       mbeanServer = MBeanServerFactory.createMBeanServer();
    }
 

Reply via email to