source: SRUServer/tags/SRUServer-1.8.0/src/main/java/eu/clarin/sru/server/SRUServer.java @ 6954

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