Step 7: Add Support for Cancellation and Exception Handling
Working through the first steps of this tutorial has given you a web service that works. In ideal conditions, it does what it is supposed to do. But as it now stands, the service isn't quite ready for prime time. Here are a few possible problems:
The client may want to cancel the credit report before Investigate has sent back a response.
Providing this ability is especially important in asynchronous exchanges, which may be long-lived.
If database control returns no data on a particular applicant, the Investigate web service needs to stop its investigation and inform the client that no data is available on the applicant.
Investigate's dependency on the credit card web service (which is accessed asynchronously) may make the overall response time to the client quite long.
You have no way of knowing how long it will take the credit card service to respond to your requests. To better manage this, you can use a timer to set a limit on how the resource may take to respond.
Operation methods (such as requestCreditReportAsynch) may throw an uncaught exception.
In your current design, this would leave your service, and its client, hanging. The active conversation would continue until it expired. The client would not receive a response to its request and might never know why. You can handle such exceptions to ensure a clean recovery.
Through the following procedures, you will enhance the Investigate web service to better handle these possible problems.
This tasks in this step are:
To Add a Method so that Clients Can Cancel Operations
If you are not in Design View, click the Design View tab.
From the Add Operation drop-down list, select Add Method.
In the field that appears, type cancelInvestigation and press Enter. the method appears in Design View.
Click cancelInvestigation to view its code in Source View.
Edit the cancelInvestigation method code so it appears as follows:
/** * @jws:operation * @jws:conversation phase="finish" */ public void cancelInvestigation() { /* Cancel the request to the credit card company because it is now unnecessary. */ creditCardReportControl.cancelRequest(); /* Use the callback to send a message to the client. Note that this also ends * the conversation because the callback's conversation phase property is set to "finish". */ callback.onCreditReportDone(null, "Investigation canceled at client's request."); }
To Add Code to Handle a Null Response from The Database
If you are not in Design View, click the Design View tab.
Click requestCreditReportAsync to view its code in Source View.
Edit the requestCreditReportAsync method code so it appears as follows:
public void requestCreditReportAsynch(String taxID)
throws java.sql.SQLException
{
/*
* Query the database via the database control
*/
Applicant dbApplicant = bankruptciesDB.checkForBankruptcies(taxID);
/*
* If the database contains data on the current applicant,
* assign the retrieved values to m_currentApplicant.
*/
if(dbApplicant != null)
{
m_currentApplicant = dbApplicant;
creditCardReportControl.getCreditCardData(taxID);
}
/*
* If the database contains no data on the current applicant,
* inform the client.
*/
else
{
callback.onCreditReportDone(null, "No bankruptcy data found on applicant" + taxID + ".");
}
}
To Add a Timer Control to Limit the Time Allowed for Response
It can be difficult to predict how long an asynchronous resource will take to respond to a request. For example, Investigate's call to the credit card service may take hours. Here, you will add a way to limit the amount of time the credit card's web service has to respond. Using a Timer control, you can specify an amount of time after which the operation should be canceled.
Click the Design View tab to return to Design View.
From the Add Control drop-down list, select Add Timer Control. The Add Timer Control dialog appears.
Enter values as shown in the following illustration:
These values specify that the Timer control will send its onTimeout callback
five minutes after the timer starts.
Click Create. You are returned to Design View.
Click requestCreditReportAsync to view its code in Source View.
Add the code shown in bold:
public void requestCreditReportAsynch(String taxID)
throws java.sql.SQLException
{
/*
* Query the database via the database control
*/
Applicant dbApplicant = bankruptciesDB.checkForBankruptcies(taxID);
/*
* If the database contains data on the current applicant,
* assign the retrieved values to m_currentApplicant.
*/
if(dbApplicant != null)
{
m_currentApplicant = dbApplicant;
creditCardReportControl.getCreditCardData(taxID);
creditCardReportTimer.start();
}
/*
* If the database contains no data on the current applicant,
* inform the client.
*/
else
{
callback.onCreditReportDone(null, "No bankruptcy data found on applicant" + taxID + ".");
}
}
This starts the timer as soon as the credit card service is called. If the service does not respond by the time the timeout duration is reached, then the timer's onTimeout callback handler is invoked.
Click the Design View tab to return to Design View.
Click creditCardDataResult (the callback handler for the service control) to view its code in Source View.
Edit the code for creditCardReportControl_creditCardDataResult so it appears as follows:
private void creditCardReportControl_creditCardDataResult(CreditCard[] cards)
{
/*
* Now that the web service has returned its results, stop the timer started
* in the requestCreditReportAsynch method.
*/
creditCardReportTimer.stop();
for(int i = 0; i < cards.length; i++)
{
m_currentApplicant.availableCCCredit += cards[i].availableCredit;
}
/*
* Use the JMS control to send the available credit and bankruptcy information to the credit
* scoring application.
*/
creditScoreJMS.sendMessage(m_currentApplicant.availableCCCredit,
m_currentApplicant.currentlyBankrupt);
}
In Source View, immediately beneath the Source View tab, from the class drop-down list, select creditCardReportTimer, as shown here:
To the right of the class drop-down list, from the member drop-down list, select onTimeout, as shown here
The source code for the JMS control's callback handler appears.
Edit the callback handler source code so that it appears as follows:
private void creditCardReportTimer_onTimeout(long time) { /* Because the credit card service has not yet returned, cancel the request. */ creditCardReportControl.cancelRequest(); /* Send a response to the client, saying something about what happened. * Remember that this will also effectively finish the conversation. */ callback.onCreditReportDone(null, "Unable to retrieve credit card information."); }
To Handle Exceptions Thrown from Operation Methods
Unhandled exceptions thrown from operations (such as methods exposed to clients) can interrupt your service's work and leave the client hanging. Such exceptions do not automatically end a service's conversation. Instead, the service may simply continue on the system, unnecessarily using resources. As a result, one action your code should take when responding to such exceptions is to finish the conversation.
The JwsContext interface that provides the finish method for ending the conversation provides other useful functionality. In this step, you will add code to implement a handler for the JwsContext.onException callback. The callback handler receives the exception object thrown from the operation, the name of the method that threw the exception, and the parameters passed to the method.
WebLogic Workshop provides a way for you to preserve information about exceptions by logging it in a text file. You will add exception handling code to log the exception, then send an appropriate response to the client.
In Source View, immediately beneath the Source View tab, from the class drop-down list, select context, as shown here:
To the right of the class drop-down list, from the member drop-down list, select onException, as shown here:
The source code for the JwsContext onException callback handler appears.
Edit the onException callback handler code so that it appears as follows:
public void context_onException(Exception e, String methodName, Object[] arguments) { /* Create a logger variable to use for logging messages. Assigning it the * "Investigate" category name will make it easier to find messages from this * service in the log file. */ Logger logger = context.getLogger("Investigate"); /* Log an error message, giving the name of the method that threw the * exception and a stack trace from the exception object. */ logger.error("Exception in " + methodName + ": " + e); /* Invoke the callback to send the client a response. */ callback.onCreditReportDone(null, "Unable to respond to request at this time. " + "Please contact us at 555-5555."); }
At the top of Investigate's source code, add the following line of code as the last of the imports:
import weblogic.jws.util.Logger;
Note: This imports the Logger class that makes logging possible.
By default, for the domain in which you develop and debug WebLogic Workshop web services, the log file is located at the following path of your WebLogic Workshop installation:
BEA_HOME/weblogic700/samples/workshop/jws.log
An entry in the log will resemble the following:
16:18:11 ERROR Investigate : Exception in requestCreditReportAsynch: <exceptionStackTrace> [ServiceException]
Because you're probably anxious to move on to creating an actual client for the Investigate service, you won't test the code you've added here. But if you want to try it out some time, you might do the following:
Test the cancellation code by starting the service, then cancelling it in Test View. When Test View launches, enter a tax ID for testing. After the test has begun, click the "Continue this conversation" link. When the page refreshes, click the cancelInvestigation button.
Test the timer by changing its timeout value to 1 second. Unless your computer is very fast, this should be enough time for the credit card service to return before the timeout. After changing the timeout value, start the service and wait one second, then click the Refresh link.
Test the exception handler by throwing an exception from somewhere in your code.
Timer Control: Using Timers in Your Web Service
JwsContext.onException Callback
How Do I: Handle Errors in a Web Service?
Click one of the following arrows to navigate through the tutorial.