001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.lang3.builder; 019 020import java.lang.reflect.AccessibleObject; 021import java.lang.reflect.Field; 022import java.util.Set; 023import java.util.function.Supplier; 024 025import org.apache.commons.lang3.SystemProperties; 026import org.apache.commons.lang3.tuple.Pair; 027 028/** 029 * Abstracts reflection access for reflection-based classes in this package. 030 * <p> 031 * See {@link AbstractBuilder#setForceAccessible(boolean)} for details. 032 * </p> 033 * 034 * @since 3.21.0 035 * @see AbstractBuilder#setForceAccessible(boolean) 036 * @see AccessibleObject#setAccessible(boolean) 037 */ 038public abstract class AbstractReflection { 039 040 /** 041 * Builds an instance of a subclass of {@link AbstractReflection}. 042 * 043 * @param <B> An AbstractBuilder subclass. 044 */ 045 public abstract static class AbstractBuilder<B extends AbstractBuilder<B>> implements Supplier<AbstractReflection> { 046 047 /** 048 * Whether the {@link AbstractReflection} subclass will call {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)} on 049 * inaccessible fields. 050 */ 051 private boolean forceAccessible = getForceAccessible(); 052 053 /** 054 * Constructs a new instance for a subclass. 055 */ 056 AbstractBuilder() { 057 // Empty. 058 } 059 060 /** 061 * Returns {@code this} instance typed as its subclass. 062 * 063 * @return {@code this} instance typed as its subclass. 064 */ 065 @SuppressWarnings("unchecked") 066 protected B asThis() { 067 return (B) this; 068 } 069 070 /** 071 * Sets whether inaccessible fields are made accessible by calling {@link AccessibleObject#setAccessible(boolean) AccessibleObject#setAccessible(true)}. 072 * <p> 073 * In general, controls whether the instances built by this builder will force the accessible flag for reflection. 074 * </p> 075 * <p> 076 * Defaults to {@code getForceAccessible()}, which defaults to true for compatibility. 077 * </p> 078 * <p> 079 * This default is read from the system property {@code "AbstractReflection.forceAccessible"}, which defaults to true for compatibility. 080 * </p> 081 * <p> 082 * The parsing rules are defined by {@link Boolean#parseBoolean(String)}. 083 * </p> 084 * <p> 085 * See subclasses for specific behavior. 086 * </p> 087 * 088 * @param forceAccessible Whether to force accessibility by calling {@link AccessibleObject#setAccessible(boolean) 089 * AccessibleObject#setAccessible(true)}. 090 * @return {@code this} instance. 091 * @see AccessibleObject#setAccessible(boolean) 092 */ 093 public B setForceAccessible(final boolean forceAccessible) { 094 this.forceAccessible = forceAccessible; 095 return asThis(); 096 } 097 } 098 099 /** 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}