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