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 }