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 import java.util.Map; 019 020 import org.apache.tapestry5.ioc.ObjectCreator; 021 import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 022 import org.apache.tapestry5.ioc.internal.util.InternalUtils; 023 import org.apache.tapestry5.ioc.services.Builtin; 024 import org.apache.tapestry5.ioc.services.ClassFabUtils; 025 import org.apache.tapestry5.ioc.services.PlasticProxyFactory; 026 import org.apache.tapestry5.ioc.services.ThunkCreator; 027 import org.apache.tapestry5.plastic.ClassInstantiator; 028 import org.apache.tapestry5.plastic.InstructionBuilder; 029 import org.apache.tapestry5.plastic.InstructionBuilderCallback; 030 import org.apache.tapestry5.plastic.PlasticClass; 031 import org.apache.tapestry5.plastic.PlasticClassTransformer; 032 import org.apache.tapestry5.plastic.PlasticField; 033 import org.apache.tapestry5.plastic.PlasticMethod; 034 import org.apache.tapestry5.plastic.PlasticUtils; 035 036 @SuppressWarnings("all") 037 public class ThunkCreatorImpl implements ThunkCreator 038 { 039 private final Map<Class, ClassInstantiator> interfaceToInstantiator = CollectionFactory.newConcurrentMap(); 040 041 private final PlasticProxyFactory proxyFactory; 042 043 private static final Method CREATE_OBJECT = PlasticUtils.getMethod(ObjectCreator.class, "createObject"); 044 045 public ThunkCreatorImpl(@Builtin 046 PlasticProxyFactory proxyFactory) 047 { 048 this.proxyFactory = proxyFactory; 049 } 050 051 public <T> T createThunk(Class<T> proxyType, ObjectCreator objectCreator, String description) 052 { 053 assert proxyType != null; 054 assert objectCreator != null; 055 assert InternalUtils.isNonBlank(description); 056 057 if (!proxyType.isInterface()) 058 throw new IllegalArgumentException(String.format( 059 "Thunks may only be created for interfaces; %s is a class.", 060 ClassFabUtils.toJavaClassName(proxyType))); 061 062 return getInstantiator(proxyType).with(ObjectCreator.class, objectCreator).with(String.class, description) 063 .newInstance(); 064 065 } 066 067 private <T> ClassInstantiator<T> getInstantiator(Class<T> interfaceType) 068 { 069 ClassInstantiator<T> result = interfaceToInstantiator.get(interfaceType); 070 071 if (result == null) 072 { 073 result = createInstantiator(interfaceType); 074 interfaceToInstantiator.put(interfaceType, result); 075 } 076 077 return result; 078 } 079 080 private <T> ClassInstantiator<T> createInstantiator(final Class<T> interfaceType) 081 { 082 return proxyFactory.createProxy(interfaceType, new PlasticClassTransformer() 083 { 084 public void transform(PlasticClass plasticClass) 085 { 086 final PlasticField objectCreatorField = plasticClass.introduceField(ObjectCreator.class, "creator") 087 .injectFromInstanceContext(); 088 089 PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(interfaceType.getName(), "delegate", 090 null, null); 091 092 delegateMethod.changeImplementation(new InstructionBuilderCallback() 093 { 094 public void doBuild(InstructionBuilder builder) 095 { 096 builder.loadThis().getField(objectCreatorField); 097 builder.invoke(CREATE_OBJECT); 098 builder.checkcast(interfaceType).returnResult(); 099 } 100 }); 101 102 for (Method method : interfaceType.getMethods()) 103 { 104 plasticClass.introduceMethod(method).delegateTo(delegateMethod); 105 } 106 107 if (!plasticClass.isMethodImplemented(PlasticUtils.TO_STRING_DESCRIPTION)) 108 { 109 final PlasticField descriptionField = plasticClass.introduceField(String.class, "description") 110 .injectFromInstanceContext(); 111 112 plasticClass.introduceMethod(PlasticUtils.TO_STRING_DESCRIPTION, new InstructionBuilderCallback() 113 { 114 public void doBuild(InstructionBuilder builder) 115 { 116 builder.loadThis().getField(descriptionField).returnResult(); 117 } 118 }); 119 } 120 } 121 }); 122 } 123 }