001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2007-2012 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import java.lang.reflect.*;
013
014/**
015 * A class derived from <code>DelegatingInvocationHandler</code> handles a
016 * method call by looking for a method in itself with identical parameters. If
017 * no such method is found, it forwards the call to a fallback object, which
018 * must implement all of the interfaces which this proxy implements.
019 *
020 * <p>It is useful in creating a wrapper class around an interface which may
021 * change over time.</p>
022 *
023 * <p>Example:
024 *
025 * <blockquote>
026 * <pre>import java.sql.Connection;
027 * Connection connection = ...;
028 * Connection tracingConnection = (Connection) Proxy.newProxyInstance(
029 *     null,
030 *     new Class[] {Connection.class},
031 *     new DelegatingInvocationHandler() {
032 *         protected Object getTarget() {
033 *             return connection;
034 *         }
035 *         Statement createStatement() {
036 *             System.out.println("statement created");
037 *             return connection.createStatement();
038 *         }
039 *     });</pre>
040 * </blockquote>
041 * </p>
042 *
043 * @author jhyde
044 */
045public abstract class DelegatingInvocationHandler
046    implements InvocationHandler
047{
048    public Object invoke(
049        Object proxy,
050        Method method,
051        Object [] args)
052        throws Throwable
053    {
054        Class clazz = getClass();
055        Method matchingMethod;
056        try {
057            matchingMethod =
058                clazz.getMethod(
059                    method.getName(),
060                    method.getParameterTypes());
061        } catch (NoSuchMethodException e) {
062            matchingMethod = null;
063        } catch (SecurityException e) {
064            matchingMethod = null;
065        }
066        try {
067            if (matchingMethod != null) {
068                // Invoke the method in the derived class.
069                return matchingMethod.invoke(this, args);
070            }
071            final Object target = getTarget();
072            if (target == null) {
073                throw new UnsupportedOperationException(
074                    "method: " + method);
075            }
076            return method.invoke(
077                target,
078                args);
079        } catch (InvocationTargetException e) {
080            throw e.getTargetException();
081        }
082    }
083
084    /**
085     * Returns the object to forward method calls to, should the derived class
086     * not implement the method. Generally, this object will be a member of the
087     * derived class, supplied as a parameter to its constructor.
088     *
089     * <p>The default implementation returns null, which will cause the
090     * {@link #invoke(Object, java.lang.reflect.Method, Object[])} method
091     * to throw an {@link UnsupportedOperationException} if the derived class
092     * does not have the required method.
093     *
094     * @return object to forward method calls to
095     *
096     * @throws InvocationTargetException if there is an error acquiring the
097     * target
098     */
099    protected Object getTarget() throws InvocationTargetException {
100        return null;
101    }
102}
103
104// End DelegatingInvocationHandler.java