[#7062] NotSerializableException when Record references reflection cache

This commit is contained in:
lukaseder 2018-01-16 10:43:05 +01:00
parent 0187539b05
commit 380cb706c8
3 changed files with 71 additions and 30 deletions

View File

@ -48,6 +48,7 @@ import java.io.StringWriter;
import java.sql.Connection;
import java.time.Clock;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@ -79,6 +80,7 @@ import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.exception.ConfigurationException;
import org.jooq.impl.ThreadLocalTransactionProvider.ThreadLocalConnectionProvider;
import org.jooq.impl.Tools.DataCacheKey;
/**
* A default implementation for configurations within a {@link DSLContext}, if no
@ -94,30 +96,34 @@ public class DefaultConfiguration implements Configuration {
/**
* Serial version UID
*/
private static final long serialVersionUID = 8193158984283234708L;
private static final long serialVersionUID = 4296981040491238190L;
// Configuration objects
private SQLDialect dialect;
private Settings settings;
private ConcurrentHashMap<Object, Object> data;
private SQLDialect dialect;
private Settings settings;
private Clock clock;
private Clock clock;
// Non-serializable Configuration objects
private transient ConnectionProvider connectionProvider;
private transient ExecutorProvider executorProvider;
private transient TransactionProvider transactionProvider;
private transient RecordMapperProvider recordMapperProvider;
private transient RecordUnmapperProvider recordUnmapperProvider;
private transient RecordListenerProvider[] recordListenerProviders;
private transient ExecuteListenerProvider[] executeListenerProviders;
private transient VisitListenerProvider[] visitListenerProviders;
private transient TransactionListenerProvider[] transactionListenerProviders;
private transient ConverterProvider converterProvider;
// These objects may be user defined and thus not necessarily serialisable
private transient ConnectionProvider connectionProvider;
private transient ExecutorProvider executorProvider;
private transient TransactionProvider transactionProvider;
private transient RecordMapperProvider recordMapperProvider;
private transient RecordUnmapperProvider recordUnmapperProvider;
private transient RecordListenerProvider[] recordListenerProviders;
private transient ExecuteListenerProvider[] executeListenerProviders;
private transient VisitListenerProvider[] visitListenerProviders;
private transient TransactionListenerProvider[] transactionListenerProviders;
private transient ConverterProvider converterProvider;
// [#7062] Apart from the possibility of containing user defined objects, the data
// map also contains the reflection cache, which isn't serializable (and
// should not be serialized anyway).
private transient ConcurrentHashMap<Object, Object> data;
// Derived objects
private org.jooq.SchemaMapping mapping;
private org.jooq.SchemaMapping mapping;
// -------------------------------------------------------------------------
// XXX: Constructors
@ -1297,8 +1303,22 @@ public class DefaultConfiguration implements Configuration {
oos.writeObject(converterProvider instanceof Serializable
? converterProvider
: null);
// [#7062] Exclude reflection cache from serialisation
for (Entry<Object, Object> entry : data.entrySet()) {
if (entry.getKey() instanceof DataCacheKey)
continue;
oos.writeObject(entry.getKey());
oos.writeObject(entry.getValue());
}
// [#7062] End of Map marker
oos.writeObject(END_OF_MAP_MARKER);
}
private static final String END_OF_MAP_MARKER = "EOM";
private <E> E[] cloneSerializables(E[] array) {
E[] clone = array.clone();
@ -1323,6 +1343,15 @@ public class DefaultConfiguration implements Configuration {
visitListenerProviders = (VisitListenerProvider[]) ois.readObject();
transactionListenerProviders = (TransactionListenerProvider[]) ois.readObject();
converterProvider = (ConverterProvider) ois.readObject();
data = new ConcurrentHashMap<Object, Object>();
Object key;
Object value;
while (!END_OF_MAP_MARKER.equals(key = ois.readObject())) {
value = ois.readObject();
data.put(key, value);
}
}
private final class ExecutorWrapper implements ExecutorProvider {

View File

@ -38,7 +38,7 @@
package org.jooq.impl;
import static java.lang.Boolean.TRUE;
import static org.jooq.impl.Tools.DATA_CACHE_RECORD_MAPPERS;
import static org.jooq.impl.Tools.DataCacheKey.DATA_CACHE_RECORD_MAPPERS;
import java.io.Serializable;

View File

@ -129,6 +129,13 @@ import static org.jooq.impl.Keywords.K_THEN;
import static org.jooq.impl.Keywords.K_THROW;
import static org.jooq.impl.Keywords.K_WHEN;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_ANNOTATED_GETTER;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_ANNOTATED_MEMBERS;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_ANNOTATED_SETTERS;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_MATCHING_GETTER;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_MATCHING_MEMBERS;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_GET_MATCHING_SETTERS;
import static org.jooq.impl.Tools.DataCacheKey.DATA_REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS;
import static org.jooq.impl.Tools.DataKey.DATA_BLOCK_NESTING;
import static org.jooq.tools.reflect.Reflect.accessible;
@ -469,14 +476,22 @@ final class Tools {
* <code>new String()</code> is used to allow for synchronizing on these
* objects.
*/
static final String DATA_REFLECTION_CACHE_GET_ANNOTATED_GETTER = new String("org.jooq.configuration.reflection-cache.get-annotated-getter");
static final String DATA_REFLECTION_CACHE_GET_ANNOTATED_MEMBERS = new String("org.jooq.configuration.reflection-cache.get-annotated-members");
static final String DATA_REFLECTION_CACHE_GET_ANNOTATED_SETTERS = new String("org.jooq.configuration.reflection-cache.get-annotated-setters");
static final String DATA_REFLECTION_CACHE_GET_MATCHING_GETTER = new String("org.jooq.configuration.reflection-cache.get-matching-getter");
static final String DATA_REFLECTION_CACHE_GET_MATCHING_MEMBERS = new String("org.jooq.configuration.reflection-cache.get-matching-members");
static final String DATA_REFLECTION_CACHE_GET_MATCHING_SETTERS = new String("org.jooq.configuration.reflection-cache.get-matching-setters");
static final String DATA_REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS = new String("org.jooq.configuration.reflection-cache.has-column-annotations");
static final String DATA_CACHE_RECORD_MAPPERS = new String("org.jooq.configuration.cache.record-mappers");
enum DataCacheKey {
DATA_REFLECTION_CACHE_GET_ANNOTATED_GETTER("org.jooq.configuration.reflection-cache.get-annotated-getter"),
DATA_REFLECTION_CACHE_GET_ANNOTATED_MEMBERS("org.jooq.configuration.reflection-cache.get-annotated-members"),
DATA_REFLECTION_CACHE_GET_ANNOTATED_SETTERS("org.jooq.configuration.reflection-cache.get-annotated-setters"),
DATA_REFLECTION_CACHE_GET_MATCHING_GETTER("org.jooq.configuration.reflection-cache.get-matching-getter"),
DATA_REFLECTION_CACHE_GET_MATCHING_MEMBERS("org.jooq.configuration.reflection-cache.get-matching-members"),
DATA_REFLECTION_CACHE_GET_MATCHING_SETTERS("org.jooq.configuration.reflection-cache.get-matching-setters"),
DATA_REFLECTION_CACHE_HAS_COLUMN_ANNOTATIONS("org.jooq.configuration.reflection-cache.has-column-annotations"),
DATA_CACHE_RECORD_MAPPERS("org.jooq.configuration.cache.record-mappers");
final String key;
private DataCacheKey(String key) {
this.key = key;
}
}
// ------------------------------------------------------------------------
// Other constants
@ -2684,7 +2699,7 @@ final class Tools {
* @return The cached value or the outcome of the cached operation.
*/
@SuppressWarnings("unchecked")
static final <V> V run(Configuration configuration, CachedOperation<V> operation, String type, Object key) {
static final <V> V run(Configuration configuration, CachedOperation<V> operation, DataCacheKey type, Object key) {
// If no configuration is provided take the default configuration that loads the default Settings
if (configuration == null)
@ -2696,8 +2711,6 @@ final class Tools {
Map<Object, Object> cache = (Map<Object, Object>) configuration.data(type);
if (cache == null) {
// String synchronization is OK as all type literals were created using new String()
synchronized (type) {
cache = (Map<Object, Object>) configuration.data(type);
@ -2709,7 +2722,6 @@ final class Tools {
}
Object result = cache.get(key);
if (result == null) {
synchronized (cache) {
result = cache.get(key);