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

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