What use is Java 6 interface MultivaluedMap? - java

What use is Java 6 interface MultivaluedMap?

The interface does not belong to "Java", meaning that the interface is not a part of the core libraries. It is a part of the javax.ws.rs hierarchy which is part of the JAX-RS specification. It is used by frameworks implementing the specification such as Jersey. It is used whenever maps should refer to not only a single value but to any number of values. An example for the use would be for example the storage of a request header where you one might want to add several values per key. Or even no keys in some cases where it is easier to handle an empty list compared to a null value.
Take this HTTP-header for example:
Accept-Encoding: compress;q=0.5, gzip;q=1.0
You would model this by
MultivaluedMap<String, String> map = ...
map.add("Accept-Encoding", "compress;q=0.5");
map.add("Accept-Encoding", "gzip;q=1.0");
internally in Jersey. This type of multiple value storage is a common problem in Java that is addressed by other implementors of maps such as Guava.
This is basically what the javadoc says:
A map of key-values pairs. Each key can have zero or more values.

Its a map of key-values pairs. Each key can have zero or multiple values
public interface MultivaluedMap<K,V> extends java.util.Map<K,java.util.List<V>>

A good use of a MultivaluedMap is with UriInfo. If you are writing a REST endpoint that takes in several QueryParams, you can use UriInfo to get all of the params and extract them using the getQuery() call. For example:
public void get(#Context UriInfo ui) {
MultivaluedMap params = ui.getRequestUri().getQuery();
// now do what you want with your params
}
The MultivaluedMap is helpful because you could have parameters with multiple values. For example if it was a customer database and you wanted to get several customers, your map would have the key of "customerID" and several values linked to it.

MultiValuedMap is a part of javax.ws.rs.core package, not Core Java. It is mainly used for storing Headers values in requests
private MediaType getMediaType(Class entityClass, Type entityType, MultivaluedMap<String, Object> headers) {
final Object mediaTypeHeader = headers.getFirst("Content-Type");
....
}
Also, it is quite useful with UriInfo
private String getJsonpFunctionName(){
UriInfo uriInfo = getUriInfo();
if (uriInfo == null) {
return null;
}
MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
if (queryParameters == null) {
return null;
}
return queryParameters.getFirst("jsonp");
}

Related

How to send map(key-value pair) as request parameter in a GET call

Is it possible to send map as parameter in a GET call.? i searched and i could find for list and set collection. But did not find anything for map collection.
I tried the following,
My controller method looks like this.
#GetMapping("/test")
public ResponseEntity<?> mapTest(#RequestParam Map<String,String> params) {
LOG.info("inside test with map "+ params );
return new ResponseEntity<String>("MAP", HttpStatus.OK);
}
And i sent the following request from postman
http://localhost:8080/test?params={a:abc,b:bcd}
Everything works without errors and exceptions. But the map which i received looks like key=params , value={a:abc,b:bcd}
I expected the received map to be like key1="a" value1=abc ,key2="b" value2="bcd"
This is documented in the Spring MVC guide:
When an #RequestParam annotation is declared as Map<String, String> or MultiValueMap<String, String> argument, the map is populated with all request parameters.
This means that the response you currently get is the expected result. The Map contains a list of all parameters, and in your case, you only have a single parameter called param.
If you need a custom parameter mapping, you'll have to implement it by yourself. Since you're not using JSON either, you probably have to manually parse the parameter.
However, if your goal is to have a dynamic map of parameters, you can still use the Map<String, String>, but you'll have to change your request into:
http://localhost:8080/test?a=abc&b=bcd

Can a query parameter name be variable? Like: #QueryParam("//anything")

I have a resource class and within it a #GET that takes one query param called operation (this should be static) and then I want to take a variable number of other query params that can be named anything.
My first thought was to do something like this:
public Response get(
#QueryParam("operation") String operation,
#QueryParam("list") final List<String> list) {
//do stuff
}
The problem here is that I would have to make a request like:
...?operation=logging&list=ABC&list=XYZ
While what I want is to be able to have something like this:
...?operation=logging&anything=ABC&something_else=XYZ
Is there a way to make the list query param #QueryParam(//anything)?
In doing some information gathering I ran across this sort of approach:
#GET
public void gettest(#Context UriInfo ui) {
MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
String operation = queryParams.getFirst("operation");
for (String theKey : queryParams.keySet()) {
System.out.println(queryParams.getFirst(theKey));
//do stuff with each other query param
}
}
Is multivaluedmap the way to go for this situation -- Or is there a way to use a variable query param name? Or a better approach? Thanks.
Edit/Update:
This is using javax.ws.rs
The use case is: this application being used as a tool for mocking responses (used for testing purposes in other applications). The mock responses are retrieved from a DB by looking up the 'operation' and then some sort of 'id'. The actual id used could be any of the "list" query params given. The reason for this is to give flexibility in different applications to use this mock service -- the urls in applications may be constructed many different ways and this makes if so one doesn't have to change around their code to be able to use the mock service.
As in this question, use a map:
#RequestMapping(value = {"/search/", "/search"}, method = RequestMethod.GET)
public String search(
#RequestParam Map<String,String> allRequestParams, ModelMap model) {
return "viewName";
}

