Restlet: Retrieving http body in verifier - java

When I want retrieve the http body of a post in my Request Verifier it kinda resets my entity and I get a nullpointer exception when I want to get the http body in my resource class.
Verifier:
JsonRepresentation jsonrep;
try {
Representation entity = request.getEntity();
jsonrep = new JsonRepresentation(entity);
//bug: entity resets when getJsonObject is being called.
JSONObject jsonobj = jsonrep.getJsonObject();
if(companyId != jsonobj.getInt("id_companies")){
return Verifier.RESULT_INVALID;
}
...
AppResource:
#Post
public Representation addApp(Representation rep) throws Exception{
//rep is null
JsonRepresentation jsonrep = new JsonRepresentation(rep);
When I dont't call:
JSONObject jsonobj = jsonrep.getJsonObject();
it just works fine.
Is anybody facing the same issue or got a solution for it?
Thanks in advance!

In fact, by default, the representation is an InputRepresentation that doesn't store the representation content.
In your case, the simplest way is to wrap the representation into a StringRepresentation within your verifier:
Representation entity = request.getEntity();
StringRepresentation sEntity = new StringRepresentation(entity);
request.setEntity(sEntity);
JsonRepresentation jsonrep = new JsonRepresentation(sEntity);
Then, the string representation will be automatically provided to your server resource method...
Hope it helps you.
Thierry

Related

Java - Access JSON Element (llegalFormatConversionException: d !=java.lang.String)

I am trying to learn JAVA and build a plugin for Minecraft; I have been successfully able to get the JSON data from my api endpoint however, the issue I am facing right now is an llegalFormatConversionException: d !=java.lang.String which means that the format I am trying to make into string isn't equal to the type of string that it's looking for.
I am trying to access a JSON element from my endpoint called condition
{
"todaysdate": "2021-02-12",
"temperature": 25,
"description": [
"Overcast"
],
"condition": 122,
}
Coming from C#; I know there's a website called json2sharp where you can create a root class for the JSON. I'm not sure how it would be applied in Java but currently, my code looks like this.
private String fetchWeather() throws IOException, InvalidConfigurationException {
// Download
final URL url = new URL(API);
final URLConnection request = url.openConnection();
// Set HEADER
request.setRequestProperty("x-api-key", plugin.apiKey);
request.setConnectTimeout(5000);
request.setReadTimeout(5000);
request.connect();
// Convert to a JSON object to print data
JsonParser jp = new JsonParser(); //from gson
JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent())); //Convert the input stream to a json element
JsonObject rootobj = root.getAsJsonObject();
//JsonElement code = rootobj.get("condition");
String condition_code = rootobj.get("condition").toString();
plugin.getLogger().fine(String.format(
"[%s] Weather is %d",
world.getName(), condition_code
));
return condition_code;
}
If I call
private JsonObject fetchWeather() throws IOException, InvalidConfigurationException {
// Download
final URL url = new URL(API);
final URLConnection request = url.openConnection();
// Set HEADER
request.setRequestProperty("x-api-key", plugin.apiKey);
request.setConnectTimeout(5000);
request.setReadTimeout(5000);
request.connect();
// Convert to a JSON object to print data
JsonParser jp = new JsonParser(); //from gson
JsonElement root = jp.parse(new InputStreamReader((InputStream) request.getContent())); //Convert the input stream to a json element
JsonObject rootobj = root.getAsJsonObject();
return rootobj;
}
state = fetchWeather();
plugin.getLogger().warning(state.toString());
I do get the actual JSON with all of the elements so I know the URL and accessing it is completely working, but I get an llegalexception for format if I try to call and print with logger the condition code.
So, if I change the return type in fetchWeather() to be the root json object and then try to print it with the state variable above it works; but if I try to return the condition json element and print it it gives me an iilegal exception.
Now before I posted this question I did read some other questions people had but I couldn't get a working solution from their suggested answers. So, I am hoping someone can point me out on what I'm doing wrong because I know I'm messing up somewhere with the variable format.
Thanks.
Line 37: state = fetchWeather();
Line 103:
plugin.getLogger().fine(String.format(
"[%s] Weather is %d",
world.getName(), bId
));
condition_code variable is String. You should use the %s format specifier.

