001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.activemq.broker.jmx; 018 019 import org.apache.activemq.broker.util.*; 020 import org.slf4j.Logger; 021 import org.slf4j.LoggerFactory; 022 023 import java.lang.annotation.Annotation; 024 import java.lang.reflect.Method; 025 import java.security.AccessController; 026 import java.security.Principal; 027 import java.util.Arrays; 028 import java.util.HashMap; 029 import java.util.Map; 030 031 import javax.management.*; 032 import javax.security.auth.Subject; 033 034 /** 035 * MBean that looks for method/parameter descriptions in the Info annotation. 036 */ 037 public class AnnotatedMBean extends StandardMBean { 038 039 private static final Map<String, Class<?>> primitives = new HashMap<String, Class<?>>(); 040 041 private static final Logger LOG = LoggerFactory.getLogger("org.apache.activemq.audit"); 042 043 private static boolean audit; 044 private static AuditLogService auditLog; 045 046 static { 047 Class<?>[] p = { byte.class, short.class, int.class, long.class, float.class, double.class, char.class, boolean.class, }; 048 for (Class<?> c : p) { 049 primitives.put(c.getName(), c); 050 } 051 audit = "true".equalsIgnoreCase(System.getProperty("org.apache.activemq.audit")); 052 if (audit) { 053 auditLog = AuditLogService.getAuditLog(); 054 } 055 } 056 057 @SuppressWarnings("unchecked") 058 public static void registerMBean(ManagementContext context, Object object, ObjectName objectName) 059 throws Exception { 060 061 String mbeanName = object.getClass().getName() + "MBean"; 062 063 for (Class c : object.getClass().getInterfaces()) { 064 if (mbeanName.equals(c.getName())) { 065 context.registerMBean(new AnnotatedMBean(object, c), objectName); 066 return; 067 } 068 } 069 070 context.registerMBean(object, objectName); 071 } 072 073 /** Instance where the MBean interface is implemented by another object. */ 074 public <T> AnnotatedMBean(T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException { 075 super(impl, mbeanInterface); 076 } 077 078 /** Instance where the MBean interface is implemented by this object. */ 079 protected AnnotatedMBean(Class<?> mbeanInterface) throws NotCompliantMBeanException { 080 super(mbeanInterface); 081 } 082 083 /** {@inheritDoc} */ 084 @Override 085 protected String getDescription(MBeanAttributeInfo info) { 086 087 String descr = info.getDescription(); 088 Method m = getMethod(getMBeanInterface(), "get"+info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1)); 089 if (m == null) 090 m = getMethod(getMBeanInterface(), "is"+info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1)); 091 if (m == null) 092 m = getMethod(getMBeanInterface(), "does"+info.getName().substring(0, 1).toUpperCase()+info.getName().substring(1)); 093 094 if (m != null) { 095 MBeanInfo d = m.getAnnotation(MBeanInfo.class); 096 if (d != null) 097 descr = d.value(); 098 } 099 return descr; 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 protected String getDescription(MBeanOperationInfo op) { 105 106 String descr = op.getDescription(); 107 Method m = getMethod(op); 108 if (m != null) { 109 MBeanInfo d = m.getAnnotation(MBeanInfo.class); 110 if (d != null) 111 descr = d.value(); 112 } 113 return descr; 114 } 115 116 /** {@inheritDoc} */ 117 @Override 118 protected String getParameterName(MBeanOperationInfo op, MBeanParameterInfo param, int paramNo) { 119 String name = param.getName(); 120 Method m = getMethod(op); 121 if (m != null) { 122 for (Annotation a : m.getParameterAnnotations()[paramNo]) { 123 if (MBeanInfo.class.isInstance(a)) 124 name = MBeanInfo.class.cast(a).value(); 125 } 126 } 127 return name; 128 } 129 130 /** 131 * Extracts the Method from the MBeanOperationInfo 132 * @param op 133 * @return 134 */ 135 private Method getMethod(MBeanOperationInfo op) { 136 final MBeanParameterInfo[] params = op.getSignature(); 137 final String[] paramTypes = new String[params.length]; 138 for (int i = 0; i < params.length; i++) 139 paramTypes[i] = params[i].getType(); 140 141 return getMethod(getMBeanInterface(), op.getName(), paramTypes); 142 } 143 144 /** 145 * Returns the Method with the specified name and parameter types for the given class, 146 * null if it doesn't exist. 147 * @param mbean 148 * @param method 149 * @param params 150 * @return 151 */ 152 private static Method getMethod(Class<?> mbean, String method, String... params) { 153 try { 154 final ClassLoader loader = mbean.getClassLoader(); 155 final Class<?>[] paramClasses = new Class<?>[params.length]; 156 for (int i = 0; i < params.length; i++) { 157 paramClasses[i] = primitives.get(params[i]); 158 if (paramClasses[i] == null) 159 paramClasses[i] = Class.forName(params[i], false, loader); 160 } 161 return mbean.getMethod(method, paramClasses); 162 } catch (RuntimeException e) { 163 throw e; 164 } catch (Exception e) { 165 return null; 166 } 167 } 168 169 @Override 170 public Object invoke(String s, Object[] objects, String[] strings) throws MBeanException, ReflectionException { 171 if (audit) { 172 Subject subject = Subject.getSubject(AccessController.getContext()); 173 String caller = "anonymous"; 174 if (subject != null) { 175 caller = ""; 176 for (Principal principal : subject.getPrincipals()) { 177 caller += principal.getName() + " "; 178 } 179 } 180 181 AuditLogEntry entry = new JMXAuditLogEntry(); 182 entry.setUser(caller); 183 entry.setTimestamp(System.currentTimeMillis()); 184 entry.setOperation(this.getMBeanInfo().getClassName() + "." + s); 185 entry.getParameters().put("arguments", objects); 186 187 auditLog.log(entry); 188 } 189 return super.invoke(s, objects, strings); 190 } 191 }