ONJava.com -- The Independent Source for Enterprise Java  
oreilly.comSafari Books Online.Conferences.
 

A Tour Through WebLogic Workshop 8.1: Westside Auto Sales
Pages: 1, 2, 3, 4, 5, 6

Timer Control

We will use a timer control to simulate our ordering pause. In the real world, it might be a few days to several months before a requested car is finally shipped to a dealer. In this case you probably wouldn't use asynchronous calls, but separate methods on the caller and sender.

To create the control, right-click on the Web service in design view and choose "Add Control/Timer". Name the control "csTimer" and give it a timeout of 10 seconds.

Asynchronous Web Service

Now we are ready to add the code to respond to an order. First we will create a variable to track the unique ID (in our case, the OrderNumber) of the car being ordered. Do this by right-clicking on the control in design view and choosing "Add Variable". Use the following as the variable declaration:

Integer OrderNumber;

Now we will add a new method that will be called by our other methods. Add the following code to CarSourceWS.jws:

    public void orderCar(int SKU, String Make, String Model, int MarkUp)
    {
        // Calculate Markup can be anywhere from 0 to MarkUp
        double markup = Math.random()*MarkUp;
        int baseprice = cSDataAccess.getPrice(Make, Model);
        double markupprice = ((markup / 100.0 * ((double)baseprice)));
        
        // Create a new order item in the DB and get the order number
        OrderNumber = new Integer(cSDataAccess.createOrder
		(SKU, Make, Model, 
		(int)(baseprice + markupprice), "On Order"));

        // Start timer
        csTimer.start();
                
        return;
    }

This code calculates the markup of the car and adds it to a base price that it pulls from the database. We then add the car to the ordering database with a status of "On Order" and send a message to the console that the car has been ordered. Finally, we start the timer to wait 10 seconds.

We will use this new method in the orderOther method (we will handle the orderHonda method later). Edit the orderOther method to the following (hitting Alt+Enter when requested to add the import for import com.beaOrderCar.OrderCarDocument.OrderCar):

/**
* @common:operation
* @jws:conversation phase="start"
*/
public String orderOther(OrderCarDocument newCar)
{
    // Get the info
    OrderCar myCar = newCar.getOrderCar();
        
   // Markup on exotic cars can be quite high - up to 10%
   int nMarkupMax = 10;

    // put a message in the console window
    System.out.println("[CarSource]Ordering 1 " + myCar.getMake() + " " 
	+ myCar.getModel() + " from other manufacturing.");        

   // Create a new order item in the DB and get the order number
   orderCar(myCar.getSKU(), myCar.getMake(), myCar.getModel(), 
   nMarkupMax);
        
   // Send back the order number
   return OrderNumber.toString();
}

Notice, the first thing we do is to use our XMLBean to get at the XML data passed into the method. We create an "OrderCar" object from the OrderCarDocument and then we can access each of the attributes as if they were member variables. This is quite powerful and eliminates the need for XML parsing and validation. Once we get the XMLBean, we send the information on the new car to order to our orderCar method, which sets the OrderNumber. Finally, we pass this OrderNumber back to the caller.

When the timer goes off 10 seconds later, we need to mark the car as shipped and send the details of the car back to the callback of the original caller. To implement the onTimeout method, click on the onTimeout method of the csTimer control in design view. Then add the following code to the csTimer_onTimeout method and the finishOrder method (again, hitting Alt+Enter when prompted to add the import for import CSControls.CSDataAccess.Order):

