All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.camunda.bpm.engine.impl.util.ReflectUtil Maven / Gradle / Ivy

There is a newer version: 7.22.0-alpha5
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.camunda.bpm.engine.impl.util;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.impl.ProcessEngineLogger;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.context.Context;

import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull;

/**
 * @author Tom Baeyens
 */
public abstract class ReflectUtil {

  private static final EngineUtilLogger LOG = ProcessEngineLogger.UTIL_LOGGER;

  private static final Map charEncodings = new HashMap<>();

  static {
    charEncodings.put("ä", "%C3%A4");
    charEncodings.put("ö", "%C3%B6");
    charEncodings.put("ü", "%C3%BC");
    charEncodings.put("Ä", "%C3%84");
    charEncodings.put("Ö", "%C3%96");
    charEncodings.put("Ü", "%C3%9C");
  }

  public static ClassLoader getClassLoader() {
    ClassLoader loader = getCustomClassLoader();
    if(loader == null) {
      loader = Thread.currentThread().getContextClassLoader();
    }
    return loader;
  }

  public static Class loadClass(String className) {
   Class clazz = null;
   ClassLoader classLoader = getCustomClassLoader();

   // First exception in chain of classloaders will be used as cause when no class is found in any of them
   Throwable throwable = null;

   if(classLoader != null) {
     try {
       LOG.debugClassLoading(className, "custom classloader", classLoader);
       clazz = Class.forName(className, true, classLoader);
     }
     catch(Throwable t) {
       throwable = t;
     }
   }
   if(clazz == null) {
     try {
       ClassLoader contextClassloader = ClassLoaderUtil.getContextClassloader();
       if(contextClassloader != null) {
         LOG.debugClassLoading(className, "current thread context classloader", contextClassloader);
         clazz = Class.forName(className, true, contextClassloader);
       }
     }
     catch(Throwable t) {
       if(throwable == null) {
         throwable = t;
       }
     }
     if(clazz == null) {
       try {
         ClassLoader localClassloader = ClassLoaderUtil.getClassloader(ReflectUtil.class);
         LOG.debugClassLoading(className, "local classloader", localClassloader);
         clazz = Class.forName(className, true, localClassloader);
       }
       catch(Throwable t) {
         if(throwable == null) {
           throwable = t;
         }
       }
     }
   }

   if(clazz == null) {
     throw LOG.classLoadingException(className, throwable);
   }
   return clazz;
  }

  public static  Class loadClass(String className, ClassLoader customClassloader, Class clazz) throws ClassNotFoundException, ClassCastException {
    if(customClassloader != null) {
      return (Class) customClassloader.loadClass(className);
    } else {
      return (Class) loadClass(className);
    }
  }

  public static InputStream getResourceAsStream(String name) {
    InputStream resourceStream = null;
    ClassLoader classLoader = getCustomClassLoader();
    if(classLoader != null) {
      resourceStream = classLoader.getResourceAsStream(name);
    }

    if(resourceStream == null) {
      // Try the current Thread context classloader
      classLoader = Thread.currentThread().getContextClassLoader();
      resourceStream = classLoader.getResourceAsStream(name);
      if(resourceStream == null) {
        // Finally, try the classloader for this class
        classLoader = ReflectUtil.class.getClassLoader();
        resourceStream = classLoader.getResourceAsStream(name);
      }
    }
    return resourceStream;
   }

  public static URL getResource(String name) {
    URL url = null;
    ClassLoader classLoader = getCustomClassLoader();
    if(classLoader != null) {
      url = classLoader.getResource(name);
    }
    if(url == null) {
      // Try the current Thread context classloader
      classLoader = Thread.currentThread().getContextClassLoader();
      url = classLoader.getResource(name);
      if(url == null) {
        // Finally, try the classloader for this class
        classLoader = ReflectUtil.class.getClassLoader();
        url = classLoader.getResource(name);
      }
    }

    return url;
   }

  public static String getResourceUrlAsString(String name) {
    String url = getResource(name).toString();
    for (Map.Entry mapping : charEncodings.entrySet()) {
      url = url.replaceAll(mapping.getKey(), mapping.getValue());
    }
    return url;
  }

  /**
   * Converts an url to an uri. Escapes whitespaces if needed.
   *
   * @param url  the url to convert
   * @return the resulting uri
   * @throws ProcessEngineException if the url has invalid syntax
   */
  public static URI urlToURI(URL url) {
    try {
      return url.toURI();
    }
    catch (URISyntaxException e) {
      throw LOG.cannotConvertUrlToUri(url, e);
    }
  }


  public static Object instantiate(String className) {
    try {
      Class< ? > clazz = loadClass(className);
      return clazz.newInstance();
    }
    catch (Exception e) {
      throw LOG.exceptionWhileInstantiatingClass(className, e);
    }
  }

  public static  T instantiate(Class type) {
    try {
      return type.newInstance();
    }
    catch (Exception e) {
      throw LOG.exceptionWhileInstantiatingClass(type.getName(), e);
    }
  }

  public static  T createInstance(Class clazz) {
    return instantiate(clazz);
  }

