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.logging.log4j.core.util;
    19
    20  import java.lang.reflect.AccessibleObject;
    21  import java.lang.reflect.Constructor;
    22  import java.lang.reflect.Field;
    23  import java.lang.reflect.InvocationTargetException;
    24  import java.lang.reflect.Member;
    25  import java.lang.reflect.Modifier;
    26
    27  /**
    28   * Utility class for performing common reflective operations.
    29   *
    30   * @since 2.1
    31   */
    32  public final class ReflectionUtil {
    33      private ReflectionUtil() {
    34      }
    35
    36      /**
    37       * Indicates whether or not a {@link Member} is both public and is contained in a public class.
    38       *
    39       * @param member the Member to check for public accessibility (must not be {@code null}).
    40       * @return {@code true} if {@code member} is public and contained in a public class.
    41       * @throws NullPointerException if {@code member} is {@code null}.
    42       */
    43      public static <T extends AccessibleObject & Member> boolean isAccessible(final T member) {
    44          Assert.requireNonNull(member, "No member provided");
    45          return Modifier.isPublic(member.getModifiers()) && Modifier.isPublic(member.getDeclaringClass().getModifiers());
    46      }
    47
    48      /**
    49       * Makes a {@link Member} {@link AccessibleObject#isAccessible() accessible} if the member is not public.
    50       *
    51       * @param member the Member to make accessible (must not be {@code null}).
    52       * @throws NullPointerException if {@code member} is {@code null}.
    53       */
    54      public static <T extends AccessibleObject & Member> void makeAccessible(final T member) {
    55          if (!isAccessible(member) && !member.isAccessible()) {
    56              member.setAccessible(true);
    57          }
    58      }
    59
    60      /**
    61       * Makes a {@link Field} {@link AccessibleObject#isAccessible() accessible} if it is not public or if it is final.
    62       *
    63       * <p>Note that using this method to make a {@code final} field writable will most likely not work very well due to
    64       * compiler optimizations and the like.</p>
    65       *
    66       * @param field the Field to make accessible (must not be {@code null}).
    67       * @throws NullPointerException if {@code field} is {@code null}.
    68       */
    69      public static void makeAccessible(final Field field) {
    70          Assert.requireNonNull(field, "No field provided");
    71          if ((!isAccessible(field) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
    72              field.setAccessible(true);
    73          }
    74      }
    75
    76      /**
    77       * Gets the value of a {@link Field}, making it accessible if required.
    78       *
    79       * @param field    the Field to obtain a value from (must not be {@code null}).
    80       * @param instance the instance to obtain the field value from or {@code null} only if the field is static.
    81       * @return the value stored by the field.
    82       * @throws NullPointerException if {@code field} is {@code null}, or if {@code instance} is {@code null} but
    83       *                              {@code field} is not {@code static}.
    84       * @see Field#get(Object)
    85       */
    86      public static Object getFieldValue(final Field field, final Object instance) {
    87          makeAccessible(field);
    88          if (!Modifier.isStatic(field.getModifiers())) {
    89              Assert.requireNonNull(instance, "No instance given for non-static field");
    90          }
    91          try {
    92              return field.get(instance);
    93          } catch (final IllegalAccessException e) {
    94              throw new UnsupportedOperationException(e);
    95          }
    96      }
    97
    98      /**
    99       * Gets the value of a static {@link Field}, making it accessible if required.
    100      *
    101      * @param field the Field to obtain a value from (must not be {@code null}).
    102      * @return the value stored by the static field.
    103      * @throws NullPointerException if {@code field} is {@code null}, or if {@code field} is not {@code static}.
    104      * @see Field#get(Object)
    105      */
    106     public static Object getStaticFieldValue(final Field field) {
    107         return getFieldValue(field, null);
    108     }
    109
    110     /**
    111      * Sets the value of a {@link Field}, making it accessible if required.
    112      *
    113      * @param field    the Field to write a value to (must not be {@code null}).
    114      * @param instance the instance to write the value to or {@code null} only if the field is static.
    115      * @param value    the (possibly wrapped) value to write to the field.
    116      * @throws NullPointerException if {@code field} is {@code null}, or if {@code instance} is {@code null} but
    117      *                              {@code field} is not {@code static}.
    118      * @see Field#set(Object, Object)
    119      */
    120     public static void setFieldValue(final Field field, final Object instance, final Object value) {
    121         makeAccessible(field);
    122         if (!Modifier.isStatic(field.getModifiers())) {
    123             Assert.requireNonNull(instance, "No instance given for non-static field");
    124         }
    125         try {
    126             field.set(instance, value);
    127         } catch (final IllegalAccessException e) {
    128             throw new UnsupportedOperationException(e);
    129         }
    130     }
    131
    132     /**
    133      * Sets the value of a static {@link Field}, making it accessible if required.
    134      *
    135      * @param field the Field to write a value to (must not be {@code null}).
    136      * @param value the (possibly wrapped) value to write to the field.
    137      * @throws NullPointerException if {@code field} is {@code null}, or if {@code field} is not {@code static}.
    138      * @see Field#set(Object, Object)
    139      */
    140     public static void setStaticFieldValue(final Field field, final Object value) {
    141         setFieldValue(field, null, value);
    142     }
    143
    144     /**
    145      * Gets the default (no-arg) constructor for a given class.
    146      *
    147      * @param clazz the class to find a constructor for
    148      * @param <T>   the type made by the constructor
    149      * @return the default constructor for the given class
    150      * @throws IllegalStateException if no default constructor can be found
    151      */
    152     public static <T> Constructor<T> getDefaultConstructor(final Class<T> clazz) {
    153         Assert.requireNonNull(clazz, "No class provided");
    154         try {
    155             final Constructor<T> constructor = clazz.getDeclaredConstructor();
    156             makeAccessible(constructor);
    157             return constructor;
    158         } catch (final NoSuchMethodException ignored) {
    159             try {
    160                 final Constructor<T> constructor = clazz.getConstructor();
    161                 makeAccessible(constructor);
    162                 return constructor;
    163             } catch (final NoSuchMethodException e) {
    164                 throw new IllegalStateException(e);
    165             }
    166         }
    167     }
    168
    169     /**
    170      * Constructs a new {@code T} object using the default constructor of its class. Any exceptions thrown by the
    171      * constructor will be rethrown by this method, possibly wrapped in an
    172      * {@link java.lang.reflect.UndeclaredThrowableException}.
    173      *
    174      * @param clazz the class to use for instantiation.
    175      * @param <T>   the type of the object to construct.
    176      * @return a new instance of T made from its default constructor.
    177      * @throws IllegalArgumentException if the given class is abstract, an interface, an array class, a primitive type,
    178      *                                  or void
    179      * @throws IllegalStateException    if access is denied to the constructor, or there are no default constructors
    180      */
    181     public static <T> T instantiate(final Class<T> clazz) {
    182         Assert.requireNonNull(clazz, "No class provided");
    183         final Constructor<T> constructor = getDefaultConstructor(clazz);
    184         try {
    185             return constructor.newInstance();
    186         } catch (final InstantiationException e) {
    187             throw new IllegalArgumentException(e);
    188         } catch (final IllegalAccessException e) {
    189             throw new IllegalStateException(e);
    190         } catch (final InvocationTargetException e) {
    191             Throwables.rethrow(e.getCause());
    192             throw new InternalError("Unreachable");
    193         }
    194     }
    195 }