Best practice generating object for response

I have rest server with spring.
There is a lot of requests where one of the params is fields fields is the set of fields that server should return in response. like: /?fields=[id,name] and server should return JSON object with both fields
I would like to know what is the best practice for generating such response.
We do it like this:
private Map<String, Object> processBook(BookEntity book, Set<String> fields, String locale){
Map<String, Object> map = new HashMap<String, Object>();
//..
if(fields.contains(ID)){
map.put(ID, book.getId());
}
if(fields.contains(ISBN)){
map.put(ISBN, book.getIsbn());
}
if(fields.contains(DESCRIPTION)){
if(locale.equals(UserLocale.UK)) map.put(DESCRIPTION, book.getDescriptionUa());
else if(locale.equals(UserLocale.RU)) map.put(DESCRIPTION, book.getDescriptionRu());
else map.put(DESCRIPTION, book.getDescriptionEn());
}
//..
return map;
}
Maybe there is much better alternative?
Note that in your case you obtain all data from DB - fully filled BookEntity object, and then show only requested fields.
In my opinion it'd be "much better alternative" to delegate field list to appropriate downstream integration call and get BookEntity object only with necessary fields. Then mentioned above method will reduce to just one line, your DB responses will be more lightweight, so it will bring simplicity and optimization gain to your system.
Any adequate DB provides such functionality: SQL or NoSQL, etc.
P.S. Plus standard approach of Object to JSON mapping such as Jackson or GSON at top level.
Instead of having a Map, you could have and object with the attributes you need and set them, instead of adding to map.Then you can use Google's Gson to transform your object into a Json object.Take a look at this quick tutorial.
One approach is to have an asMap function.
Map<String, Object> map = book.asMap();
map.keySet().retainAll(fields);

How to pass generic List<User> to restful web services

I have the requirement to create the search operation using restful web services, i.e, using #GET. The method signature takes String and List as input argument and returns List.
public Generic List <Employer> getAllEmployer(String employeeName, Generic List <employeeLocation>);
Kindly request if someone could describe on how to implement the same. Should I use query param or path param or form param. I need to return the List of employer in json format.
if the employee locations are just string, pass them as comma seperated values and spring will take care of converting it to list. I would rather suggest just to have it as a pathparam rather than having it as a query param.
This is just my opinion but I think that the "RESTful" way to pass multiple parameters with the same name would be to a MultiValueMap.
Spring and Jersey both have implementations of MultiValueMap however, below is an example of a spring implementation:
#RequestMapping(method = RequestMethod.GET, value = {"/employer/_search"})
public List<Employer> search(#RequestParam MultiValueMap<String,String> params) {
return someService.search(params);
}
They way that you would call this url would then become:
/employer/_search?employeeName=name&location=1&location=2&location=3
Then behind the scenes spring will create the MultiValueMap for you which is a Map<String,List<String>> where any parameters with the same name are put into the same list.

Is it portable to reference the request parameter map after a request-cycle?

I'd like to know if its conform to the java servlet specification 2.5 to reference/save the return value of request.getParameterMap() between requests.
The final specification only states at page 203:
Returns: an immutable java.util.Map containing parameter names as keys
and parameter values as map values. The keys in the parameter map are of
type String. The values in the parameter map are of type String array.
But it's not clear to me:
if the Map is only immutable to the application, not to the application server
if it's allowed that the application server may re-use the instance for another request (for example, if the parameters and their values are the same as in the previous request)
EDIT:
The reason for this:
I'd like to save the request map of each request and in case of an error to print them out for diagnostic purposes.
if the Map is only immutable to the application, not to the application server
It's immutable in the API, yes. It also makes sense, what's the point of changing the request parameter map? The servletcontainer implementation in turn can of course hold of a mutable map of it "behind the scenes". But you should not worry about the implementation specific details.
if it's allowed that the application server may re-use the instance for another request (for example, if the parameters and their values are the same as in the previous request)
No, the server doesn't do that. It just creates a new one. The parameters are bound to a specific request.
If you want to collect the parameters of every request during a session, then you need to do this yourself. The best place for this would be a Filter.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest hsr = (HttpServletRequest) request;
List<Map<String, String[]>> allParameters = (List<Map<String, String[]>>) hsr.getSession().getAttribute("allParameters");
if (allParameters == null) {
allParameters = new ArrayList<Map<String,String[]>>();
hsr.getSession().setAttribute("allParameters", allParameters);
}
allParameters.add(hsr.getParameterMap());
chain.doFilter(request, response);
}
For all intents and purposes, you cannot modify the contents of the map. However, if you want to keep for future reference, you can always create a new map of your own, copy over all the key/value pairs, and keep it in the Session f.e. (That will cause funkiness if the user has 2 windows open at the same time, but that story is for another time...)
I think you can safely save it for next request. I do this all the time.
Not sure about other implementations. It creates a new HashMap for every requests in Tomcat,
parameters = new HashMap();
parameters = copyMap(getRequest().getParameterMap());
mergeParameters();
parsedParams = true;

Categories