001/*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *                                                                           *
008 * Original code by Centerline Computers, Inc.                               *
009 *****************************************************************************/
010package org.picocontainer.gems.adapters;
011
012import java.lang.reflect.Type;
013
014import org.picocontainer.ComponentAdapter;
015import org.picocontainer.ComponentMonitor;
016import org.picocontainer.PicoCompositionException;
017import org.picocontainer.PicoContainer;
018import org.picocontainer.PicoVisitor;
019import org.picocontainer.gems.util.DelegateMethod;
020
021/**
022 * Object construction is sometimes expensive, especially when it is seldom used
023 * object. The goal of this adapter is to use the
024 * {@link org.picocontainer.gems.util.DelegateMethod} type to allow delayed
025 * construction of objects.
026 * <p>
027 * For example, in a web application, to be able to have classes that depend on
028 * the HttpSession object, you would have to call
029 * HttpServletRequest.getSession(true). This is fine unless you don't want to
030 * create a session until you absolutely have to.
031 * </p>
032 * <p>
033 * Enter DelegateMethodAdapter:
034 * </p>
035 * 
036 * <pre>
037 * //Assumed Variables: request == HttpServletRequest.
038 * //Typical PicoContainer
039 * MutablePicoContainer pico = new PicoBuilder().withLifecycle().withCaching()
040 *              .build();
041 * 
042 * //Create a delegate method that will invoke HttpServletRequest.getSession(true) when invoke() is called.
043 * DelegateMethod delegateMethod = new DelegateMethod(HttpServletRequest.class,
044 *              &quot;getSession&quot;, true);
045 * 
046 * //Create the Adapter wrapping the delegate method.  
047 * DelegateMethodAdapter methodAdapter = new DelegateMethodAdapter(
048 *              HttpSession.class, request, delegateMethod);
049 * pico.addAdapter(methodAdapter);
050 * 
051 * //If only executing this code, the HttpSession should not be created yet.
052 * assertNull(request.getSession(false));
053 * 
054 * //Will get the session object by having the delegate method call HttpServletRequest.getSession(true)
055 * HttpSession session = pico.getComponent(HttpSession.class);
056 * assertNotNull(session);
057 * 
058 * //Should demonstrate that the session has now been created.
059 * assertNotNull(request.getSession(false));
060 * </pre>
061 * 
062 * <p>
063 * With an adapter like this, you can write classes like:
064 * </p>
065 * 
066 * <pre>
067 * public class SessionUser {
068 *      public SessionUser(HttpSession session) {
069 *              //.....
070 *      }
071 * }
072 * </pre>
073 * 
074 * <p>
075 * With impunity, and are guaranteed that the session would not be created
076 * unless the SessionUser object was constructed.
077 * </p>
078 * 
079 * @author Michael Rimov
080 */
081public class DelegateMethodAdapter<T> implements ComponentAdapter<T> {
082
083        /**
084         * The delegate method instance that will ultimately invoke via reflection some method
085         * on targetInstance.
086         */
087        private final DelegateMethod factoryMethod;
088
089        /**
090         * The target instance on which the delegate method's invoke() call will operate.
091         */
092        private final Object targetInstance;
093
094        /**
095         * Object key.
096         */
097        private final Object key;
098
099        /**
100         * @param componentKey
101         * @param Component
102         *            Implementation will be the expected return type of the factory
103         *            method.
104         */
105        public DelegateMethodAdapter(final Object componentKey,
106                        final Object targetInstance, final DelegateMethod factoryMethod) {
107                this.factoryMethod = factoryMethod;
108                this.targetInstance = targetInstance;
109                this.key = componentKey;
110        }
111
112        /**
113         * @param componentKey
114         * @param componentImplementation
115         * @param monitor
116         */
117        public DelegateMethodAdapter(final Object componentKey,
118                        final ComponentMonitor monitor, final Object targetInstance,
119                        final DelegateMethod factoryMethod) {
120                this.factoryMethod = factoryMethod;
121                this.targetInstance = targetInstance;
122                this.key = componentKey;
123        }
124
125        /**
126         * Returns the
127         */
128        public T getComponentInstance(final PicoContainer container, final Type into)
129                        throws PicoCompositionException {
130                try {
131                        return (T) factoryMethod.invoke(targetInstance);
132                } catch (RuntimeException e) {
133                        throw new PicoCompositionException(
134                                        "Error invoking delegate for object construction", e);
135                }
136        }
137
138        /**
139         * {@inheritDoc}
140         * 
141         * @see org.picocontainer.ComponentAdapter#getDescriptor()
142         */
143        public String getDescriptor() {
144                return "Delegate Adapter.  Delegate: " + this.factoryMethod.toString();
145        }
146
147        /** {@inheritDoc} **/
148        public void verify(final PicoContainer container)
149                        throws PicoCompositionException {
150                // Currently does nothing.
151        }
152
153        /** {@inheritDoc} **/
154        public void accept(final PicoVisitor visitor) {
155                visitor.visitComponentAdapter(this);
156        }
157
158        /** {@inheritDoc} **/
159        public ComponentAdapter<T> findAdapterOfType(final Class adapterType) {
160                if (adapterType == null) {
161                        return null;
162                }
163
164                if (DelegateMethodAdapter.class.isAssignableFrom(adapterType)) {
165                        return this;
166                }
167
168                return null;
169        }
170
171        /** {@inheritDoc} **/
172        @SuppressWarnings("unchecked")
173        public Class<? extends T> getComponentImplementation() {
174                return this.factoryMethod.getReturnType();
175        }
176
177        /** {@inheritDoc} **/
178        @Deprecated
179        public T getComponentInstance(final PicoContainer container)
180                        throws PicoCompositionException {
181                return getComponentInstance(container, null);
182        }
183
184        /** {@inheritDoc} **/
185        public Object getComponentKey() {
186                return key;
187        }
188
189        /**
190         * No delegates.
191         */
192        public ComponentAdapter<T> getDelegate() {
193                return null;
194        }
195
196}