1 /*
2  * Hibernate Validator, declare and validate application constraints
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

7 package org.hibernate.validator.internal.properties.javabean;
8
9 import static org.hibernate.validator.internal.util.TypeHelper.isHibernateValidatorEnhancedBean;
10
11 import java.lang.reflect.Method;
12 import java.security.AccessController;
13 import java.security.PrivilegedAction;
14
15 import org.hibernate.validator.HibernateValidatorPermission;
16 import org.hibernate.validator.engine.HibernateValidatorEnhancedBean;
17 import org.hibernate.validator.internal.metadata.raw.ConstrainedElement.ConstrainedElementKind;
18 import org.hibernate.validator.internal.properties.Getter;
19 import org.hibernate.validator.internal.properties.PropertyAccessor;
20 import org.hibernate.validator.internal.util.Contracts;
21 import org.hibernate.validator.internal.util.ExecutableParameterNameProvider;
22 import org.hibernate.validator.internal.util.ReflectionHelper;
23 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod;
24
25 /**
26  * @author Marko Bekhta
27  */

28 public class JavaBeanGetter extends JavaBeanMethod implements Getter {
29
30     private final String propertyName;
31     private final String resolvedPropertyName;
32
33     /**
34      * The class of the method for which the constraint was defined.
35      * <p>
36      * It is usually the same as the declaring class of the method itself, except in the XML case when a user could
37      * declare a constraint for a specific subclass.
38      */

39     private final Class<?> declaringClass;
40
41     public JavaBeanGetter(Class<?> declaringClass, Method method, String propertyName, String resolvedPropertyName) {
42         super( method );
43         Contracts.assertNotNull( propertyName, "Property name cannot be null." );
44
45         this.declaringClass = declaringClass;
46         this.propertyName = propertyName;
47         this.resolvedPropertyName = resolvedPropertyName;
48     }
49
50     @Override
51     public String getPropertyName() {
52         return propertyName;
53     }
54
55     @Override
56     public String getResolvedPropertyName() {
57         return resolvedPropertyName;
58     }
59
60     @Override
61     public boolean hasReturnValue() {
62         // getters should always have a return value
63         return true;
64     }
65
66     @Override
67     public boolean hasParameters() {
68         // getters should never have parameters
69         return false;
70     }
71
72     @Override
73     public String getParameterName(ExecutableParameterNameProvider parameterNameProvider, int parameterIndex) {
74         throw new IllegalStateException( "Getters may not have parameters" );
75     }
76
77     @Override
78     public Class<?> getDeclaringClass() {
79         return declaringClass;
80     }
81
82     @Override
83     public ConstrainedElementKind getConstrainedElementKind() {
84         return ConstrainedElementKind.GETTER;
85     }
86
87     @Override
88     public PropertyAccessor createAccessor() {
89         if ( isHibernateValidatorEnhancedBean( executable.getDeclaringClass() ) ) {
90             return new EnhancedBeanGetterAccessor( executable.getName() );
91         }
92         else {
93             return new GetterAccessor( executable );
94         }
95
96     }
97
98     @Override
99     public boolean equals(Object o) {
100         if ( this == o ) {
101             return true;
102         }
103         if ( o == null || this.getClass() != o.getClass() ) {
104             return false;
105         }
106         if ( !super.equals( o ) ) {
107             return false;
108         }
109
110         JavaBeanGetter that = (JavaBeanGetter) o;
111
112         return this.propertyName.equals( that.propertyName );
113     }
114
115     @Override
116     public int hashCode() {
117         int result = super.hashCode();
118         result = 31 * result + this.propertyName.hashCode();
119         return result;
120     }
121
122     private static class EnhancedBeanGetterAccessor implements PropertyAccessor {
123         private final String getterFullName;
124
125         private EnhancedBeanGetterAccessor(final String getterFullName) {
126             this.getterFullName = getterFullName;
127         }
128
129         @Override
130         public Object getValueFrom(Object bean) {
131             // we don't do an instanceof check here as it should already be applied when the accessor was created.
132             return ( (HibernateValidatorEnhancedBean) bean ).$$_hibernateValidator_getGetterValue( getterFullName );
133         }
134     }
135     private static class GetterAccessor implements PropertyAccessor {
136
137         private Method accessibleGetter;
138
139         private GetterAccessor(Method getter) {
140             this.accessibleGetter = getAccessible( getter );
141         }
142
143         @Override
144         public Object getValueFrom(Object bean) {
145             return ReflectionHelper.getValue( accessibleGetter, bean );
146         }
147     }
148
149     /**
150      * Returns an accessible copy of the given method.
151      */

152     private static Method getAccessible(Method original) {
153         SecurityManager sm = System.getSecurityManager();
154         if ( sm != null ) {
155             sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS );
156         }
157
158         Class<?> clazz = original.getDeclaringClass();
159         Method accessibleMethod = run( GetDeclaredMethod.andMakeAccessible( clazz, original.getName() ) );
160
161         return accessibleMethod;
162     }
163
164     /**
165      * Runs the given privileged action, using a privileged block if required.
166      * <p>
167      * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
168      * privileged actions within HV's protection domain.
169      */

170     private static <T> T run(PrivilegedAction<T> action) {
171         return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
172     }
173 }
174