  public static Object invoke(Object target, String methodName, Object[] args) {
    try {
      Class clazz = target.getClass();
      Method method = findMethod(clazz, methodName, Arrays.stream(args).map(Object::getClass).toArray(Class[]::new));
      method.setAccessible(true);
      return method.invoke(target, args);
    }
    catch (Exception e) {
      throw LOG.exceptionWhileInvokingMethod(methodName, target, e);
    }
  }

  /**
   * Returns the field of the given object or null if it doesnt exist.
   */
  public static Field getField(String fieldName, Object object) {
    return getField(fieldName, object.getClass());
  }

  /**
   * Returns the field of the given class or null if it doesnt exist.
   */
  public static Field getField(String fieldName, Class clazz) {
    Field field = null;
    try {
      field = clazz.getDeclaredField(fieldName);
    }
    catch (SecurityException e) {
      throw LOG.unableToAccessField(field, clazz.getName());
    }
    catch (NoSuchFieldException e) {
      // for some reason getDeclaredFields doesnt search superclasses
      // (which getFields() does ... but that gives only public fields)
      Class superClass = clazz.getSuperclass();
      if (superClass != null) {
        return getField(fieldName, superClass);
      }
    }
    return field;
  }

  public static void setField(Field field, Object object, Object value) {
    try {
      field.setAccessible(true);
      field.set(object, value);
    }
    catch (Exception e) {
      throw LOG.exceptionWhileSettingField(field, object, value, e);
    }
  }

  /**
   * Returns the setter-method for the given field name or null if no setter exists.
   */
  public static Method getSetter(String fieldName, Class clazz, Class fieldType) {
    String setterName = buildSetterName(fieldName);
    try {
      // Using getMathods(), getMathod(...) expects exact parameter type
      // matching and ignores inheritance-tree.
      Method[] methods = clazz.getMethods();
      for(Method method : methods) {
        if(method.getName().equals(setterName)) {
          Class[] paramTypes = method.getParameterTypes();
          if(paramTypes != null && paramTypes.length == 1 && paramTypes[0].isAssignableFrom(fieldType)) {
            return method;
          }
        }
      }
      return null;
    }
    catch (SecurityException e) {
      throw LOG.unableToAccessMethod(setterName, clazz.getName());
    }
  }

  /**
   * Returns a setter method based on the fieldName and the java beans setter naming convention or null if none exists.
   * If multiple setters with different parameter types are present, an exception is thrown.
   * If they have the same parameter type, one of those methods is returned.
   */
  public static Method getSingleSetter(String fieldName, Class clazz) {
    String setterName = buildSetterName(fieldName);
    try {
      // Using getMathods(), getMathod(...) expects exact parameter type
      // matching and ignores inheritance-tree.
      Method[] methods = clazz.getMethods();
      List candidates = new ArrayList<>();
      Set> parameterTypes = new HashSet<>();
      for(Method method : methods) {
        if(method.getName().equals(setterName)) {
          Class[] paramTypes = method.getParameterTypes();

          if(paramTypes != null && paramTypes.length == 1) {
            candidates.add(method);
            parameterTypes.add(paramTypes[0]);
          }
        }
      }

      if (parameterTypes.size() > 1) {
        throw LOG.ambiguousSetterMethod(setterName, clazz.getName());
      }
      if (candidates.size() >= 1) {
        return candidates.get(0);
      }

      return null;
    }
    catch (SecurityException e) {
      throw LOG.unableToAccessMethod(setterName, clazz.getName());
    }
  }

  private static String buildSetterName(String fieldName) {
    return "set" + Character.toTitleCase(fieldName.charAt(0)) +
        fieldName.substring(1, fieldName.length());
  }

  private static Method findMethod(Class< ? extends Object> clazz, String methodName, Class< ? >[] args) {
    for (Method method : clazz.getDeclaredMethods()) {
      // TODO add parameter matching
      if ( method.getName().equals(methodName)
           && matches(method.getParameterTypes(), args)
         ) {
        return method;
      }
    }
    Class< ? > superClass = clazz.getSuperclass();
    if (superClass!=null) {
      return findMethod(superClass, methodName, args);
    }
    return null;
  }

  public static Object instantiate(String className, Object[] args) {
    Class clazz = loadClass(className);
    Constructor constructor = findMatchingConstructor(clazz, args);
    ensureNotNull("couldn't find constructor for " + className + " with args " + Arrays.asList(args), "constructor", constructor);
    try {
      return constructor.newInstance(args);
    } catch (Exception e) {
      throw LOG.exceptionWhileInstantiatingClass(className, e);
    }
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  private static  Constructor findMatchingConstructor(Class clazz, Object[] args) {
    for (Constructor constructor: clazz.getDeclaredConstructors()) { // cannot use  or  due to JDK 5/6 incompatibility
      if (matches(constructor.getParameterTypes(), Arrays.stream(args).map(Object::getClass).toArray(Class[]::new))){
        return constructor;
      }
    }
    return null;
  }

  private static boolean matches(Class< ? >[] parameterTypes, Class< ? >[] args) {
    if ( parameterTypes==null
         || parameterTypes.length==0
       ) {
      return args==null
               || args.length==0;
    }
    if ( args==null
         || parameterTypes.length!=args.length
       ) {
      return false;
    }
    for (int i=0; i declaringType, String methodName, Class... parameterTypes) {
    return findMethod(declaringType, methodName, parameterTypes);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy