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 015package org.apache.tapestry5.internal.transform; 016 017import org.apache.tapestry5.func.F; 018import org.apache.tapestry5.func.Flow; 019import org.apache.tapestry5.func.Predicate; 020import org.apache.tapestry5.func.Worker; 021import org.apache.tapestry5.model.MutableComponentModel; 022import org.apache.tapestry5.plastic.*; 023import org.apache.tapestry5.runtime.PageLifecycleListener; 024import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; 025import org.apache.tapestry5.services.transform.TransformationSupport; 026 027import 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 */ 033public 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}