Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1997, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025 Contributors to the Eclipse Foundation. All rights reserved.
* Copyright (c) 2025-2026 Contributors to the Eclipse Foundation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
Expand All @@ -15,12 +15,8 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -45,7 +41,34 @@ public final class ClassFactory {
/**
* Cache from a class to its default constructor.
*/
private static final Map<Class, WeakReference<Constructor>> constructorClassCache = Collections.synchronizedMap(new WeakHashMap<>());
private static final ClassValue<Constructor> DECLARED_CTORS =
new ClassValue<>() {
@Override
protected Constructor computeValue(Class<?> clazz) {
Constructor cons;
if (System.getSecurityManager() == null) {
cons = tryGetDeclaredConstructor(clazz);
} else {
cons = AccessController.doPrivileged(
(PrivilegedAction<Constructor<?>>) () -> tryGetDeclaredConstructor(clazz)
);
}

int classMod = clazz.getModifiers();

if(!Modifier.isPublic(classMod) || !Modifier.isPublic(cons.getModifiers())) {
// attempt to make it work even if the constructor is not accessible
try {
cons.setAccessible(true);
} catch(SecurityException e) {
// but if we don't have a permission to do so, work gracefully.
logger.log(Level.FINE, e, () -> "Unable to make the constructor of "+clazz+" accessible");
throw e;
}
}
return cons;
}
};

private ClassFactory() {}

Expand All @@ -61,46 +84,15 @@ public static void cleanCache() {
* Creates a new instance of the class but throw exceptions without catching it.
*/
public static <T> T create0( final Class<T> clazz ) throws IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<T> cons = null;
WeakReference<Constructor> consRef = constructorClassCache.get(clazz);
if(consRef!=null)
cons = consRef.get();
if(cons==null) {
if (System.getSecurityManager() == null) {
cons = tryGetDeclaredConstructor(clazz);
} else {
cons = AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Constructor<T> run() {
return tryGetDeclaredConstructor(clazz);
}
});
}

int classMod = clazz.getModifiers();

if(!Modifier.isPublic(classMod) || !Modifier.isPublic(cons.getModifiers())) {
// attempt to make it work even if the constructor is not accessible
try {
cons.setAccessible(true);
} catch(SecurityException e) {
// but if we don't have a permission to do so, work gracefully.
logger.log(Level.FINE,"Unable to make the constructor of "+clazz+" accessible",e);
throw e;
}
}

constructorClassCache.put(clazz, new WeakReference<>(cons));
}

Constructor<T> cons = DECLARED_CTORS.get(clazz);
return cons.newInstance(emptyObject);
}

private static <T> Constructor<T> tryGetDeclaredConstructor(Class<T> clazz) {
try {
return clazz.getDeclaredConstructor(emptyClass);
} catch (NoSuchMethodException e) {
logger.log(Level.INFO,"No default constructor found on "+clazz,e);
logger.log(Level.INFO, e, () -> "No default constructor found on "+clazz);
NoSuchMethodError exp;
if(clazz.getDeclaringClass()!=null && !Modifier.isStatic(clazz.getModifiers())) {
exp = new NoSuchMethodError(Messages.NO_DEFAULT_CONSTRUCTOR_IN_INNER_CLASS
Expand All @@ -121,10 +113,10 @@ public static <T> T create( Class<T> clazz ) {
try {
return create0(clazz);
} catch (InstantiationException e) {
logger.log(Level.INFO,"failed to create a new instance of "+clazz,e);
logger.log(Level.INFO, e, () -> "failed to create a new instance of "+clazz);
throw new InstantiationError(e.toString());
} catch (IllegalAccessException e) {
logger.log(Level.INFO,"failed to create a new instance of "+clazz,e);
logger.log(Level.INFO, e, () -> "failed to create a new instance of "+clazz);
throw new IllegalAccessError(e.toString());
} catch (InvocationTargetException e) {
Throwable target = e.getTargetException();
Expand Down Expand Up @@ -163,10 +155,10 @@ public static Object create(Method method) {

throw new IllegalStateException(target);
} catch (IllegalAccessException e) {
logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),e);
logger.log(Level.INFO, e, () -> "failed to create a new instance of "+method.getReturnType().getName());
throw new IllegalAccessError(e.toString());
} catch (IllegalArgumentException | NullPointerException | ExceptionInInitializerError iae){
logger.log(Level.INFO,"failed to create a new instance of "+method.getReturnType().getName(),iae);
logger.log(Level.INFO, iae, () -> "failed to create a new instance of "+method.getReturnType().getName());
errorMsg = iae;
}

Expand All @@ -175,9 +167,9 @@ public static Object create(Method method) {
exp.initCause(errorMsg);
throw exp;
}

/**
* Infers the instanciable implementation class that can be assigned to the given field type.
* Infers the instantiable implementation class that can be assigned to the given field type.
*
* @return null
* if inference fails.
Expand Down
Loading