summaryrefslogtreecommitdiff
path: root/qpid/java/broker-plugins
diff options
context:
space:
mode:
authorAlex Rudyy <orudyy@apache.org>2013-02-19 09:35:28 +0000
committerAlex Rudyy <orudyy@apache.org>2013-02-19 09:35:28 +0000
commita973713561140fe7395368ae53def8f7edfa18a3 (patch)
tree7bda80afada592df681fb73908400e7a189f015f /qpid/java/broker-plugins
parent1b0f1d06188e73e9440dc1789c28ee65e24d539d (diff)
downloadqpid-python-a973713561140fe7395368ae53def8f7edfa18a3.tar.gz
QPID-4390: Introduce a configuration store in java broker allowing runtime modifications and replace existing xml file configuration with json configuration store
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1447646 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker-plugins')
-rw-r--r--qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java4
-rw-r--r--qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java12
-rw-r--r--qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java50
-rw-r--r--qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java2
-rw-r--r--qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java42
-rw-r--r--qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java22
-rw-r--r--qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java69
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java328
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java31
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java52
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java13
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java6
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java31
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java14
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java19
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java5
-rw-r--r--qpid/java/broker-plugins/management-http/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.PluginFactory (renamed from qpid/java/broker-plugins/management-http/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ManagementFactory)0
-rw-r--r--qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementFactoryTest.java56
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/CustomRMIServerSocketFactory.java68
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java380
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java188
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagementFactory.java21
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java87
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporter.java95
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameAccessor.java26
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameCachingRMIJRMPServer.java100
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java66
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.PluginFactory (renamed from qpid/java/broker-plugins/management-jmx/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ManagementFactory)0
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/JMXManagementFactoryTest.java29
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogActorTest.java173
-rw-r--r--qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporterTest.java101
31 files changed, 1268 insertions, 822 deletions
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java
index 44c48523e2..f87374ac80 100644
--- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java
+++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java
@@ -22,8 +22,6 @@ package org.apache.qpid.server.security.access.config;
import java.io.File;
-import org.apache.commons.configuration.ConfigurationException;
-
public abstract class AbstractConfiguration implements ConfigurationFile
{
private File _file;
@@ -39,7 +37,7 @@ public abstract class AbstractConfiguration implements ConfigurationFile
return _file;
}
- public RuleSet load() throws ConfigurationException
+ public RuleSet load()
{
_config = new RuleSet();
return _config;
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java
index 8b1a00259b..966c32e24e 100644
--- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java
+++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ConfigurationFile.java
@@ -22,7 +22,7 @@ package org.apache.qpid.server.security.access.config;
import java.io.File;
-import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
public interface ConfigurationFile
{
@@ -33,19 +33,17 @@ public interface ConfigurationFile
/**
* Load this configuration file's contents into a {@link RuleSet}.
- *
- * @throws ConfigurationException if the configuration file has errors.
+ * @throws IllegalConfigurationException if the configuration file has errors.
* @throws IllegalArgumentException if individual tokens cannot be parsed.
*/
- RuleSet load() throws ConfigurationException;
+ RuleSet load() throws IllegalConfigurationException;
/**
* Reload this configuration file's contents.
- *
- * @throws ConfigurationException if the configuration file has errors.
+ * @throws IllegalConfigurationException if the configuration file has errors.
* @throws IllegalArgumentException if individual tokens cannot be parsed.
*/
- RuleSet reload() throws ConfigurationException;
+ RuleSet reload() throws IllegalConfigurationException;
RuleSet getConfiguration();
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java
index 86f8ca3217..ab309c54ce 100644
--- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java
+++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java
@@ -32,9 +32,9 @@ import java.util.List;
import java.util.Map;
import java.util.Stack;
-import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.security.access.ObjectType;
import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.access.Permission;
@@ -73,7 +73,7 @@ public class PlainConfiguration extends AbstractConfiguration
}
@Override
- public RuleSet load() throws ConfigurationException
+ public RuleSet load()
{
RuleSet ruleSet = super.load();
@@ -127,7 +127,7 @@ public class PlainConfiguration extends AbstractConfiguration
stack.removeElementAt(0);
if (stack.isEmpty())
{
- throw new ConfigurationException(String.format(NOT_ENOUGH_TOKENS_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(NOT_ENOUGH_TOKENS_MSG, getLine()));
}
// check for and parse optional initial number for ACL lines
@@ -148,7 +148,7 @@ public class PlainConfiguration extends AbstractConfiguration
{
if(StringUtils.equalsIgnoreCase("GROUP", first))
{
- throw new ConfigurationException(String.format("GROUP keyword not supported. Groups should defined via a Group Provider, not in the ACL file.", getLine()));
+ throw new IllegalConfigurationException(String.format("GROUP keyword not supported. Groups should defined via a Group Provider, not in the ACL file.", getLine()));
}
else if (StringUtils.equalsIgnoreCase(CONFIG, first))
{
@@ -156,12 +156,12 @@ public class PlainConfiguration extends AbstractConfiguration
}
else
{
- throw new ConfigurationException(String.format(UNRECOGNISED_INITIAL_MSG, first, getLine()));
+ throw new IllegalConfigurationException(String.format(UNRECOGNISED_INITIAL_MSG, first, getLine()));
}
}
else
{
- throw new ConfigurationException(String.format(NUMBER_NOT_ALLOWED_MSG, first, getLine()));
+ throw new IllegalConfigurationException(String.format(NUMBER_NOT_ALLOWED_MSG, first, getLine()));
}
// reset stack, start next line
@@ -183,7 +183,7 @@ public class PlainConfiguration extends AbstractConfiguration
}
// invalid location for continuation character (add one to line beacuse we ate the EOL)
- throw new ConfigurationException(String.format(PREMATURE_CONTINUATION_MSG, getLine() + 1));
+ throw new IllegalConfigurationException(String.format(PREMATURE_CONTINUATION_MSG, getLine() + 1));
}
else if (_st.ttype == '\'' || _st.ttype == '"')
{
@@ -198,20 +198,20 @@ public class PlainConfiguration extends AbstractConfiguration
if (!stack.isEmpty())
{
- throw new ConfigurationException(String.format(PREMATURE_EOF_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PREMATURE_EOF_MSG, getLine()));
}
}
catch (IllegalArgumentException iae)
{
- throw new ConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, getLine()), iae);
+ throw new IllegalConfigurationException(String.format(PARSE_TOKEN_FAILED_MSG, getLine()), iae);
}
catch (FileNotFoundException fnfe)
{
- throw new ConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, file.getName()), fnfe);
+ throw new IllegalConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, file.getName()), fnfe);
}
catch (IOException ioe)
{
- throw new ConfigurationException(String.format(CANNOT_LOAD_MSG, file.getName()), ioe);
+ throw new IllegalConfigurationException(String.format(CANNOT_LOAD_MSG, file.getName()), ioe);
}
finally
{
@@ -223,7 +223,7 @@ public class PlainConfiguration extends AbstractConfiguration
}
catch (IOException e)
{
- throw new ConfigurationException(String.format(CANNOT_CLOSE_MSG, file.getName()), e);
+ throw new IllegalConfigurationException(String.format(CANNOT_CLOSE_MSG, file.getName()), e);
}
}
}
@@ -232,11 +232,11 @@ public class PlainConfiguration extends AbstractConfiguration
return ruleSet;
}
- private void parseAcl(Integer number, List<String> args) throws ConfigurationException
+ private void parseAcl(Integer number, List<String> args)
{
if (args.size() < 3)
{
- throw new ConfigurationException(String.format(NOT_ENOUGH_ACL_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(NOT_ENOUGH_ACL_MSG, getLine()));
}
Permission permission = Permission.parse(args.get(0));
@@ -245,7 +245,7 @@ public class PlainConfiguration extends AbstractConfiguration
if (number != null && !getConfiguration().isValidNumber(number))
{
- throw new ConfigurationException(String.format(BAD_ACL_RULE_NUMBER_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(BAD_ACL_RULE_NUMBER_MSG, getLine()));
}
if (args.size() == 3)
@@ -261,11 +261,11 @@ public class PlainConfiguration extends AbstractConfiguration
}
}
- private void parseConfig(List<String> args) throws ConfigurationException
+ private void parseConfig(List<String> args)
{
if (args.size() < 3)
{
- throw new ConfigurationException(String.format(NOT_ENOUGH_CONFIG_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(NOT_ENOUGH_CONFIG_MSG, getLine()));
}
Map<String, Boolean> properties = toPluginProperties(args);
@@ -273,7 +273,7 @@ public class PlainConfiguration extends AbstractConfiguration
getConfiguration().configure(properties);
}
- private AclRulePredicates toRulePredicates(List<String> args) throws ConfigurationException
+ private AclRulePredicates toRulePredicates(List<String> args)
{
AclRulePredicates predicates = new AclRulePredicates();
Iterator<String> i = args.iterator();
@@ -282,15 +282,15 @@ public class PlainConfiguration extends AbstractConfiguration
String key = i.next();
if (!i.hasNext())
{
- throw new ConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
}
if (!"=".equals(i.next()))
{
- throw new ConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
}
if (!i.hasNext())
{
- throw new ConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine()));
}
String value = i.next();
@@ -300,7 +300,7 @@ public class PlainConfiguration extends AbstractConfiguration
}
/** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */
- protected Map<String, Boolean> toPluginProperties(List<String> args) throws ConfigurationException
+ protected Map<String, Boolean> toPluginProperties(List<String> args)
{
Map<String, Boolean> properties = new HashMap<String, Boolean>();
Iterator<String> i = args.iterator();
@@ -309,15 +309,15 @@ public class PlainConfiguration extends AbstractConfiguration
String key = i.next().toLowerCase();
if (!i.hasNext())
{
- throw new ConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PROPERTY_KEY_ONLY_MSG, getLine()));
}
if (!"=".equals(i.next()))
{
- throw new ConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PROPERTY_NO_EQUALS_MSG, getLine()));
}
if (!i.hasNext())
{
- throw new ConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine()));
+ throw new IllegalConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine()));
}
// parse property value and save
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java
index 19b9135ea6..6f7885da94 100644
--- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java
+++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java
@@ -45,7 +45,7 @@ public class DefaultAccessControl implements AccessControl
private RuleSet _ruleSet;
- public DefaultAccessControl(String fileName) throws ConfigurationException
+ public DefaultAccessControl(String fileName)
{
if (_logger.isDebugEnabled())
{
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java
index 38ea61357e..a3d7823caf 100644
--- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java
+++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java
@@ -20,28 +20,40 @@
*/
package org.apache.qpid.server.security.access.plugins;
-import org.apache.commons.configuration.Configuration;
-import org.apache.commons.configuration.ConfigurationException;
+import java.io.File;
+import java.util.Map;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.plugin.AccessControlFactory;
import org.apache.qpid.server.security.AccessControl;
public class DefaultAccessControlFactory implements AccessControlFactory
{
- public AccessControl createInstance(Configuration securityConfiguration)
- {
- String aclConfiguration = securityConfiguration.getString("acl");
- if(aclConfiguration == null)
- {
- return null;
- }
+ public static final String ATTRIBUTE_ACL_FILE = "aclFile";
- try
- {
- return new DefaultAccessControl(aclConfiguration);
- }
- catch (ConfigurationException e)
+ public AccessControl createInstance(Map<String, Object> aclConfiguration)
+ {
+ if (aclConfiguration != null)
{
- throw new RuntimeException("caught exception during instance creation", e);
+ Object aclFile = aclConfiguration.get(ATTRIBUTE_ACL_FILE);
+ if (aclFile != null)
+ {
+ if (aclFile instanceof String)
+ {
+ String aclPath = (String) aclFile;
+ if (!new File(aclPath).exists())
+ {
+ throw new IllegalConfigurationException("ACL file '" + aclPath + "' is not found");
+ }
+ return new DefaultAccessControl(aclPath);
+ }
+ else
+ {
+ throw new IllegalConfigurationException("Expected '" + ATTRIBUTE_ACL_FILE + "' attribute value of type String but was " + aclFile.getClass()
+ + ": " + aclFile);
+ }
+ }
}
+ return null;
}
}
diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java
index 21d8ff4400..cbfc9003c8 100644
--- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java
+++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/config/PlainConfigurationTest.java
@@ -26,7 +26,7 @@ import java.util.Map;
import junit.framework.TestCase;
-import org.apache.commons.configuration.ConfigurationException;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.security.access.ObjectProperties;
import org.apache.qpid.server.security.access.ObjectProperties.Property;
import org.apache.qpid.server.security.access.ObjectType;
@@ -73,7 +73,7 @@ public class PlainConfigurationTest extends TestCase
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.CONFIG_NOT_FOUND_MSG, "doesnotexist"), ce.getMessage());
assertTrue(ce.getCause() instanceof FileNotFoundException);
@@ -87,7 +87,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("ACL ALLOW ALL \\ ALL");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.PREMATURE_CONTINUATION_MSG, 1), ce.getMessage());
}
@@ -100,7 +100,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("ACL unparsed ALL ALL");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.PARSE_TOKEN_FAILED_MSG, 1), ce.getMessage());
assertTrue(ce.getCause() instanceof IllegalArgumentException);
@@ -115,7 +115,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("ACL ALLOW");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_ACL_MSG, 1), ce.getMessage());
}
@@ -128,7 +128,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("CONFIG");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_TOKENS_MSG, 1), ce.getMessage());
}
@@ -141,7 +141,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("INVALID");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_TOKENS_MSG, 1), ce.getMessage());
}
@@ -154,7 +154,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("ACL ALLOW adk CREATE QUEUE name");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.PROPERTY_KEY_ONLY_MSG, 1), ce.getMessage());
}
@@ -167,7 +167,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("ACL ALLOW adk CREATE QUEUE name test");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.PROPERTY_NO_EQUALS_MSG, 1), ce.getMessage());
}
@@ -180,7 +180,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("ACL ALLOW adk CREATE QUEUE name =");
fail("fail");
}
- catch (ConfigurationException ce)
+ catch (IllegalConfigurationException ce)
{
assertEquals(String.format(PlainConfiguration.PROPERTY_NO_VALUE_MSG, 1), ce.getMessage());
}
@@ -432,7 +432,7 @@ public class PlainConfigurationTest extends TestCase
writeACLConfig("GROUP group1 bob alice");
fail("Expected exception not thrown");
}
- catch(ConfigurationException e)
+ catch(IllegalConfigurationException e)
{
assertTrue(e.getMessage().contains("GROUP keyword not supported"));
}
diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java
new file mode 100644
index 0000000000..ca1f19098f
--- /dev/null
+++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java
@@ -0,0 +1,69 @@
+package org.apache.qpid.server.security.access.plugins;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.security.AccessControl;
+import org.apache.qpid.test.utils.QpidTestCase;
+import org.apache.qpid.test.utils.TestFileUtils;
+
+public class DefaultAccessControlFactoryTest extends QpidTestCase
+{
+ public void testCreateInstanceWhenAclFileIsNotPresent()
+ {
+ DefaultAccessControlFactory factory = new DefaultAccessControlFactory();
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ AccessControl acl = factory.createInstance(attributes);
+ assertNull("ACL was created without a configuration file", acl);
+ }
+
+ public void testCreateInstanceWhenAclFileIsSpecified()
+ {
+ File aclFile = TestFileUtils.createTempFile(this, ".acl", "ACL ALLOW all all");
+ DefaultAccessControlFactory factory = new DefaultAccessControlFactory();
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile.getAbsolutePath());
+ AccessControl acl = factory.createInstance(attributes);
+
+ assertNotNull("ACL was not created from acl file: " + aclFile.getAbsolutePath(), acl);
+ }
+
+ public void testCreateInstanceWhenAclFileIsSpecifiedButDoesNotExist()
+ {
+ File aclFile = new File(TMP_FOLDER, "my-non-existing-acl-" + System.currentTimeMillis());
+ assertFalse("ACL file " + aclFile.getAbsolutePath() + " actually exists but should not", aclFile.exists());
+ DefaultAccessControlFactory factory = new DefaultAccessControlFactory();
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile.getAbsolutePath());
+ try
+ {
+ factory.createInstance(attributes);
+ fail("It should not be possible to create ACL from non existing file");
+ }
+ catch (IllegalConfigurationException e)
+ {
+ assertTrue("Unexpected exception message", Pattern.matches("ACL file '.*' is not found", e.getMessage()));
+ }
+ }
+
+ public void testCreateInstanceWhenAclFileIsSpecifiedAsNonString()
+ {
+ DefaultAccessControlFactory factory = new DefaultAccessControlFactory();
+ Map<String, Object> attributes = new HashMap<String, Object>();
+ Integer aclFile = new Integer(0);
+ attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile);
+ try
+ {
+ factory.createInstance(attributes);
+ fail("It should not be possible to create ACL from Integer");
+ }
+ catch (IllegalConfigurationException e)
+ {
+ assertEquals("Unexpected exception message", "Expected '" + DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE
+ + "' attribute value of type String but was " + Integer.class + ": " + aclFile, e.getMessage());
+ }
+ }
+}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index f10b308e24..59dbc6e530 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -21,15 +21,20 @@
package org.apache.qpid.server.management.plugin;
import java.io.File;
-import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.UUID;
-import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet;
import org.apache.qpid.server.management.plugin.servlet.FileServlet;
+import org.apache.qpid.server.management.plugin.servlet.rest.AbstractServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.LogoutServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.MessageContentServlet;
@@ -46,13 +51,18 @@ import org.apache.qpid.server.model.Exchange;
import org.apache.qpid.server.model.Group;
import org.apache.qpid.server.model.GroupMember;
import org.apache.qpid.server.model.GroupProvider;
+import org.apache.qpid.server.model.KeyStore;
+import org.apache.qpid.server.model.Plugin;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.Session;
-import org.apache.qpid.server.model.Transport;
+import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.User;
import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.adapter.AbstractPluginAdapter;
+import org.apache.qpid.server.plugin.PluginFactory;
+import org.apache.qpid.server.util.MapValueConverter;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionManager;
@@ -62,98 +72,186 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
-public class HttpManagement implements ManagementPlugin
+public class HttpManagement extends AbstractPluginAdapter
{
private final Logger _logger = Logger.getLogger(HttpManagement.class);
+ // 10 minutes by default
+ public static final int DEFAULT_TIMEOUT_IN_SECONDS = 60 * 10;
+ public static final boolean DEFAULT_HTTP_BASIC_AUTHENTICATION_ENABLED = false;
+ public static final boolean DEFAULT_HTTPS_BASIC_AUTHENTICATION_ENABLED = true;
+ public static final boolean DEFAULT_HTTP_SASL_AUTHENTICATION_ENABLED = true;
+ public static final boolean DEFAULT_HTTPS_SASL_AUTHENTICATION_ENABLED = true;
+ public static final String DEFAULT_NAME = "httpManagement";
+
+ public static final String TIME_OUT = "sessionTimeout";
+ public static final String HTTP_BASIC_AUTHENTICATION_ENABLED = "httpBasicAuthenticationEnabled";
+ public static final String HTTPS_BASIC_AUTHENTICATION_ENABLED = "httpsBasicAuthenticationEnabled";
+ public static final String HTTP_SASL_AUTHENTICATION_ENABLED = "httpSaslAuthenticationEnabled";
+ public static final String HTTPS_SASL_AUTHENTICATION_ENABLED = "httpsSaslAuthenticationEnabled";
+
+ public static final String PLUGIN_TYPE = "MANAGEMENT-HTTP";
+
+ @SuppressWarnings("serial")
+ private static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableSet(new HashSet<String>(Plugin.AVAILABLE_ATTRIBUTES)
+ {{
+ add(HTTP_BASIC_AUTHENTICATION_ENABLED);
+ add(HTTPS_BASIC_AUTHENTICATION_ENABLED);
+ add(HTTP_SASL_AUTHENTICATION_ENABLED);
+ add(HTTPS_SASL_AUTHENTICATION_ENABLED);
+ add(TIME_OUT);
+ add(PluginFactory.PLUGIN_TYPE);
+ }});
+
public static final String ENTRY_POINT_PATH = "/management";
private static final String OPERATIONAL_LOGGING_NAME = "Web";
- private final Broker _broker;
- private final Collection<Server> _servers = new ArrayList<Server>();
+ @SuppressWarnings("serial")
+ public static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>()
+ {{
+ put(HTTP_BASIC_AUTHENTICATION_ENABLED, DEFAULT_HTTP_BASIC_AUTHENTICATION_ENABLED);
+ put(HTTPS_BASIC_AUTHENTICATION_ENABLED, DEFAULT_HTTPS_BASIC_AUTHENTICATION_ENABLED);
+ put(HTTP_SASL_AUTHENTICATION_ENABLED, DEFAULT_HTTP_SASL_AUTHENTICATION_ENABLED);
+ put(HTTPS_SASL_AUTHENTICATION_ENABLED, DEFAULT_HTTPS_SASL_AUTHENTICATION_ENABLED);
+ put(TIME_OUT, DEFAULT_TIMEOUT_IN_SECONDS);
+ put(NAME, DEFAULT_NAME);
+ }});
+
+ @SuppressWarnings("serial")
+ private static final Map<String, Class<?>> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Class<?>>(){{
+ put(HTTP_BASIC_AUTHENTICATION_ENABLED, Boolean.class);
+ put(HTTPS_BASIC_AUTHENTICATION_ENABLED, Boolean.class);
+ put(HTTP_SASL_AUTHENTICATION_ENABLED, Boolean.class);
+ put(HTTPS_SASL_AUTHENTICATION_ENABLED, Boolean.class);
+ put(NAME, String.class);
+ put(TIME_OUT, Integer.class);
+ put(PluginFactory.PLUGIN_TYPE, String.class);
+ }});
+
+ private final Broker _broker;
- private final String _keyStorePassword;
- private final String _keyStorePath;
- private final int _sessionTimeout;
+ private Server _server;
- public HttpManagement(Broker broker, String keyStorePath, String keyStorePassword, int sessionTimeout) throws ConfigurationException
+ public HttpManagement(UUID id, Broker broker, Map<String, Object> attributes)
{
+ super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), broker.getTaskExecutor());
_broker = broker;
- _keyStorePassword = keyStorePassword;
- _keyStorePath = keyStorePath;
- _sessionTimeout = sessionTimeout;
+ addParent(Broker.class, broker);
+ }
- Collection<Port> ports = broker.getPorts();
- int httpPort = -1, httpsPort = -1;
- for (Port port : ports)
+ @Override
+ protected boolean setState(State currentState, State desiredState)
+ {
+ if(desiredState == State.ACTIVE)
{
- if (port.getProtocols().contains(Protocol.HTTP))
- {
- if (port.getTransports().contains(Transport.TCP))
- {
- httpPort = port.getPort();
- }
- }
- if (port.getProtocols().contains(Protocol.HTTPS))
- {
- if (port.getTransports().contains(Transport.SSL))
- {
- httpsPort = port.getPort();
- }
- }
+ start();
+ return true;
+ }
+ else if(desiredState == State.STOPPED)
+ {
+ stop();
+ return true;
}
+ return false;
+ }
+
+ private void start()
+ {
+ CurrentActor.get().message(ManagementConsoleMessages.STARTUP(OPERATIONAL_LOGGING_NAME));
- if (httpPort != -1 || httpsPort != -1)
+ Collection<Port> httpPorts = getHttpPorts(_broker.getPorts());
+ _server = createServer(httpPorts);
+ try
{
- _servers.add(createServer(httpPort, httpsPort));
- if (_logger.isDebugEnabled())
- {
- _logger.debug(_servers.size() + " server(s) defined");
- }
+ _server.start();
+ logOperationalListenMessages(_server);
}
- else
+ catch (Exception e)
{
- if (_logger.isInfoEnabled())
+ throw new RuntimeException("Failed to start http management on ports " + httpPorts);
+ }
+
+ CurrentActor.get().message(ManagementConsoleMessages.READY(OPERATIONAL_LOGGING_NAME));
+ }
+
+ private void stop()
+ {
+ if (_server != null)
+ {
+ try
{
- _logger.info("Cannot create web server as neither HTTP nor HTTPS port specified");
+ _server.stop();
+ logOperationalShutdownMessage(_server);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Failed to stop http management on port " + getHttpPorts(_broker.getPorts()));
}
}
+
+ CurrentActor.get().message(ManagementConsoleMessages.STOPPED(OPERATIONAL_LOGGING_NAME));
+ }
+
+ /** Added for testing purposes */
+ Broker getBroker()
+ {
+ return _broker;
+ }
+
+ /** Added for testing purposes */
+ int getSessionTimeout()
+ {
+ return (Integer)getAttribute(TIME_OUT);
+ }
+
+ private boolean isManagementHttp(Port port)
+ {
+ return port.getProtocols().contains(Protocol.HTTP) || port.getProtocols().contains(Protocol.HTTPS);
}
@SuppressWarnings("unchecked")
- private Server createServer(int port, int sslPort) throws ConfigurationException
+ private Server createServer(Collection<Port> ports)
{
if (_logger.isInfoEnabled())
{
- _logger.info("Starting up web server on" + (port == -1 ? "" : " HTTP port " + port)
- + (sslPort == -1 ? "" : " HTTPS port " + sslPort));
+ _logger.info("Starting up web server on " + ports);
}
Server server = new Server();
-
- if (port != -1)
+ for (Port port : ports)
{
- SelectChannelConnector connector = new SelectChannelConnector();
- connector.setPort(port);
- if (sslPort != -1)
+ final Collection<Protocol> protocols = port.getProtocols();
+ Connector connector = null;
+
+ //TODO: what to do if protocol HTTP and transport SSL?
+ if (protocols.contains(Protocol.HTTP))
{
- connector.setConfidentialPort(sslPort);
+ connector = new SelectChannelConnector();
}
- server.addConnector(connector);
- }
-
- if (sslPort != -1)
- {
- checkKeyStorePath(_keyStorePath);
+ else if (protocols.contains(Protocol.HTTPS))
+ {
+ KeyStore keyStore = _broker.getDefaultKeyStore();
+ if (keyStore == null)
+ {
+ throw new IllegalConfigurationException("Key store is not configured. Cannot start management on HTTPS port without keystore");
+ }
+ String keyStorePath = (String)keyStore.getAttribute(KeyStore.PATH);
+ String keyStorePassword = keyStore.getPassword();
+ validateKeystoreParameters(keyStorePath, keyStorePassword);
- SslContextFactory factory = new SslContextFactory();
- factory.setKeyStorePath(_keyStorePath);
- factory.setKeyStorePassword(_keyStorePassword);
+ SslContextFactory factory = new SslContextFactory();
+ factory.setKeyStorePath(keyStorePath);
+ factory.setKeyStorePassword(keyStorePassword);
- SslSocketConnector connector = new SslSocketConnector(factory);
- connector.setPort(sslPort);
+ connector = new SslSocketConnector(factory);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unexpected protocol " + protocols);
+ }
+ connector.setPort(port.getPort());
server.addConnector(connector);
}
@@ -161,6 +259,10 @@ public class HttpManagement implements ManagementPlugin
root.setContextPath("/");
server.setHandler(root);
+ // set servlet context attributes for broker and configuration
+ root.getServletContext().setAttribute(AbstractServlet.ATTR_BROKER, _broker);
+ root.getServletContext().setAttribute(AbstractServlet.ATTR_MANAGEMENT, this);
+
addRestServlet(root, "broker");
addRestServlet(root, "virtualhost", VirtualHost.class);
addRestServlet(root, "authenticationprovider", AuthenticationProvider.class);
@@ -175,13 +277,13 @@ public class HttpManagement implements ManagementPlugin
addRestServlet(root, "port", Port.class);
addRestServlet(root, "session", VirtualHost.class, Connection.class, Session.class);
- root.addServlet(new ServletHolder(new StructureServlet(_broker)), "/rest/structure");
- root.addServlet(new ServletHolder(new MessageServlet(_broker)), "/rest/message/*");
- root.addServlet(new ServletHolder(new MessageContentServlet(_broker)), "/rest/message-content/*");
+ root.addServlet(new ServletHolder(new StructureServlet()), "/rest/structure");
+ root.addServlet(new ServletHolder(new MessageServlet()), "/rest/message/*");
+ root.addServlet(new ServletHolder(new MessageContentServlet()), "/rest/message-content/*");
- root.addServlet(new ServletHolder(new LogRecordsServlet(_broker)), "/rest/logrecords");
+ root.addServlet(new ServletHolder(new LogRecordsServlet()), "/rest/logrecords");
- root.addServlet(new ServletHolder(new SaslServlet(_broker)), "/rest/sasl");
+ root.addServlet(new ServletHolder(new SaslServlet()), "/rest/sasl");
root.addServlet(new ServletHolder(new DefinedFileServlet("index.html")), ENTRY_POINT_PATH);
root.addServlet(new ServletHolder(new LogoutServlet()), "/logout");
@@ -199,61 +301,34 @@ public class HttpManagement implements ManagementPlugin
final SessionManager sessionManager = root.getSessionHandler().getSessionManager();
- sessionManager.setMaxInactiveInterval(_sessionTimeout);
+ sessionManager.setMaxInactiveInterval((Integer)getAttribute(TIME_OUT));
return server;
}
private void addRestServlet(ServletContextHandler root, String name, Class<? extends ConfiguredObject>... hierarchy)
{
- root.addServlet(new ServletHolder(new RestServlet(_broker, hierarchy)), "/rest/" + name + "/*");
+ root.addServlet(new ServletHolder(new RestServlet(hierarchy)), "/rest/" + name + "/*");
}
- @Override
- public void start() throws Exception
+ private void validateKeystoreParameters(String keyStorePath, String password)
{
- CurrentActor.get().message(ManagementConsoleMessages.STARTUP(OPERATIONAL_LOGGING_NAME));
-
- for (Server server : _servers)
+ if (keyStorePath == null)
{
- server.start();
-
- logOperationalListenMessages(server);
+ throw new RuntimeException("Management SSL keystore path not defined, unable to start SSL protected HTTP connector");
}
-
- CurrentActor.get().message(ManagementConsoleMessages.READY(OPERATIONAL_LOGGING_NAME));
- }
-
- @Override
- public void stop() throws Exception
- {
- for (Server server : _servers)
+ if (password == null)
{
- logOperationalShutdownMessage(server);
-
- server.stop();
+ throw new RuntimeException("Management SSL keystore password, unable to start SSL protected HTTP connector");
}
-
- CurrentActor.get().message(ManagementConsoleMessages.STOPPED(OPERATIONAL_LOGGING_NAME));
- }
-
- private void checkKeyStorePath(String keyStorePath) throws ConfigurationException
- {
- if (keyStorePath == null)
+ File ksf = new File(keyStorePath);
+ if (!ksf.exists())
{
- throw new ConfigurationException("Management SSL keystore path not defined, unable to start SSL protected HTTP connector");
+ throw new RuntimeException("Cannot find management SSL keystore file: " + ksf);
}
- else
+ if (!ksf.canRead())
{
- File ksf = new File(keyStorePath);
- if (!ksf.exists())
- {
- throw new ConfigurationException("Cannot find management SSL keystore file: " + ksf);
- }
- if (!ksf.canRead())
- {
- throw new ConfigurationException("Cannot read management SSL keystore file: " + ksf + ". Check permissions.");
- }
+ throw new RuntimeException("Cannot read management SSL keystore file: " + ksf + ". Check permissions.");
}
}
@@ -288,27 +363,50 @@ public class HttpManagement implements ManagementPlugin
return connector instanceof SslSocketConnector ? "HTTPS" : "HTTP";
}
- /** Added for testing purposes */
- Broker getBroker()
+ private Collection<Port> getHttpPorts(Collection<Port> ports)
{
- return _broker;
+ Collection<Port> httpPorts = new HashSet<Port>();
+ for (Port port : ports)
+ {
+ if (isManagementHttp(port))
+ {
+ httpPorts.add(port);
+ }
+ }
+ return httpPorts;
}
- /** Added for testing purposes */
- String getKeyStorePassword()
+
+ @Override
+ public String getName()
{
- return _keyStorePassword;
+ return (String)getAttribute(NAME);
}
- /** Added for testing purposes */
- String getKeyStorePath()
+ @Override
+ public Collection<String> getAttributeNames()
{
- return _keyStorePath;
+ return Collections.unmodifiableCollection(AVAILABLE_ATTRIBUTES);
}
- /** Added for testing purposes */
- int getSessionTimeout()
+ public boolean isHttpsSaslAuthenticationEnabled()
{
- return _sessionTimeout;
+ return (Boolean)getAttribute(HTTPS_SASL_AUTHENTICATION_ENABLED);
}
+
+ public boolean isHttpSaslAuthenticationEnabled()
+ {
+ return (Boolean)getAttribute(HTTP_SASL_AUTHENTICATION_ENABLED);
+ }
+
+ public boolean isHttpsBasicAuthenticationEnabled()
+ {
+ return (Boolean)getAttribute(HTTPS_BASIC_AUTHENTICATION_ENABLED);
+ }
+
+ public boolean isHttpBasicAuthenticationEnabled()
+ {
+ return (Boolean)getAttribute(HTTP_BASIC_AUTHENTICATION_ENABLED);
+ }
+
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java
index 452d52d598..ccf5373234 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java
@@ -18,37 +18,24 @@
*/
package org.apache.qpid.server.management.plugin;
-import org.apache.commons.configuration.ConfigurationException;
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.configuration.ServerConfiguration;
+import java.util.Map;
+import java.util.UUID;
+
import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.plugin.ManagementFactory;
+import org.apache.qpid.server.model.Plugin;
+import org.apache.qpid.server.plugin.PluginFactory;
-public class HttpManagementFactory implements ManagementFactory
+public class HttpManagementFactory implements PluginFactory
{
- private static final Logger LOGGER = Logger.getLogger(HttpManagementFactory.class);
@Override
- public HttpManagement createInstance(ServerConfiguration configuration, Broker broker)
+ public Plugin createInstance(UUID id, Map<String, Object> attributes, Broker broker)
{
-
- if (!configuration.getHTTPManagementEnabled() && !configuration.getHTTPSManagementEnabled())
+ if (!HttpManagement.PLUGIN_TYPE.equals(attributes.get(PLUGIN_TYPE)))
{
- LOGGER.info("HttpManagement is disabled");
return null;
}
- try
- {
- return new HttpManagement(
- broker,
- configuration.getManagementKeyStorePath(),
- configuration.getManagementKeyStorePassword(),
- configuration.getHTTPManagementSessionTimeout());
- }
- catch (ConfigurationException e)
- {
- throw new RuntimeException(e);
- }
+ return new HttpManagement(id, broker, attributes);
}
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
index 2d15b8a1d9..689bdb50d8 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
@@ -28,6 +28,8 @@ import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.security.auth.Subject;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -40,9 +42,9 @@ import org.apache.qpid.server.logging.LogActor;
import org.apache.qpid.server.logging.RootMessageLogger;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.actors.HttpManagementActor;
+import org.apache.qpid.server.management.plugin.HttpManagement;
import org.apache.qpid.server.management.plugin.session.LoginLogoutReporter;
import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.security.SubjectCreator;
import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus;
@@ -53,25 +55,38 @@ public abstract class AbstractServlet extends HttpServlet
{
private static final Logger LOGGER = Logger.getLogger(AbstractServlet.class);
+ /**
+ * Servlet context attribute holding a reference to a broker instance
+ */
+ public static final String ATTR_BROKER = "Qpid.broker";
+
+ /**
+ * Servlet context attribute holding a reference to plugin configuration
+ */
+ public static final String ATTR_MANAGEMENT = "Qpid.management";
+
private static final String ATTR_LOGIN_LOGOUT_REPORTER = "AbstractServlet.loginLogoutReporter";
private static final String ATTR_SUBJECT = "AbstractServlet.subject";
private static final String ATTR_LOG_ACTOR = "AbstractServlet.logActor";
- private final Broker _broker;
-
+ private Broker _broker;
private RootMessageLogger _rootLogger;
+ private HttpManagement _httpManagement;
protected AbstractServlet()
{
super();
- _broker = ApplicationRegistry.getInstance().getBroker();
- _rootLogger = ApplicationRegistry.getInstance().getRootMessageLogger();
}
- protected AbstractServlet(Broker broker)
+ @Override
+ public void init() throws ServletException
{
- _broker = broker;
- _rootLogger = ApplicationRegistry.getInstance().getRootMessageLogger();
+ ServletConfig servletConfig = getServletConfig();
+ ServletContext servletContext = servletConfig.getServletContext();
+ _broker = (Broker)servletContext.getAttribute(ATTR_BROKER);
+ _rootLogger = _broker.getRootMessageLogger();
+ _httpManagement = (HttpManagement)servletContext.getAttribute(ATTR_MANAGEMENT);
+ super.init();
}
@Override
@@ -263,7 +278,7 @@ public abstract class AbstractServlet extends HttpServlet
return subject;
}
- SubjectCreator subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(getSocketAddress(request));
+ SubjectCreator subjectCreator = getSubjectCreator(request);
subject = authenticate(request, subjectCreator);
if (subject != null)
{
@@ -293,7 +308,7 @@ public abstract class AbstractServlet extends HttpServlet
@Override
public Void run() throws Exception
{
- boolean allowed = ApplicationRegistry.getInstance().getSecurityManager().accessManagement();
+ boolean allowed = getSecurityManager().accessManagement();
if (!allowed)
{
throw new AccessControlException("User is not authorised for management");
@@ -382,8 +397,8 @@ public abstract class AbstractServlet extends HttpServlet
private boolean isBasicAuthSupported(HttpServletRequest req)
{
- return req.isSecure() ? ApplicationRegistry.getInstance().getConfiguration().getHTTPSManagementBasicAuth()
- : ApplicationRegistry.getInstance().getConfiguration().getHTTPManagementBasicAuth();
+ return req.isSecure() ? _httpManagement.isHttpsBasicAuthenticationEnabled()
+ : _httpManagement.isHttpBasicAuthenticationEnabled();
}
private HttpManagementActor getLogActorAndCacheInSession(HttpServletRequest req)
@@ -441,5 +456,18 @@ public abstract class AbstractServlet extends HttpServlet
return new HttpManagementActor(_rootLogger, request.getRemoteAddr(), request.getRemotePort());
}
+ protected HttpManagement getManagement()
+ {
+ return _httpManagement;
+ }
+ protected SecurityManager getSecurityManager()
+ {
+ return _broker.getSecurityManager();
+ }
+
+ protected SubjectCreator getSubjectCreator(HttpServletRequest request)
+ {
+ return _broker.getSubjectCreator(getSocketAddress(request));
+ }
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java
index 04eda2a787..f2cf5d7734 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java
@@ -26,8 +26,6 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.qpid.server.logging.LogRecorder;
-import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.registry.ApplicationRegistry;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
@@ -35,12 +33,7 @@ public class LogRecordsServlet extends AbstractServlet
{
public LogRecordsServlet()
{
- super(ApplicationRegistry.getInstance().getBroker());
- }
-
- public LogRecordsServlet(Broker broker)
- {
- super(broker);
+ super();
}
@Override
@@ -53,10 +46,10 @@ public class LogRecordsServlet extends AbstractServlet
response.setHeader("Pragma","no-cache");
response.setDateHeader ("Expires", 0);
- ApplicationRegistry applicationRegistry = (ApplicationRegistry) ApplicationRegistry.getInstance();
List<Map<String,Object>> logRecords = new ArrayList<Map<String, Object>>();
- for(LogRecorder.Record record : applicationRegistry.getLogRecorder())
+ LogRecorder logRecorder = getBroker().getLogRecorder();
+ for(LogRecorder.Record record : logRecorder)
{
logRecords.add(logRecordToObject(record));
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
index ae794472bf..d61c48bb2c 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java
@@ -29,7 +29,6 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.qpid.server.message.MessageReference;
import org.apache.qpid.server.message.ServerMessage;
-import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.queue.QueueEntry;
@@ -42,11 +41,6 @@ public class MessageContentServlet extends AbstractServlet
super();
}
- public MessageContentServlet(Broker broker)
- {
- super(broker);
- }
-
@Override
protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java
index 4bbb43be70..49e0c2b1bf 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java
@@ -34,13 +34,10 @@ import org.apache.log4j.Logger;
import org.apache.qpid.server.message.AMQMessageHeader;
import org.apache.qpid.server.message.MessageReference;
import org.apache.qpid.server.message.ServerMessage;
-import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.Queue;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.queue.QueueEntryVisitor;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.registry.IApplicationRegistry;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.subscription.Subscription;
@@ -56,11 +53,6 @@ public class MessageServlet extends AbstractServlet
super();
}
- public MessageServlet(Broker broker)
- {
- super(broker);
- }
-
@Override
protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
@@ -422,7 +414,7 @@ public class MessageServlet extends AbstractServlet
// FIXME: added temporary authorization check until we introduce management layer
// and review current ACL rules to have common rules for all management interfaces
String methodName = isMoveTransaction? "moveMessages":"copyMessages";
- if (isQueueUpdateMethodAuthorized(methodName, vhost.getName()))
+ if (isQueueUpdateMethodAuthorized(methodName, vhost))
{
final Queue destinationQueue = getQueueFromVirtualHost(destQueueName, vhost);
final List messageIds = new ArrayList((List) providedObject.get("messages"));
@@ -466,7 +458,7 @@ public class MessageServlet extends AbstractServlet
// FIXME: added temporary authorization check until we introduce management layer
// and review current ACL rules to have common rules for all management interfaces
- if (isQueueUpdateMethodAuthorized("deleteMessages", vhost.getName()))
+ if (isQueueUpdateMethodAuthorized("deleteMessages", vhost))
{
vhost.executeTransaction(new DeleteTransaction(sourceQueue, messageIds));
response.setStatus(HttpServletResponse.SC_OK);
@@ -478,25 +470,10 @@ public class MessageServlet extends AbstractServlet
}
- private boolean isQueueUpdateMethodAuthorized(String methodName, String virtualHost)
+ private boolean isQueueUpdateMethodAuthorized(String methodName, VirtualHost host)
{
- SecurityManager securityManager = getSecurityManager(virtualHost);
+ SecurityManager securityManager = host.getSecurityManager();
return securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName);
}
- private SecurityManager getSecurityManager(String virtualHost)
- {
- IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
- SecurityManager security;
- if (virtualHost == null)
- {
- security = appRegistry.getSecurityManager();
- }
- else
- {
- security = appRegistry.getVirtualHostRegistry().getVirtualHost(virtualHost).getSecurityManager();
- }
- return security;
- }
-
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
index 203fa66ff9..3fab26cde5 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
@@ -47,29 +47,29 @@ public class RestServlet extends AbstractServlet
private Class<? extends ConfiguredObject>[] _hierarchy;
- private volatile boolean initializationRequired = false;
-
private final ConfiguredObjectToMapConverter _objectConverter = new ConfiguredObjectToMapConverter();
+ private final boolean _hierarchyInitializationRequired;
public RestServlet()
{
super();
- initializationRequired = true;
+ _hierarchyInitializationRequired = true;
}
- public RestServlet(Broker broker, Class<? extends ConfiguredObject>... hierarchy)
+ public RestServlet(Class<? extends ConfiguredObject>... hierarchy)
{
- super(broker);
+ super();
_hierarchy = hierarchy;
+ _hierarchyInitializationRequired = false;
}
@Override
public void init() throws ServletException
{
- if (initializationRequired)
+ super.init();
+ if (_hierarchyInitializationRequired)
{
doInitialization();
- initializationRequired = false;
}
}
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java
index f8c8b52023..069132af1e 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java
@@ -25,8 +25,7 @@ import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.apache.log4j.Logger;
-import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.management.plugin.HttpManagement;
import org.apache.qpid.server.security.SubjectCreator;
import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
@@ -48,6 +47,7 @@ import java.util.Random;
public class SaslServlet extends AbstractServlet
{
+
private static final Logger LOGGER = Logger.getLogger(SaslServlet.class);
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
@@ -57,17 +57,11 @@ public class SaslServlet extends AbstractServlet
private static final String ATTR_EXPIRY = "SaslServlet.Expiry";
private static final long SASL_EXCHANGE_EXPIRY = 1000L;
-
public SaslServlet()
{
super();
}
- public SaslServlet(Broker broker)
- {
- super(broker);
- }
-
protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws
ServletException,
IOException
@@ -82,7 +76,7 @@ public class SaslServlet extends AbstractServlet
HttpSession session = request.getSession();
getRandom(session);
- SubjectCreator subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(getSocketAddress(request));
+ SubjectCreator subjectCreator = getSubjectCreator(request);
String[] mechanisms = subjectCreator.getMechanisms().split(" ");
Map<String, Object> outputObject = new LinkedHashMap<String, Object>();
@@ -140,7 +134,7 @@ public class SaslServlet extends AbstractServlet
String id = request.getParameter("id");
String saslResponse = request.getParameter("response");
- SubjectCreator subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(getSocketAddress(request));
+ SubjectCreator subjectCreator = getSubjectCreator(request);
if(mechanism != null)
{
@@ -202,13 +196,14 @@ public class SaslServlet extends AbstractServlet
private void checkSaslAuthEnabled(HttpServletRequest request)
{
boolean saslAuthEnabled;
+ HttpManagement management = getManagement();
if (request.isSecure())
{
- saslAuthEnabled = ApplicationRegistry.getInstance().getConfiguration().getHTTPSManagementSaslAuthEnabled();
+ saslAuthEnabled = management.isHttpsSaslAuthenticationEnabled();
}
else
{
- saslAuthEnabled = ApplicationRegistry.getInstance().getConfiguration().getHTTPManagementSaslAuthEnabled();
+ saslAuthEnabled = management.isHttpSaslAuthenticationEnabled();
}
if (!saslAuthEnabled)
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java
index 5f553beb26..40d3c02768 100644
--- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java
+++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java
@@ -41,11 +41,6 @@ public class StructureServlet extends AbstractServlet
super();
}
- public StructureServlet(Broker broker)
- {
- super(broker);
- }
-
@Override
protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
diff --git a/qpid/java/broker-plugins/management-http/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ManagementFactory b/qpid/java/broker-plugins/management-http/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.PluginFactory
index 7ffb9a9013..7ffb9a9013 100644
--- a/qpid/java/broker-plugins/management-http/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ManagementFactory
+++ b/qpid/java/broker-plugins/management-http/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.PluginFactory
diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementFactoryTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementFactoryTest.java
index fedd9b88d8..bb4c46826c 100644
--- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementFactoryTest.java
+++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementFactoryTest.java
@@ -19,64 +19,42 @@
package org.apache.qpid.server.management.plugin;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import org.apache.qpid.server.configuration.ServerConfiguration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.plugin.PluginFactory;
import org.apache.qpid.test.utils.QpidTestCase;
public class HttpManagementFactoryTest extends QpidTestCase
{
- private static final String KEY_STORE_PASSWORD = "keyStorePassword";
- private static final String KEY_STORE_PATH = "keyStorePath";
private static final int SESSION_TIMEOUT = 3600;
- private HttpManagementFactory _managementFactory = new HttpManagementFactory();
- private ServerConfiguration _configuration = mock(ServerConfiguration.class);
+ private PluginFactory _pluginFactory = new HttpManagementFactory();
+ private Map<String, Object> _attributes = new HashMap<String, Object>();
private Broker _broker = mock(Broker.class);
+ private UUID _id = UUID.randomUUID();
- public void testNoHttpManagementConfigured() throws Exception
+ public void testCreateInstanceReturnsNullWhenPluginTypeMissing() throws Exception
{
- ManagementPlugin management = _managementFactory.createInstance(_configuration, _broker);
- assertNull(management);
+ assertNull(_pluginFactory.createInstance(_id, _attributes, _broker));
}
-
- public void testHttpTransportConfigured() throws Exception
+ public void testCreateInstanceReturnsNullWhenPluginTypeNotHttp()
{
- when(_configuration.getHTTPManagementEnabled()).thenReturn(true);
- when(_configuration.getHTTPSManagementEnabled()).thenReturn(false);
-
- when(_configuration.getManagementKeyStorePassword()).thenReturn(null);
- when(_configuration.getManagementKeyStorePath()).thenReturn(null);
-
- when(_configuration.getHTTPManagementSessionTimeout()).thenReturn(SESSION_TIMEOUT);
-
- HttpManagement management = _managementFactory.createInstance(_configuration, _broker);
-
- assertNotNull(management);
- assertEquals(_broker, management.getBroker());
- assertNull(management.getKeyStorePassword());
- assertNull(management.getKeyStorePath());
- assertEquals(SESSION_TIMEOUT, management.getSessionTimeout());
-
+ _attributes.put(PluginFactory.PLUGIN_TYPE, "notHttp");
+ assertNull(_pluginFactory.createInstance(_id, _attributes, _broker));
}
- public void testHttpsTransportConfigured() throws Exception
+ public void testCreateInstance() throws Exception
{
- when(_configuration.getHTTPManagementEnabled()).thenReturn(false);
- when(_configuration.getHTTPSManagementEnabled()).thenReturn(true);
-
- when(_configuration.getManagementKeyStorePassword()).thenReturn(KEY_STORE_PASSWORD);
- when(_configuration.getManagementKeyStorePath()).thenReturn(KEY_STORE_PATH);
-
- when(_configuration.getHTTPManagementSessionTimeout()).thenReturn(SESSION_TIMEOUT);
+ _attributes.put(PluginFactory.PLUGIN_TYPE, HttpManagement.PLUGIN_TYPE);
+ _attributes.put(HttpManagement.TIME_OUT, SESSION_TIMEOUT);
- HttpManagement management = _managementFactory.createInstance(_configuration, _broker);
+ HttpManagement management = (HttpManagement) _pluginFactory.createInstance(_id, _attributes, _broker);
- assertNotNull(management);
assertEquals(_broker, management.getBroker());
- assertEquals(KEY_STORE_PASSWORD, management.getKeyStorePassword());
- assertEquals(KEY_STORE_PATH, management.getKeyStorePath());
assertEquals(SESSION_TIMEOUT, management.getSessionTimeout());
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/CustomRMIServerSocketFactory.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/CustomRMIServerSocketFactory.java
new file mode 100644
index 0000000000..b7aab78e45
--- /dev/null
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/CustomRMIServerSocketFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.jmx;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.rmi.server.RMIServerSocketFactory;
+
+/**
+ * Custom RMIServerSocketFactory class, used to prevent updates to the RMI registry.
+ * Supplied to the registry at creation, this will prevent RMI-based operations on the
+ * registry such as attempting to bind a new object, thereby securing it from tampering.
+ * This is accomplished by always returning null when attempting to determine the address
+ * of the caller, thus ensuring the registry will refuse the attempt. Calls to bind etc
+ * made using the object reference will not be affected and continue to operate normally.
+ */
+class CustomRMIServerSocketFactory implements RMIServerSocketFactory
+{
+
+ public ServerSocket createServerSocket(int port) throws IOException
+ {
+ return new NoLocalAddressServerSocket(port);
+ }
+
+ private static class NoLocalAddressServerSocket extends ServerSocket
+ {
+ NoLocalAddressServerSocket(int port) throws IOException
+ {
+ super(port);
+ }
+
+ @Override
+ public Socket accept() throws IOException
+ {
+ Socket s = new NoLocalAddressSocket();
+ super.implAccept(s);
+ return s;
+ }
+ }
+
+ private static class NoLocalAddressSocket extends Socket
+ {
+ @Override
+ public InetAddress getInetAddress()
+ {
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java
index cf7a83a54e..a045683de1 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java
@@ -20,56 +20,44 @@
*/
package org.apache.qpid.server.jmx;
-import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.configuration.BrokerProperties;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Transport;
-
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator;
+
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
-import javax.management.Notification;
-import javax.management.NotificationFilterSupport;
-import javax.management.NotificationListener;
import javax.management.ObjectName;
-import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.MBeanServerForwarder;
-import javax.management.remote.rmi.RMIConnection;
import javax.management.remote.rmi.RMIConnectorServer;
-import javax.management.remote.rmi.RMIJRMPServerImpl;
-import javax.management.remote.rmi.RMIServerImpl;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
-import javax.security.auth.Subject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.management.ManagementFactory;
-import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
import java.net.UnknownHostException;
import java.rmi.AlreadyBoundException;
import java.rmi.NoSuchObjectException;
import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
/**
* This class starts up an MBeanserver. If out of the box agent has been enabled then there are no
@@ -82,34 +70,32 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
private static final String OPERATIONAL_LOGGING_NAME = "JMX";
private final MBeanServer _mbeanServer;
+
private JMXConnectorServer _cs;
private Registry _rmiRegistry;
- private boolean _useCustomSocketFactory;
- private final int _jmxPortRegistryServer;
- private final int _jmxPortConnectorServer;
+ private final Broker _broker;
+ private final Port _registryPort;
+ private final Port _connectorPort;
- private final ServerConfiguration _serverConfiguration;
-
- public JMXManagedObjectRegistry(ServerConfiguration serverConfiguration) throws AMQException
+ public JMXManagedObjectRegistry(
+ Broker broker,
+ Port connectorPort, Port registryPort,
+ JMXManagement jmxManagement)
{
- _log.info("Initialising managed object registry using platform MBean server");
-
- _serverConfiguration = serverConfiguration;
+ _broker = broker;
+ _registryPort = registryPort;
+ _connectorPort = connectorPort;
- // Retrieve the config parameters
- _useCustomSocketFactory = _serverConfiguration.getUseCustomRMISocketFactory();
- boolean platformServer = _serverConfiguration.getPlatformMbeanserver();
+ boolean usePlatformServer = (Boolean)jmxManagement.getAttribute(JMXManagement.USE_PLATFORM_MBEAN_SERVER);
_mbeanServer =
- platformServer ? ManagementFactory.getPlatformMBeanServer()
+ usePlatformServer ? ManagementFactory.getPlatformMBeanServer()
: MBeanServerFactory.createMBeanServer(ManagedObject.DOMAIN);
+ }
- _jmxPortRegistryServer = _serverConfiguration.getJMXPortRegistryServer();
- _jmxPortConnectorServer = _serverConfiguration.getJMXConnectorServerPort();
- }
-
- public void start() throws IOException, ConfigurationException
+ @Override
+ public void start() throws IOException
{
CurrentActor.get().message(ManagementConsoleMessages.STARTUP(OPERATIONAL_LOGGING_NAME));
@@ -117,59 +103,31 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
if (areOutOfTheBoxJMXOptionsSet())
{
CurrentActor.get().message(ManagementConsoleMessages.READY(OPERATIONAL_LOGGING_NAME));
- return;
}
+ else
+ {
+ startRegistryAndConnector();
+ }
+ }
- //Socket factories for the RMIConnectorServer, either default or SLL depending on configuration
+ private void startRegistryAndConnector() throws IOException
+ {
+ //Socket factories for the RMIConnectorServer, either default or SSL depending on configuration
RMIClientSocketFactory csf;
RMIServerSocketFactory ssf;
- //check ssl enabled option in config, default to true if option is not set
- boolean sslEnabled = _serverConfiguration.getManagementSSLEnabled();
+ //check ssl enabled option on connector port (note we don't provide ssl for registry server at
+ //moment).
+ boolean connectorSslEnabled = _connectorPort.getTransports().contains(Transport.SSL);
- if (sslEnabled)
+ if (connectorSslEnabled)
{
- //set the SSL related system properties used by the SSL RMI socket factories to the values
- //given in the configuration file
- String keyStorePath = _serverConfiguration.getManagementKeyStorePath();
+ String keyStorePath = System.getProperty("javax.net.ssl.keyStore");
+ String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
- //check the keystore path value is valid
- if (keyStorePath == null)
- {
- throw new ConfigurationException("JMX management SSL keystore path not defined, " +
- "unable to start SSL protected JMX ConnectorServer");
- }
- else
- {
- //ensure the system property is set (for use by SslRMIClientSocketFactory and SslRMIServerSocketFactory)
- System.setProperty("javax.net.ssl.keyStore", keyStorePath);
+ validateKeyStoreProperties(keyStorePath, keyStorePassword);
- //check the file is usable
- File ksf = new File(keyStorePath);
-
- if (!ksf.exists())
- {
- throw new FileNotFoundException("Cannot find JMX management SSL keystore file: " + ksf);
- }
- if (!ksf.canRead())
- {
- throw new FileNotFoundException("Cannot read JMX management SSL keystore file: "
- + ksf + ". Check permissions.");
- }
-
- CurrentActor.get().message(ManagementConsoleMessages.SSL_KEYSTORE(ksf.getAbsolutePath()));
- }
-
- if (_serverConfiguration.getManagementKeyStorePassword() == null)
- {
- throw new ConfigurationException("JMX management SSL keystore password not defined, " +
- "unable to start requested SSL protected JMX server");
- }
- else
- {
- System.setProperty("javax.net.ssl.keyStorePassword",
- _serverConfiguration.getManagementKeyStorePassword());
- }
+ CurrentActor.get().message(ManagementConsoleMessages.SSL_KEYSTORE(keyStorePath));
//create the SSL RMI socket factories
csf = new SslRMIClientSocketFactory();
@@ -182,29 +140,23 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
ssf = null;
}
+ int jmxPortRegistryServer = _registryPort.getPort();
+ int jmxPortConnectorServer = _connectorPort.getPort();
+
//add a JMXAuthenticator implementation the env map to authenticate the RMI based JMX connector server
- RMIPasswordAuthenticator rmipa = new RMIPasswordAuthenticator(ApplicationRegistry.getInstance(), new InetSocketAddress(_jmxPortRegistryServer));
- HashMap<String,Object> env = new HashMap<String,Object>();
- env.put(JMXConnectorServer.AUTHENTICATOR, rmipa);
+ RMIPasswordAuthenticator rmipa = new RMIPasswordAuthenticator(_broker, new InetSocketAddress(jmxPortConnectorServer));
+ HashMap<String,Object> connectorEnv = new HashMap<String,Object>();
+ connectorEnv.put(JMXConnectorServer.AUTHENTICATOR, rmipa);
+
+ System.setProperty("java.rmi.server.randomIDs", "true");
+ boolean useCustomSocketFactory = Boolean.parseBoolean(System.getProperty(BrokerProperties.PROPERTY_USE_CUSTOM_RMI_SOCKET_FACTORY, Boolean.TRUE.toString()));
/*
* Start a RMI registry on the management port, to hold the JMX RMI ConnectorServer stub.
* Using custom socket factory to prevent anyone (including us unfortunately) binding to the registry using RMI.
* As a result, only binds made using the object reference will succeed, thus securing it from external change.
*/
- System.setProperty("java.rmi.server.randomIDs", "true");
- if(_useCustomSocketFactory)
- {
- _log.debug("Using custom RMIServerSocketFactory");
- _rmiRegistry = LocateRegistry.createRegistry(_jmxPortRegistryServer, null, new CustomRMIServerSocketFactory());
- }
- else
- {
- _log.debug("Using default RMIServerSocketFactory");
- _rmiRegistry = LocateRegistry.createRegistry(_jmxPortRegistryServer, null, null);
- }
-
- CurrentActor.get().message(ManagementConsoleMessages.LISTENING("RMI Registry", _jmxPortRegistryServer));
+ _rmiRegistry = createRmiRegistry(jmxPortRegistryServer, useCustomSocketFactory);
/*
* We must now create the RMI ConnectorServer manually, as the JMX Factory methods use RMI calls
@@ -212,57 +164,16 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
* locked it from any RMI based modifications, including our own. Instead, we will manually bind
* the RMIConnectorServer stub to the registry using its object reference, which will still succeed.
*
- * The registry is exported on the defined management port 'port'. We will export the RMIConnectorServer
- * on 'port +1'. Use of these two well-defined ports will ease any navigation through firewall's.
+ * The registry is exported on the defined management port 'port'.
*/
- final Map<String, String> connectionIdUsernameMap = new ConcurrentHashMap<String, String>();
- final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env)
- {
-
- /**
- * Override makeClient so we can cache the username of the client in a Map keyed by connectionId.
- * ConnectionId is guaranteed to be unique per client connection, according to the JMX spec.
- * An instance of NotificationListener (mapCleanupListener) will be responsible for removing these Map
- * entries.
- *
- * @see javax.management.remote.rmi.RMIJRMPServerImpl#makeClient(String, javax.security.auth.Subject)
- */
- @Override
- protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException
- {
- final RMIConnection makeClient = super.makeClient(connectionId, subject);
- final AuthenticatedPrincipal authenticatedPrincipalFromSubject = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject);
- connectionIdUsernameMap.put(connectionId, authenticatedPrincipalFromSubject.getName());
- return makeClient;
- }
- };
-
- // Create a Listener responsible for removing the map entries add by the #makeClient entry above.
- final NotificationListener mapCleanupListener = new NotificationListener()
- {
+ final UsernameCachingRMIJRMPServer usernameCachingRmiServer = new UsernameCachingRMIJRMPServer(jmxPortConnectorServer, csf, ssf, connectorEnv);
- public void handleNotification(Notification notification, Object handback)
- {
- final String connectionId = ((JMXConnectionNotification) notification).getConnectionId();
- connectionIdUsernameMap.remove(connectionId);
- }
- };
-
- String localHost;
- try
- {
- localHost = InetAddress.getLocalHost().getHostName();
- }
- catch(UnknownHostException ex)
- {
- localHost="127.0.0.1";
- }
- final String hostname = localHost;
+ final String localHostName = getLocalhost();
final JMXServiceURL externalUrl = new JMXServiceURL(
- "service:jmx:rmi://"+hostname+":"+(_jmxPortConnectorServer)+"/jndi/rmi://"+hostname+":"+_jmxPortRegistryServer+"/jmxrmi");
+ "service:jmx:rmi://"+localHostName+":"+(jmxPortConnectorServer)+"/jndi/rmi://"+localHostName+":"+jmxPortRegistryServer+"/jmxrmi");
- final JMXServiceURL internalUrl = new JMXServiceURL("rmi", hostname, _jmxPortConnectorServer);
- _cs = new RMIConnectorServer(internalUrl, env, rmiConnectorServerStub, _mbeanServer)
+ final JMXServiceURL internalUrl = new JMXServiceURL("rmi", localHostName, jmxPortConnectorServer);
+ _cs = new RMIConnectorServer(internalUrl, connectorEnv, usernameCachingRmiServer, _mbeanServer)
{
@Override
public synchronized void start() throws IOException
@@ -270,7 +181,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
try
{
//manually bind the connector server to the registry at key 'jmxrmi', like the out-of-the-box agent
- _rmiRegistry.bind("jmxrmi", rmiConnectorServerStub);
+ _rmiRegistry.bind("jmxrmi", usernameCachingRmiServer);
}
catch (AlreadyBoundException abe)
{
@@ -298,7 +209,6 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
}
catch (NotBoundException nbe)
{
- // TODO consider if we want to keep new logging
_log.error("Failed to unbind jmxrmi", nbe);
//ignore
}
@@ -313,97 +223,100 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
//must return our pre-crafted url that includes the full details, inc JNDI details
return externalUrl;
}
-
};
-
//Add the custom invoker as an MBeanServerForwarder, and start the RMIConnectorServer.
- MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
+ MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance(_broker);
_cs.setMBeanServerForwarder(mbsf);
+ // Install a ManagementLogonLogoffReporter so we can report as users logon/logoff
+ ManagementLogonLogoffReporter jmxManagementUserLogonLogoffReporter = new ManagementLogonLogoffReporter(_broker.getRootMessageLogger(), usernameCachingRmiServer);
+ _cs.addNotificationListener(jmxManagementUserLogonLogoffReporter, jmxManagementUserLogonLogoffReporter, null);
- // Get the handler that is used by the above MBInvocationHandler Proxy.
- // which is the MBeanInvocationHandlerImpl and so also a NotificationListener.
- final NotificationListener invocationHandler = (NotificationListener) Proxy.getInvocationHandler(mbsf);
-
- // Install a notification listener on OPENED, CLOSED, and FAILED,
- // passing the map of connection-ids to usernames as hand-back data.
- final NotificationFilterSupport invocationHandlerFilter = new NotificationFilterSupport();
- invocationHandlerFilter.enableType(JMXConnectionNotification.OPENED);
- invocationHandlerFilter.enableType(JMXConnectionNotification.CLOSED);
- invocationHandlerFilter.enableType(JMXConnectionNotification.FAILED);
- _cs.addNotificationListener(invocationHandler, invocationHandlerFilter, connectionIdUsernameMap);
-
- // Install a second notification listener on CLOSED AND FAILED only to remove the entry from the
- // Map. Here we rely on the fact that JMX will call the listeners in the order in which they are
- // installed.
- final NotificationFilterSupport mapCleanupHandlerFilter = new NotificationFilterSupport();
- mapCleanupHandlerFilter.enableType(JMXConnectionNotification.CLOSED);
- mapCleanupHandlerFilter.enableType(JMXConnectionNotification.FAILED);
- _cs.addNotificationListener(mapCleanupListener, mapCleanupHandlerFilter, null);
+ // Install the usernameCachingRmiServer as a listener so it may cleanup as clients disconnect
+ _cs.addNotificationListener(usernameCachingRmiServer, usernameCachingRmiServer, null);
_cs.start();
- String connectorServer = (sslEnabled ? "SSL " : "") + "JMX RMIConnectorServer";
- CurrentActor.get().message(ManagementConsoleMessages.LISTENING(connectorServer, _jmxPortConnectorServer));
-
+ String connectorServer = (connectorSslEnabled ? "SSL " : "") + "JMX RMIConnectorServer";
+ CurrentActor.get().message(ManagementConsoleMessages.LISTENING(connectorServer, jmxPortConnectorServer));
CurrentActor.get().message(ManagementConsoleMessages.READY(OPERATIONAL_LOGGING_NAME));
}
- /*
- * Custom RMIServerSocketFactory class, used to prevent updates to the RMI registry.
- * Supplied to the registry at creation, this will prevent RMI-based operations on the
- * registry such as attempting to bind a new object, thereby securing it from tampering.
- * This is accomplished by always returning null when attempting to determine the address
- * of the caller, thus ensuring the registry will refuse the attempt. Calls to bind etc
- * made using the object reference will not be affected and continue to operate normally.
- */
-
- private static class CustomRMIServerSocketFactory implements RMIServerSocketFactory
+ private Registry createRmiRegistry(int jmxPortRegistryServer, boolean useCustomRmiRegistry)
+ throws RemoteException
{
-
- public ServerSocket createServerSocket(int port) throws IOException
+ Registry rmiRegistry;
+ if(useCustomRmiRegistry)
{
- return new NoLocalAddressServerSocket(port);
+ _log.debug("Using custom RMIServerSocketFactory");
+ rmiRegistry = LocateRegistry.createRegistry(jmxPortRegistryServer, null, new CustomRMIServerSocketFactory());
}
-
- private static class NoLocalAddressServerSocket extends ServerSocket
+ else
{
- NoLocalAddressServerSocket(int port) throws IOException
- {
- super(port);
- }
+ _log.debug("Using default RMIServerSocketFactory");
+ rmiRegistry = LocateRegistry.createRegistry(jmxPortRegistryServer, null, null);
+ }
- @Override
- public Socket accept() throws IOException
- {
- Socket s = new NoLocalAddressSocket();
- super.implAccept(s);
- return s;
- }
+ CurrentActor.get().message(ManagementConsoleMessages.LISTENING("RMI Registry", jmxPortRegistryServer));
+ return rmiRegistry;
+ }
+
+ private void validateKeyStoreProperties(String keyStorePath, String keyStorePassword) throws FileNotFoundException
+ {
+ if (keyStorePath == null)
+ {
+ throw new IllegalConfigurationException("JVM system property 'javax.net.ssl.keyStore' is not set, "
+ + "unable to start requested SSL protected JMX connector");
+ }
+ if (keyStorePassword == null)
+ {
+ throw new IllegalConfigurationException( "JVM system property 'javax.net.ssl.keyStorePassword' is not set, "
+ + "unable to start requested SSL protected JMX connector");
}
- private static class NoLocalAddressSocket extends Socket
+ File ksf = new File(keyStorePath);
+ if (!ksf.exists())
{
- @Override
- public InetAddress getInetAddress()
- {
- return null;
- }
+ throw new FileNotFoundException("Cannot find JMX management SSL keystore file: " + ksf);
+ }
+ if (!ksf.canRead())
+ {
+ throw new FileNotFoundException("Cannot read JMX management SSL keystore file: "
+ + ksf + ". Check permissions.");
}
}
-
+ @Override
public void registerObject(ManagedObject managedObject) throws JMException
{
_mbeanServer.registerMBean(managedObject, managedObject.getObjectName());
}
+ @Override
public void unregisterObject(ManagedObject managedObject) throws JMException
{
_mbeanServer.unregisterMBean(managedObject.getObjectName());
}
+ @Override
+ public void close()
+ {
+ _log.debug("close() called");
+
+ closeConnectorAndRegistryServers();
+
+ unregisterAllMbeans();
+
+ CurrentActor.get().message(ManagementConsoleMessages.STOPPED(OPERATIONAL_LOGGING_NAME));
+ }
+
+ private void closeConnectorAndRegistryServers()
+ {
+ closeConnectorServer();
+ closeRegistryServer();
+ }
+
// checks if the system properties are set which enable the JVM's out-of-the-box JMXAgent.
private boolean areOutOfTheBoxJMXOptionsSet()
{
@@ -420,29 +333,26 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
return false;
}
- //Stops the JMXConnectorServer and RMIRegistry, then unregisters any remaining MBeans from the MBeanServer
- public void close()
+ private String getLocalhost()
{
- _log.debug("close() called");
-
- if (_cs != null)
+ String localHost;
+ try
{
- // Stopping the JMX ConnectorServer
- try
- {
- CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("JMX RMIConnectorServer", _cs.getAddress().getPort()));
- _cs.stop();
- }
- catch (IOException e)
- {
- _log.error("Exception while closing the JMX ConnectorServer: ", e);
- }
+ localHost = InetAddress.getLocalHost().getHostName();
}
+ catch(UnknownHostException ex)
+ {
+ localHost="127.0.0.1";
+ }
+ return localHost;
+ }
+ private void closeRegistryServer()
+ {
if (_rmiRegistry != null)
{
// Stopping the RMI registry
- CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("RMI Registry", _jmxPortRegistryServer));
+ CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("RMI Registry", _registryPort.getPort()));
try
{
boolean success = UnicastRemoteObject.unexportObject(_rmiRegistry, false);
@@ -455,8 +365,36 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
{
_log.error("Exception while closing the RMI Registry: ", e);
}
+ finally
+ {
+ _rmiRegistry = null;
+ }
}
+ }
+ private void closeConnectorServer()
+ {
+ if (_cs != null)
+ {
+ // Stopping the JMX ConnectorServer
+ try
+ {
+ CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("JMX RMIConnectorServer", _cs.getAddress().getPort()));
+ _cs.stop();
+ }
+ catch (IOException e)
+ {
+ _log.error("Exception while closing the JMX ConnectorServer: ", e);
+ }
+ finally
+ {
+ _cs = null;
+ }
+ }
+ }
+
+ private void unregisterAllMbeans()
+ {
//ObjectName query to gather all Qpid related MBeans
ObjectName mbeanNameQuery = null;
try
@@ -479,8 +417,6 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
_log.error("Exception unregistering MBean '"+ name +"': " + e.getMessage());
}
}
-
- CurrentActor.get().message(ManagementConsoleMessages.STOPPED(OPERATIONAL_LOGGING_NAME));
}
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java
index f529793118..f307f5118a 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java
@@ -21,81 +21,214 @@
package org.apache.qpid.server.jmx;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.UUID;
+
import javax.management.JMException;
-import javax.management.StandardMBean;
import org.apache.log4j.Logger;
-import org.apache.qpid.server.configuration.ServerConfiguration;
import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBean;
import org.apache.qpid.server.jmx.mbeans.UserManagementMBean;
import org.apache.qpid.server.jmx.mbeans.ServerInformationMBean;
import org.apache.qpid.server.jmx.mbeans.Shutdown;
import org.apache.qpid.server.jmx.mbeans.VirtualHostMBean;
import org.apache.qpid.server.logging.log4j.LoggingManagementFacade;
-import org.apache.qpid.server.management.plugin.ManagementPlugin;
+import org.apache.qpid.server.model.AuthenticationProvider;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfigurationChangeListener;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider;
+import org.apache.qpid.server.model.Plugin;
+import org.apache.qpid.server.model.Port;
+import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.adapter.AbstractPluginAdapter;
+import org.apache.qpid.server.plugin.PluginFactory;
import org.apache.qpid.server.plugin.QpidServiceLoader;
+import org.apache.qpid.server.util.MapValueConverter;
-public class JMXManagement implements ConfigurationChangeListener, ManagementPlugin
+public class JMXManagement extends AbstractPluginAdapter implements ConfigurationChangeListener
{
private static final Logger LOGGER = Logger.getLogger(JMXManagement.class);
+ public static final String PLUGIN_TYPE = "MANAGEMENT-JMX";
+
+ // attributes
+ public static final String USE_PLATFORM_MBEAN_SERVER = "usePlatformMBeanServer";
+ public static final String NAME = "name";
+
+ // default values
+ public static final String DEFAULT_NAME = "JMXManagement";
+ public static final boolean DEFAULT_USE_PLATFORM_MBEAN_SERVER = true;
+
+ @SuppressWarnings("serial")
+ private static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableCollection(new HashSet<String>(Plugin.AVAILABLE_ATTRIBUTES){{
+ add(NAME);
+ add(USE_PLATFORM_MBEAN_SERVER);
+ add(PluginFactory.PLUGIN_TYPE);
+ }});
+
+ @SuppressWarnings("serial")
+ private static final Map<String, Object> DEFAULTS = new HashMap<String, Object>(){{
+ put(USE_PLATFORM_MBEAN_SERVER, DEFAULT_USE_PLATFORM_MBEAN_SERVER);
+ put(NAME, DEFAULT_NAME);
+ put(PluginFactory.PLUGIN_TYPE, PLUGIN_TYPE);
+ }};
+
+ @SuppressWarnings("serial")
+ private static final Map<String, Class<?>> ATTRIBUTE_TYPES = new HashMap<String, Class<?>>(){{
+ put(USE_PLATFORM_MBEAN_SERVER, Boolean.class);
+ put(NAME, String.class);
+ put(PluginFactory.PLUGIN_TYPE, String.class);
+ }};
+
private final Broker _broker;
private JMXManagedObjectRegistry _objectRegistry;
private final Map<ConfiguredObject, AMQManagedObject> _children = new HashMap<ConfiguredObject, AMQManagedObject>();
- private final ServerConfiguration _serverConfiguration;
-
- public JMXManagement(ServerConfiguration serverConfiguration, Broker broker)
+ public JMXManagement(UUID id, Broker broker, Map<String, Object> attributes)
{
+ super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), broker.getTaskExecutor());
_broker = broker;
- _serverConfiguration = serverConfiguration;
+ addParent(Broker.class, broker);
}
@Override
- public void start() throws Exception
+ protected boolean setState(State currentState, State desiredState)
{
- _objectRegistry = new JMXManagedObjectRegistry(_serverConfiguration);
+ if(desiredState == State.ACTIVE)
+ {
+ try
+ {
+ start();
+ }
+ catch (JMException e)
+ {
+ throw new RuntimeException("Couldn't start JMX management", e);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("Couldn't start JMX management", e);
+ }
+ return true;
+ }
+ else if(desiredState == State.STOPPED)
+ {
+ stop();
+ return true;
+ }
+ return false;
+ }
+
+ private void start() throws JMException, IOException
+ {
+ Port connectorPort = null;
+ Port registryPort = null;
+ Collection<Port> ports = _broker.getPorts();
+ for (Port port : ports)
+ {
+ if(isRegistryPort(port))
+ {
+ registryPort = port;
+ }
+ else if(isConnectorPort(port))
+ {
+ connectorPort = port;
+ }
+ }
+ if(connectorPort == null)
+ {
+ throw new IllegalStateException("No JMX connector port found supporting protocol " + Protocol.JMX_RMI);
+ }
+ if(registryPort == null)
+ {
+ throw new IllegalStateException("No JMX RMI port found supporting protocol " + Protocol.RMI);
+ }
+
+ _objectRegistry = new JMXManagedObjectRegistry(_broker, connectorPort, registryPort, this);
_broker.addChangeListener(this);
+
synchronized (_children)
{
for(VirtualHost virtualHost : _broker.getVirtualHosts())
{
if(!_children.containsKey(virtualHost))
{
- _children.put(virtualHost, new VirtualHostMBean(virtualHost, _objectRegistry));
+ LOGGER.debug("Create MBean for virtual host:" + virtualHost.getName());
+ VirtualHostMBean mbean = new VirtualHostMBean(virtualHost, _objectRegistry);
+ LOGGER.debug("Check for additional MBeans for virtual host:" + virtualHost.getName());
+ createAdditionalMBeansFromProviders(virtualHost, mbean);
+ }
+ }
+ Collection<AuthenticationProvider> authenticationProviders = _broker.getAuthenticationProviders();
+ for (AuthenticationProvider authenticationProvider : authenticationProviders)
+ {
+ if(authenticationProvider instanceof PasswordCredentialManagingAuthenticationProvider)
+ {
+ UserManagementMBean mbean = new UserManagementMBean(
+ (PasswordCredentialManagingAuthenticationProvider) authenticationProvider,
+ _objectRegistry);
+ _children.put(authenticationProvider, mbean);
}
}
}
new Shutdown(_objectRegistry);
new ServerInformationMBean(_objectRegistry, _broker);
new LoggingManagementMBean(LoggingManagementFacade.getCurrentInstance(), _objectRegistry);
-
_objectRegistry.start();
}
- @Override
- public void stop()
+ private boolean isConnectorPort(Port port)
{
- _broker.removeChangeListener(this);
+ return port.getProtocols().contains(Protocol.JMX_RMI);
+ }
+ private boolean isRegistryPort(Port port)
+ {
+ return port.getProtocols().contains(Protocol.RMI);
+ }
+
+ private void stop()
+ {
+ synchronized (_children)
+ {
+ for(ConfiguredObject object : _children.keySet())
+ {
+ AMQManagedObject mbean = _children.get(object);
+ if (mbean instanceof ConfigurationChangeListener)
+ {
+ object.removeChangeListener((ConfigurationChangeListener)mbean);
+ }
+ try
+ {
+ mbean.unregister();
+ }
+ catch (JMException e)
+ {
+ LOGGER.error("Error unregistering mbean", e);
+ }
+ }
+ _children.clear();
+ }
+ _broker.removeChangeListener(this);
_objectRegistry.close();
}
+ @Override
public void stateChanged(ConfiguredObject object, State oldState, State newState)
{
-
+ // no-op
}
+ @Override
public void childAdded(ConfiguredObject object, ConfiguredObject child)
{
synchronized (_children)
@@ -130,7 +263,7 @@ public class JMXManagement implements ConfigurationChangeListener, ManagementPlu
}
}
-
+ @Override
public void childRemoved(ConfiguredObject object, ConfiguredObject child)
{
// TODO - implement vhost removal (possibly just removing the instanceof check below)
@@ -157,6 +290,12 @@ public class JMXManagement implements ConfigurationChangeListener, ManagementPlu
}
}
+ @Override
+ public void attributeSet(ConfiguredObject object, String attributeName, Object oldAttributeValue, Object newAttributeValue)
+ {
+ // no-op
+ }
+
private void createAdditionalMBeansFromProviders(ConfiguredObject child, AMQManagedObject mbean) throws JMException
{
_children.put(child, mbean);
@@ -168,7 +307,7 @@ public class JMXManagement implements ConfigurationChangeListener, ManagementPlu
if (provider.isChildManageableByMBean(child))
{
LOGGER.debug("Provider will create mbean ");
- StandardMBean bean = provider.createMBean(child, mbean);
+ provider.createMBean(child, mbean);
// TODO track the mbeans that have been created on behalf of a child in a map, then
// if the child is ever removed, destroy these beans too.
}
@@ -181,9 +320,16 @@ public class JMXManagement implements ConfigurationChangeListener, ManagementPlu
return _broker;
}
- /** Added for testing purposes */
- ServerConfiguration getServerConfiguration()
+ @Override
+ public String getName()
{
- return _serverConfiguration;
+ return (String)getAttribute(NAME);
}
+
+ @Override
+ public Collection<String> getAttributeNames()
+ {
+ return AVAILABLE_ATTRIBUTES;
+ }
+
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagementFactory.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagementFactory.java
index 12d4711595..c2186c372b 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagementFactory.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagementFactory.java
@@ -18,27 +18,30 @@
*/
package org.apache.qpid.server.jmx;
+import java.util.Map;
+import java.util.UUID;
+
import org.apache.log4j.Logger;
-import org.apache.qpid.server.configuration.ServerConfiguration;
import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.plugin.ManagementFactory;
+import org.apache.qpid.server.model.Plugin;
+import org.apache.qpid.server.plugin.PluginFactory;
-public class JMXManagementFactory implements ManagementFactory
+public class JMXManagementFactory implements PluginFactory
{
- private static final Logger _logger = Logger.getLogger(JMXManagementFactory.class);
+ private static final Logger LOGGER = Logger.getLogger(JMXManagementFactory.class);
@Override
- public JMXManagement createInstance(ServerConfiguration serverConfiguration, Broker broker)
+ public Plugin createInstance(UUID id, Map<String, Object> attributes, Broker broker)
{
- if (serverConfiguration.getJMXManagementEnabled())
+ if (JMXManagement.PLUGIN_TYPE.equals(attributes.get(PLUGIN_TYPE)))
{
- return new JMXManagement(serverConfiguration, broker);
+ return new JMXManagement(id, broker, attributes);
}
else
{
- if(_logger.isDebugEnabled())
+ if (LOGGER.isDebugEnabled())
{
- _logger.debug("Skipping registration of JMX plugin as JMX Management disabled in config.");
+ LOGGER.debug("Skipping registration of JMX plugin as JMX Management disabled in config.");
}
return null;
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java
index 694b0b2913..8bc2afb176 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java
@@ -22,12 +22,11 @@ package org.apache.qpid.server.jmx;
import org.apache.log4j.Logger;
-import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.configuration.BrokerProperties;
import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.server.logging.actors.ManagementActor;
-import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.security.access.Operation;
import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
@@ -37,11 +36,8 @@ import javax.management.JMException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
-import javax.management.Notification;
-import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.RuntimeErrorException;
-import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.MBeanServerForwarder;
import javax.security.auth.Subject;
import java.lang.reflect.InvocationHandler;
@@ -51,26 +47,32 @@ import java.lang.reflect.Proxy;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Arrays;
-import java.util.Map;
/**
* This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. It delegates
* JMX access decisions to the SecurityPlugin.
*/
-public class MBeanInvocationHandlerImpl implements InvocationHandler, NotificationListener
+public class MBeanInvocationHandlerImpl implements InvocationHandler
{
private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class);
- private final IApplicationRegistry _appRegistry = ApplicationRegistry.getInstance();
private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate";
private MBeanServer _mbs;
- private final ManagementActor _logActor = new ManagementActor(_appRegistry.getRootMessageLogger());
- private final boolean _managementRightsInferAllAccess =
- _appRegistry.getConfiguration().getManagementRightsInferAllAccess();
+ private final ManagementActor _logActor;
- public static MBeanServerForwarder newProxyInstance()
+ private final boolean _managementRightsInferAllAccess;
+ private final Broker _broker;
+
+ MBeanInvocationHandlerImpl(Broker broker)
+ {
+ _managementRightsInferAllAccess = Boolean.valueOf(System.getProperty(BrokerProperties.PROPERTY_MANAGEMENT_RIGHTS_INFER_ALL_ACCESS, "true"));
+ _broker = broker;
+ _logActor = new ManagementActor(broker.getRootMessageLogger());
+ }
+
+ public static MBeanServerForwarder newProxyInstance(Broker broker)
{
- final InvocationHandler handler = new MBeanInvocationHandlerImpl();
+ final InvocationHandler handler = new MBeanInvocationHandlerImpl(broker);
final Class<?>[] interfaces = new Class[] { MBeanServerForwarder.class };
Object proxy = Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(), interfaces, handler);
@@ -212,11 +214,16 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati
SecurityManager security;
if (vhost == null)
{
- security = _appRegistry.getSecurityManager();
+ security = _broker.getSecurityManager();
}
else
{
- security = _appRegistry.getVirtualHostRegistry().getVirtualHost(vhost).getSecurityManager();
+ VirtualHost virtualHost = _broker.findVirtualHostByName(vhost);
+ if (virtualHost == null)
+ {
+ throw new IllegalArgumentException("Virtual host with name '" + vhost + "' is not found.");
+ }
+ security = virtualHost.getSecurityManager();
}
methodName = getMethodName(method, args);
@@ -361,51 +368,5 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati
return (methodName.startsWith("query") || methodName.startsWith("get") || methodName.startsWith("is"));
}
- /**
- * Receives notifications from the MBeanServer.
- */
- public void handleNotification(final Notification notification, final Object handback)
- {
- assert notification instanceof JMXConnectionNotification;
-
- final String connectionId = ((JMXConnectionNotification) notification).getConnectionId();
- final String type = notification.getType();
-
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Notification connectionId : " + connectionId + " type : " + type
- + " Notification handback : " + handback);
- }
-
- // Normally JMXManagedObjectRegistry provides a Map as handback data containing a map
- // between connection id and username.
- String user = null;
- if (handback instanceof Map)
- {
- @SuppressWarnings("unchecked")
- final Map<String, String> connectionIdUsernameMap = (Map<String, String>) handback;
- user = connectionIdUsernameMap.get(connectionId);
- }
-
- // If user is still null, fallback to an unordered list of Principals from the connection id.
- if (user == null)
- {
- final String[] splitConnectionId = connectionId.split(" ");
- user = splitConnectionId[1];
- }
-
- // use a separate instance of actor as subject is not set on connect/disconnect
- // we need to pass principal name explicitly into log actor
- LogActor logActor = new ManagementActor(_appRegistry.getRootMessageLogger(), user);
- if (JMXConnectionNotification.OPENED.equals(type))
- {
- logActor.message(ManagementConsoleMessages.OPEN(user));
- }
- else if (JMXConnectionNotification.CLOSED.equals(type) ||
- JMXConnectionNotification.FAILED.equals(type))
- {
- logActor.message(ManagementConsoleMessages.CLOSE(user));
- }
- }
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporter.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporter.java
new file mode 100644
index 0000000000..ae0574dc21
--- /dev/null
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporter.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.jmx;
+
+import static javax.management.remote.JMXConnectionNotification.CLOSED;
+import static javax.management.remote.JMXConnectionNotification.FAILED;
+import static javax.management.remote.JMXConnectionNotification.OPENED;
+
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.remote.JMXConnectionNotification;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.RootMessageLogger;
+import org.apache.qpid.server.logging.actors.ManagementActor;
+import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
+
+public class ManagementLogonLogoffReporter implements NotificationListener, NotificationFilter
+{
+ private static final Logger LOGGER = Logger.getLogger(ManagementLogonLogoffReporter.class);
+ private final RootMessageLogger _rootMessageLogger;
+ private final UsernameAccessor _usernameAccessor;
+
+ public ManagementLogonLogoffReporter(RootMessageLogger rootMessageLogger, UsernameAccessor usernameAccessor)
+ {
+ _rootMessageLogger = rootMessageLogger;
+ _usernameAccessor = usernameAccessor;
+ }
+
+ @Override
+ public void handleNotification(final Notification notification, final Object handback)
+ {
+ final String connectionId = ((JMXConnectionNotification) notification).getConnectionId();
+ final String type = notification.getType();
+
+ if (LOGGER.isDebugEnabled())
+ {
+ LOGGER.debug("Notification connectionId : " + connectionId + " type : " + type);
+ }
+
+ String user = _usernameAccessor.getUsernameForConnectionId(connectionId);
+
+ // If user is still null, fallback to an unordered list of Principals from the connection id.
+ if (user == null)
+ {
+ final String[] splitConnectionId = connectionId.split(" ");
+ user = splitConnectionId[1];
+ }
+
+ // use a separate instance of actor as subject is not set on connect/disconnect
+ // we need to pass principal name explicitly into log actor
+ LogActor logActor = new ManagementActor(_rootMessageLogger, user);
+ if (JMXConnectionNotification.OPENED.equals(type))
+ {
+ logActor.message(ManagementConsoleMessages.OPEN(user));
+ }
+ else if (JMXConnectionNotification.CLOSED.equals(type) ||
+ JMXConnectionNotification.FAILED.equals(type))
+ {
+ logActor.message(ManagementConsoleMessages.CLOSE(user));
+ }
+ }
+
+ @Override
+ public boolean isNotificationEnabled(Notification notification)
+ {
+ return notification instanceof JMXConnectionNotification && isLogonTypeEvent(notification);
+ }
+
+ private boolean isLogonTypeEvent(Notification notification)
+ {
+ final String type = notification.getType();
+ return CLOSED.equals(type) || FAILED.equals(type) || OPENED.equals(type);
+ }
+
+}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameAccessor.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameAccessor.java
new file mode 100644
index 0000000000..0cbb0d2687
--- /dev/null
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameAccessor.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.jmx;
+
+public interface UsernameAccessor
+{
+ public String getUsernameForConnectionId(String connectionId);
+
+}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameCachingRMIJRMPServer.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameCachingRMIJRMPServer.java
new file mode 100644
index 0000000000..838e9e5664
--- /dev/null
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/UsernameCachingRMIJRMPServer.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.qpid.server.jmx;
+
+import static javax.management.remote.JMXConnectionNotification.CLOSED;
+import static javax.management.remote.JMXConnectionNotification.FAILED;
+
+import java.io.IOException;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.management.Notification;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.rmi.RMIConnection;
+import javax.management.remote.rmi.RMIJRMPServerImpl;
+import javax.security.auth.Subject;
+
+import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
+
+/**
+ * An implementation of RMIJRMPServerImpl that caches the usernames of users as they log-on
+ * and makes the same available via {@link UsernameAccessor#getUsernameForConnectionId(String)}.
+ *
+ * Caller is responsible for installing this object as a {@link NotificationListener} of the
+ * {@link JMXConnectorServer} so the cache entries are removed as the clients disconnect.
+ *
+ */
+public class UsernameCachingRMIJRMPServer extends RMIJRMPServerImpl implements NotificationListener, NotificationFilter, UsernameAccessor
+{
+ // ConnectionId is guaranteed to be unique per client connection, according to the JMX spec.
+ private final Map<String, String> _connectionIdUsernameMap = new ConcurrentHashMap<String, String>();
+
+ UsernameCachingRMIJRMPServer(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf,
+ Map<String, ?> env) throws IOException
+ {
+ super(port, csf, ssf, env);
+ }
+
+ @Override
+ protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException
+ {
+ final RMIConnection makeClient = super.makeClient(connectionId, subject);
+ final AuthenticatedPrincipal authenticatedPrincipalFromSubject = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject);
+ _connectionIdUsernameMap.put(connectionId, authenticatedPrincipalFromSubject.getName());
+ return makeClient;
+ }
+
+ @Override
+ public String getUsernameForConnectionId(String connectionId)
+ {
+ return _connectionIdUsernameMap.get(connectionId);
+ }
+
+ @Override
+ public void handleNotification(Notification notification, Object handback)
+ {
+ final String connectionId = ((JMXConnectionNotification) notification).getConnectionId();
+ removeConnectionIdFromCache(connectionId);
+ }
+
+ @Override
+ public boolean isNotificationEnabled(Notification notification)
+ {
+ return isClientDisconnectEvent(notification);
+ }
+
+ private void removeConnectionIdFromCache(String connectionId)
+ {
+ _connectionIdUsernameMap.remove(connectionId);
+ }
+
+ private boolean isClientDisconnectEvent(Notification notification)
+ {
+ final String type = notification.getType();
+ return CLOSED.equals(type) || FAILED.equals(type);
+ }
+
+} \ No newline at end of file
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
index 6990a40dee..51dea92775 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
+++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/mbeans/VirtualHostMBean.java
@@ -65,7 +65,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
_managerMBean = new VirtualHostManagerMBean(this);
}
- private void initQueues() throws JMException
+ private void initQueues()
{
synchronized (_children)
{
@@ -73,13 +73,20 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
{
if(!_children.containsKey(queue))
{
- _children.put(queue, new QueueMBean(queue, this));
+ try
+ {
+ _children.put(queue, new QueueMBean(queue, this));
+ }
+ catch(Exception e)
+ {
+ LOGGER.error("Cannot create queue mbean for queue " + queue.getName(), e);
+ }
}
}
}
}
- private void initExchanges() throws JMException
+ private void initExchanges()
{
synchronized (_children)
{
@@ -87,13 +94,20 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
{
if(!_children.containsKey(exchange))
{
- _children.put(exchange, new ExchangeMBean(exchange, this));
+ try
+ {
+ _children.put(exchange, new ExchangeMBean(exchange, this));
+ }
+ catch(Exception e)
+ {
+ LOGGER.error("Cannot create exchange mbean for exchange " + exchange.getName(), e);
+ }
}
}
}
}
- private void initConnections() throws JMException
+ private void initConnections()
{
synchronized (_children)
{
@@ -101,7 +115,14 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
{
if(!_children.containsKey(conn))
{
- _children.put(conn, new ConnectionMBean(conn, this));
+ try
+ {
+ _children.put(conn, new ConnectionMBean(conn, this));
+ }
+ catch(Exception e)
+ {
+ LOGGER.error("Cannot create connection mbean for connection " + conn.getName(), e);
+ }
}
}
}
@@ -119,7 +140,7 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
public void stateChanged(ConfiguredObject object, State oldState, State newState)
{
- // ignore
+ // no-op
}
public void childAdded(ConfiguredObject object, ConfiguredObject child)
@@ -208,4 +229,35 @@ public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtual
return queues;
}
+
+ @Override
+ public void unregister() throws JMException
+ {
+ synchronized (_children)
+ {
+ for (AMQManagedObject mbean : _children.values())
+ {
+ if(mbean != null)
+ {
+ try
+ {
+ mbean.unregister();
+ }
+ catch(JMException e)
+ {
+ LOGGER.error("Failed to remove mbean for child : " + mbean, e);
+ }
+ }
+ }
+ _children.clear();
+ }
+ _managerMBean.unregister();
+ }
+
+ @Override
+ public void attributeSet(ConfiguredObject object, String attributeName, Object oldAttributeValue, Object newAttributeValue)
+ {
+ // no-op
+ }
+
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ManagementFactory b/qpid/java/broker-plugins/management-jmx/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.PluginFactory
index 8fa778269e..8fa778269e 100644
--- a/qpid/java/broker-plugins/management-jmx/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.ManagementFactory
+++ b/qpid/java/broker-plugins/management-jmx/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.PluginFactory
diff --git a/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/JMXManagementFactoryTest.java b/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/JMXManagementFactoryTest.java
index 6b6d4018aa..5af1369239 100644
--- a/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/JMXManagementFactoryTest.java
+++ b/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/JMXManagementFactoryTest.java
@@ -19,33 +19,42 @@
package org.apache.qpid.server.jmx;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import org.apache.qpid.server.configuration.ServerConfiguration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.plugin.PluginFactory;
import org.apache.qpid.test.utils.QpidTestCase;
public class JMXManagementFactoryTest extends QpidTestCase
{
private final JMXManagementFactory _jmxManagementFactory = new JMXManagementFactory();
- private final ServerConfiguration _serverConfiguration = mock(ServerConfiguration.class);
+ private final Map<String, Object> _attributes = new HashMap<String, Object>();
private final Broker _broker = mock(Broker.class);
+ private UUID _id = UUID.randomUUID();
public void testJMXConfigured() throws Exception
{
- when(_serverConfiguration.getJMXManagementEnabled()).thenReturn(true);
+ _attributes.put(PluginFactory.PLUGIN_TYPE, JMXManagement.PLUGIN_TYPE);
- JMXManagement jmxManagement = _jmxManagementFactory.createInstance(_serverConfiguration, _broker);
+ JMXManagement jmxManagement = (JMXManagement) _jmxManagementFactory.createInstance(_id, _attributes, _broker);
assertNotNull(jmxManagement);
+ assertEquals("Unexpected plugin type", JMXManagement.PLUGIN_TYPE, jmxManagement.getAttribute(JMXManagementFactory.PLUGIN_TYPE));
+ assertEquals("Unexpected default mbean platform", JMXManagement.DEFAULT_USE_PLATFORM_MBEAN_SERVER, jmxManagement.getAttribute(JMXManagement.USE_PLATFORM_MBEAN_SERVER));
+ assertEquals("Unexpected default name", JMXManagement.DEFAULT_NAME, jmxManagement.getAttribute(JMXManagement.NAME));
}
- public void testJMXNotConfigured() throws Exception
+ public void testCreateInstanceReturnsNullWhenPluginTypeMissing()
{
- when(_serverConfiguration.getJMXManagementEnabled()).thenReturn(false);
-
- JMXManagement jmxManagement = _jmxManagementFactory.createInstance(_serverConfiguration, _broker);
+ assertNull(_jmxManagementFactory.createInstance(_id, _attributes, _broker));
+ }
- assertNull(jmxManagement);
+ public void testCreateInstanceReturnsNullWhenPluginTypeNotJmx()
+ {
+ _attributes.put(PluginFactory.PLUGIN_TYPE, "notJmx");
+ assertNull(_jmxManagementFactory.createInstance(_id, _attributes, _broker));
}
}
diff --git a/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogActorTest.java b/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogActorTest.java
deleted file mode 100644
index b055cc476f..0000000000
--- a/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogActorTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.jmx;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.management.JMException;
-import javax.management.MBeanServerConnection;
-import javax.management.MBeanServerInvocationHandler;
-import javax.management.ObjectName;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXConnectorFactory;
-import javax.management.remote.JMXServiceURL;
-
-import org.apache.commons.configuration.XMLConfiguration;
-import org.apache.qpid.server.configuration.ServerConfiguration;
-import org.apache.qpid.server.logging.actors.CurrentActor;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.security.Result;
-import org.apache.qpid.server.security.AccessControl;
-import org.apache.qpid.server.security.access.ObjectProperties;
-import org.apache.qpid.server.security.access.ObjectType;
-import org.apache.qpid.server.security.access.Operation;
-import org.apache.qpid.server.util.TestApplicationRegistry;
-import org.apache.qpid.test.utils.QpidTestCase;
-
-public class ManagementLogActorTest extends QpidTestCase
-{
- private ApplicationRegistry _registry;
- private JMXManagedObjectRegistry _objectRegistry;
- private int _registryPort;
- private int _connectorPort;
- private TestPlugin _plugin;
-
- @Override
- public void setUp() throws Exception
- {
- super.setUp();
-
- _registryPort = findFreePort();
- _connectorPort = getNextAvailable(_registryPort + 1);
-
- //Start a TestApplicationRegistry with (JMX) management disabled, because we
- //will instantiate our own directly in order to manipulate it for the test.
- XMLConfiguration config = new XMLConfiguration();
- config.addProperty("management.enabled", "false");
- _registry = new TestApplicationRegistry(new ServerConfiguration(config));
- ApplicationRegistry.initialise(_registry);
-
- _plugin = new TestPlugin();
- _registry.getSecurityManager().addHostPlugin(_plugin);
-
- //Now start up a test JMXManagedObjectRegistry directly
- XMLConfiguration jmxConfig = new XMLConfiguration();
- jmxConfig.addProperty(ServerConfiguration.MGMT_JMXPORT_REGISTRYSERVER, _registryPort + "");
- jmxConfig.addProperty(ServerConfiguration.MGMT_JMXPORT_CONNECTORSERVER, _connectorPort + "");
-
- _objectRegistry = new JMXManagedObjectRegistry(new ServerConfiguration(jmxConfig));
- new TestMBean(_objectRegistry);
- _objectRegistry.start();
- }
-
- public void tearDown() throws Exception
- {
- _objectRegistry.close();
- ApplicationRegistry.remove();
- super.tearDown();
- }
-
- public void testPrincipalInLogMessage() throws Throwable
- {
- Map<String, Object> environment = new HashMap<String, Object>();
- environment.put(JMXConnector.CREDENTIALS, new String[] { "admin", "admin" });
- String urlString = "service:jmx:rmi:///jndi/rmi://localhost:" + _registryPort + "/jmxrmi";
- JMXServiceURL url = new JMXServiceURL(urlString);
- JMXConnector jmxConnector = JMXConnectorFactory.connect(url, environment);
- MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
- ObjectName mbeanObject = new ObjectName("org.apache.qpid:type=TestMBean,name=test");
-
- CurrentActorRetriever mbean = MBeanServerInvocationHandler.newProxyInstance(mbsc,
- mbeanObject, CurrentActorRetriever.class, false);
- String actorLogMessage = mbean.getActorLogMessage();
-
- assertTrue("Unexpected log principal in security plugin", _plugin.getLogMessage().startsWith("[mng:admin"));
- assertTrue("Unexpected log principal in MBean", actorLogMessage.startsWith("[mng:admin"));
- }
-
- public static class TestMBean extends DefaultManagedObject implements CurrentActorRetriever
- {
-
- public TestMBean(ManagedObjectRegistry registry) throws JMException
- {
- super(CurrentActorRetriever.class, "TestMBean", registry);
- register();
- }
-
- @Override
- public String getObjectInstanceName()
- {
- return "test";
- }
-
- @Override
- public ManagedObject getParentObject()
- {
- return null;
- }
-
- @Override
- public String getActorLogMessage()
- {
- return CurrentActor.get().getLogMessage();
- }
-
- }
-
- public static interface CurrentActorRetriever
- {
- String getActorLogMessage();
- }
-
- public static class TestPlugin implements AccessControl
- {
- private String _logMessage;
-
- @Override
- public Result getDefault()
- {
- return Result.ALLOWED;
- }
-
- @Override
- public Result access(ObjectType objectType, Object instance)
- {
- return Result.ALLOWED;
- }
-
- @Override
- public Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties)
- {
- // set thread name to work around logic in MangementActor
- Thread.currentThread().setName("RMI TCP Connection(1)-" + System.currentTimeMillis());
- _logMessage = CurrentActor.get().getLogMessage();
- return Result.ALLOWED;
- }
-
- public String getLogMessage()
- {
- return _logMessage;
- }
-
- }
-
-}
diff --git a/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporterTest.java b/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporterTest.java
new file mode 100644
index 0000000000..ba9c2cdaa5
--- /dev/null
+++ b/qpid/java/broker-plugins/management-jmx/src/test/java/org/apache/qpid/server/jmx/ManagementLogonLogoffReporterTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.jmx;
+
+import static javax.management.remote.JMXConnectionNotification.OPENED;
+import static javax.management.remote.JMXConnectionNotification.CLOSED;
+import static javax.management.remote.JMXConnectionNotification.FAILED;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+
+import javax.management.remote.JMXConnectionNotification;
+
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.RootMessageLogger;
+
+import junit.framework.TestCase;
+
+public class ManagementLogonLogoffReporterTest extends TestCase
+{
+ private static final String TEST_JMX_UNIQUE_CONNECTION_ID = "jmxconnectionid1 jmxuser,group";
+ private static final String TEST_USER = "jmxuser";
+
+ private ManagementLogonLogoffReporter _reporter;
+ private UsernameAccessor _usernameAccessor;
+ private RootMessageLogger _rootMessageLogger;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ _usernameAccessor = mock(UsernameAccessor.class);
+ _rootMessageLogger = mock(RootMessageLogger.class);
+ // Enable messaging so we can valid the generated strings
+ when(_rootMessageLogger.isMessageEnabled(any(LogActor.class), anyString())).thenReturn(true);
+
+ _reporter = new ManagementLogonLogoffReporter(_rootMessageLogger, _usernameAccessor);
+ }
+
+ public void testOpenedNotification()
+ {
+ when(_usernameAccessor.getUsernameForConnectionId(TEST_JMX_UNIQUE_CONNECTION_ID)).thenReturn(TEST_USER);
+ JMXConnectionNotification openNotification = createMockNotification(TEST_JMX_UNIQUE_CONNECTION_ID, OPENED);
+
+ _reporter.handleNotification(openNotification, null);
+
+ verify(_rootMessageLogger).rawMessage("[main] MNG-1007 : Open : User jmxuser", "qpid.message.managementconsole.open");
+ }
+
+ public void testClosedNotification()
+ {
+ when(_usernameAccessor.getUsernameForConnectionId(TEST_JMX_UNIQUE_CONNECTION_ID)).thenReturn(TEST_USER);
+ JMXConnectionNotification closeNotification = createMockNotification(TEST_JMX_UNIQUE_CONNECTION_ID, CLOSED);
+
+ _reporter.handleNotification(closeNotification, null);
+
+ verify(_rootMessageLogger).rawMessage("[main] MNG-1008 : Close : User jmxuser", "qpid.message.managementconsole.close");
+ }
+
+ public void tesNotifiedForLogOnTypeEvents()
+ {
+ JMXConnectionNotification openNotification = createMockNotification(TEST_JMX_UNIQUE_CONNECTION_ID, OPENED);
+ JMXConnectionNotification closeNotification = createMockNotification(TEST_JMX_UNIQUE_CONNECTION_ID, CLOSED);
+ JMXConnectionNotification failedNotification = createMockNotification(TEST_JMX_UNIQUE_CONNECTION_ID, FAILED);
+
+ assertTrue(_reporter.isNotificationEnabled(openNotification));
+ assertTrue(_reporter.isNotificationEnabled(closeNotification));
+ assertTrue(_reporter.isNotificationEnabled(failedNotification));
+
+ JMXConnectionNotification otherNotification = createMockNotification(TEST_JMX_UNIQUE_CONNECTION_ID, "other");
+ assertFalse(_reporter.isNotificationEnabled(otherNotification));
+ }
+
+ private JMXConnectionNotification createMockNotification(String connectionId, String notificationType)
+ {
+ JMXConnectionNotification notification = mock(JMXConnectionNotification.class);
+ when(notification.getConnectionId()).thenReturn(connectionId);
+ when(notification.getType()).thenReturn(notificationType);
+ return notification;
+ }
+}