001 // Copyright 2009 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 org.apache.tapestry5.ioc.ObjectCreator;
018 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
019 import org.apache.tapestry5.ioc.internal.util.Defense;
020 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
021 import org.apache.tapestry5.ioc.services.*;
022
023 import java.lang.reflect.InvocationTargetException;
024 import java.lang.reflect.Modifier;
025 import java.util.Map;
026
027 public class ThunkCreatorImpl implements ThunkCreator
028 {
029 /**
030 * Map from an interface type to a corresponding "thunk" class that implements the interface.
031 */
032 private final Map<Class, Class> interfaceToThunkClass = CollectionFactory.newConcurrentMap();
033
034 private final ClassFactory classFactory;
035
036 private final MethodSignature toStringSignature = new MethodSignature(String.class, "toString", null, null);
037
038 private static final int PRIVATE_FINAL = Modifier.FINAL + Modifier.PRIVATE;
039
040 private static final String DESCRIPTION_FIELD = "_$description";
041 private static final String CREATOR_FIELD = "_$creator";
042 private static final String DELEGATE_METHOD = "_$delegate";
043
044 public ThunkCreatorImpl(@Builtin ClassFactory classFactory)
045 {
046 this.classFactory = classFactory;
047 }
048
049 public <T> T createThunk(Class<T> proxyType, ObjectCreator objectCreator, String description)
050 {
051 Defense.notNull(proxyType, "proxyType");
052 Defense.notNull(objectCreator, "objectCreator");
053 Defense.notBlank(description, "description");
054
055 if (!proxyType.isInterface())
056 throw new IllegalArgumentException(
057 String.format("Thunks may only be created for interfaces; %s is a class.",
058 ClassFabUtils.toJavaClassName(proxyType)));
059
060 final Class thunkClass = getThunkClass(proxyType);
061
062 Throwable failure;
063
064 try
065 {
066 return proxyType.cast(thunkClass.getConstructors()[0].newInstance(description, objectCreator));
067 }
068 catch (InvocationTargetException ex)
069 {
070 failure = ex.getTargetException();
071 }
072 catch (Exception ex)
073 {
074 failure = ex;
075 }
076
077 throw new RuntimeException(String.format("Exception instantiating thunk class %s: %s",
078 thunkClass.getName(),
079 InternalUtils.toMessage(failure)),
080 failure);
081 }
082
083 private Class getThunkClass(Class type)
084 {
085 Class result = interfaceToThunkClass.get(type);
086
087 if (result == null)
088 {
089 result = constructThunkClass(type);
090 interfaceToThunkClass.put(type, result);
091 }
092
093 return result;
094 }
095
096 private Class constructThunkClass(Class interfaceType)
097 {
098 ClassFab classFab = classFactory.newClass(interfaceType);
099
100 classFab.addField(DESCRIPTION_FIELD, PRIVATE_FINAL, String.class);
101
102 classFab.addField(CREATOR_FIELD, PRIVATE_FINAL, ObjectCreator.class);
103
104 classFab.addConstructor(new Class[] { String.class, ObjectCreator.class }, null,
105 String.format("{ %s = $1; %s = $2; }", DESCRIPTION_FIELD, CREATOR_FIELD));
106
107 MethodSignature sig = new MethodSignature(interfaceType, DELEGATE_METHOD, null, null);
108
109 classFab.addMethod(Modifier.PRIVATE, sig, String.format("return ($r) %s.createObject();", CREATOR_FIELD));
110
111 MethodIterator mi = new MethodIterator(interfaceType);
112
113 while (mi.hasNext())
114 {
115 sig = mi.next();
116
117 classFab.addMethod(Modifier.PUBLIC, sig,
118 String.format("return ($r) %s().%s($$);", DELEGATE_METHOD, sig.getName()));
119 }
120
121 if (!mi.getToString())
122 classFab.addMethod(Modifier.PUBLIC, toStringSignature, String.format("return %s;", DESCRIPTION_FIELD));
123
124 return classFab.createClass();
125 }
126 }