source: SRUClient/trunk/src/main/java/eu/clarin/sru/client/SRUSimpleClient.java @ 2131

Last change on this file since 2131 was 2131, checked in by oschonef, 12 years ago
  • re-factoring (2/2)
    • extract SRUClient interface
    • remove isStrictMode()/setStrictMode() methods

HEADS UP:

  • Instantiate SRUSimpleClient instead of SRUClient for the non-threaded SRU client.
  • Property svn:eol-style set to native
File size: 41.6 KB
Line 
1/**
2 * This software is copyright (c) 2011 by
3 *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
4 * This is free software. You can redistribute it
5 * and/or modify it under the terms described in
6 * the GNU General Public License v3 of which you
7 * should have received a copy. Otherwise you can download
8 * it from
9 *
10 *   http://www.gnu.org/licenses/gpl-3.0.txt
11 *
12 * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
13 *
14 * @license http://www.gnu.org/licenses/gpl-3.0.txt
15 *  GNU General Public License v3
16 */
17package eu.clarin.sru.client;
18
19import java.io.ByteArrayInputStream;
20import java.io.IOException;
21import java.io.InputStream;
22import java.net.URI;
23import java.net.UnknownHostException;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.List;
27import java.util.Map;
28import java.util.concurrent.TimeUnit;
29
30import javax.xml.stream.XMLStreamException;
31import javax.xml.stream.XMLStreamReader;
32
33import org.apache.commons.lang.NullArgumentException;
34import org.apache.http.HttpEntity;
35import org.apache.http.HttpResponse;
36import org.apache.http.HttpStatus;
37import org.apache.http.StatusLine;
38import org.apache.http.client.ClientProtocolException;
39import org.apache.http.client.HttpClient;
40import org.apache.http.client.methods.HttpGet;
41import org.apache.http.impl.client.DefaultHttpClient;
42import org.apache.http.params.CoreProtocolPNames;
43import org.apache.http.util.EntityUtils;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
47import eu.clarin.sru.client.SRUScanHandler.WhereInList;
48
49/**
50 * A non-threaded SRU client.
51 * <p>This class is <em>not</em> thread-safe!</p>
52 */
53public class SRUSimpleClient implements SRUClient {
54    /** constant record data schema parser to match any schema */
55    public static final String RECORD_DATA_PARSER_SCHEMA_ANY = "*";
56    /** default version the client will use, if not otherwise specified */
57    public static final SRUVersion DEFAULT_SRU_VERSION = SRUVersion.VERSION_1_2;
58    private static final String SRU_NS =
59            "http://www.loc.gov/zing/srw/";
60    private static final String SRU_DIAGNOSIC_NS =
61            "http://www.loc.gov/zing/srw/diagnostic/";
62    private static final String SRU_DIAGNOSTIC_RECORD_SCHEMA =
63            "info:srw/schema/1/diagnostics-v1.1";
64    private static final String VERSION_1_1 = "1.1";
65    private static final String VERSION_1_2 = "1.2";
66    private static final String RECORD_PACKING_XML = "xml";
67    private static final String RECORD_PACKING_STRING = "string";
68    private static final Logger logger =
69            LoggerFactory.getLogger(SRUSimpleClient.class);
70    private final SRUVersion defaultVersion;
71    private final HttpClient httpClient;
72    private final Map<String, SRURecordDataParser> parsers =
73            new HashMap<String, SRURecordDataParser>();
74    private final XmlStreamReaderProxy proxy = new XmlStreamReaderProxy();
75    private boolean strictMode;
76
77
78    /**
79     * Constructor. This constructor will create a <em>strict</em> client and
80     * use the default SRU version.
81     *
82     * @see #SRUSimpleClient(SRUVersion, boolean)
83     * @see #DEFAULT_SRU_VERSION
84     */
85    public SRUSimpleClient() {
86        this(DEFAULT_SRU_VERSION, true);
87    }
88
89
90    /**
91     * Constructor. This constructor will create a <em>strict</em> client.
92     *
93     * @param defaultVersion
94     *            the default version to use for SRU requests; may be overridden
95     *            by individual requests
96     * @see #SRUSimpleClient(SRUVersion, boolean)
97     */
98    public SRUSimpleClient(SRUVersion defaultVersion) {
99        this(defaultVersion, true);
100    }
101
102
103    /**
104     * Constructor.
105     *
106     * @param defaultVersion
107     *            the default version to use for SRU requests; may be overridden
108     *            by individual requests
109     * @param strictMode
110     *            if <code>true</code> the client will strictly adhere to the
111     *            SRU standard and raise fatal errors on violations, if
112     *            <code>false</code> it will act more forgiving and ignore
113     *            certain violations
114     *
115     */
116    public SRUSimpleClient(SRUVersion defaultVersion, boolean strictMode) {
117        if (defaultVersion == null) {
118            throw new NullPointerException("version == null");
119        }
120        this.defaultVersion = defaultVersion;
121        this.strictMode = strictMode;
122        this.httpClient = new DefaultHttpClient();
123        this.httpClient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,
124                    "eu.clarin.sru.client/0.0.1");
125    }
126
127
128    @Override
129    public void registerRecordParser(SRURecordDataParser parser)
130            throws SRUClientException {
131        if (parser == null) {
132            throw new NullPointerException("parser == null");
133        }
134        final String recordSchema = parser.getRecordSchema();
135        if (recordSchema == null) {
136            throw new NullPointerException("parser.getRecordSchema() == null");
137        }
138        if (recordSchema.isEmpty()) {
139            throw new IllegalArgumentException(
140                    "parser.getRecordSchema() returns empty string");
141        }
142
143        if (!parsers.containsKey(recordSchema)) {
144            parsers.put(recordSchema, parser);
145        } else {
146            throw new SRUClientException(
147                    "record data parser already registered: " + recordSchema);
148        }
149    }
150
151
152    @Override
153    public void explain(SRUExplainRequest request, SRUExplainHandler 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("explain");
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            parseExplainResponse(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    @Override
220    public void scan(SRUScanRequest request, SRUScanHandler handler)
221            throws SRUClientException {
222        if (request == null) {
223            throw new NullPointerException("request == null");
224        }
225        if (handler == null) {
226            throw new NullArgumentException("handler == null");
227        }
228        logger.debug("searchRetrieve: scanClause = {}", request.getScanClause());
229
230        final long ts_start = System.nanoTime();
231
232        // create URI and perform request
233        final URI uri = request.makeURI(defaultVersion);
234        HttpResponse response = executeRequest(uri);
235        HttpEntity entity = response.getEntity();
236        if (entity == null) {
237            throw new SRUClientException("cannot get entity");
238        }
239
240        InputStream stream = null;
241        SRUXMLStreamReader reader = null;
242        try {
243            stream = entity.getContent();
244
245            final long ts_parsing = System.nanoTime();
246            reader = createReader(stream, true);
247            parseScanResponse(reader, request, handler);
248            final long ts_end = System.nanoTime();
249
250            final long millisTotal =
251                    TimeUnit.NANOSECONDS.toMillis(ts_end - ts_start);
252            final long millisNetwork =
253                    TimeUnit.NANOSECONDS.toMillis(ts_parsing - ts_start);
254            final long millisParsing =
255                    TimeUnit.NANOSECONDS.toMillis(ts_end - ts_parsing);
256            logger.debug("{} byte(s) in {} milli(s) ({} milli(s) network / {} milli(s) parsing)",
257                    new Object[] { reader.getByteCount(),
258                            millisTotal, millisNetwork, millisParsing });
259            handler.onRequestStatistics((int) reader.getByteCount(),
260                    millisTotal, millisNetwork, millisParsing);
261        } catch (IllegalStateException e) {
262            throw new SRUClientException("error reading response", e);
263        } catch (IOException e) {
264            throw new SRUClientException("error reading response", e);
265        } catch (XMLStreamException e) {
266            throw new SRUClientException("error reading response", e);
267        } finally {
268            if (reader != null) {
269                try {
270                    reader.close();
271                } catch (XMLStreamException e) {
272                    /* IGNORE */
273                }
274            }
275            if (stream != null) {
276                try {
277                    stream.close();
278                } catch (IOException e) {
279                    /* IGNORE */
280                }
281            }
282        }
283    }
284
285
286    @Override
287    public void searchRetrieve(SRUSearchRetrieveRequest request,
288            SRUSearchRetrieveHandler handler) throws SRUClientException {
289        if (request == null) {
290            throw new NullPointerException("request == null");
291        }
292        if (handler == null) {
293            throw new NullArgumentException("handler == null");
294        }
295        logger.debug("searchRetrieve: query = {}", request.getQuery());
296
297        final long ts_start = System.nanoTime();
298
299        // create URI and perform request
300        final URI uri = request.makeURI(defaultVersion);
301        HttpResponse response = executeRequest(uri);
302        HttpEntity entity = response.getEntity();
303        if (entity == null) {
304            throw new SRUClientException("cannot get entity");
305        }
306
307        InputStream stream = null;
308        SRUXMLStreamReader reader = null;
309        try {
310            stream = entity.getContent();
311
312            final long ts_parsing = System.nanoTime();
313            reader = createReader(stream, true);
314            parseSearchRetrieveResponse(reader, request, handler);
315            final long ts_end = System.nanoTime();
316
317            final long millisTotal =
318                    TimeUnit.NANOSECONDS.toMillis(ts_end - ts_start);
319            final long millisNetwork =
320                    TimeUnit.NANOSECONDS.toMillis(ts_parsing - ts_start);
321            final long millisParsing =
322                    TimeUnit.NANOSECONDS.toMillis(ts_end - ts_parsing);
323            logger.debug("{} byte(s) in {} milli(s) ({} milli(s) network / {} milli(s) parsing)",
324                    new Object[] { reader.getByteCount(),
325                            millisTotal, millisNetwork, millisParsing });
326            handler.onRequestStatistics((int) reader.getByteCount(),
327                    millisTotal, millisNetwork, millisParsing);
328        } catch (IllegalStateException e) {
329            throw new SRUClientException("error reading response", e);
330        } catch (IOException e) {
331            throw new SRUClientException("error reading response", e);
332        } catch (XMLStreamException e) {
333            throw new SRUClientException("error reading response", e);
334        } finally {
335            if (reader != null) {
336                try {
337                    reader.close();
338                } catch (XMLStreamException e) {
339                    /* IGNORE */
340                }
341            }
342            if (stream != null) {
343                try {
344                    stream.close();
345                } catch (IOException e) {
346                    /* IGNORE */
347                }
348            }
349        }
350    }
351
352
353    private HttpResponse executeRequest(URI uri) throws SRUClientException {
354        HttpGet request = null;
355        HttpResponse response = null;
356        try {
357            logger.debug("performing HTTP request: {}", uri.toString());
358            try {
359                request = new HttpGet(uri);
360                response = httpClient.execute(request);
361                StatusLine status = response.getStatusLine();
362                if (status.getStatusCode() != HttpStatus.SC_OK) {
363                    if (status.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
364                        throw new SRUClientException("not found: " + uri);
365                    } else {
366                        throw new SRUClientException("unexpected status: " +
367                                status.getStatusCode());
368                    }
369                }
370                return response;
371            } catch (ClientProtocolException e) {
372                throw new SRUClientException("client protocol exception", e);
373            } catch (UnknownHostException e) {
374                throw new SRUClientException(
375                        "unknown host: " + uri.getHost(), e);
376            } catch (IOException e) {
377                String msg = null;
378                if ((e.getMessage() != null) && !e.getMessage().isEmpty()) {
379                    msg = e.getMessage();
380                }
381                throw new SRUClientException(msg != null
382                        ? msg
383                        : "input/output error", e);
384            }
385        } catch (SRUClientException e) {
386            /*
387             * if an error occurred, make sure we are freeing up the resources
388             * we've used
389             */
390            if (response != null) {
391                try {
392                    EntityUtils.consume(response.getEntity());
393                } catch (IOException ex) {
394                    /* IGNORE */
395                }
396            }
397            if (request != null) {
398                request.abort();
399            }
400            throw e;
401        }
402    }
403
404
405    private void parseExplainResponse(final SRUXMLStreamReader reader,
406            final SRUAbstractRequest request, final SRUExplainHandler handler)
407            throws SRUClientException {
408        logger.debug("parsing 'explain' response");
409        try {
410            // explainResponse
411            reader.readStart(SRU_NS, "explainResponse", true);
412
413            // explainResponse/version
414            SRUVersion version = parseVersion(reader);
415            logger.debug("version = {}, requested = {}",
416                    version, request.getVersionPerformed());
417
418            // explainResponse/record
419            reader.readStart(SRU_NS, "record", true);
420
421            String schema = reader.readContent(SRU_NS, "recordSchema", true);
422
423            SRURecordPacking packing = parseRecordPacking(reader, strictMode);
424
425            logger.debug("schema = {}, packing = {}", schema, packing);
426
427            // explainResponse/record/recordData
428            reader.readStart(SRU_NS, "recordData", true);
429            reader.readEnd(SRU_NS, "recordData", true);
430
431            // explainResponse/record/recordPosition
432            if (reader.readStart(SRU_NS, "recordPosition", false)) {
433                reader.readEnd(SRU_NS, "recordPosition", true);
434            }
435
436            // explainResponse/record/extraRecordData
437            if (reader.readStart(SRU_NS, "extraRecordData", false)) {
438                reader.readEnd(SRU_NS, "extraRecordData", true);
439            }
440
441            reader.readEnd(SRU_NS, "record");
442
443            // explainResponse/echoedExplainRequest
444            if (reader.readStart(SRU_NS, "echoedExplainRequest", false)) {
445                reader.readEnd(SRU_NS, "echoedExplainRequest", true);
446            }
447
448            /*
449             * common error: echoedExplainRequest in default namespace
450             */
451            if (reader.readStart("", "echoedExplainRequest", false)) {
452                logger.error("Element 'echoedExplainRequest' must be in SRU namespace, but endpoint put it into default namespace");
453                if (strictMode) {
454                    throw new SRUClientException("Element 'echoedExplainRequest' must be in SRU namespace, but endpoint put it into default namespace");
455                }
456                reader.readEnd("", "echoedExplainRequest", true);
457            }
458
459            // explainResponse/diagnostics
460            final List<SRUDiagnostic> diagnostics = parseDiagnostics(reader);
461            if (diagnostics != null) {
462                handler.onDiagnostics(diagnostics);
463            }
464
465            // explainResponse/extraResponseData
466            if (reader.readStart(SRU_NS, "extraResponseData", false)) {
467                reader.consumeWhitespace();
468                proxy.reset(reader);
469                try {
470                    handler.onExtraResponseData(proxy);
471                } catch (XMLStreamException e) {
472                    throw new SRUClientException("handler triggered "
473                            + "error while parsing 'extraResponseData'", e);
474                }
475                reader.consumeWhitespace();
476                reader.readEnd(SRU_NS, "extraResponseData", true);
477            }
478
479            reader.readEnd(SRU_NS, "explainResponse");
480        } catch (XMLStreamException e) {
481            throw new SRUClientException(e.getMessage(), e);
482        }
483    }
484
485
486    private void parseScanResponse(final SRUXMLStreamReader reader,
487            final SRUScanRequest request, final SRUScanHandler handler)
488            throws SRUClientException {
489        try {
490            /*
491             * if the endpoint cannot determine the operation, it should create
492             * a explain response.
493             */
494            if (reader.peekStart(SRU_NS, "explainResponse")) {
495                parseExplainResponse(reader, request, new SRUExplainHandler() {
496                    @Override
497                    public void onRequestStatistics(int bytes, long millisTotal,
498                            long millisNetwork, long millisParsing) {
499                    }
500
501
502                    @Override
503                    public void onExtraResponseData(XMLStreamReader reader)
504                            throws XMLStreamException, SRUClientException {
505                    }
506
507
508                    @Override
509                    public void onDiagnostics(List<SRUDiagnostic> diagnostics)
510                            throws SRUClientException {
511                        handler.onDiagnostics(diagnostics);
512                    }
513                });
514            } else {
515                logger.debug("parsing 'scanResponse' response");
516
517                // scanResponse
518                reader.readStart(SRU_NS, "scanResponse", true);
519
520                // scanResponse/version
521                SRUVersion version = parseVersion(reader);
522                logger.debug("version = {}, requested = {}", version,
523                        request.getVersionPerformed());
524
525                // scanResponse/terms
526                if (reader.readStart(SRU_NS, "terms", false)) {
527                    boolean first = true;
528                    while (reader.readStart(SRU_NS, "term", first)) {
529                        if (first) {
530                            first = false;
531                            handler.onStartTerms();
532                        }
533
534                        // scanResponse/terms/value
535                        String value = reader
536                                .readContent(SRU_NS, "value", true);
537
538                        // scanResponse/terms/numberOfRecords
539                        int numberOfRecords = reader.readContent(SRU_NS,
540                                "numberOfRecords", false, -1);
541
542                        // scanResponse/terms/displayTerm
543                        String displayTerm = reader.readContent(SRU_NS,
544                                "displayTerm", false);
545
546                        // scanResponse/terms/whereInList
547                        String s = reader.readContent(SRU_NS,
548                                "whereInList", false);
549                        WhereInList whereInList = null;
550                        if (s != null) {
551                            if ("first".equals(s)) {
552                                whereInList = WhereInList.FIRST;
553                            } else if ("last".equals(s)) {
554                                whereInList = WhereInList.LAST;
555                            } else if ("only".equals(s)) {
556                                whereInList = WhereInList.ONLY;
557                            } else if ("inner".equals(s)) {
558                                whereInList = WhereInList.INNER;
559                            } else {
560                                throw new SRUClientException(
561                                        "invalid value for 'whereInList': " + s);
562                            }
563                        }
564                        logger.debug("value = {}, numberOfRecords = {}, "
565                                + "displayTerm = {}, whereInList = {}",
566                                new Object[] { value, numberOfRecords,
567                                        displayTerm, whereInList });
568                        handler.onTerm(value, numberOfRecords, displayTerm,
569                                whereInList);
570
571                        // scanResponse/terms/extraTermData
572                        if (reader.readStart(SRU_NS, "extraTermData", first)) {
573                            reader.consumeWhitespace();
574                            proxy.reset(reader);
575                            try {
576                                handler.onExtraTermData(value, proxy);
577                            } catch (XMLStreamException e) {
578                                throw new SRUClientException("handler "
579                                        + "triggered error while parsing "
580                                        + "'extraTermData'", e);
581                            }
582                            reader.consumeWhitespace();
583                            reader.readEnd(SRU_NS, "extraTermData", true);
584                        }
585                        reader.readEnd(SRU_NS, "term", true);
586
587                    } // while
588                    reader.readEnd(SRU_NS, "terms");
589                    handler.onFinishTerms();
590                }
591
592                // scanResponse/echoedScanRequest
593                if (reader.readStart(SRU_NS, "echoedScanRequest", false)) {
594                    reader.readEnd(SRU_NS, "echoedScanRequest", true);
595                }
596
597                /*
598                 * common error: echoedScanRequest in default namespace
599                 */
600                if (reader.readStart("", "echoedScanRequest", false)) {
601                    logger.error("Element 'echoedScanRequest' must be in SRU namespace, but endpoint put it into default namespace");
602                    if (strictMode) {
603                        throw new SRUClientException("Element 'echoedScanRequest' must be in SRU namespace, but endpoint put it into default namespace");
604                    }
605                    reader.readEnd("", "echoedScanRequest", true);
606                }
607
608                // scanResponse/diagnostics
609                final List<SRUDiagnostic> diagnostics = parseDiagnostics(reader);
610                if (diagnostics != null) {
611                    handler.onDiagnostics(diagnostics);
612                }
613
614                // scanResponse/extraResponseData
615                if (reader.readStart(SRU_NS, "extraResponseData", false)) {
616                    reader.consumeWhitespace();
617                    proxy.reset(reader);
618                    try {
619                        handler.onExtraResponseData(proxy);
620                    } catch (XMLStreamException e) {
621                        throw new SRUClientException("handler triggered "
622                                + "error while parsing 'extraResponseData'", e);
623                    }
624                    reader.consumeWhitespace();
625                    reader.readEnd(SRU_NS, "extraResponseData", true);
626                }
627
628                reader.readEnd(SRU_NS, "scanResponse");
629            }
630        } catch (XMLStreamException e) {
631            throw new SRUClientException(e.getMessage(), e);
632        }
633    }
634
635
636    private void parseSearchRetrieveResponse(final SRUXMLStreamReader reader,
637            final SRUSearchRetrieveRequest request,
638            final SRUSearchRetrieveHandler handler) throws SRUClientException {
639        try {
640            /*
641             * if the endpoint cannot determine the operation, it should create
642             * a explain response.
643             */
644            if (reader.peekStart(SRU_NS, "explainResponse")) {
645                parseExplainResponse(reader, request, new SRUExplainHandler() {
646                    @Override
647                    public void onRequestStatistics(int bytes, long millisTotal,
648                            long millisNetwork, long millisParsing) {
649                    }
650
651
652                    @Override
653                    public void onExtraResponseData(XMLStreamReader reader)
654                            throws XMLStreamException, SRUClientException {
655                    }
656
657
658                    @Override
659                    public void onDiagnostics(List<SRUDiagnostic> diagnostics)
660                            throws SRUClientException {
661                        handler.onDiagnostics(diagnostics);
662                    }
663                });
664            } else {
665                logger.debug("parsing 'serarchRetrieve' response");
666
667                // searchRetrieveResponse
668                reader.readStart(SRU_NS, "searchRetrieveResponse", true);
669
670                // searchRetrieveResponse/version
671                SRUVersion version = parseVersion(reader);
672                logger.debug("version = {}, requested = {}", version,
673                        request.getVersionPerformed());
674
675                // searchRetrieveResponse/numberOfRecords
676                int numberOfRecords = reader.readContent(SRU_NS,
677                        "numberOfRecords", true, -1);
678
679                // searchRetrieveResponse/resultSetId
680                int resultSetId = reader.readContent(SRU_NS,
681                        "resultSetId", false, -1);
682
683                // searchRetrieveResponse/resultSetIdleTime
684                int resultSetIdleTime = reader.readContent(SRU_NS,
685                        "resultSetIdleTime", false, -1);
686
687                logger.debug("numberOfRecords = {}, resultSetId = {}, "
688                        + "resultSetIdleTime = {}", new Object[] {
689                        numberOfRecords, resultSetId, resultSetIdleTime });
690
691                // searchRetrieveResponse/results
692                if (numberOfRecords > 0) {
693                    reader.readStart(SRU_NS, "records", true);
694
695                    // searchRetrieveResponse/records/record
696                    boolean first = true;
697                    while (reader.readStart(SRU_NS, "record", first)) {
698                        if (first) {
699                            first = false;
700                            handler.onStartRecords(numberOfRecords,
701                                    resultSetId, resultSetIdleTime);
702                        }
703
704                        String schema = reader.readContent(SRU_NS,
705                                "recordSchema", true);
706
707                        SRURecordPacking packing =
708                                parseRecordPacking(reader, strictMode);
709
710                        logger.debug("schema = {}, packing = {}, " +
711                                "requested packing = {}",
712                                new Object[] { schema, packing,
713                                        request.getRecordPacking() });
714
715                        if ((request.getRecordPacking() != null) &&
716                                (packing != request.getRecordPacking())) {
717                            final SRURecordPacking p =
718                                    request.getRecordPacking();
719                            logger.error("requested '{}' record packing, but " +
720                                "server responded with '{}' record packing",
721                                    p.getStringValue(),
722                                    packing.getStringValue());
723                            if (strictMode) {
724                                throw new SRUClientException("requested '" +
725                                        p.getStringValue() +
726                                        "' record packing, but server " +
727                                        "responded with '" +
728                                        packing.getStringValue() +
729                                        "' record packing");
730                            }
731                        }
732
733                        // searchRetrieveResponse/record/recordData
734                        reader.readStart(SRU_NS, "recordData", true);
735                        reader.consumeWhitespace();
736
737                        SRURecordData recordData = null;
738                        SRUDiagnostic surrogate = null;
739                        SRUXMLStreamReader recordReader = null;
740
741                        if (packing == SRURecordPacking.STRING) {
742                            /*
743                             * read content into temporary buffer and then use
744                             * a new XML reader to parse record data
745                             */
746                            final String data = reader.readString(true);
747                            InputStream in =
748                                    new ByteArrayInputStream(data.getBytes());
749                            // FIXME: namespace context?
750                            recordReader = createReader(in, false);
751                        } else {
752                            recordReader = reader;
753                        }
754
755                        if (SRU_DIAGNOSTIC_RECORD_SCHEMA.equals(schema)) {
756                            surrogate = parseDiagnostic(recordReader, true);
757                        } else {
758                            SRURecordDataParser parser = findParser(schema);
759                            if (parser != null) {
760                                try {
761                                    proxy.reset(recordReader);
762                                    recordData = parser.parse(proxy);
763                                } catch (XMLStreamException e) {
764                                    throw new SRUClientException(
765                                            "error parsing record", e);
766                                }
767                                if (recordData == null) {
768                                    // FIXME: handle this better? maybe throw?
769                                    logger.warn("parse did not correctly "
770                                            + "parse the record, will skip "
771                                            + "handler callback.");
772                                }
773                            } else {
774                                // FIXME: handle this better?
775                                logger.debug("no record parser found for schema '{}'",
776                                        schema);
777                            }
778                        }
779
780                        if (packing == SRURecordPacking.STRING) {
781                            recordReader.closeCompletly();
782                        }
783
784                        reader.consumeWhitespace();
785                        reader.readEnd(SRU_NS, "recordData", true);
786
787                        String identifier = null;
788                        if (version == SRUVersion.VERSION_1_2) {
789                            identifier = reader.readContent(SRU_NS,
790                                    "recordIdentifier", false);
791                        }
792
793                        int position = reader.readContent(SRU_NS,
794                                "recordPosition", false, -1);
795
796                        logger.debug("recordIdentifier = {}, recordPosition = {}",
797                                identifier, position);
798
799                        // notify handler
800                        if (surrogate != null) {
801                            handler.onSurrogateRecord(identifier,
802                                    position, surrogate);
803                        } else {
804                            if (recordData != null) {
805                                handler.onRecord(identifier,
806                                        position, recordData);
807                            }
808                        }
809
810                        if (reader.readStart(SRU_NS, "extraRecordData", false)) {
811                            reader.consumeWhitespace();
812                            proxy.reset(reader);
813                            try {
814                                handler.onExtraRecordData(identifier,
815                                        position, proxy);
816                            } catch (XMLStreamException e) {
817                                throw new SRUClientException("handler "
818                                        + "triggered error while parsing "
819                                        + "'extraRecordData'", e);
820                            }
821                            reader.consumeWhitespace();
822                            reader.readEnd(SRU_NS, "extraRecordData", true);
823                        }
824
825                        reader.readEnd(SRU_NS, "record");
826                    } // while
827                    reader.readEnd(SRU_NS, "records");
828                } else {
829                    /*
830                     * provide a better error format, if
831                     */
832                    if (reader.readStart(SRU_NS, "records", false)) {
833                        int bad = 0;
834                        while (reader.readStart(SRU_NS, "record", false)) {
835                            bad++;
836                            reader.readEnd(SRU_NS, "record", true);
837                        }
838                        reader.readEnd(SRU_NS, "records", true);
839                        if (bad == 0) {
840                            logger.error("endpoint declared 0 results, but " +
841                                    "response contained an empty 'records' " +
842                                    "element");
843                            if (strictMode) {
844                                throw new SRUClientException(
845                                        "endpoint declared 0 results, but response contained an empty 'records' element (behavior violates SRU specification)");
846                            }
847                        } else {
848                            logger.error("endpoint declared 0 results, but " +
849                                    "response contained an " + bad +
850                                    " record(s)");
851                            if (strictMode) {
852                            throw new SRUClientException("endpoint declared 0 results, but response containted " +
853                                    bad + " records (behavior may violate SRU specification)");
854                            }
855                        }
856                    }
857                }
858
859                int nextRecordPosition = reader.readContent(SRU_NS,
860                        "nextRecordPosition", false, -1);
861                logger.debug("nextRecordPosition = {}", nextRecordPosition);
862                handler.onFinishRecords(nextRecordPosition);
863
864                // searchRetrieveResponse/echoedSearchRetrieveResponse
865                if (reader.readStart(SRU_NS,
866                        "echoedSearchRetrieveRequest", false)) {
867                    reader.readEnd(SRU_NS, "echoedSearchRetrieveRequest", true);
868                }
869
870                /*
871                 * common error: echoedSearchRetrieveRequest in default namespace
872                 */
873                if (reader.readStart("", "echoedSearchRetrieveRequest", false)) {
874                    logger.error("Element 'echoedSearchRetrieveRequest' must be in SRU namespace, but endpoint put it into default namespace");
875                    if (strictMode) {
876                        throw new SRUClientException("Element 'echoedSearchRetrieveRequest' must be in SRU namespace, but endpoint put it into default namespace");
877                    }
878                    reader.readEnd("", "echoedSearchRetrieveRequest", true);
879                }
880
881                // searchRetrieveResponse/diagnostics
882                final List<SRUDiagnostic> diagnostics = parseDiagnostics(reader);
883                if (diagnostics != null) {
884                    handler.onDiagnostics(diagnostics);
885                }
886
887                // explainResponse/extraResponseData
888                if (reader.readStart(SRU_NS, "extraResponseData", false)) {
889                    reader.consumeWhitespace();
890                    proxy.reset(reader);
891                    try {
892                        handler.onExtraResponseData(proxy);
893                    } catch (XMLStreamException e) {
894                        throw new SRUClientException("handler triggered "
895                                + "error while parsing 'extraResponseData'", e);
896                    }
897                    reader.consumeWhitespace();
898                    reader.readEnd(SRU_NS, "extraResponseData", true);
899                }
900
901                reader.readEnd(SRU_NS, "searchRetrieveResponse");
902            }
903        } catch (XMLStreamException e) {
904            throw new SRUClientException(e.getMessage(), e);
905        }
906    }
907
908
909    private static SRUVersion parseVersion(SRUXMLStreamReader reader)
910        throws XMLStreamException, SRUClientException {
911        final String v = reader.readContent(SRU_NS, "version", true);
912        if (VERSION_1_1.equals(v)) {
913            return SRUVersion.VERSION_1_1;
914        } else if (VERSION_1_2.equals(v)) {
915            return SRUVersion.VERSION_1_2;
916        } else {
917            throw new SRUClientException("invalid value '" + v +
918                    "' for version (valid values are: '" + VERSION_1_1 +
919                    "' and '" + VERSION_1_2 + "')");
920        }
921    }
922
923
924    private static List<SRUDiagnostic> parseDiagnostics(
925            SRUXMLStreamReader reader) throws XMLStreamException,
926            SRUClientException {
927        if (reader.readStart(SRU_NS, "diagnostics", false)) {
928            List<SRUDiagnostic> diagnostics = null;
929
930            SRUDiagnostic diagnostic = null;
931            while ((diagnostic = parseDiagnostic(reader,
932                    (diagnostics == null))) != null) {
933                if (diagnostics == null) {
934                    diagnostics = new ArrayList<SRUDiagnostic>();
935                }
936                diagnostics.add(diagnostic);
937            } // while
938            reader.readEnd(SRU_NS, "diagnostics");
939            return diagnostics;
940        } else {
941            return null;
942        }
943    }
944
945
946    private static SRUDiagnostic parseDiagnostic(SRUXMLStreamReader reader,
947            boolean required) throws XMLStreamException, SRUClientException {
948        if (reader.readStart(SRU_DIAGNOSIC_NS, "diagnostic", required)) {
949
950            // diagnostic/uri
951            String uri = reader.readContent(SRU_DIAGNOSIC_NS, "uri", true);
952
953            // diagnostic/details
954            String details =
955                    reader.readContent(SRU_DIAGNOSIC_NS, "details", false);
956
957            // diagnostic/message
958            String message =
959                    reader.readContent(SRU_DIAGNOSIC_NS, "message", false);
960
961            reader.readEnd(SRU_DIAGNOSIC_NS, "diagnostic");
962
963            logger.debug("diagostic: uri={}, detail={}, message={}",
964                    new Object[] { uri, details, message });
965            return new SRUDiagnostic(uri, details, message);
966        } else {
967            return null;
968        }
969    }
970
971
972    private static SRURecordPacking parseRecordPacking(
973            SRUXMLStreamReader reader, boolean pedantic)
974            throws XMLStreamException, SRUClientException {
975        final String v = reader.readContent(SRU_NS, "recordPacking", true);
976
977        if (RECORD_PACKING_XML.equals(v)) {
978            return SRURecordPacking.XML;
979        } else if (RECORD_PACKING_STRING.equals(v)) {
980            return SRURecordPacking.STRING;
981        } else if (!pedantic && RECORD_PACKING_XML.equalsIgnoreCase(v)) {
982            logger.error("invalid value '{}' for record packing, should be '{}'",
983                         v, RECORD_PACKING_XML);
984            return SRURecordPacking.XML;
985        } else if (!pedantic && RECORD_PACKING_STRING.equalsIgnoreCase(v)) {
986            logger.error("invalid value '{}' for record packing, should be '{}'",
987                         v, RECORD_PACKING_STRING);
988            return SRURecordPacking.STRING;
989
990        } else {
991            throw new SRUClientException("invalid value '" + v +
992                    "' for record packing (valid values are: '" +
993                    RECORD_PACKING_XML + "' and '" + RECORD_PACKING_STRING +
994                    "')");
995        }
996    }
997
998
999    private SRURecordDataParser findParser(String schema) {
1000        SRURecordDataParser parser = parsers.get(schema);
1001        if (parser == null) {
1002            parser = parsers.get(RECORD_DATA_PARSER_SCHEMA_ANY);
1003        }
1004        return parser;
1005    }
1006
1007
1008    private SRUXMLStreamReader createReader(InputStream in, boolean wrap)
1009            throws XMLStreamException {
1010        return new SRUXMLStreamReader(in, wrap);
1011    }
1012
1013} // class SRUClient
Note: See TracBrowser for help on using the repository browser.