Skip to content

Instantly share code, notes, and snippets.

@basinilya
Last active August 1, 2020 19:01
Show Gist options
  • Save basinilya/d257de5b5add484d23c15e7e99ef4f03 to your computer and use it in GitHub Desktop.
Save basinilya/d257de5b5add484d23c15e7e99ef4f03 to your computer and use it in GitHub Desktop.
package org.foo.testproxy;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
public class CoalescingProperties {
public static void main(String[] args) throws Exception {
CoalescingProperties inst = new CoalescingProperties();
A a = new A();
inst.setABCValue(a, null);
if (null != a.getB()) {
throw new Exception();
}
inst.setABCValue(a, "xxx");
if (!"xxx".equals(a.getB().getC().getValue())) {
throw new Exception();
}
}
public String setABCValue(A a, String newValue) throws Exception {
a = Coalescing.wrap(a);
C c = a.getB().getC();
String oldValue = c.getValue();
if (!Objects.equals(oldValue, newValue)) {
c.setValue(newValue);
}
return oldValue;
}
public static class Coalescing {
public static <T> T wrap(T obj) throws Exception {
return wrap(obj, null, null, null);
}
public static <T> T wrap(T obj, Object parent, Method parentSetter, CoalescingInterceptor<?> parentProxy) throws Exception {
ProxyFactory factory = new ProxyFactory(obj);
CoalescingInterceptor<T> adv = new CoalescingInterceptor<T>(obj, parent, parentSetter, parentProxy);
factory.addAdvice(adv);
//factory.addInterface(obj.getClass());
@SuppressWarnings("unchecked")
T proxy = (T)factory.getProxy();
return proxy;
}
}
public static class CoalescingInterceptor<T> implements MethodInterceptor {
private Object parent;
private Method parentSetter;
private CoalescingInterceptor<?> parentProxy;
private Map<Method, PropertyDescriptor> propMap = new HashMap<>();
private Map<String, Object> props = new HashMap<>();
public CoalescingInterceptor(T obj, Object parent, Method parentSetter, CoalescingInterceptor<?> parentProxy) throws Exception {
this.parent = parent;
this.parentSetter = parentSetter;
this.parentProxy = parentProxy;
Class<?> clazz = obj.getClass();
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor prop : props) {
Method getter = prop.getReadMethod();
Method setter = prop.getWriteMethod();
if (getter != null && setter != null && getter.getParameterTypes().length == 0
&& setter.getParameterTypes().length == 1
) {
propMap.put(getter, prop);
propMap.put(setter, prop);
}
}
}
private static boolean isComplexType(Class<?> clazz) {
return !(clazz.isPrimitive() ||
clazz.getPackage().getName().startsWith("java"));
}
public Object invoke(MethodInvocation invocation) throws Throwable {
Object res = invocation.proceed();
Method method = invocation.getMethod();
PropertyDescriptor prop = propMap.get(method);
Object self = invocation.getThis();
if (prop != null) {
String propName = prop.getName();
Method readMethod = prop.getReadMethod();
if (method.equals(readMethod)) {
Object cachedProxy = props.get(propName);
if (cachedProxy != null) {
res = cachedProxy;
} else if (isComplexType(prop.getPropertyType())) {
if (res == null) {
Class<?> resClazz = method.getReturnType();
res = resClazz.newInstance();
}
res = Coalescing.wrap(res, self, prop.getWriteMethod(), this);
props.put(propName, res);
}
} else { // setter
commit(self);
}
}
return res;
}
private void commit(Object self) throws Exception {
if (parent != null) {
parentSetter.invoke(parent, self);
parentProxy.commit(parent);
}
}
}
public static class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public static class B {
private C c;
public C getC() {
return c;
}
public void setC(C b) {
this.c = b;
}
}
public static class C {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
package org.foo.testproxy;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
public class PropFromLambda {
public static ChildBean getOrCreateFast(ParentBean bean, Function<ParentBean, ChildBean> getter) throws Exception {
ChildBean res = getter.apply(bean);
if (res == null) {
res = new ChildBean();
bean.setB(res);
}
return res;
}
public static <T,R> R getOrCreate(T bean, Function<T, R> getter) throws Exception {
log("getOrCreate");
R res = getter.apply(bean);
if (res == null) {
log("getter returned null, trying INITER_CACHE");
WeakIdentityHashMap<Function<T, R>, FunctionWithExceptions<T, R, Exception>> initerCache = getIniterCache();
FunctionWithExceptions<T, R, Exception> initer = initerCache.get(getter);
if (initer != null) {
log("initer found");
return (R)initer.apply(bean);
}
log("initer not found, trying proxy");
Entry<T, CoalescingInterceptor<T>> pair = wrap(bean);
ThreadLocal<FunctionWithExceptions<T, R, Exception>> initerTls =
pair.getValue().getIniterTls();
try {
res = getter.apply( pair.getKey() );
initer = initerTls.get();
} finally {
initerTls.set(null);
}
if (initer != null) {
res = initer.apply(bean);
} else {
initer = cast(NULL_INITER);
}
String comment = "getter " + System.identityHashCode(getter);
log(comment);
initerCache.put(getter, initer, comment);
}
return res;
}
public static class ChildBean {
ChildBean() {
log("ChildBean");
}
}
public static class ParentBean {
ChildBean b;
public ChildBean getB() {
return b;
}
public void setB(ChildBean a) {
log("setB");
this.b = a;
}
}
public static void main(String[] args) throws Exception {
doStuff();
long stime = System.currentTimeMillis();
long etime = stime + 1000 * 10;
int i = 0;
for (;etime - System.currentTimeMillis() > 0;i++) {
doStuff();
log("");
//gc();
log("");
doStuff();
}
System.out.println(MessageFormat.format("{0}", i));
}
static void log(String msg) {
//System.out.println(msg);
}
private static void gc() {
for (int i = 0; i < 100; i++) {
(new byte[1000000]).getClass();
System.gc();
}
}
private static void doStuff() throws Exception {
ParentBean a = new ParentBean();
ChildBean b;
boolean useMRef = true;
if (useMRef) {
//b = getOrCreate(a, PropFromLambda::dummy);
b = getOrCreate(a, ParentBean::getB);
} else {
double f = Math.random() + 1.0;
b = getOrCreate(a, (a2) -> { return f != 0 ? a2.getB() : null; });
}
String.valueOf(b);
log("");
a.setB(null);
if (useMRef) {
//b = getOrCreate(a, PropFromLambda::dummy);
b = getOrCreate(a, ParentBean::getB);
} else {
double f = Math.random();
b = getOrCreate(a, (a2) -> { return f != 0 ? a2.getB() : null; });
}
String.valueOf(b);
}
static ChildBean dummy(ParentBean bean) {
return null;
}
static final WeakIdentityHashMap<?,?> INITER_CACHE2 =
new WeakIdentityHashMap<>();
static <T, R> WeakIdentityHashMap<Function<T, R>, FunctionWithExceptions<T, R, Exception>>
getIniterCache() {
return cast(INITER_CACHE2);
}
static final WeakIdentityHashMap<?, ?> HOOK_CACHE2 = new WeakIdentityHashMap<>();
static <T> WeakIdentityHashMap<T, WeakReference<Map.Entry<T, CoalescingInterceptor<T> > > > getHookCache() {
return cast(HOOK_CACHE2);
}
private static final FunctionWithExceptions<?, ?, Exception> NULL_INITER = x -> null;
@SuppressWarnings("unchecked")
private static <T> Entry<T, CoalescingInterceptor<T>> wrap(T obj ) throws Exception {
Entry<T, CoalescingInterceptor<T>> pair = null;
CoalescingInterceptor<T> inter = null;
T proxy = null;
WeakIdentityHashMap<T, WeakReference<Entry<T, CoalescingInterceptor<T>>>> hookCache =
getHookCache();
WeakReference<Map.Entry<T, CoalescingInterceptor<T>>> ref = hookCache.get(obj);
if (ref != null) {
pair = ref.get();
if (pair != null) {
log("proxy found");
proxy = pair.getKey();
inter = pair.getValue();
}
}
if (pair == null) {
log("proxy not found, creating");
ProxyFactory factory = new ProxyFactory(obj);
inter = findInterceptor(obj.getClass());
factory.addAdvice(inter);
proxy = (T)factory.getProxy();
pair = new AbstractMap.SimpleEntry<>(proxy, inter);
String comment = "object " + System.identityHashCode(obj);
log(comment);
hookCache.put(obj, new WeakReference<>(pair), comment);
}
return pair;
}
private static <T> CoalescingInterceptor<T> findInterceptor(Class<?> clazz) throws Exception {
return new CoalescingInterceptor<T>(clazz);
}
private static class CoalescingInterceptor<T> implements MethodInterceptor {
private final ThreadLocal<Object> initerTls = new ThreadLocal<>();
<R> ThreadLocal< FunctionWithExceptions<T, R, Exception> > getIniterTls() {
return cast(initerTls);
}
private Map<Method, PropertyDescriptor> propMap = new HashMap<>();
CoalescingInterceptor(Class<?> clazz) throws Exception {
log("CoalescingInterceptor");
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor prop : props) {
Method getter = prop.getReadMethod();
Method setter = prop.getWriteMethod();
if (getter != null && setter != null && getter.getParameterTypes().length == 0
&& setter.getParameterTypes().length == 1) {
propMap.put(getter, prop);
}
}
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object res = invocation.proceed();
if (res == null) {
Method method = invocation.getMethod();
log("invoke " + method.getName());
PropertyDescriptor prop = propMap.get(method);
if (prop != null) {
Class<?> resClazz = method.getReturnType();
Method setterMethod = prop.getWriteMethod();
final int DIRECT = 0; // 257 095 851
final int REFLECT = 1; // 221 991 485
final int INVOKE = 2; // 197 712 765
final int EXACT_PARAMS_CAST = 3; // 210 515 640
final int EXACT_HANDLERS_ADAPTED = 4; // 220 141 084
int mode = REFLECT;
FunctionWithExceptions<T, ?, Throwable> initer;
switch (mode) {
case DIRECT:
initer = target -> {
ChildBean res2 = new ChildBean();
((ParentBean)target).setB(res2);
return res2;
};
break;
case REFLECT:
initer = target -> {
Object res2 = resClazz.newInstance();
setterMethod.invoke(target, res2);
return res2;
};
break;
default:
Lookup lookup = MethodHandles.lookup();
final MethodHandle setterHandle = lookup.unreflect(setterMethod);
final MethodHandle constructorHandle = lookup.findConstructor(resClazz, MethodType.methodType(void.class));
if (mode == INVOKE) {
initer = target -> {
Object res2 = constructorHandle.invoke();
setterHandle.invoke(target, res2);
return res2;
};
} else if (mode == EXACT_HANDLERS_ADAPTED) {
final MethodHandle setterHandle2 = setterHandle.asType( MethodType.methodType(void.class, Object.class,Object.class));
final MethodHandle constructorHandle2 = constructorHandle.asType(MethodType.methodType( Object.class ));
initer = target -> {
Object res2 = constructorHandle2.invokeExact();
setterHandle2.invokeExact (target, res2);
return res2;
};
} else {
initer = target -> {
ChildBean res2 = (ChildBean)constructorHandle.invokeExact();
setterHandle.invokeExact ((ParentBean)target, res2);
return res2;
};
}
}
initerTls.set(initer);
}
}
return res;
}
}
static <T> T invokeExact(MethodHandle constructorHandle) throws Throwable {
return (T)constructorHandle.invokeExact();
}
@SuppressWarnings("unchecked")
static <T> T cast(Object x) {
return (T)x;
}
@FunctionalInterface
private interface FunctionWithExceptions<T, R, E extends Throwable> {
R apply(T t) throws E;
}
}
package org.foo.testproxy;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
/**
* <p>A map where keys are compared using identity comparison (like
* IdentityHashMap) but where the presence of an object as a key in
* the map does not prevent it being garbage collected (like
* WeakHashMap). This class does not implement the Map interface
* because it is difficult to ensure correct semantics for iterators
* over the entrySet().</p>
*
* <p>Because we do not implement Map, we do not copy the questionable
* interface where you can call get(k) or remove(k) for any type of k,
* which of course can only have an effect if k is of type K.</p>
*
* <p>This map does not support null keys.</p>
*/
/*
* The approach
* is to wrap each key in a WeakReference and use the wrapped value as
* a key in an ordinary HashMap. The WeakReference has to be a
* subclass IdentityWeakReference (IWR) where two IWRs are equal if
* they refer to the same object. This enables us to find the entry
* again.
*/
class WeakIdentityHashMap<K, V> {
WeakIdentityHashMap() {}
V get(K key) {
expunge();
WeakReference<K> keyref = makeReference(key);
return map.get(keyref);
}
public void clear() {
map.clear();
}
public V put(K key, V value, String comment) {
expunge();
if (key == null)
throw new IllegalArgumentException("Null key");
WeakReference<K> keyref = makeReference(key, refQueue, comment);
return map.put(keyref, value);
}
public V remove(K key) {
expunge();
WeakReference<K> keyref = makeReference(key);
return map.remove(keyref);
}
private void expunge() {
Reference<? extends K> ref;
while ((ref = refQueue.poll()) != null) {
PropFromLambda.log("expunge: " + ((IdentityWeakReference<?>)ref).comment);
map.remove(ref);
}
}
private WeakReference<K> makeReference(K referent) {
return new IdentityWeakReference<K>(referent);
}
private WeakReference<K> makeReference(K referent, ReferenceQueue<K> q, String comment) {
return new IdentityWeakReference<K>(referent, q, comment);
}
/**
* WeakReference where equals and hashCode are based on the
* referent. More precisely, two objects are equal if they are
* identical or if they both have the same non-null referent. The
* hashCode is the value the original referent had. Even if the
* referent is cleared, the hashCode remains. Thus, objects of
* this class can be used as keys in hash-based maps and sets.
*/
private static class IdentityWeakReference<T> extends WeakReference<T> {
private final String comment;
IdentityWeakReference(T o) {
this(o, null, null);
}
IdentityWeakReference(T o, ReferenceQueue<T> q, String comment) {
super(o, q);
this.hashCode = (o == null) ? 0 : System.identityHashCode(o);
this.comment = comment;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof IdentityWeakReference<?>))
return false;
IdentityWeakReference<?> wr = (IdentityWeakReference<?>) o;
Object got = get();
return (got != null && got == wr.get());
}
public int hashCode() {
return hashCode;
}
private final int hashCode;
}
private Map<WeakReference<K>, V> map = new HashMap<>();
private ReferenceQueue<K> refQueue = new ReferenceQueue<K>();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment