001 // Copyright 2004, 2005 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 package org.apache.tapestry.annotations;
015
016 import java.beans.BeanInfo;
017 import java.beans.Introspector;
018 import java.beans.PropertyDescriptor;
019 import java.lang.annotation.Annotation;
020 import java.lang.reflect.Method;
021 import java.lang.reflect.ParameterizedType;
022 import java.lang.reflect.Type;
023 import java.lang.reflect.TypeVariable;
024 import java.util.List;
025
026 import org.apache.hivemind.ApplicationRuntimeException;
027 import org.apache.tapestry.enhance.EnhanceUtils;
028 import org.apache.tapestry.enhance.EnhancementOperation;
029 import org.apache.tapestry.enhance.EnhancementWorker;
030 import org.apache.tapestry.spec.IComponentSpecification;
031 import org.apache.tapestry.spec.IPropertySpecification;
032
033
034 /**
035 * Performs runtime checks on persistent properties to ensure that objects being
036 * managed by competing bytecode enhancement libraries (such as Hibernate) aren't
037 * proxied.
038 */
039 public class ComponentPropertyProxyWorker implements EnhancementWorker {
040
041 private List<String> _excludedPackages;
042
043 /**
044 * {@inheritDoc}
045 */
046 public void performEnhancement(EnhancementOperation op, IComponentSpecification spec) {
047 for (Object o : spec.getPropertySpecificationNames()) {
048 String name = (String) o;
049 IPropertySpecification ps = spec.getPropertySpecification(name);
050
051 checkProxy(op, ps);
052 }
053 }
054
055 public Class extractPropertyType(Class type, String propertyName, IPropertySpecification ps) {
056
057 try {
058 BeanInfo info = Introspector.getBeanInfo(type);
059 PropertyDescriptor[] props = info.getPropertyDescriptors();
060
061 for (PropertyDescriptor prop : props) {
062
063 if (!propertyName.equals(prop.getName())) {
064 continue;
065 }
066
067 Method m = prop.getReadMethod();
068 if (m != null && !m.getGenericReturnType().getClass().getName().equals("java.lang.Class")
069 && TypeVariable.class.isAssignableFrom(m.getGenericReturnType().getClass())) {
070
071 ps.setGeneric(true);
072 TypeVariable tvar = (TypeVariable)m.getGenericReturnType();
073
074 // try to set the actual type
075 if (type.getGenericSuperclass() != null
076 && ParameterizedType.class.isInstance(type.getGenericSuperclass())) {
077
078 ParameterizedType ptype = (ParameterizedType)type.getGenericSuperclass();
079 if (ptype.getActualTypeArguments().length > 0) {
080
081 ps.setCanProxy(canProxyType((Class)ptype.getActualTypeArguments()[0]));
082 ps.setType(((Class)tvar.getBounds()[0]).getName());
083
084 return (Class)tvar.getBounds()[0];
085 }
086 }
087
088 return null;
089 } else if (m != null) {
090
091 ps.setCanProxy(canProxyType(m.getReturnType()));
092 ps.setType(m.getReturnType().getName());
093
094 return m.getReturnType();
095 }
096
097 // try write method instead
098
099 if (m == null && prop.getWriteMethod() == null)
100 return null;
101
102 m = prop.getWriteMethod();
103 if (m.getParameterTypes().length != 1)
104 return null;
105
106 Type genParam = m.getGenericParameterTypes()[0];
107 Class param = m.getParameterTypes()[0];
108
109 if (!genParam.getClass().getName().equals("java.lang.Class")
110 && TypeVariable.class.isAssignableFrom(genParam.getClass())) {
111
112 TypeVariable tvar = (TypeVariable)genParam;
113 ps.setGeneric(true);
114
115 if (type.getGenericSuperclass() != null) {
116
117 ParameterizedType ptype = (ParameterizedType)type.getGenericSuperclass();
118 if (ptype.getActualTypeArguments().length > 0) {
119
120 ps.setCanProxy(canProxyType((Class)ptype.getActualTypeArguments()[0]));
121 ps.setType(((Class)tvar.getBounds()[0]).getName());
122
123 return (Class)tvar.getBounds()[0];
124 }
125 }
126 }
127
128 ps.setCanProxy(canProxyType(param));
129 ps.setType(param.getName());
130 return param;
131 }
132
133 } catch (Throwable t) {
134
135 throw new ApplicationRuntimeException("Error reading property " + propertyName + " from base component class : " + type, t);
136 }
137
138 return null;
139 }
140
141 boolean canProxyType(Class type)
142 {
143 if (type == null)
144 return false;
145
146 if (!EnhanceUtils.canProxyPropertyType(type))
147 return false;
148
149 for (Annotation an : type.getAnnotations()) {
150 if (isExcluded(an)) {
151 return false;
152 }
153 }
154
155 return true;
156 }
157
158 void checkProxy(EnhancementOperation op, IPropertySpecification ps) {
159 ps.setProxyChecked(true);
160
161 if (!ps.isPersistent()) {
162 return;
163 }
164
165 extractPropertyType(op.getBaseClass(), ps.getName(), ps);
166 }
167
168 boolean isExcluded(Annotation annotation) {
169 for (String match : _excludedPackages) {
170
171 if (annotation.annotationType().getName().indexOf(match) > -1) {
172 return true;
173 }
174 }
175
176 return false;
177 }
178
179 public void setExcludedPackages(List<String> packages) {
180 _excludedPackages = packages;
181 }
182 }
183