How to properly get the body of a WebTarget get request response (JAX-RS)?

How do i get the body of a HTTP get request with the javax.ws.rs library?
I tried:
Response response = service.path(path).request().get();
String body = response.readEntity(String.class);
but that doesn't work. Instead i get this exception: java.lang.NoClassDefFoundError: javax/ws/rs/core/NoContentException
Then i tried:
Response response = service.path(path).request(MediaType.APPLICATION_JSON_TYPE).get();
String body = response.readEntity(JSONObject.class);
because I should actually get a JSON String back but i don't need it as JSON Object, that's the reason why i tried it like above before.
But that doesn't work at all, i even can't compile and get the (IntelliJ) message
no instance(s) of type variable(s) exist so that JSONObject conforms to String inference variable T has incompatible bounds: equality constraints: JSONObject upper bounds: Object, String
Addition:
I get 200 as response status so everything worked fine and when i do the same in e.g. Postman or Fiddler it works.
You can do this by using Jackson: first you create object from body
ObjectMapper mapper = new ObjectMapper();
InputStream responseBody = response.getBody().in();
JSONObject jsonobject = mapper.readValue(responseBody, JSONObject.class);
Then transform jsonobject to string:
String jsonInString = mapper.writeValueAsString(jsonobject);
In this specific case it should also work with
Response response = service.path(path).request(MediaType.APPLICATION_JSON_TYPE).get();
String body = response.readEntity(String.class);
because it tries to read the Entity and construct an object of the given type out of it. So I think that should work.

Generate ImapMessage from Response Object

