Batch HTTP requests in Play!Framework - java

I have the current set of of routes implemented (for example):
GET /api/:version/:entity my.controllers.~~~~~
GET /api/:version/:entity/:id my.controllers.~~~~~
POST /api/:version/:entity my.controllers.~~~~~
POST /api/:version/:entity/:id my.controllers.~~~~~
DELETE /api/:version/:entity my.controllers.~~~~~
POST /api/:version/search/:entity my.controllers.~~~~~
And they work beautifully. Now let's say I want to implement a "batch endpoint" for the same API. It should look something like this:
POST /api/:version/batch my.controllers.~~~~~
and the body should look like this:
[
{
"method": "POST",
"call": "/api/1/customer",
"body": {
"name": "antonio",
"email": "tonysmallhands#gmail.com"
}
},
{
"method": "POST",
"call": "/api/1/customer/2",
"body": {
"name": "mario"
}
},
{
"method": "GET",
"call": "/api/1/company"
},
{
"method": "DELETE",
"call": "/api/1/company/22"
}
]
To do that I would like to know how I can call the play framework router to pass those requests? I was planning to use something similar as what is advised for unit tests:
#Test
public void badRoute() {
Result result = play.test.Helpers.routeAndCall(fakeRequest(GET, "/xx/Kiki"));
assertThat(result).isNull();
}
by going into the source code of routeAndCall(), you find something like this:
/**
* Use the Router to determine the Action to call for this request and executes it.
* #deprecated
* #see #route instead
*/
#SuppressWarnings(value = "unchecked")
public static Result routeAndCall(FakeRequest fakeRequest) {
try {
return routeAndCall((Class<? extends play.core.Router.Routes>)FakeRequest.class.getClassLoader().loadClass("Routes"), fakeRequest);
} catch(RuntimeException e) {
throw e;
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
/**
* Use the Router to determine the Action to call for this request and executes it.
* #deprecated
* #see #route instead
*/
public static Result routeAndCall(Class<? extends play.core.Router.Routes> router, FakeRequest fakeRequest) {
try {
play.core.Router.Routes routes = (play.core.Router.Routes)router.getClassLoader().loadClass(router.getName() + "$").getDeclaredField("MODULE$").get(null);
if(routes.routes().isDefinedAt(fakeRequest.getWrappedRequest())) {
return invokeHandler(routes.routes().apply(fakeRequest.getWrappedRequest()), fakeRequest);
} else {
return null;
}
} catch(RuntimeException e) {
throw e;
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
So my question is: Is there a less "hacky" way to do this with Play (I am not against mixing Scala and Java to get to it) than to copy the above code? I would have also like to give the option of executing the batched calls in parallel or in sequence ... I guess instantiating only one Routes using the class loader would be problematic then?

You can use the following method call to route your fake requests:
Play.current.global.onRouteRequest. Please see this post for a full example: http://yefremov.net/blog/play-batch-api/

You can probably use WS API for that, but personally I'd just create a private methods for collecting data and use them from both - 'single' and also 'batch' actions - it will be faster for sure.

Related

how to continue the program when there is exception while calling feign client api in java

public string prospect(List<ProspectRequest> prospectRequest, String primaryClientId) {
if(p2p(primaryClientId)=="Success") {
for(ProspectRequest prospect : prospectRequest) {
p2p(prospect.getId());
}
// rest of code i would like to continue
}
}
public string p2p(String id){
crmApi.getProspectId(id);//this is external client api
String message = "Success";
return message;
}
if p2p(primaryClientId) is failed then I need to stop the entire process .How do i like to continue with "rest of code i would like to continue".
if the feign client api crmApi.getProspectId is success then the success message is returned and then its a good case.
If the crmApi.getProspectId api gives error then I still need to continue the p2p() program for next set of clients also .How does that work.
Thanks in Advance.
Seems like you just need to use a try catch finally block
public string prospect(List<ProspectRequest> prospectRequest, String primaryClientId) {
try {
if(p2p(primaryClientId)=="Success") {
for(ProspectRequest prospect : prospectRequest) {
p2p(prospect.getId());
}
} catch (RuntimeException e) {} // Exception can be specified exp. RuntimeException
finally {
// rest of code i would like to continue
}
}
}
public string p2p(String id){
crmApi.getProspectId(id);//this is external client api
String message = "Success";
return message;
}
if you need something out of the first if-block you can put an option (if the if-block fails) in the catch-block. When
if(p2p(primaryClientId)=="Success") {
for(ProspectRequest prospect : prospectRequest) {
p2p(prospect.getId());
}
throws an checked exception you need to change RuntimeExpection to the superclass Exception
edited:
for(ProspectRequest prospect : prospectRequest) {
try {
p2p(prospect.getId());
} catch (RuntimeExpection e) {}
}

Grouping with Sentry

I want to group exception with Sentry, the exception comes from different servers, but I want all exception by type together, for example, all NPE be grouped. I know you can extend EventBuilderHelper and this is how sentry group things, but sentry java doesn't provide features to send an event with fingerprints of the method, error type, etc, as others SDK like this example in docs.sentry.io
function makeRequest(method, path, options) {
return fetch(method, path, options).catch(err => {
Sentry.withScope(scope => {
// group errors together based on their request and response
scope.setFingerprint([method, path, err.statusCode]);
Sentry.captureException(err);
});
});
}
this is what I try to do, but in this scope, don't have knowledge about method, error, etc.
package com.test;
import io.sentry.SentryClient;
import io.sentry.event.EventBuilder;
import io.sentry.event.helper.ContextBuilderHelper;
public class FingerprintEventBuilderHelper extends ContextBuilderHelper {
private static final String EXCEPTION_TYPE = "exception_type";
public FingerprintEventBuilderHelper(SentryClient sentryClient) {
super(sentryClient);
}
#Override
public void helpBuildingEvent(EventBuilder eventBuilder) {
super.helpBuildingEvent(eventBuilder);
//Get the exception type
String exceptionType =
if (exceptionType != null) {
eventBuilder.withTag(EXCEPTION_TYPE, exceptionType);
}
//Get method information and params
if (paramX != null) {
eventBuilder.withTag("PARAM", paramX);
}
}
}
the json send to the server has some information about the exception, but I dont know hoy to get it
...
"release": null,
"dist": null,
"platform": "java",
"culprit": "com.sun.ejb.containers.BaseContainer in checkExceptionClientTx",
"message": "Task execution failed",
"datetime": "2019-06-26T14:13:29.000000Z",
"time_spent": null,
"tags": [
["logger", "com.test.TestService"],
["server_name", "localhost"],
["level", "error"]
],
"errors": [],
"extra": {
"Sentry-Threadname": "MainThread",
"rid": "5ff37e943-f4b4-4hc9-870b-4f8c4d18cf84"
},
"fingerprint": ["{{ default }}"],
"key_id": 3,
"metadata": {
"type": "NullPointerException",
"value": ""
},
...
You can get the type of exception that was raised, but I have my doubts about getting the parameters related to functions in the trace
EventBuilderHelper myEventBuilderHelper = new EventBuilderHelper() {
public void helpBuildingEvent(EventBuilder eventBuilder) {
eventBuilder.withMessage("Overwritten by myEventBuilderHelper!");
Map<String, SentryInterface> ifs = eventBuilder.getEvent().getSentryInterfaces();
if (ifs.containsKey("sentry.interfaces.Exception"))
{
ExceptionInterface exI = (ExceptionInterface) ifs.get("sentry.interfaces.Exception");
for (SentryException ex: exI.getExceptions()){
String exceptionType = ex.getExceptionClassName();
}
}
}
};
If you look at the sendException method of the client, it initiates the ExceptionInterface with the actual Exception
public void sendException(Throwable throwable) {
EventBuilder eventBuilder = (new EventBuilder()).withMessage(throwable.getMessage()).withLevel(Level.ERROR).withSentryInterface(new ExceptionInterface(throwable));
this.sendEvent(eventBuilder);
}
And the constructor for the same is like
public ExceptionInterface(Throwable throwable) {
this(SentryException.extractExceptionQueue(throwable));
}
public ExceptionInterface(Deque<SentryException> exceptions) {
this.exceptions = exceptions;
}
So each exception get converted to a SentryException, but the original exception is not stored. So if you need params also, you will need to throw a custom exception with those parameter and also override the sendException method, not a straightforward way

Android - waiting for json to get value before continuing with method

I am using this library in order to get data from server. The data is decode in the server into JSONObject. The method I made will call the url and return the number of rows inside a mysql table.
The method is working however, I cannot return the value properly from my method:
ParseLevels.java
static public int countLevels() {
final int[] count = {0};
AndroidNetworking.get("https://example.com/gameLevels.php?option=count")
.setPriority(Priority.LOW)
.build()
.getAsJSONObject(new JSONObjectRequestListener() {
#Override
public void onResponse(JSONObject response) {
// responce is {count:20}
try {
count[0] = response.getInt("count"); // this is getting the value of 20
} catch (JSONException e) {
e.printStackTrace();
}
Log.d("responce_app", String.valueOf(count[0]));
}
#Override
public void onError(ANError error) {
// handle error
Log.d("responce_app", String.valueOf(error)); //Logs out 20
}
});
return count[0]; // return always 0
}
When I call from my MainActivity
Log.d("responce_app", String.valueOf(ParseLevels.countLevels()));
the method returns always 0.
I understand that the return is fired before the jsonObject is fetched however, how can I wait for the method to fetch the jsonObject and after return the value?
In iOS I use something like:
static func getLevels(feedsArray: (levelsCount : [Int]) -> Void) {
}
how could convert this into Java?
How can I wait for the method to fetch the jsonObject and after return
the value?
Two options:
1. Do code inside onResponse method which want to execute according to result of request.
2. Create event listener using interface and implement it in countLevels method caller class to execute block of code when onResponse method execution done
You should look at the the Making Synchronous Request example from your library's README:
https://github.com/amitshekhariitbhu/Fast-Android-Networking
ANRequest request = AndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllUsers/{pageNumber}")
.addPathParameter("pageNumber", "0")
.addQueryParameter("limit", "3")
.build();
ANResponse<List<User>> response = request.executeForParsed(new TypeToken<List<User>>() {});
if (response.isSuccess()) {
List<User> users = responseTwo.getResult();
} else {
//handle error
}
You would return your count in the response.isSuccess() conditional.

Check client is alive with HttpServletRequest object

I'm writing a Spring web application and I'm mapping the "/do" URL path to the following Controller's method
#Controller
public class MyController
{
#RequestMapping(value="/do", method=RequestMethod.GET)
public String do()
{
File f = new File("otherMethodEnded.tmp");
while (!f.exists())
{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
// ok, let's continue
}
}
The otherMethodEnded.tmp file is written by one another Controller's method, so when the client calls the second URL I expect the first method to exit the while loop.
Everything works, except when the client calls the "/do" URL and then closes the connection before the response was received. The problem is that the server remains in the while (!f.exists()) loop even though the client is down and cannot call the second URL to unlock the while loop.
I would try to retrieve the connection status of the "/do" URL and exit the loop when the connection is closed by the client, but I cannot find any way to do so.
I tried with the HttpServletRequest.getSession(false) method but the returned HttpSession object is always not null, so the HttpServletRequest object is not updated in case of connection close of the client.
How can I check whether the client is still waiting for the risponse or not?
The simplest way to verify something is not right is to define a timeout value and then during your loop test if your time spent waiting has exceeded the timeout.
something like:
#Controller
public class MyController
{
private static final long MAX_LOOP_TIME = 1000 * 60 * 5; // 5 minutes? choose a value
#RequestMapping(value="/do", method=RequestMethod.GET)
public String do()
{
File f = new File("otherMethodEnded.tmp");
long startedAt = System.currentTimeMillis()
boolean forcedExit = false;
while (!forcedExit && !f.exists())
{
try {
Thread.sleep(5000);
if (System.currentTimeMillis() - startedAt > MAX_LOOP_TIME) {
forcedExit = true;
}
} catch (InterruptedException e) {
forcedExit = true;
}
}
// ok, let's continue
// if forcedExit , handle error scenario?
}
}
Additionally: InterruptedException is not something to blindly catch and ignore. see this discussion
In your case I would really exit the while loop if you're interrupted.
You only know if the client is no longer waiting on your connection when you notice the output stream you write to (response.outputstream) is closed. But there isn't a way to detect it.
(see this question for details)
Seeing as you've indicated your client does occasional callbacks, you could on the clientside poll if the other call has been completed. If this other call has completed, do the operation, otherwise return directly and have the client do the call again. (assuming you are sending json, but adapt as you require)
something like
public class MyController
{
#RequestMapping(value="/do", method=RequestMethod.GET)
public String do()
{
File f = new File("otherMethodEnded.tmp");
if (f.exists()) {
// do what you set out to do
// ok, let's continue
// and return with a response that indicates the call did what it did
// view that returns json { "result" : "success" }
return "viewThatSIgnalsToClientThatOperationSucceeded";
} else {
// view that returns json: { "result" : "retry" }
return "viewThatSignalsToClientToRetryIn5Seconds";
}
}
}
Then the clientside would run something like: (pseudojavascript as it's been a while)
val callinterval = setInterval(function() { checkServer() }, 5000);
function checkServer() {
$.ajax({
// ...
success: successFunction
});
}
function successFunction(response) {
// connection succeeded
var json = $.parseJSON(response);
if (json.result === "retry") {
// interval will call this again
} else {
clearInterval(callinterval);
if (json.result === "success") {
// do the good stuff
} else if (json.result === "failure") {
// report that the server reported an error
}
}
}
Ofcourse this is just semi-serious code but it's roughly how i'd try it if I were to have the dependency. If this is regarding afile upload, keep in mind that this file may not contain all of the bytes yet. file exists != file = completely uploaded, unless you use move it. cp / scp / etc. is not atomic.

RESTEasy JSON Exception Response: Send list of objects

I have web client (HTML5) and backend server based on RESTEasy webservices and session beans. In my server side code I am iterating over list of objects and per object i am executing some business logic:
List<TestTO> failedTestList = new ArrayList<TestTO>();
for (TestTO testTO : testTOList) {
try {
// some weired business logic :P
} catch (Exception e) {
logger.error("Unable to create data -" + e.getMessage());
failedTestList.add(testTO);
}
}
if (!failedTestList.isEmpty()) {
// throw custom exception embedded with failed TO list
}
I have written custome exception handlers, to catch exceptions and return proper response back to client. This class looks like:
public class CustomExceptionHandler implements ExceptionMapper<CustomException> {
public CustomException getCustomErrorCode(final CustomException customException) {
// Some logic to get cause and set error code
return customException;
}
#Override
public Response toResponse(final CustomException customException) {
return Response.serverError().entity(
"{\"Error Code\":\"" + getCustomErrorCode(customException).getErrorCode() + "\", "
+ "\"Error Message\":\"" + customException.getLocalizedMessage() + "\"}").build();
}
}
I am thinking of an option to send this failed TO list back to client, so that it can understand processing of which objects got failed. I was going through different articles, but could not find anything which fits my requirement.
Please give me an idea and link to reference, on how to implement such requirement. Please note that, my client expects response in JSON format. Please let me know, if you require more information.
Thanks.

Categories

Resources