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    
018    package org.apache.commons.proxy.interceptor;
019    
020    import org.apache.commons.proxy.ObjectProvider;
021    import org.apache.commons.proxy.ProxyFactory;
022    import org.apache.commons.proxy.ProxyUtils;
023    import org.apache.commons.proxy.Interceptor;
024    
025    /**
026     * An <code>InterceptorChain</code> assists with creating proxies which go through a series of
027     * {@link Interceptor interceptors}.
028     *
029     * <pre>
030     *   MyServiceInterface serviceImpl = ...;
031     *   ProxyFactory factory = ...;
032     *   Interceptor[] interceptors = ...;
033     *   InterceptorChain chain = new InterceptorChain(interceptors);
034     *   ObjectProvider provider = chain.createProxyProvider(factory, serviceImpl);
035     *   MyServiceInterface serviceProxy = ( MyServiceInterface )provider.getObject();
036     *   serviceProxy.someServiceMethod(...); // This will go through the interceptors! 
037     * </pre>
038     *
039     * @author James Carman
040     * @since 1.0
041     */
042    public class InterceptorChain
043    {
044    //----------------------------------------------------------------------------------------------------------------------
045    // Fields
046    //----------------------------------------------------------------------------------------------------------------------
047        private final Interceptor[] interceptors;
048    
049    //----------------------------------------------------------------------------------------------------------------------
050    // Constructors
051    //----------------------------------------------------------------------------------------------------------------------
052    
053        public InterceptorChain( Interceptor[] interceptors )
054        {
055            this.interceptors = interceptors;
056        }
057    
058    //----------------------------------------------------------------------------------------------------------------------
059    // Other Methods
060    //----------------------------------------------------------------------------------------------------------------------
061    
062        private Object createProxy( ProxyFactory proxyFactory, ClassLoader classLoader, Object terminus,
063                                    Class[] proxyClasses )
064        {
065            Object currentTarget = terminus;
066            for( int i = interceptors.length - 1; i >= 0; --i )
067            {
068                currentTarget = proxyFactory
069                        .createInterceptorProxy( classLoader, currentTarget, interceptors[i], proxyClasses );
070            }
071            return currentTarget;
072        }
073    
074        /**
075         * Creates an {@link ObjectProvider} which will return a proxy that sends method invocations through this
076         * chain of interceptors and ultimately arrive at the supplied terminus object.  The proxy will support all
077         * interfaces implemented by the terminus object.  The thread context classloader will be used to generate the
078         * proxy class.
079         * 
080         * @param proxyFactory the {@link ProxyFactory} to use to create the proxy
081         * @param terminus the terminus
082         * @return an {@link ObjectProvider} which will return a proxy that sends method invocations through this
083         * chain of interceptors and ultimately arrive at the supplied terminus object
084         */
085        public ObjectProvider createProxyProvider( ProxyFactory proxyFactory, Object terminus )
086        {
087            return createProxyProvider( proxyFactory, terminus, null );
088        }
089    
090        /**
091         * Creates an {@link ObjectProvider} which will return a proxy that sends method invocations through this
092         * chain of interceptors and ultimately arrive at the supplied terminus object.  The proxy will support only
093         * the specified interfaces/classes.  The thread context classloader will be used to generate the
094         * proxy class.
095         *
096         * @param proxyFactory the {@link ProxyFactory} to use to create the proxy
097         * @param terminus the terminus
098         * @param proxyClasses the interfaces to support
099         * @return an {@link ObjectProvider} which will return a proxy that sends method invocations through this
100         * chain of interceptors and ultimately arrive at the supplied terminus object
101         */
102        public ObjectProvider createProxyProvider( ProxyFactory proxyFactory, Object terminus, Class[] proxyClasses )
103        {
104            return createProxyProvider( proxyFactory, Thread.currentThread().getContextClassLoader(), terminus,
105                                        proxyClasses );
106        }
107    
108        /**
109         * Creates an {@link ObjectProvider} which will return a proxy that sends method invocations through this
110         * chain of interceptors and ultimately arrive at the supplied terminus object.  The proxy will support only
111         * the specified interfaces/classes.  The specified classloader will be used to generate the
112         * proxy class.
113         *
114         * @param proxyFactory the {@link ProxyFactory} to use to create the proxy
115         * @param classLoader the classloader to be used to generate the proxy class
116         * @param terminus the terminus
117         * @param proxyClasses the interfaces to support
118         * @return an {@link ObjectProvider} which will return a proxy that sends method invocations through this
119         * chain of interceptors and ultimately arrive at the supplied terminus object
120         */
121        public ObjectProvider createProxyProvider( ProxyFactory proxyFactory, ClassLoader classLoader, Object terminus,
122                                                   Class[] proxyClasses )
123        {
124            if( proxyClasses == null || proxyClasses.length == 0 )
125            {
126                proxyClasses = ProxyUtils.getAllInterfaces( terminus.getClass() );
127            }
128            return new ProxyObjectProvider( proxyFactory, classLoader, terminus, proxyClasses );
129        }
130    
131    //----------------------------------------------------------------------------------------------------------------------
132    // Inner Classes
133    //----------------------------------------------------------------------------------------------------------------------
134    
135        private class ProxyObjectProvider implements ObjectProvider
136        {
137            private final ClassLoader classLoader;
138            private final Class[] proxyClasses;
139            private final Object terminus;
140            private final ProxyFactory proxyFactory;
141    
142            public ProxyObjectProvider( ProxyFactory proxyFactory, ClassLoader classLoader, Object terminus,
143                                        Class[] proxyClasses )
144            {
145                this.classLoader = classLoader;
146                this.proxyClasses = proxyClasses;
147                this.terminus = terminus;
148                this.proxyFactory = proxyFactory;
149            }
150    
151            public Object getObject()
152            {
153                return createProxy( proxyFactory, classLoader, terminus, proxyClasses );
154            }
155        }
156    }
157