1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.allanbank.mongodb.client.callback;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25
26 import com.allanbank.mongodb.Callback;
27 import com.allanbank.mongodb.MongoDbException;
28 import com.allanbank.mongodb.bson.Document;
29 import com.allanbank.mongodb.bson.Element;
30 import com.allanbank.mongodb.bson.NumericElement;
31 import com.allanbank.mongodb.bson.element.NullElement;
32 import com.allanbank.mongodb.bson.element.StringElement;
33 import com.allanbank.mongodb.client.Message;
34 import com.allanbank.mongodb.client.message.Reply;
35 import com.allanbank.mongodb.error.CursorNotFoundException;
36 import com.allanbank.mongodb.error.DuplicateKeyException;
37 import com.allanbank.mongodb.error.DurabilityException;
38 import com.allanbank.mongodb.error.MaximumTimeLimitExceededException;
39 import com.allanbank.mongodb.error.QueryFailedException;
40 import com.allanbank.mongodb.error.ReplyException;
41 import com.allanbank.mongodb.error.ShardConfigStaleException;
42
43
44
45
46
47
48
49
50
51 public abstract class AbstractValidatingReplyCallback implements ReplyCallback {
52
53
54 public static final String ERROR_CODE_FIELD = "code";
55
56
57 public static final List<String> ERROR_MESSAGE_FIELDS;
58
59 static {
60 final List<String> fields = new ArrayList<String>(3);
61 fields.add("jnote");
62 fields.add("wnote");
63 fields.add("$err");
64 fields.add("errmsg");
65 fields.add("err");
66
67 ERROR_MESSAGE_FIELDS = Collections.unmodifiableList(fields);
68 }
69
70
71
72
73 public AbstractValidatingReplyCallback() {
74 super();
75 }
76
77
78
79
80
81
82
83
84
85
86 @Override
87 public void callback(final Reply result) {
88
89 try {
90 verify(result);
91 handle(result);
92 }
93 catch (final MongoDbException error) {
94 exception(error);
95 }
96 }
97
98
99
100
101 @Override
102 public abstract void exception(final Throwable thrown);
103
104
105
106
107
108
109
110
111 protected MongoDbException asError(final Reply reply) {
112 return asError(reply, false);
113 }
114
115
116
117
118
119
120
121
122
123
124 protected MongoDbException asError(final Reply reply,
125 final boolean knownError) {
126 final List<Document> results = reply.getResults();
127 if (results.size() == 1) {
128 final Document doc = results.get(0);
129 final Element okElem = doc.get("ok");
130 final Element errorNumberElem = doc.get(ERROR_CODE_FIELD);
131
132 Element errorMessageElem = null;
133 for (int i = 0; (errorMessageElem == null)
134 && (i < ERROR_MESSAGE_FIELDS.size()); ++i) {
135 errorMessageElem = doc.get(ERROR_MESSAGE_FIELDS.get(i));
136 }
137
138 if (okElem != null) {
139 final int okValue = toInt(okElem);
140 if (okValue != 1) {
141 return asError(reply, okValue, toInt(errorNumberElem),
142 asString(errorMessageElem));
143 }
144 else if ((errorMessageElem != null)
145 && !(errorMessageElem instanceof NullElement)) {
146 return asError(reply, okValue, toInt(errorNumberElem),
147 asString(errorMessageElem));
148 }
149 }
150 else if (knownError) {
151 return asError(reply, -1, toInt(errorNumberElem),
152 asString(errorMessageElem));
153
154 }
155 }
156 return null;
157 }
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 protected final MongoDbException asError(final Reply reply,
177 final int okValue, final int errorNumber,
178 final boolean knownDurabilityError, final String errorMessage,
179 final Message message) {
180
181 if (isDurabilityFailure(reply, knownDurabilityError, errorMessage)) {
182 return new DurabilityException(okValue, errorNumber, errorMessage,
183 message, reply);
184 }
185 else if ((errorNumber == 11000) || (errorNumber == 11001)
186 || errorMessage.startsWith("E11000")
187 || errorMessage.startsWith("E11001")) {
188 return new DuplicateKeyException(okValue, errorNumber,
189 errorMessage, message, reply);
190 }
191 else if ((errorNumber == 50) ||
192 (errorNumber == 13475) ||
193 (errorNumber == 16711)) {
194 return new MaximumTimeLimitExceededException(okValue, errorNumber,
195 errorMessage, message, reply);
196 }
197 return new ReplyException(okValue, errorNumber, errorMessage, message,
198 reply);
199 }
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 protected MongoDbException asError(final Reply reply, final int okValue,
215 final int errorNumber, final String errorMessage) {
216 return asError(reply, okValue, errorNumber, false, errorMessage, null);
217 }
218
219
220
221
222
223
224
225
226
227
228 protected String asString(final Element errorMessageElem) {
229 if (errorMessageElem instanceof StringElement) {
230 return ((StringElement) errorMessageElem).getValue();
231 }
232 return String.valueOf(errorMessageElem);
233 }
234
235
236
237
238
239
240
241
242
243 protected void checkForError(final Reply reply) throws MongoDbException {
244 final MongoDbException exception = asError(reply);
245 if (exception != null) {
246 throw exception;
247 }
248 }
249
250
251
252
253
254
255
256 protected abstract void handle(Reply reply);
257
258
259
260
261
262
263
264
265
266 protected int toInt(final Element element) {
267 if (element instanceof NumericElement) {
268 return ((NumericElement) element).getIntValue();
269 }
270
271 return -1;
272 }
273
274
275
276
277
278
279
280
281
282 protected void verify(final Reply reply) throws MongoDbException {
283 if (reply.isCursorNotFound()) {
284 throw new CursorNotFoundException(reply, asError(reply, true));
285 }
286 else if (reply.isQueryFailed()) {
287 final MongoDbException error = asError(reply, true);
288 if ((error == null) || (error.getClass() == ReplyException.class)) {
289 throw new QueryFailedException(reply, error);
290 }
291
292 throw error;
293 }
294 else if (reply.isShardConfigStale()) {
295 throw new ShardConfigStaleException(reply, asError(reply, true));
296 }
297 else {
298 checkForError(reply);
299 }
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313
314 private boolean isDurabilityFailure(final Reply reply,
315 final boolean knownDurabilityError, final String errorMessage) {
316 boolean durabilityError = knownDurabilityError;
317
318 final List<Document> results = reply.getResults();
319 if ((results.size() == 1) && !knownDurabilityError) {
320 final Document doc = results.get(0);
321
322 durabilityError = doc.contains("wtimeout")
323 || doc.contains("wnote")
324 || doc.contains("jnote")
325 || doc.contains("badGLE")
326 || errorMessage.startsWith("cannot use 'j' option")
327 || errorMessage
328 .startsWith("could not enforce write concern");
329 }
330 return durabilityError;
331 }
332
333 }