How to use the query parameters in Java Jersey Application? - java

I am following a tutorial and also used the Stackoverflow question here.
Here is my Java class:
package com.crunchify.tutorial;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Consumes;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.json.simple.JSONObject;
#Path("api")
public class CrunchifyAPI {
#SuppressWarnings("unchecked")
#GET
#Path("/get")
#Consumes(MediaType.TEXT_PLAIN)
public String get(
#DefaultValue("111") #QueryParam("user") int user,
#Context UriInfo uriInfo
) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
String nameParam = queryParams.getFirst("user");
System.out.println("Data Received: " + uriInfo.getRequestUri().getQuery()
+ " | " + nameParam);
JSONObject obj = new JSONObject();
obj.put("auth", true);
String ret = JSONObject.toJSONString(obj);
return ret;
}
}
Following is what I am GET'ing from postman:
GET>> localhost/api/get?user=123
Response is:
{"auth":true}
Server console:
Starting Crunchify's Embedded Jersey HTTPServer...
Started Crunchify's Embedded Jersey HTTPServer Successfully !!!
Data Received: ?user=123 | null
User Authenticated: true
I have tried with passing String, Integer etc but nothing works. The uri Info is getting printed correctly and the response back is also fine. The issue is that I am not getting the parameter to be read in Java Code. I will need to pass many other parameters once I am able to get this going. Please suggest. Thanks!!

I think you're trying too hard. As far as I can tell, doing the following should get you what you want if you call localhost/api/get?user=123:
package com.crunchify.tutorial;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Consumes;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.json.simple.JSONObject;
#Path("api")
public class CrunchifyAPI {
#SuppressWarnings("unchecked")
#GET
#Path("/get")
#Consumes(MediaType.TEXT_PLAIN)
public String get(
#DefaultValue("111") #QueryParam("user") Integer user,
#Context UriInfo uriInfo
) {
System.out.println("Data Received: " + uriInfo.getRequestUri().getQuery()
+ " | " + name);
JSONObject obj = new JSONObject();
obj.put("auth", true);
String ret = JSONObject.toJSONString(obj);
return ret;
}
}
All that extra stuff with the query string isn't needed if all you need is the information passed in the user parameter.

#QueryParam("user") int user
the value of that user int should be 123
See https://www.mkyong.com/webservices/jax-rs/jax-rs-queryparam-example/

Well, I think you're having a problem with Java Types.
If your user is an Integer you should pass it to String first if you want to work with a String (Integer.toString() or String.valueof()).
But the way you're passing the parameter is bothering me, I'm not sure if you can pass integers by text plain medi types.

Related

Looking for a alternative to create a JSON object instead using Json.createObjectBuilder

Just for learn, I'm trying understand a better way to create a JSON response. I expect the following output on my request:
HTTP/1.1 200 OK
Content-Length: 15
Content-Type: application/json
connection: close
{
"status": "ok"
}
Using Java for this task (In this case Quarkus), I have the follow code:
package br.com.bb.feedback.Controller;
import javax.ws.rs.Path;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.GET;
import javax.ws.rs.core.Response;
import javax.json.JsonObject;
import javax.json.Json;
#Path("/health-check")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class HealthCheckController {
#GET
public Response check() {
JsonObject health = Json.createObjectBuilder().add("status", "ok").build();
return Response.status(Response.Status.OK).entity(health).build();
}
}
So, to build the entity object I use Json.createObjectBuilder(). Have a better way to do this?
"Better" way is a matter of opinion, but there are easy to use alternate ways:
Using a Map. In Java 9+, that's really easy:
#GET
public Response check() {
return Response.status(Response.Status.OK)
.entity(Map.of("status", "ok"))
.build();
}
In Java 8 or earlier, it takes a bit extra:
#GET
public Response check() {
Map<String, String> health = new HashMap<>();
health.put("status", "ok");
return Response.status(Response.Status.OK).entity(health).build();
}
Using a POJO:
#GET
public Response check() {
return Response.status(Response.Status.OK)
.entity(new Health("ok"))
.build();
}
static final class Health {
private String status;
// constructors, getters, and setters here
}

Adding next line in a large json string from Jersey, Rest API

I want to split a very large string into multiple lines. When I use next line characters, those characters are displayed in Swagger UI without having multiple lines.
Code is as below:
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
#Path("test")
#Api(tags = {"Testing"})
public class TestingService {
#Context
private HttpServletRequest request;
#GET
#Path("testing")
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Testing",
response = String.class)
public String getString(
throws Exception {
return "abcdef,fghijk";
}
}
Input is:
abcdef,fghijk
Current Output:
{
"messages": "abcdef,fghijk"
}
Expected Output:
{
"messages": "abcdef,
fghijk"
}
I have tried \n, \\n, \\\n and \r\n.
Note: I am using Jersey Framework for this REST API & Swagger for UI.
Thanks a lot in advance for the help.
Workaround :
(a) split the string on some basis
(b) create a list of object where object stores the split string.
(c)Send the list to UI as response.
Output will be as below:
{
"messages_list": [
{"message": "abcdef"},
{"message": "fghijk"}
]
}
Note: This was not the actual requirement. This is just workaround to display long & different messages in different lines.

