001    // Copyright 2009, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.ioc.internal.services;
016    
017    import java.lang.reflect.Method;
018    
019    import org.apache.tapestry5.ioc.Invocation;
020    import org.apache.tapestry5.ioc.MethodAdvice;
021    import org.apache.tapestry5.ioc.MethodAdviceReceiver;
022    import org.apache.tapestry5.ioc.ObjectCreator;
023    import org.apache.tapestry5.ioc.annotations.NotLazy;
024    import org.apache.tapestry5.ioc.annotations.PreventServiceDecoration;
025    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
026    import org.apache.tapestry5.ioc.services.LazyAdvisor;
027    import org.apache.tapestry5.ioc.services.ThunkCreator;
028    
029    @PreventServiceDecoration
030    public class LazyAdvisorImpl implements LazyAdvisor
031    {
032        private final ThunkCreator thunkCreator;
033    
034        public LazyAdvisorImpl(ThunkCreator thunkCreator)
035        {
036            this.thunkCreator = thunkCreator;
037        }
038    
039        public void addLazyMethodInvocationAdvice(MethodAdviceReceiver methodAdviceReceiver)
040        {
041            for (Method m : methodAdviceReceiver.getInterface().getMethods())
042            {
043                if (filter(m))
044                    addAdvice(m, methodAdviceReceiver);
045            }
046        }
047    
048        private void addAdvice(Method method, MethodAdviceReceiver receiver)
049        {
050            final Class thunkType = method.getReturnType();
051    
052            final String description = String.format("<%s Thunk for %s>", thunkType.getName(),
053                    InternalUtils.asString(method));
054    
055            MethodAdvice advice = new MethodAdvice()
056            {
057                /**
058                 * When the method is invoked, we don't immediately proceed. Instead, we return a thunk instance
059                 * that defers its behavior to the lazily invoked invocation.
060                 */
061                public void advise(final Invocation invocation)
062                {
063                    ObjectCreator deferred = new ObjectCreator()
064                    {
065                        public Object createObject()
066                        {
067                            invocation.proceed();
068    
069                            return invocation.getResult();
070                        }
071                    };
072    
073                    ObjectCreator cachingObjectCreator = new CachingObjectCreator(deferred);
074    
075                    Object thunk = thunkCreator.createThunk(thunkType, cachingObjectCreator, description);
076    
077                    invocation.overrideResult(thunk);
078                }
079            };
080    
081            receiver.adviseMethod(method, advice);
082        }
083    
084        private boolean filter(Method method)
085        {
086            if (method.getAnnotation(NotLazy.class) != null)
087                return false;
088    
089            if (!method.getReturnType().isInterface())
090                return false;
091    
092            for (Class extype : method.getExceptionTypes())
093            {
094                if (!RuntimeException.class.isAssignableFrom(extype))
095                    return false;
096            }
097    
098            return true;
099        }
100    }