Skip to content

Commit 29a04f4

Browse files
committed
Removed dependency on asm. We need to make beans implement an interface but there is no need to generate the interface itself.
1 parent a31d396 commit 29a04f4

File tree

5 files changed

+119
-164
lines changed

5 files changed

+119
-164
lines changed

src/main/java/org/apache/ibatis/executor/loader/DeserializedObjectProxy.java

Lines changed: 0 additions & 112 deletions
This file was deleted.
Lines changed: 94 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
package org.apache.ibatis.executor.loader;
22

3-
import java.io.ObjectStreamException;
43
import java.lang.reflect.Method;
54
import java.util.Arrays;
65
import java.util.HashSet;
76
import java.util.List;
7+
import java.util.Locale;
88
import java.util.Set;
99

10-
import net.sf.cglib.core.Signature;
10+
import net.sf.cglib.proxy.Callback;
1111
import net.sf.cglib.proxy.Enhancer;
12-
import net.sf.cglib.proxy.InterfaceMaker;
1312
import net.sf.cglib.proxy.MethodInterceptor;
1413
import net.sf.cglib.proxy.MethodProxy;
1514

15+
import org.apache.ibatis.executor.ExecutorException;
1616
import org.apache.ibatis.logging.Log;
1717
import org.apache.ibatis.logging.LogFactory;
1818
import org.apache.ibatis.reflection.ExceptionUtil;
1919
import org.apache.ibatis.reflection.factory.ObjectFactory;
2020
import org.apache.ibatis.reflection.property.PropertyCopier;
2121
import org.apache.ibatis.reflection.property.PropertyNamer;
2222
import org.apache.ibatis.type.TypeHandlerRegistry;
23-
import org.objectweb.asm.Type;
2423

2524
public class ResultObjectProxy {
2625

@@ -31,23 +30,50 @@ public class ResultObjectProxy {
3130
private static final String FINALIZE_METHOD = "finalize";
3231
private static final String WRITE_REPLACE_METHOD = "writeReplace";
3332

34-
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, boolean aggressive,
35-
ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
36-
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, aggressive, objectFactory,
37-
constructorArgTypes, constructorArgs);
33+
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, boolean aggressive, ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
34+
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, aggressive, objectFactory, constructorArgTypes, constructorArgs);
3835
}
3936

