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.event.support;
020
021import java.lang.reflect.Method;
022import java.lang.reflect.Modifier;
023
024/**
025 * A event listener that invokes a target object's method that accepts a single event argument.
026 *
027 * @since 1.3
028 */
029public class SingleArgumentMethodEventListener implements TypedEventListener {
030
031    private final Object target;
032    private final Method method;
033
034    public SingleArgumentMethodEventListener(Object target, Method method) {
035        this.target = target;
036        this.method = method;
037        //assert that the method is defined as expected:
038        getMethodArgumentType(method);
039
040        assertPublicMethod(method);
041    }
042
043    public Object getTarget() {
044        return this.target;
045    }
046
047    public Method getMethod() {
048        return this.method;
049    }
050
051    private void assertPublicMethod(Method method) {
052        int modifiers = method.getModifiers();
053        if (!Modifier.isPublic(modifiers)) {
054            throw new IllegalArgumentException("Event handler method [" + method + "] must be public.");
055        }
056    }
057
058    public boolean accepts(Object event) {
059        return event != null && getEventType().isInstance(event);
060    }
061
062    public Class getEventType() {
063        return getMethodArgumentType(getMethod());
064    }
065
066    public void onEvent(Object event) {
067        Method method = getMethod();
068        try {
069            method.invoke(getTarget(), event);
070        } catch (Exception e) {
071            throw new IllegalStateException("Unable to invoke event handler method [" + method + "].", e);
072        }
073    }
074
075    protected Class getMethodArgumentType(Method method) {
076        Class[] paramTypes = method.getParameterTypes();
077        if (paramTypes.length != 1) {
078            //the default implementation expects a single typed argument and nothing more:
079            String msg = "Event handler methods must accept a single argument.";
080            throw new IllegalArgumentException(msg);
081        }
082        return paramTypes[0];
083    }
084}