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 java.lang.annotation.Annotation;
020 import java.lang.reflect.Method;
021 import java.security.AccessController;
022 import java.security.Principal;
023 import java.util.HashMap;
024 import java.util.Map;
025
026 import javax.management.MBeanAttributeInfo;
027 import javax.management.MBeanException;
028 import javax.management.MBeanOperationInfo;
029 import javax.management.MBeanParameterInfo;
030 import javax.management.NotCompliantMBeanException;
031 import javax.management.ObjectName;
032 import javax.management.ReflectionException;
033 import javax.management.StandardMBean;
034 import javax.security.auth.Subject;
035
036 import org.apache.activemq.broker.util.AuditLogEntry;
037 import org.apache.activemq.broker.util.AuditLogService;
038 import org.apache.activemq.broker.util.JMXAuditLogEntry;
039 import org.slf4j.Logger;
040 import org.slf4j.LoggerFactory;
041
042 /**
043 * MBean that looks for method/parameter descriptions in the Info annotation.
044 */
045 public class AnnotatedMBean extends StandardMBean {
046
047 private static final Map<String, Class<?>> primitives = new HashMap<String, Class<?>>();
048
049 private static final Logger LOG = LoggerFactory.getLogger("org.apache.activemq.audit");
050
051 private static boolean audit;
052 private static AuditLogService auditLog;
053
054 static {
055 Class<?>[] p = { byte.class, short.class, int.class, long.class, float.class, double.class, char.class, boolean.class, };
056 for (Class<?> c : p) {
057 primitives.put(c.getName(), c);
058 }
059 audit = "true".equalsIgnoreCase(System.getProperty("org.apache.activemq.audit"));
060 if (audit) {
061 auditLog = AuditLogService.getAuditLog();
062 }
063 }
064
065 @SuppressWarnings({ "unchecked", "rawtypes" })
066 public static void registerMBean(ManagementContext context, Object object, ObjectName objectName) throws Exception {
067
068 String mbeanName = object.getClass().getName() + "MBean";
069
070 for (Class c : object.getClass().getInterfaces()) {
071 if (mbeanName.equals(c.getName())) {
072 context.registerMBean(new AnnotatedMBean(object, c), objectName);
073 return;
074 }
075 }
076
077 context.registerMBean(object, objectName);
078 }
079
080 /** Instance where the MBean interface is implemented by another object. */
081 public <T> AnnotatedMBean(T impl, Class<T> mbeanInterface) throws NotCompliantMBeanException {
082 super(impl, mbeanInterface);
083 }
084
085 /** Instance where the MBean interface is implemented by this object. */
086 protected AnnotatedMBean(Class<?> mbeanInterface) throws NotCompliantMBeanException {
087 super(mbeanInterface);
088 }
089
090 /** {@inheritDoc} */
091 @Override
092 protected String getDescription(MBeanAttributeInfo info) {
093
094 String descr = info.getDescription();
095 Method m = getMethod(getMBeanInterface(), "get" + info.getName().substring(0, 1).toUpperCase() + info.getName().substring(1));
096 if (m == null)
097 m = getMethod(getMBeanInterface(), "is" + info.getName().substring(0, 1).toUpperCase() + info.getName().substring(1));
098 if (m == null)
099 m = getMethod(getMBeanInterface(), "does" + info.getName().substring(0, 1).toUpperCase() + info.getName().substring(1));
100
101 if (m != null) {
102 MBeanInfo d = m.getAnnotation(MBeanInfo.class);
103 if (d != null)
104 descr = d.value();
105 }
106 return descr;
107 }
108
109 /** {@inheritDoc} */
110 @Override
111 protected String getDescription(MBeanOperationInfo op) {
112
113 String descr = op.getDescription();
114 Method m = getMethod(op);
115 if (m != null) {
116 MBeanInfo d = m.getAnnotation(MBeanInfo.class);
117 if (d != null)
118 descr = d.value();
119 }
120 return descr;
121 }
122
123 /** {@inheritDoc} */
124 @Override
125 protected String getParameterName(MBeanOperationInfo op, MBeanParameterInfo param, int paramNo) {
126 String name = param.getName();
127 Method m = getMethod(op);
128 if (m != null) {
129 for (Annotation a : m.getParameterAnnotations()[paramNo]) {
130 if (MBeanInfo.class.isInstance(a))
131 name = MBeanInfo.class.cast(a).value();
132 }
133 }
134 return name;
135 }
136
137 /**
138 * Extracts the Method from the MBeanOperationInfo
139 *
140 * @param op
141 * @return
142 */
143 private Method getMethod(MBeanOperationInfo op) {
144 final MBeanParameterInfo[] params = op.getSignature();
145 final String[] paramTypes = new String[params.length];
146 for (int i = 0; i < params.length; i++)
147 paramTypes[i] = params[i].getType();
148
149 return getMethod(getMBeanInterface(), op.getName(), paramTypes);
150 }
151
152 /**
153 * Returns the Method with the specified name and parameter types for the
154 * given class, null if it doesn't exist.
155 *
156 * @param mbean
157 * @param method
158 * @param params
159 * @return
160 */
161 private static Method getMethod(Class<?> mbean, String method, String... params) {
162 try {
163 final ClassLoader loader = mbean.getClassLoader();
164 final Class<?>[] paramClasses = new Class<?>[params.length];
165 for (int i = 0; i < params.length; i++) {
166 paramClasses[i] = primitives.get(params[i]);
167 if (paramClasses[i] == null)
168 paramClasses[i] = Class.forName(params[i], false, loader);
169 }
170 return mbean.getMethod(method, paramClasses);
171 } catch (RuntimeException e) {
172 throw e;
173 } catch (Exception e) {
174 return null;
175 }
176 }
177
178 @Override
179 public Object invoke(String s, Object[] objects, String[] strings) throws MBeanException, ReflectionException {
180 if (audit) {
181 Subject subject = Subject.getSubject(AccessController.getContext());
182 String caller = "anonymous";
183 if (subject != null) {
184 caller = "";
185 for (Principal principal : subject.getPrincipals()) {
186 caller += principal.getName() + " ";
187 }
188 }
189
190 AuditLogEntry entry = new JMXAuditLogEntry();
191 entry.setUser(caller);
192 entry.setTimestamp(System.currentTimeMillis());
193 entry.setOperation(this.getMBeanInfo().getClassName() + "." + s);
194 entry.getParameters().put("arguments", objects);
195
196 auditLog.log(entry);
197 }
198 return super.invoke(s, objects, strings);
199 }
200 }