1 package com.fasterxml.jackson.databind.ser; 2 3 import java.util.*; 4 import java.util.concurrent.atomic.AtomicReference; 5 6 import com.fasterxml.jackson.databind.*; 7 import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap; 8 import com.fasterxml.jackson.databind.util.TypeKey; 9 10 /** 11 * Simple cache object that allows for doing 2-level lookups: first level is 12 * by "local" read-only lookup Map (used without locking) 13 * and second backup level is by a shared modifiable HashMap. 14 * The idea is that after a while, most serializers are found from the 15 * local Map (to optimize performance, reduce lock contention), 16 * but that during buildup we can use a shared map to reduce both 17 * number of distinct read-only maps constructed, and number of 18 * serializers constructed. 19 *<p> 20 * Cache contains three kinds of entries, 21 * based on combination of class pair key. First class in key is for the 22 * type to serialize, and second one is type used for determining how 23 * to resolve value type. One (but not both) of entries can be null. 24 */ 25 public final class SerializerCache 26 { 27 /** 28 * Shared, modifiable map; all access needs to be through synchronized blocks. 29 *<p> 30 * NOTE: keys are of various types (see below for key types), in addition to 31 * basic {@link JavaType} used for "untyped" serializers. 32 */ 33 private final HashMap<TypeKey, JsonSerializer<Object>> _sharedMap 34 = new HashMap<TypeKey, JsonSerializer<Object>>(64); 35 36 /** 37 * Most recent read-only instance, created from _sharedMap, if any. 38 */ 39 private final AtomicReference<ReadOnlyClassToSerializerMap> _readOnlyMap 40 = new AtomicReference<ReadOnlyClassToSerializerMap>(); 41 SerializerCache()42 public SerializerCache() { } 43 44 /** 45 * Method that can be called to get a read-only instance populated from the 46 * most recent version of the shared lookup Map. 47 */ getReadOnlyLookupMap()48 public ReadOnlyClassToSerializerMap getReadOnlyLookupMap() 49 { 50 ReadOnlyClassToSerializerMap m = _readOnlyMap.get(); 51 if (m != null) { 52 return m; 53 } 54 return _makeReadOnlyLookupMap(); 55 } 56 _makeReadOnlyLookupMap()57 private final synchronized ReadOnlyClassToSerializerMap _makeReadOnlyLookupMap() { 58 // double-locking; safe, but is it really needed? Not doing that is only a perf problem, 59 // not correctness 60 ReadOnlyClassToSerializerMap m = _readOnlyMap.get(); 61 if (m == null) { 62 m = ReadOnlyClassToSerializerMap.from(_sharedMap); 63 _readOnlyMap.set(m); 64 } 65 return m; 66 } 67 68 /* 69 /********************************************************** 70 /* Lookup methods for accessing shared (slow) cache 71 /********************************************************** 72 */ 73 size()74 public synchronized int size() { 75 return _sharedMap.size(); 76 } 77 78 /** 79 * Method that checks if the shared (and hence, synchronized) lookup Map might have 80 * untyped serializer for given type. 81 */ untypedValueSerializer(Class<?> type)82 public JsonSerializer<Object> untypedValueSerializer(Class<?> type) 83 { 84 synchronized (this) { 85 return _sharedMap.get(new TypeKey(type, false)); 86 } 87 } 88 untypedValueSerializer(JavaType type)89 public JsonSerializer<Object> untypedValueSerializer(JavaType type) 90 { 91 synchronized (this) { 92 return _sharedMap.get(new TypeKey(type, false)); 93 } 94 } 95 typedValueSerializer(JavaType type)96 public JsonSerializer<Object> typedValueSerializer(JavaType type) 97 { 98 synchronized (this) { 99 return _sharedMap.get(new TypeKey(type, true)); 100 } 101 } 102 typedValueSerializer(Class<?> cls)103 public JsonSerializer<Object> typedValueSerializer(Class<?> cls) 104 { 105 synchronized (this) { 106 return _sharedMap.get(new TypeKey(cls, true)); 107 } 108 } 109 110 /* 111 /********************************************************** 112 /* Methods for adding shared serializer instances 113 /********************************************************** 114 */ 115 116 /** 117 * Method called if none of lookups succeeded, and caller had to construct 118 * a serializer. If so, we will update the shared lookup map so that it 119 * can be resolved via it next time. 120 */ addTypedSerializer(JavaType type, JsonSerializer<Object> ser)121 public void addTypedSerializer(JavaType type, JsonSerializer<Object> ser) 122 { 123 synchronized (this) { 124 if (_sharedMap.put(new TypeKey(type, true), ser) == null) { 125 // let's invalidate the read-only copy, too, to get it updated 126 _readOnlyMap.set(null); 127 } 128 } 129 } 130 addTypedSerializer(Class<?> cls, JsonSerializer<Object> ser)131 public void addTypedSerializer(Class<?> cls, JsonSerializer<Object> ser) 132 { 133 synchronized (this) { 134 if (_sharedMap.put(new TypeKey(cls, true), ser) == null) { 135 // let's invalidate the read-only copy, too, to get it updated 136 _readOnlyMap.set(null); 137 } 138 } 139 } 140 addAndResolveNonTypedSerializer(Class<?> type, JsonSerializer<Object> ser, SerializerProvider provider)141 public void addAndResolveNonTypedSerializer(Class<?> type, JsonSerializer<Object> ser, 142 SerializerProvider provider) 143 throws JsonMappingException 144 { 145 synchronized (this) { 146 if (_sharedMap.put(new TypeKey(type, false), ser) == null) { 147 _readOnlyMap.set(null); 148 } 149 // Need resolution to handle cyclic POJO type dependencies 150 /* 14-May-2011, tatu: Resolving needs to be done in synchronized manner; 151 * this because while we do need to register instance first, we also must 152 * keep lock until resolution is complete. 153 */ 154 if (ser instanceof ResolvableSerializer) { 155 ((ResolvableSerializer) ser).resolve(provider); 156 } 157 } 158 } 159 addAndResolveNonTypedSerializer(JavaType type, JsonSerializer<Object> ser, SerializerProvider provider)160 public void addAndResolveNonTypedSerializer(JavaType type, JsonSerializer<Object> ser, 161 SerializerProvider provider) 162 throws JsonMappingException 163 { 164 synchronized (this) { 165 if (_sharedMap.put(new TypeKey(type, false), ser) == null) { 166 _readOnlyMap.set(null); 167 } 168 // Need resolution to handle cyclic POJO type dependencies 169 /* 14-May-2011, tatu: Resolving needs to be done in synchronized manner; 170 * this because while we do need to register instance first, we also must 171 * keep lock until resolution is complete. 172 */ 173 if (ser instanceof ResolvableSerializer) { 174 ((ResolvableSerializer) ser).resolve(provider); 175 } 176 } 177 } 178 179 /** 180 * Another alternative that will cover both access via raw type and matching 181 * fully resolved type, in one fell swoop. 182 * 183 * @since 2.7 184 */ addAndResolveNonTypedSerializer(Class<?> rawType, JavaType fullType, JsonSerializer<Object> ser, SerializerProvider provider)185 public void addAndResolveNonTypedSerializer(Class<?> rawType, JavaType fullType, 186 JsonSerializer<Object> ser, 187 SerializerProvider provider) 188 throws JsonMappingException 189 { 190 synchronized (this) { 191 Object ob1 = _sharedMap.put(new TypeKey(rawType, false), ser); 192 Object ob2 = _sharedMap.put(new TypeKey(fullType, false), ser); 193 if ((ob1 == null) || (ob2 == null)) { 194 _readOnlyMap.set(null); 195 } 196 if (ser instanceof ResolvableSerializer) { 197 ((ResolvableSerializer) ser).resolve(provider); 198 } 199 } 200 } 201 202 /** 203 * Method called by StdSerializerProvider#flushCachedSerializers() to 204 * clear all cached serializers 205 */ flush()206 public synchronized void flush() { 207 _sharedMap.clear(); 208 } 209 } 210