Skip to content

Exception Handling Section #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7ccde39
Create generic exception mapper
Jul 27, 2020
17cb996
Exception mapper for NotFoundException
Jul 27, 2020
2cea4f1
Exception handling section in README
Jul 27, 2020
4ef5b9c
Change status code to int
Jul 27, 2020
f1ba9ff
README updates
Jul 27, 2020
f50926b
Add file hotspot
Jul 27, 2020
dfe2460
Add gitkeep
Aug 31, 2020
d3c23b4
Merge branch 'master' of ssh://github.com/openliberty/guide-rest-intr…
Aug 31, 2020
774dcc9
Add hotspot
Aug 31, 2020
8142f90
Fix comment
Aug 31, 2020
6eb52be
Fix hotspot
Aug 31, 2020
2875ad2
Add file numbers to code commands
Aug 31, 2020
9c46dc4
Merge branch 'qa' into exception-handling
gkwan-ibm Oct 7, 2020
281bdae
gitignore update
Oct 8, 2020
a0bf96d
Suggested README changes made
Oct 8, 2020
5d55c19
Add endpoint to get individual properties to show
Oct 8, 2020
a0b98f1
README updates
Oct 8, 2020
4f64414
Fix class names in README
Oct 9, 2020
91d1901
Fix line too long for site
Oct 9, 2020
f6ad019
Mention creation of `PropertyNotFoundException.java`
Oct 9, 2020
8703afb
Fix file name in README
Oct 9, 2020
64c991f
Fix line too long for site
Oct 9, 2020
f622c39
Hotspot fixes
Oct 9, 2020
bd43aa4
Hotspot fix
Oct 9, 2020
ed6c2ae
Change output to JSON
Oct 9, 2020
2cf7c12
Merge branch 'qa' into exception-handling
gkwan-ibm Dec 10, 2020
ab34acb
Merge branch 'qa' into exception-handling
gkwan-ibm Dec 11, 2020
1eeb4fc
Merge branch 'qa' into exception-handling
gkwan-ibm Jan 14, 2021
a1e13a1
Merge branch 'qa' into exception-handling
gkwan-ibm Apr 1, 2021
e79e851
Merge branch 'prod' into exception-handling
gkwan-ibm Jun 25, 2021
594b666
Merge branch 'staging' into exception-handling
gkwan-ibm Jun 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ target/
.settings/
.DS_store
.vscode/
.gradle/
build/
bin/
84 changes: 80 additions & 4 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,18 @@ the [hotspot=path file=0]`@Path` annotation in this class indicates that the res
path.

JAX-RS maps the HTTP methods on the URL to the methods of the class by using annotations.
Your application uses the [hotspot=get]`GET` annotation to map an HTTP `GET` request
Your application uses the [hotspot=get1]`GET` annotation to map an HTTP `GET` request
to the `System/properties` path.

The [hotspot=get file=0]`@GET` annotation on the method indicates that this method is to be called for the HTTP `GET`
method. The [hotspot=produces file=0]`@Produces` annotation indicates the format of the content that will be returned. The
value of the [hotspot=produces file=0]`@Produces` annotation will be specified in the HTTP `Content-Type` response header.
The [hotspot=get1 hotspot=get2 file=0]`@GET` annotation on the method indicates that this method is to be called for the HTTP `GET`
method. The [hotspot=produces1 hotspot=produces2 file=0]`@Produces` annotation indicates the format of the content that will be returned.
The value of the [hotspot=produces1 hotspot=produces2 file=0]`@Produces` annotation will be specified in the HTTP `Content-Type` response header.
For this application, a JSON structure is to be returned. The desired `Content-Type` for a JSON
response is `application/json` with `MediaType.APPLICATION_JSON` instead of the `String` content type. Using a constant such as `MediaType.APPLICATION_JSON` is better because if there's a spelling error, a compile failure occurs.

The [hotspot=pathParam1 file=0]`@Path` annotation with curly braces passes text from the path to the function using the [hotspot=pathParam2 file=0]`@PathParam` annotation.
For example going to the path `System/properties/os.name` will pass in `os.name` as a String to the [hotspot=getProperty file=0]`getProperty()` function.

JAX-RS supports a number of ways to marshal JSON. The JAX-RS 2.1 specification mandates JSON-Binding
(JSON-B). The method body returns the result of `System.getProperties()`, which is of type `java.util.Properties`. Since the method
is annotated with `@Produces(MediaType.APPLICATION_JSON)`, JAX-RS uses JSON-B to automatically convert the returned object
Expand Down Expand Up @@ -196,6 +199,79 @@ include::{common-includes}/devmode-build.adoc[]

Check out the service that you created at the http://localhost:9080/LibertyProject/System/properties[^] URL.

// =================================================================================================
// Exception Handling
// =================================================================================================

== Exception Handling

Most application developers will want to ensure that when an exception occurs in the application (such as the client requesting a resource that does not exist or posting an invalid resource, etc.) that it is handled consistently and in a way that allows the client to fully understand the failure (i.e. bad input, server outage, etc.).
This can be accomplished using the exception handling mechanisms in JAX-RS such as sending a `Response` via a `WebApplicationException` (or subclass) or by using `ExceptionMapper` providers.

