All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.rest.RequestQuery Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
// * with the License.  You may obtain a copy of the License at                                                              *
// *                                                                                                                         *
// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
// *                                                                                                                         *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
// * specific language governing permissions and limitations under the License.                                              *
// ***************************************************************************************************************************
package org.apache.juneau.rest;

import static org.apache.juneau.internal.ArrayUtils.*;
import static org.apache.juneau.internal.StringUtils.*;

import java.lang.reflect.*;
import java.util.*;

import javax.servlet.http.*;

import org.apache.juneau.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.oapi.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.rest.exception.*;
import org.apache.juneau.utils.*;

/**
 * Represents the query parameters in an HTTP request.
 *
 * 

* Similar in functionality to the {@link HttpServletRequest#getParameter(String)} except only looks in the URL string, not parameters from * URL-Encoded FORM posts. *
This can be useful in cases where you're using GET parameters on FORM POSTs, and you don't want the body of the request to be read. * *

See Also:
*
    *
*/ @SuppressWarnings("unchecked") public final class RequestQuery extends LinkedHashMap { private static final long serialVersionUID = 1L; private final RestRequest req; private HttpPartParser parser; RequestQuery(RestRequest req) { this.req = req; } RequestQuery parser(HttpPartParser parser) { this.parser = parser; return this; } /* * Create a copy of the request query parameters. */ RequestQuery copy() { RequestQuery rq = new RequestQuery(req); rq.putAll(this); return rq; } /** * Adds default entries to these query parameters. * *

* This includes the default queries defined at the resource and method levels. * * @param defaultEntries * The default entries. *
Can be null. * @return This object (for method chaining). */ public RequestQuery addDefault(Map defaultEntries) { if (defaultEntries != null) { for (Map.Entry e : defaultEntries.entrySet()) { String key = e.getKey(); Object value = e.getValue(); String[] v = get(key); if (v == null || v.length == 0 || StringUtils.isEmpty(v[0])) put(key, asStrings(value)); } } return this; } /** * Adds a default entries to these query parameters. * *

* Similar to {@link #put(String, Object)} but doesn't override existing values. * * @param name * The query parameter name. * @param value * The query parameter value. *
Converted to a String using toString(). *
Ignored if value is null or blank. * @return This object (for method chaining). */ public RequestQuery addDefault(String name, Object value) { return addDefault(Collections.singletonMap(name, value)); } /** * Sets a request query parameter value. * *

* This overwrites any existing value. * * @param name The parameter name. * @param value * The parameter value. *
Can be null. */ public void put(String name, Object value) { if (value == null) put(name, null); else put(name, asStrings(value)); } /** * Returns a query parameter value as a string. * *

* If multiple query parameters have the same name, this returns only the first instance. * * @param name The URL parameter name. * @return * The parameter value, or null if parameter not specified or has no value (e.g. "&foo"). */ public String getString(String name) { String[] v = get(name); if (v == null || v.length == 0) return null; // Fix for behavior difference between Tomcat and WAS. // getParameter("foo") on "&foo" in Tomcat returns "". // getParameter("foo") on "&foo" in WAS returns null. if (v.length == 1 && v[0] == null) return ""; return v[0]; } /** * Same as {@link #getString(String)} but returns the specified default value if the query parameter was not * specified. * * @param name The URL parameter name. * @param def The default value. * @return * The parameter value, or the default value if parameter not specified or has no value (e.g. "&foo"). */ public String getString(String name, String def) { String s = getString(name); return StringUtils.isEmpty(s) ? def : s; } /** * Same as {@link #getString(String)} but converts the value to an integer. * * @param name The URL parameter name. * @return * The parameter value, or 0 if parameter not specified or has no value (e.g. "&foo"). */ public int getInt(String name) { return getInt(name, 0); } /** * Same as {@link #getString(String,String)} but converts the value to an integer. * * @param name The URL parameter name. * @param def The default value. * @return * The parameter value, or the default value if parameter not specified or has no value (e.g. "&foo"). */ public int getInt(String name, int def) { String s = getString(name); return StringUtils.isEmpty(s) ? def : Integer.parseInt(s); } /** * Same as {@link #getString(String)} but converts the value to a boolean. * * @param name The URL parameter name. * @return * The parameter value, or false if parameter not specified or has no value (e.g. "&foo"). */ public boolean getBoolean(String name) { return getBoolean(name, false); } /** * Same as {@link #getString(String,String)} but converts the value to a boolean. * * @param name The URL parameter name. * @param def The default value. * @return * The parameter value, or the default value if parameter not specified or has no value (e.g. "&foo"). */ public boolean getBoolean(String name, boolean def) { String s = getString(name); return StringUtils.isEmpty(s) ? def : Boolean.parseBoolean(s); } /** * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource. * *

Examples:
*

* // Parse into an integer. * int myparam = query.get("myparam", int.class); * * // Parse into an int array. * int[] myparam = query.get("myparam", int[].class); * // Parse into a bean. * MyBean myparam = query.get("myparam", MyBean.class); * * // Parse into a linked-list of objects. * List myparam = query.get("myparam", LinkedList.class); * * // Parse into a map of object keys/values. * Map myparam = query.get("myparam", TreeMap.class); *

* *
See Also:
*
    *
  • {@link RestContext#REST_partParser} *
* * @param name The parameter name. * @param type The class type to convert the parameter value to. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(String name, Class type) throws BadRequest, InternalServerError { return getInner(null, null, name, null, getClassMeta(type)); } /** * Same as {@link #get(String, Class)} but allows you to override the part parser. * * @param parser * The parser to use for parsing the string value. *
If null, uses the part parser defined on the resource/method. * @param schema * The schema object that defines the format of the input. *
If null, defaults to the schema defined on the parser. *
If that's also null, defaults to {@link HttpPartSchema#DEFAULT}. *
Only used if parser is schema-aware (e.g. {@link OpenApiParser}). * @param name The parameter name. * @param type The class type to convert the parameter value to. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed or fails schema validation. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(HttpPartParser parser, HttpPartSchema schema, String name, Class type) throws BadRequest, InternalServerError { return getInner(parser, schema, name, null, getClassMeta(type)); } /** * Same as {@link #get(String, Class)} except returns a default value if not found. * * @param name The parameter name. * @param def The default value if the parameter was not specified or is null. * @param type The class type to convert the parameter value to. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(String name, T def, Class type) throws BadRequest, InternalServerError { return getInner(null, null, name, def, getClassMeta(type)); } /** * Same as {@link #get(String, Object, Class)} but allows you to override the part parser. * * @param parser * The parser to use for parsing the string value. *
If null, uses the part parser defined on the resource/method. * @param schema * The schema object that defines the format of the input. *
If null, defaults to the schema defined on the parser. *
If that's also null, defaults to {@link HttpPartSchema#DEFAULT}. *
Only used if parser is schema-aware (e.g. {@link OpenApiParser}). * @param name The parameter name. * @param def The default value if the parameter was not specified or is null. * @param type The class type to convert the parameter value to. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed or fails schema validation. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Class type) throws BadRequest, InternalServerError { return getInner(parser, schema, name, def, getClassMeta(type)); } /** * Returns the specified query parameter value converted to a POJO using the {@link HttpPartParser} registered with the resource. * *

* Similar to {@link #get(String,Class)} but allows for complex collections of POJOs to be created. * *

Examples:
*

* // Parse into a linked-list of strings. * List<String> myparam = query.get("myparam", LinkedList.class, String.class); * * // Parse into a linked-list of linked-lists of strings. * List<List<String>> myparam = query.get("myparam", LinkedList.class, LinkedList.class, String.class); * * // Parse into a map of string keys/values. * Map<String,String> myparam = query.get("myparam", TreeMap.class, String.class, String.class); * * // Parse into a map containing string keys and values of lists containing beans. * Map<String,List<MyBean>> myparam = query.get("myparam", TreeMap.class, String.class, List.class, MyBean.class); *

* *
Notes:
*
    *
  • * Collections must be followed by zero or one parameter representing the value type. *
  • * Maps must be followed by zero or two parameters representing the key and value types. *
* *
See Also:
*
    *
  • {@link RestContext#REST_partParser} *
* * @param name The parameter name. * @param type * The type of object to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(String name, Type type, Type...args) throws BadRequest, InternalServerError { return getInner(null, null, name, null, (ClassMeta)getClassMeta(type, args)); } /** * Same as {@link #get(String, Type, Type...)} but allows you to override the part parser. * * @param parser * The parser to use for parsing the string value. *
If null, uses the part parser defined on the resource/method. * @param schema * The schema object that defines the format of the input. *
If null, defaults to the schema defined on the parser. *
If that's also null, defaults to {@link HttpPartSchema#DEFAULT}. *
Only used if parser is schema-aware (e.g. {@link OpenApiParser}). * @param name The parameter name. * @param type * The type of object to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed or fails schema validation. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError { return getInner(parser, schema, name, null, (ClassMeta)getClassMeta(type, args)); } /** * Same as {@link #get(String, Class)} except returns a default value if not found. * * @param name The parameter name. * @param type * The type of object to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @param def The default value if the parameter was not specified or is null. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(String name, T def, Type type, Type...args) throws BadRequest, InternalServerError { return getInner(null, null, name, def, (ClassMeta)getClassMeta(type, args)); } /** * Same as {@link #get(String, Object, Type, Type...)} but allows you to override the part parser. * * @param parser * The parser to use for parsing the string value. *
If null, uses the part parser defined on the resource/method. * @param schema * The schema object that defines the format of the input. *
If null, defaults to the schema defined on the parser. *
If that's also null, defaults to {@link HttpPartSchema#DEFAULT}. *
Only used if parser is schema-aware (e.g. {@link OpenApiParser}). * @param name The parameter name. * @param type * The type of object to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @param def The default value if the parameter was not specified or is null. * @param The class type to convert the parameter value to. * @return The parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed or fails schema validation. * @throws InternalServerError Thrown if any other exception occurs. */ public T get(HttpPartParser parser, HttpPartSchema schema, String name, T def, Type type, Type...args) throws BadRequest, InternalServerError { return getInner(parser, schema, name, def, (ClassMeta)getClassMeta(type, args)); } /** * Same as {@link #get(String, Class)} except for use on multi-part parameters * (e.g. "&key=1&key=2&key=3" instead of "&key=@(1,2,3)"). * *

* This method must only be called when parsing into classes of type Collection or array. * * @param name The query parameter name. * @param c The class type to convert the parameter value to. * @param The class type to convert the parameter value to. * @return The query parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed. * @throws InternalServerError Thrown if any other exception occurs. */ public T getAll(String name, Class c) throws BadRequest, InternalServerError { return getAllInner(null, null, name, null, getClassMeta(c)); } /** * Same as {@link #get(String, Type, Type...)} except for use on multi-part parameters * (e.g. "&key=1&key=2&key=3" instead of "&key=@(1,2,3)"). * *

* This method must only be called when parsing into classes of type Collection or array. * * @param name The query parameter name. * @param type * The type of object to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @param The class type to convert the parameter value to. * @return The query parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed. * @throws InternalServerError Thrown if any other exception occurs. */ public T getAll(String name, Type type, Type...args) throws BadRequest, InternalServerError { return getAllInner(null, null, name, null, (ClassMeta)getClassMeta(type, args)); } /** * Same as {@link #getAll(String, Type, Type...)} but allows you to override the part parser. * * @param parser * The parser to use for parsing the string value. *
If null, uses the part parser defined on the resource/method. * @param schema * The schema object that defines the format of the input. *
If null, defaults to the schema defined on the parser. *
If that's also null, defaults to {@link HttpPartSchema#DEFAULT}. *
Only used if parser is schema-aware (e.g. {@link OpenApiParser}). * @param name The query parameter name. * @param type * The type of object to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @param The class type to convert the parameter value to. * @return The query parameter value converted to the specified class type. * @throws BadRequest Thrown if input could not be parsed or fails schema validation. * @throws InternalServerError Thrown if any other exception occurs. */ public T getAll(HttpPartParser parser, HttpPartSchema schema, String name, Type type, Type...args) throws BadRequest, InternalServerError { return getAllInner(parser, schema, name, null, (ClassMeta)getClassMeta(type, args)); } /** * Returns true if the request contains any of the specified query parameters. * * @param params The list of parameters to check for. * @return true if the request contains any of the specified query parameters. */ public boolean containsAnyKeys(String...params) { for (String p : params) if (containsKey(p)) return true; return false; } /** * Locates the special search query arguments in the query and returns them as a {@link SearchArgs} object. * *

* The query arguments are as follows: *

    *
  • * "&s=" - A comma-delimited list of column-name/search-token pairs. *
    Example: "&s=column1=foo*,column2=*bar" *
  • * "&v=" - A comma-delimited list column names to view. *
    Example: "&v=column1,column2" *
  • * "&o=" - A comma-delimited list column names to sort by. *
    Column names can be suffixed with '-' to indicate descending order. *
    Example: "&o=column1,column2-" *
  • * "&p=" - The zero-index row number of the first row to display. *
    Example: "&p=100" *
  • * "&l=" - The number of rows to return. *
    0 implies return all rows. *
    Example: "&l=100" *
  • * "&i=" - The case-insensitive search flag. *
    Example: "&i=true" *
* *
Notes:
*
    *
  • * Whitespace is trimmed in the parameters. *
* * @return * A new {@link SearchArgs} object initialized with the special search query arguments. *
Returns null if no search arguments were found. */ public SearchArgs getSearchArgs() { if (hasAny("s","v","o","p","l","i")) { return new SearchArgs.Builder() .search(getString("s")) .view(getString("v")) .sort(getString("o")) .position(getInt("p")) .limit(getInt("l")) .ignoreCase(getBoolean("i")) .build(); } return null; } /** * Returns true if the query parameters contains any of the specified names. * * @param paramNames The parameter names to check for. * @return true if the query parameters contains any of the specified names. */ public boolean hasAny(String...paramNames) { for (String p : paramNames) if (containsKey(p)) return true; return false; } /* Workhorse method */ private T getInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta cm) throws BadRequest, InternalServerError { try { if (cm.isMapOrBean() && isOneOf(name, "*", "")) { ObjectMap m = new ObjectMap(); for (Map.Entry e : this.entrySet()) { String k = e.getKey(); HttpPartSchema pschema = schema == null ? null : schema.getProperty(k); ClassMeta cm2 = cm.getValueType(); if (cm.getValueType().isCollectionOrArray()) m.put(k, getAllInner(parser, pschema, k, null, cm2)); else m.put(k, getInner(parser, pschema, k, null, cm2)); } return req.getBeanSession().convertToType(m, cm); } T t = parse(parser, schema, getString(name), cm); return (t == null ? def : t); } catch (SchemaValidationException e) { throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name); } catch (ParseException e) { throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ; } catch (Exception e) { throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ; } } /* Workhorse method */ @SuppressWarnings("rawtypes") private T getAllInner(HttpPartParser parser, HttpPartSchema schema, String name, T def, ClassMeta cm) throws BadRequest, InternalServerError { String[] p = get(name); if (p == null) return def; if (schema == null) schema = HttpPartSchema.DEFAULT; try { if (cm.isArray()) { List c = new ArrayList(); for (int i = 0; i < p.length; i++) c.add(parse(parser, schema.getItems(), p[i], cm.getElementType())); return (T)toArray(c, cm.getElementType().getInnerClass()); } else if (cm.isCollection()) { Collection c = (Collection)(cm.canCreateNewInstance() ? cm.newInstance() : new ObjectList()); for (int i = 0; i < p.length; i++) c.add(parse(parser, schema.getItems(), p[i], cm.getElementType())); return (T)c; } } catch (SchemaValidationException e) { throw new BadRequest(e, "Validation failed on query parameter ''{0}''. ", name); } catch (ParseException e) { throw new BadRequest(e, "Could not parse query parameter ''{0}''.", name) ; } catch (Exception e) { throw new InternalServerError(e, "Could not parse query parameter ''{0}''.", name) ; } throw new InternalServerError("Invalid call to getParameters(String, ClassMeta). Class type must be a Collection or array."); } private T parse(HttpPartParser parser, HttpPartSchema schema, String val, ClassMeta c) throws SchemaValidationException, ParseException { if (parser == null) parser = this.parser; return parser.createPartSession(req.getParserSessionArgs()).parse(HttpPartType.QUERY, schema, val, c); } /** * Converts the query parameters to a readable string. * * @param sorted Sort the query parameters by name. * @return A JSON string containing the contents of the query parameters. */ public String toString(boolean sorted) { Map m = (sorted ? new TreeMap() : new LinkedHashMap()); for (Map.Entry e : this.entrySet()) { String[] v = e.getValue(); m.put(e.getKey(), v.length == 1 ? v[0] : v); } return SimpleJsonSerializer.DEFAULT.toString(m); } /** * Converts this object to a query string. * *

* Returned query string does not start with '?'. * * @return A new query string, or an empty string if this object is empty. */ public String toQueryString() { StringBuilder sb = new StringBuilder(); for (Map.Entry e : this.entrySet()) { for (int i = 0; i < e.getValue().length; i++) { if (sb.length() > 0) sb.append("&"); sb.append(urlEncode(e.getKey())).append('=').append(urlEncode(e.getValue()[i])); } } return sb.toString(); } @Override /* Object */ public String toString() { return toString(false); } //----------------------------------------------------------------------------------------------------------------- // Helper methods //----------------------------------------------------------------------------------------------------------------- private ClassMeta getClassMeta(Type type, Type...args) { return req.getBeanSession().getClassMeta(type, args); } private ClassMeta getClassMeta(Class type) { return req.getBeanSession().getClassMeta(type); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy