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 }