Following my recent post on JMX Secure Connections / Avoiding Java System Properties, I am making another addition to MarkUtils: MarkUtils-JMX.
JMX Management Bean Metadata
My primary inspiration for this library was that JMX provides a generous amount of metadata along with each management bean, attribute, operation, and parameter - including names, descriptions, and impacts
(INFO
, ACTION_INFO
, ACTION
, or UNKNOWN
) for operations.
Parameter names must be provided through metadata, otherwise only the generated defaults of p0
, p1
, etc. are displayed.
Unfortunately, associating this data with a management bean is currently a pain, and the only support available for this through the JDK itself is by subclassing StandardMBean
,
or completely instantiating a MBeanInfo
yourself, and returning from a subclass of StandardMBean
or from a custom
DynamicMBean
implementation.
MarkUtils-JMX provides a MBeanInfoBuilder
class that serves as an alternative to the non-public classes used to create MBeanInfo
's within the JDK.
The most significant feature of MBeanInfoBuilder
is support for including names, descriptions, and impacts that are read from Java annotations.
The supported annotations are provided as part of this library in the com.ziesemer.utils.jmx.beanInfo
package.
Similar functionality is planned for release with Java 7 as part of JMX 2.0 / JSR 255, as detailed by Eamonn McManus in Playing with the JMX 2.0 API (2008-08-06, weblogs.java.net).
Unfortunately, while I'm sure the annotations will not be compatible with my own (even just due to mine being in a com.ziesemer
package), mine are available immediately, and support both Java 1.5 / 5.0 and Java 1.6 / 6.
MBeanInfoBuilder
is not intended to be a complete replacement, as it currently doesn't support constructors, notifications, and descriptors, mostly as I currently have no use for them.
However, these limitations are almost completely mitigated by MBeanInfoCombiner
, which allows the metadata from a custom MBeanInfo
built by MBeanInfoBuilder
to supplement the default MBeanInfo
built by the JDK's StandardMBean
- effectively adding support for the custom annotations.
SimpleMBean
, also included, is a alternative to the JDK's StandardMBean
(as with MBeanInfoBuilder
, and doesn't depend upon non-public classes.
Along with utilizing MBeanInfoBuilder
by default, this allows for easy extension / customization of the implementation.
Additionally, both MBeanInfoBuilder
and SimpleMBean
are designed and written with performance as a primary focus.
Performance is more of a concern for the actual MBean implementation than the building of the MBeanInfo
, as the information should only have to be built once, where as the MBean implementation will likely be called repeatedly and often throughout the lifetime of the hosting application.
Authentication and Authorization
Also included in this library are classes to handle most of the work around securing JMX access by providing authentication and authorization.
Authentication is the simpler half, and is provided by com.ziesemer.utils.jmx.server.authentication.BaseJmxAuthenticator
.
This abstract class handles all the validation and breaking-down of the Object
input parameter.
An implementing class must only implement Subject authenticate(String username, String password)
.
The authentication implementation is then registered as a value to the JMXConnectorServer.AUTHENTICATOR
property,
and passed-in as part of the environment map to JMXConnectorServerFactory.newJMXConnectorServer
.
Authorization is a little more complex.
Limited support is built-in to the JDK through Java's security policy and MBeanPermission
.
Unfortunately, the JDK approach seems overly-complex and did not meet several of my requirements.
This library provides a high-performance and flexible alternative, BaseJmxAuthorizer
.
BaseJmxAuthorizer
and the other classes in com.ziesemer.utils.jmx.server.authorization
are based on "Use Case 2" in Luis-Miguel Alventosa's blog post,
Authentication and Authorization in JMX RMI connectors (2006-09-25, blogs.sun.com),
where an InvocationHandler
is used to selectively proxy requests to the management bean.
Again, BaseJmxAuthorizer
was designed and written with performance as a primary focus, along with flexibility.
It is based on a Map
(implemented with a HashMap
),
associating incoming method names (e.g. getAttribute
or invoke
) with an associated handler.
This Map
is publicly exposed, allowing for easy customization through modifications made to the map.
Several default handlers, including the simple and unconditional AllowHandler
and DenyHandler
, are included in the package.
A ReadOnlyInvokeHandler
is also provided, and allows all calls to operations / methods that are considered to be "read-only".
The request impact (MBeanOperationInfo.getImpact()
) is first consulted, and the request is allowed if INFO
is returned.
The request is otherwise denied, unless the impact returned is UNKNOWN
, in which case an attempt to determine if the method has a "read-only" name is made,
which checks for if the method name starts with "get
", "is
", "list
", "query
", or is equal to "hashCode
".
By default, it also allows for calls to ThreadMXBean.dumpAllThreads
.
These allowances allow for full and easy use of all "read-only" functionality provided by JConsole by default, without compromising on security.
Unfortunately, under Java 1.5 / 5.0, this currently denies access to get*
methods on ThreadMXBean
and LoggingMXBean
, as these operations are marked as
ACTION_INFO
rather than
UNKNOWN
(or more preferably / correctly, INFO
).
This appears to have been somewhat fixed in Java 1.6 / 6, as per Sun Bug 6320104, it seems that UNKNOWN
was simply not supported by the infrastructure in 1.5 / 5.0 at the time.
A ReadOnlyJavaFixInvokeHandler
is provided in the package as a work-around for use under Java 1.5 / 5.0.
I reported Sun Bug 6933325 to address that these operations should now return INFO
instead of ACTION_INFO
, now that it is possible.
Please view the Javadocs and/or source for additional details.
BaseJmxAuthorizer
handles basic logging of method invocations through SLF4J by default.
This library's BaseJmxAuthorizer
also simplifies authentication and improves performance by associating permissions with a subclass of JMXPrincipal
, JmxPermissionPrincipal
.
This allows permissions to be obtained for a user once during authorization, without having to re-check permissions on each JMX call.
Just be sure that this caching is accounted for, such that a user doesn't maintain unattended access if / once a permission is removed by the underlying security directory.
A few options could be implementing a session timeout, or adding a call to a customized implementation to also remove the permissions from the runtime if / once removed from the user.
JmxPermissionPrincipal
maintains an instance of a subclass of PermissionCollection
, JmxPermissions
,
which contains one or more JmxPermission
(subclass of Permission
instances.
Default provided JmxPermission
's are CONNECT
, READ_EX
, INVOKE
, and ADMIN
.
Other subclasses may be created and used, but should be kept as singletons.
Please view the Javadocs and/or source for additional details.
Packaging with Apache Maven, Java versions
As with all my other MarkUtils libraries, MarkUtils-JMX is configured, compiled, tested, and packaged using Apache Maven. This library posed a little bit of a challenge, however, as I wanted to provide 2 versions, one built for Java 1.5 / 5.0, and one for Java 1.6 / 6. This is one of the few times I wish that Java had standard support for conditional compilation directives, such as those supported by the C preprocessor.
The primary reason for requiring dual versions is for Java 6's support of MXBean
's, which were not supported in Java 5.
This includes use of the additional 2-arg constructor added to StandardMBean
,
which as far as I can see, is impossible to implement without being available at compile-time - even if considering reflection tricks.
The solution I decided on for now is to offer dual packages / builds: com.ziesemer.utils.jmx.java5
and com.ziesemer.utils.jmx.java6
.
The Java 6 version includes the source paths from the Java 5 version.
To clarify, only the Java 5 or the Java 6 version is required, as the Java 6 version is a superset of the Java 5 version.
The Java 5 version includes a StandardMBeanInfoCombiner5
class that is a subclass of StandardMBean
and utilizes MBeanInfoBuilder
and MBeanInfoCombiner
.
The Java 6 version includes a StandardMBeanInfoCombiner6
, which is the same as the Java 5 version, but exposes the added constructor and allows use of MXBeans.
This setup actually worked with Maven fairly well, including the ability to automatically run Maven builds against both versions in one operation, and including packaging the build outputs together into the final distribution.
The Java 6 version also includes a DefaultRegistration
class, which registers a few additional custom MXBeans that I found helpful and that provide functionality not currently offered by the MXBeans provided by the JDK (ManagementFactory
).
These include my custom CharsetMXBean
, SecurityMXBean
, and SystemMXBean
.
(As these custom beans themselves don't require any Java 6-specific features or calls at compile time, they are actually included in the Java 5 package.
They just require Java 6 to be registered as MXBeans rather than regular MBeans.)
Download
com.ziesemer.utils.jmx
is available on ziesemer.dev.java.net under the GPL license, complete with source code, a compiled .jar, generated JavaDocs, and a suite of 40+ JUnit tests.
Download the com.ziesemer.utils.jmx-*.zip
distribution from here.
Please report any bugs or feature requests on the java.net Issue Tracker.
No comments:
Post a Comment