Skip to content

Commit db4ab6c

Browse files
committed
Fix to http://code.google.com/p/mybatis/issues/detail?id=200 . Implement CGLIB partially loaded objects deserialization. Thanks again to Arkadi Shishlov.
1 parent c10820c commit db4ab6c

File tree

8 files changed

+426
-291
lines changed

8 files changed

+426
-291
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.apache.ibatis.executor.loader;
2+
3+
import java.io.ObjectStreamException;
4+
import java.lang.reflect.Method;
5+
import java.util.HashSet;
6+
import java.util.Locale;
7+
import java.util.Set;
8+
9+
import net.sf.cglib.core.Signature;
10+
import net.sf.cglib.proxy.Enhancer;
11+
import net.sf.cglib.proxy.InterfaceMaker;
12+
import net.sf.cglib.proxy.MethodInterceptor;
13+
import net.sf.cglib.proxy.MethodProxy;
14+
15+
import org.apache.ibatis.executor.ExecutorException;
16+
import org.apache.ibatis.logging.Log;
17+
import org.apache.ibatis.logging.LogFactory;
18+
import org.apache.ibatis.reflection.ExceptionUtil;
19+
import org.apache.ibatis.reflection.factory.ObjectFactory;
20+
import org.apache.ibatis.reflection.property.PropertyCopier;
21+
import org.apache.ibatis.reflection.property.PropertyNamer;
22+
import org.objectweb.asm.Type;
23+
24+
public class DeserializedObjectProxy {
25+
26+
private static final Log log = LogFactory.getLog(DeserializedObjectProxy.class);
27+
28+
private static final String FINALIZE_METHOD = "finalize";
29+
private static final String WRITE_REPLACE_METHOD = "writeReplace";
30+
31+
public static Object createProxy(Object target, String[] unloadedProperties, ObjectFactory objectFactory) {
32+
return DeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory);
33+
}
34+
35+
private static class DeserializationProxyImpl implements MethodInterceptor {
36+
37+
private Class type;
38+
private Set<String> unloadedProperties;
39+
private ObjectFactory objectFactory;
40+
41+
private DeserializationProxyImpl(Class type, String[] unloadedProperties, ObjectFactory objectFactory) {
42+
this.type = type;
43+
this.unloadedProperties = new HashSet<String>();
44+
for (String s : unloadedProperties) {
45+
this.unloadedProperties.add(s);
46+
}
47+
this.objectFactory = objectFactory;
48+
}
49+
50+
public static Object createProxy(Object target, String[] unloadedProperties, ObjectFactory objectFactory) {
51+
final Class type = target.getClass();
52+
DeserializationProxyImpl proxy = new DeserializationProxyImpl(type, unloadedProperties, objectFactory);
53+
Enhancer enhancer = new Enhancer();
54+
enhancer.setCallback(proxy);
55+
enhancer.setSuperclass(type);
56+
57+
try {
58+
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
59+
// don´t warn if a writeReplace is found, the bean was once serialized so it is ok
60+
} catch (NoSuchMethodException e) {
61+
InterfaceMaker writeReplaceInterface = new InterfaceMaker();
62+
Signature signature = new Signature(WRITE_REPLACE_METHOD, Type.getType(Object.class), new Type[] {});
63+
writeReplaceInterface.add(signature, new Type[] { Type.getType(ObjectStreamException.class) });
64+
enhancer.setInterfaces(new Class[] { writeReplaceInterface.create() });
65+
} catch (SecurityException e) {
66+
// nothing to do here
67+
}
68+
69+
final Object enhanced = enhancer.create();
70+
PropertyCopier.copyBeanProperties(type, target, enhanced);
71+
return enhanced;
72+
}
73+
74+
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
75+
final String methodName = method.getName();
76+
try {
77+
if (WRITE_REPLACE_METHOD.equals(methodName)) {
78+
Object original = objectFactory.create(type);
79+
PropertyCopier.copyBeanProperties(type, enhanced, original);
80+
return new SerialStatusHolder(original, unloadedProperties.toArray(new String[unloadedProperties.size()]), objectFactory);
81+
} else {
82+
if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName)) {
83+
final String property = PropertyNamer.methodToProperty(methodName);
84+
if (unloadedProperties.contains(property.toUpperCase(Locale.ENGLISH))) {
85+
throw new ExecutorException("An attempt has been made to read a not loaded lazy property '"
86+
+ property + "' of a disconnected object");
87+
}
88+
}
89+
return methodProxy.invokeSuper(enhanced, args);
90+
}
91+
} catch (Throwable t) {
92+
throw ExceptionUtil.unwrapThrowable(t);
93+
}
94+
}
95+
}
96+
97+
}
Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,69 @@
1-
package org.apache.ibatis.executor.loader;
2-
3-
import java.sql.SQLException;
4-
import java.util.HashMap;
5-
import java.util.Locale;
6-
import java.util.Map;
7-
import java.util.Set;
8-
9-
import org.apache.ibatis.reflection.MetaObject;
10-
11-
public class ResultLoaderMap {
12-
13-
private final Map<String, LoadPair> loaderMap = new HashMap<String, LoadPair>();
14-
15-
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
16-
String upperFirst = getUppercaseFirstProperty(property);
17-
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
18-
}
19-
20-
public int size() {
21-
return loaderMap.size();
22-
}
23-
24-
public boolean hasLoader(String methodName) {
25-
return loaderMap.containsKey(methodName.toUpperCase(Locale.ENGLISH));
26-
}
27-
28-
public boolean load(String property) throws SQLException {
29-
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
30-
if (pair != null) {
31-
pair.load();
32-
return true;
33-
}
34-
return false;
35-
}
36-
37-
public void loadAll() throws SQLException {
38-
final Set<String> methodNameSet = loaderMap.keySet();
39-
String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
40-
for (String methodName : methodNames) {
41-
load(methodName);
42-
}
43-
}
44-
45-
private static String getUppercaseFirstProperty(String property) {
46-
String[] parts = property.split("\\.");
47-
return parts[0].toUpperCase(Locale.ENGLISH);
48-
}
49-
50-
private static class LoadPair {
51-
private String property;
52-
private MetaObject metaResultObject;
53-
private ResultLoader resultLoader;
54-
55-
private LoadPair(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
56-
this.property = property;
57-
this.metaResultObject = metaResultObject;
58-
this.resultLoader = resultLoader;
59-
}
60-
61-
public void load() throws SQLException {
62-
metaResultObject.setValue(property, resultLoader.loadResult());
63-
}
64-
}
65-
}
1+
package org.apache.ibatis.executor.loader;
2+
3+
import java.sql.SQLException;
4+
import java.util.HashMap;
5+
import java.util.Locale;
6+
import java.util.Map;
7+
import java.util.Set;
8+
9+
import org.apache.ibatis.reflection.MetaObject;
10+
11+
public class ResultLoaderMap {
12+
13+
private final Map<String, LoadPair> loaderMap = new HashMap<String, LoadPair>();
14+
15+
public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
16+
String upperFirst = getUppercaseFirstProperty(property);
17+
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
18+
}
19+
20+
public Set<String> getPropertyNames() {
21+
return loaderMap.keySet();
22+
}
23+
24+
public int size() {
25+
return loaderMap.size();
26+
}
27+
28+
public boolean hasLoader(String property) {
29+
return loaderMap.containsKey(property.toUpperCase(Locale.ENGLISH));
30+
}
31+
32+
public boolean load(String property) throws SQLException {
33+
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
34+
if (pair != null) {
35+
pair.load();
36+
return true;
37+
}
38+
return false;
39+
}
40+
41+
public void loadAll() throws SQLException {
42+
final Set<String> methodNameSet = loaderMap.keySet();
43+
String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
44+
for (String methodName : methodNames) {
45+
load(methodName);
46+
}
47+
}
48+
49+
private static String getUppercaseFirstProperty(String property) {
50+
String[] parts = property.split("\\.");
51+
return parts[0].toUpperCase(Locale.ENGLISH);
52+
}
53+
54+
private static class LoadPair {
55+
private String property;
56+
private MetaObject metaResultObject;
57+
private ResultLoader resultLoader;
58+
59+
private LoadPair(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
60+
this.property = property;
61+
this.metaResultObject = metaResultObject;
62+
this.resultLoader = resultLoader;
63+
}
64+
65+
public void load() throws SQLException {
66+
metaResultObject.setValue(property, resultLoader.loadResult());
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)