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    }