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