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
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 */
48 public ReadOnlyClassToSerializerMap getReadOnlyLookupMap()
49 {
50 ReadOnlyClassToSerializerMap m = _readOnlyMap.get();
51 if (m != null) {
52 return m;
53 }
54 return _makeReadOnlyLookupMap();
55 }
56
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
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 */
82 public JsonSerializer<Object> untypedValueSerializer(Class<?> type)
83 {
84 synchronized (this) {
85 return _sharedMap.get(new TypeKey(type, false));
86 }
87 }
88
89 public JsonSerializer<Object> untypedValueSerializer(JavaType type)
90 {
91 synchronized (this) {
92 return _sharedMap.get(new TypeKey(type, false));
93 }
94 }
95
96 public JsonSerializer<Object> typedValueSerializer(JavaType type)
97 {
98 synchronized (this) {
99 return _sharedMap.get(new TypeKey(type, true));
100 }
101 }
102
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 */
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
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
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
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 */
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 */
206 public synchronized void flush() {
207 _sharedMap.clear();
208 }
209 }
210