I've created a custom command to retrieve multiple objects in the same request (in order to solve some performance issues), instead of using the folder method .getMessage(..) which in my case retrieved an ImapMessage object:
Argument args = new Argument();
args.writeString(Integer.toString(start) + ":" + Integer.toString(end));
args.writeString("BODY[]");
FetchResponse fetch;
BODY body;
MimeMessage mm;
ByteArrayInputStream is = null;
Response[] r = protocol.command("FETCH", args);
Response status = r[r.length-1];
if(status.isOK()) {
for (int i = 0; i < r.length - 1; i++) {
...
}
}
Currently I'm validating if the object is a ImapResponse like this:
if (r[i] instanceof IMAPResponse) {
IMAPResponse imr = (IMAPResponse)r[i];
My question is, how can I turn this response into an ImapMessage?
Thank you.
Are you trying to download the entire message content for multiple messages at once? Have you tried using IMAPFolder.FetchProfileItem.MESSAGE? That will cause Folder.fetch to download the entire message content, which you can then access using the Message objects.
I haven't succeeded yet to convert it into a IMAPMessage but I'm now able transform it into a MIME Message. It isn't perfect but I guess it will have to work for now:
FetchResponse fetch = (FetchResponse) r[i];
BODY body = (BODY) fetch.getItem(0);
ByteArrayInputStream is = body.getByteArrayInputStream();
MimeMessage mm = new MimeMessage(session, is);
Then, it can be used to get information like this:
String contentType = mm.getContentType();
Object contentObject = mm.getContent();
There are also other methods to get information like the sender, date, etc.

Getting java object back from JSON

I am putting some java objects in the Json at server side
like this :
ArrayList<VisjsNode>visjsNodes = new ArrayList<VisjsNode>();
ArrayList<VisjsConnection> visjsConnections = new ArrayList<VisjsConnection>();
String jsondata = null;
org.json.JSONObject object = new org.json.JSONObject();
try {
object.put("nodes", visjsNodes);
object.put("connections", visjsConnections);
jsondata = object.toString();
Now is there a way I can get these objects back from this json (jsondata) at client side
I am doing this:
com.google.gwt.json.client.JSONValue jsonValue = JSONParser.parseStrict(jsondata);
com.google.gwt.json.client.JSONObject jsonObject = jsonValue.isObject();
jsonValue = jsonObject.get("nodes");
Now I am trying this to get ArrayList back , by doing this
ArrayList<VisjsNode>visjsNodesFromjson = jsonValue ;
But its not compiling ,it says Incompatable types...
Can you please guide how we can retrieve the Java Object back from Json ..
That's because you're using two different JsonObject class. First you use the one which comes from org.json (org.json.JsonObject) and the other is com.google.gwt.json.client.JSONObject. Nevertheless, they're named similar, they're completely different classes.

How to deal with input parameter in CXF request handler in general?

I have been doing some work with apache CXF(version 2.2.2) JAX-RS. I am trying to introduce data validation layer in CXF request handler before business method be invoked. Fortunately :), I am encountering an issue on input parameter handling in request handler(DataValidationHandler). I can read the JSON Object manually by following code lines in request handler. But it's duplicated with JSONProvider registered in CXF framework. Because JSON object input stream only can be read once, otherwise we will meet exception "java.io.EOFException: No content to map to Object due to end of input". Moreover, duplicated JSON object deserializing will impacts performance. Following code is sample for your reference.
Read JSON Object from HTTP body manually:
OperationResourceInfo ori = paramMessage.getExchange().get(OperationResourceInfo.class);
MultivaluedMap<String, String> values = new MetadataMap<String, String>();
List<Object> objList = JAXRSUtils.processParameters(ori, values, paramMessage);
Register JSONProvider in CXF JAX-RS framework:
<bean id="JSONProvider" class="com.accela.govxml2.jaxrs.util.JSONProvider"></bean>
Read JSON Object to Java Object from input stream:
public Object readFrom(......){
ObjectMapper objectMapper = new ObjectMapper();
Object result = objectMapper.readValue(entityStream, TypeFactory.defaultInstance().constructType(genericType));
Return result;
}
I am dealing with Path Parameter manually by following code lines.
OperationResourceInfo ori = paramMessage.getExchange().get(OperationResourceInfo.class);
URITemplate t1 = ori.getClassResourceInfo().getURITemplate();
URITemplate t2 = ori.getURITemplate();
UriInfo uriInfo = new UriInfoImpl(paramMessage, null);
MultivaluedMap<String, String> map = new MetadataMap<String, String>();
t1.match(uriInfo.getPath(), map);
String str = map.get(URITemplate.FINAL_MATCH_GROUP).get(0);
t2.match(str, map);
String pathParameter= null;
if (map.containsKey("pathParam") && !ValidationUtil.isEmpty(map.get("pathParam")))
{
pathParameter= map.get("pathParam").get(0);
}
My questions are here:
How to deal with POST/PUT input parameter of http body in request handler in general?
How to avoid performance issue to read input parameter efficiently?
Is there any way to inject the validation (handler/interceptor) layer between parameter reading by CXF(JSONProvider) and business method invoking?
Is there any elegant way to deal with path parameter?
Thanks for your help. Any comments & suggestions will be appreciated.
Regards,
Dylan
I have found another way to inject DataValidation Interceptor into reading parameter phase. We can reuse deserialized input model from message content, which be deserialized by JSONProvider registered in framework. It can improve performance, because only deserialize input model once.
public class DataValidationInInterceptor extends AbstractPhaseInterceptor<Message>
{
public DataValidationInInterceptor()
{
super(Phase.READ);
}
#Override
public void handleMessage(Message message)
{
OperationResourceInfo ori = message.getExchange().get(OperationResourceInfo.class);
Method method = ori.getMethodToInvoke();
Class<?>[] types = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < types.length; i++)
{
Class<?> type = types[i];
List obj = (List) message.getContent(List.class);
System.out.println(obj);
System.out.println(type);
}
}
}
After researching, I can read the input stream twice based on the following question's answer ( Read stream twice ).
However, JSON object deserializing performance is still my concern. Who has better solution for it?
Intercept request and change message content from CoyoteInputStream to ByteArrayInputStream, so I can read the InputStream twice.
InputStream in = message.getContent(InputStream.class);
if (in != null)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(in, baos);
byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
message.setContent(InputStream.class, bais);
}
Reset ByteArrayInputStream before Read JSON Object to Java Object from input stream:
public Object readFrom(......){
ObjectMapper objectMapper = new ObjectMapper();
if (entityStream.markSupported())
{
entityStream.reset();
}
Object result = objectMapper.readValue(entityStream, TypeFactory.defaultInstance().constructType(genericType));
return result;
}

Categories