# Step 6: Add Support for Cancellation and Exception Handling

Working through the first steps of this tutorial has given you a sense of how a Java control works. In ideal conditions, it does what it is supposed to do. But as it now stands, it 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.
- 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 requestCreditReport) may throw an uncaught exception.

Below you will enhance the Investigate Java control to better handle these possible problems.

This tasks in this step are:

- To add a method so that clients can cancel operations
- To add a Timer Control to limit the time allowed for response
- To handle exceptions thrown from operation methods
- To test the Investigate Java Control

To Add a Method so that Clients Can Cancel Operations

1. On the Application tab, double-click InvestigateImpl.jcs.
2. Click the Design View tab.
3. From the Insert menu, select Method.
4. In the field that appears, type cancelInvestigation and press Enter.
5. Click the link text cancelInvestigation to view its code in Source View.
6. Edit the cancelInvestigation method code so it appears as follows. Code to add is shown in red.

           /**
            * @common:operation
            */
           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".
                */
               m_currentApplicant.message = "Investigation canceled.";
               callback.onCreditReportDone(m_currentApplicant);
           }

7. Press Ctrl+S to save your work.

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 report web 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.

1. Confirm that InvestigateImpl.jcs is displayed in the main work area.
2. Click the Design View tab.
3. From the Insert menu, select Controls-->Timer. The Insert Control dialog appears.
4. 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.
5. Click Create.
6. Click the link text for the requestCreditReport method to view its code in Source View.
7. Add the code shown in red:

           public void requestCreditReport(String taxID)
           {
               m_currentApplicant.taxID = taxID;

               /**
                * Retrieve data from the database and store it in the rec object.
                */
               Record rec = bankruptciesDB.checkForBankruptcies(taxID);
               /**
                * If the database returns substantial data, then store that data
                * in the m_currentApplicant object.
                */
               if(rec != null)
               {
                   m_currentApplicant.firstName = rec.firstname;
                   m_currentApplicant.lastName = rec.lastname;
                   m_currentApplicant.currentlyBankrupt = rec.currentlyBankrupt;

                   /**
                    * Invoke the Credit Card Report web service.
                    * Results from the web service will be provided via a callback.
                    */
                   creditCardReportControl.getCreditCardData(taxID);

                   /*
                    * Start the timer.  If the credit card report is not
                    * received within 5 minutes, the conversation will be finished
                    * and the client will be notified that
                    * there was a problem.
                    */
                    creditCardReportTimer.start();
               }
               /**
                * If the database does not return substantial data, notify the client
                * that there is a problem.
                */
               else
               {
                   m_currentApplicant.message = "No data could be found on the applicant. Please call (555) 555-5555 for assistance. ";
                   /*
                    * Send the error message to the client via a callback.
                    */
                   callback.onCreditReportDone(m_currentApplicant);
               }
           }

   This starts the timer as soon as the credit card service is called. If the credit card service does not respond by the time the timeout duration is reached, then the timer's onTimeout callback handler is invoked.
8. Click the Design View tab to view to Design View for InvestigateImpl.jcs.
9. Click the link text onCreditCardDataReady (the callback handler for the web service control) to view its code in Source View.
10. Edit the code for creditCardReportControl_onCreditCardDataReady so it appears as follows. Add the code appearing in red.

            public void creditCardReportControl_onCreditCardDataReady(java.lang.String cardData)
            {
                /*
                 * Stop the timer that was started when the Credit Card Report web service
                 * was called, because the callback from the web service has been received.
                 */
                creditCardReportTimer.stop();

                try
                {
                    /*
                     * When a report is delivered, extract the relevant credit card info,
                     * and store it in m_currentApplicant
                     */
                    CreditCardDataDocument cardInfo = CreditCardDataDocument.Factory.parse(cardData);
                    CustomerType[] customer = cardInfo.getCreditCardData().getCustomerArray();
                    CardType[] cards = customer[0].getCardArray();
                    for(int i = 0; i < cards.length; i++)
                    {
                        m_currentApplicant.availableCCCredit += cards[i].getAvailableCredit();
                    }
                }
                catch(XmlException xe)
                {
                    /*
                     * If there is a problem with extracting the credit card info,
                     * store an error message in m_currentApplicant.
                     */
                    m_currentApplicant.message =  "There was an error retrieving the credit card information. Please call (555) 555-5555 for assistance.";

                     /*
                      * Send the the error message back to the client.
                      */
                     callback.onCreditReportDone(m_currentApplicant);
                }

                /*
                 * Construct and send an XML message through the JMS control
                 * to the credit scoring application.
                 */
                String messageToJMSControl = makeMessageToJMS(m_currentApplicant.availableCCCredit, m_currentApplicant.currentlyBankrupt);
                creditScoreJMS.subscribe();
                creditScoreJMS.sendTextMessage(messageToJMSControl);
            }

