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

Last change on this file since 6825 was 6825, checked in by Oliver Schonefeld, 9 years ago
  • add SRUQueryParserFactory and expose interface to register query parsers
  • Property svn:eol-style set to native
File size: 40.5 KB
Line 
1/**
2 * This software is copyright (c) 2011-2013 by
3 *  - Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
4 * This is free software. You can redistribute it
5 * and/or modify it under the terms described in
6 * the GNU General Public License v3 of which you
7 * should have received a copy. Otherwise you can download
8 * it from
9 *
10 *   http://www.gnu.org/licenses/gpl-3.0.txt
11 *
12 * @copyright Institut fuer Deutsche Sprache (http://www.ids-mannheim.de)
13 *
14 * @license http://www.gnu.org/licenses/gpl-3.0.txt
15 *  GNU General Public License v3
16 */
17package eu.clarin.sru.server;
18
19import java.io.IOException;
20import java.util.ArrayList;
21import java.util.Collections;
22import java.util.Enumeration;
23import java.util.HashMap;
24import java.util.List;
25import java.util.Map;
26
27import javax.servlet.http.HttpServletRequest;
28
29import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31import org.z3950.zing.cql.CQLNode;
32import org.z3950.zing.cql.CQLParseException;
33import org.z3950.zing.cql.CQLParser;
34
35
36final class SRURequestImpl implements SRURequest, SRUDiagnosticList {
37    private static final Logger logger =
38            LoggerFactory.getLogger(SRURequest.class);
39    /* general / explain related parameter names */
40    private static final String PARAM_OPERATION            = "operation";
41    private static final String PARAM_VERSION              = "version";
42    private static final String PARAM_STYLESHEET           = "stylesheet";
43    private static final String PARAM_RENDER_BY            = "renderedBy";
44    private static final String PARAM_HTTP_ACCEPT          = "httpAccept";
45    private static final String PARAM_RESPONSE_TYPE        = "responseType";
46    /* searchRetrieve related parameter names */
47    private static final String PARAM_QUERY                = "query";
48    private static final String PARAM_QUERY_TYPE           = "queryType";
49    private static final String PARAM_START_RECORD         = "startRecord";
50    private static final String PARAM_MAXIMUM_RECORDS      = "maximumRecords";
51    private static final String PARAM_RECORD_XML_ESCAPING  = "recordXMLEscaping";
52    private static final String PARAM_RECORD_PACKING       = "recordPacking";
53    private static final String PARAM_RECORD_SCHEMA        = "recordSchema";
54    private static final String PARAM_RECORD_X_PATH        = "recordXPath";
55    private static final String PARAM_RESULT_SET_TTL       = "resultSetTTL";
56    private static final String PARAM_SORT_KEYS            = "sortKeys";
57    /* scan related parameter names */
58    private static final String PARAM_SCAN_CLAUSE          = "scanClause";
59    private static final String PARAM_RESPONSE_POSITION    = "responsePosition";
60    private static final String PARAM_MAXIMUM_TERMS        = "maximumTerms";
61    /* operations */
62    private static final String OP_EXPLAIN                 = "explain";
63    private static final String OP_SCAN                    = "scan";
64    private static final String OP_SEARCH_RETRIEVE         = "searchRetrieve";
65    private static final String VERSION_1_1                = "1.1";
66    private static final String VERSION_1_2                = "1.2";
67    /* various parameter values */
68    private static final String RECORD_XML_ESCAPING_XML    = "xml";
69    private static final String RECORD_XML_ESCPAING_STRING = "string";
70    private static final String RECORD_PACKING_PACKED      = "packed";
71    private static final String RECORD_PACKING_UNPACKED    = "unpacked";
72    private static final String RENDER_BY_CLIENT           = "client";
73    private static final String RENDER_BY_SERVER           = "server";
74    private static final String PARAM_EXTENSION_PREFIX     = "x-";
75    private static final String X_UNLIMITED_RESULTSET      = "x-unlimited-resultset";
76    private static final String X_UNLIMITED_TERMLIST       = "x-unlimited-termlist";
77    private static final String X_INDENT_RESPONSE          = "x-indent-response";
78    private static final int DEFAULT_START_RECORD          = 1;
79    private static final int DEFAULT_RESPONSE_POSITION     = 1;
80    private final SRUServerConfig config;
81    private final SRUQueryParserRegistry queryParsers;
82    private final HttpServletRequest request;
83    private List<SRUDiagnostic> diagnostics;
84    private SRUOperation operation;
85    private SRUVersion version;
86    private SRURecordXmlEscaping recordXmlEscaping;
87    private SRURecordPacking recordPacking;
88    private SRUQuery<?> query;
89    private int startRecord = DEFAULT_START_RECORD;
90    private int maximumRecords = -1;
91    private String recordSchemaIdentifier;
92    private String stylesheet;
93    private SRURenderBy renderBy;
94    private String responseType;
95    private String httpAccept;
96    private String recordXPath;
97    private int resultSetTTL = -1;
98    private String sortKeys;
99    private CQLNode scanClause;
100    private int responsePosition = DEFAULT_RESPONSE_POSITION;
101    private int maximumTerms = -1;
102
103    private static enum Parameter {
104        STYLESHEET,
105        RENDER_BY,
106        HTTP_ACCEPT,
107        RESPONSE_TYPE,
108        START_RECORD,
109        MAXIMUM_RECORDS,
110        RECORD_XML_ESCAPING,
111        RECORD_PACKING,
112        RECORD_SCHEMA,
113        RECORD_XPATH,
114        RESULT_SET_TTL,
115        SORT_KEYS,
116        SCAN_CLAUSE,
117        RESPONSE_POSITION,
118        MAXIMUM_TERMS,
119    }
120
121    private static final class ParameterInfo {
122        private final Parameter parameter;
123        private final SRUVersion min;
124        private final SRUVersion max;
125        private final boolean mandatory;
126
127        private ParameterInfo(Parameter name, boolean mandatory,
128                SRUVersion min, SRUVersion max) {
129            this.parameter = name;
130            this.mandatory = mandatory;
131            this.min       = min;
132            this.max       = max;
133        }
134
135        public Parameter getParameter() {
136            return parameter;
137        }
138
139        public String getName(SRUVersion version) {
140            switch (parameter) {
141            case STYLESHEET:
142                return PARAM_STYLESHEET;
143            case RENDER_BY:
144                return PARAM_RENDER_BY;
145            case HTTP_ACCEPT:
146                return PARAM_HTTP_ACCEPT;
147            case RESPONSE_TYPE:
148                return PARAM_RESPONSE_TYPE;
149            case START_RECORD:
150                return PARAM_START_RECORD;
151            case MAXIMUM_RECORDS:
152                return PARAM_MAXIMUM_RECORDS;
153            case RECORD_XML_ESCAPING:
154                /*
155                 * 'recordPacking' was renamed to 'recordXMLEscaping' in SRU
156                 * 2.0. For library API treat 'recordPacking' parameter as
157                 * 'recordPacking' for SRU 1.1 and SRU 1.2.
158                 */
159                if (version == SRUVersion.VERSION_2_0) {
160                    return PARAM_RECORD_XML_ESCAPING;
161                } else {
162                    return PARAM_RECORD_PACKING;
163                }
164            case RECORD_PACKING:
165                /*
166                 * 'recordPacking' only exists in SRU 2.0; the old variant is
167                 * handled by the case for RECORD_XML_ESCAPING
168                 */
169                if (version == SRUVersion.VERSION_2_0) {
170                    return PARAM_RECORD_PACKING;
171                } else {
172                    return null;
173                }
174            case RECORD_SCHEMA:
175                return PARAM_RECORD_SCHEMA;
176            case RECORD_XPATH:
177                return PARAM_RECORD_X_PATH;
178            case RESULT_SET_TTL:
179                return PARAM_RESULT_SET_TTL;
180            case SORT_KEYS:
181                return PARAM_SORT_KEYS;
182            case SCAN_CLAUSE:
183                return PARAM_SCAN_CLAUSE;
184            case RESPONSE_POSITION:
185                return PARAM_RESPONSE_POSITION;
186            case MAXIMUM_TERMS:
187                return PARAM_MAXIMUM_TERMS;
188            default:
189                throw new InternalError();
190            } // switch
191        }
192
193        public boolean getMandatory() {
194            return mandatory;
195        }
196
197        public boolean isForVersion(SRUVersion version) {
198            return (min.getVersionNumber() <= version.getVersionNumber()) &&
199                    (version.getVersionNumber() <= max.getVersionNumber());
200        }
201    } // class ParameterInfo
202
203    private static final ParameterInfo[] PARAMETER_SET_EXPLAIN = {
204            new ParameterInfo(Parameter.STYLESHEET, false,
205                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
206            new ParameterInfo(Parameter.RECORD_XML_ESCAPING, false,
207                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2)
208    };
209    private static final ParameterInfo[] PARAMETER_SET_SCAN = {
210            new ParameterInfo(Parameter.STYLESHEET, false,
211                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
212            new ParameterInfo(Parameter.HTTP_ACCEPT, false,
213                    SRUVersion.VERSION_2_0, SRUVersion.VERSION_2_0),
214            new ParameterInfo(Parameter.SCAN_CLAUSE, true,
215                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
216            new ParameterInfo(Parameter.RESPONSE_POSITION, false,
217                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
218            new ParameterInfo(Parameter.MAXIMUM_TERMS, false,
219                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0)
220    };
221    private static final ParameterInfo[] PARAMETER_SET_SEARCH_RETRIEVE = {
222        new ParameterInfo(Parameter.STYLESHEET, false,
223                    SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
224        new ParameterInfo(Parameter.HTTP_ACCEPT, false,
225                SRUVersion.VERSION_2_0, SRUVersion.VERSION_2_0),
226        new ParameterInfo(Parameter.RENDER_BY, false,
227                SRUVersion.VERSION_2_0, SRUVersion.VERSION_2_0),
228        new ParameterInfo(Parameter.RESPONSE_TYPE, false,
229                SRUVersion.VERSION_2_0, SRUVersion.VERSION_2_0),
230        new ParameterInfo(Parameter.START_RECORD, false,
231                SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
232        new ParameterInfo(Parameter.MAXIMUM_RECORDS, false,
233                SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
234        new ParameterInfo(Parameter.RECORD_XML_ESCAPING, false,
235                SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
236        new ParameterInfo(Parameter.RECORD_PACKING, false,
237                SRUVersion.VERSION_2_0, SRUVersion.VERSION_2_0),
238        new ParameterInfo(Parameter.RECORD_SCHEMA, false,
239                SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
240        new ParameterInfo(Parameter.RESULT_SET_TTL, false,
241                SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
242        new ParameterInfo(Parameter.RECORD_XPATH, false,
243                SRUVersion.VERSION_1_1, SRUVersion.VERSION_1_2),
244        new ParameterInfo(Parameter.SORT_KEYS, false,
245                SRUVersion.VERSION_1_1, SRUVersion.VERSION_2_0),
246    };
247
248
249    SRURequestImpl(SRUServerConfig config,
250            SRUQueryParserRegistry queryParsers,
251            HttpServletRequest request) {
252        this.config       = config;
253        this.queryParsers = queryParsers;
254        this.request      = request;
255    }
256
257
258    /**
259     * Validate incoming request parameters.
260     *
261     * @return <code>true</code> if successful, <code>false</code> if something
262     *         went wrong
263     */
264    boolean checkParameters() {
265        final SRUVersion minVersion = config.getMinVersion();
266        final SRUVersion maxVersion = config.getMaxVersion();
267
268        /*
269         * generally assume, we will also allow processing of SRU 1.1 or 1.2
270         */
271        boolean processSruOld = true;
272
273        /*
274         * Heuristic to detect SRU version and operation ...
275         */
276        SRUOperation operation = null;
277        SRUVersion version = null;
278        if (maxVersion.compareTo(SRUVersion.VERSION_2_0) >= 0) {
279            if (getParameter(PARAM_VERSION, false, false) == null) {
280                /*
281                 * Ok, we're committed to SRU 2.0 now, so don't allow processing
282                 * of SRU 1.1 and 1.2 ...
283                 */
284                processSruOld = false;
285
286                logger.debug("handling request as SRU 2.0, because no '{}' " +
287                        "parameter was found in the request", PARAM_VERSION);
288                if ((getParameter(PARAM_QUERY, false, false) != null) ||
289                        (getParameter(PARAM_QUERY_TYPE, false, false) != null)) {
290                    logger.debug("found parameter '{}' or '{}' therefore " +
291                            "assuming '{}' operation",
292                            PARAM_QUERY, PARAM_QUERY_TYPE,
293                            SRUOperation.SEARCH_RETRIEVE);
294                    operation = SRUOperation.SEARCH_RETRIEVE;
295                } else if (getParameter(PARAM_SCAN_CLAUSE, false, false) != null) {
296                    logger.debug("found parameter '{}' therefore " +
297                            "assuming '{}' operation",
298                            PARAM_SCAN_CLAUSE, SRUOperation.SCAN);
299                    operation = SRUOperation.SCAN;
300                } else {
301                    logger.debug("no special parameter found therefore " +
302                            "assuming '{}' operation",
303                            SRUOperation.EXPLAIN);
304                    operation = SRUOperation.EXPLAIN;
305                }
306
307                /* record version ... */
308                version = SRUVersion.VERSION_2_0;
309
310                /* do pedantic check for 'operation' parameter */
311                final String op = getParameter(PARAM_OPERATION, false, false);
312                if (op != null) {
313                    /*
314                     * XXX: if operation is searchRetrive and the 'operation'
315                     * parameter is also searchRetrieve, should the server just
316                     * ignore it?
317                     */
318                    if (!(operation == SRUOperation.SEARCH_RETRIEVE) &&
319                            op.equals(OP_SEARCH_RETRIEVE)) {
320                        addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER,
321                                PARAM_OPERATION, "Parameter \"" +
322                                        PARAM_OPERATION +
323                                        "\" is not valid for SRU version 2.0");
324                    }
325                }
326            } else {
327                logger.debug("handling request as legacy SRU, because found " +
328                        "parameter '{}' in request", PARAM_VERSION);
329            }
330        }
331
332        if (processSruOld) {
333            // parse mandatory operation parameter
334            final String op = getParameter(PARAM_OPERATION, false, false);
335            if (op != null) {
336                if (!op.isEmpty()) {
337                    if (op.equals(OP_EXPLAIN)) {
338                        operation = SRUOperation.EXPLAIN;
339                    } else if (op.equals(OP_SCAN)) {
340                        operation = SRUOperation.SCAN;
341                    } else if (op.equals(OP_SEARCH_RETRIEVE)) {
342                        operation = SRUOperation.SEARCH_RETRIEVE;
343                    } else {
344                        addDiagnostic(SRUConstants.SRU_UNSUPPORTED_OPERATION,
345                                null, "Operation \"" + op + "\" is not supported.");
346                    }
347                } else {
348                    addDiagnostic(SRUConstants.SRU_UNSUPPORTED_OPERATION,
349                            null, "An empty parameter \"" +
350                                    PARAM_OPERATION +
351                                  "\" is not supported.");
352                }
353
354                // parse and check version
355                version = parseAndCheckVersionParameter(operation);
356            } else {
357                /*
358                 * absent parameter should be interpreted as "explain"
359                 */
360                operation = SRUOperation.EXPLAIN;
361
362                // parse and check version
363                version = parseAndCheckVersionParameter(operation);
364            }
365        }
366
367
368        /*
369         * sanity check
370         */
371        if ((version != null) && (operation != null)) {
372            logger.debug("min = {}, min? = {}, max = {}, max? = {}, version = {}",
373                    minVersion,
374                    version.compareTo(minVersion),
375                    maxVersion,
376                    version.compareTo(maxVersion),
377                    version);
378            if ((version.compareTo(minVersion) >= 0) &&
379                    (version.compareTo(maxVersion) <= 0)) {
380                /*
381                 * FIXME: re-factor to make this nicer ...
382                 */
383                this.version   = version;
384                this.operation = operation;
385                return checkParameters2();
386            } else {
387                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_VERSION,
388                        maxVersion.getVersionString(),
389                        "Version \"" + version.getVersionString() +
390                        "\" is not supported by this endpoint.");
391            }
392        }
393        logger.debug("bailed");
394        return false;
395    }
396
397
398    private boolean checkParameters2() {
399        if (diagnostics == null) {
400            // check mandatory/optional parameters for operation
401            ParameterInfo[] parameter_set;
402            switch (operation) {
403            case EXPLAIN:
404                parameter_set = PARAMETER_SET_EXPLAIN;
405                break;
406            case SCAN:
407                parameter_set = PARAMETER_SET_SCAN;
408                break;
409            case SEARCH_RETRIEVE:
410                parameter_set = PARAMETER_SET_SEARCH_RETRIEVE;
411                break;
412            default:
413                /* actually cannot happen */
414                addDiagnostic(SRUConstants.SRU_GENERAL_SYSTEM_ERROR, null,
415                        "internal error (invalid operation)");
416                return false;
417            }
418
419            /*
420             * keep list of all submitted parameters (except "operation" and
421             * "version"), so we can later warn if an unsupported parameter
422             * was sent (= not all parameters were consumed).
423             */
424            List<String> parameterNames = getParameterNames();
425
426            // check parameters ...
427            for (ParameterInfo parameter : parameter_set) {
428                final String name = parameter.getName(version);
429                if (name == null) {
430                    /*
431                     * this parameter is not supported in the SRU version that
432                     * was used for the request
433                     */
434                    continue;
435                }
436                final String value = getParameter(name, true, true);
437                if (value != null) {
438                    // remove supported parameter from list
439                    parameterNames.remove(name);
440
441                    /*
442                     * if parameter is not supported in this version, skip
443                     * it and create add an diagnostic.
444                     */
445                    if (!parameter.isForVersion(version)) {
446                        addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER,
447                                name,
448                                "Version " + version.getVersionString() +
449                                        " does not support parameter \"" +
450                                        name + "\".");
451                        continue;
452                    }
453
454                    // validate and parse parameters ...
455                    switch (parameter.getParameter()) {
456                    case RECORD_XML_ESCAPING:
457                        if (value.equals(RECORD_XML_ESCAPING_XML)) {
458                            recordXmlEscaping = SRURecordXmlEscaping.XML;
459                        } else if (value.equals(RECORD_XML_ESCPAING_STRING)) {
460                            recordXmlEscaping = SRURecordXmlEscaping.STRING;
461                        } else {
462                            addDiagnostic(
463                                    SRUConstants.SRU_UNSUPPORTED_XML_ESCAPING_VALUE,
464                                    null, "Record XML escaping \"" + value +
465                                            "\" is not supported.");
466                        }
467                        break;
468                    case RECORD_PACKING:
469                        if (value.equals(RECORD_PACKING_PACKED)) {
470                            recordPacking = SRURecordPacking.PACKED;
471                        } else if (value.equals(RECORD_PACKING_UNPACKED)) {
472                            recordPacking = SRURecordPacking.UNPACKED;
473                        } else {
474                            addDiagnostic(
475                                    SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
476                                    null, "Record packing \"" + value +
477                                            "\" is not supported.");
478                        }
479                        break;
480                    case START_RECORD:
481                        startRecord = parseNumberedParameter(name, value, 1);
482                        break;
483                    case MAXIMUM_RECORDS:
484                        maximumRecords = parseNumberedParameter(name, value, 0);
485                        break;
486                    case RECORD_SCHEMA:
487                        /*
488                         * The parameter recordSchema may contain either schema
489                         * identifier or the short name. If available, set to
490                         * appropriate schema identifier in the request object.
491                         */
492                        SRUServerConfig.SchemaInfo schemaInfo =
493                            config.findSchemaInfo(value);
494                        if (schemaInfo != null) {
495                            recordSchemaIdentifier = schemaInfo.getIdentifier();
496                        } else {
497                            /*
498                             * SRU servers are supposed to raise a non-surrogate
499                             * (fatal) diagnostic in case the record schema is
500                             * not known to the server.
501                             */
502                            addDiagnostic(
503                                    SRUConstants.SRU_UNKNOWN_SCHEMA_FOR_RETRIEVAL,
504                                    value, "Record schema \"" + value +
505                                    "\" is not supported  for retrieval.");
506                        }
507                        break;
508                    case RECORD_XPATH:
509                        recordXPath = value;
510                        break;
511                    case RESULT_SET_TTL:
512                        resultSetTTL = parseNumberedParameter(name, value, 0);
513                        break;
514                    case SORT_KEYS:
515                        sortKeys = value;
516                        break;
517                    case SCAN_CLAUSE:
518                        scanClause = parseScanQueryParameter(name, value);
519                        break;
520                    case RESPONSE_POSITION:
521                        responsePosition = parseNumberedParameter(
522                                name, value, 0);
523                        break;
524                    case MAXIMUM_TERMS:
525                        maximumTerms = parseNumberedParameter(name, value, 0);
526                        break;
527                    case STYLESHEET:
528                        stylesheet = value;
529                        break;
530                    case RENDER_BY:
531                        if (value.equals(RENDER_BY_CLIENT)) {
532                            renderBy = SRURenderBy.CLIENT;
533                        } else if (value.equals(RENDER_BY_SERVER)) {
534                            renderBy = SRURenderBy.SERVER;
535                        } else {
536                            addDiagnostic(
537                                    SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
538                                    null,
539                                    "Value \"" + value + "\" for parameter '" +
540                                            name + "' is not supported.");
541                        }
542                        break;
543                    case RESPONSE_TYPE:
544                        /*
545                         * FIXME: check parameter validity?!
546                         */
547                        responseType = value;
548                        break;
549                    case HTTP_ACCEPT:
550                        /*
551                         * FIXME: check parameter validity?!
552                         */
553                        httpAccept = value;
554                        break;
555                    } // switch
556                } else {
557                    if (parameter.getMandatory()) {
558                        addDiagnostic(
559                                SRUConstants.SRU_MANDATORY_PARAMETER_NOT_SUPPLIED,
560                                name, "Mandatory parameter \"" + name +
561                                        "\" was not supplied.");
562                    }
563                }
564            } // for
565
566            /*
567             * handle query and queryType
568             */
569            if (operation == SRUOperation.SEARCH_RETRIEVE) {
570                /*
571                 * determine queryType
572                 */
573                String queryType = null;
574                if (version == SRUVersion.VERSION_2_0) {
575                    parameterNames.remove(PARAM_QUERY_TYPE);
576                    String value = getParameter(PARAM_QUERY_TYPE, true, true);
577                    if (value == null) {
578                        queryType = SRUConstants.SRU_QUERY_TYPE_CQL;
579                    } else {
580                        boolean badCharacters = false;
581                        for (int i = 0; i < value.length(); i++) {
582                            final char ch = value.charAt(i);
583                            if (!((ch >= 'a' && ch <= 'z') ||
584                                    (ch >= 'A' && ch <= 'Z') ||
585                                    (ch >= '0' && ch <= '9') ||
586                                    ((i > 0) && ((ch == '-') || ch == '_')))) {
587                                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
588                                        PARAM_QUERY_TYPE, "Value contains illegal characters.");
589                                badCharacters = true;
590                                break;
591                            }
592                        }
593                        if (!badCharacters) {
594                            queryType = value;
595                        }
596                    }
597                } else {
598                    // SRU 1.1 and SRU 1.2 only support CQL
599                    queryType = SRUConstants.SRU_QUERY_TYPE_CQL;
600                }
601
602
603                if (queryType != null) {
604                    logger.debug("looking for query parser for query type '{}'",
605                            queryType);
606                    final SRUQueryParser<?> queryParser =
607                            queryParsers.findQueryParser(queryType);
608                    if (queryParser != null) {
609                        /*
610                         * gather query parameters (as required by QueryParser
611                         * implementation
612                         */
613                        final Map<String, String> queryParameters =
614                                new HashMap<String, String>();
615                        List<String> missingParameter = null;
616                        for (String name : queryParser.getQueryParameterNames()) {
617                            parameterNames.remove(name);
618                            final String value = getParameter(name, true, false);
619                            if (value != null) {
620                                queryParameters.put(name, value);
621                            } else {
622                                if (missingParameter == null) {
623                                    missingParameter = new ArrayList<String>();
624                                }
625                                missingParameter.add(name);
626                            }
627                        }
628
629                        if (missingParameter == null) {
630                            logger.debug("parsing query with parser for " +
631                                    "type '{}' and parameters {}",
632                                    queryParser.getQueryType(),
633                                    queryParameters);
634                            query = queryParser.parseQuery(version,
635                                    queryParameters, this);
636                        } else {
637                            logger.debug("parameters {} missing, cannot parse query",
638                                    missingParameter);
639                            for (String name : missingParameter) {
640                                addDiagnostic(
641                                        SRUConstants.SRU_MANDATORY_PARAMETER_NOT_SUPPLIED,
642                                        name, "Mandatory parameter '" + name +
643                                                "' is missing or empty. " +
644                                                "Required to perform query " +
645                                                "of query type '" +
646                                                queryType + "'.");
647                            }
648                        }
649                    } else {
650                        logger.debug("no parser for query type '{}' found", queryType);
651                        addDiagnostic(SRUConstants.SRU_CANNOT_PROCESS_QUERY_REASON_UNKNOWN, null,
652                                "Cannot find query parser for query type '" + queryType + "'.");
653                    }
654                } else {
655                    logger.debug("cannot determine query type");
656                    addDiagnostic(SRUConstants.SRU_CANNOT_PROCESS_QUERY_REASON_UNKNOWN, null,
657                            "Cannot determine query type.");
658                }
659            }
660
661
662            /*
663             *  check if any parameters where not consumed and
664             *  add appropriate warnings
665             */
666            if (!parameterNames.isEmpty()) {
667                for (String name : parameterNames) {
668                    // skip extraRequestData (aka extensions)
669                    if (!name.startsWith(PARAM_EXTENSION_PREFIX)) {
670                        addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER,
671                                name, "Parameter \"" + name + "\" is not " +
672                                        "supported for this operation.");
673                    }
674                }
675            }
676        }
677
678        /*
679         *  diagnostics == null -> consider as success
680         *  FIXME: this should ne done nicer!
681         */
682        return (diagnostics == null);
683    }
684
685
686    List<SRUDiagnostic> getDiagnostics() {
687        return diagnostics;
688    }
689
690
691    SRUVersion getRawVersion() {
692        return version;
693    }
694
695
696    SRURecordXmlEscaping getRawRecordPacking() {
697        return recordXmlEscaping;
698    }
699
700
701    String getRawRecordSchemaIdentifier() {
702        return getParameter(PARAM_RECORD_SCHEMA, true, false);
703    }
704
705
706    String getRawQuery() {
707        return getParameter(PARAM_QUERY, true, false);
708    }
709
710
711    int getRawMaximumRecords() {
712        return maximumRecords;
713    }
714
715
716    String getRawScanClause() {
717        return getParameter(PARAM_SCAN_CLAUSE, true, false);
718    }
719
720
721    String getRawHttpAccept() {
722        return getParameter(PARAM_HTTP_ACCEPT, true, false);
723    }
724
725
726    int getIndentResponse() {
727        if (config.allowOverrideIndentResponse()) {
728            String s = getExtraRequestData(X_INDENT_RESPONSE);
729            if (s != null) {
730                try {
731                    int x = Integer.parseInt(s);
732                    if ((x > -2) && (x < 9)) {
733                        return x;
734                    }
735                } catch (NumberFormatException e) {
736                    /* IGNORE */
737                }
738            }
739        }
740        return config.getIndentResponse();
741    }
742
743
744    @Override
745    public SRUOperation getOperation() {
746        return operation;
747    }
748
749
750    @Override
751    public SRUVersion getVersion() {
752        return (version != null) ? version : config.getDefaultVersion();
753    }
754
755
756    @Override
757    public boolean isVersion(SRUVersion version) {
758        if (version == null) {
759            throw new NullPointerException("version == null");
760        }
761        return getVersion().equals(version);
762    }
763
764
765    @Override
766    public boolean isVersion(SRUVersion min, SRUVersion max) {
767        if (min == null) {
768            throw new NullPointerException("min == null");
769        }
770        if (max == null) {
771            throw new NullPointerException("max == null");
772        }
773        if (min.getVersionNumber() > max.getVersionNumber()) {
774            throw new IllegalArgumentException("min > max");
775        }
776        final SRUVersion v = getVersion();
777        return (min.getVersionNumber() >= v.getVersionNumber()) &&
778                (v.getVersionNumber() <= max.getVersionNumber());
779    }
780
781
782    @Override
783    public SRURecordXmlEscaping getRecordXmlEscaping() {
784        return (recordXmlEscaping != null)
785                ? recordXmlEscaping
786                : config.getDefaultRecordXmlEscaping();
787    }
788
789
790    @Override
791    public SRURecordPacking getRecordPacking() {
792        return (recordPacking != null)
793                ? recordPacking
794                : config.getDefaultRecordPacking();
795    }
796
797
798    @Override
799    public SRUQuery<?> getQuery() {
800        return query;
801    }
802
803
804    @Override
805    public boolean isQueryType(String queryType) {
806        if ((queryType != null) && (query != null)) {
807            return query.getQueryType().equals(queryType);
808        }
809        return false;
810    }
811
812
813    @Override
814    public String getQueryType() {
815        if (query != null) {
816            return query.getQueryType();
817        }
818        return null;
819    }
820
821
822    @Override
823    public int getStartRecord() {
824        return startRecord;
825    }
826
827
828    @Override
829    public int getMaximumRecords() {
830        if (config.allowOverrideMaximumRecords() &&
831                (getExtraRequestData(X_UNLIMITED_RESULTSET) != null)) {
832            return -1;
833        }
834        if (maximumRecords == -1) {
835            return config.getNumberOfRecords();
836        } else {
837            if (maximumRecords > config.getMaximumRecords()) {
838                return config.getMaximumRecords();
839            } else {
840                return maximumRecords;
841            }
842        }
843    }
844
845
846    @Override
847    public String getRecordSchemaIdentifier() {
848        return recordSchemaIdentifier;
849    }
850
851
852    @Override
853    public String getRecordXPath() {
854        return recordXPath;
855    }
856
857
858    @Override
859    public int getResultSetTTL() {
860        return resultSetTTL;
861    }
862
863
864    @Override
865    public String getSortKeys() {
866        return sortKeys;
867    }
868
869
870    @Override
871    public CQLNode getScanClause() {
872        return scanClause;
873    }
874
875
876    @Override
877    public int getResponsePosition() {
878        return responsePosition;
879    }
880
881
882    @Override
883    public int getMaximumTerms() {
884        if (config.allowOverrideMaximumTerms() &&
885                (getExtraRequestData(X_UNLIMITED_TERMLIST) != null)) {
886            return -1;
887        }
888        if (maximumTerms == -1) {
889            return config.getNumberOfTerms();
890        } else {
891            if (maximumTerms > config.getMaximumTerms()) {
892                return config.getMaximumTerms();
893            } else {
894                return maximumTerms;
895            }
896        }
897    }
898
899
900    @Override
901    public String getStylesheet() {
902        return stylesheet;
903    }
904
905
906    @Override
907    public SRURenderBy getRenderBy() {
908        return renderBy;
909    }
910
911
912    @Override
913    public String getResponeType() {
914        return responseType;
915    }
916
917
918    @Override
919    public String getHttpAccept() {
920        if (httpAccept != null) {
921            return httpAccept;
922        } else {
923            return request.getHeader("ACCEPT");
924        }
925    }
926
927
928    @Override
929    public String getProtocolScheme() {
930        return request.isSecure() ? "https://" : "http://";
931    }
932
933
934    @Override
935    public List<String> getExtraRequestDataNames() {
936        List<String> result = null;
937        for (Enumeration<?> i = request.getParameterNames(); i.hasMoreElements(); ) {
938            String name = (String) i.nextElement();
939            if (name.startsWith(PARAM_EXTENSION_PREFIX)) {
940                if (result == null) {
941                    result = new ArrayList<String>();
942                }
943                result.add(name);
944            }
945        }
946
947        if (result != null) {
948            return result;
949        } else {
950            return Collections.emptyList();
951        }
952    }
953
954
955    @Override
956    public String getExtraRequestData(String name) {
957        if (name == null) {
958            throw new NullPointerException("name == null");
959        }
960        if (!name.startsWith(PARAM_EXTENSION_PREFIX)) {
961            throw new IllegalArgumentException(
962                    "name must start with \"" + PARAM_EXTENSION_PREFIX + "\"");
963        }
964        return request.getParameter(name);
965    }
966
967
968    @Override
969    public HttpServletRequest getServletRequest() {
970        return request;
971    }
972
973
974    @Override
975    public void addDiagnostic(String uri, String details, String message) {
976        final SRUDiagnostic diagnostic =
977                new SRUDiagnostic(uri, details, message);
978        if (diagnostics == null) {
979            diagnostics = new ArrayList<SRUDiagnostic>();
980        }
981        diagnostics.add(diagnostic);
982    }
983
984
985    private List<String> getParameterNames() {
986        List<String> list = new ArrayList<String>();
987        for (Enumeration<?> i = request.getParameterNames();
988                i.hasMoreElements();) {
989            String name = (String) i.nextElement();
990            if (!(name.equals(PARAM_OPERATION) || name.equals(PARAM_VERSION))) {
991                list.add(name);
992            }
993        }
994        return list;
995    }
996
997
998    private String getParameter(String name, boolean nullify,
999            boolean diagnosticIfEmpty) {
1000        String s = request.getParameter(name);
1001        if (s != null) {
1002            s = s.trim();
1003            if (nullify && s.isEmpty()) {
1004                s = null;
1005                if (diagnosticIfEmpty) {
1006                    addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
1007                            name, "An empty parameter \"" + name +
1008                            "\" is not supported.");
1009                }
1010            }
1011        }
1012        return s;
1013    }
1014
1015
1016    private SRUVersion parseAndCheckVersionParameter(SRUOperation operation) {
1017        final String v = getParameter(PARAM_VERSION, true, true);
1018        if (v != null) {
1019            SRUVersion version = null;
1020            if (v.equals(VERSION_1_1)) {
1021                version = SRUVersion.VERSION_1_1;
1022            } else if (v.equals(VERSION_1_2)) {
1023                version = SRUVersion.VERSION_1_2;
1024            } else {
1025                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_VERSION,
1026                        VERSION_1_2, "Version \"" + v +
1027                                "\" is not supported");
1028            }
1029            return version;
1030        } else {
1031            /*
1032             * except for "explain" operation, complain if "version"
1033             * parameter was not supplied.
1034             */
1035            if (operation != SRUOperation.EXPLAIN) {
1036                addDiagnostic(
1037                        SRUConstants.SRU_MANDATORY_PARAMETER_NOT_SUPPLIED,
1038                        PARAM_VERSION, "Mandatory parameter \"" +
1039                                PARAM_VERSION + "\" was not supplied.");
1040            }
1041
1042            /*
1043             * this is an explain operation, assume default version
1044             */
1045            return config.getDefaultVersion();
1046        }
1047    }
1048
1049
1050    private int parseNumberedParameter(String param, String value,
1051            int minValue) {
1052        int result = -1;
1053
1054        if (value != null) {
1055            try {
1056                result = Integer.parseInt(value);
1057                if (result < minValue) {
1058                    addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
1059                            param, "Value is less than " + minValue + ".");
1060                }
1061            } catch (NumberFormatException e) {
1062                addDiagnostic(SRUConstants.SRU_UNSUPPORTED_PARAMETER_VALUE,
1063                        param, "Invalid number format.");
1064            }
1065        }
1066        return result;
1067    }
1068
1069
1070    private CQLNode parseScanQueryParameter(String param, String value) {
1071        CQLNode result = null;
1072
1073        /*
1074         * XXX: maybe query length against limit and return
1075         * "Too many characters in query" error?
1076         */
1077        try {
1078            int compat = -1;
1079            switch (version) {
1080            case VERSION_1_1:
1081                compat = CQLParser.V1POINT1;
1082                break;
1083            case VERSION_1_2:
1084                /* FALL-THROUGH */
1085            case VERSION_2_0:
1086                compat = CQLParser.V1POINT2;
1087            }
1088            result = new CQLParser(compat).parse(value);
1089        } catch (CQLParseException e) {
1090            addDiagnostic(SRUConstants.SRU_QUERY_SYNTAX_ERROR,
1091                    null, "error parsing query");
1092        } catch (IOException e) {
1093            addDiagnostic(SRUConstants.SRU_QUERY_SYNTAX_ERROR,
1094                    null, "error parsing query");
1095        }
1096        return result;
1097    }
1098
1099} // class SRURequestImpl
Note: See TracBrowser for help on using the repository browser.