001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.aop;
020
021import java.lang.annotation.Annotation;
022import java.lang.reflect.Method;
023
024/**
025 * Default {@code AnnotationResolver} implementation that merely inspects the
026 * {@link MethodInvocation MethodInvocation}'s {@link MethodInvocation#getMethod() target method},
027 * and returns {@code targetMethod}.{@link Method#getAnnotation(Class) getAnnotation(class)}.
028 * <p/>
029 * Unfortunately Java's default reflection API for Annotations is not very robust, and this logic
030 * may not be enough - if the incoming method invocation represents a method from an interface,
031 * this default logic would not discover the annotation if it existed on the method implementation
032 * directly (as opposed to being defined directly in the interface definition).
033 * <p/>
034 * More complex class hierarchy traversal logic is required to exhaust a method's target object's
035 * classes, parent classes, interfaces and parent interfaces.  That logic will likely be added
036 * to this implementation in due time, but for now, this implementation relies on the JDK's default
037 * {@link Method#getAnnotation(Class) Method.getAnnotation(class)} logic.
038 *
039 * @since 1.1
040 */
041public class DefaultAnnotationResolver implements AnnotationResolver {
042
043    /**
044     * Returns {@code methodInvocation.}{@link org.apache.shiro.aop.MethodInvocation#getMethod() getMethod()}.{@link Method#getAnnotation(Class) getAnnotation(clazz)}.
045     *
046     * @param mi    the intercepted method to be invoked.
047     * @param clazz the annotation class to use to find an annotation instance on the method.
048     * @return the discovered annotation or {@code null} if an annotation instance could not be
049     *         found.
050     */
051    public Annotation getAnnotation(MethodInvocation mi, Class<? extends Annotation> clazz) {
052        if (mi == null) {
053            throw new IllegalArgumentException("method argument cannot be null");
054        }
055        Method m = mi.getMethod();
056        if (m == null) {
057            String msg = MethodInvocation.class.getName() + " parameter incorrectly constructed.  getMethod() returned null";
058            throw new IllegalArgumentException(msg);
059
060        }
061        Annotation annotation = m.getAnnotation(clazz);
062        if (annotation == null ) {
063            Object miThis = mi.getThis();
064            //SHIRO-473 - miThis could be null for static methods, just return null
065            annotation = miThis != null ? miThis.getClass().getAnnotation(clazz) : null;
066        }
067        return annotation;
068    }
069}