11. Click the Design View tab to view InvestigateImpl.jcs in Design View.
12. On the Timer control, click the link text for the onTimeout callback handler to view its source code.
13. Edit the onTimeout callback handler source code so that it appears as follows. Code to add appears in red.

            public void creditCardReportTimer_onTimeout(long time)
            {
                /**
                 * Because the Credit Card Report web service has not calledback in the time allowed,
                 * cancel the request for a report from the web service
                 */
                creditCardReportControl.cancelRequest();

                /**
                 * Cancel the current investigation.
                 */
                cancelInvestigation();
            }

14. Press Ctrl+S to save your work.

To Handle Exceptions Thrown from Operation Methods

Unhandled exceptions thrown from operations (such as methods exposed to clients) can leave the client hanging and the Java control may simply continue operating on the system, unnecessarily using resources.

In this step, you will add code to implement a handler for the ControlContext.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.

1. If InvestigateImpl.jcs is not displayed in Source View, click the Source View tab.
2. Add the following import statement to the list of import statements at the top of the InvestigateImpl.jcs directly underneath the package declaration.

       import com.bea.wlw.util.Logger;

3. Add the following annotation and declaration to the body of InvestigateImpl.jcs.

           /**
            * @common:context
            */
           ControlContext context;

4. In Source View, add the following onException callback handler to the body of InvestigateImpl.jcs.

        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 its messages
             * 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.
             * The message will be written to the jws.log file located
             * in the server root folder, in this case:
             * BEA_HOME/weblogic81/samples/domains/workshop
             */
            logger.error("Exception in " + methodName + ": " + e);

            /*
             * Send a callback to the client with notification that an error has occurred.
             */
            m_currentApplicant.message = "Unable to respond to request at this time. Please contact us at (555) 555-5555.";
            callback.onCreditReportDone(m_currentApplicant);
        }

5. Press Ctrl+S to save your work.

By default the log file is located in the server's domain root folder, in this case the Workshop domain root folder:

BEA_HOME/weblogic81/samples/domains/workshop/workshop.log

An entry in the log will resemble the following:

    21 Aug 2003 16:38:26,610 ERROR *** Investigate ***: Exception in requestCreditReport: com.bea.control.ServiceControlException: SERVICE FAULT:
    Code:java.io.FileNotFoundException
    String:Response: '401: Unauthorized xxx' for url: 'https://weblogic:wrong_password@localhost:7002/CreditCardReport_JCSTutorial/webservice/CreditCardReport.jws'
    Detail:
    END SERVICE FAULT [ServiceException]

To test the Investigate Java Control

Try the following test scenarios through InvestigateTest.jws.

- Enter a random string when you invoke requestCreditReport, such as "qwe123qwe". (Note: this will not raise an error in workshop.log, instead a message will be sent to the client saying that no data could be found.)
- Test the timer control by changing its timeout value to 1 second. Unless your computer is very fast, 1 second should not be enough time for the Credit Card Report web service to send a callback before the timeout. After changing the timeout value, run InvestigateImplTest.jws, wait one second, then click the Refresh link. The message log will resemble the following.
- Test the exception handler by throwing an exception from somewhere in your code. Then check the workshop.log file to see the message that results.

Related Topics

Timer Control

Click one of the following arrows to navigate through the tutorial: