    001    // Copyright 2007, 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.internal.transform;
    016
    017    import org.apache.tapestry5.func.F;
    018    import org.apache.tapestry5.func.Flow;
    019    import org.apache.tapestry5.func.Predicate;
    020    import org.apache.tapestry5.func.Worker;
    021    import org.apache.tapestry5.model.MutableComponentModel;
    022    import org.apache.tapestry5.plastic.*;
    023    import org.apache.tapestry5.runtime.PageLifecycleListener;
    024    import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
    025    import org.apache.tapestry5.services.transform.TransformationSupport;
    026
    027    import java.lang.annotation.Annotation;
    028
    029    /**
    030     * Similar to {@link org.apache.tapestry5.internal.transform.RenderPhaseMethodWorker} but applies to annotations/methods
    031     * related to the overall page lifecycle. Page lifecycle methods are always void and take no parameters.
    032     */
    033    public class PageLifecycleAnnotationWorker implements ComponentClassTransformWorker2
    034    {
    035        private final Class<? extends Annotation> methodAnnotationClass;
    036
    037        private final MethodDescription lifecycleMethodDescription;
    038
    039        private final String methodAlias;
    040
    041        private final Predicate<PlasticMethod> MATCHER = new Predicate<PlasticMethod>()
    042        {
    043            public boolean accept(PlasticMethod method)
    044            {
    045                return method.getDescription().methodName.equalsIgnoreCase(methodAlias)
    046                        || method.hasAnnotation(methodAnnotationClass);
    047            }
    048        };
    049
    050        private final Worker<PlasticMethod> VALIDATE = new Worker<PlasticMethod>()
    051        {
    052            public void work(PlasticMethod method)
    053            {
    054                if (!method.isVoid())
    055                    throw new RuntimeException(String.format("Method %s is a lifecycle method and should return void.", method
    056                            .getMethodIdentifier()));
    057
    058                if (!method.getParameters().isEmpty())
    059                    throw new RuntimeException(String.format("Method %s is a lifecycle method and should take no parameters.",
    060                            method.getMethodIdentifier()));
    061
    062            }
    063        };
    064
    065        public PageLifecycleAnnotationWorker(Class<? extends Annotation> methodAnnotationClass,
    066                                             MethodDescription lifecycleMethodDescription, String methodAlias)
    067        {
    068            this.methodAnnotationClass = methodAnnotationClass;
    069            this.lifecycleMethodDescription = lifecycleMethodDescription;
    070            this.methodAlias = methodAlias;
    071        }
    072
    073        public void transform(PlasticClass plasticClass, TransformationSupport support, MutableComponentModel model)
    074        {
    075            Flow<PlasticMethod> methods = matchLifecycleMethods(plasticClass);
    076
    077            if (methods.isEmpty())
    078            {
    079                return;
    080            }
    081
    082            plasticClass.introduceInterface(PageLifecycleListener.class);
    083
    084            for (PlasticMethod method : methods)
    085            {
    086                invokeMethodWithinLifecycle(plasticClass, method);
    087            }
    088        }
    089
    090
    091        private void invokeMethodWithinLifecycle(PlasticClass plasticClass, PlasticMethod method)
    092        {
    093            MethodHandle handle = method.getHandle();
    094
    095            plasticClass.introduceMethod(lifecycleMethodDescription).addAdvice(createAdvice(handle));
    096        }
    097
    098        private MethodAdvice createAdvice(final MethodHandle handle)
    099        {
    100            return new MethodAdvice()
    101            {
    102                public void advise(MethodInvocation invocation)
    103                {
    104                    invocation.proceed();
    105
    106                    handle.invoke(invocation.getInstance()).rethrow();
    107                }
    108            };
    109        }
    110
    111        private Flow<PlasticMethod> matchLifecycleMethods(PlasticClass plasticClass)
    112        {
    113            return F.flow(plasticClass.getMethods()).filter(MATCHER).each(VALIDATE);
    114        }
    115    }