public void csTimer_onTimeout(long time)
{
    // Stop the timer
    csTimer.stop();
    
    // Mark the car as shipped
    Order myOrder = cSDataAccess.sendOrder(OrderNumber.intValue());
    
    OrderCarDocument myOrderDoc = OrderCarDocument.Factory.newInstance();
    OrderCar retOrder = myOrderDoc.addNewOrderCar();

    retOrder.setOrderNumber(OrderNumber.intValue());
    retOrder.setPrice(myOrder.Price);
    retOrder.setSKU(myOrder.SKU);
    retOrder.setStatus("Shipped");
    
     System.out.println("[CarSource]Order " + OrderNumber + " 
	 Complete. Sending message back to requestor.");

    callback.finishOrder(myOrderDoc); 
    
    return;
}

public interface Callback extends com.bea.control.ServiceControl
{
   // Send the car order info back to the requesting web service
   /**
    * @jws:conversation phase="finish"
    */
   public void finishOrder(OrderCarDocument yourOrder);
}

When the timer goes off, we stop the timer and mark the car as shipped using the database control. This returns an Order object (that we defined in the database control). We then create a new XML document and use an XMLBean to set all the attributes of the new document. This document is then passed back to the callback, which sends it on to the requestor. Notice that we are using a conversation in our Web service. The orderOther and orderHonda methods start the conversation, and the callback ends it. The conversation tags indicate that state should be maintained for the lifetime of the conversation. This allows us to handle multiple requests concurrently without having to worry about multi-threading issues.

At this point, we are ready to test our Web service. Save any dirty files, then switch to the CarSourceWS.jws file and click "Start". This opens up a browser window that gives you an interface to test the Web service. Because our methods take XML as an argument, click on the "Test XML" tab at the top of the screen. In the orderOther section you are given a sample XML argument. Alter this so we are ordering a Volkswagen New Beetle like so:

<orderOther xmlns="http://www.openuri.org/" 
xmlns:bea="http://www.BEA-OrderCar.com">
  <bea:OrderCar>
    <bea:SKU>3</bea:SKU>
    <bea:Year>2003</bea:Year>
    <bea:Make>Volkswagen</bea:Make>
    <bea:Model>New Beetle</bea:Model>
    <bea:Options>None</bea:Options>
    <bea:Price></bea:Price>
    <bea:OrderNumber></bea:OrderNumber>
    <bea:Status></bea:Status>
  </bea:OrderCar>
</orderOther>

Notice we don't care about the Price, OrderNumber or Status because this will be set by the Web service. Click the orderOther button to start the conversation. You will immediately get back an orderNumber in the form of XML:

Service Response 
Submitted at Friday, July 11, 2003 4:02:04 PM PDT

<SOAP-ENV:Envelope xmlns:SOAP-ENV=
"http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<ns:orderOtherResponse xmlns:ns="http://www.openuri.org/">
<ns:orderOtherResult>1</ns:orderOtherResult>
</ns:orderOtherResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope> 

Meanwhile, the timer is counting down. If you click refresh on the message log, you will be able to see the onTimeout being called and then the callback being executed. If you click on the callback you should see the XML for the OrderCar being returned to the caller:

Client Callback 
Submitted at Friday, July 11, 2003 4:02:13 PM PDT

<SOAP-ENV:Envelope xmlns:SOAP-ENC=
"http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<CallbackHeader xmlns=
"http://www.openuri.org/2002/04/soap/conversation/">
<conversationID>1057964523220</conversationID>
</CallbackHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns:finishOrder xmlns:ns="http://www.openuri.org/">
<bea:OrderCar xmlns:bea="http://www.BEA-OrderCar.com">
<bea:SKU>3</bea:SKU>
<bea:Price>19162</bea:Price>
<bea:OrderNumber>1</bea:OrderNumber>
<bea:Status>Shipped</bea:Status>
</bea:OrderCar>
</ns:finishOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Notice the SKU stayed the same and the price and status have been updated to reflect that the car has shipped. If you look in your console window, you should find the following messages:

[CarSource]Ordering 1 Volkswagen New Beetle from manufacturing.
[CarSource]Order 1 Complete. Sending message back to requestor.

Xquery

Before we add the code to call this Web service to our original Westside application, we will add code to accept a different XML package and translate it to the OrderCar package automatically via XQuery. In the real world, sometimes there are several different accepted schemas for transmitting data. In our example, it is possible that most car dealerships use the OrderCar schema for transmitting car-ordering information but that Honda dealers use a different HondaCar format. Usually it is a major amount of work to map between different schemas. The new WebLogic XQuery tools make this mapping simple. As an example, our orderHonda method will accept the HondaCar schema and translate it on the fly to the OrderCar schema we use in our orderOther method.

First we will create the orderHonda schema and its related XMLBeans. Right-click on the Schemas project and choose "New XML Schema". Name this Honda.xsd and then add the following to it:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://www.BEA-Honda.com" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:fn="http://www.BEA-Honda.com" elementFormDefault="qualified">
	<xs:element name="HondaCar">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="countrycode" 
				type="xs:int"/>
                              <xs:element name="SKU" 
							  type="xs:int"/>
				<xs:element name="model" 
				type="xs:string"/>
				<xs:element name="options" 
				type="xs:string"/>
				<xs:element name="engine" 
				type="xs:string"/>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>

Now alter the orderHonda method so it looks like this:

/**
 * @common:operation
 * @jws:conversation phase="start"
 */
public String orderHonda(OrderCarDocument hondaCar)
{
   return "";
}

Like the orderOther method, the orderHonda method will mark the start of the conversation. Also like orderOther, this method takes an OrderCarDocument as the input parameter. However, we know that people ordering Hondas will be passing in the HondaCar schema. We could do the mapping between these two schemas manually within the method, but it's much easier to let XQuery do it for us. To set up the mapping, switch to design view and double-click on the arrow to the left of the orderHonda method. This takes you to the XQuery Mapper. To map from a HondaCar to an OrderCar, click the Choose button on the Parameter XML tab. Select the HondaCar element under Honda.xsd and click OK. Now click on the Edit XQuery button to edit the mapping. Mapping between the two schemas is as simple as drawing lines between SKU, Model, and Options on the XQuery screen. As you drag one field onto the other, green lines show the connections. Click OK, then OK again. You now have a map between the two schemas defined in XQuery:

* @jws:parameter-xml schema-element="ns0:HondaCar" xquery::
* declare namespace ns0="http://www.BEA-Honda.com"
* declare namespace ns1="http://www.openuri.org/"
* declare namespace ns2="http://www.BEA-OrderCar.com"

* <ns1:orderHonda>
*     <ns2:OrderCar>
*         <ns2:SKU>{data($input/ns0:SKU)}</ns2:SKU>
*         <ns2:Model>{data($input/ns0:model)}</ns2:Model>
*         <ns2:Options>{data($input/ns0:options)}</ns2:Options>
*     </ns2:OrderCar>
* </ns1:orderHonda>

This means you don't have to worry that your users are going to pass in a HondaCar XML document; you can simply treat the method as if it always gets an OrderCar XML document. Now we are ready to add the rest of the code to our orderHonda method:

public String orderHonda(OrderCarDocument hondaCar)
{
    // Get the info
    OrderCar myCar = hondaCar.getOrderCar();
    
    // Markup on Hondas is minimal
    int nMarkupMax = 5;

    // put a message in the console window
    System.out.println("[CarSource]Ordering 1 " + myCar.getMake() 
	+ " " + myCar.getModel() + " from Honda manufacturing.");
    
    // Create a new order item in the DB and get the order number
    orderCar(myCar.getSKU(), "Honda", myCar.getModel(), nMarkupMax);
    
    // Send back the order number
    return OrderNumber.toString();
}

Just like in the orderOther method, we generate an XMLBean from the document (in this case, a HondaCar XML document translated to an OrderCar XML document by XQuery) and call the method that adds the car to the database and sets the OrderNumber. Like the orderOther method, we first send back the OrderNumber and then start the timer. When the onTimout method gets hit, we send the vehicle information back to the caller.

Let's test our Web service again. Save any dirty files, make sure the CarSourceWS.jws is in the edit window, and hit "start". Again, because our method accepts an XML package, click on Test XML. In the orderHonda method, notice how incoming XML is a HondaCar format. Fill in the test XML as follows:

<HondaCar xmlns="http://www.BEA-Honda.com">
  <countrycode>3</countrycode>
  <SKU>3</SKU>
  <model>Civic Coupe</model>
  <options>None</options>
  <engine>I4</engine>
</HondaCar>

Click the orderHonda button. You will get the OrderNumber back, and 10 seconds later if you hit the refresh button you will get the new car with the price. If you look in the console, you will see the following message telling you that we successfully called the orderHonda method:

[CarSource]Ordering 1 null Civic Coupe from Honda manufacturing.
[CarSource]Order 2 Complete. Sending message back to requestor.

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow