Throwing WebapplicationException from one webapp and getting UniformInterfaceException on other - java

I see a weird issue. I have two webapps. One for the rest webservice that we exposed using Jersey. Another has the JSF Front End implementation which calls above webservice to fetch the details. We are using Tomcat as a container.
The issue that i am facing is when i call a rest webservice, i throw WebApplicationException like this
catch (CustomExceptions e) {
ResponseBuilder response = new ResponseBuilderImpl();
response.status(500);
response.entity(e.getStackTrace());
throw new WebApplicationException(e, response.build());
}
And on the other hand on FE webapps, i do the following:
try {
r.get(MyClass.class);
return "SUCCESS";
} catch (WebApplicationException wae) {
return "FAILURE";
} catch (UniformInterfaceException wae) {
return "FAILURE";
}
Here in the catch block i was expecting the WebApplicationException but its throwing UniformInterfaceException which is weird. Also if it throws UniformInterfaceException, It does not maintain the stacktrace. Even the response that i passed in from rest call is lost. Can somebody help me how can i get the original stacktrace from the rest call?
Thanks in advance.

As specified in both the user guide and the api documentation, a UniformInterfaceException is the expected result when you attempt to retrieve a specific entity by type but receive a non-200-series response. The WebApplicationException is a convenience exception for your use on the server side to embed custom responses in an exception.
Nothing is "lost". Keep in mind you're making an HTTP request here. If you want to inspect the status code and response yourself, use
ClientResponse response = r.get(ClientResponse.class);
if (response.getStatus() == <expected status code>) [
response.getEntity(MyClass.class);
// do happy path stuff
} else {
// bad stuff here
}
Alternately, assume everything will go well, and only check the response on exceptions:
try {
r.get(MyClass.class);
} catch (UniformInterfaceException e) {
ClientResponse response = e.getResponse();
// do whatever with the raw response
}

I had same issue before. I think it's because Jersey WebResource.get method throws only UniformInterfaceException which is not in the inheritance hierarchy of WebApplicationExcpetion. What I did is to abandon Jersey client side api but use plain ApacheHttpClient. Will be able to get the right stacktrace in the httpResponse contents.
Hope this helps.
Thanks, Ryan. I tried your solution. Very nice.

Related

Send an error to appengine endpoint client

In my GAE endpoint, in my user register API method, I check whether a user already exists with the given username. If a user already exists, I need to send an error to the endpoint client.
Currently I throw an exception like below from endpoint.
User user = ofy().load().key(Key.create(User.class, username)).now();
if (user != null) {
throw new BadRequestException("Username already exists");
}
Then in endpoint client, I catch the exception like below.
try {
gaeEndpoint.registerUser(mEmail, mPassword).execute();
} catch (HttpResponseException e) {
mErrorMessage = e.getContent();
} catch (IOException e) {
mErrorMessage = "Failed to communicate with server. Please check your internet connection.";
}
When endpoint throws a BadRequestException, client gets HttpResponseException e and e.getContent() contains a json string including the error message string sent from endpoint. I need to parse json to get the actual error message sent from server.
Even though this works, I feel that there should be a better way to send an error message to the client from the endpoint. So, does anyone know a better (or a recommended) way of doing this?
I think you're doing everything right.
HTTP is designed to send a response with 4xx code and meaningful content to a user. It can be HTML for a browser, JSON for an api client or anything that can provide some context to the client.
If you're worried about using exceptions, that's fine too. Of course, you can prepare a response with a manually set status code, but that doesn't change anything, it's just more code. It's also less likely that you'll create a bug when you're using exceptions in this case.

The requested route has not been mapped in Spark

