1 | package eu.clarin.sru.client; |
---|
2 | |
---|
3 | import java.io.ByteArrayInputStream; |
---|
4 | import java.io.IOException; |
---|
5 | import java.io.InputStream; |
---|
6 | import java.net.URI; |
---|
7 | import java.net.UnknownHostException; |
---|
8 | import java.util.ArrayList; |
---|
9 | import java.util.List; |
---|
10 | import java.util.concurrent.ConcurrentHashMap; |
---|
11 | import java.util.concurrent.ConcurrentMap; |
---|
12 | import java.util.concurrent.TimeUnit; |
---|
13 | |
---|
14 | import javax.xml.stream.XMLStreamException; |
---|
15 | import javax.xml.stream.XMLStreamReader; |
---|
16 | |
---|
17 | import org.apache.commons.lang.NullArgumentException; |
---|
18 | import org.apache.http.HttpEntity; |
---|
19 | import org.apache.http.HttpResponse; |
---|
20 | import org.apache.http.HttpStatus; |
---|
21 | import org.apache.http.StatusLine; |
---|
22 | import org.apache.http.client.ClientProtocolException; |
---|
23 | import org.apache.http.client.HttpClient; |
---|
24 | import org.apache.http.client.methods.HttpGet; |
---|
25 | import org.apache.http.impl.client.DefaultHttpClient; |
---|
26 | import org.apache.http.params.CoreProtocolPNames; |
---|
27 | import org.apache.http.util.EntityUtils; |
---|
28 | import org.slf4j.Logger; |
---|
29 | import org.slf4j.LoggerFactory; |
---|
30 | |
---|
31 | import eu.clarin.sru.client.SRUScanHandler.WhereInList; |
---|
32 | |
---|
33 | |
---|
34 | public class SRUClient { |
---|
35 | private static final String SRU_NS = |
---|
36 | "http://www.loc.gov/zing/srw/"; |
---|
37 | private static final String SRU_DIAGNOSIC_NS = |
---|
38 | "http://www.loc.gov/zing/srw/diagnostic/"; |
---|
39 | private static final String SRU_DIAGNOSTIC_RECORD_SCHEMA = |
---|
40 | "info:srw/schema/1/diagnostics-v1.1"; |
---|
41 | private static final String VERSION_1_1 = "1.1"; |
---|
42 | private static final String VERSION_1_2 = "1.2"; |
---|
43 | private static final String RECORD_PACKING_XML = "xml"; |
---|
44 | private static final String RECORD_PACKING_STRING = "string"; |
---|
45 | private static final Logger logger = |
---|
46 | LoggerFactory.getLogger(SRUClient.class); |
---|
47 | private final SRUVersion defaultVersion; |
---|
48 | private final HttpClient httpClient; |
---|
49 | private final ConcurrentMap<String, SRURecordDataParser> parsers = |
---|
50 | new ConcurrentHashMap<String, SRURecordDataParser>(); |
---|
51 | private final XmlStreamReaderProxy proxy = new XmlStreamReaderProxy(); |
---|
52 | |
---|
53 | |
---|
54 | public SRUClient(SRUVersion defaultVersion) { |
---|
55 | if (defaultVersion == null) { |
---|
56 | throw new NullPointerException("version == null"); |
---|
57 | } |
---|
58 | this.defaultVersion = defaultVersion; |
---|
59 | this.httpClient = new DefaultHttpClient(); |
---|
60 | this.httpClient.getParams().setParameter(CoreProtocolPNames.USER_AGENT, |
---|
61 | "eu.clarin.sru.client/0.0.1"); |
---|
62 | } |
---|
63 | |
---|
64 | |
---|
65 | public void registerRecordParser(SRURecordDataParser parser) |
---|
66 | throws SRUClientException { |
---|
67 | if (parser == null) { |
---|
68 | throw new NullPointerException("parser == null"); |
---|
69 | } |
---|
70 | final String recordSchema = parser.getRecordSchema(); |
---|
71 | if (recordSchema == null) { |
---|
72 | throw new NullPointerException("parser.getRecordSchema() == null"); |
---|
73 | } |
---|
74 | if (recordSchema.isEmpty()) { |
---|
75 | throw new IllegalArgumentException( |
---|
76 | "parser.getRecordSchema() returns empty string"); |
---|
77 | } |
---|
78 | |
---|
79 | SRURecordDataParser old = parsers.putIfAbsent(recordSchema, parser); |
---|
80 | if (old != null) { |
---|
81 | throw new SRUClientException( |
---|
82 | "record data parser already registered: " + recordSchema); |
---|
83 | } |
---|
84 | } |
---|
85 | |
---|
86 | |
---|
87 | public void explain(SRUExplainRequest request, SRUExplainHandler handler) |
---|
88 | throws SRUClientException { |
---|
89 | if (request == null) { |
---|
90 | throw new NullPointerException("request == null"); |
---|
91 | } |
---|
92 | if (handler == null) { |
---|
93 | throw new NullArgumentException("handler == null"); |
---|
94 | } |
---|
95 | logger.debug("explain"); |
---|
96 | |
---|
97 | final long ts_start = System.nanoTime(); |
---|
98 | |
---|
99 | // create URI and perform request |
---|
100 | final URI uri = request.makeURI(defaultVersion); |
---|
101 | HttpResponse response = executeRequest(uri); |
---|
102 | HttpEntity entity = response.getEntity(); |
---|
103 | if (entity == null) { |
---|
104 | throw new SRUClientException("cannot get entity"); |
---|
105 | } |
---|
106 | |
---|
107 | InputStream stream = null; |
---|
108 | SRUXMLStreamReader reader = null; |
---|
109 | try { |
---|
110 | stream = entity.getContent(); |
---|
111 | |
---|
112 | final long ts_parsing = System.nanoTime(); |
---|
113 | reader = createReader(stream, true); |
---|
114 | parseExplainResponse(reader, request, handler); |
---|
115 | final long ts_end = System.nanoTime(); |
---|
116 | |
---|
117 | final long millisTotal = |
---|
118 | TimeUnit.NANOSECONDS.toMillis(ts_end - ts_start); |
---|
119 | final long millisNetwork = |
---|
120 | TimeUnit.NANOSECONDS.toMillis(ts_parsing - ts_start); |
---|
121 | final long millisParsing = |
---|
122 | TimeUnit.NANOSECONDS.toMillis(ts_end - ts_parsing); |
---|
123 | logger.debug("{} byte(s) in {} milli(s) ({} milli(s) network / {} milli(s) parsing)", |
---|
124 | new Object[] { reader.getByteCount(), |
---|
125 | millisTotal, millisNetwork, millisParsing }); |
---|
126 | handler.onRequestStatistics((int) reader.getByteCount(), |
---|
127 | millisTotal, millisNetwork, millisParsing); |
---|
128 | } catch (IllegalStateException e) { |
---|
129 | throw new SRUClientException("error reading response", e); |
---|
130 | } catch (IOException e) { |
---|
131 | throw new SRUClientException("error reading response", e); |
---|
132 | } catch (XMLStreamException e) { |
---|
133 | throw new SRUClientException("error reading response", e); |
---|
134 | } finally { |
---|
135 | if (reader != null) { |
---|
136 | try { |
---|
137 | reader.close(); |
---|
138 | } catch (XMLStreamException e) { |
---|
139 | /* IGNORE */ |
---|
140 | } |
---|
141 | } |
---|
142 | if (stream != null) { |
---|
143 | try { |
---|
144 | stream.close(); |
---|
145 | } catch (IOException e) { |
---|
146 | /* IGNORE */ |
---|
147 | } |
---|
148 | } |
---|
149 | } |
---|
150 | } |
---|
151 | |
---|
152 | |
---|
153 | public void scan(SRUScanRequest request, SRUScanHandler handler) |
---|
154 | throws SRUClientException { |
---|
155 | if (request == null) { |
---|
156 | throw new NullPointerException("request == null"); |
---|
157 | } |
---|
158 | if (handler == null) { |
---|
159 | throw new NullArgumentException("handler == null"); |
---|
160 | } |
---|
161 | logger.debug("searchRetrieve: scanClause = {}", request.getScanClause()); |
---|
162 | |
---|
163 | final long ts_start = System.nanoTime(); |
---|
164 | |
---|
165 | // create URI and perform request |
---|
166 | final URI uri = request.makeURI(defaultVersion); |
---|
167 | HttpResponse response = executeRequest(uri); |
---|
168 | HttpEntity entity = response.getEntity(); |
---|
169 | if (entity == null) { |
---|
170 | throw new SRUClientException("cannot get entity"); |
---|
171 | } |
---|
172 | |
---|
173 | InputStream stream = null; |
---|
174 | SRUXMLStreamReader reader = null; |
---|
175 | try { |
---|
176 | stream = entity.getContent(); |
---|
177 | |
---|
178 | final long ts_parsing = System.nanoTime(); |
---|
179 | reader = createReader(stream, true); |
---|
180 | parseScanResponse(reader, request, handler); |
---|
181 | final long ts_end = System.nanoTime(); |
---|
182 | |
---|
183 | final long millisTotal = |
---|
184 | TimeUnit.NANOSECONDS.toMillis(ts_end - ts_start); |
---|
185 | final long millisNetwork = |
---|
186 | TimeUnit.NANOSECONDS.toMillis(ts_parsing - ts_start); |
---|
187 | final long millisParsing = |
---|
188 | TimeUnit.NANOSECONDS.toMillis(ts_end - ts_parsing); |
---|
189 | logger.debug("{} byte(s) in {} milli(s) ({} milli(s) network / {} milli(s) parsing)", |
---|
190 | new Object[] { reader.getByteCount(), |
---|
191 | millisTotal, millisNetwork, millisParsing }); |
---|
192 | handler.onRequestStatistics((int) reader.getByteCount(), |
---|
193 | millisTotal, millisNetwork, millisParsing); |
---|
194 | } catch (IllegalStateException e) { |
---|
195 | throw new SRUClientException("error reading response", e); |
---|
196 | } catch (IOException e) { |
---|
197 | throw new SRUClientException("error reading response", e); |
---|
198 | } catch (XMLStreamException e) { |
---|
199 | throw new SRUClientException("error reading response", e); |
---|
200 | } finally { |
---|
201 | if (reader != null) { |
---|
202 | try { |
---|
203 | reader.close(); |
---|
204 | } catch (XMLStreamException e) { |
---|
205 | /* IGNORE */ |
---|
206 | } |
---|
207 | } |
---|
208 | if (stream != null) { |
---|
209 | try { |
---|
210 | stream.close(); |
---|
211 | } catch (IOException e) { |
---|
212 | /* IGNORE */ |
---|
213 | } |
---|
214 | } |
---|
215 | } |
---|
216 | } |
---|
217 | |
---|
218 | |
---|
219 | public void searchRetrieve(SRUSearchRetrieveRequest request, |
---|
220 | SRUSearchRetrieveHandler handler) throws SRUClientException { |
---|
221 | if (request == null) { |
---|
222 | throw new NullPointerException("request == null"); |
---|
223 | } |
---|
224 | if (handler == null) { |
---|
225 | throw new NullArgumentException("handler == null"); |
---|
226 | } |
---|
227 | logger.debug("searchRetrieve: query = {}", request.getQuery()); |
---|
228 | |
---|
229 | final long ts_start = System.nanoTime(); |
---|
230 | |
---|
231 | // create URI and perform request |
---|
232 | final URI uri = request.makeURI(defaultVersion); |
---|
233 | HttpResponse response = executeRequest(uri); |
---|
234 | HttpEntity entity = response.getEntity(); |
---|
235 | if (entity == null) { |
---|
236 | throw new SRUClientException("cannot get entity"); |
---|
237 | } |
---|
238 | |
---|
239 | InputStream stream = null; |
---|
240 | SRUXMLStreamReader reader = null; |
---|
241 | try { |
---|
242 | stream = entity.getContent(); |
---|
243 | |
---|
244 | final long ts_parsing = System.nanoTime(); |
---|
245 | reader = createReader(stream, true); |
---|
246 | parseSearchRetrieveResponse(reader, request, handler); |
---|
247 | final long ts_end = System.nanoTime(); |
---|
248 | |
---|
249 | final long millisTotal = |
---|
250 | TimeUnit.NANOSECONDS.toMillis(ts_end - ts_start); |
---|
251 | final long millisNetwork = |
---|
252 | TimeUnit.NANOSECONDS.toMillis(ts_parsing - ts_start); |
---|
253 | final long millisParsing = |
---|
254 | TimeUnit.NANOSECONDS.toMillis(ts_end - ts_parsing); |
---|
255 | logger.debug("{} byte(s) in {} milli(s) ({} milli(s) network / {} milli(s) parsing)", |
---|
256 | new Object[] { reader.getByteCount(), |
---|
257 | millisTotal, millisNetwork, millisParsing }); |
---|
258 | handler.onRequestStatistics((int) reader.getByteCount(), |
---|
259 | millisTotal, millisNetwork, millisParsing); |
---|
260 | } catch (IllegalStateException e) { |
---|
261 | throw new SRUClientException("error reading response", e); |
---|
262 | } catch (IOException e) { |
---|
263 | throw new SRUClientException("error reading response", e); |
---|
264 | } catch (XMLStreamException e) { |
---|
265 | throw new SRUClientException("error reading response", e); |
---|
266 | } finally { |
---|
267 | if (reader != null) { |
---|
268 | try { |
---|
269 | reader.close(); |
---|
270 | } catch (XMLStreamException e) { |
---|
271 | /* IGNORE */ |
---|
272 | } |
---|
273 | } |
---|
274 | if (stream != null) { |
---|
275 | try { |
---|
276 | stream.close(); |
---|
277 | } catch (IOException e) { |
---|
278 | /* IGNORE */ |
---|
279 | } |
---|
280 | } |
---|
281 | } |
---|
282 | } |
---|
283 | |
---|
284 | |
---|
285 | private HttpResponse executeRequest(URI uri) throws SRUClientException { |
---|
286 | HttpGet request = null; |
---|
287 | HttpResponse response = null; |
---|
288 | try { |
---|
289 | logger.debug("executing HTTP request: {}", uri.toString()); |
---|
290 | try { |
---|
291 | request = new HttpGet(uri); |
---|
292 | response = httpClient.execute(request); |
---|
293 | StatusLine status = response.getStatusLine(); |
---|
294 | if (status.getStatusCode() != HttpStatus.SC_OK) { |
---|
295 | if (status.getStatusCode() == HttpStatus.SC_NOT_FOUND) { |
---|
296 | throw new SRUClientException("not found: " + uri); |
---|
297 | } else { |
---|
298 | throw new SRUClientException("unexpected status: " + |
---|
299 | status.getStatusCode()); |
---|
300 | } |
---|
301 | } |
---|
302 | return response; |
---|
303 | } catch (ClientProtocolException e) { |
---|
304 | throw new SRUClientException("client protocol exception", e); |
---|
305 | } catch (UnknownHostException e) { |
---|
306 | throw new SRUClientException("unknown host: " + uri.getHost(), |
---|
307 | e); |
---|
308 | } catch (IOException e) { |
---|
309 | throw new SRUClientException("input/output error", e); |
---|
310 | } |
---|
311 | } catch (SRUClientException e) { |
---|
312 | /* |
---|
313 | * if an error occurred, make sure we are freeing up the resources |
---|
314 | * we've used |
---|
315 | */ |
---|
316 | if (response != null) { |
---|
317 | try { |
---|
318 | EntityUtils.consume(response.getEntity()); |
---|
319 | } catch (IOException ex) { |
---|
320 | /* IGNORE */ |
---|
321 | } |
---|
322 | } |
---|
323 | if (request != null) { |
---|
324 | request.abort(); |
---|
325 | } |
---|
326 | throw e; |
---|
327 | } |
---|
328 | } |
---|
329 | |
---|
330 | |
---|
331 | private void parseExplainResponse(final SRUXMLStreamReader reader, |
---|
332 | final SRUAbstractRequest request, final SRUExplainHandler handler) |
---|
333 | throws SRUClientException { |
---|
334 | logger.debug("parsing 'explain' response"); |
---|
335 | try { |
---|
336 | // explainResponse |
---|
337 | reader.readStart(SRU_NS, "explainResponse", true); |
---|
338 | |
---|
339 | // explainResponse/version |
---|
340 | SRUVersion version = parseVersion(reader); |
---|
341 | logger.debug("version = {}, requested = {}", |
---|
342 | version, request.getVersionPerformed()); |
---|
343 | |
---|
344 | // explainResponse/record |
---|
345 | reader.readStart(SRU_NS, "record", true); |
---|
346 | |
---|
347 | String schema = reader.readContent(SRU_NS, "recordSchema", true); |
---|
348 | |
---|
349 | SRURecordPacking packing = parseRecordPacking(reader); |
---|
350 | |
---|
351 | logger.debug("schema = {}, packing = {}", schema, packing); |
---|
352 | |
---|
353 | // explainResponse/record/recordData |
---|
354 | reader.readStart(SRU_NS, "recordData", true); |
---|
355 | reader.readEnd(SRU_NS, "recordData", true); |
---|
356 | |
---|
357 | // explainResponse/record/recordPosition |
---|
358 | if (reader.readStart(SRU_NS, "recordPosition", false)) { |
---|
359 | reader.readEnd(SRU_NS, "recordPosition", true); |
---|
360 | } |
---|
361 | |
---|
362 | // explainResponse/record/extraRecordData |
---|
363 | if (reader.readStart(SRU_NS, "extraRecordData", false)) { |
---|
364 | reader.readEnd(SRU_NS, "extraRecordData", true); |
---|
365 | } |
---|
366 | |
---|
367 | reader.readEnd(SRU_NS, "record"); |
---|
368 | |
---|
369 | // explainResponse/echoedExplainRequest |
---|
370 | if (reader.readStart(SRU_NS, "echoedExplainRequest", false)) { |
---|
371 | reader.readEnd(SRU_NS, "echoedExplainRequest", true); |
---|
372 | } |
---|
373 | |
---|
374 | // explainResponse/diagnostics |
---|
375 | final List<SRUDiagnostic> diagnostics = parseDiagnostics(reader); |
---|
376 | if (diagnostics != null) { |
---|
377 | handler.onDiagnostics(diagnostics); |
---|
378 | } |
---|
379 | |
---|
380 | // explainResponse/extraResponseData |
---|
381 | if (reader.readStart(SRU_NS, "extraResponseData", false)) { |
---|
382 | reader.consumeWhitespace(); |
---|
383 | proxy.reset(reader); |
---|
384 | try { |
---|
385 | handler.onExtraResponseData(proxy); |
---|
386 | } catch (XMLStreamException e) { |
---|
387 | throw new SRUClientException("handler triggered " |
---|
388 | + "error while parsing 'extraResponseData'", e); |
---|
389 | } |
---|
390 | reader.consumeWhitespace(); |
---|
391 | reader.readEnd(SRU_NS, "extraResponseData", true); |
---|
392 | } |
---|
393 | |
---|
394 | reader.readEnd(SRU_NS, "explainResponse"); |
---|
395 | } catch (XMLStreamException e) { |
---|
396 | throw new SRUClientException(e.getMessage(), e); |
---|
397 | } |
---|
398 | } |
---|
399 | |
---|
400 | |
---|
401 | private void parseScanResponse(final SRUXMLStreamReader reader, |
---|
402 | final SRUScanRequest request, final SRUScanHandler handler) |
---|
403 | throws SRUClientException { |
---|
404 | try { |
---|
405 | /* |
---|
406 | * if the endpoint cannot determine the operation, it should create |
---|
407 | * a explain response. |
---|
408 | */ |
---|
409 | if (reader.peekStart(SRU_NS, "explainResponse")) { |
---|
410 | parseExplainResponse(reader, request, new SRUExplainHandler() { |
---|
411 | @Override |
---|
412 | public void onRequestStatistics(int bytes, long millisTotal, |
---|
413 | long millisNetwork, long millisParsing) { |
---|
414 | } |
---|
415 | |
---|
416 | |
---|
417 | @Override |
---|
418 | public void onExtraResponseData(XMLStreamReader reader) |
---|
419 | throws XMLStreamException, SRUClientException { |
---|
420 | } |
---|
421 | |
---|
422 | |
---|
423 | @Override |
---|
424 | public void onDiagnostics(List<SRUDiagnostic> diagnostics) |
---|
425 | throws SRUClientException { |
---|
426 | handler.onDiagnostics(diagnostics); |
---|
427 | } |
---|
428 | }); |
---|
429 | } else { |
---|
430 | logger.debug("parsing 'scanResponse' response"); |
---|
431 | |
---|
432 | // scanResponse |
---|
433 | reader.readStart(SRU_NS, "scanResponse", true); |
---|
434 | |
---|
435 | // scanResponse/version |
---|
436 | SRUVersion version = parseVersion(reader); |
---|
437 | logger.debug("version = {}, requested = {}", version, |
---|
438 | request.getVersionPerformed()); |
---|
439 | |
---|
440 | // scanResponse/terms |
---|
441 | if (reader.readStart(SRU_NS, "terms", false)) { |
---|
442 | boolean first = true; |
---|
443 | while (reader.readStart(SRU_NS, "term", first)) { |
---|
444 | if (first) { |
---|
445 | first = false; |
---|
446 | handler.onStartTerms(); |
---|
447 | } |
---|
448 | |
---|
449 | // scanResponse/terms/value |
---|
450 | String value = reader |
---|
451 | .readContent(SRU_NS, "value", true); |
---|
452 | |
---|
453 | // scanResponse/terms/numberOfRecords |
---|
454 | int numberOfRecords = reader.readContent(SRU_NS, |
---|
455 | "numberOfRecords", false, -1); |
---|
456 | |
---|
457 | // scanResponse/terms/displayTerm |
---|
458 | String displayTerm = reader.readContent(SRU_NS, |
---|
459 | "displayTerm", false); |
---|
460 | |
---|
461 | // scanResponse/terms/whereInList |
---|
462 | String s = reader.readContent(SRU_NS, |
---|
463 | "whereInList", false); |
---|
464 | WhereInList whereInList = null; |
---|
465 | if (s != null) { |
---|
466 | if ("first".equals(s)) { |
---|
467 | whereInList = WhereInList.FIRST; |
---|
468 | } else if ("last".equals(s)) { |
---|
469 | whereInList = WhereInList.LAST; |
---|
470 | } else if ("only".equals(s)) { |
---|
471 | whereInList = WhereInList.ONLY; |
---|
472 | } else if ("inner".equals(s)) { |
---|
473 | whereInList = WhereInList.INNER; |
---|
474 | } else { |
---|
475 | throw new SRUClientException( |
---|
476 | "invalid value for 'whereInList': " + s); |
---|
477 | } |
---|
478 | } |
---|
479 | logger.debug("value = {}, numberOfRecords = {}, " |
---|
480 | + "displayTerm = {}, whereInList = {}", |
---|
481 | new Object[] { value, numberOfRecords, |
---|
482 | displayTerm, whereInList }); |
---|
483 | handler.onTerm(value, numberOfRecords, displayTerm, |
---|
484 | whereInList); |
---|
485 | |
---|
486 | // scanResponse/terms/extraTermData |
---|
487 | if (reader.readStart(SRU_NS, "extraTermData", first)) { |
---|
488 | reader.consumeWhitespace(); |
---|
489 | proxy.reset(reader); |
---|
490 | try { |
---|
491 | handler.onExtraTermData(value, proxy); |
---|
492 | } catch (XMLStreamException e) { |
---|
493 | throw new SRUClientException("handler " |
---|
494 | + "triggered error while parsing " |
---|
495 | + "'extraTermData'", e); |
---|
496 | } |
---|
497 | reader.consumeWhitespace(); |
---|
498 | reader.readEnd(SRU_NS, "extraTermData", true); |
---|
499 | } |
---|
500 | reader.readEnd(SRU_NS, "term", true); |
---|
501 | |
---|
502 | } // while |
---|
503 | reader.readEnd(SRU_NS, "terms"); |
---|
504 | handler.onFinishTerms(); |
---|
505 | } |
---|
506 | |
---|
507 | // scanResponse/echoedScanRequest |
---|
508 | if (reader.readStart(SRU_NS, "echoedScanRequest", false)) { |
---|
509 | reader.readEnd(SRU_NS, "echoedScanRequest", true); |
---|
510 | } |
---|
511 | |
---|
512 | // scanResponse/diagnostics |
---|
513 | final List<SRUDiagnostic> diagnostics = parseDiagnostics(reader); |
---|
514 | if (diagnostics != null) { |
---|
515 | handler.onDiagnostics(diagnostics); |
---|
516 | } |
---|
517 | |
---|
518 | // scanResponse/extraResponseData |
---|
519 | if (reader.readStart(SRU_NS, "extraResponseData", false)) { |
---|
520 | reader.consumeWhitespace(); |
---|
521 | proxy.reset(reader); |
---|
522 | try { |
---|
523 | handler.onExtraResponseData(proxy); |
---|
524 | } catch (XMLStreamException e) { |
---|
525 | throw new SRUClientException("handler triggered " |
---|
526 | + "error while parsing 'extraResponseData'", e); |
---|
527 | } |
---|
528 | reader.consumeWhitespace(); |
---|
529 | reader.readEnd(SRU_NS, "extraResponseData", true); |
---|
530 | } |
---|
531 | |
---|
532 | reader.readEnd(SRU_NS, "scanResponse"); |
---|
533 | } |
---|
534 | } catch (XMLStreamException e) { |
---|
535 | throw new SRUClientException(e.getMessage(), e); |
---|
536 | } |
---|
537 | } |
---|
538 | |
---|
539 | |
---|
540 | private void parseSearchRetrieveResponse(final SRUXMLStreamReader reader, |
---|
541 | final SRUSearchRetrieveRequest request, |
---|
542 | final SRUSearchRetrieveHandler handler) throws SRUClientException { |
---|
543 | try { |
---|
544 | /* |
---|
545 | * if the endpoint cannot determine the operation, it should create |
---|
546 | * a explain response. |
---|
547 | */ |
---|
548 | if (reader.peekStart(SRU_NS, "explainResponse")) { |
---|
549 | parseExplainResponse(reader, request, new SRUExplainHandler() { |
---|
550 | @Override |
---|
551 | public void onRequestStatistics(int bytes, long millisTotal, |
---|
552 | long millisNetwork, long millisParsing) { |
---|
553 | } |
---|
554 | |
---|
555 | |
---|
556 | @Override |
---|
557 | public void onExtraResponseData(XMLStreamReader reader) |
---|
558 | throws XMLStreamException, SRUClientException { |
---|
559 | } |
---|
560 | |
---|
561 | |
---|
562 | @Override |
---|
563 | public void onDiagnostics(List<SRUDiagnostic> diagnostics) |
---|
564 | throws SRUClientException { |
---|
565 | handler.onDiagnostics(diagnostics); |
---|
566 | } |
---|
567 | }); |
---|
568 | } else { |
---|
569 | logger.debug("parsing 'serarchRetrieve' response"); |
---|
570 | |
---|
571 | // searchRetrieveResponse |
---|
572 | reader.readStart(SRU_NS, "searchRetrieveResponse", true); |
---|
573 | |
---|
574 | // searchRetrieveResponse/version |
---|
575 | SRUVersion version = parseVersion(reader); |
---|
576 | logger.debug("version = {}, requested = {}", version, |
---|
577 | request.getVersionPerformed()); |
---|
578 | |
---|
579 | // searchRetrieveResponse/numberOfRecords |
---|
580 | int numberOfRecords = reader.readContent(SRU_NS, |
---|
581 | "numberOfRecords", true, -1); |
---|
582 | |
---|
583 | // searchRetrieveResponse/resultSetId |
---|
584 | int resultSetId = reader.readContent(SRU_NS, |
---|
585 | "resultSetId", false, -1); |
---|
586 | |
---|
587 | // searchRetrieveResponse/resultSetIdleTime |
---|
588 | int resultSetIdleTime = reader.readContent(SRU_NS, |
---|
589 | "resultSetIdleTime", false, -1); |
---|
590 | |
---|
591 | logger.debug("numberOfRecords = {}, resultSetId = {}, " |
---|
592 | + "resultSetIdleTime = {}", new Object[] { |
---|
593 | numberOfRecords, resultSetId, resultSetIdleTime }); |
---|
594 | |
---|
595 | // searchRetrieveResponse/results |
---|
596 | if (numberOfRecords > 0) { |
---|
597 | reader.readStart(SRU_NS, "records", true); |
---|
598 | |
---|
599 | // searchRetrieveResponse/records/record |
---|
600 | boolean first = true; |
---|
601 | while (reader.readStart(SRU_NS, "record", first)) { |
---|
602 | if (first) { |
---|
603 | first = false; |
---|
604 | handler.onStartRecords(numberOfRecords, |
---|
605 | resultSetId, resultSetIdleTime); |
---|
606 | } |
---|
607 | |
---|
608 | String schema = reader.readContent(SRU_NS, |
---|
609 | "recordSchema", true); |
---|
610 | |
---|
611 | SRURecordPacking packing = parseRecordPacking(reader); |
---|
612 | |
---|
613 | logger.debug("schema = {}, packing = {}, requested packing = {}", |
---|
614 | new Object[] { schema, packing, |
---|
615 | request.getRecordPacking() }); |
---|
616 | |
---|
617 | if ((request.getRecordPacking() != null) && |
---|
618 | (packing != request.getRecordPacking())) { |
---|
619 | logger.warn("requested '{}' record packing, but server responded with '{}' record packing", |
---|
620 | request.getRecordPacking() |
---|
621 | .toProtocolString(), packing |
---|
622 | .toProtocolString()); |
---|
623 | // XXX: only throw if client is pedantic? |
---|
624 | throw new SRUClientException( |
---|
625 | "requested '" + |
---|
626 | request.getRecordPacking() |
---|
627 | .toProtocolString() + |
---|
628 | "' record packing, but server responded with '" + |
---|
629 | packing.toProtocolString() + |
---|
630 | "' record packing"); |
---|
631 | } |
---|
632 | |
---|
633 | // searchRetrieveResponse/record/recordData |
---|
634 | reader.readStart(SRU_NS, "recordData", true); |
---|
635 | reader.consumeWhitespace(); |
---|
636 | |
---|
637 | SRURecordData recordData = null; |
---|
638 | SRUDiagnostic surrogate = null; |
---|
639 | SRUXMLStreamReader recordReader = null; |
---|
640 | |
---|
641 | if (packing == SRURecordPacking.STRING) { |
---|
642 | /* |
---|
643 | * read content into temporary buffer and then use |
---|
644 | * a new XML reader to parse record data |
---|
645 | */ |
---|
646 | final String data = reader.readString(true); |
---|
647 | InputStream in = |
---|
648 | new ByteArrayInputStream(data.getBytes()); |
---|
649 | // FIXME: namespace context? |
---|
650 | recordReader = createReader(in, false); |
---|
651 | } else { |
---|
652 | recordReader = reader; |
---|
653 | } |
---|
654 | |
---|
655 | if (SRU_DIAGNOSTIC_RECORD_SCHEMA.equals(schema)) { |
---|
656 | surrogate = parseDiagnostic(recordReader, true); |
---|
657 | } else { |
---|
658 | SRURecordDataParser parser = parsers.get(schema); |
---|
659 | if (parser != null) { |
---|
660 | try { |
---|
661 | proxy.reset(recordReader); |
---|
662 | recordData = parser.parse(proxy); |
---|
663 | } catch (XMLStreamException e) { |
---|
664 | throw new SRUClientException( |
---|
665 | "error parsing record", e); |
---|
666 | } |
---|
667 | } else { |
---|
668 | // FIXME: handle this better? |
---|
669 | logger.debug("no record parser found for schema '{}'", |
---|
670 | schema); |
---|
671 | } |
---|
672 | } |
---|
673 | |
---|
674 | if (packing == SRURecordPacking.STRING) { |
---|
675 | recordReader.closeCompletly(); |
---|
676 | } |
---|
677 | |
---|
678 | reader.consumeWhitespace(); |
---|
679 | reader.readEnd(SRU_NS, "recordData", true); |
---|
680 | |
---|
681 | String identifier = null; |
---|
682 | if (version == SRUVersion.VERSION_1_2) { |
---|
683 | identifier = reader.readContent(SRU_NS, |
---|
684 | "recordIdentifier", false); |
---|
685 | } |
---|
686 | |
---|
687 | int position = reader.readContent(SRU_NS, |
---|
688 | "recordPosition", false, -1); |
---|
689 | |
---|
690 | logger.debug("recordIdentifier = {}, recordPosition = {}", |
---|
691 | identifier, position); |
---|
692 | |
---|
693 | // notify handler |
---|
694 | if (surrogate != null) { |
---|
695 | handler.onSurrogateRecord(identifier, |
---|
696 | position, surrogate); |
---|
697 | } else { |
---|
698 | if (recordData != null) { |
---|
699 | handler.onRecord(identifier, |
---|
700 | position, recordData); |
---|
701 | } |
---|
702 | } |
---|
703 | |
---|
704 | if (reader.readStart(SRU_NS, "extraRecordData", false)) { |
---|
705 | reader.consumeWhitespace(); |
---|
706 | proxy.reset(reader); |
---|
707 | try { |
---|
708 | handler.onExtraRecordData(identifier, |
---|
709 | position, proxy); |
---|
710 | } catch (XMLStreamException e) { |
---|
711 | throw new SRUClientException("handler " |
---|
712 | + "triggered error while parsing " |
---|
713 | + "'extraRecordData'", e); |
---|
714 | } |
---|
715 | reader.consumeWhitespace(); |
---|
716 | reader.readEnd(SRU_NS, "extraRecordData", true); |
---|
717 | } |
---|
718 | |
---|
719 | reader.readEnd(SRU_NS, "record"); |
---|
720 | } // while |
---|
721 | reader.readEnd(SRU_NS, "records"); |
---|
722 | } |
---|
723 | |
---|
724 | int nextRecordPosition = reader.readContent(SRU_NS, |
---|
725 | "nextRecordPosition", false, -1); |
---|
726 | logger.debug("nextRecordPosition = {}", nextRecordPosition); |
---|
727 | handler.onFinishRecords(nextRecordPosition); |
---|
728 | |
---|
729 | // searchRetrieveResponse/echoedSearchRetrieveResponse |
---|
730 | if (reader.readStart(SRU_NS, |
---|
731 | "echoedSearchRetrieveRequest", false)) { |
---|
732 | reader.readEnd(SRU_NS, "echoedSearchRetrieveRequest", true); |
---|
733 | } |
---|
734 | |
---|
735 | // searchRetrieveResponse/diagnostics |
---|
736 | final List<SRUDiagnostic> diagnostics = parseDiagnostics(reader); |
---|
737 | if (diagnostics != null) { |
---|
738 | handler.onDiagnostics(diagnostics); |
---|
739 | } |
---|
740 | |
---|
741 | // explainResponse/extraResponseData |
---|
742 | if (reader.readStart(SRU_NS, "extraResponseData", false)) { |
---|
743 | reader.consumeWhitespace(); |
---|
744 | proxy.reset(reader); |
---|
745 | try { |
---|
746 | handler.onExtraResponseData(proxy); |
---|
747 | } catch (XMLStreamException e) { |
---|
748 | throw new SRUClientException("handler triggered " |
---|
749 | + "error while parsing 'extraResponseData'", e); |
---|
750 | } |
---|
751 | reader.consumeWhitespace(); |
---|
752 | reader.readEnd(SRU_NS, "extraResponseData", true); |
---|
753 | } |
---|
754 | |
---|
755 | reader.readEnd(SRU_NS, "searchRetrieveResponse"); |
---|
756 | } |
---|
757 | } catch (XMLStreamException e) { |
---|
758 | throw new SRUClientException(e.getMessage(), e); |
---|
759 | } |
---|
760 | } |
---|
761 | |
---|
762 | |
---|
763 | private static SRUVersion parseVersion(SRUXMLStreamReader reader) |
---|
764 | throws XMLStreamException, SRUClientException { |
---|
765 | final String v = reader.readContent(SRU_NS, "version", true); |
---|
766 | if (VERSION_1_1.equals(v)) { |
---|
767 | return SRUVersion.VERSION_1_1; |
---|
768 | } else if (VERSION_1_2.equals(v)) { |
---|
769 | return SRUVersion.VERSION_1_2; |
---|
770 | } else { |
---|
771 | throw new SRUClientException("invalid value '" + v + |
---|
772 | "' for version (valid values are: '" + VERSION_1_1 + |
---|
773 | "' and '" + VERSION_1_2 + "')"); |
---|
774 | } |
---|
775 | } |
---|
776 | |
---|
777 | |
---|
778 | private static List<SRUDiagnostic> parseDiagnostics( |
---|
779 | SRUXMLStreamReader reader) throws XMLStreamException, |
---|
780 | SRUClientException { |
---|
781 | if (reader.readStart(SRU_NS, "diagnostics", false)) { |
---|
782 | List<SRUDiagnostic> diagnostics = null; |
---|
783 | |
---|
784 | SRUDiagnostic diagnostic = null; |
---|
785 | while ((diagnostic = parseDiagnostic(reader, |
---|
786 | (diagnostics == null))) != null) { |
---|
787 | if (diagnostics == null) { |
---|
788 | diagnostics = new ArrayList<SRUDiagnostic>(); |
---|
789 | } |
---|
790 | diagnostics.add(diagnostic); |
---|
791 | } // while |
---|
792 | reader.readEnd(SRU_NS, "diagnostics"); |
---|
793 | return diagnostics; |
---|
794 | } else { |
---|
795 | return null; |
---|
796 | } |
---|
797 | } |
---|
798 | |
---|
799 | |
---|
800 | private static SRUDiagnostic parseDiagnostic(SRUXMLStreamReader reader, |
---|
801 | boolean required) throws XMLStreamException, SRUClientException { |
---|
802 | if (reader.readStart(SRU_DIAGNOSIC_NS, "diagnostic", required)) { |
---|
803 | |
---|
804 | // diagnostic/uri |
---|
805 | String uri = reader.readContent(SRU_DIAGNOSIC_NS, "uri", true); |
---|
806 | |
---|
807 | // diagnostic/details |
---|
808 | String details = |
---|
809 | reader.readContent(SRU_DIAGNOSIC_NS, "details", false); |
---|
810 | |
---|
811 | // diagnostic/message |
---|
812 | String message = |
---|
813 | reader.readContent(SRU_DIAGNOSIC_NS, "message", false); |
---|
814 | |
---|
815 | reader.readEnd(SRU_DIAGNOSIC_NS, "diagnostic"); |
---|
816 | |
---|
817 | logger.debug("diagostic: uri={}, detail={}, message={}", |
---|
818 | new Object[] { uri, details, message }); |
---|
819 | return new SRUDiagnostic(uri, details, message); |
---|
820 | } else { |
---|
821 | return null; |
---|
822 | } |
---|
823 | } |
---|
824 | |
---|
825 | |
---|
826 | private static SRURecordPacking parseRecordPacking(SRUXMLStreamReader reader) |
---|
827 | throws XMLStreamException, SRUClientException { |
---|
828 | final String v = reader.readContent(SRU_NS, "recordPacking", true); |
---|
829 | |
---|
830 | if (RECORD_PACKING_XML.equals(v)) { |
---|
831 | return SRURecordPacking.XML; |
---|
832 | } else if (RECORD_PACKING_STRING.equals(v)) { |
---|
833 | return SRURecordPacking.STRING; |
---|
834 | } else { |
---|
835 | throw new SRUClientException("invalid value '" + v + |
---|
836 | "' for record packing (valid values are: '" + |
---|
837 | RECORD_PACKING_XML + "' and '" + RECORD_PACKING_STRING + |
---|
838 | "')"); |
---|
839 | } |
---|
840 | } |
---|
841 | |
---|
842 | |
---|
843 | private SRUXMLStreamReader createReader(InputStream in, boolean wrap) |
---|
844 | throws XMLStreamException { |
---|
845 | return new SRUXMLStreamReader(in, wrap); |
---|
846 | } |
---|
847 | |
---|
848 | } // class SRUClient |
---|