    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    }