View Javadoc
1   /*
2    *    Copyright 2009-2024 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    *       https://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  import java.util.concurrent.locks.ReentrantLock;
23  
24  import org.apache.ibatis.executor.ExecutorException;
25  import org.apache.ibatis.reflection.ExceptionUtil;
26  import org.apache.ibatis.reflection.factory.ObjectFactory;
27  import org.apache.ibatis.reflection.property.PropertyCopier;
28  import org.apache.ibatis.reflection.property.PropertyNamer;
29  
30  /**
31   * @author Clinton Begin
32   */
33  public abstract class AbstractEnhancedDeserializationProxy {
34  
35    protected static final String FINALIZE_METHOD = "finalize";
36    protected static final String WRITE_REPLACE_METHOD = "writeReplace";
37    private final Class<?> type;
38    private final Map<String, ResultLoaderMap.LoadPair> unloadedProperties;
39    private final ObjectFactory objectFactory;
40    private final List<Class<?>> constructorArgTypes;
41    private final List<Object> constructorArgs;
42    private final ReentrantLock lock = new ReentrantLock();
43    private boolean reloadingProperty;
44  
45    protected AbstractEnhancedDeserializationProxy(Class<?> type,
46        Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
47        List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
48      this.type = type;
49      this.unloadedProperties = unloadedProperties;
50      this.objectFactory = objectFactory;
51      this.constructorArgTypes = constructorArgTypes;
52      this.constructorArgs = constructorArgs;
53      this.reloadingProperty = false;
54    }
55  
56    public final Object invoke(Object enhanced, Method method, Object[] args) throws Throwable {
57      final String methodName = method.getName();
58      try {
59        if (WRITE_REPLACE_METHOD.equals(methodName)) {
60          final Object original;
61          if (constructorArgTypes.isEmpty()) {
62            original = objectFactory.create(type);
63          } else {
64            original = objectFactory.create(type, constructorArgTypes, constructorArgs);
65          }
66  
67          PropertyCopier.copyBeanProperties(type, enhanced, original);
68          return this.newSerialStateHolder(original, unloadedProperties, objectFactory, constructorArgTypes,
69              constructorArgs);
70        }
71        lock.lock();
72        try {
73          if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName) && !reloadingProperty) {
74            final String property = PropertyNamer.methodToProperty(methodName);
75            final String propertyKey = property.toUpperCase(Locale.ENGLISH);
76            if (unloadedProperties.containsKey(propertyKey)) {
77              final ResultLoaderMap.LoadPair loadPair = unloadedProperties.remove(propertyKey);
78              if (loadPair != null) {
79                try {
80                  reloadingProperty = true;
81                  loadPair.load(enhanced);
82                } finally {
83                  reloadingProperty = false;
84                }
85              } else {
86                /*
87                 * I'm not sure if this case can really happen or is just in tests - we have an unread property but no
88                 * loadPair to load it.
89                 */
90                throw new ExecutorException("An attempt has been made to read a not loaded lazy property '" + property
91                    + "' of a disconnected object");
92              }
93            }
94          }
95  
96          return enhanced;
97        } finally {
98          lock.unlock();
99        }
100     } catch (Throwable t) {
101       throw ExceptionUtil.unwrapThrowable(t);
102     }
103   }
104 
105   protected abstract AbstractSerialStateHolder newSerialStateHolder(Object userBean,
106       Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
107       List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
108 
109 }