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 }