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

(cherry picked from commit d2b0affedb38c5439bce2fb5a8e321bc5d0ec713)
This commit is contained in:
gtully 2018-11-08 12:10:01 +00:00 committed by Christopher L. Shannon (cshannon)
parent 509c781669
commit 70b2a43183
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) {
byte val = OFF;
String config = System.getProperty(s, "").toLowerCase(Locale.ENGLISH);
@ -88,7 +90,7 @@ public class AnnotatedMBean extends StandardMBean {
for (Class c : object.getClass().getInterfaces()) {
if (mbeanName.equals(c.getName())) {
context.registerMBean(new AnnotatedMBean(object, c), objectName);
context.registerMBean(new AnnotatedMBean(object, c, objectName), objectName);
return;
}
}
@ -97,13 +99,15 @@ public class AnnotatedMBean extends StandardMBean {
}
/** 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);
this.objectName = objectName;
}
/** 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);
this.objectName = objectName;
}
/** {@inheritDoc} */
@ -212,6 +216,7 @@ public class AnnotatedMBean extends StandardMBean {
entry = new JMXAuditLogEntry();
entry.setUser(caller);
entry.setTimestamp(System.currentTimeMillis());
entry.setTarget(extractTargetTypeProperty(objectName));
entry.setOperation(this.getMBeanInfo().getClassName() + "." + s);
try
@ -245,6 +250,21 @@ public class AnnotatedMBean extends StandardMBean {
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 {
Class[] parameterTypes = new Class[signature.length];
for (int i = 0; i < signature.length; i++) {

View File

@ -36,15 +36,15 @@ public class AsyncAnnotatedMBean extends AnnotatedMBean {
private ExecutorService executor;
private long timeout = 0;
public <T> AsyncAnnotatedMBean(ExecutorService executor, long timeout, T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException {
super(impl, mbeanInterface);
public <T> AsyncAnnotatedMBean(ExecutorService executor, long timeout, T impl, Class<T> mbeanInterface, ObjectName objectName) throws NotCompliantMBeanException {
super(impl, mbeanInterface, objectName);
this.executor = executor;
this.timeout = timeout;
}
protected AsyncAnnotatedMBean(Class<?> mbeanInterface) throws NotCompliantMBeanException {
super(mbeanInterface);
protected AsyncAnnotatedMBean(Class<?> mbeanInterface, ObjectName objectName) throws NotCompliantMBeanException {
super(mbeanInterface, objectName);
}
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()) {
if (mbeanName.equals(c.getName())) {
if (timeout == 0) {
context.registerMBean(new AnnotatedMBean(object, c), objectName);
context.registerMBean(new AnnotatedMBean(object, c, objectName), objectName);
} else {
context.registerMBean(new AsyncAnnotatedMBean(executor, timeout, object, c), objectName);
context.registerMBean(new AsyncAnnotatedMBean(executor, timeout, object, c, objectName), objectName);
}
return;
}

View File

@ -21,14 +21,25 @@ import java.util.Arrays;
public class JMXAuditLogEntry extends AuditLogEntry {
public static final String[] VERBS = new String[] {" called ", " ended "};
private int state = 0;
protected String target;
public void complete() {
setTimestamp(System.currentTimeMillis());
state = 1;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
@Override
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 {
System.setProperty("org.apache.activemq.audit", "all");
bindAddress = "tcp://localhost:0";
useTopic = false;
super.setUp();

View File

@ -30,6 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.TestSupport;
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.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue;
@ -62,10 +64,13 @@ public class JmxAuditLogTest extends TestSupport
broker = new BrokerService();
broker.setUseJmx(true);
broker.setDeleteAllMessagesOnStartup(true);
portToUse = findOpenPort();
broker.setManagementContext(createManagementContext("broker", portToUse));
broker.setPopulateUserNameInMBeans(true);
broker.setDestinations(createDestinations());
TransportConnector transportConnector = broker.addConnector("tcp://0.0.0.0:0");
transportConnector.setName("TCP");
broker.start();
}
@ -154,4 +159,74 @@ public class JmxAuditLogTest extends TestSupport
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
public void testBridgeRegistration() throws Exception {
System.setProperty("org.apache.activemq.audit", "all");
BrokerService broker = new BrokerService();
broker.setBrokerName(BROKER_NAME);
broker.setUseJmx(true); // explicitly set this so no funny issues