AbstractSerialStateHolder.java
/*
* Copyright 2009-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.loader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.StreamCorruptedException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.SerialFilterChecker;
import org.apache.ibatis.reflection.factory.ObjectFactory;
/**
* @author Eduardo Macarron
* @author Franta Mejta
*/
public abstract class AbstractSerialStateHolder implements Externalizable {
private static final long serialVersionUID = 8940388717901644661L;
private static final ThreadLocal<ObjectOutputStream> stream = new ThreadLocal<>();
private byte[] userBeanBytes = {};
private Object userBean;
private Map<String, ResultLoaderMap.LoadPair> unloadedProperties;
private ObjectFactory objectFactory;
private Class<?>[] constructorArgTypes;
private Object[] constructorArgs;
public AbstractSerialStateHolder() {
}
public AbstractSerialStateHolder(final Object userBean,
final Map<String, ResultLoaderMap.LoadPair> unloadedProperties, final ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
this.userBean = userBean;
this.unloadedProperties = new HashMap<>(unloadedProperties);
this.objectFactory = objectFactory;
this.constructorArgTypes = constructorArgTypes.toArray(new Class<?>[0]);
this.constructorArgs = constructorArgs.toArray(new Object[0]);
}
@Override
public final void writeExternal(final ObjectOutput out) throws IOException {
boolean firstRound = false;
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream os = stream.get();
if (os == null) {
os = new ObjectOutputStream(baos);
firstRound = true;
stream.set(os);
}
os.writeObject(this.userBean);
os.writeObject(this.unloadedProperties);
os.writeObject(this.objectFactory);
os.writeObject(this.constructorArgTypes);
os.writeObject(this.constructorArgs);
final byte[] bytes = baos.toByteArray();
out.writeObject(bytes);
if (firstRound) {
stream.remove();
}
}
@Override
public final void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
final Object data = in.readObject();
if (data.getClass().isArray()) {
this.userBeanBytes = (byte[]) data;
} else {
this.userBean = data;
}
}
@SuppressWarnings("unchecked")
protected final Object readResolve() throws ObjectStreamException {
/* Second run */
if (this.userBean != null && this.userBeanBytes.length == 0) {
return this.userBean;
}
SerialFilterChecker.check();
/* First run */
try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(this.userBeanBytes))) {
this.userBean = in.readObject();
this.unloadedProperties = (Map<String, ResultLoaderMap.LoadPair>) in.readObject();
this.objectFactory = (ObjectFactory) in.readObject();
this.constructorArgTypes = (Class<?>[]) in.readObject();
this.constructorArgs = (Object[]) in.readObject();
} catch (final IOException ex) {
throw (ObjectStreamException) new StreamCorruptedException().initCause(ex);
} catch (final ClassNotFoundException ex) {
throw (ObjectStreamException) new InvalidClassException(ex.getLocalizedMessage()).initCause(ex);
}
final Map<String, ResultLoaderMap.LoadPair> arrayProps = new HashMap<>(this.unloadedProperties);
final List<Class<?>> arrayTypes = Arrays.asList(this.constructorArgTypes);
final List<Object> arrayValues = Arrays.asList(this.constructorArgs);
return this.createDeserializationProxy(userBean, arrayProps, objectFactory, arrayTypes, arrayValues);
}
protected abstract Object createDeserializationProxy(Object target,
Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
}