The following example uses an `ExceptionMapper` which maps exceptions to responses and builds appropriate JSON payloads.

[role="code_command hotspot file=0", subs="quotes"]
----
#Create the `ErrorResponse` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/ErrorResponse.java`
----

ErrorResponse.java
[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/ErrorResponse.java[]
----

The [hotspot file=0]`ErrorResponse` class will define the JSON contents of our response.
It contains fields for the error code and error message.

[role="code_command hotspot file=1", subs="quotes"]
----
#Create the `GenericExceptionMapper` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/GenericExceptionMapper.java`
----

GenericExceptionMapper.java
[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/GenericExceptionMapper.java[]
----

When JAX-RS sees any `Throwable`, it will search for a matching `ExceptionMapper` interface that has been marked with the [hotspot=provider file=1]`@Provider` annotation.
It will pass the `Throwable` to the [hotspot=toResponse file=1]`toResponse()` method to construct a response using the [hotspot file=0]`ErrorResponse` class.

The [hotspot file=1]`GenericExceptionMapper` class will be used for any `Throwable` that does not have a more specific `ExceptionMapper` interface implemented.

[role="code_command hotspot file=2", subs="quotes"]
----
#Create the `PropertyNotFoundException` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/PropertyNotFoundException.java`
----

PropertyNotFoundException.java
[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/PropertyNotFoundException.java[]
----

[role="code_command hotspot file=3", subs="quotes"]
----
#Create the `PropertyNotFoundExceptionMapper` class.#
`src/main/java/io/openliberty/guides/rest/exceptions/PropertyNotFoundExceptionMapper.java`
----

PropertyNotFoundExceptionMapper.java
[source, Java, linenums, role="code_column hide_tags=comment"]
----
include::finish/src/main/java/io/openliberty/guides/rest/exceptions/PropertyNotFoundExceptionMapper.java[]
----

The [hotspot file=3]`PropertyNotFoundExceptionMapper` class is a more specific implementation of the `ExceptionMapper` interface.
The type of the generic inside is now [hotspot=generic file=3]`PropertyNotFoundException` instead of `Throwable`.
It will only map to instances of [hotspot file=2]`PropertyNotFoundException` that the application throws.

See this error by visiting a URL for a property that doesn't exist such as http://localhost:9080/LibertyProject/System/properties/doesNotExist[^].

// =================================================================================================
// Testing the service
// =================================================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,58 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
// end::comment[]
// end::comment[]
package io.openliberty.guides.rest;

import java.util.Properties;

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.MediaType;

import io.openliberty.guides.rest.exceptions.PropertyNotFoundException;

// tag::path[]
@Path("properties")
// end::path[]
public class PropertiesResource {

// tag::get[]
// tag::get1[]
@GET
// end::get[]
// tag::produces[]
// end::get1[]
// tag::produces1[]
@Produces(MediaType.APPLICATION_JSON)
// end::produces[]
// end::produces1[]
public Properties getProperties() {
return System.getProperties();
}

// tag::get2[]
@GET
// end::get2[]
// tag::pathParam1[]
@Path("/{property}")
// end::pathParam1[]
// tag::produces2[]
@Produces(MediaType.APPLICATION_JSON)
// end::produces2[]
// tag::getProperty[]
// tag::pathParam2[]
public String getProperty(@PathParam("property") String prop)
// end::pathParam2[]
throws PropertyNotFoundException {
Properties properties = System.getProperties();
System.out.println(prop);
if (properties.containsKey(prop)) {
return "{" +
"\"" + prop + "\" : " +
"\"" + properties.getProperty(prop) + "\"" +
"}";
}
throw new PropertyNotFoundException("Property " + prop + " not found");
}
// end::getProperty[]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.openliberty.guides.rest.exceptions;

public class ErrorResponse {
int errorCode;
String errorMessage;

public ErrorResponse(int errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}

public int getErrorCode() {
return this.errorCode;
}

public String getErrorMessage() {
return this.errorMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.openliberty.guides.rest.exceptions;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

// tag::provider[]
@Provider
// end::provider[]
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

// tag::toResponse[]
@Override
public Response toResponse(Throwable t) {
ErrorResponse response = new ErrorResponse(500, t.getMessage());
return Response.serverError()
.entity(response)
.type(MediaType.APPLICATION_JSON)
.build();
}
// end::toResponse[]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.openliberty.guides.rest.exceptions;

public class PropertyNotFoundException extends Exception {

public PropertyNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.openliberty.guides.rest.exceptions;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class PropertyNotFoundExceptionMapper
// tag::generic[]
implements ExceptionMapper<PropertyNotFoundException> {
// end::generic[]

@Override
public Response toResponse(PropertyNotFoundException ex) {
ErrorResponse response = new ErrorResponse(404, ex.getMessage());
return Response.status(Response.Status.NOT_FOUND)
.entity(response)
.type(MediaType.APPLICATION_JSON)
.build();
}

}
Empty file.