View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3.builder;
19  
20  import java.lang.reflect.AccessibleObject;
21  import java.lang.reflect.Field;
22  import java.util.Set;
23  import java.util.function.Supplier;
24  
25  import org.apache.commons.lang3.SystemProperties;
26  import org.apache.commons.lang3.tuple.Pair;
27  
28  /**
29   * Abstracts reflection access for reflection-based classes in this package.
30   * <p>
31   * See {@link AbstractBuilder#setForceAccessible(boolean)} for details.
32   * </p>
33   *
34   * @since 3.21.0
35   * @see AbstractBuilder#setForceAccessible(boolean)
36   * @see AccessibleObject#setAccessible(boolean)
37   */
38  public abstract class AbstractReflection {
39  
40      /**
41       * Builds an instance of a subclass of {@link AbstractReflection}.
42       *
43       * @param <B> An AbstractBuilder subclass.
44       */
45      public abstract static class AbstractBuilder<B extends AbstractBuilder<B>> implements Supplier<AbstractReflection> {
46  
47          /**
48           * Whether the {@link AbstractReflection} subclass will call {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)} on
49           * inaccessible fields.
50           */
51          private boolean forceAccessible = getForceAccessible();
52  
53          /**
54           * Constructs a new instance for a subclass.
55           */
56          AbstractBuilder() {
57              // Empty.
58          }
59  
60          /**
61           * Returns {@code this} instance typed as its subclass.
62           *
63           * @return {@code this} instance typed as its subclass.
64           */
65          @SuppressWarnings("unchecked")
66          protected B asThis() {
67              return (B) this;
68          }
69  
70          /**
71           * Sets whether inaccessible fields are made accessible by calling {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
72           * <p>
73           * In general, controls whether the instances built by this builder will force the accessible flag for reflection.
74           * </p>
75           * <p>
76           * Defaults to {@code getForceAccessible()}, which defaults to true for compatibility.
77           * </p>
78           * <p>
79           * This default is read from the system property {@code "AbstractReflection.forceAccessible"}, which defaults to true for compatibility.
80           * </p>
81           * <p>
82           * The parsing rules are defined by {@link Boolean#parseBoolean(String)}.
83           * </p>
84           * <p>
85           * See subclasses for specific behavior.
86           * </p>
87           *
88           * @param forceAccessible Whether to force accessibility by calling {@link AccessibleObject#setAccessible(boolean)
89           *                        AccessibleObject#setAccessible(true)}.
90           * @return {@code this} instance.
91           * @see AccessibleObject#setAccessible(boolean)
92           */
93          public B setForceAccessible(final boolean forceAccessible) {
94              this.forceAccessible = forceAccessible;
95              return asThis();
96          }
97      }
98  
99      /**
100      * Gets whether the system property {@code "AbstractReflection.forceAccessible"} is set to true.
101      * <p>
102      * The parsing rules are defined by {@link Boolean#parseBoolean(String)}.
103      * </p>
104      * <p>
105      * If the property is not set, return true.
106      * </p>
107      *
108      * @return whether the system property {@code "AbstractReflection.forceAccessible"} is set to true with true as the default.
109      * @see Boolean#parseBoolean(String)
110      * @since 3.21.0
111      */
112     public static boolean getForceAccessible() {
113         return SystemProperties.getBoolean(AbstractReflection.class, "forceAccessible", () -> true);
114     }
115 
116     static boolean isRegistered(final Object lhs, final Object rhs, final Set<Pair<IDKey, IDKey>> registry) {
117         final Pair<IDKey, IDKey> pair = toRegisterPair(lhs, rhs);
118         final Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getRight(), pair.getLeft());
119         return registry != null && (registry.contains(pair) || registry.contains(swappedPair));
120     }
121 
122     static void register(final Object lhs, final Object rhs, final Set<Pair<IDKey, IDKey>> registry) {
123         registry.add(toRegisterPair(lhs, rhs));
124     }
125 
126     /**
127      * Sets {@code accessibleObject} to be accessible if {@code forceAccessible} is true and the object is not already accessible. Calls
128      * {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
129      *
130      * @param forceAccessible  Whether to call {@link AccessibleObject#setAccessible(boolean)} if the object is not already accessible.
131      * @param accessibleObject The accessible object to set; may be {@code null}.
132      * @return {@code true} if {@code accessibleObject} is non-null and accessible after this call; {@code false} otherwise (including when
133      *         {@code accessibleObject} is {@code null}, or when it is inaccessible and {@code forceAccessible} is {@code false}).
134      * @throws SecurityException Thrown if {@code forceAccessible} is true and the request is denied.
135      * @see AccessibleObject#setAccessible(boolean)
136      * @see SecurityManager#checkPermission
137      */
138     public static boolean setAccessible(final boolean forceAccessible, final AccessibleObject accessibleObject) {
139         return accessibleObject != null && (accessibleObject.isAccessible() || forceAccessible && setAccessibleTrue(accessibleObject));
140     }
141 
142     /**
143      * Sets the accessible object as accessible by calling {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
144      * <p>
145      * Callers must ensure {@code accessibleObject} is non-null before calling this method.
146      * </p>
147      *
148      * @param accessibleObject The accessible object to set; must be non-null.
149      * @return {@code true} if {@code accessibleObject} is accessible after this call; {@code false} otherwise.
150      * @throws SecurityException Thrown if the request is denied.
151      * @see AccessibleObject#setAccessible(boolean)
152      * @see SecurityManager#checkPermission
153      */
154     private static boolean setAccessibleTrue(final AccessibleObject accessibleObject) {
155         // Test isAccessible() to avoid the permission check.
156         if (!accessibleObject.isAccessible()) {
157             accessibleObject.setAccessible(true);
158         }
159         return accessibleObject.isAccessible();
160     }
161 
162     /**
163      * Converters value pair into a register pair.
164      *
165      * @param lhs {@code this} object.
166      * @param rhs The other object.
167      * @return The pair.
168      */
169     static Pair<IDKey, IDKey> toRegisterPair(final Object lhs, final Object rhs) {
170         return Pair.of(new IDKey(lhs), new IDKey(rhs));
171     }
172 
173     static void unregister(final Object lhs, final Object rhs, final Set<Pair<IDKey, IDKey>> registry, final ThreadLocal<Set<Pair<IDKey, IDKey>>> registryTL) {
174         registry.remove(toRegisterPair(lhs, rhs));
175         if (registry.isEmpty()) {
176             registryTL.remove();
177         }
178     }
179 
180     /**
181      * Whether to call {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)} on inaccessible fields.
182      */
183     private final boolean forceAccessible;
184 
185     /**
186      * Constructs a new instance.
187      *
188      * @param <T>     The type to build.
189      * @param builder The builder.
190      */
191     <T extends AbstractBuilder<T>> AbstractReflection(final AbstractBuilder<T> builder) {
192         this.forceAccessible = builder.forceAccessible;
193     }
194 
195     /**
196      * Tests whether fields should be made accessible with {@link AccessibleObject#setAccessible(boolean)}.
197      *
198      * @return whether fields should be made accessible with {@link AccessibleObject#setAccessible(boolean)}.
199      */
200     protected boolean isForceAccessible() {
201         return forceAccessible;
202     }
203 
204     /**
205      * Sets the field to be accessible if {@code forceAccessible} is true and the field is not already accessible. Calls
206      * {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}.
207      *
208      * @param field The field to set; may be {@code null}.
209      * @return {@code true} if {@code field} is non-null and accessible after this call; {@code false} otherwise.
210      * @throws SecurityException Thrown if {@code forceAccessible} flag is true and the request is denied.
211      * @see AccessibleObject#setAccessible(boolean)
212      * @see SecurityManager#checkPermission
213      */
214     boolean setAccessible(final Field field) {
215         return setAccessible(isForceAccessible(), field);
216     }
217 }