org.frankframework.util.ClassUtils Maven / Gradle / Ivy
/*
Copyright 2013, 2016-2017 Nationale-Nederlanden, 2020-2023 WeAreFrank!
Licensed under the Apache License, Version 2.0 (the "License");
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.frankframework.util;
import java.io.FileNotFoundException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.frankframework.core.INamedObject;
/**
* A collection of class management utility methods.
* @author Johan Verrips
*
*/
public abstract class ClassUtils {
private static final Logger log = LogManager.getLogger(ClassUtils.class);
/**
* Return the context ClassLoader.
*/
private static ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
public static URL getResourceURL(String resource) throws FileNotFoundException {
String resourceToUse = resource; //Don't change the original resource name for logging purposes
// Remove slash like Class.getResource(String name) is doing before delegation to ClassLoader.
// Resources retrieved from ClassLoaders should never start with a leading slash
if (resourceToUse.startsWith("/")) {
resourceToUse = resourceToUse.substring(1);
}
URL url = getClassLoader().getResource(resourceToUse);
// then try to get it as a URL
if (url == null && resourceToUse.contains(":")) {
try {
url = new URL(resourceToUse.replace(" ", "%20"));
} catch (MalformedURLException e) {
FileNotFoundException fnfe = new FileNotFoundException("Cannot find resource ["+resourceToUse+"]");
fnfe.initCause(e);
throw fnfe;
}
}
return url;
}
/**
* Retrieves the constructor of a class, based on the parameters
**/
public static Constructor> getConstructorOnType(Class> clas, Class>[] parameterTypes) throws NoSuchMethodException {
try {
return clas.getDeclaredConstructor(parameterTypes);
} catch (NoSuchMethodException e) {
StringBuilder builder = new StringBuilder("cannot create constructor for Class [" + clas.getName() + "]");
for (int i = 0; i < parameterTypes.length; i++) {
builder.append(", parameter [").append(i).append("] type [").append(parameterTypes[i].getName()).append("]");
}
log.error(builder.toString(), e);
throw e;
}
}
/**
* Create a new instance given a class name. The constructor of the class does NOT have parameters.
*
* @param className The class name to load
* @param expectedType The class type to expect
* @return A new instance
* @exception ReflectiveOperationException If an instantiation error occurs
* @exception SecurityException If a security violation occurs
*/
@SuppressWarnings("unchecked") // because we checked it...
public static T newInstance(String className, Class expectedType) throws ReflectiveOperationException, SecurityException {
Class> clazz = loadClass(className);
if(expectedType.isAssignableFrom(clazz)) {
return (T) newInstance(clazz);
}
throw new InstantiationException("created class ["+className+"] is not of required type ["+expectedType.getSimpleName()+"]");
}
public static T newInstance(Class clazz) throws ReflectiveOperationException, SecurityException {
return clazz.getDeclaredConstructor().newInstance();
}
/**
* Load a class given its name. We want to use a known ClassLoader.
*
* @param className A class name
* @return The class pointed to by className
* @exception ClassNotFoundException If a loading error occurs
*/
public static Class> loadClass(String className) throws ClassNotFoundException {
return ClassUtils.getClassLoader().loadClass(className);
}
/**
* returns the className of the object, without the package name.
*/
@Nonnull
public static String nameOf(Object o) {
String tail=null;
if (o instanceof INamedObject object) {
String name = object.getName();
if (StringUtils.isNotEmpty(name)) {
tail = "["+ name +"]";
}
}
return StringUtil.concatStrings(classNameOf(o), " ", tail);
}
/**
* returns the className of the object, like {@link #nameOf(Object)}, but without [name] suffix for a {@link INamedObject}.
*/
@Nonnull
public static String classNameOf(Object o) {
if (o==null) {
return "";
}
Class> clazz;
if(isClassPresent("org.springframework.util.ClassUtils")) {
if(o instanceof Class> class1) {
clazz = org.springframework.util.ClassUtils.getUserClass(class1);
} else {
clazz = org.springframework.util.ClassUtils.getUserClass(o);
}
} else {
clazz = o instanceof Class> c ? c : o.getClass();
}
final String simpleName = clazz.getSimpleName();
return StringUtils.isNotEmpty(simpleName) ? simpleName : clazz.getTypeName();
}
public static boolean isClassPresent(String className) {
try {
Class.forName(className);
return true;
} catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
/**
* Throws IllegalArgumentException if the argument type is incompatible
* Throws IllegalStateException if the argument cannot be set on the target bean
*/
public static void invokeSetter(Object bean, Method method, String valueToSet) {
if(!method.getName().startsWith("set") || method.getParameterTypes().length != 1) {
throw new IllegalStateException("method must start with [set] and may only contain [1] parameter");
}
try {//Only always grab the first value because we explicitly check method.getParameterTypes().length != 1
Object castValue = parseValueToSet(method, valueToSet);
log.trace("trying to set method [{}] with value [{}] of type [{}] on [{}]", method::getName, () -> valueToSet, () -> castValue.getClass()
.getCanonicalName(), () -> ClassUtils.nameOf(bean));
method.invoke(bean, castValue);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("error while calling method ["+method.getName()+"] on ["+ClassUtils.nameOf(bean)+"]", e);
}
}
private static Object parseValueToSet(Method method, String value) throws IllegalArgumentException {
Class> setterArgumentClass = method.getParameters()[0].getType();
//Try to parse as primitive
try {
return convertToType(setterArgumentClass, value);
} catch (IllegalArgumentException e) {
String fieldName = StringUtil.lcFirst(method.getName().substring(3));
throw new IllegalArgumentException("cannot set field ["+fieldName+"]: " + e.getMessage(), e);
}
}
/**
* Converts the String value to the supplied type.
* @param type to convert the input value to
* @return The converted value, of type {@literal }.
* @throws IllegalArgumentException (or NumberFormatException) when the value cannot be converted to the given type T.
*/
@SuppressWarnings("unchecked")
@Nullable
public static T convertToType(Class type, String value) throws IllegalArgumentException {
return (T) convertToTypeRawTyped(type, value);
}
@Nullable
private static Object convertToTypeRawTyped(Class> type, String value) throws IllegalArgumentException {
if (value == null) {
return null;
}
//Try to parse the value as an Enum
if(type.isEnum()) {
return parseAsEnum(type, value);
}
// Unbox an array to its component type. Convert string input to values. Put back into an array with the right type
if (type.isArray()) {
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy