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 015package org.apache.tapestry5.ioc.internal.services; 016 017import java.lang.reflect.Method; 018import java.util.Map; 019 020import org.apache.tapestry5.commons.ObjectCreator; 021import org.apache.tapestry5.commons.services.PlasticProxyFactory; 022import org.apache.tapestry5.commons.util.CollectionFactory; 023import org.apache.tapestry5.ioc.internal.util.InternalUtils; 024import org.apache.tapestry5.ioc.services.Builtin; 025import org.apache.tapestry5.ioc.services.ThunkCreator; 026import org.apache.tapestry5.plastic.ClassInstantiator; 027import org.apache.tapestry5.plastic.InstructionBuilder; 028import org.apache.tapestry5.plastic.InstructionBuilderCallback; 029import org.apache.tapestry5.plastic.PlasticClass; 030import org.apache.tapestry5.plastic.PlasticClassTransformer; 031import org.apache.tapestry5.plastic.PlasticField; 032import org.apache.tapestry5.plastic.PlasticMethod; 033import org.apache.tapestry5.plastic.PlasticUtils; 034 035@SuppressWarnings("all") 036public class ThunkCreatorImpl implements ThunkCreator 037{ 038 private final Map<Class, ClassInstantiator> interfaceToInstantiator = CollectionFactory.newConcurrentMap(); 039 040 private final PlasticProxyFactory proxyFactory; 041 042 private static final Method CREATE_OBJECT = PlasticUtils.getMethod(ObjectCreator.class, "createObject"); 043 044 public ThunkCreatorImpl(@Builtin 045 PlasticProxyFactory proxyFactory) 046 { 047 this.proxyFactory = proxyFactory; 048 } 049 050 @Override 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 PlasticUtils.toTypeName(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 @Override 085 public void transform(PlasticClass plasticClass) 086 { 087 final PlasticField objectCreatorField = plasticClass.introduceField(ObjectCreator.class, "creator") 088 .injectFromInstanceContext(); 089 090 PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(interfaceType.getName(), "delegate", 091 null, null); 092 093 delegateMethod.changeImplementation(new InstructionBuilderCallback() 094 { 095 @Override 096 public void doBuild(InstructionBuilder builder) 097 { 098 builder.loadThis().getField(objectCreatorField); 099 builder.invoke(CREATE_OBJECT); 100 builder.checkcast(interfaceType).returnResult(); 101 } 102 }); 103 104 for (Method method : interfaceType.getMethods()) 105 { 106 plasticClass.introduceMethod(method).delegateTo(delegateMethod); 107 } 108 109 if (!plasticClass.isMethodImplemented(PlasticUtils.TO_STRING_DESCRIPTION)) 110 { 111 final PlasticField descriptionField = plasticClass.introduceField(String.class, "description") 112 .injectFromInstanceContext(); 113 114 plasticClass.introduceMethod(PlasticUtils.TO_STRING_DESCRIPTION, new InstructionBuilderCallback() 115 { 116 @Override 117 public void doBuild(InstructionBuilder builder) 118 { 119 builder.loadThis().getField(descriptionField).returnResult(); 120 } 121 }); 122 } 123 } 124 }); 125 } 126}