How can I transfer an ArrayList<Map> via REST?

Edit:
I tried to implement the suggestions of #Durgpal Singh and #Nikhil. I changed the code so it looks like this.
Client:
Client client = ClientBuilder.newClient();
WebTarget target = client
.target("http://localhost:8087/api/ls3algorithm/" + petrinets + "/" + Integer.toString(k) + "/" + Float.toString(theta));
Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
Map<String, List<Map>> result_ = response.readEntity(new GenericType<Map<String, List<Map>>>() { });
result = (ArrayList<Map>) result_.get("data");
Server:
ArrayList<Map> result;
result = new Ls3Algorithm().execute(new File("petrinetze").getAbsolutePath(), k, theta);
Map<String, List<Map>> map = new HashMap<>();
map.put("data", result);
return Response.ok(map).build();
Unfortunately this leads to Exception in thread "main" org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=interface java.util.Map, genericType=java.util.Map<java.lang.String, java.util.List<java.util.Map>>.
Where do I go wrong?
-------------------------------
I'm pretty new to RESTful web services and currently writing a microservice which provides a calculating algorithm. I'm testing the service as posted below.
Workflow:
Client saves some data in a MongoDB database and sends the names of the relevant files via #PathParam as part of the GET request. The server then retrieves the files from the MongoDB, processes its algorithm and sends back the result as List<Map> packed in a Response object.
Goal:
Transfer the result (List<Map>) as JSON and print it out on the client console.
Client:
package ls3test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSInputFile;
public class Ls3TransmissionTest {
final static String petrinets = "eins, zwei, drei, vier";
final static int k = 3;
final static float theta = 0.9f;
public static void main(String[] args) throws IOException {
[... save all the relevant files in the MongoDB ...]
ArrayList<Map> result = new ArrayList<Map>();
Client client = ClientBuilder.newClient();
WebTarget target = client
.target("http://localhost:8087/api/ls3algorithm/" + petrinets + "/" + Integer.toString(k) + "/" + Float.toString(theta));
Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
result = response.readEntity(new GenericType<ArrayList<Map>>() {
});
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = ow.writeValueAsString(result);
}
}
Server:
package service;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
#SuppressWarnings("deprecation")
#Path("/ls3algorithm")
public class Resource {
// SLF4J is provided with Dropwizard
Logger log = LoggerFactory.getLogger(Resource.class);
#SuppressWarnings("rawtypes")
#GET
#Path("/{petrinets}/{k}/{theta}")
#Produces(MediaType.APPLICATION_JSON)
public Response ls3execute(#PathParam("petrinets") String petrinetNames, #PathParam("k") int k,
#PathParam("theta") float theta) {
[... get all the relevant files from the MongoDB ...]
List<Map> result;
Ls3Algorithm ls3Algorithm = new Ls3Algorithm();
result = ls3Algorithm.execute(new File("petrinetze").getAbsolutePath(), k, theta);
GenericEntity<List<Map>> entity = new GenericEntity<List<Map>>(result) {};
Response response = Response.ok(entity).build();
return response;
}
}
This is not working, the exception I get is posted below:
Exception in thread "main" org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.ArrayList<java.util.Map>.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:231)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:155)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1085)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:874)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:834)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:368)
at org.glassfish.jersey.client.InboundJaxrsResponse$2.call(InboundJaxrsResponse.java:126)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:419)
at org.glassfish.jersey.client.InboundJaxrsResponse.runInScopeIfPossible(InboundJaxrsResponse.java:267)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:123)
at ls3test.Ls3TransmissionTest.main(Ls3TransmissionTest.java:89)
Ls3TransmissionTest.java:89 is ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
I spent plenty of time now doing research on this problem, but I cannot find an example that really fits it. What do I miss? Any help or hint is highly appreciated!
You can send a map. Like this
Map<String, Object> map = new HashMap<>();
map.put("data", entity);
Response.ok(map).build();
return Response;
Cannot see why do you need to wrap the List with GenericEntity. Something as simple as below will work:-
#SuppressWarnings("rawtypes")
#GET
#Path("/{petrinets}/{k}/{theta}")
#Produces(MediaType.APPLICATION_JSON)
public Response ls3execute(#PathParam("petrinets") String petrinetNames, #PathParam("k") int k,
#PathParam("theta") float theta) {
//[... get all the relevant files from the MongoDB ...]
List<Map> result;
Ls3Algorithm ls3Algorithm = new Ls3Algorithm();
result = ls3Algorithm.execute(new File("petrinetze").getAbsolutePath(), k, theta);
Response response = Response.ok(result).build();
return response;
}
And in the client side,
String result = response.readEntity(String.class);
return result;

