AMQ-7094 - track the objectName with an annotated mbean such that the jmx audit log event can extract that target of an mbean operation, fix and test

This commit is contained in:
gtully 2018-11-08 12:10:01 +00:00
parent 85859fd8dc
commit d2b0affedb
6 changed files with 120 additions and 10 deletions

View File

@ -68,6 +68,8 @@ public class AnnotatedMBean extends StandardMBean {
} }
} }
private final ObjectName objectName;
private static byte byteFromProperty(String s) { private static byte byteFromProperty(String s) {
byte val = OFF; byte val = OFF;
String config = System.getProperty(s, "").toLowerCase(Locale.ENGLISH); String config = System.getProperty(s, "").toLowerCase(Locale.ENGLISH);
@ -88,7 +90,7 @@ public class AnnotatedMBean extends StandardMBean {
for (Class c : object.getClass().getInterfaces()) { for (Class c : object.getClass().getInterfaces()) {
if (mbeanName.equals(c.getName())) { if (mbeanName.equals(c.getName())) {
context.registerMBean(new AnnotatedMBean(object, c), objectName); context.registerMBean(new AnnotatedMBean(object, c, objectName), objectName);
return; return;
} }
} }
@ -97,13 +99,15 @@ public class AnnotatedMBean extends StandardMBean {
} }
/** Instance where the MBean interface is implemented by another object. */ /** Instance where the MBean interface is implemented by another object. */
public <T> AnnotatedMBean(T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException { public <T> AnnotatedMBean(T impl, Class<T> mbeanInterface, ObjectName objectName) throws NotCompliantMBeanException {
super(impl, mbeanInterface); super(impl, mbeanInterface);
this.objectName = objectName;
} }
/** Instance where the MBean interface is implemented by this object. */ /** Instance where the MBean interface is implemented by this object. */
protected AnnotatedMBean(Class<?> mbeanInterface) throws NotCompliantMBeanException { protected AnnotatedMBean(Class<?> mbeanInterface, ObjectName objectName) throws NotCompliantMBeanException {
super(mbeanInterface); super(mbeanInterface);
this.objectName = objectName;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -212,6 +216,7 @@ public class AnnotatedMBean extends StandardMBean {
entry = new JMXAuditLogEntry(); entry = new JMXAuditLogEntry();
entry.setUser(caller); entry.setUser(caller);
entry.setTimestamp(System.currentTimeMillis()); entry.setTimestamp(System.currentTimeMillis());
entry.setTarget(extractTargetTypeProperty(objectName));
entry.setOperation(this.getMBeanInfo().getClassName() + "." + s); entry.setOperation(this.getMBeanInfo().getClassName() + "." + s);
try try
@ -245,6 +250,21 @@ public class AnnotatedMBean extends StandardMBean {
return result; return result;
} }
// keep brokerName last b/c objectNames include the brokerName
final static String[] targetPropertiesCandidates = new String[] {"destinationName", "networkConnectorName", "connectorName", "connectionName", "brokerName"};
private String extractTargetTypeProperty(ObjectName objectName) {
String result = null;
for (String attr: targetPropertiesCandidates) {
try {
result = objectName.getKeyProperty(attr);
if (result != null) {
break;
}
} catch (NullPointerException ok) {}
}
return result;
}
private Method getMBeanMethod(Class clazz, String methodName, String[] signature) throws ReflectiveOperationException { private Method getMBeanMethod(Class clazz, String methodName, String[] signature) throws ReflectiveOperationException {
Class[] parameterTypes = new Class[signature.length]; Class[] parameterTypes = new Class[signature.length];
for (int i = 0; i < signature.length; i++) { for (int i = 0; i < signature.length; i++) {

View File

@ -36,15 +36,15 @@ public class AsyncAnnotatedMBean extends AnnotatedMBean {
private ExecutorService executor; private ExecutorService executor;
private long timeout = 0; private long timeout = 0;
public <T> AsyncAnnotatedMBean(ExecutorService executor, long timeout, T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException { public <T> AsyncAnnotatedMBean(ExecutorService executor, long timeout, T impl, Class<T> mbeanInterface, ObjectName objectName) throws NotCompliantMBeanException {
super(impl, mbeanInterface); super(impl, mbeanInterface, objectName);
this.executor = executor; this.executor = executor;
this.timeout = timeout; this.timeout = timeout;
} }
protected AsyncAnnotatedMBean(Class<?> mbeanInterface) throws NotCompliantMBeanException { protected AsyncAnnotatedMBean(Class<?> mbeanInterface, ObjectName objectName) throws NotCompliantMBeanException {
super(mbeanInterface); super(mbeanInterface, objectName);
} }
protected Object asyncInvole(String s, Object[] objects, String[] strings) throws MBeanException, ReflectionException { protected Object asyncInvole(String s, Object[] objects, String[] strings) throws MBeanException, ReflectionException {
@ -67,9 +67,9 @@ public class AsyncAnnotatedMBean extends AnnotatedMBean {
for (Class c : object.getClass().getInterfaces()) { for (Class c : object.getClass().getInterfaces()) {
if (mbeanName.equals(c.getName())) { if (mbeanName.equals(c.getName())) {
if (timeout == 0) { if (timeout == 0) {
context.registerMBean(new AnnotatedMBean(object, c), objectName); context.registerMBean(new AnnotatedMBean(object, c, objectName), objectName);
} else { } else {
context.registerMBean(new AsyncAnnotatedMBean(executor, timeout, object, c), objectName); context.registerMBean(new AsyncAnnotatedMBean(executor, timeout, object, c, objectName), objectName);
} }
return; return;
} }

View File

@ -21,14 +21,25 @@ import java.util.Arrays;
public class JMXAuditLogEntry extends AuditLogEntry { public class JMXAuditLogEntry extends AuditLogEntry {
public static final String[] VERBS = new String[] {" called ", " ended "}; public static final String[] VERBS = new String[] {" called ", " ended "};
private int state = 0; private int state = 0;
protected String target;
public void complete() { public void complete() {
setTimestamp(System.currentTimeMillis()); setTimestamp(System.currentTimeMillis());
state = 1; state = 1;
} }
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
@Override @Override
public String toString() { public String toString() {
return user.trim() + VERBS[state] + operation + Arrays.toString((Object[])parameters.get("arguments")) + " at " + getFormattedTime(); return user.trim() + VERBS[state] + operation + Arrays.toString((Object[])parameters.get("arguments"))
+ (target != null ? " on " + target : "")
+ " at " + getFormattedTime();
} }
} }

View File

@ -151,6 +151,7 @@ public class DLQRetryTest extends EmbeddedBrokerTestSupport {
} }
protected void setUp() throws Exception { protected void setUp() throws Exception {
System.setProperty("org.apache.activemq.audit", "all");
bindAddress = "tcp://localhost:0"; bindAddress = "tcp://localhost:0";
useTopic = false; useTopic = false;
super.setUp(); super.setUp();

View File

@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.TestSupport; import org.apache.activemq.TestSupport;
import org.apache.activemq.broker.BrokerService; import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.TransportConnector;
import org.apache.activemq.broker.jmx.BrokerMBeanSupport;
import org.apache.activemq.broker.jmx.ManagementContext; import org.apache.activemq.broker.jmx.ManagementContext;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
@ -62,10 +64,13 @@ public class JmxAuditLogTest extends TestSupport
broker = new BrokerService(); broker = new BrokerService();
broker.setUseJmx(true); broker.setUseJmx(true);
broker.setDeleteAllMessagesOnStartup(true);
portToUse = findOpenPort(); portToUse = findOpenPort();
broker.setManagementContext(createManagementContext("broker", portToUse)); broker.setManagementContext(createManagementContext("broker", portToUse));
broker.setPopulateUserNameInMBeans(true); broker.setPopulateUserNameInMBeans(true);
broker.setDestinations(createDestinations()); broker.setDestinations(createDestinations());
TransportConnector transportConnector = broker.addConnector("tcp://0.0.0.0:0");
transportConnector.setName("TCP");
broker.start(); broker.start();
} }
@ -154,4 +159,74 @@ public class JmxAuditLogTest extends TestSupport
assertEquals("got two messages", 2, logCount.get()); assertEquals("got two messages", 2, logCount.get());
} }
@Test
public void testNameTargetVisible() throws Exception
{
Logger log4jLogger = Logger.getLogger("org.apache.activemq.audit");
log4jLogger.setLevel(Level.INFO);
final AtomicInteger logCount = new AtomicInteger(0);
final AtomicBoolean gotEnded = new AtomicBoolean(false);
final AtomicBoolean gotQueueName = new AtomicBoolean(false);
final AtomicBoolean gotBrokerName = new AtomicBoolean(false);
final AtomicBoolean gotConnectorName = new AtomicBoolean(false);
final String queueName = queue.getQueueName();
Appender appender = new DefaultTestAppender()
{
@Override
public void doAppend(LoggingEvent event)
{
if (event.getMessage() instanceof String)
{
String message = (String) event.getMessage();
System.out.println(message);
if (message.contains(VERBS[0])) {
if (message.contains(queueName)) {
gotQueueName.set(true);
}
if (message.contains(broker.getBrokerName())) {
gotBrokerName.set(true);
}
if (message.contains("TCP")) {
gotConnectorName.set(true);
}
}
if (message.contains(VERBS[1])) {
gotEnded.set(true);
}
}
logCount.incrementAndGet();
}
};
log4jLogger.addAppender(appender);
MBeanServerConnection conn = createJMXConnector(portToUse);
ObjectName queueObjName = new ObjectName(broker.getBrokerObjectName() + ",destinationType=Queue,destinationName=" + queueName);
Object[] params = {};
String[] signature = {};
conn.invoke(queueObjName, "purge", params, signature);
assertTrue("got ended statement", gotEnded.get());
assertEquals("got two messages", 2, logCount.get());
assertTrue("got queueName in called statement", gotQueueName.get());
// call broker to verify brokerName
conn.invoke(broker.getBrokerObjectName(), "resetStatistics", params, signature);
assertEquals("got 4 messages", 4, logCount.get());
assertTrue("got brokerName in called statement", gotBrokerName.get());
ObjectName transportConnectorON = BrokerMBeanSupport.createConnectorName(broker.getBrokerObjectName(), "clientConnectors", "TCP");
conn.invoke(transportConnectorON, "stop", params, signature);
assertEquals("got messages", 6, logCount.get());
assertTrue("got connectorName in called statement", gotConnectorName.get());
log4jLogger.removeAppender(appender);
}
} }

View File

@ -38,6 +38,9 @@ public class JmxCreateNCTest {
@Test @Test
public void testBridgeRegistration() throws Exception { public void testBridgeRegistration() throws Exception {
System.setProperty("org.apache.activemq.audit", "all");
BrokerService broker = new BrokerService(); BrokerService broker = new BrokerService();
broker.setBrokerName(BROKER_NAME); broker.setBrokerName(BROKER_NAME);
broker.setUseJmx(true); // explicitly set this so no funny issues broker.setUseJmx(true); // explicitly set this so no funny issues