I want to do something to sign up users with spark+java+hibernate+postgres
This is my code:
post("/registrar", (request, response) -> {
EntityManagerFactory emf = Persistence.
createEntityManagerFactory("compradorcitoPU");
EntityManager em = emf.createEntityManager();em.getTransaction().begin();
em.persist(u);
em.getTransaction().commit();
em.close(); return null; });
but this error shows up:
INFO spark.webserver.MatcherFilter - The requested route
[/registrarnull] has not been mapped in Spark
I had a similar problem. The items I'm returning are large and I wanted to write them out over stream. So, my software looked like this:
post("/apiserver", "application/json", (request, response) -> {
log.info("Received request from " + request.raw().getRemoteAddr());
ServerHandler handler = new ServerHandler();
return handler.handleRequest(request, response);
});
In my handler, I got the raw HttpResponse object, opened its OutputStream and wrote over it like so:
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(response.raw().getOutputStream(), records);
Since I knew I had written over the OutputStream what the caller had asked for at that point (or an error), I figured I could just return null. My program worked fine. Spark would route the request to my handler as expected. And, since I was writing over the raw OutputStream, I was getting back what was expected on the client side. But, I kept seeing the message '/apiserver route not defined' in my server logs.
In looking at the Spark documentation, it says:
The main building block of a Spark application is a set of routes. A route is made up of three simple pieces:
A verb (get, post, put, delete, head, trace, connect, options)
A path (/hello, /users/:name)
A callback (request, response) -> { }
Obviously Spark does not know what you wrote over the raw HttpResponse and as a web-server, you should be providing some response to callers. So, if your response is null, you haven't fulfilled the requirements of providing a callback and you get the error that there's no map found even if Spark behaved as expected otherwise. Just return a response (null is not a response, "200 OK" is) and the error will go away.
[Edit] Spelling and grammar.
do not "return null" instead return the empty string or something
As explained in the comments of this issue, SparkJava considers that returning null means the route has not been mapped and therefore it logs the error message and replies a response with 404 status.
To avoid such behaviour you have to return a String (possibly empty).
The error message will disappear and a response with the String as body and 200 status will be replied.
In my case, I had to implement the options request to please the preflight CORS check:
options("/*", (request,response)->{
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if(accessControlRequestMethod != null){
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
});

Jersey Client 2.19 doesn't throw exceptions on bad status code

I'm building a Rest Client using jersey-client 2.19:
public ReleaseEntity createRelease(ReleaseEntity newRelease, int workspaceId) {
Releases wrapper = new Releases();
wrapper.setData(Arrays.asList(newRelease));
WebTarget target = client.target(urlPrefix)
.path(AgmUrls.getReleasesUrl(workspaceId));
wrapper = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(wrapper, MediaType.APPLICATION_JSON))
.readEntity(Releases.class);
return wrapper.getData().get(0);
}
The client is initialized in the constructor
this.client = ClientBuilder.newClient();
The problem is that, in case of bad response the post call does not throw an exception, neither explicit nor runtime.
Should I do this manually, or am I missing something?
This question is quite dated, but better prevent others to repeat the same mistake...
Instead of
result = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(input, MediaType.APPLICATION_JSON))
.readEntity(Releases.class);
which has post(entity) return a Response on which readEntity is called, better use overloaded post(entity, responseType) which will throw WebApplicationException on Error-Statuscodes.
// throws runtime exception derived from WebApplicationException
// on error-statuscodes
result = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(input, MediaType.APPLICATION_JSON), Releases.class);
Every http method in JAX-RS has such overloaded methods for reading either Responses or representation objects. Reading representation objects is highly advised to consume potential response bodies in any case.
// consumes response as string and guarantees to close the http call.
// A no-arg delete(); would be a potential leak!
target.request().delete(String.class);
Unfortunately, when response-headers must be read, it is still required to read Response instead of the representation objects.
The framework should not throw an exception. The user should handle the response however they see fit. This is the same with any client. The Response object will contain all the context you need to handle the response however you see fit.
So what you should do is get the Response first
Response response = target
.request()
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity(wrapper, MediaType.APPLICATION_JSON));
Then you can check the status
int status = response.getStatus();
Then handle the status
if (status == 200) {
wrapper = response.readEntity(Releases.class);
...
} else {
handleOtherStatus();
}
If you do not get the Response first, then you have no idea what the actual problem is, as readEntity(...) will fail (as there it's not the body you are expecting), and throw a different exception. With the Response at least you have some context if you want to tell the user what actual problem is.

Handling wrong URLs

I have csv files stored on my server. If I enter the right key (which is a part of URL) I get what I want, but if the entered key was wrong my app crashes. I want to be able to catch the error.
String url="http://mysite.com/template";
url=url+et.getText().toString().toLowerCase()+".csv";
csv.setURL(url);
if(csv.checkURL()){
enterToDB();
}
else{
tv.setText("Wrong key");
}
and my CSVReader looks like:
public void setURL(String file){
try {
URL url = new URL(file);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
success=true;
in.close();
} catch (MalformedURLException e) { success=false;} catch (IOException e) { success=false; }
}
public boolean checkURL(){
return success;
}
}
Without the complete minimal code necessary to replicate the problem, and without information about what the specific error is (like a stack trace), I'm just guessing that the setURL/checkURL routines don't exactly do what you want. They appear to assume that openStream will throw an exception if the key in the URL is wrong, but that's not the case. Even if the path or key in the URL is wrong, the HTTP server will still provide a response. The response might not be 200 OK and the response body might not include what you're looking for, but it'll still give a response, and the open stream can be used to read the response.
So, if I understand correctly, you actually want to inspect the contents of the response (including probably the HTTP status code), before deciding whether the "success" is true or false.
Thane posted some code I gave him over in How should I handle server timeouts and error code responses to an http post in Android App?. I recommend reviewing it and seeing if it provides the structure you need to handle successful and failed responses accordingly.
Making sense?