unable to read the file from postman

I was trying postman with a Java clientava client. I was getting the following output
com.squareup.okhttp.internal.http.RealResponseBody#c9673cf
the original output is
Curl -I "http://ec2-52-34-14-38.us-west-2.compute.amazonaws.com:14000/webhdfs/v1/user/ec2-user/prediction_output/part-00000?user.name=ec2-user&op=OPEN"
1234.566788
here is my java code.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import java.io.IOException;
#Path("/hello")
public class HelloWorldService {
#GET
#Produces("application/json")
public Response getMsg() throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://ec2-52-34-14-38.us-west-2.compute.amazonaws.com:14000/webhdfs/v1/user/ec2-user/prediction_output/part-00000?user.name=ec2-user&op=OPEN")
.build();
com.squareup.okhttp.Response responses = null;
responses = client.newCall(request).execute();
System.out.println(responses);
return Response.status(200).entity(responses.body().toString()).build();
}
}
any help will be appreciated.
Use response.body().string() not toString().
Use the above also in System.out.println.
Your method getMsg() says #Produces("application/json") but your built response is text/plain.

How to get full REST request body using Jersey?

How can one get the full HTTP REST request body for a POST request using Jersey?
In our case the data will be XML. Size would vary from 1K to 1MB.
The docs seem to indicate you should use MessageBodyReader but I can't see any examples.
Turns out you don't have to do much at all.
See below - the parameter x will contain the full HTTP body (which is XML in our case).
#POST
public Response go(String x) throws IOException {
...
}
You could use the #Consumes annotation to get the full body:
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
#Path("doc")
public class BodyResource
{
#POST
#Consumes(MediaType.APPLICATION_XML)
public void post(Document doc) throws TransformerConfigurationException, TransformerException
{
Transformer tf = TransformerFactory.newInstance().newTransformer();
tf.transform(new DOMSource(doc), new StreamResult(System.out));
}
}
Note: Don't forget the "Content-Type: application/xml" header by the request.
Try this using this single code:
import javax.ws.rs.POST;
import javax.ws.rs.Path;
#Path("/serviceX")
public class MyClassRESTService {
#POST
#Path("/doSomething")
public void someMethod(String x) {
System.out.println(x);
// String x contains the body, you can process
// it, parse it using JAXB and so on ...
}
}
The url for try rest services ends .... /serviceX/doSomething
Since you're transferring data in xml, you could also (un)marshal directly from/to pojos.
There's an example (and more info) in the jersey user guide, which I copy here:
POJO with JAXB annotations:
#XmlRootElement
public class Planet {
public int id;
public String name;
public double radius;
}
Resource:
#Path("planet")
public class Resource {
#GET
#Produces(MediaType.APPLICATION_XML)
public Planet getPlanet() {
Planet p = new Planet();
p.id = 1;
p.name = "Earth";
p.radius = 1.0;
return p;
}
#POST
#Consumes(MediaType.APPLICATION_XML)
public void setPlanet(Planet p) {
System.out.println("setPlanet " + p.name);
}
}
The xml that gets produced/consumed:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<planet>
<id>1</id>
<name>Earth</name>
<radius>1.0</radius>
</planet>
It does seem you would have to use a MessageBodyReader here. Here's an example, using jdom:
import org.jdom.Document;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.MediaType;
import javax.ws.rs.ext.MultivaluedMap;
import java.lang.reflect.Type;
import java.lang.annotation.Annotation;
import java.io.InputStream;
#Provider // this annotation is necessary!
#ConsumeMime("application/xml") // this is a hint to the system to only consume xml mime types
public class XMLMessageBodyReader implements MessageBodyReader<Document> {
private SAXBuilder builder = new SAXBuilder();
public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
// check if we're requesting a jdom Document
return Document.class.isAssignableFrom(type);
}
public Document readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) {
try {
return builder.build(entityStream);
}
catch (Exception e) {
// handle error somehow
}
}
}
Add this class to the list of resources your jersey deployment will process (usually configured via web.xml, I think). You can then use this reader in one of your regular resource classes like this:
#Path("/somepath") #POST
public void handleXMLData(Document doc) {
// do something with the document
}
I haven't verified that this works exactly as typed, but that's the gist of it. More reading here:
http://weblogs.java.net/blog/mhadley/archive/2008/02/integrating_jer_2.html
http://blogs.oracle.com/sandoz/entry/jersey_and_abdera_with_a

Categories

Resources