001    // SECTION-START[License Header]
002    // <editor-fold defaultstate="collapsed" desc=" Generated License ">
003    /*
004     *   Copyright (c) 2009 The JOMC Project
005     *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
006     *   All rights reserved.
007     *
008     *   Redistribution and use in source and binary forms, with or without
009     *   modification, are permitted provided that the following conditions
010     *   are met:
011     *
012     *     o Redistributions of source code must retain the above copyright
013     *       notice, this list of conditions and the following disclaimer.
014     *
015     *     o Redistributions in binary form must reproduce the above copyright
016     *       notice, this list of conditions and the following disclaimer in
017     *       the documentation and/or other materials provided with the
018     *       distribution.
019     *
020     *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
021     *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022     *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023     *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
024     *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025     *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026     *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027     *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028     *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
029     *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
030     *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031     *
032     *   $Id: DefaultInvoker.java 1102 2009-12-07 03:01:58Z schulte2005 $
033     *
034     */
035    // </editor-fold>
036    // SECTION-END
037    package org.jomc.ri;
038    
039    import java.lang.reflect.InvocationTargetException;
040    import org.jomc.model.Instance;
041    import org.jomc.spi.Invocation;
042    import org.jomc.spi.Invoker;
043    
044    // SECTION-START[Documentation]
045    // <editor-fold defaultstate="collapsed" desc=" Generated Documentation ">
046    /**
047     * Default {@code Invoker} implementation.
048     * @see DefaultInvocation
049     *
050     * @author <a href="mailto:cs@jomc.org">Christian Schulte</a> 1.0
051     * @version $Id: DefaultInvoker.java 1102 2009-12-07 03:01:58Z schulte2005 $
052     */
053    // </editor-fold>
054    // SECTION-END
055    // SECTION-START[Annotations]
056    // <editor-fold defaultstate="collapsed" desc=" Generated Annotations ">
057    @javax.annotation.Generated( value = "org.jomc.tools.JavaSources",
058                                 comments = "See http://jomc.sourceforge.net/jomc/1.0-alpha-11/jomc-tools" )
059    // </editor-fold>
060    // SECTION-END
061    public class DefaultInvoker implements Invoker
062    {
063        // SECTION-START[Invoker]
064    
065        /**
066         * Performs a method invocation on an object.
067         * <p>This method first passes the given invocation to the {@code preInvoke} method. If the result property of the
068         * invocation returned by the {@code preInvoke} method is an instance of {@code Throwable}, that instance will be
069         * thrown; otherwise the invocation returned by the {@code preInvoke} method is performed and then passed to the
070         * {@code postInvoke} method. If the result property of the invocation returned from the {@code postInvoke} method
071         * is an instance of {@code Throwable}, that instance will be thrown; otherwise the value of the result property is
072         * returned by this method.</p>
073         *
074         * @param invocation The invocation to perform.
075         *
076         * @return The return value of the invocation. If the declared return type of the method of the invocation is a
077         * primitive type, then the value returned by this method must be an instance of the corresponding primitive wrapper
078         * class; otherwise, it must be a type assignable to the declared return type of the method of the invocation.
079         * If the value returned by this method is {@code null} and the declared return type of the method of the invocation
080         * is primitive, then a {@code NullPointerException} will be thrown. If the value returned by this method is
081         * otherwise not compatible to the declared return type of the method of the invocation, a
082         * {@code ClassCastException} will be thrown.
083         *
084         * @throws Throwable The exception thrown from the method invocation. The exception's type must be assignable
085         * either to any of the exception types declared in the {@code throws} clause of the method of the invocation or to
086         * the unchecked exception types {@code java.lang.RuntimeException} or {@code java.lang.Error}.
087         * If a checked exception is thrown by this method that is not assignable to any of the exception types declared in
088         * the {@code throws} clause of the method of the invocation, then an {@code UndeclaredThrowableException}
089         * containing the exception that was thrown by this method will be thrown.
090         *
091         * @see #preInvoke(org.jomc.spi.Invocation)
092         * @see #postInvoke(org.jomc.spi.Invocation)
093         */
094        public Object invoke( final Invocation invocation ) throws Throwable
095        {
096            Invocation current = invocation;
097            final Instance instance = (Instance) current.getContext().get( DefaultInvocation.INSTANCE_KEY );
098    
099            try
100            {
101                if ( instance != null && instance.isStateless() )
102                {
103                    try
104                    {
105                        current = this.preInvoke( current );
106                    }
107                    catch ( final Throwable t )
108                    {
109                        this.handleException( current, t );
110                    }
111    
112                    if ( !( current.getResult() instanceof Throwable ) )
113                    {
114                        try
115                        {
116                            current.setResult( current.getMethod().invoke( current.getObject(), current.getArguments() ) );
117                        }
118                        catch ( final Throwable t )
119                        {
120                            this.handleException( current, t );
121                        }
122                    }
123    
124                    try
125                    {
126                        current = this.postInvoke( current );
127                    }
128                    catch ( final Throwable t )
129                    {
130                        this.handleException( current, t );
131                    }
132    
133                    if ( current.getResult() instanceof Throwable )
134                    {
135                        throw (Throwable) current.getResult();
136                    }
137    
138                    return current.getResult();
139                }
140                else
141                {
142                    synchronized ( invocation.getObject() )
143                    {
144                        try
145                        {
146                            current = this.preInvoke( current );
147                        }
148                        catch ( final Throwable t )
149                        {
150                            this.handleException( current, t );
151                        }
152    
153                        if ( !( current.getResult() instanceof Throwable ) )
154                        {
155                            try
156                            {
157                                current.setResult( current.getMethod().invoke( current.getObject(),
158                                                                               current.getArguments() ) );
159    
160                            }
161                            catch ( final Throwable t )
162                            {
163                                this.handleException( current, t );
164                            }
165                        }
166    
167                        try
168                        {
169                            current = this.postInvoke( current );
170                        }
171                        catch ( final Throwable t )
172                        {
173                            this.handleException( current, t );
174                        }
175    
176                        if ( current.getResult() instanceof Throwable )
177                        {
178                            throw (Throwable) current.getResult();
179                        }
180    
181                        return current.getResult();
182                    }
183                }
184            }
185            finally
186            {
187                invocation.getContext().clear();
188            }
189        }
190    
191        // SECTION-END
192        // SECTION-START[DefaultInvoker]
193        /**
194         * Called before an invocation is performed.
195         * <p>Overriding classes may use this method to perform any kind of operation prior to an invocation and to create
196         * custom invocation instances. If an overriding class wishes to throw an exception, it may do so by setting the
197         * result property of the returned invocation to an instance of {@code Throwable} thrown as the result of the
198         * invocation. If an overriding class wishes to provide a custom {@code Invocation} class, it may do so by returning
199         * a different instance from this method. By default, this method does nothing and returns the given invocation
200         * unchanged.</p>
201         *
202         * @param invocation The invocation about to be performed.
203         *
204         * @return The processed invocation.
205         *
206         * @throws NullPointerException if {@code invocation} is {@code null}.
207         */
208        public Invocation preInvoke( final Invocation invocation )
209        {
210            if ( invocation == null )
211            {
212                throw new NullPointerException( "invocation" );
213            }
214    
215            return invocation;
216        }
217    
218        /**
219         * Called after an invocation has been performed.
220         * <p>Overriding classes may use this method to perform any kind of operation after an invocation has been
221         * performed and to maintain custom invocation instances. If an overriding class wishes to throw an exception, it
222         * may do so by setting the result property of the returned invocation to an instance of {@code Throwable} thrown as
223         * the result of the invocation. Since the result property of the given invocation already holds the result of the
224         * invocation (which may already be an instance of {@code Throwable}), care must be taken when updating that result.
225         * By default, this method does nothing and returns the given invocation unchanged.</p>
226         *
227         * @param invocation The performed invocation.
228         *
229         * @return The processed invocation.
230         *
231         * @throws NullPointerException if {@code invocation} is {@code null}.
232         */
233        public Invocation postInvoke( final Invocation invocation )
234        {
235            if ( invocation == null )
236            {
237                throw new NullPointerException( "invocation" );
238            }
239    
240            return invocation;
241        }
242    
243        /**
244         * Called whenever an exception has been caught.
245         * <p>Overriding classes may use this method for handling exceptions. By default, this method updates the result of
246         * the given invocation with the given throwable. If that throwable is an instance of
247         * {@code InvocationTargetException}, this method updates the result with the value of that exception's target
248         * exception. If the result of the given invocation already is an instance of {@code Throwable}, this method does
249         * not update the result.</p>
250         *
251         * @param invocation The invocation to update.
252         * @param t The throwable to update {@code invocation} with.
253         */
254        public void handleException( final Invocation invocation, final Throwable t )
255        {
256            if ( invocation != null && !( invocation.getResult() instanceof Throwable ) )
257            {
258                if ( t instanceof InvocationTargetException &&
259                     ( (InvocationTargetException) t ).getTargetException() != null )
260                {
261                    invocation.setResult( ( (InvocationTargetException) t ).getTargetException() );
262                    return;
263                }
264    
265                invocation.setResult( t );
266            }
267        }
268    
269        // SECTION-END
270        // SECTION-START[Constructors]
271        // <editor-fold defaultstate="collapsed" desc=" Generated Constructors ">
272    
273        /** Creates a new {@code DefaultInvoker} instance. */
274        @javax.annotation.Generated( value = "org.jomc.tools.JavaSources",
275                                     comments = "See http://jomc.sourceforge.net/jomc/1.0-alpha-11/jomc-tools" )
276        public DefaultInvoker()
277        {
278            // SECTION-START[Default Constructor]
279            super();
280            // SECTION-END
281        }
282        // </editor-fold>
283        // SECTION-END
284        // SECTION-START[Dependencies]
285        // SECTION-END
286        // SECTION-START[Properties]
287        // SECTION-END
288        // SECTION-START[Messages]
289        // SECTION-END
290    }