All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.objectstyle.cayenne.util.Invocation Maven / Gradle / Ivy

There is a newer version: 1.2.4
Show newest version
/* ====================================================================
 * 
 * The ObjectStyle Group Software License, version 1.1
 * ObjectStyle Group - http://objectstyle.org/
 * 
 * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
 * of the software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The end-user documentation included with the redistribution, if any,
 *    must include the following acknowlegement:
 *    "This product includes software developed by independent contributors
 *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, email
 *    "andrus at objectstyle dot org".
 * 
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
 *    names without prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by many
 * individuals and hosted on ObjectStyle Group web site.  For more
 * information on the ObjectStyle Group, please see
 * .
 */

package org.objectstyle.cayenne.util;

import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.objectstyle.cayenne.CayenneRuntimeException;

/**
 * Invocation represents a dynamic method invocation bound to a specific target. The
 * target is kept with a WeakReference and can therefore be reclaimed by the Garbage
 * Collector.
 * 
 * @author Holger Hoffstaette
 * @author Dirk Olmes
 */
public class Invocation extends Object {

    private WeakReference _target;
    private Method _method;
    private Class[] _parameterTypes;

    /**
     * Prevent use of empty default constructor
     */
    private Invocation() {
    }

    /**
     * Constructor for an Invocation without arguments in the target's method.
     * 
     * @see #Invocation(Object, String, Class[])
     */
    public Invocation(Object target, String methodName) throws NoSuchMethodException {
        this(target, methodName, (Class[]) null);
    }

    /**
     * Constructor for an Invocation with a single argument in the target's method.
     * 
     * @see #Invocation(Object, String, Class[])
     */
    public Invocation(Object target, String methodName, Class parameterType)
            throws NoSuchMethodException {
        this(target, methodName, new Class[] {
            parameterType
        });
    }

    /**
     * Constructor for an Invocation with arbitrary arguments in the target's method.
     * 
     * @param target
     * @param methodName
     * @param parameterTypes
     * @throws NoSuchMethodException if methodName could not be found in
     *             the target
     * @throws IllegalArgumentException if target or methodName are null,
     *             or parameterTypes is empty or contains null elements
     */
    public Invocation(Object target, String methodName, Class[] parameterTypes)
            throws NoSuchMethodException {
        super();

        if (target == null) {
            throw new IllegalArgumentException("target argument must not be null");
        }

        if (methodName == null) {
            throw new IllegalArgumentException("method name must not be null");
        }

        if (parameterTypes != null) {
            if (parameterTypes.length > 0) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (parameterTypes[i] == null) {
                        throw new IllegalArgumentException("parameter type["
                                + i
                                + "] must not be null");
                    }
                }
            }
            else {
                throw new IllegalArgumentException("parameter types must not be empty");
            }
        }

        // allow access to public methods of inaccessible classes, if such methods were
        // declared in a public interface

        _method = lookupMethodInHierarchy(target.getClass(), methodName, parameterTypes);

        if (_method == null) {
            throw new NoSuchMethodException("No such method: "
                    + target.getClass().getName()
                    + "."
                    + methodName);
        }

        if (!Util.isAccessible(_method)) {
            _method.setAccessible(true);
        }

        _parameterTypes = parameterTypes;
        _target = new WeakReference(target);
    }

    Method lookupMethodInHierarchy(
            Class objectClass,
            String methodName,
            Class[] parameterTypes) throws SecurityException, NoSuchMethodException {

        try {
            return objectClass.getDeclaredMethod(methodName, parameterTypes);
        }
        catch (NoSuchMethodException e) {

            Class superClass = objectClass.getSuperclass();
            if (superClass == null || superClass.getName().equals(Object.class.getName())) {
                throw e;
            }

            return lookupMethodInHierarchy(superClass, methodName, parameterTypes);
        }
    }

    /**
     * Invoke the target's method without any arguments.
     * 
     * @see #fire(Object[])
     */
    public boolean fire() {
        return this.fire(null);
    }

    /**
     * Invoke the target's method with a single argument.
     * 
     * @param argument an object passed to the target's method
     * @see #fire(Object[])
     */
    public boolean fire(Object argument) {
        return this.fire(new Object[] {
            argument
        });
    }

    /**
     * Invoke the target's method with an arbitrary number of arguments. The number of
     * arguments must be consistent with the arguments given at construction time of this
     * Invocation.
     * 
     * @param arguments an array of objects passed to the target's method
     * @return true if invocation of the method succeeded, otherwise
     *         false.
     * @throws IllegalArgumentException if the passed arguments are inconsistent with the
     *             arguments passed to this instance's constructor
     * @see #fire(Object[])
     */
    public boolean fire(Object[] arguments) {

        if (_parameterTypes == null) {
            if (arguments != null) {
                throw new IllegalArgumentException("arguments unexpectedly != null");
            }
        }
        else if (arguments == null) {
            throw new IllegalArgumentException("arguments must not be null");
        }
        else if (_parameterTypes.length != arguments.length) {
            throw new IllegalArgumentException(
                    "inconsistent number of arguments: expected"
                            + _parameterTypes.length
                            + ", got "
                            + arguments.length);
        }

        Object currentTarget = _target.get();
        if (currentTarget == null) {
            return false;
        }

        try {
            _method.invoke(currentTarget, arguments);
            return true;
        }
        catch (InvocationTargetException ite) {
            // this is the only type of exception that can be rethrown, since
            // listener can have a valid need to respond to an event with exception,
            // and this does not indicate that it is being in invalid state

            Throwable cause = ite.getCause();
            if (cause instanceof RuntimeException) {
                throw (RuntimeException) cause;
            }
            else {
                throw new CayenneRuntimeException(cause);
            }
        }
        catch (Exception ex) {
            // all other exceptions indicate propblems with the listener,
            // so return invalid status
            return false;
        }
    }

    /**
     * @see Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {
        if ((obj != null) && (obj.getClass().equals(this.getClass()))) {
            Invocation otherInvocation = (Invocation) obj;
            if (_method.equals(otherInvocation.getMethod())) {
                Object otherTarget = otherInvocation.getTarget();
                Object target = _target.get();

                if ((target == null) && (otherTarget == null)) {
                    return true;
                }

                if ((target == null) && (otherTarget != null)) {
                    return false;
                }

                if (target != null) {
                    return target.equals(otherTarget);
                }
            }

            return false;
        }
        else {
            return super.equals(obj);
        }
    }

    /**
     * @see Object#hashCode()
     */
    public int hashCode() {
        // IMPORTANT: DO NOT include Invocation target into whatever
        // algorithm is used to compute hashCode, since it is using a
        // WeakReference and can be released at a later time, altering
        // hashCode, and breaking collections using Invocation as a key
        // (e.g. event DispatchQueue)

        // TODO: use Jakarta commons HashBuilder
        int hash = 42, hashMultiplier = 59;
        return hash * hashMultiplier + _method.hashCode();
    }

    /**
     * @return the method to be invoked on the target
     */
    public Method getMethod() {
        return _method;
    }

    /**
     * @return the target object of this Invocation
     */
    public Object getTarget() {
        return _target.get();
    }

    /**
     * @return an array of Classes describing the target method's parameters
     */
    public Class[] getParameterTypes() {
        return _parameterTypes;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy