Using a custom exception class
In this recipe, we will go over the steps necessary to set up a custom application exception class derived from the JboException
base exception class. Some Reasons why you might want to do this include:
Customize the exception error message
Use error codes to locate the error messages in the resource bundle
Use a single resource bundle per locale for the error messages and their parameters
Getting ready
We will add the custom application exception class to the SharedComponents
workspace we created in the Breaking up the application in multiple workspaces recipe in this chapter.
How to do it…
1. Start by opening the
SharedComponents
workspace.2. Create a new class called
ExtJboException
by right-clicking on the business components project and selecting New….3. Then select Java under the General category and Java Class from list of Items on the right.
4. Click OK to display the Create Java Class dialog. Enter
ExtJboException
for the Name,com.packt.jdeveloper.cookbook.shared.bc.exceptions
for the Package andoracle.jbo.JboException
for the Extends.5. Click OK to proceed with the creation of the custom exception class.
6. The next step is to add two additional constructors, to allow for the instantiation of the custom application exception using a standard error message code with optional error message parameters. The additional constructors look similar to the following code sample:
public ExtJboException(final String errorCode, final Object[] errorParameters) { super(ResourceBundle.class, errorCode, errorParameters); } public ExtJboException(final String errorCode) { super(ResourceBundle.class, errorCode, null); }
7. Now, click on the Override Methods… icon on the top of the editor window and override the
getMessage()
method, as shown in the following screenshot:8. Enter the following code for the
getMessage()
method:public String getMessage() { // default message String errorMessage = ""; try { // get access to the error messages bundle final ResourceBundle messagesBundle = ResourceBundle.getBundle (ERRORS_BUNDLE, Locale.getDefault()); // construct the error message errorMessage =this.getErrorCode() + " - " + messages Bundle.getString(MESSAGE_PREFIX + this.getErrorCode()); // get access to the error message parameters bundle final ResourceBundle parametersBundle = ResourceBundle .getBundle(PARAMETERS_BUNDLE, Locale.getDefault()); // loop for all parameters for (int i = 0; i < this.getErrorParameters().length; i++) { // get parameter value final String parameterValue = parametersBundle.getString(PARAMETER_PREFIX + (String)this.getErrorParameters()[i]); // replace parameter placeholder in the error message string errorMessage = errorMessage.replaceAll ("\\{" + (i + 1) + "}", parameterValue); } } catch (Exception e) { // log the exception LOGGER.warning(e); } return errorMessage; }
9. Make sure that you also add the following constants:
private static final String ERRORS_BUNDLE = "com.packt.jdeveloper. cookbook.shared.bc.exceptions.messages.ErrorMessages"; private static final String PARAMETERS_BUNDLE = "com.packt. jdeveloper.cookbook.shared.bc.exceptions.messages.ErrorParams"; private static final String MESSAGE_PREFIX = "message."; private static final String PARAMETER_PREFIX = "parameter."; private static final ADFLogger LOGGER =ADFLogger .createADFLogger(ExtJboException.class);
10. For testing purposes add the following
main()
method:// for testing purposes; remove or comment if not needed public static void main(String[] args) { // throw a custom exception with error code "00001" and two parameters throw new ExtJboException("00001", new String[] { "FirstParameter", "SecondParameter" }); }
How it works…
We have created a custom exception at the ADF-BC level by overriding the JboException
class. In order to use application-specific error codes, we have introduced two new constructors. Both of them accept the error code as a parameter. One of them also accepts the message error parameters.
public ExtJboException(final String errorCode, final Object[] errorParameters) { super(ResourceBundle.class, errorCode, errorParameters); }
In our constructor, we call the base class' constructor and pass the message error code and parameters to it.
Then we override the getMessage()
method in order to construct the exception message. In getMessage()
, we first get access to the error messages resource bundle by calling ResourceBundle.getBundle()
as shown in the following code snippet:
final ResourceBundle messagesBundle = ResourceBundle.getBundle(ERRORS_BUNDLE, Locale.getDefault());
This method accepts the name of the resource bundle and the locale. For the name of the resource bundle, we pass the constant ERRORS_BUNDLE
, which we define as com.packt.jdeveloper.cookbook.shared.bc.exceptions.messages.ErrorMessages
. This is the ErrorMessages.properties
file in the com/packt/jdeveloper/cookbook/shared/bc/exceptions/messages
directory where we have added all of our messages. For the locale, we use the default locale by calling Locale.getDefault()
.
Then we proceed by loading the error message from the bundle:
errorMessage = this.getErrorCode() + " - " + messagesBundle.getString(MESSAGE_PREFIX + this.getErrorCode());
An error message definition in the messages resource bundle looks similar to the following:
message.00001=This is an error message that accepts two parameters. The first parameter is '{1}'. The second parameter is '{2}'.
As you can see, we have added the string prefix message
. to the actual error message code. How you form the error message identifiers in the resource bundle is up to you. You could, for example, use a module identifier for each message and change the code in getMessage()
appropriately. Also, we have used braces, that is, {1}, {2}
as placeholders for the actual message parameter values. Based on all these, we constructed the message identifier by adding the message prefix to the message error code as: MESSAGE_PREFIX + this.getErrorCode()
and called getString()
on the messagesBundle
to load it.
Then we proceed with iterating the message parameters. In a similar fashion, we call getString()
on the parameters bundle to load the parameter values.
The parameter definitions in the parameters resource bundle look similar to the following:
parameter.FirstParameter=Hello parameter.SecondParameter=World
So we add the prefix parameter
to the actual parameter identifier before loading it from the bundle.
The last step is to replace the parameter placeholders in the error message with the actual parameter values. We do this by calling replaceAll()
on the raw error message, as shown in the following code snippet:
errorMessage = errorMessage.replaceAll("\\{" + (i + 1) + "}", parameterValue);
For testing purposes, we have added a main()
method to test our custom exception. You will similarly throw
the exception in your business components code, as follows:
throw new ExtJboException("00001", // message code new String[] { "FirstParameter", "SecondParameter" } // message parameters);
There's more…
You can combine the error message and the error message parameters bundles into a single resource bundle, if you want, and change the getMessage()
method as needed to load both from the same resource bundle.
Bundled Exceptions
By default, exceptions are bundled at the transaction level for ADF-BC-based web applications. This means that all exceptions thrown during attribute and entity validations are saved and reported once the validation process is complete. In other words, the validation will not stop on the first error, rather it will continue until the validation process completes and then report all exceptions in a single error message. Bundled validation exceptions are implemented by wrapping exceptions as details of a new parent exception that contains them. For instance, if multiple attributes in a single entity object fail attribute validation, these multiple ValidationException
objects are wrapped in a RowValException
. This wrapping exception contains the row key of the row that has failed validation. At transaction commit time, if multiple rows do not successfully pass the validation performed during commit, then all of the RowValException
objects will get wrapped in an enclosing TxnValException
object. Then you can use the getDetails()
method of the JboException
base exception class to recursively process the bundled exceptions contained inside it.
Exception bundling can be configured at the transaction level by calling setBundledExceptionMode()
on the oracle.jbo.Transaction
. This method accepts a Boolean value indicating that bundled transactions will be used or not, respectively.
Note
Note that in the Using a generic backing bean actions framework recipe in this chapter, we refactored the code in getMessage()
to a reusable BundleUtils.loadMessage()
method. Consequently, we changed the ExtJboException getMessage()
in that recipe to the following:
public String getMessage() { return BundleUtils.loadMessage(this.getErrorCode(), this.getErrorParameters()); }
See also
Handling security, session timeouts, exceptions and errors, Chapter 9,Handling Security, Session Timeouts, Exceptions and Errors
Breaking up the application in multiple workspaces, in this chapter