Skip to content
Open
Show file tree
Hide file tree
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
Expand Up @@ -23,16 +23,12 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Data item, which contains an instance of any class.
*/
public class SimpleDataItem implements DataItem {

private static final Map<Class<?>, MethodsCache> methodCacheMap = new ConcurrentHashMap<>();

protected Object item;

public SimpleDataItem(Object item) {
Expand Down Expand Up @@ -95,8 +91,7 @@ public Object getValue(String path) {
}

protected MethodsCache getMethodsCache(Object object) {
Class<?> cls = object.getClass();
return methodCacheMap.computeIfAbsent(cls, k -> MethodsCache.getOrCreate(cls));
return MethodsCache.getOrCreate(object.getClass());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@
import com.google.common.collect.ImmutableMap;
import io.jmix.core.metamodel.annotation.JmixProperty;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;

import org.springframework.lang.Nullable;
import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;

Expand All @@ -48,10 +47,15 @@ public class MethodsCache {
.put(boolean.class, Boolean.class)
.build();

private static final Map<Class, MethodsCache> methodCacheMap = new ConcurrentHashMap<>();
private static final ClassValue<MethodsCache> methodCacheMap = new ClassValue<>() {
@Override
protected MethodsCache computeValue(Class<?> type) {
return new MethodsCache(type);
}
};

public static MethodsCache getOrCreate(Class clazz) {
return methodCacheMap.computeIfAbsent(clazz, MethodsCache::new);
return methodCacheMap.get(clazz);
}

private MethodsCache(Class clazz) {
Expand Down Expand Up @@ -153,42 +157,70 @@ private Method chooseGetter(Class clazz, String propertyName, Method found, @Nul
}

private Function createGetter(Class clazz, Method method) {
Function getter;
try {
MethodHandles.Lookup caller = MethodHandles.lookup();
CallSite site = LambdaMetafactory.metafactory(caller,
"apply",
MethodType.methodType(Function.class),
MethodType.methodType(Object.class, Object.class),
caller.findVirtual(clazz, method.getName(), MethodType.methodType(method.getReturnType())),
MethodType.methodType(method.getReturnType(), clazz));
MethodHandle factory = site.getTarget();
getter = (Function) factory.invoke();
} catch (Throwable t) {
throw new RuntimeException("Can not create getter", t);
// If a class was hot-deployed, then it will be loaded
// by a different class loader. This will make it impossible to create a lambda
// using LambdaMetaFactory for producing the method in Java 17+
if (getClass().getClassLoader() == clazz.getClassLoader()) {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
CallSite site = LambdaMetafactory.metafactory(lookup,
"apply",
MethodType.methodType(Function.class),
MethodType.methodType(Object.class, Object.class),
lookup.unreflect(method),
MethodType.methodType(method.getReturnType(), clazz));
return (Function) site.getTarget().invoke();
} catch (Throwable t) {
throw new RuntimeException("Can not create getter", t);
}
} else {
try {
MethodHandle methodHandle = MethodHandles.lookup().unreflect(method);
return obj -> {
try {
return methodHandle.invoke(obj);
} catch (Throwable throwable) {
throw new RuntimeException("Error calling getter", throwable);
}
};
} catch (IllegalAccessException e) {
throw new RuntimeException("Can not create getter", e);
}
}

return getter;
}

private BiConsumer createSetter(Class clazz, Method method) {
Class valueType = method.getParameterTypes()[0];
BiConsumer setter;
try {
MethodHandles.Lookup caller = MethodHandles.lookup();
CallSite site = LambdaMetafactory.metafactory(caller,
"accept",
MethodType.methodType(BiConsumer.class),
MethodType.methodType(void.class, Object.class, Object.class),
caller.findVirtual(clazz, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()[0])),
MethodType.methodType(void.class, clazz, valueType.isPrimitive() ? primitivesToObjects.get(valueType) : valueType));
MethodHandle factory = site.getTarget();
setter = (BiConsumer) factory.invoke();
} catch (Throwable t) {
throw new RuntimeException("Can not create setter", t);
// If a class was hot-deployed, then it will be loaded
// by a different class loader. This will make it impossible to create a lambda
// using LambdaMetaFactory for producing the method in Java 17+
if (getClass().getClassLoader() == clazz.getClassLoader()) {
try {
Class valueType = method.getParameterTypes()[0];
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
CallSite site = LambdaMetafactory.metafactory(lookup,
"accept",
MethodType.methodType(BiConsumer.class),
MethodType.methodType(void.class, Object.class, Object.class),
lookup.unreflect(method),
MethodType.methodType(void.class, clazz, valueType.isPrimitive() ? primitivesToObjects.get(valueType) : valueType));
return (BiConsumer) site.getTarget().invoke();
} catch (Throwable t) {
throw new RuntimeException("Can not create setter", t);
}
} else {
try {
MethodHandle methodHandle = MethodHandles.lookup().unreflect(method);
return (obj, value) -> {
try {
methodHandle.invoke(obj, value);
} catch (Throwable throwable) {
throw new RuntimeException("Error calling setter", throwable);
}
};
} catch (IllegalAccessException e) {
throw new RuntimeException("Can not create setter", e);
}
}

return setter;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.springframework.lang.Nullable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
Expand All @@ -35,7 +34,6 @@
public class ListPivotTableItems<T> implements PivotTableItems<T> {

private EventBus eventBus;
private static final Map<Class<?>, MethodsCache> methodCacheMap = new ConcurrentHashMap<>();

protected final List<T> items = new ArrayList<>();
protected final String idAttribute;
Expand Down Expand Up @@ -246,8 +244,7 @@ protected Object deserializeId(String stringValue) {
}

protected MethodsCache getMethodsCache(Object object) {
Class<?> cls = object.getClass();
return methodCacheMap.computeIfAbsent(cls, k -> MethodsCache.getOrCreate(cls));
return MethodsCache.getOrCreate(object.getClass());
}

protected void fireChangeEvent(ItemsChangeType operationType, Collection<T> items) {
Expand Down