How to redirect user when certain condition is met using REST JAX-RS implementation?

I used to just use Tomcat and JSP pages which I can execute query, then assign query result into the array or object then pass that data onto client side via response.
request.setAttribute("errorMessage", "this is error!!");
request.getRequestDispatcher("report.jsp").forward(request, response);
In client jsp code, I could do something like:
${errorMessage}
Then the "this is error!!" message would show up.
I want to do the same thing with REST JAX-RS GlassFish v3.
#Path("schedule/test")
#POST
#Consumes("application/x-www-form-urlencoded")
#Produces("application/vnd.ms-excel")
public Object tmpTest(String content) {
try {
//just my method to execute query and get result
Vector out = (Vector)QueryManager.executeQuery;
//if query result is empty, I want user to redirect to report.jsp page
if(out.isEmpty()) {
request.setAttribute("errorMessage", "This is error!!");
request.getRequestDispatcher("report.jsp").forward(request, response);
return null;
}
....continue code......
}
This results in mysterious exception I've never seen.
java.lang.ClassCastException: $Proxy109 cannot be cast to org.apache.catalina.core.ApplicationHttpRequest
at org.apache.catalina.core.ApplicationHttpRequest.getRequestFacade(ApplicationHttpRequest.java:1001)
at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:472)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:379)
at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:336)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:314)
So how can I redirect a user to report.jsp and also pass message like "This is error" ?
The client jsp expects the error msg variable to have a value:
<b>${errorMessage}</b>
That's not RESTful. You need to throw a WebApplicationException with a specific status code so that the client understands what exactly went wrong. E.g. when it's actually the server's mistake:
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
Or when it was after all client's mistake:
throw new WebApplicationException(Response.Status.BAD_REQUEST);
See also HTTP status code definitions for an overview.
The ClassCastException which you're facing is by the way occurring because the dispatched request is actually not an instance of the servletcontainer-provided implementation (in this particular case, the one of Tomcat or a Tomcat-fork). After all, you shouldn't be doing it this way. You're developing a REST webservice, not a JSP/Servlet website. It are two distinct worlds.
As mentioned before, you should try WebApplicationException.
I believe you this would give you your desired answer:
Try this:
if(out.isEmpty()) {
java.net.URI location = new java.net.URI("report.jsp");
throw new WebApplicationException(Response.seeOther(location).build());
}

Categories