AbstractEnhancedDeserializationProxy.java

  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. import java.lang.reflect.Method;
  18. import java.util.List;
  19. import java.util.Locale;
  20. import java.util.Map;
  21. import java.util.concurrent.locks.ReentrantLock;

  22. import org.apache.ibatis.executor.ExecutorException;
  23. import org.apache.ibatis.reflection.ExceptionUtil;
  24. import org.apache.ibatis.reflection.factory.ObjectFactory;
  25. import org.apache.ibatis.reflection.property.PropertyCopier;
  26. import org.apache.ibatis.reflection.property.PropertyNamer;

  27. /**
  28.  * @author Clinton Begin
  29.  */
  30. public abstract class AbstractEnhancedDeserializationProxy {

  31.   protected static final String FINALIZE_METHOD = "finalize";
  32.   protected static final String WRITE_REPLACE_METHOD = "writeReplace";
  33.   private final Class<?> type;
  34.   private final Map<String, ResultLoaderMap.LoadPair> unloadedProperties;
  35.   private final ObjectFactory objectFactory;
  36.   private final List<Class<?>> constructorArgTypes;
  37.   private final List<Object> constructorArgs;
  38.   private final ReentrantLock lock = new ReentrantLock();
  39.   private boolean reloadingProperty;

  40.   protected AbstractEnhancedDeserializationProxy(Class<?> type,
  41.       Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
  42.       List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  43.     this.type = type;
  44.     this.unloadedProperties = unloadedProperties;
  45.     this.objectFactory = objectFactory;
  46.     this.constructorArgTypes = constructorArgTypes;
  47.     this.constructorArgs = constructorArgs;
  48.     this.reloadingProperty = false;
  49.   }

  50.   public final Object invoke(Object enhanced, Method method, Object[] args) throws Throwable {
  51.     final String methodName = method.getName();
  52.     try {
  53.       if (WRITE_REPLACE_METHOD.equals(methodName)) {
  54.         final Object original;
  55.         if (constructorArgTypes.isEmpty()) {
  56.           original = objectFactory.create(type);
  57.         } else {
  58.           original = objectFactory.create(type, constructorArgTypes, constructorArgs);
  59.         }

  60.         PropertyCopier.copyBeanProperties(type, enhanced, original);
  61.         return this.newSerialStateHolder(original, unloadedProperties, objectFactory, constructorArgTypes,
  62.             constructorArgs);
  63.       }
  64.       lock.lock();
  65.       try {
  66.         if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName) && !reloadingProperty) {
  67.           final String property = PropertyNamer.methodToProperty(methodName);
  68.           final String propertyKey = property.toUpperCase(Locale.ENGLISH);
  69.           if (unloadedProperties.containsKey(propertyKey)) {
  70.             final ResultLoaderMap.LoadPair loadPair = unloadedProperties.remove(propertyKey);
  71.             if (loadPair != null) {
  72.               try {
  73.                 reloadingProperty = true;
  74.                 loadPair.load(enhanced);
  75.               } finally {
  76.                 reloadingProperty = false;
  77.               }
  78.             } else {
  79.               /*
  80.                * I'm not sure if this case can really happen or is just in tests - we have an unread property but no
  81.                * loadPair to load it.
  82.                */
  83.               throw new ExecutorException("An attempt has been made to read a not loaded lazy property '" + property
  84.                   + "' of a disconnected object");
  85.             }
  86.           }
  87.         }

  88.         return enhanced;
  89.       } finally {
  90.         lock.unlock();
  91.       }
  92.     } catch (Throwable t) {
  93.       throw ExceptionUtil.unwrapThrowable(t);
  94.     }
  95.   }

  96.   protected abstract AbstractSerialStateHolder newSerialStateHolder(Object userBean,
  97.       Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
  98.       List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

  99. }