| Title: Exception Management |
| Notice: Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| . |
| http://www.apache.org/licenses/LICENSE-2.0 |
| . |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| #Exception Management |
| |
| Managing Exceptions should not be a core part of your business logic. In fact with the Transaction Control |
| Service is doing its job you shouldn't need to worry about exceptions at all! A key design goal for the |
| Transaction Control Service is to avoid the need for try/catch/finally blocks as far as possible. |
| |
| ##Throwing Exceptions from scoped work |
| |
| Resources can be tempramental sometimes, and usually have defined exceptions that are thrown in certain |
| error cases. These can be generic, like <code>SQLException</code> or <code>JMSException</code>, |
| or specific like <code>EntityNotFoundException</code>. |
| |
| In any event - exceptions indicate that a problem has occurred. By default an exception thrown from inside a |
| transactional scope will cause the transaction to roll back. This means that the code can safely ignore any |
| updates that were made. Furthermore, because a piece of scoped work is defined as a <code>Callable</code> |
| it is not necessary to catch or wrap an Exception raised in a scope. |
| |
| // An SQLException may be raised by the query, |
| // but we don't need to catch it |
| txControl.required(() -> connection.createStatement() |
| .executeQuery("Insert into TEST_TABLE values ( 'Hello World!' )")); |
| |
| |
| ##Catching Exceptions thrown from scoped work |
| |
| In general Exceptions should not form part of your client API, so catching an Exception from a piece of scoped |
| work is rarely necessary. Usually exceptions generated by scoped work are eventually handled by catch all |
| collectors at the incoming request point (for example a servlet) and do not require special handling. |
| |
| Sometimes, however, we have to work within an existing API that *does* use an Exception as a type of |
| return value. In that case it is important to know what happened to the Exception. |
| |
| ### The <code>ScopedWorkException</code> |
| |
| Scoped work is free to throw checked or unchecked Exceptions, however these Exceptions cannot be directly |
| thrown on by the TransactionControl implementation. The primary reason for this is that directly rethrowing the |
| Exception would force users of the Transaction Control Service to either *always* declare |
| <code>throws Exception</code> on their methods or to add try/catch blocks around every call. |
| |
| Exceptions generated as part of Scoped Work are therefore wrapped by the Transaction Control Service in a |
| <code>ScopedWorkException</code>. <code>ScopedWorkException</code> is an *unchecked exception* |
| and so can be ignored by your component if it does not require special handling (the typical case). |
| |
| ### Unwrapping the <code>ScopedWorkException</code> |
| |
| As mentioned above, sometimes it is necessary for an API to throw a particular type of Exception as a return value. |
| |
| This model can be supported by unwrapping the ScopedWorkException. |
| |
| try { |
| txControl.required(() -> connection.createStatement() |
| .executeQuery("Insert into TEST_TABLE values ( 'Hello World!' )")); |
| } catch (ScopedWorkException swe) { |
| // This line throws the cause of the ScopedWorkException as |
| // an SQLException or as a RuntimeException if appropriate |
| throw swe.as(SQLException.class); |
| } |
| |
| This mechanism also supports multiple Exception types: |
| |
| try { |
| txControl.required(() -> connection.createStatement() |
| .executeQuery("Insert into TEST_TABLE values ( 'Hello World!' )")); |
| } catch (ScopedWorkException swe) { |
| // This line throws the cause of the ScopedWorkException |
| // as one of the two SQLException types or as a |
| // RuntimeException if appropriate |
| throw swe.asOneOf(SQLRecoverableException.class, SQLTransientException.class); |
| } |
| |
| Note that if you unwrap a <code>ScopedWorkException</code> into a checked exception then you will have |
| to list that Exception in your <code>throws</code> clause. |