A form handler that can manipulate repository items should ensure that all operations that occur in a handler method call happen in a single transaction. If all the operations in a method call do not occur in the same transaction, there is the risk that if something goes wrong in the middle of the operation, incomplete data is committed to the repository. Committing all the operations at once ensures that a repository or database transaction is either completed successfully, or not completed at all, and any partially committed data is rolled back.
The RepositoryFormHandler
and TransactionalRepositoryFormHandler
classes ensure atomic transactions in this way. If you subclass either class without overriding the handler methods, your subclass handles transactions properly. If you override any handler methods, or add new handler methods, you must make sure that these methods handle transactions properly.
To create a form handler that works with repository items while a transaction is in progress:
Create a form handler that is a subclass of either
RepositoryFormHandler
orTransactionalRepositoryFormHandler
. See Using RepositoryFormHandler or TransactionalRepositoryFormHandler.Create methods on your new form handler that are transaction-aware. See Creating Methods that Support Transactions.
Note: Keep in mind that RepositoryFormHandler
and TransactionalRepositoryFormHandler
are useful for manipulating repository items specifically. If you’d rather work with the JDBC directly in a form handler that supports transactions, use the atg.droplet.TransactionFormHandler
.
Using RepositoryFormHandler or TransactionalRepositoryFormHandler
You need to decide which form handler, TransactionalRepositoryFormHandler
or RepositoryFormHandler
, you want to subclass. The source code for both form handlers is provided here: <ATG9dir>/DAS/src/Java/atg/repository/servlet
. You can also navigate to API documentation for both classes in ATG API Reference.
The atg.repository.servlet.RepositoryFormHandler
is a base form handler that provides tools for creating, modifying, and deleting items stored in an SQL repository. The only direct instance of this class provided in the ATG Adaptive Scenario Engine is /atg/demo/QuincyFunds/FormHandlers/EmailRepositoryFormHandler
. Learn more about this form handler by reading the Using Repository Form Handlers chapter of the ATG Page Developer’s Guide.
The atg.repository.servlet.TransactionalRepositoryFormHandler
, a subclass of the RepositoryFormhandler
, provides enhanced transaction support by broadening the scope of the transaction. This class also defines a few additional properties that are useful for transaction monitoring. ATG Adaptive Scenario Engine does not include any instances of this class.
Both form handlers create a transaction if one is not already in progress and provide the status of all operations performed by the form handler while the transaction is in place. The main difference between the form handlers is the lifespan of the transaction.
In the RepositoryFormhandler
, the transaction is governed entirely by the submit handler method, meaning the transaction starts when a submit handler method is invoked and ends when it completes execution. Transaction status is reported for the data validation and the data commit operations.
Transactions in the TransactionalRepositoryFormHandler
begin when the beforeSet
method is invoked and end with the afterSet
method. Because a transaction status is generated for all operations that occur during its execution, a status is recorded for each the following operations:
beforeSet
method executionprocessing of all other tags in the JSP (tags that implement submit operations have the lowest priority on the page)
submit handler method data validation
submit handler method data commit
afterSet
method execution
So, choose your superclass form handler class based on the actions that you want included in a transaction.
Note: A transaction is started and ended by a form handler only when there are no other active transactions. If a transaction is in progress, the form handler returns a status for each operation it attempts and permits the transaction to continue after the form handler itself finishes processing.
Creating Methods that Support Transactions
After you create your subclass, make sure that the methods you specify for it can correspond with the Transaction Manager. There are two ways to do this:
Base your new handler methods on the existing
handleUpdate
code so that you can re-use the transaction code in it and then modify the rest accordingly.Expand the existing handler methods by inserting code before or after their execution in the
preX
orpostX
methods respectively.
New Methods Based on handleUpdate Source Code
The code provided here implements the handleUpdate
method. Create your own handler methods by making changes to this code sample and inserting it into your subclassed form handler:
public boolean handleUpdate(DynamoHttpServletRequest pRequest, DynamoHttpServletResponse pResponse) throws ServletException, IOException { TransactionDemarcation td = getTransactionDemarcation(); TransactionManager tm = getTransactionManager(); try { if (tm != null) td.begin(tm, td.REQUIRED); int status = checkFormError(getUpdateErrorURL(), pRequest, pResponse); if (status != STATUS_SUCCESS) return status == STATUS_ERROR_STAY; // update the repository item preUpdateItem(pRequest, pResponse); if (!getFormError()) updateItem(pRequest, pResponse); postUpdateItem(pRequest, pResponse); // try to redirect on errors if ((status = checkFormError(getUpdateErrorURL(), pRequest, pResponse)) != STATUS_SUCCESS) return status == STATUS_ERROR_STAY; // try to redirect on success return checkFormSuccess(getUpdateSuccessURL(), pRequest, pResponse); } catch (TransactionDemarcationException e) { throw new ServletException(e); } finally { try { if (tm != null) td.end(); } catch (TransactionDemarcationException e) { } } }
Extending Existing Methods
The three existing submit handler methods (handleCreate
, handleUpdate
, and handleDelete
) each come with a pair of empty pre
and post
methods to which you can add custom code.
It is likely that you use either the preX
or the postX
method for a given existing handler method although it is possible for you to customize both. For example, consider a subclass of TransactionalRepositoryFormHandler
where preUpdate
and postUpdate
are used:
The form is rendered, which causes
getX
method to display current values for properties used in the form.The user fills in the form and submits it.
The form handler’s
beforeSet
method is invoked. If a transaction is not currently in progress, theTransactionalRepositoryFormHandler
component creates a one.If tag converters are used, they are applied to the specified content. Any form exceptions that occur now or at any point during the form handler execution are saved to the form handler’s
formException
property.The
setX
method is called, followed by the form handler’shandleUpdate
method. Severe form exceptions might cause form processing to stop and the transaction to rollback, before redirecting users to a different page.The
preUpdateItem
method is invoked. ThepreX
method for thehandleUpdate
method ispreUpdateItem
. Serious errors generated from this operation might also prompt the transaction to rollback.The
updateItem
method, which is thehandleUpdate
method responsible for processing the content and updating the database, is invoked. Again, this is another operation that can cause a transaction to rollback when serious errors are detected. At this point, the changes made by the actions associated with the transaction are kept private, meaning that they are only visible within the transaction itself.The
postUpdateItem
method is invoked. Again, the transaction is rolled back if serious errors are detected.The
afterSet
method is invoked. If the transaction was started by thebeforeSet
method, the transaction concludes and the content it saved to the database is publicly visible.
You should use the preX
method when you want to expand the constraints for data validation. For example, you might want to check if the user-entered zip code and country correspond to each other. The countries that use zip codes require them to be a certain length. The preX
method can verify that a zip code uses the appropriate format for the country, before saving the zip code and country values to the database. Any discrepancies produce a form exception and roll back the transaction.
Alternatively, the postX
method is useful when you want to verify user entered-data after that data has been converted by tag converters. For example, a form handler that saves credit card information might use a postX
method to handle authorization. After that credit card number has been formatted correctly and all related information is updated in the database, the postX
method executes. If the authorization fails, the transaction is rolled back, the original data refreshed, a form error exception is thrown, and the user is redirected to a page where the use can re-enter credit card information.