source: SRUServer/trunk/src/main/java/eu/clarin/sru/server/SRUServer.java @ 6825

Last change on this file since 6825 was 6825, checked in by Oliver Schonefeld, 9 years ago
  • add SRUQueryParserFactory and expose interface to register query parsers
  • Property svn:eol-style set to native
File size: 54.8 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.FilterOutputStream;
20import java.io.IOException;
21import java.io.OutputStream;
22import java.util.Arrays;
23import java.util.List;
24import java.util.NoSuchElementException;
25
26import javax.servlet.http.HttpServletRequest;
27import javax.servlet.http.HttpServletResponse;
28import javax.xml.stream.XMLOutputFactory;
29import javax.xml.stream.XMLStreamException;
30import javax.xml.stream.XMLStreamWriter;
31
32import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34import org.z3950.zing.cql.CQLNode;
35
36import eu.clarin.sru.server.SRUServerConfig.DatabaseInfo;
37import eu.clarin.sru.server.SRUServerConfig.IndexInfo;
38import eu.clarin.sru.server.SRUServerConfig.LocalizedString;
39import eu.clarin.sru.server.SRUServerConfig.SchemaInfo;
40import eu.clarin.sru.server.utils.SRUServerServlet;
41
42
43/**
44 * SRU/CQL protocol implementation for the server-side (SRU/S). This class
45 * implements SRU/CQL version 1.1 and and 1.2.
46 *
47 * @see SRUServerConfig
48 * @see SRUSearchEngine
49 * @see SRUServerServlet
50 * @see <a href="http://www.loc.gov/standards/sru/">SRU/CQL protocol 1.2</a>
51 */
52public final class SRUServer {
53    private static final String SRU_DIAGNOSTIC_RECORD_SCHEMA =
54            "info:srw/schema/1/diagnostics-v1.1";
55    static final String RESPONSE_ENCODING = "utf-8";
56    private static final String RESPONSE_CONTENT_TYPE = "application/xml";
57    private static final Logger logger =
58            LoggerFactory.getLogger(SRUServer.class);
59    private final SRUServerConfig config;
60    private final SRUQueryParserRegistry queryParsers;
61    private final SRUSearchEngine searchEngine;
62    private final XMLOutputFactory writerFactory;
63
64
65    /**
66     * Constructor.
67     *
68     * @param config
69     *            a {@link SRUServerConfig} object
70     * @param queryParsers
71     *            a {@link SRUQueryParserRegistry} object
72     * @param searchEngine
73     *            an object implementing the {@link SRUSearchEngine} interface
74     * @throws NullPointerException
75     *             if config, queryParserRegistry or searchEngine is
76     *             <code>null</code>
77     * @throws SRUException
78     *             if an error occurred
79     */
80    public SRUServer(SRUServerConfig config,
81            SRUQueryParserRegistry queryParsers,
82            SRUSearchEngine searchEngine) throws SRUException {
83        if (config == null) {
84            throw new NullPointerException("config == null");
85        }
86        this.config = config;
87
88        if (queryParsers == null) {
89            throw new NullPointerException("queryParserRegistry == null");
90        }
91        this.queryParsers = queryParsers;
92
93        if (searchEngine == null) {
94            throw new NullPointerException("searchEngine == null");
95        }
96        this.searchEngine = searchEngine;
97
98        this.writerFactory = XMLOutputFactory.newInstance();
99    }
100
101
102    /**
103     * Handle a SRU request.
104     *
105     * @param request
106     *            a HttpServletRequest request
107     * @param response
108     *            a HttpServletResponse request
109     */
110    public void handleRequest(HttpServletRequest request,
111            HttpServletResponse response) {
112        final SRURequestImpl req =
113                new SRURequestImpl(config, queryParsers, request);
114        try {
115            // set response properties
116            response.setContentType(RESPONSE_CONTENT_TYPE);
117            response.setCharacterEncoding(RESPONSE_ENCODING);
118            response.setStatus(HttpServletResponse.SC_OK);
119            // make sure we can reset the stream later in case of error ...
120            response.setBufferSize(config.getResponseBufferSize());
121            try {
122                if (req.checkParameters()) {
123                    switch (req.getOperation()) {
124                    case EXPLAIN:
125                        explain(req, response);
126                        break;
127                    case SCAN:
128                        scan(req, response);
129                        break;
130                    case SEARCH_RETRIEVE:
131                        search(req, response);
132                        break;
133                    }
134                } else {
135                    // (some) parameters are malformed, send error
136                    SRUXMLStreamWriter out =
137                        createXMLStreamWriter(response.getOutputStream(),
138                                SRURecordXmlEscaping.XML, false,
139                                req.getIndentResponse());
140                    final SRUNamespaces ns = getNamespaces(req.getVersion());
141                    writeFatalError(out, ns, req, req.getDiagnostics());
142                }
143            } catch (XMLStreamException e) {
144                logger.error("An error occurred while serializing response", e);
145                throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
146                        "An error occurred while serializing response.", e);
147            } catch (IOException e) {
148                /*
149                 * Well, can't really do anything useful here ...
150                 */
151                logger.error("An unexpected exception occurred", e);
152            }
153        } catch (SRUException e) {
154            if (!response.isCommitted()) {
155                if (logger.isInfoEnabled()) {
156                    final String message = e.getDiagnostic().getMessage();
157                    if (message != null) {
158                        logger.info("Sending fatal diagnostic '{}' with " +
159                                "message '{}'",
160                                e.getDiagnostic().getURI(),
161                                message);
162                    } else {
163                        logger.info("Sending fatal diagnostic '{}'",
164                                e.getDiagnostic().getURI());
165                    }
166                    logger.debug("Fatal diagnostic was caused by " +
167                            "this exception", e);
168                }
169                response.resetBuffer();
170                try {
171                    List<SRUDiagnostic> diagnostics = req.getDiagnostics();
172                    if (diagnostics != null) {
173                        diagnostics.add(e.getDiagnostic());
174                    } else {
175                        diagnostics = Arrays.asList(e.getDiagnostic());
176                    }
177                    SRUXMLStreamWriter out =
178                            createXMLStreamWriter(response.getOutputStream(),
179                                    SRURecordXmlEscaping.XML, false,
180                                    req.getIndentResponse());
181                    final SRUNamespaces ns = getNamespaces(req.getVersion());
182                    writeFatalError(out, ns, req, diagnostics);
183                } catch (Exception ex) {
184                    logger.error("An exception occurred while in error state",
185                            ex);
186                }
187            } else {
188                /*
189                 * The Servlet already flushed the output buffer, so cannot
190                 * degrade gracefully anymore and, unfortunately, will produce
191                 * ill-formed XML output.
192                 * Increase the response buffer size, if you want to avoid
193                 * this (at the cost of memory).
194                 */
195                logger.error("A fatal error occurred, but the response was "
196                        + "already committed. Unable to recover gracefully.", e);
197            }
198        }
199    }
200
201
202    private void explain(SRURequestImpl request, HttpServletResponse response)
203            throws IOException, XMLStreamException, SRUException {
204        logger.info("explain");
205
206        // commence explain ...
207        final SRUExplainResult result =
208                searchEngine.explain(config, request, request);
209
210        try {
211            final SRUNamespaces ns = getNamespaces(request.getVersion());
212
213            // send results
214            SRUXMLStreamWriter out =
215                    createXMLStreamWriter(response.getOutputStream(),
216                                          request.getRecordXmlEscaping(),
217                                          true,
218                                          request.getIndentResponse());
219
220            beginResponse(out, ns, request);
221
222            // write the explain record
223            writeExplainRecord(out, ns, request);
224
225            if (config.getEchoRequests()) {
226                writeEchoedExplainRequest(out, ns, request);
227            }
228
229            // diagnostics
230            writeDiagnosticList(out, ns, request.getDiagnostics());
231
232            // extraResponseData
233            if (result != null) {
234                if (result.hasExtraResponseData()) {
235                    out.writeStartElement(ns.getResponseNS(), "extraResponseData");
236                    result.writeExtraResponseData(out);
237                    out.writeEndElement(); // "extraResponseData" element
238                }
239            }
240
241            endResponse(out);
242        } finally {
243            if (result != null) {
244                result.close();
245            }
246        }
247    }
248
249
250    private void scan(SRURequestImpl request, HttpServletResponse response)
251            throws IOException, XMLStreamException, SRUException {
252        logger.info("scan: scanClause = \"{}\"",
253                new Object[] { request.getRawScanClause() });
254
255        // commence scan
256        final SRUScanResultSet result =
257                searchEngine.scan(config, request, request);
258        if (result == null) {
259            throw new SRUException(SRUConstants.SRU_UNSUPPORTED_OPERATION,
260                    "The 'scan' operation is not supported by this endpoint.");
261        }
262
263        try {
264            final SRUNamespaces ns = getNamespaces(request.getVersion());
265
266            /*
267             * FIXME: re-check, if while scan response needs to be put
268             * in scan namespace for SRU 2.0!
269             */
270            // send results
271            SRUXMLStreamWriter out =
272                    createXMLStreamWriter(response.getOutputStream(),
273                                          request.getRecordXmlEscaping(),
274                                          true,
275                                          request.getIndentResponse());
276
277            beginResponse(out, ns, request);
278
279            try {
280                /*
281                 * a scan result without a list of terms is a valid response;
282                 * make sure, to produce the correct output and omit in that case
283                 * the <terms> ...
284                 */
285                boolean wroteTerms = false;
286                while (result.nextTerm()) {
287                    if (!wroteTerms) {
288                        final boolean needNsDecl =
289                                !ns.getResponseNS().equals(ns.getScanNS());
290                        if (needNsDecl) {
291                            out.setPrefix(ns.getScanPrefix(), ns.getScanNS());
292                        }
293                        out.writeStartElement(ns.getScanNS(), "terms");
294                        if (needNsDecl) {
295                            out.writeNamespace(ns.getScanPrefix(),
296                                    ns.getScanNS());
297                        }
298                        wroteTerms = true;
299                    }
300                    out.writeStartElement(ns.getScanNS(), "term");
301
302                    out.writeStartElement(ns.getScanNS(), "value");
303                    out.writeCharacters(result.getValue());
304                    out.writeEndElement(); // "value" element
305
306                    if (result.getNumberOfRecords() > -1) {
307                        out.writeStartElement(ns.getScanNS(), "numberOfRecords");
308                        out.writeCharacters(
309                                Integer.toString(result.getNumberOfRecords()));
310                        out.writeEndElement(); // "numberOfRecords" element
311                    }
312
313                    if (result.getDisplayTerm() != null) {
314                        out.writeStartElement(ns.getScanNS(), "displayTerm");
315                        out.writeCharacters(result.getDisplayTerm());
316                        out.writeEndElement(); // "displayTerm" element
317                    }
318
319                    if (result.getWhereInList() != null) {
320                        out.writeStartElement(ns.getScanNS(), "whereInList");
321                        switch (result.getWhereInList()) {
322                        case FIRST:
323                            out.writeCharacters("first");
324                            break;
325                        case LAST:
326                            out.writeCharacters("last");
327                            break;
328                        case ONLY:
329                            out.writeCharacters("only");
330                            break;
331                        case INNER:
332                            out.writeCharacters("inner");
333                            break;
334                        } // switch
335                        out.writeEndElement(); // "whereInList" element
336                    }
337
338                    if (result.hasExtraTermData()) {
339                        out.writeStartElement(ns.getScanNS(), "extraTermData");
340                        result.writeExtraTermData(out);
341                        out.writeEndElement(); // "extraTermData" element
342                    }
343
344                    out.writeEndElement(); // "term" element
345                } // while
346                if (wroteTerms) {
347                    out.writeEndElement(); // "terms" element
348                }
349            } catch (NoSuchElementException e) {
350                throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
351                        "An internal error occurred while "
352                                + "serializing scan results.");
353            }
354
355            // echoedScanRequest
356            if (config.getEchoRequests()) {
357                writeEchoedScanRequest(out, ns, request, request.getScanClause());
358            }
359
360            // diagnostics
361            writeDiagnosticList(out, ns, request.getDiagnostics());
362
363            // extraResponseData
364            if (result.hasExtraResponseData()) {
365                out.writeStartElement(ns.getResponseNS(), "extraResponseData");
366                result.writeExtraResponseData(out);
367                out.writeEndElement(); // "extraResponseData" element
368            }
369
370            endResponse(out);
371        } finally {
372            result.close();
373        }
374    }
375
376
377    private void search(SRURequestImpl request, HttpServletResponse response)
378            throws IOException, XMLStreamException, SRUException {
379        logger.info("searchRetrieve: query = \"{}\", startRecord = {}, " +
380                "maximumRecords = {}, recordSchema = {}, resultSetTTL = {}",
381                new Object[] { request.getRawQuery(), request.getStartRecord(),
382                        request.getMaximumRecords(),
383                        request.getRecordSchemaIdentifier(),
384                        request.getResultSetTTL() });
385
386        // commence search ...
387        final SRUSearchResultSet result =
388                searchEngine.search(config, request, request);
389        if (result == null) {
390            throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
391                    "SRUSearchEngine implementation returned invalid result (null).");
392        }
393
394
395        // check, of startRecord position is greater than total record set
396        if ((result.getTotalRecordCount() >= 0) &&
397            (request.getStartRecord() > 1) &&
398            (request.getStartRecord() > result.getTotalRecordCount())) {
399            throw new SRUException(
400                    SRUConstants.SRU_FIRST_RECORD_POSITION_OUT_OF_RANGE);
401        }
402
403        try {
404            final SRUNamespaces ns = getNamespaces(request.getVersion());
405
406            // send results
407            SRUXMLStreamWriter out =
408                    createXMLStreamWriter(response.getOutputStream(),
409                                          request.getRecordXmlEscaping(),
410                                          true,
411                                          request.getIndentResponse());
412
413            beginResponse(out, ns, request);
414
415            // numberOfRecords
416            out.writeStartElement(ns.getResponseNS(), "numberOfRecords");
417            out.writeCharacters(
418                    Integer.toString(result.getTotalRecordCount()));
419            out.writeEndElement(); // "numberOfRecords" element
420
421            // resultSetId
422            if (result.getResultSetId() != null) {
423                out.writeStartElement(ns.getResponseNS(), "resultSetId");
424                out.writeCharacters(result.getResultSetId());
425                out.writeEndElement(); // "resultSetId" element
426            }
427
428            // resultSetIdleTime (SRU 1.1 and SRU 1.2)
429            if (!request.isVersion(SRUVersion.VERSION_2_0) &&
430                    (result.getResultSetTTL() >= 0)) {
431                out.writeStartElement(ns.getResponseNS(), "resultSetIdleTime");
432                out.writeCharacters(Integer.toString(result
433                        .getResultSetTTL()));
434                out.writeEndElement(); // "resultSetIdleTime" element
435            }
436
437            int position = (request.getStartRecord() > 0)
438                    ? request.getStartRecord() : 1;
439            if (result.getRecordCount() > 0) {
440                final int maxPositionOffset =
441                        (request.getMaximumRecords() != -1)
442                        ? (position + request.getMaximumRecords() - 1)
443                        : -1;
444                try {
445                    out.writeStartElement(ns.getResponseNS(), "records");
446                    while (result.nextRecord()) {
447                        /*
448                         * Sanity check: do not return more then the maximum
449                         * requested records. If the search engine
450                         * implementation does not honor limit truncate the
451                         * result set.
452                         */
453                        if ((maxPositionOffset != -1) &&
454                                (position > maxPositionOffset)) {
455                            logger.error("SRUSearchEngine implementation did " +
456                                    "not honor limit for the amount of " +
457                                    "requsted records. Result set truncated!");
458                            break;
459                        }
460
461                        out.writeStartElement(ns.getResponseNS(), "record");
462
463                        /*
464                         * We need to output either the record or a surrogate
465                         * diagnostic. In case of the latter, we need to output
466                         * the appropriate record schema ...
467                         */
468                        final SRUDiagnostic diagnostic =
469                                result.getSurrogateDiagnostic();
470
471                        out.writeStartElement(ns.getResponseNS(), "recordSchema");
472                        if (diagnostic == null) {
473                            out.writeCharacters(
474                                    result.getRecordSchemaIdentifier());
475                        } else {
476                            out.writeCharacters(SRU_DIAGNOSTIC_RECORD_SCHEMA);
477                        }
478                        out.writeEndElement(); // "recordSchema" element
479
480                        // recordPacking
481                        writeRecordPacking(out, ns, request.getRecordXmlEscaping());
482
483                        /*
484                         * Output either record data or surrogate diagnostic ...
485                         */
486                        out.writeStartElement(ns.getResponseNS(), "recordData");
487                        out.startRecord();
488                        if (diagnostic == null) {
489                            result.writeRecord(out);
490                        } else {
491                            // write a surrogate diagnostic
492                            writeDiagnostic(out, ns, diagnostic, true);
493                        }
494                        out.endRecord();
495                        out.writeEndElement(); // "recordData" element
496
497                        /*
498                         * recordIdentifier is version 1.2+ only
499                         */
500                        if (request.isVersion(SRUVersion.VERSION_1_2,
501                                SRUVersion.VERSION_2_0)) {
502                            final String identifier =
503                                    result.getRecordIdentifier();
504                            if (identifier != null) {
505                                out.writeStartElement(ns.getResponseNS(),
506                                                      "recordIdentifier");
507                                out.writeCharacters(identifier);
508                                out.writeEndElement(); // "recordIdentifier" element
509                            }
510                        }
511
512                        out.writeStartElement(ns.getResponseNS(),
513                                "recordPosition");
514                        out.writeCharacters(Integer.toString(position));
515                        out.writeEndElement(); // "recordPosition" element
516
517                        if (result.hasExtraRecordData()) {
518                            out.writeStartElement(ns.getResponseNS(),
519                                    "extraRecordData");
520                            result.writeExtraRecordData(out);
521                            out.writeEndElement(); // "extraRecordData"
522                        }
523
524                        out.writeEndElement(); // "record" element
525
526                        position++;
527                    } // while
528                    out.writeEndElement(); // "records" element
529                } catch (NoSuchElementException e) {
530                    throw new SRUException(
531                            SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
532                            "An internal error occurred while " +
533                            "serializing search result set.");
534                }
535            }
536
537            // nextRecordPosition
538            if (position <= result.getTotalRecordCount()) {
539                out.writeStartElement(ns.getResponseNS(), "nextRecordPosition");
540                out.writeCharacters(Integer.toString(position));
541                out.writeEndElement();
542            }
543
544            // echoedSearchRetrieveRequest
545            if (config.getEchoRequests()) {
546                writeEchoedSearchRetrieveRequest(out, ns, request,
547                                                 request.getQuery());
548            }
549
550            // diagnostics
551            writeDiagnosticList(out, ns, request.getDiagnostics());
552
553            // extraResponseData
554            if (result.hasExtraResponseData()) {
555                out.writeStartElement(ns.getResponseNS(), "extraResponseData");
556                result.writeExtraResponseData(out);
557                out.writeEndElement(); // "extraResponseData" element
558            }
559
560            // SRU 2.0 stuff ...
561            if (request.isVersion(SRUVersion.VERSION_2_0)) {
562                // resultSetTTL
563                if (result.getResultSetTTL() >= 0) {
564                    out.writeStartElement(ns.getResponseNS(), "resultSetTTL");
565                    out.writeCharacters(
566                            Integer.toString(result.getResultSetTTL()));
567                    out.writeEndElement(); // "resultSetTTL" element
568                }
569
570                // resultCountPrecision
571                final SRUResultCountPrecision precision =
572                        result.getResultCountPrecision();
573                if (precision != null) {
574                    out.writeStartElement(ns.getResponseNS(), "resultCountPrecision");
575                    switch (precision) {
576                    case EXACT:
577                        out.writeCharacters("info:srw/vocabulary/resultCountPrecision/1/exact");
578                        break;
579                    case UNKNOWN:
580                        out.writeCharacters("info:srw/vocabulary/resultCountPrecision/1/unknown");
581                        break;
582                    case ESTIMATE:
583                        out.writeCharacters("info:srw/vocabulary/resultCountPrecision/1/estimate");
584                        break;
585                    case MAXIMUM:
586                        out.writeCharacters("info:srw/vocabulary/resultCountPrecision/1/maximum");
587                        break;
588                    case MINIMUM:
589                        out.writeCharacters("info:srw/vocabulary/resultCountPrecision/1/minimum");
590                        break;
591                    case CURRENT:
592                        out.writeCharacters("info:srw/vocabulary/resultCountPrecision/1/current");
593                        break;
594                    } // switch
595                    out.writeEndElement(); // "resultCountPrecision" element
596                }
597
598                // facetedResults
599                // NOT YET SUPPORTED
600
601                // searchResultAnalysis
602                // NOT YET SUPPORTED
603            }
604            endResponse(out);
605        } finally {
606            result.close();
607        }
608    }
609
610
611    private void beginResponse(SRUXMLStreamWriter out, SRUNamespaces ns,
612            SRUOperation operation, SRUVersion version, String stylesheet)
613            throws XMLStreamException {
614        out.writeStartDocument("utf-8", "1.0");
615
616        if (stylesheet != null) {
617            StringBuilder param = new StringBuilder();
618            param.append("type=\"text/xsl\"");
619            param.append(" ");
620            param.append("href=\"");
621            param.append(stylesheet);
622            param.append("\"");
623            out.writeProcessingInstruction("xml-stylesheet", param.toString());
624        }
625
626        out.setPrefix(ns.getResponsePrefix(), ns.getResponseNS());
627        switch (operation) {
628        case EXPLAIN:
629            out.writeStartElement(ns.getResponseNS(), "explainResponse");
630            break;
631        case SCAN:
632            out.writeStartElement(ns.getResponseNS(), "scanResponse");
633            break;
634        case SEARCH_RETRIEVE:
635            out.writeStartElement(ns.getResponseNS(), "searchRetrieveResponse");
636            break;
637        }
638        out.writeNamespace(ns.getResponsePrefix(), ns.getResponseNS());
639
640        // version
641        writeVersion(out, ns, version);
642    }
643
644
645    private void beginResponse(SRUXMLStreamWriter out, SRUNamespaces ns,
646            SRURequest request) throws XMLStreamException {
647        beginResponse(out, ns, request.getOperation(), request.getVersion(),
648                request.getStylesheet());
649    }
650
651
652    private void endResponse(SRUXMLStreamWriter out)
653            throws XMLStreamException {
654        out.writeEndElement(); // "root" element
655
656        out.writeEndDocument();
657        out.close();
658        try {
659            out.getWriter().close();
660        } catch (IOException e) {
661            /* IGNORE */
662        }
663    }
664
665
666    private void writeFatalError(SRUXMLStreamWriter out, SRUNamespaces ns,
667            SRURequestImpl request, List<SRUDiagnostic> diagnotics)
668            throws XMLStreamException {
669        /*
670         * if operation is unknown, default to 'explain'
671         */
672        SRUOperation operation = request.getOperation();
673        if (operation == null) {
674            operation = SRUOperation.EXPLAIN;
675        }
676        SRUVersion version = request.getVersion();
677        if (version == null) {
678            version = config.getDefaultVersion();
679        }
680        /*
681         * write a response which conforms to the schema
682         */
683        beginResponse(out, ns, operation, version, null);
684        switch (operation) {
685        case EXPLAIN:
686            // 'explain' requires a complete explain record ...
687            writeExplainRecord(out, ns, request);
688            break;
689        case SCAN:
690            // 'scan' fortunately does not need any elements ...
691            break;
692        case SEARCH_RETRIEVE:
693            // 'searchRetrieve' needs numberOfRecords ..
694            out.writeStartElement(ns.getResponseNS(), "numberOfRecords");
695            out.writeCharacters("0");
696            out.writeEndElement(); // "numberOfRecords" element
697            break;
698        }
699        writeDiagnosticList(out, ns, diagnotics);
700        endResponse(out);
701    }
702
703
704    private void writeDiagnosticList(SRUXMLStreamWriter out, SRUNamespaces ns,
705            List<SRUDiagnostic> diagnostics) throws XMLStreamException {
706        if ((diagnostics != null) && !diagnostics.isEmpty()) {
707            out.setPrefix(ns.getDiagnosticPrefix(), ns.getDiagnosticNS());
708            out.writeStartElement(ns.getDiagnosticNS(), "diagnostics");
709            out.writeNamespace(ns.getDiagnosticPrefix(), ns.getDiagnosticNS());
710            for (SRUDiagnostic diagnostic : diagnostics) {
711                writeDiagnostic(out, ns, diagnostic, false);
712            }
713            out.writeEndElement(); // "diagnostics" element
714        }
715    }
716
717
718    private void writeExplainRecord(SRUXMLStreamWriter out, SRUNamespaces ns,
719            SRURequestImpl request) throws XMLStreamException {
720        out.writeStartElement(ns.getResponseNS(), "record");
721
722        out.writeStartElement(ns.getResponseNS(), "recordSchema");
723        out.writeCharacters(ns.getExplainNS());
724        out.writeEndElement(); // "recordSchema" element
725
726        // recordPacking
727        writeRecordPacking(out, ns, request.getRecordXmlEscaping());
728
729        out.writeStartElement(ns.getResponseNS(), "recordData");
730
731        out.startRecord();
732
733        // explain ...
734        out.setPrefix(ns.getExplainPrefix(), ns.getExplainNS());
735        out.writeStartElement(ns.getExplainNS(), "explain");
736        out.writeNamespace(ns.getExplainPrefix(), ns.getExplainNS());
737
738        // explain/serverInfo
739        out.writeStartElement(ns.getExplainNS(), "serverInfo");
740        out.writeAttribute("protocol", "SRU");
741        switch (config.getDefaultVersion()) {
742        case VERSION_1_1:
743            out.writeAttribute("version", "1.1");
744            break;
745        case VERSION_1_2:
746            out.writeAttribute("version", "1.2");
747            break;
748        case VERSION_2_0:
749            out.writeAttribute("version", "2.0");
750            break;
751        } // switch
752        out.writeAttribute("transport", config.getTransports());
753        out.writeStartElement(ns.getExplainNS(), "host");
754        out.writeCharacters(config.getHost());
755        out.writeEndElement(); // "host" element
756        out.writeStartElement(ns.getExplainNS(), "port");
757        out.writeCharacters(Integer.toString(config.getPort()));
758        out.writeEndElement(); // "port" element
759        out.writeStartElement(ns.getExplainNS(), "database");
760        out.writeCharacters(config.getDatabase());
761        out.writeEndElement(); // "database" element
762        out.writeEndElement(); // "serverInfo" element
763
764        // explain/databaseInfo
765        final DatabaseInfo dbinfo = config.getDatabaseInfo();
766        if (dbinfo != null) {
767            out.writeStartElement(ns.getExplainNS(), "databaseInfo");
768            writeLocalizedStrings(out, ns, "title",
769                    dbinfo.getTitle());
770            writeLocalizedStrings(out, ns, "description",
771                    dbinfo.getDescription());
772            writeLocalizedStrings(out, ns, "author",
773                    dbinfo.getAuthor());
774            writeLocalizedStrings(out, ns, "extent",
775                    dbinfo.getExtend());
776            writeLocalizedStrings(out, ns, "history",
777                    dbinfo.getHistory());
778            writeLocalizedStrings(out, ns, "langUsage",
779                    dbinfo.getLangUsage());
780            writeLocalizedStrings(out, ns, "restrictions",
781                    dbinfo.getRestrictions());
782            writeLocalizedStrings(out, ns, "subjects",
783                    dbinfo.getSubjects());
784            writeLocalizedStrings(out, ns, "links",
785                    dbinfo.getLinks());
786            writeLocalizedStrings(out, ns, "implementation",
787                    dbinfo.getImplementation());
788            out.writeEndElement(); // "databaseInfo" element
789        }
790
791        // explain/indexInfo
792        final IndexInfo indexInfo = config.getIndexInfo();
793        if (indexInfo != null) {
794            out.writeStartElement(ns.getExplainNS(), "indexInfo");
795
796            List<IndexInfo.Set> sets = indexInfo.getSets();
797            if (sets != null) {
798                for (IndexInfo.Set set : sets) {
799                    out.writeStartElement(ns.getExplainNS(), "set");
800                    out.writeAttribute("identifier", set.getIdentifier());
801                    out.writeAttribute("name", set.getName());
802                    writeLocalizedStrings(out, ns, "title", set.getTitle());
803                    out.writeEndElement(); // "set" element
804                }
805            }
806
807            List<IndexInfo.Index> indexes = indexInfo.getIndexes();
808            if (indexes != null) {
809                for (IndexInfo.Index index : indexes) {
810                    out.writeStartElement(ns.getExplainNS(), "index");
811                    out.writeAttribute("search",
812                            index.canSearch() ? "true" : "false");
813                    out.writeAttribute("scan",
814                            index.canScan() ? "true" : "false");
815                    out.writeAttribute("sort",
816                            index.canSort() ? "true" : "false");
817                    writeLocalizedStrings(out, ns, "title", index.getTitle());
818                    List<IndexInfo.Index.Map> maps = index.getMaps();
819                    if (maps != null) {
820                        for (IndexInfo.Index.Map map : maps) {
821                            out.writeStartElement(ns.getExplainNS(), "map");
822                            if (map.isPrimary()) {
823                                out.writeAttribute("primary", "true");
824                            }
825                            out.writeStartElement(ns.getExplainNS(), "name");
826                            out.writeAttribute("set", map.getSet());
827                            out.writeCharacters(map.getName());
828                            out.writeEndElement(); // "name" element
829                            out.writeEndElement(); // "map" element
830                        }
831                    }
832                    out.writeEndElement(); // "index" element
833                }
834            }
835            out.writeEndElement(); // "indexInfo" element
836        }
837
838        // explain/schemaInfo
839        final List<SchemaInfo> schemaInfo =
840                config.getSchemaInfo();
841        if (schemaInfo != null) {
842            out.writeStartElement(ns.getExplainNS(), "schemaInfo");
843            for (SRUServerConfig.SchemaInfo schema : schemaInfo) {
844                out.writeStartElement(ns.getExplainNS(), "schema");
845                out.writeAttribute("identifier", schema.getIdentifier());
846                out.writeAttribute("name", schema.getName());
847                /*
848                 * default is "false", so only add attribute if set to true
849                 */
850                if (schema.getSort() ) {
851                    out.writeAttribute("sort", "true");
852                }
853                /*
854                 * default is "true", so only add attribute if set to false
855                 */
856                if (!schema.getRetrieve()) {
857                    out.writeAttribute("retrieve", "false");
858                }
859                writeLocalizedStrings(out, ns, "title", schema.getTitle());
860                out.writeEndElement(); // "schema" element
861            }
862            out.writeEndElement(); // "schemaInfo" element
863        }
864
865        // explain/configInfo
866        out.writeStartElement(ns.getExplainNS(), "configInfo");
867        // numberOfRecords (default)
868        out.writeStartElement(ns.getExplainNS(), "default");
869        out.writeAttribute("type", "numberOfRecords");
870        out.writeCharacters(Integer.toString(config.getNumberOfRecords()));
871        out.writeEndElement(); // default" element
872
873        // maximumRecords (setting)
874        out.writeStartElement(ns.getExplainNS(), "setting");
875        out.writeAttribute("type", "maximumRecords");
876        out.writeCharacters(Integer.toString(config.getMaximumRecords()));
877        out.writeEndElement(); // "setting" element
878
879        out.writeEndElement(); // "configInfo" element
880
881        out.writeEndElement(); // "explain" element
882
883        out.endRecord();
884
885        out.writeEndElement(); // "recordData" element
886        out.writeEndElement(); // "record" element
887    }
888
889
890    private void writeDiagnostic(SRUXMLStreamWriter out, SRUNamespaces ns,
891            SRUDiagnostic diagnostic, boolean writeNsDecl)
892            throws XMLStreamException {
893        if (writeNsDecl) {
894            out.setPrefix(ns.getDiagnosticPrefix(), ns.getDiagnosticNS());
895        }
896        out.writeStartElement(ns.getDiagnosticNS(), "diagnostic");
897        if (writeNsDecl) {
898            out.writeNamespace(ns.getDiagnosticPrefix(), ns.getDiagnosticNS());
899        }
900        out.writeStartElement(ns.getDiagnosticNS(), "uri");
901        out.writeCharacters(diagnostic.getURI());
902        out.writeEndElement(); // "uri" element
903        if (diagnostic.getDetails() != null) {
904            out.writeStartElement(ns.getDiagnosticNS(), "details");
905            out.writeCharacters(diagnostic.getDetails());
906            out.writeEndElement(); // "details" element
907        }
908        if (diagnostic.getMessage() != null) {
909            out.writeStartElement(ns.getDiagnosticNS(), "message");
910            out.writeCharacters(diagnostic.getMessage());
911            out.writeEndElement(); // "message" element
912        }
913        out.writeEndElement(); // "diagnostic" element
914    }
915
916
917    private void writeEchoedExplainRequest(SRUXMLStreamWriter out,
918            SRUNamespaces ns, SRURequestImpl request)
919            throws XMLStreamException, SRUException {
920        // echoedSearchRetrieveRequest
921        out.writeStartElement(ns.getResponseNS(), "echoedExplainRequest");
922
923        // echoedExplainRequest/version
924        if (request.getRawVersion() != null) {
925            writeVersion(out, ns, request.getRawVersion());
926        }
927
928        // echoedExplainRequest/recordPacking
929        if (request.getRawRecordPacking() != null) {
930            writeRecordPacking(out, ns, request.getRawRecordPacking());
931        }
932
933        // echoedExplainRequest/stylesheet
934        if (request.getStylesheet() != null) {
935            out.writeStartElement(ns.getResponseNS(), "stylesheet");
936            out.writeCharacters(request.getStylesheet());
937            out.writeEndElement(); // "stylesheet" element
938        }
939
940        out.writeEndElement(); // "echoedExplainRequest" element
941    }
942
943
944    private void writeEchoedScanRequest(SRUXMLStreamWriter out,
945            SRUNamespaces ns, SRURequestImpl request, CQLNode cql)
946            throws XMLStreamException, SRUException {
947        // echoedScanRequest
948        out.writeStartElement(ns.getResponseNS(), "echoedScanRequest");
949
950        // echoedScanRequest/version
951        if (request.getRawVersion() != null) {
952            writeVersion(out, ns, request.getRawVersion());
953        }
954
955        // echoedScanRequest/scanClause
956        out.writeStartElement(ns.getResponseNS(), "scanClause");
957        out.writeCharacters(request.getRawScanClause());
958        out.writeEndElement(); // "query"
959
960        // echoedScanRequest/xScanClause
961        out.setDefaultNamespace(ns.getXcqlNS());
962        out.writeStartElement(ns.getResponseNS(), "xScanClause");
963        out.writeDefaultNamespace(ns.getXcqlNS());
964        out.writeXCQL(cql, false);
965        out.writeEndElement(); // "xScanClause" element
966
967        // echoedScanRequest/responsePosition
968        if (request.getResponsePosition() != -1) {
969            out.writeStartElement(ns.getResponseNS(), "responsePosition");
970            out.writeCharacters(
971                    Integer.toString(request.getResponsePosition()));
972            out.writeEndElement(); // "responsePosition" element
973        }
974
975        // echoedScanRequest/maximumTerms
976        if (request.getMaximumTerms() != -1) {
977            out.writeStartElement(ns.getResponseNS(), "maximumTerms");
978            out.writeCharacters(Integer.toString(request.getMaximumTerms()));
979            out.writeEndElement(); // "maximumTerms" element
980        }
981
982        // echoedScanRequest/stylesheet
983        if (request.getStylesheet() != null) {
984            out.writeStartElement(ns.getResponseNS(), "stylesheet");
985            out.writeCharacters(request.getStylesheet());
986            out.writeEndElement(); // "stylesheet" element
987        }
988
989        out.writeEndElement(); // "echoedScanRequest" element
990    }
991
992
993    private void writeEchoedSearchRetrieveRequest(SRUXMLStreamWriter out,
994            SRUNamespaces ns, SRURequestImpl request, SRUQuery<?> query)
995            throws XMLStreamException, SRUException {
996        // echoedSearchRetrieveRequest
997        out.writeStartElement(ns.getResponseNS(),
998                "echoedSearchRetrieveRequest");
999
1000        // echoedSearchRetrieveRequest/version
1001        if (request.getRawVersion() != null) {
1002            writeVersion(out, ns, request.getRawVersion());
1003        }
1004
1005        /*
1006         * XXX: unclear, if <query> should only be echoed if queryType is CQL!?
1007         */
1008        if (SRUConstants.SRU_QUERY_TYPE_CQL.equals(query.getQueryType())) {
1009            final CQLQueryParser.CQLQuery cql = (CQLQueryParser.CQLQuery) query;
1010            // echoedSearchRetrieveRequest/query
1011            out.writeStartElement(ns.getResponseNS(), "query");
1012            out.writeCharacters(cql.getRawQuery());
1013            out.writeEndElement(); // "query"
1014
1015            // echoedSearchRetrieveRequest/xQuery
1016            out.setDefaultNamespace(ns.getXcqlNS());
1017            out.writeStartElement(ns.getResponseNS(), "xQuery");
1018            out.writeDefaultNamespace(ns.getXcqlNS());
1019            out.writeXCQL(cql.getParsedQuery(), true);
1020            out.writeEndElement(); // "xQuery" element
1021        }
1022
1023        // echoedSearchRetrieveRequest/startRecord
1024        if (request.getStartRecord() > 0) {
1025            out.writeStartElement(ns.getResponseNS(), "startRecord");
1026            out.writeCharacters(Integer.toString(request.getStartRecord()));
1027            out.writeEndElement(); // "startRecord" element
1028        }
1029
1030        // echoedSearchRetrieveRequest/maximumRecords
1031        if (request.getRawMaximumRecords() > 0) {
1032            out.writeStartElement(ns.getResponseNS(), "maximumRecords");
1033            out.writeCharacters(
1034                    Integer.toString(request.getRawMaximumRecords()));
1035            out.writeEndElement(); // "startRecord" element
1036        }
1037
1038        // echoedSearchRetrieveRequest/recordPacking
1039        if (request.getRawRecordPacking() != null) {
1040            writeRecordPacking(out, ns, request.getRawRecordPacking());
1041        }
1042
1043        // echoedSearchRetrieveRequest/recordSchema
1044        if (request.getRawRecordSchemaIdentifier() != null) {
1045            out.writeStartElement(ns.getResponseNS(), "recordSchema");
1046            out.writeCharacters(request.getRawRecordSchemaIdentifier());
1047            out.writeEndElement(); // "recordSchema" element
1048        }
1049
1050        // echoedSearchRetrieveRequest/recordXPath (1.1)
1051        if (request.isVersion(SRUVersion.VERSION_1_1) &&
1052                (request.getRecordXPath() != null)) {
1053            out.writeStartElement(ns.getResponseNS(), "recordXPath");
1054            out.writeCharacters(request.getRecordXPath());
1055            out.writeEndElement(); // "recordXPath" element
1056        }
1057
1058        // echoedSearchRetrieveRequest/resultSetTTL
1059        if (request.getResultSetTTL() > 0) {
1060            out.writeStartElement(ns.getResponseNS(), "resultSetTTL");
1061            out.writeCharacters(Long.toString(request.getResultSetTTL()));
1062            out.writeEndElement(); // "resultSetTTL" element
1063        }
1064
1065        // echoedSearchRetrieveRequest/sortKeys
1066        if (request.isVersion(SRUVersion.VERSION_1_1) &&
1067                (request.getSortKeys() != null)) {
1068            out.writeStartElement(ns.getResponseNS(), "sortKeys");
1069            out.writeCharacters(request.getSortKeys());
1070            out.writeEndElement(); // "sortKeys" element
1071        }
1072
1073        // echoedSearchRetrieveRequest/xsortKeys
1074
1075        // echoedSearchRetrieveRequest/stylesheet
1076        if (request.getStylesheet() != null) {
1077            out.writeStartElement(ns.getResponseNS(), "stylesheet");
1078            out.writeCharacters(request.getStylesheet());
1079            out.writeEndElement(); // "stylesheet" element
1080        }
1081
1082        // echoedSearchRetrieveRequest/renderedBy
1083        if (request.isVersion(SRUVersion.VERSION_2_0) && (request.getRenderBy() != null)) {
1084            out.writeStartElement(ns.getResponseNS(), "renderedBy");
1085            switch (request.getRenderBy()) {
1086            case SERVER:
1087                out.writeCharacters("server");
1088                break;
1089            case CLIENT:
1090                out.writeCharacters("client");
1091                break;
1092            }
1093            out.writeEndElement(); // "renderedBy" element
1094        }
1095
1096        // echoedSearchRetrieveRequest/extraRequestParameter
1097        // FIXME: NOT YET IMPLEMENTED
1098
1099        // echoedSearchRetrieveRequest/httpAccept
1100        if (request.isVersion(SRUVersion.VERSION_2_0) && (request.getRawHttpAccept() != null)) {
1101            out.writeStartElement(ns.getResponseNS(), "renderedBy");
1102            out.writeCharacters(request.getRawHttpAccept());
1103            out.writeEndElement(); // "renderedBy" element
1104        }
1105
1106        // echoedSearchRetrieveRequest/responseType
1107        if (request.isVersion(SRUVersion.VERSION_2_0) && (request.getResponeType() != null)) {
1108            out.writeStartElement(ns.getResponseNS(), "responseType");
1109            out.writeCharacters(request.getResponeType());
1110            out.writeEndElement(); // "responseType" element
1111        }
1112
1113        out.writeEndElement(); // "echoedSearchRetrieveRequest" element
1114    }
1115
1116
1117    private void writeVersion(SRUXMLStreamWriter out, SRUNamespaces ns,
1118            SRUVersion version) throws XMLStreamException {
1119        out.writeStartElement(ns.getResponseNS(), "version");
1120        switch (version) {
1121        case VERSION_1_1:
1122            out.writeCharacters("1.1");
1123            break;
1124        case VERSION_1_2:
1125            out.writeCharacters("1.2");
1126            break;
1127        case VERSION_2_0:
1128            out.writeCharacters("2.0");
1129            break;
1130        } // switch
1131        out.writeEndElement(); // "version" element
1132    }
1133
1134
1135    private void writeRecordPacking(SRUXMLStreamWriter out, SRUNamespaces ns,
1136            SRURecordXmlEscaping recordPacking) throws XMLStreamException {
1137        out.writeStartElement(ns.getResponseNS(), "recordPacking");
1138        switch (recordPacking) {
1139        case XML:
1140            out.writeCharacters("xml");
1141            break;
1142        case STRING:
1143            out.writeCharacters("string");
1144            break;
1145        } // switch
1146        out.writeEndElement(); // "recordPacking" element
1147    }
1148
1149
1150    private void writeLocalizedStrings(XMLStreamWriter writer,
1151            SRUNamespaces ns, String name, List<LocalizedString> list)
1152            throws XMLStreamException {
1153        if ((list != null) && !list.isEmpty()) {
1154            for (LocalizedString item : list) {
1155                writer.writeStartElement(ns.getExplainNS(), name);
1156                if (item.getLang() != null) {
1157                    writer.writeAttribute("lang", item.getLang());
1158                }
1159                if (item.isPrimary()) {
1160                    writer.writeAttribute("primary", "true");
1161                }
1162                writer.writeCharacters(item.getValue());
1163                writer.writeEndElement();
1164            }
1165        }
1166    }
1167
1168
1169    private SRUXMLStreamWriter createXMLStreamWriter(OutputStream out,
1170            SRURecordXmlEscaping recordPacking, boolean skipFlush, int indent)
1171            throws SRUException {
1172        try {
1173            if (skipFlush) {
1174                /*
1175                 * Add a FilterOutputStream to delay flush() as long as
1176                 * possible. Doing so, enabled us to send an appropriate SRU
1177                 * diagnostic in case an error occurs during the serialization
1178                 * of the response.
1179                 * Of course, if an error occurs when the Servlet response
1180                 * buffer already had been flushed, because it was to large,
1181                 * we cannot fail gracefully and we will produce ill-formed
1182                 * XML output.
1183                 */
1184                out = new FilterOutputStream(out) {
1185                    @Override
1186                    public void flush() throws IOException {
1187                    }
1188
1189
1190                    @Override
1191                    public void close() throws IOException {
1192                        super.flush();
1193                        super.close();
1194                    }
1195                };
1196            }
1197            return new SRUXMLStreamWriter(out, writerFactory,
1198                    recordPacking, indent);
1199        } catch (Exception e) {
1200            throw new SRUException(SRUConstants.SRU_GENERAL_SYSTEM_ERROR,
1201                    "Error creating output stream.", e);
1202
1203        }
1204    }
1205
1206
1207    private SRUNamespaces getNamespaces(SRUVersion version) {
1208        if (version == null) {
1209            throw new NullPointerException("version == null");
1210        }
1211        switch (version) {
1212        case VERSION_1_1:
1213            /* FALL-THROUGH */
1214        case VERSION_1_2:
1215            switch (config.getLegacyNamespaceMode()) {
1216            case LOC:
1217                return NAMESPACES_LEGACY_LOC;
1218            case OASIS:
1219                return NAMESPACES_1_2_OASIS;
1220            default:
1221                // FIXME: better exception?
1222                throw new IllegalAccessError("invalid legacy mode: " + version);
1223            } // switch
1224        case VERSION_2_0:
1225            return NAMESPACES_2_0;
1226        default:
1227            // FIXME: better exception?
1228            throw new IllegalAccessError("invalid version: " + version);
1229        }
1230    }
1231
1232
1233
1234
1235    private static final SRUNamespaces NAMESPACES_LEGACY_LOC = new SRUNamespaces() {
1236        private static final String SRU_NS =
1237                "http://www.loc.gov/zing/srw/";
1238        private static final String SRU_PREFIX =
1239                "sru";
1240        private static final String SRU_DIAGNOSIC_NS =
1241                "http://www.loc.gov/zing/srw/diagnostic/";
1242        private static final String SRU_DIAGNOSTIC_PREFIX =
1243                "diag";
1244        private static final String SRU_EXPLAIN_NS =
1245                "http://explain.z3950.org/dtd/2.0/";
1246        private static final String SRU_EXPLAIN_PREFIX =
1247                "zr";
1248        private static final String SRU_XCQL_NS =
1249                "http://www.loc.gov/zing/cql/xcql/";
1250
1251
1252        @Override
1253        public String getResponseNS() {
1254            return SRU_NS;
1255        }
1256
1257
1258        @Override
1259        public String getResponsePrefix() {
1260            return SRU_PREFIX;
1261        }
1262
1263
1264        @Override
1265        public String getScanNS() {
1266            return SRU_NS;
1267        }
1268
1269
1270        @Override
1271        public String getScanPrefix() {
1272            return SRU_PREFIX;
1273        }
1274
1275
1276        @Override
1277        public String getDiagnosticNS() {
1278            return SRU_DIAGNOSIC_NS;
1279        }
1280
1281
1282        @Override
1283        public String getDiagnosticPrefix() {
1284            return SRU_DIAGNOSTIC_PREFIX;
1285        }
1286
1287
1288        @Override
1289        public String getExplainNS() {
1290            return SRU_EXPLAIN_NS;
1291        }
1292
1293
1294        @Override
1295        public String getExplainPrefix() {
1296            return SRU_EXPLAIN_PREFIX;
1297        }
1298
1299
1300        @Override
1301        public String getXcqlNS() {
1302            return SRU_XCQL_NS;
1303        }
1304    };
1305
1306
1307    private static final SRUNamespaces NAMESPACES_1_2_OASIS = new SRUNamespaces() {
1308        private static final String SRU_RESPONSE_NS =
1309                "http://docs.oasis-open.org/ns/search-ws/sruResponse";
1310        private static final String SRU_RESPONSE_PREFIX =
1311                "sruResponse";
1312        private static final String SRU_SCAN_NS =
1313                "http://docs.oasis-open.org/ns/search-ws/scan";
1314        private static final String SRU_SCAN_PREFIX =
1315                "scan";
1316        private static final String SRU_DIAGNOSIC_NS =
1317                "http://docs.oasis-open.org/ns/search-ws/diagnostic";
1318        private static final String SRU_DIAGNOSTIC_PREFIX =
1319                "diag";
1320        private static final String SRU_EXPLAIN_NS =
1321                "http://explain.z3950.org/dtd/2.0/";
1322        private static final String SRU_EXPLAIN_PREFIX =
1323                "zr";
1324        private static final String SRU_XCQL_NS =
1325                "http://docs.oasis-open.org/ns/search-ws/xcql";
1326
1327
1328        @Override
1329        public String getResponseNS() {
1330            return SRU_RESPONSE_NS;
1331        }
1332
1333
1334        @Override
1335        public String getResponsePrefix() {
1336            return SRU_RESPONSE_PREFIX;
1337        }
1338
1339
1340        @Override
1341        public String getScanNS() {
1342            return SRU_SCAN_NS;
1343        }
1344
1345
1346        @Override
1347        public String getScanPrefix() {
1348            return SRU_SCAN_PREFIX;
1349        }
1350
1351
1352        @Override
1353        public String getDiagnosticNS() {
1354            return SRU_DIAGNOSIC_NS;
1355        }
1356
1357
1358        @Override
1359        public String getDiagnosticPrefix() {
1360            return SRU_DIAGNOSTIC_PREFIX;
1361        }
1362
1363
1364        @Override
1365        public String getExplainNS() {
1366            return SRU_EXPLAIN_NS;
1367        }
1368
1369
1370        @Override
1371        public String getExplainPrefix() {
1372            return SRU_EXPLAIN_PREFIX;
1373        }
1374
1375
1376        @Override
1377        public String getXcqlNS() {
1378            return SRU_XCQL_NS;
1379        }
1380    };
1381
1382
1383    private static final SRUNamespaces NAMESPACES_2_0 = new SRUNamespaces() {
1384        private static final String SRU_RESPONSE_NS =
1385                "http://docs.oasis-open.org/ns/search-ws/sruResponse";
1386        private static final String SRU_RESPONSE_PREFIX =
1387                "sruResponse";
1388        private static final String SRU_SCAN_NS =
1389                "http://docs.oasis-open.org/ns/search-ws/scan";
1390        private static final String SRU_SCAN_PREFIX =
1391                "scan";
1392        private static final String SRU_DIAGNOSIC_NS =
1393                "http://docs.oasis-open.org/ns/search-ws/diagnostic";
1394        private static final String SRU_DIAGNOSTIC_PREFIX =
1395                "diag";
1396        private static final String SRU_EXPLAIN_NS =
1397                "http://explain.z3950.org/dtd/2.0/";
1398        private static final String SRU_EXPLAIN_PREFIX =
1399                "zr";
1400        private static final String SRU_XCQL_NS =
1401                "http://docs.oasis-open.org/ns/search-ws/xcql";
1402
1403
1404        @Override
1405        public String getResponseNS() {
1406            return SRU_RESPONSE_NS;
1407        }
1408
1409
1410        @Override
1411        public String getResponsePrefix() {
1412            return SRU_RESPONSE_PREFIX;
1413        }
1414
1415
1416        @Override
1417        public String getScanNS() {
1418            return SRU_SCAN_NS;
1419        }
1420
1421
1422        @Override
1423        public String getScanPrefix() {
1424            return SRU_SCAN_PREFIX;
1425        }
1426
1427
1428        @Override
1429        public String getDiagnosticNS() {
1430            return SRU_DIAGNOSIC_NS;
1431        }
1432
1433
1434        @Override
1435        public String getDiagnosticPrefix() {
1436            return SRU_DIAGNOSTIC_PREFIX;
1437        }
1438
1439
1440        @Override
1441        public String getExplainNS() {
1442            return SRU_EXPLAIN_NS;
1443        }
1444
1445
1446        @Override
1447        public String getExplainPrefix() {
1448            return SRU_EXPLAIN_PREFIX;
1449        }
1450
1451
1452        @Override
1453        public String getXcqlNS() {
1454            return SRU_XCQL_NS;
1455        }
1456    };
1457
1458} // class SRUServer
Note: See TracBrowser for help on using the repository browser.