View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.executor.loader;
17  
18  import java.lang.reflect.Method;
19  import java.util.List;
20  import java.util.Locale;
21  import java.util.Map;
22  
23  import org.apache.ibatis.executor.ExecutorException;
24  import org.apache.ibatis.reflection.ExceptionUtil;
25  import org.apache.ibatis.reflection.factory.ObjectFactory;
26  import org.apache.ibatis.reflection.property.PropertyCopier;
27  import org.apache.ibatis.reflection.property.PropertyNamer;
28  
29  /**
30   * @author Clinton Begin
31   */
32  public abstract class AbstractEnhancedDeserializationProxy {
33  
34    protected static final String FINALIZE_METHOD = "finalize";
35    protected static final String WRITE_REPLACE_METHOD = "writeReplace";
36    private final Class<?> type;
37    private final Map<String, ResultLoaderMap.LoadPair> unloadedProperties;
38    private final ObjectFactory objectFactory;
39    private final List<Class<?>> constructorArgTypes;
40    private final List<Object> constructorArgs;
41    private final Object reloadingPropertyLock;
42    private boolean reloadingProperty;
43  
44    protected AbstractEnhancedDeserializationProxy(Class<?> type, Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
45            ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
46      this.type = type;
47      this.unloadedProperties = unloadedProperties;
48      this.objectFactory = objectFactory;
49      this.constructorArgTypes = constructorArgTypes;
50      this.constructorArgs = constructorArgs;
51      this.reloadingPropertyLock = new Object();
52      this.reloadingProperty = false;
53    }
54  
55    public final Object invoke(Object enhanced, Method method, Object[] args) throws Throwable {
56      final String methodName = method.getName();
57      try {
58        if (WRITE_REPLACE_METHOD.equals(methodName)) {
59          final Object original;
60          if (constructorArgTypes.isEmpty()) {
61            original = objectFactory.create(type);
62          } else {
63            original = objectFactory.create(type, constructorArgTypes, constructorArgs);
64          }
65  
66          PropertyCopier.copyBeanProperties(type, enhanced, original);
67          return this.newSerialStateHolder(original, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
68        } else {
69          synchronized (this.reloadingPropertyLock) {
70            if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName) && !reloadingProperty) {
71              final String property = PropertyNamer.methodToProperty(methodName);
72              final String propertyKey = property.toUpperCase(Locale.ENGLISH);
73              if (unloadedProperties.containsKey(propertyKey)) {
74                final ResultLoaderMap.LoadPair loadPair = unloadedProperties.remove(propertyKey);
75                if (loadPair != null) {
76                  try {
77                    reloadingProperty = true;
78                    loadPair.load(enhanced);
79                  } finally {
80                    reloadingProperty = false;
81                  }
82                } else {
83                  /* I'm not sure if this case can really happen or is just in tests -
84                   * we have an unread property but no loadPair to load it. */
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            }
90  
91            return enhanced;
92          }
93        }
94      } catch (Throwable t) {
95        throw ExceptionUtil.unwrapThrowable(t);
96      }
97    }
98  
99    protected abstract AbstractSerialStateHolder newSerialStateHolder(
100           Object userBean,
101           Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
102           ObjectFactory objectFactory,
103           List<Class<?>> constructorArgTypes,
104           List<Object> constructorArgs);
105 
106 }