40-
private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {
37+
public static Object createDeserializationProxy(Object target, Set<String> unloadedProperties, ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
38+
return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
39+
}
40+
41+
private static Object crateProxy(Class type, Callback callback, List<Class> constructorArgTypes, List<Object> constructorArgs) {
42+
43+
Enhancer enhancer = new Enhancer();
44+
enhancer.setCallback(callback);
45+
enhancer.setSuperclass(type);
46+
47+
try {
48+
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
49+
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
50+
log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
51+
} catch (NoSuchMethodException e) {
52+
enhancer.setInterfaces(new Class[] { WriteReplaceInterface.class });
53+
} catch (SecurityException e) {
54+
// nothing to do here
55+
}
4156

57+
Object enhanced = null;
58+
if (constructorArgTypes.isEmpty()) {
59+
enhanced = enhancer.create();
60+
} else {
61+
Class[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
62+
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
63+
enhanced = enhancer.create(typesArray, valuesArray);
64+
}
65+
return enhanced;
66+
}
67+
68+
private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {
4269
private Class type;
4370
private ResultLoaderMap lazyLoader;
4471
private boolean aggressive;
4572
private ObjectFactory objectFactory;
4673
private List<Class> constructorArgTypes;
4774
private List<Object> constructorArgs;
4875

49-
private EnhancedResultObjectProxyImpl(Class type, ResultLoaderMap lazyLoader, boolean aggressive,
50-
ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
76+
private EnhancedResultObjectProxyImpl(Class type, ResultLoaderMap lazyLoader, boolean aggressive, ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
5177
this.type = type;
5278
this.lazyLoader = lazyLoader;
5379
this.aggressive = aggressive;
@@ -56,39 +82,13 @@ private EnhancedResultObjectProxyImpl(Class type, ResultLoaderMap lazyLoader, bo
5682
this.constructorArgs = constructorArgs;
5783
}
5884

59-
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, boolean aggressive,
60-
ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
85+
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, boolean aggressive, ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
6186
final Class type = target.getClass();
6287
if (registry.hasTypeHandler(type)) {
6388
return target;
64-
} else {
65-
EnhancedResultObjectProxyImpl proxy = new EnhancedResultObjectProxyImpl(type, lazyLoader, aggressive,
66-
objectFactory, constructorArgTypes, constructorArgs);
67-
Enhancer enhancer = new Enhancer();
68-
enhancer.setCallback(proxy);
69-
enhancer.setSuperclass(type);
70-
71-
try {
72-
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
73-
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
74-
log.warn(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
75-
} catch (NoSuchMethodException e) {
76-
InterfaceMaker writeReplaceInterface = new InterfaceMaker();
77-
Signature signature = new Signature(WRITE_REPLACE_METHOD, Type.getType(Object.class), new Type[] {});
78-
writeReplaceInterface.add(signature, new Type[] { Type.getType(ObjectStreamException.class) });
79-
enhancer.setInterfaces(new Class[] { writeReplaceInterface.create() });
80-
} catch (SecurityException e) {
81-
// nothing to do here
82-
}
83-
84-
Object enhanced = null;
85-
if (constructorArgTypes.isEmpty()) {
86-
enhanced = enhancer.create();
87-
} else {
88-
Class[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
89-
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
90-
enhanced = enhancer.create(typesArray, valuesArray);
91-
}
89+
} else {
90+
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, aggressive, objectFactory, constructorArgTypes, constructorArgs);
91+
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
9292
PropertyCopier.copyBeanProperties(type, target, enhanced);
9393
return enhanced;
9494
}
@@ -107,8 +107,7 @@ public Object intercept(Object enhanced, Method method, Object[] args, MethodPro
107107
}
108108
PropertyCopier.copyBeanProperties(type, enhanced, original);
109109
if (lazyLoader.size() > 0) {
110-
return new SerialStateHolder(original, lazyLoader.getPropertyNames(), objectFactory,
111-
constructorArgTypes, constructorArgs);
110+
return new SerialStateHolder(original, lazyLoader.getPropertyNames(), objectFactory, constructorArgTypes, constructorArgs);
112111
} else {
113112
return original;
114113
}
@@ -132,4 +131,56 @@ public Object intercept(Object enhanced, Method method, Object[] args, MethodPro
132131
}
133132
}
134133

134+
private static class EnhancedDeserializationProxyImpl implements MethodInterceptor {
135+
private Class type;
136+
private Set<String> unloadedProperties;
137+
private ObjectFactory objectFactory;
138+
private List<Class> constructorArgTypes;
139+
private List<Object> constructorArgs;
140+
141+
private EnhancedDeserializationProxyImpl(Class type, Set<String> unloadedProperties, ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
142+
this.type = type;
143+
this.unloadedProperties = unloadedProperties;
144+
this.objectFactory = objectFactory;
145+
this.constructorArgTypes = constructorArgTypes;
146+
this.constructorArgs = constructorArgs;
147+
}
148+
149+
public static Object createProxy(Object target, Set<String> unloadedProperties, ObjectFactory objectFactory, List<Class> constructorArgTypes, List<Object> constructorArgs) {
150+
final Class type = target.getClass();
151+
EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
152+
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
153+
PropertyCopier.copyBeanProperties(type, target, enhanced);
154+
return enhanced;
155+
}
156+
157+
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
158+
final String methodName = method.getName();
159+
try {
160+
if (WRITE_REPLACE_METHOD.equals(methodName)) {
161+
Object original = null;
162+
if (constructorArgTypes.isEmpty()) {
163+
original = objectFactory.create(type);
164+
} else {
165+
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
166+
}
167+
PropertyCopier.copyBeanProperties(type, enhanced, original);
168+
return new SerialStateHolder(original, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
169+
} else {
170+
if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName)) {
171+
final String property = PropertyNamer.methodToProperty(methodName);
172+
if (unloadedProperties.contains(property.toUpperCase(Locale.ENGLISH))) {
173+
throw new ExecutorException("An attempt has been made to read a not loaded lazy property '"
174+
+ property
175+
+ "' of a disconnected object");
176+
}
177+
}
178+
return methodProxy.invokeSuper(enhanced, args);
179+
}
180+
} catch (Throwable t) {
181+
throw ExceptionUtil.unwrapThrowable(t);
182+
}
183+
}
184+
}
185+
135186
}

src/main/java/org/apache/ibatis/executor/loader/SerialStateHolder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ protected Object readResolve() throws ObjectStreamException {
3131
Set<String> arrayProps = new HashSet<String>(Arrays.asList(this.unloadedProperties));
3232
List<Class> arrayTypes = Arrays.asList(this.constructorArgTypes);
3333
List<Object> arrayValues = Arrays.asList(this.constructorArgs);
34-
return DeserializedObjectProxy.createProxy(userBean, arrayProps, objectFactory, arrayTypes, arrayValues);
34+
return ResultObjectProxy.createDeserializationProxy(userBean, arrayProps, objectFactory, arrayTypes, arrayValues);
3535
}
3636

3737
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.apache.ibatis.executor.loader;
2+
3+
import java.io.ObjectStreamException;
4+
5+
public interface WriteReplaceInterface {
6+
7+
Object writeReplace() throws ObjectStreamException;
8+
9+
}

src/test/java/org/apache/ibatis/executor/SerializableProxyTest.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import java.util.ArrayList;
1616
import java.util.HashSet;
1717

18-
import org.apache.ibatis.executor.loader.DeserializedObjectProxy;
1918
import org.apache.ibatis.executor.loader.ResultLoaderMap;
2019
import org.apache.ibatis.executor.loader.ResultObjectProxy;
20+
import org.apache.ibatis.executor.loader.WriteReplaceInterface;
2121
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
2222
import org.junit.Test;
2323

@@ -93,19 +93,19 @@ public void shouldFailCallingAnUnloadedProperty() throws Exception {
9393
// yes, it must go in uppercase
9494
HashSet<String> unloadedProperties = new HashSet<String>();
9595
unloadedProperties.add("ID");
96-
Author author2 = (Author) DeserializedObjectProxy.createProxy(author, unloadedProperties, new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
96+
Author author2 = (Author) ResultObjectProxy.createDeserializationProxy(author, unloadedProperties, new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
9797
author2.getId();
9898
}
9999

100100
@Test
101101
public void shouldLetCallALoadedProperty() throws Exception {
102-
Author author2 = (Author) DeserializedObjectProxy.createProxy(author, new HashSet<String>(), new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
102+
Author author2 = (Author) ResultObjectProxy.createDeserializationProxy(author, new HashSet<String>(), new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
103103
assertEquals(999, author2.getId());
104104
}
105105

106106
@Test
107107
public void shouldSerizalizeADeserlizaliedProxy() throws Exception {
108-
Object proxy = DeserializedObjectProxy.createProxy(author, new HashSet<String>(), new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
108+
Object proxy = ResultObjectProxy.createDeserializationProxy(author, new HashSet<String>(), new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
109109
Author author2 = (Author) deserialize(serialize((Serializable) proxy));
110110
assertEquals(author, author2);
111111
assertFalse(author.getClass().equals(author2.getClass()));
@@ -120,20 +120,27 @@ public void shouldGenerateWriteReplace() throws Exception {
120120
// ok
121121
}
122122
Object proxy = ResultObjectProxy.createProxy(author, new ResultLoaderMap(), true, new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
123-
proxy.getClass().getDeclaredMethod("writeReplace");
123+
Method m = proxy.getClass().getDeclaredMethod("writeReplace");
124124
}
125125

126126
@Test
127127
public void shouldNotGenerateWriteReplaceItThereIsAlreadyOne() throws Exception {
128128
AuthorWithWriteReplaceMethod beanWithWriteReplace = new AuthorWithWriteReplaceMethod(999, "someone", "!@#@!#!@#", "[email protected]", "blah", Section.NEWS);
129-
Object proxy = ResultObjectProxy.createProxy(author, new ResultLoaderMap(), true, new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
130129
try {
131130
beanWithWriteReplace.getClass().getDeclaredMethod("writeReplace");
132131
} catch (NoSuchMethodException e) {
133132
fail("Bean should declare a writeReplace method");
134133
}
135-
Method m = proxy.getClass().getDeclaredMethod("writeReplace");
136-
assertFalse(m.isAccessible()); //generated method is public so this one should be protected
134+
Object proxy = ResultObjectProxy.createProxy(beanWithWriteReplace, new ResultLoaderMap(), true, new DefaultObjectFactory(), new ArrayList<Class>(), new ArrayList<Object>());
135+
Class[] interfaces = proxy.getClass().getInterfaces();
136+
boolean ownInterfaceFound = false;
137+
for (Class i : interfaces) {
138+
if (i.equals(WriteReplaceInterface.class)) {
139+
ownInterfaceFound = true;
140+
break;
141+
}
142+
}
143+
assertFalse(ownInterfaceFound);
137144
}
138145

139146
@Test

0 commit comments

Comments
 (0)