Handling errors in a Zowe REST API
A key concept within the Zowe ecosystem is the creation of a REST infrastructure API to access the functionality of an existing application. A component named zowe-rest-api-commons-spring provides a layer of functionality on top of Spring Boot to facilitate writing a REST service that interacts with a z/OS application. The “commons” component can be found within the GitHub repository: sample-spring-boot-api-service. You can use capabilities provided by the commons component when writing your own REST API, which include:
- Authentication
- Authorization
- Running a thread with the security environment of your end user
- Facilitating the discovery of your API by the Zowe API Mediation Layer
See the documentation within the repository above for more detailed information on the functionality of the commons component.
Also within that repository is a rest-api-sample, which is a REST server that uses the commons component. Combined with the commons documentation, the sample is intended to provide a model for how one could write his/her own REST service.
Among the facilities provided by commons is a model for handling errors and forming error messages in a REST response. The focus of this article is to provide a tutorial of how one initializes and uses the error-handling capabilities of the commons component. These recommendations result from my efforts to create a set of commons-conformant errors within a new REST service for a Broadcom mainframe application. The version of the commons component available at the time of this effort was version 0.5.1.
Where are the building blocks?
As with many facilities in Spring Boot, functionality is provided by convention. That is, one sets values in one place, which enables capabilities that are used in very different places. Numerous small updates in many different places result in functionality that happens without obvious logic in your source code. The layering of Zowe commons functionality on top of Spring Boot can sometimes further obfuscate the cause and effect of these small, required changes. I will attempt to guide you through the steps needed to produce error messages and a REST error response.
The rest-api-sample contains a file named ApplicationConfig.java. You will have a file like this if you mimicked the sample for your own app. It contains an ‘errorService’ object of type ErrorService annotated with @Bean. Spring Boot will instantiate this object, making it available within your app. You will use the errorService later in an exception handler that you will write. The errorService returns a new ErrorServiceImpl, which is passed a string “/messages.yml”. This specifies the name of your messages file in which you place error message text and formatting. In your source directory, the messages file will reside in YourApp/src/main/resources/messages.yml.
You do not have to modify anything in your ApplicationConfig file, but if you know that this is where your messages file name and ErrorService comes from, it will save you much time investigating the question “How the heck does my code have access to the things that it needs?”
Creating a messages file
Within your messages file, you place the text of your error messages, which can later be translated. The content and purpose of the message file is described in Error Handling in REST Controllers. Briefly, you add a new message by specifying a “key” property that you will use to access your message. In the “text” property, you supply a printf-like statement. For example:
- key: your.key.for.your.new.message
number: ABCDE01
type: ERROR
text: “Operation %s failed with ReturnCode=0x%X ReasonCode=0x%X.”
Messages Gotcha #1
The Zowe REST-API documentation describes optional message properties like messageReason and messageAction. The rest-api-sample has no reference to such properties. The rest-api-commons package does reference such properties, but only in private methods.
These properties are not currently available, so do not try to use them at this time. The commons component is still evolving. We expect that such optional message properties will be exposed in a future version of the commons component.
As a result, I limited my messages to the four core properties: key, number, type, and text.
Messages Gotcha #2
The messages.yml file is very touchy about format and layout. The wrong number of spaces, or an unprintable character accidentally placed in the file, will cause failures.
- Copying a string with quotes from a word processor into your messages.yml can introduce special characters that may not be obvious to the eye, but which cause YAML parsing to fail with a harsh error.
- In one case, after adding a new message to messages.yml, my REST API server crashed upon startup. I reverted my messages.yml file and re-typed the content of my new message in a fashion that was in all discernible ways the same. The second version worked fine. I never did figure out what I did wrong the first time.
Every error in your REST Server is a Java exception
The rest-api-sample contains EmptyNameError.java. If you install and run an unmodified version of the rest-api-sample you can observe an error response through a URI like:https://YourHost:YourPort/api/v1/greeting?name=%20
When you are ready to create your own new error, you create a new Java exception in a file like YourNewException.java. Your exception can extend from Java’s Exception class or other exception subclass.
- Create a constructor for your new exception.
- Specify the values that you are interested in displaying in your error, as parameters to your exception’s constructor.
- In the constructor, store each parameter into private member variables within your exception class.
- Provide getter methods to get those member variables later.
When your code detects an error, it throws your new exception.
You specify as many parameters as you placed in messages.yml.
Catch the errors that you throw
If you never handle the exceptions that you throw, the commons component will deliver a REST error response with a generic uncaught exception message. This is fine for an error like “NullPointerException” that is thrown in some 3rd party utility that you call (of course we all know that would never happen within your code itself). However, you want to catch the errors that you created and threw, so that you can form the error messages that you so meticulously created earlier.
Create a new file like YourExceptionHandler.java based on the sample’s GreetingControllerExceptionHandler.java. It will handle all of your exceptions for which you want to return a custom error in the REST response. The sample takes advantage of Spring dependency injection and annotations like @Bean, @ControllerAdvice, and @Autowired to make items (that we described earlier) automatically available for use in your exception handlers.
Following the sample, you create a new @ExceptionHandler(YourNewException.class) method to handle your new exception. You will eventually place additional exception handlers into YourExceptionHandler.java.
- In that new exception handling method, call
createApiMessage(“your.key.for.your.new.message”, value1, value2, …)
- The trailing parameters supply values for the printf-like format that you placed in the messages.yml file.
- YourNewExeception is passed into this exception handler as a parameter (named ‘exception’ in the sample). Thus, you use that ‘exception’ variable to supply values for your printf-like message by specifying values that you originally stored in your exception object through the constructor for YourNewException.
The return value of your exception handler is a ResponseEntity.
- In a chained-builder-approach, you supply the HTTP status value, the content type, and the body of the REST response.
- The HTTP Status used below is INTERNAL_SERVER_ERROR (value is 500).
- You can return any HTTP status value that you want, but you should be aware that this is where you control the HTTP status value returned from your REST request.
- For the body you return the ApiMessage object that was returned by your earlier call to createApiMessage().
Using the sample names from this article, the new exception handler that you would place into YourExceptionHandler.java might look like the following:
Am I done yet !!!
Actually, yes you are.
As I mentioned at the outset, there is a lot of setup in numerous different places. However, once your exceptions are setup, the rest of your code just throws an exception when needed, and the desired REST response is magically returned to your client.