source: SRUServer/tags/SRUServer-1.4.1/src/main/java/eu/clarin/sru/server/SRUServer.java @ 2691

Last change on this file since 2691 was 2691, checked in by oschonef, 11 years ago
  • tag version 1.4.1
  • Property svn:eol-style set to native
File size: 41.0 KB
Line 
1/**
2 * This software is copyright (c) 2011-2013 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.server;
18
19import java.io.IOException;
20import java.io.OutputStream;
21import java.util.Arrays;
22import java.util.List;
23import java.util.NoSuchElementException;
24
25import javax.servlet.http.HttpServletRequest;
26import javax.servlet.http.HttpServletResponse;
27import javax.xml.stream.XMLOutputFactory;
28import javax.xml.stream.XMLStreamException;
29import javax.xml.stream.XMLStreamWriter;
30
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33import org.z3950.zing.cql.CQLNode;
34
35import eu.clarin.sru.server.SRUServerConfig.DatabaseInfo;
36import eu.clarin.sru.server.SRUServerConfig.IndexInfo;
37import eu.clarin.sru.server.SRUServerConfig.LocalizedString;
38import eu.clarin.sru.server.SRUServerConfig.SchemaInfo;
39import eu.clarin.sru.server.utils.SRUServerServlet;
40
41
42/**
43 * SRU/CQL protocol implementation for the server-side (SRU/S). This class
44 * implements SRU/CQL version 1.1 and and 1.2.
45 *
46 * @see SRUServerConfig
47 * @see SRUSearchEngine
48 * @see SRUServerServlet
49 * @see <a href="http://www.loc.gov/standards/sru/">SRU/CQL protocol 1.2</a>
50 */
51public final class SRUServer {
52    private static final String SRU_NS =
53            "http://www.loc.gov/zing/srw/";
54    private static final String SRU_PREFIX = "sru";
55    private static final String SRU_DIAGNOSIC_NS =
56            "http://www.loc.gov/zing/srw/diagnostic/";
57    private static final String SRU_DIAGNOSTIC_PREFIX = "diag";
58    private static final String SRU_DIAGNOSTIC_RECORD_SCHEMA =
59            "info:srw/schema/1/diagnostics-v1.1";
60    private static final String SRU_EXPLAIN_NS =
61            "http://explain.z3950.org/dtd/2.0/";
62    private static final String SRU_EXPLAIN_PREFIX = "zr";
63    private static final String SRU_XCQL_NS =
64            "http://www.loc.gov/zing/cql/xcql/";
65    static final String RESPONSE_ENCODING = "utf-8";
66    private static final String RESPONSE_CONTENT_TYPE = "application/xml";
67    private static final int RESPONSE_BUFFER_SIZE = 64 * 1024;
68    private static final Logger logger =
69            LoggerFactory.getLogger(SRUServer.class);
70    private final SRUServerConfig config;
71    private final SRUSearchEngine searchEngine;
72    private final XMLOutputFactory writerFactory;
73
74
75    /**
76     * Constructor.
77     *
78     * @param config
79     *            a SRUEndpointConfig object
80     * @param searchEngine
81     *            an object implementing the SRUSearchEngine interface
82     * @throws NullPointerException
83     *             if config or searchEngine is <code>null</code>
84     * @throws SRUException
85     *             if an error occurred
86     */
87    public SRUServer(SRUServerConfig config, SRUSearchEngine searchEngine)
88            throws SRUException {
89        if (config == null) {
90            throw new NullPointerException("config == null");
91        }
92        this.config = config;
93        if (searchEngine == null) {
94            throw new NullPointerException("searchEngine == null");
95        }
96        this.searchEngine = searchEngine;
97        this.writerFactory = XMLOutputFactory.newInstance();
98    }
99
100
101    /**
102     * Handle a SRU request.
103     *
104     * @param request
105     *            a HttpServletRequest request
106     * @param response
107     *            a HttpServletResponse request
108     */
109    public void handleRequest(HttpServletRequest request,
110            HttpServletResponse response) {
111        final SRURequestImpl req = new SRURequestImpl(config, request);
112        try {
113            // set response properties
114            response.setContentType(RESPONSE_CONTENT_TYPE);
115            response.setCharacterEncoding(RESPONSE_ENCODING);
116            response.setStatus(HttpServletResponse.SC_OK);
117            // make sure we can reset the stream later in case of error ...
118            response.setBufferSize(RESPONSE_BUFFER_SIZE);
119
120            try {
121                if (req.checkParameters()) {
122                    switch (req.getOperation()) {
123                    case EXPLAIN:
124                        explain(req, response);
125                        break;
126                    case SCAN:
127                        scan(req, response);
128                        break;
129                    case SEARCH_RETRIEVE:
130                        search(req, response);
131                        break;
132                    }
133                } else {
134                    // (some) parameters are malformed, send error
135                    SRUXMLStreamWriter out =
136                        createXMLStreamWriter(response.getOutputStream(),
137                                SRURecordPacking.XML, req.getIndentResponse());
138                    writeFatalError(out, req, req.getDiagnostics());
139                }
140            } catch (XMLStreamException e) {
141                logger.error("An error occurred while serializing response", e);
142                throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
143                        "An error occurred while serializing response.", e);
144            } catch (IOException e) {
145                /*
146                 * Well, can't really do anything useful here ...
147                 */
148                logger.error("An unexpected exception occurred", e);
149            }
150        } catch (SRUException e) {
151            if (!response.isCommitted()) {
152                response.resetBuffer();
153                try {
154                    List<SRUDiagnostic> diagnostics = req.getDiagnostics();
155                    if (diagnostics != null) {
156                        diagnostics.add(e.getDiagnostic());
157                    } else {
158                        diagnostics = Arrays.asList(e.getDiagnostic());
159                    }
160                    SRUXMLStreamWriter out =
161                            createXMLStreamWriter(response.getOutputStream(),
162                                    SRURecordPacking.XML,
163                                    req.getIndentResponse());
164                    writeFatalError(out, req, diagnostics);
165                } catch (Exception ex) {
166                    logger.error("An exception occurred while in error state",
167                            ex);
168                }
169            } else {
170                logger.error("A fatal error occurred, but the response was "
171                        + "already committed", e);
172            }
173        }
174    }
175
176
177    private void explain(SRURequestImpl request, HttpServletResponse response)
178            throws IOException, XMLStreamException, SRUException {
179        logger.info("explain");
180
181        // commence explain ...
182        final SRUExplainResult result =
183                searchEngine.explain(config, request, request);
184
185        try {
186            // send results
187            SRUXMLStreamWriter out =
188                    createXMLStreamWriter(response.getOutputStream(),
189                                          request.getRecordPacking(),
190                                          request.getIndentResponse());
191
192            beginResponse(out, request);
193
194            // write the explain record
195            writeExplainRecord(out, request);
196
197            if (config.getEchoRequests()) {
198                writeEchoedExplainRequest(out, request);
199            }
200
201            // diagnostics
202            writeDiagnosticList(out, request.getDiagnostics());
203
204            // extraResponseData
205            if (result != null) {
206                if (result.hasExtraResponseData()) {
207                    out.writeStartElement(SRU_NS, "extraResponseData");
208                    result.writeExtraResponseData(out);
209                    out.writeEndElement(); // "extraResponseData" element
210                }
211            }
212
213            endResponse(out);
214        } finally {
215            if (result != null) {
216                result.close();
217            }
218        }
219    }
220
221
222    private void scan(SRURequestImpl request, HttpServletResponse response)
223            throws IOException, XMLStreamException, SRUException {
224        logger.info("scan: scanClause = \"{}\"",
225                new Object[] { request.getRawScanClause() });
226
227        // commence scan
228        final SRUScanResultSet result =
229                searchEngine.scan(config, request, request);
230        if (result == null) {
231            throw new SRUException(SRUConstants.SRU_UNSUPPORTED_OPERATION,
232                    "The 'scan' operation is not supported by this endpoint.");
233        }
234
235        try {
236            // send results
237            SRUXMLStreamWriter out =
238                    createXMLStreamWriter(response.getOutputStream(),
239                                          request.getRecordPacking(),
240                                          request.getIndentResponse());
241
242            beginResponse(out, request);
243
244            try {
245                /*
246                 * a scan result without a list of terms is a valid response;
247                 * make sure, to produce the correct output and omit in that case
248                 * the <terms> ...
249                 */
250                boolean wroteTerms = false;
251                while (result.nextTerm()) {
252                    if (!wroteTerms) {
253                        out.writeStartElement(SRU_NS, "terms");
254                        wroteTerms = true;
255                    }
256                    out.writeStartElement(SRU_NS, "term");
257
258                    out.writeStartElement(SRU_NS, "value");
259                    out.writeCharacters(result.getValue());
260                    out.writeEndElement(); // "value" element
261
262                    if (result.getNumberOfRecords() > -1) {
263                        out.writeStartElement(SRU_NS, "numberOfRecords");
264                        out.writeCharacters(
265                                Integer.toString(result.getNumberOfRecords()));
266                        out.writeEndElement(); // "numberOfRecords" element
267                    }
268
269                    if (result.getDisplayTerm() != null) {
270                        out.writeStartElement(SRU_NS, "displayTerm");
271                        out.writeCharacters(result.getDisplayTerm());
272                        out.writeEndElement(); // "displayTerm" element
273                    }
274
275                    if (result.getWhereInList() != null) {
276                        out.writeStartElement(SRU_NS, "whereInList");
277                        switch (result.getWhereInList()) {
278                        case FIRST:
279                            out.writeCharacters("first");
280                            break;
281                        case LAST:
282                            out.writeCharacters("last");
283                            break;
284                        case ONLY:
285                            out.writeCharacters("only");
286                            break;
287                        case INNER:
288                            out.writeCharacters("inner");
289                            break;
290                        } // switch
291                        out.writeEndElement(); // "whereInList" element
292                    }
293
294                    if (result.hasExtraTermData()) {
295                        out.writeStartElement(SRU_NS, "extraTermData");
296                        result.writeExtraTermData(out);
297                        out.writeEndElement(); // "extraTermData" element
298                    }
299
300                    out.writeEndElement(); // "term" element
301                } // while
302                if (wroteTerms) {
303                    out.writeEndElement(); // "terms" element
304                }
305            } catch (NoSuchElementException e) {
306                throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
307                        "An internal error occurred while "
308                                + "serializing scan results.");
309            }
310
311            // echoedScanRequest
312            if (config.getEchoRequests()) {
313                writeEchoedScanRequest(out, request, request.getScanClause());
314            }
315
316            // diagnostics
317            writeDiagnosticList(out, request.getDiagnostics());
318
319            // extraResponseData
320            if (result.hasExtraResponseData()) {
321                out.writeStartElement(SRU_NS, "extraResponseData");
322                result.writeExtraResponseData(out);
323                out.writeEndElement(); // "extraResponseData" element
324            }
325
326            endResponse(out);
327        } finally {
328            if (result != null) {
329                result.close();
330            }
331        }
332    }
333
334
335    private void search(SRURequestImpl request, HttpServletResponse response)
336            throws IOException, XMLStreamException, SRUException {
337        logger.info("searchRetrieve: query = \"{}\", startRecord = {}, " +
338                "maximumRecords = {}, recordSchema = {}, resultSetTTL = {}",
339                new Object[] { request.getRawQuery(), request.getStartRecord(),
340                        request.getMaximumRecords(),
341                        request.getRecordSchemaIdentifier(),
342                        request.getResultSetTTL() });
343
344        // commence search ...
345        final SRUSearchResultSet result =
346                searchEngine.search(config, request, request);
347        if (result == null) {
348            throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
349                    "SRUSearchEngine implementation returned invalid result (null).");
350        }
351
352
353        // check, of startRecord position is greater than total record set
354        if ((result.getTotalRecordCount() >= 0) &&
355            (request.getRawStartRecord() > 0) &&
356            (request.getRawStartRecord() > result.getTotalRecordCount())) {
357            throw new SRUException(
358                    SRUConstants.SRU_FIRST_RECORD_POSITION_OUT_OF_RANGE);
359        }
360
361        try {
362            // send results
363            SRUXMLStreamWriter out =
364                    createXMLStreamWriter(response.getOutputStream(),
365                                          request.getRecordPacking(),
366                                          request.getIndentResponse());
367
368            beginResponse(out, request);
369
370            // numberOfRecords
371            out.writeStartElement(SRU_NS, "numberOfRecords");
372            out.writeCharacters(
373                    Integer.toString(result.getTotalRecordCount()));
374            out.writeEndElement(); // "numberOfRecords" element
375
376            // resultSetId
377            if (result.getResultSetId() != null) {
378                out.writeStartElement(SRU_NS, "resultSetId");
379                out.writeCharacters(result.getResultSetId());
380                out.writeEndElement(); // "resultSetId" element
381            }
382
383            // resultSetIdleTime
384            if (result.getResultSetIdleTime() > 0) {
385                out.writeStartElement(SRU_NS, "resultSetIdleTime");
386                out.writeCharacters(Integer.toString(result
387                        .getResultSetIdleTime()));
388                out.writeEndElement(); // "resultSetIdleTime" element
389            }
390
391            int position = (request.getStartRecord() > 0)
392                    ? request.getStartRecord() : 1;
393            if (result.getRecordCount() > 0) {
394                final int maxPositionOffset =
395                        (request.getMaximumRecords() != -1)
396                        ? (position + request.getMaximumRecords() - 1)
397                        : -1;
398                try {
399                    out.writeStartElement(SRU_NS, "records");
400                    while (result.nextRecord()) {
401                        /*
402                         * Sanity check: do not return more then the maximum
403                         * requested records. If the search engine
404                         * implementation does not honor limit truncate the
405                         * result set.
406                         */
407                        if ((maxPositionOffset != -1) &&
408                                (position > maxPositionOffset)) {
409                            logger.error("SRUSearchEngine implementation did " +
410                                    "not honor limit for the amount of " +
411                                    "requsted records. Result set truncated!");
412                            break;
413                        }
414
415                        out.writeStartElement(SRU_NS, "record");
416
417                        /*
418                         * We need to output either the record or a surrogate
419                         * diagnostic. In case of the latter, we need to output
420                         * the appropriate record schema ...
421                         */
422                        final SRUDiagnostic diagnostic =
423                                result.getSurrogateDiagnostic();
424
425                        out.writeStartElement(SRU_NS, "recordSchema");
426                        if (diagnostic == null) {
427                            out.writeCharacters(
428                                    result.getRecordSchemaIdentifier());
429                        } else {
430                            out.writeCharacters(SRU_DIAGNOSTIC_RECORD_SCHEMA);
431                        }
432                        out.writeEndElement(); // "recordSchema" element
433
434                        // recordPacking
435                        writeRecordPacking(out, request.getRecordPacking());
436
437                        /*
438                         * Output either record data or surrogate diagnostic ...
439                         */
440                        out.writeStartElement(SRU_NS, "recordData");
441                        out.startRecord();
442                        if (diagnostic == null) {
443                            result.writeRecord(out);
444                        } else {
445                            // write a surrogate diagnostic
446                            writeDiagnostic(out, diagnostic, true);
447                        }
448                        out.endRecord();
449                        out.writeEndElement(); // "recordData" element
450
451                        /*
452                         * recordIdentifier is version 1.2 only
453                         */
454                        if (request.isVersion(SRUVersion.VERSION_1_2)) {
455                            final String identifier =
456                                    result.getRecordIdentifier();
457                            if (identifier != null) {
458                                out.writeStartElement(SRU_NS,
459                                                      "recordIdentifier");
460                                out.writeCharacters(identifier);
461                                out.writeEndElement(); // "recordIdentifier" element
462                            }
463                        }
464
465                        out.writeStartElement(SRU_NS, "recordPosition");
466                        out.writeCharacters(Integer.toString(position));
467                        out.writeEndElement(); // "recordPosition" element
468
469                        if (result.hasExtraRecordData()) {
470                            out.writeStartElement(SRU_NS, "extraRecordData");
471                            result.writeExtraRecordData(out);
472                            out.writeEndElement(); // "extraRecordData"
473                        }
474
475                        out.writeEndElement(); // "record" element
476
477                        position++;
478                    } // while
479                    out.writeEndElement(); // "records" element
480                } catch (NoSuchElementException e) {
481                    throw new SRUException(
482                            SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
483                            "An internal error occurred while " +
484                            "serializing search result set.");
485                }
486            }
487
488            // nextRecordPosition
489            if (position <= result.getTotalRecordCount()) {
490                out.writeStartElement(SRU_NS, "nextRecordPosition");
491                out.writeCharacters(Integer.toString(position));
492                out.writeEndElement();
493            }
494
495            // echoedSearchRetrieveRequest
496            if (config.getEchoRequests()) {
497                writeEchoedSearchRetrieveRequest(out, request,
498                                                 request.getQuery());
499            }
500
501            // diagnostics
502            writeDiagnosticList(out, request.getDiagnostics());
503
504            // extraResponseData
505            if (result.hasExtraResponseData()) {
506                out.writeStartElement(SRU_NS, "extraResponseData");
507                result.writeExtraResponseData(out);
508                out.writeEndElement(); // "extraResponseData" element
509            }
510
511            endResponse(out);
512        } finally {
513            result.close();
514        }
515    }
516
517
518    private void beginResponse(SRUXMLStreamWriter out, SRUOperation operation,
519            SRUVersion version, String stylesheet) throws XMLStreamException {
520        out.writeStartDocument("utf-8", "1.0");
521
522        if (stylesheet != null) {
523            StringBuilder param = new StringBuilder();
524            param.append("type=\"text/xsl\"");
525            param.append(" ");
526            param.append("href=\"");
527            param.append(stylesheet);
528            param.append("\"");
529            out.writeProcessingInstruction("xml-stylesheet", param.toString());
530        }
531
532        out.setPrefix(SRU_PREFIX, SRU_NS);
533        switch (operation) {
534        case EXPLAIN:
535            out.writeStartElement(SRU_NS, "explainResponse");
536            break;
537        case SCAN:
538            out.writeStartElement(SRU_NS, "scanResponse");
539            break;
540        case SEARCH_RETRIEVE:
541            out.writeStartElement(SRU_NS, "searchRetrieveResponse");
542            break;
543        }
544        out.writeNamespace(SRU_PREFIX, SRU_NS);
545
546        // version
547        writeVersion(out, version);
548    }
549
550
551    private void beginResponse(SRUXMLStreamWriter out, SRURequest request)
552            throws XMLStreamException {
553        beginResponse(out, request.getOperation(), request.getVersion(),
554                request.getStylesheet());
555    }
556
557
558    private void endResponse(SRUXMLStreamWriter out)
559            throws XMLStreamException {
560        out.writeEndElement(); // "root" element
561
562        out.writeEndDocument();
563        out.close();
564        try {
565            out.getWriter().close();
566        } catch (IOException e) {
567            /* IGNORE */
568        }
569    }
570
571
572    private void writeFatalError(SRUXMLStreamWriter out,
573            SRURequestImpl request, List<SRUDiagnostic> diagnotics)
574            throws XMLStreamException {
575        /*
576         * if operation is unknown, default to 'explain'
577         */
578        SRUOperation operation = request.getOperation();
579        if (operation == null) {
580            operation = SRUOperation.EXPLAIN;
581        }
582        SRUVersion version = request.getVersion();
583        if (version == null) {
584            version = config.getDefaultVersion();
585        }
586        /*
587         * write a response which conforms to the schema
588         */
589        beginResponse(out, operation, version, null);
590        switch (operation) {
591        case EXPLAIN:
592            // 'explain' requires a complete explain record ...
593            writeExplainRecord(out, request);
594            break;
595        case SCAN:
596            // 'scan' fortunately does not need any elements ...
597            break;
598        case SEARCH_RETRIEVE:
599            // 'searchRetrieve' needs numberOfRecords ..
600            out.writeStartElement(SRU_NS, "numberOfRecords");
601            out.writeCharacters("0");
602            out.writeEndElement(); // "numberOfRecords" element
603            break;
604        }
605        writeDiagnosticList(out, diagnotics);
606        endResponse(out);
607    }
608
609
610    private void writeDiagnosticList(SRUXMLStreamWriter out,
611            List<SRUDiagnostic> diagnostics) throws XMLStreamException {
612        if ((diagnostics != null) && !diagnostics.isEmpty()) {
613            out.setPrefix(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
614            out.writeStartElement(SRU_NS, "diagnostics");
615            out.writeNamespace(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
616            for (SRUDiagnostic diagnostic : diagnostics) {
617                writeDiagnostic(out, diagnostic, false);
618            }
619            out.writeEndElement(); // "diagnostics" element
620        }
621    }
622
623
624    private void writeExplainRecord(SRUXMLStreamWriter out,
625            SRURequestImpl request) throws XMLStreamException {
626        out.writeStartElement(SRU_NS, "record");
627
628        out.writeStartElement(SRU_NS, "recordSchema");
629        out.writeCharacters(SRU_EXPLAIN_NS);
630        out.writeEndElement(); // "recordSchema" element
631
632        // recordPacking
633        writeRecordPacking(out, request.getRecordPacking());
634
635        out.writeStartElement(SRU_NS, "recordData");
636
637        out.startRecord();
638
639        // explain ...
640        out.setPrefix(SRU_EXPLAIN_PREFIX, SRU_EXPLAIN_NS);
641        out.writeStartElement(SRU_EXPLAIN_NS, "explain");
642        out.writeNamespace(SRU_EXPLAIN_PREFIX, SRU_EXPLAIN_NS);
643
644        // explain/serverInfo
645        out.writeStartElement(SRU_EXPLAIN_NS, "serverInfo");
646        out.writeAttribute("protocol", "SRU");
647        switch (config.getDefaultVersion()) {
648        case VERSION_1_1:
649            out.writeAttribute("version", "1.1");
650            break;
651        case VERSION_1_2:
652            out.writeAttribute("version", "1.2");
653        } // switch
654        out.writeAttribute("transport", config.getTransports());
655        out.writeStartElement(SRU_EXPLAIN_NS, "host");
656        out.writeCharacters(config.getHost());
657        out.writeEndElement(); // "host" element
658        out.writeStartElement(SRU_EXPLAIN_NS, "port");
659        out.writeCharacters(Integer.toString(config.getPort()));
660        out.writeEndElement(); // "port" element
661        out.writeStartElement(SRU_EXPLAIN_NS, "database");
662        out.writeCharacters(config.getDatabase());
663        out.writeEndElement(); // "database" element
664        out.writeEndElement(); // "serverInfo" element
665
666        // explain/databaseInfo
667        final DatabaseInfo dbinfo = config.getDatabaseInfo();
668        if (dbinfo != null) {
669            out.writeStartElement(SRU_EXPLAIN_NS, "databaseInfo");
670            writeLocalizedStrings(out, "title", dbinfo.getTitle());
671            writeLocalizedStrings(out, "description", dbinfo.getDescription());
672            writeLocalizedStrings(out, "author", dbinfo.getAuthor());
673            writeLocalizedStrings(out, "extent", dbinfo.getExtend());
674            writeLocalizedStrings(out, "history", dbinfo.getHistory());
675            writeLocalizedStrings(out, "langUsage", dbinfo.getLangUsage());
676            writeLocalizedStrings(out, "restrictions", dbinfo.getRestrictions());
677            writeLocalizedStrings(out, "subjects", dbinfo.getSubjects());
678            writeLocalizedStrings(out, "links", dbinfo.getLinks());
679            writeLocalizedStrings(out, "implementation",
680                    dbinfo.getImplementation());
681            out.writeEndElement(); // "databaseInfo" element
682        }
683
684        // explain/indexInfo
685        final IndexInfo indexInfo = config.getIndexInfo();
686        if (indexInfo != null) {
687            out.writeStartElement(SRU_EXPLAIN_NS, "indexInfo");
688
689            List<IndexInfo.Set> sets = indexInfo.getSets();
690            if (sets != null) {
691                for (IndexInfo.Set set : sets) {
692                    out.writeStartElement(SRU_EXPLAIN_NS, "set");
693                    out.writeAttribute("identifier", set.getIdentifier());
694                    out.writeAttribute("name", set.getName());
695                    writeLocalizedStrings(out, "title", set.getTitle());
696                    out.writeEndElement(); // "set" element
697                }
698            }
699
700            List<IndexInfo.Index> indexes = indexInfo.getIndexes();
701            if (indexes != null) {
702                for (IndexInfo.Index index : indexes) {
703                    out.writeStartElement(SRU_EXPLAIN_NS, "index");
704                    out.writeAttribute("search",
705                            index.canSearch() ? "true" : "false");
706                    out.writeAttribute("scan",
707                            index.canScan() ? "true" : "false");
708                    out.writeAttribute("sort",
709                            index.canSort() ? "true" : "false");
710                    writeLocalizedStrings(out, "title", index.getTitle());
711                    List<IndexInfo.Index.Map> maps = index.getMaps();
712                    if (maps != null) {
713                        for (IndexInfo.Index.Map map : maps) {
714                            out.writeStartElement(SRU_EXPLAIN_NS, "map");
715                            if (map.isPrimary()) {
716                                out.writeAttribute("primary", "true");
717                            }
718                            out.writeStartElement(SRU_EXPLAIN_NS, "name");
719                            out.writeAttribute("set", map.getSet());
720                            out.writeCharacters(map.getName());
721                            out.writeEndElement(); // "name" element
722                            out.writeEndElement(); // "map" element
723                        }
724                    }
725                    out.writeEndElement(); // "index" element
726                }
727            }
728            out.writeEndElement(); // "indexInfo" element
729        }
730
731        // explain/schemaInfo
732        final List<SchemaInfo> schemaInfo =
733                config.getSchemaInfo();
734        if (schemaInfo != null) {
735            out.writeStartElement(SRU_EXPLAIN_NS, "schemaInfo");
736            for (SRUServerConfig.SchemaInfo schema : schemaInfo) {
737                out.writeStartElement(SRU_EXPLAIN_NS, "schema");
738                out.writeAttribute("identifier", schema.getIdentifier());
739                out.writeAttribute("name", schema.getName());
740                /*
741                 * default is "false", so only add attribute if set to true
742                 */
743                if (schema.getSort() ) {
744                    out.writeAttribute("sort", "true");
745                }
746                /*
747                 * default is "true", so only add attribute if set to false
748                 */
749                if (!schema.getRetrieve()) {
750                    out.writeAttribute("retrieve", "false");
751                }
752                writeLocalizedStrings(out, "title", schema.getTitle());
753                out.writeEndElement(); // "schema" element
754            }
755            out.writeEndElement(); // "schemaInfo" element
756        }
757
758        // explain/configInfo
759        out.writeStartElement(SRU_EXPLAIN_NS, "configInfo");
760        // numberOfRecords (default)
761        out.writeStartElement(SRU_EXPLAIN_NS, "default");
762        out.writeAttribute("type", "numberOfRecords");
763        out.writeCharacters(Integer.toString(config.getNumberOfRecords()));
764        out.writeEndElement(); // default" element
765
766        // maximumRecords (setting)
767        out.writeStartElement(SRU_EXPLAIN_NS, "setting");
768        out.writeAttribute("type", "maximumRecords");
769        out.writeCharacters(Integer.toString(config.getMaximumRecords()));
770        out.writeEndElement(); // "setting" element
771
772        out.writeEndElement(); // "configInfo" element
773
774        out.writeEndElement(); // "explain" element
775
776        out.endRecord();
777
778        out.writeEndElement(); // "recordData" element
779        out.writeEndElement(); // "record" element
780    }
781
782
783    private void writeDiagnostic(SRUXMLStreamWriter out,
784            SRUDiagnostic diagnostic, boolean writeNsDecl)
785            throws XMLStreamException {
786        if (writeNsDecl) {
787            out.setPrefix(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
788        }
789        out.writeStartElement(SRU_DIAGNOSIC_NS, "diagnostic");
790        if (writeNsDecl) {
791            out.writeNamespace(SRU_DIAGNOSTIC_PREFIX, SRU_DIAGNOSIC_NS);
792        }
793        out.writeStartElement(SRU_DIAGNOSIC_NS, "uri");
794        out.writeCharacters(SRUConstants.SRU_DIAGNOSTIC_URI_PREFIX);
795        out.writeCharacters(Integer.toString(diagnostic.getCode()));
796        out.writeEndElement(); // "uri" element
797        if (diagnostic.getDetails() != null) {
798            out.writeStartElement(SRU_DIAGNOSIC_NS, "details");
799            out.writeCharacters(diagnostic.getDetails());
800            out.writeEndElement(); // "details" element
801        }
802        if (diagnostic.getMessage() != null) {
803            out.writeStartElement(SRU_DIAGNOSIC_NS, "message");
804            out.writeCharacters(diagnostic.getMessage());
805            out.writeEndElement(); // "message" element
806        }
807        out.writeEndElement(); // "diagnostic" element
808    }
809
810
811    private void writeEchoedExplainRequest(SRUXMLStreamWriter out,
812            SRURequestImpl request) throws XMLStreamException,
813            SRUException {
814        // echoedSearchRetrieveRequest
815        out.writeStartElement(SRU_NS, "echoedExplainRequest");
816
817        // echoedExplainRequest/version
818        if (request.getRawVersion() != null) {
819            writeVersion(out, request.getRawVersion());
820        }
821
822        // echoedExplainRequest/recordPacking
823        if (request.getRawRecordPacking() != null) {
824            writeRecordPacking(out, request.getRawRecordPacking());
825        }
826
827        // echoedExplainRequest/stylesheet
828        if (request.getStylesheet() != null) {
829            out.writeStartElement(SRU_NS, "stylesheet");
830            out.writeCharacters(request.getStylesheet());
831            out.writeEndElement(); // "stylesheet" element
832        }
833
834        // echoedExplainRequest/baseUrl (SRU 1.2 only)
835        if (request.isVersion(SRUVersion.VERSION_1_2)) {
836            writeBaseUrl(out, request);
837        }
838
839        out.writeEndElement(); // "echoedExplainRequest" element
840    }
841
842
843    private void writeEchoedScanRequest(SRUXMLStreamWriter out,
844            SRURequestImpl request, CQLNode cql) throws XMLStreamException,
845            SRUException {
846        // echoedScanRequest
847        out.writeStartElement(SRU_NS, "echoedScanRequest");
848
849        // echoedScanRequest/version
850        if (request.getRawVersion() != null) {
851            writeVersion(out, request.getRawVersion());
852        }
853
854        // echoedScanRequest/scanClause
855        out.writeStartElement(SRU_NS, "scanClause");
856        out.writeCharacters(request.getRawScanClause());
857        out.writeEndElement(); // "query"
858
859        // echoedScanRequest/xScanClause
860        out.setDefaultNamespace(SRU_XCQL_NS);
861        out.writeStartElement(SRU_NS, "xScanClause");
862        out.writeDefaultNamespace(SRU_XCQL_NS);
863        out.writeXCQL(cql, false);
864        out.writeEndElement(); // "xScanClause" element
865
866        // echoedScanRequest/responsePosition
867        if (request.getResponsePosition() != -1) {
868            out.writeStartElement(SRU_NS, "responsePosition");
869            out.writeCharacters(
870                    Integer.toString(request.getResponsePosition()));
871            out.writeEndElement(); // "responsePosition" element
872        }
873
874        // echoedScanRequest/maximumTerms
875        if (request.getMaximumTerms() != -1) {
876            out.writeStartElement(SRU_NS, "maximumTerms");
877            out.writeCharacters(Integer.toString(request.getMaximumTerms()));
878            out.writeEndElement(); // "maximumTerms" element
879        }
880
881        // echoedScanRequest/stylesheet
882        if (request.getStylesheet() != null) {
883            out.writeStartElement(SRU_NS, "stylesheet");
884            out.writeCharacters(request.getStylesheet());
885            out.writeEndElement(); // "stylesheet" element
886        }
887
888        // echoedScanRequest/baseUrl (SRU 1.2 only)
889        if (request.isVersion(SRUVersion.VERSION_1_2)) {
890            writeBaseUrl(out, request);
891        }
892
893        out.writeEndElement(); // "echoedScanRequest" element
894    }
895
896
897    private void writeEchoedSearchRetrieveRequest(SRUXMLStreamWriter out,
898            SRURequestImpl request, CQLNode cql) throws XMLStreamException,
899            SRUException {
900        // echoedSearchRetrieveRequest
901        out.writeStartElement(SRU_NS, "echoedSearchRetrieveRequest");
902
903        // echoedSearchRetrieveRequest/version
904        if (request.getRawVersion() != null) {
905            writeVersion(out, request.getRawVersion());
906        }
907
908        // echoedSearchRetrieveRequest/query
909        out.writeStartElement(SRU_NS, "query");
910        out.writeCharacters(request.getRawQuery());
911        out.writeEndElement(); // "query"
912
913        // echoedSearchRetrieveRequest/xQuery
914        out.setDefaultNamespace(SRU_XCQL_NS);
915        out.writeStartElement(SRU_NS, "xQuery");
916        out.writeDefaultNamespace(SRU_XCQL_NS);
917        out.writeXCQL(cql, true);
918        out.writeEndElement(); // "xQuery" element
919
920        // echoedSearchRetrieveRequest/startRecord
921        if (request.getStartRecord() > 0) {
922            out.writeStartElement(SRU_NS, "startRecord");
923            out.writeCharacters(Integer.toString(request.getStartRecord()));
924            out.writeEndElement(); // "startRecord" element
925        }
926
927        // echoedSearchRetrieveRequest/maximumRecords
928        if (request.getRawMaximumRecords() > 0) {
929            out.writeStartElement(SRU_NS, "maximumRecords");
930            out.writeCharacters(
931                    Integer.toString(request.getRawMaximumRecords()));
932            out.writeEndElement(); // "startRecord" element
933        }
934
935        // echoedSearchRetrieveRequest/recordPacking
936        if (request.getRawRecordPacking() != null) {
937            writeRecordPacking(out, request.getRawRecordPacking());
938        }
939
940        // echoedSearchRetrieveRequest/recordSchema
941        if (request.getRecordSchemaName() != null) {
942            out.writeStartElement(SRU_NS, "recordSchema");
943            out.writeCharacters(request.getRecordSchemaName());
944            out.writeEndElement(); // "recordSchema" element
945        }
946
947        // echoedSearchRetrieveRequest/recordXPath (1.1)
948        if (request.isVersion(SRUVersion.VERSION_1_1) &&
949                (request.getRecordXPath() != null)) {
950            out.writeStartElement(SRU_NS, "recordXPath");
951            out.writeCharacters(request.getRecordXPath());
952            out.writeEndElement(); // "recordXPath" element
953        }
954
955        // echoedSearchRetrieveRequest/resultSetTTL
956        if (request.getResultSetTTL() > 0) {
957            out.writeStartElement(SRU_NS, "resultSetTTL");
958            out.writeCharacters(Long.toString(request.getResultSetTTL()));
959            out.writeEndElement(); // "resultSetTTL" element
960        }
961
962        // echoedSearchRetrieveRequest/sortKeys
963        if (request.isVersion(SRUVersion.VERSION_1_1) &&
964                (request.getSortKeys() != null)) {
965            out.writeStartElement(SRU_NS, "sortKeys");
966            out.writeCharacters(request.getSortKeys());
967            out.writeEndElement(); // "sortKeys" element
968        }
969
970        // echoedSearchRetrieveRequest/xsortKeys
971
972        // echoedSearchRetrieveRequest/stylesheet
973        if (request.getStylesheet() != null) {
974            out.writeStartElement(SRU_NS, "stylesheet");
975            out.writeCharacters(request.getStylesheet());
976            out.writeEndElement(); // "stylesheet" element
977        }
978
979        // echoedSearchRetrieveRequest/baseUrl (SRU 1.2 only)
980        if (request.isVersion(SRUVersion.VERSION_1_2)) {
981            writeBaseUrl(out, request);
982        }
983
984        out.writeEndElement(); // "echoedSearchRetrieveRequest" element
985    }
986
987
988    private void writeVersion(SRUXMLStreamWriter out, SRUVersion version)
989            throws XMLStreamException {
990        out.writeStartElement(SRU_NS, "version");
991        switch (version) {
992        case VERSION_1_1:
993            out.writeCharacters("1.1");
994            break;
995        case VERSION_1_2:
996            out.writeCharacters("1.2");
997            break;
998        } // switch
999        out.writeEndElement(); // "version" element
1000    }
1001
1002
1003    private void writeRecordPacking(SRUXMLStreamWriter out,
1004            SRURecordPacking recordPacking) throws XMLStreamException {
1005        out.writeStartElement(SRU_NS, "recordPacking");
1006        switch (recordPacking) {
1007        case XML:
1008            out.writeCharacters("xml");
1009            break;
1010        case STRING:
1011            out.writeCharacters("string");
1012            break;
1013        } // switch
1014        out.writeEndElement(); // "recordPacking" element
1015    }
1016
1017
1018    private void writeBaseUrl(SRUXMLStreamWriter out,
1019            SRURequest request) throws XMLStreamException {
1020        out.writeStartElement(SRU_NS, "baseUrl");
1021        out.writeCharacters(request.getProtocolScheme());
1022        out.writeCharacters(config.getBaseUrl());
1023        out.writeEndElement(); // "baseUrl" element
1024    }
1025
1026
1027    private void writeLocalizedStrings(XMLStreamWriter writer, String name,
1028            List<LocalizedString> list) throws XMLStreamException {
1029        if ((list != null) && !list.isEmpty()) {
1030            for (LocalizedString item : list) {
1031                writer.writeStartElement(SRU_EXPLAIN_NS, name);
1032                if (item.getLang() != null) {
1033                    writer.writeAttribute("lang", item.getLang());
1034                }
1035                if (item.isPrimary()) {
1036                    writer.writeAttribute("primary", "true");
1037                }
1038                writer.writeCharacters(item.getValue());
1039                writer.writeEndElement();
1040            }
1041        }
1042    }
1043
1044
1045    private SRUXMLStreamWriter createXMLStreamWriter(OutputStream out,
1046            SRURecordPacking recordPacking, int indent) throws SRUException {
1047        try {
1048            return new SRUXMLStreamWriter(out, writerFactory, recordPacking,
1049                    indent);
1050        } catch (Exception e) {
1051            throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
1052                    "Error creating output stream.", e);
1053
1054        }
1055    }
1056
1057} // class SRUService
Note: See TracBrowser for help on using the repository browser.