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 }