ReadOnlyInvokeHandler.java
package com.ziesemer.utils.jmx.server.authorization;
import java.lang.management.ManagementFactory;
import java.util.logging.LogManager;
import javax.management.MBeanOperationInfo;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
/**
* <p>Checks for attempts to call "read-only" methods.</p>
* <p>Default implementation allows all calls to
* {@link javax.management.MBeanServer#invoke(ObjectName, String, Object[], String[])}
* where ({@link JmxAuthorizeContext#getImpact()} == {@link MBeanOperationInfo#INFO},
* or {@link MBeanOperationInfo#UNKNOWN} with an <code>operationName</code>
* checked against {@link #isReadOnlyName(String)}).
* Additionally allows "<code>dumpAllThreads</code>" on {@link java.lang.management.ThreadMXBean}
* only (introduced in Java 1.6 / 6).</p>
* <p>Unfortunately, under Java 1.5 / 5.0, this currently denies access to
* <code>get</code>* methods on {@link java.lang.management.ThreadMXBean}
* and {@link java.util.logging.LoggingMXBean}, as these operations are marked as
* {@link MBeanOperationInfo#ACTION_INFO} rather than {@link MBeanOperationInfo#UNKNOWN}.
* This appears to have been somewhat fixed in Java 1.6 / 6 (by using
* {@link MBeanOperationInfo#UNKNOWN}, even though {@link MBeanOperationInfo#INFO}
* would be more appropriate.
* (<a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6320104"
* >http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6320104</a>? /
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6933325"
* >http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6933325</a>.)
* Use {@link ReadOnlyJavaFixInvokeHandler} as a work-around for Java 1.5 / 5.0.</p>
* @author Mark A. Ziesemer
* <a href="http://www.ziesemer.com."><www.ziesemer.com></a>
*/
public class ReadOnlyInvokeHandler implements IJmxInvocationAuthorizer{
/**
* {@link ObjectName} for {@link ManagementFactory#THREAD_MXBEAN_NAME}.
*/
protected static final ObjectName THREAD_MX_ON;
/**
* {@link ObjectName} for {@link LogManager#LOGGING_MXBEAN_NAME}.
*/
protected static final ObjectName LOGGING_MX_ON;
static{
try{
THREAD_MX_ON = ObjectName.getInstance(ManagementFactory.THREAD_MXBEAN_NAME);
LOGGING_MX_ON = ObjectName.getInstance(LogManager.LOGGING_MXBEAN_NAME);
}catch(final MalformedObjectNameException mone){
// Should never happen.
throw new RuntimeException(mone.getMessage(), mone);
}
}
public Object invoke(final JmxAuthorizeContext ac) throws Throwable{
if(isAllow(ac)){
return ac.invoke();
}
throw new SecurityException("Access denied (non-read-only invoke)");
}
protected boolean isAllow(final JmxAuthorizeContext ac) throws Exception{
if(isDefaultReadOnlyOperation(ac)){
return true;
}
final ObjectName on = (ObjectName)ac.args[0];
final String methodName = (String)ac.args[1];
if(THREAD_MX_ON.equals(on) && isThreadReadOnlyName(methodName)){
return true;
}
return false;
}
protected boolean isReadOnlyName(final String name){
return name.startsWith("get")
|| name.startsWith("is")
|| name.startsWith("list")
|| name.startsWith("query")
|| "hashCode".equals(name);
}
protected boolean isThreadReadOnlyName(final String name){
return "dumpAllThreads".equals(name);
}
protected boolean isDefaultReadOnlyOperation(final JmxAuthorizeContext ac) throws Exception{
final int impact = ac.getImpact();
return (impact == MBeanOperationInfo.INFO)
|| (impact == MBeanOperationInfo.UNKNOWN && isReadOnlyName((String)ac.args[1]));
}
}