Tutorial: Building Your First Data Transformation

     Previous  Next    Open TOC in new window    View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

Step 4: Mapping Repeating Elements—Creating a Join

In this step, you will add additional mappings to the existing query. In the previous sections, you mapped some data from the source type defined by the PriceQuote.xsd XML Schema to the target type defined by the Quote.xsd XML Schema. In this section, you will map additional data from the source types (defined by the PriceQuote.xsd XML Schema, the AvailQuote.xsd XML Schema, and the Java float primitive: taxRate) to the target type (defined by the Quote.xsd XML Schema) as shown in the following figure.

Figure 5-1 Adding Data from Source Types

Adding Data from Source Types

Mappings created in this section will create a join between repeating elements in the source and target XML Schemas. Complete the following tasks to create, test, and alter the join:

Create a User-Defined Java Method to Invoke From the Join Query

In this task, you will create a user-defined Java method in the MyTutorialJoin Transformation file that calculates the total price of the widgets requested including tax. In Call the calculateTotalPrice User Method From the Query, you will change the query to invoke this method.

  1. In the Navigator pane, browse to the src/requestquote folder, and select MyTutorialJoin.java.
  2. Right-click on the Source of the MyTutorialJoin Transformation .
  3. From the shortcut menu, select Transform > Add User Method.
  4. A User method is created in the MyTutorialJoin Transformation file.

  5. A new method called newusermethod1 will be added in the Source. Rename this method to calculateTotalPrice.
  6. Edit the MyTutorialJoin Transformation file and replace the following generated calculateTotalPrice Java method:
  7. 	 public java.lang.String calculateTotalPrice() {
    return "";
    }

    With the following calculateTotalPrice Java method:

    public static float calculateTotalPrice(float taxRate, int quantity, float price, boolean fillOrder)
    {
    float totalTax, costNoTax, totalCost;
    if (fillOrder)
    {
    // Calculate the total tax
    totalTax = taxRate * quantity * price;
    // Calculate the total cost without tax
    costNoTax = quantity * price;
    // Add the tax and the cost to get the total cost
    totalCost = totalTax + costNoTax;
    		}
    		else
    		{
    			    totalCost = 0;
    		}
    		return totalCost;
       }
Note: Ensure you modify the return type of the calculateTotalPrice function from String to float.
  1. Save all the files in this application. From the BEA WorkSpace Studio menu bar, choose File > Save All.
  2. Note: XQuery Mapper executes the user defined java method used in XQuery only if the java method is static.
To Join Two Sets of Repeating Elements
  1. View myJoin.xq in the Design view:
    1. In the Navigator pane, double-click Tutorial_Process_Application_Web\src\requestquote\myJoin.xq and select the Design.
  2. In the Source pane, collapse the shipAddress node.
  3. From the Source pane, drag-and-drop the priceQuote1\priceRequests\priceRequest node onto the quote\quoteResponse node in the Target pane.
  4. These nodes are both repeating nodes. A repeating node means more than one instance of this node can be specified. The + symbol to the right of the node indicates these nodes are repeating nodes.

    WARNING: You must select the priceRequest node and not the priceRequests node.

    A dashed line linking the two repeating nodes is displayed, as shown in the following figure.

    The dashed line with short dashes represents a structural link—a link between two parent structures that does not map data directly.

    To learn more about XML repeating nodes, see Understanding XML Repeating Nodes.

  5. From the Source pane, drag-and-drop the availQuote1\availRequest node onto the quote\quoteResponse node in the Target pane.
  6. A dashed line linking the two repeating elements is displayed, as shown in the following figure.

    Figure 5-2 Joining Two Sets of Repeating Elements


    Joining Two Sets of Repeating Elements

  7. Select the Source tab to view the changes to the query.
  8. The following query is displayed in Source:

    declare namespace xf = "http://tempuri.org/Tutorial_Project_Web/src/requestquote/myJoin/";
    declare namespace ns0 = "http://www.example.org/price";
    declare namespace ns1 = "http://www.example.org/avail";
    declare namespace ns2 = "http://www.example.org/quote";
    declare function xf:myJoin($priceQuote1 as element(ns0:priceQuote),
        $availQuote1 as element(ns1:availQuote),
        $taxRate as xs:float)
        as element(ns2:quote) {
            <ns2:quote>
                <name>{ data($priceQuote1/ns0:customerName) }</name>
                <address>{ concat($priceQuote1/ns0:shipAddress/@street ,",", $priceQuote1/ns0:shipAddress/@city ,",", fn:upper-case($priceQuote1/ns0:shipAddress/@state) , ",",$priceQuote1/ns0:shipAddress/@zip) }</address>
                {
    for 
    $priceRequest in $priceQuote1/ns0:priceRequests/ns0:priceRequest,
    $availRequest in $availQuote1/ns1:availRequest
    return
                        <quoteResponse/>
                }
            </ns2:quote>
    };
    declare variable $priceQuote1 as element(ns0:priceQuote) external;
    declare variable $availQuote1 as element(ns1:availQuote) external;
    declare variable $taxRate as xs:float external;
    xf:myJoin($priceQuote1,
        $availQuote1,
        $taxRate)

    In the preceding query, there are no data links between the children of the repeating nodes, so the quoteResponse element is empty. (The string: <quoteResponse/> is an empty node.)

    The structural links between the repeating nodes generates the for loop which is shown in bold in the preceding query listing. This XQuery for loop iterates through the set of priceRequest and availReqest repeating elements. For example, if the source XML data to this query contains three instances of the priceRequest element and three instances of the availRequest element, the for loop would execute a total of nine times with the following combinations:

    • The first instance of the priceRequest element with the first instance of availRequest element.
    • The first instance of the priceRequest element with the second instance of availRequest element.
    • The first instance of the priceRequest element with the third instance of availRequest element.
    • The second instance of the priceRequest element with the first instance of availRequest element.
    • The second instance of the priceRequest element with the second instance of availRequest element.
    • The second instance of the priceRequest element with the third instance of availRequest element.
    • The third instance of the priceRequest element with the first instance of availRequest element.
    • The third instance of the priceRequest element with the second instance of availRequest element.
    • The third instance of the priceRequest element with the third instance of availRequest element.
    • For some transformations, you may want the query to generate all the possible combinations but for others, you may want to constrain the combinations as described in the following steps.

  9. Select the Design tab.
  10. From the Source pane, drag-and-drop the priceQuote1/priceRequests/priceRequest/widgetId node onto the availQuote1/availRequest/widgetId node. Both of these elements are in the Source pane.
  11. A line between the two widgetId nodes is displayed, as shown in the following figure.

    Figure 5-3 Link Between Two WidgetId Nodes


    Link Between Two WidgetId Nodes

  12. View the changes to the query by clicking the Source tab.
  13. The following query is displayed:

    declare namespace xf = "http://tempuri.org/Tutorial_Process_Application_Web/src/requestquote/myJoin/";
    declare namespace ns0 = "http://www.example.org/price";
    declare namespace ns1 = "http://www.example.org/avail";
    declare namespace ns2 = "http://www.example.org/quote";
    declare function xf:myJoin($priceQuote1 as element(ns0:priceQuote),
       $availQuote1 as element(ns1:availQuote),
       $taxRate as xs:float)
       as element(ns2:quote) {
    <ns2:quote>
                <name>{ data($priceQuote1/ns0:customerName) }</name>
                  <address>{ concat($priceQuote1/ns0:shipAddress/@street ,",",                 $priceQuote1/ns0:shipAddress/@city ,",",                 fn:upper-case($priceQuote1/ns0:shipAddress/@state) , ",",                 $priceQuote1/ns0:shipAddress/@zip) }</address>
                {
    for $priceRequest in $priceQuote1/ns0:priceRequests/ns0:priceRequest,
        $availRequest in $availQuote1/ns1:availRequest
    where 
    data($priceRequest/ns0:widgetId) = data($availRequest/ns1:widgetId)
    return
                        <quoteResponse/>
                }
            </ns2:quote>
    };
    declare variable $priceQuote1 as element(ns0:priceQuote) external;
    declare variable $availQuote1 as element(ns1:availQuote) external;
    declare variable $taxRate as xs:float external;
    xf:myJoin($priceQuote1,
        $availQuote1,
        $taxRate)

    The link between the widgetId nodes generates the where clause in the for loop, as shown in bold in the preceding query listing. This where clause constrains or limits the output of the for loop. Specifically, the where clause specifies that if the expression in the where clause is true, the for loop will output the contents of the return. For this example, if the widgetId of the availRequest element is equal to the widgetId of the priceQuest element, the following XML data is returned:

    <quoteResponse/>

    An empty quoteReponse element isn’t very useful. In the following task: Add Links to Populate the quoteResponse Element, you will add data links that will populate the quoteResponse element.

Add Links to Populate the quoteResponse Element
  1. Select the Design tab.
  2. From the Source pane, drag-and-drop the priceQuote1/priceRequests/priceRequest/widgetId node onto the quote/quoteResponse/widgetId node in the Target pane.
  3. From the Source pane, drag-and-drop the priceQuote1/priceRequests/priceRequest/price node onto the quote/quoteResponse/unitPrice node in the Target pane.
  4. From the Source pane, drag-and-drop the availQuote1/availRequest/requestedQuantity node onto the quote/quoteResponse/requestedQuantity node in the Target pane.
  5. From the Source pane, drag-and-drop the availQuote1/availRequest/quantityAvail node onto the quote/quoteResponse/fillOrder node in the Target pane.
  6. From the Source pane, drag-and-drop the availQuote1/availRequest/shipDate node onto the quote/quoteResponse/shipDate node in the Target pane.
  7. From the Source pane, drag-and-drop the taxRate Java primitive onto the quote/quoteResponse/taxRate node in the Target pane.
  8. From the Source pane, drag-and-drop the taxRate Java primitive onto the quote/quoteResponse/totalCost node in the Target pane.
  9. Note: In the next section, to calculate the total cost of the order, you will edit the link between the taxRate Java primitive and the quote/quoteResponse/totalCost node.

    In the Design view, the following links are displayed, as shown in the following figure.

    Figure 5-4 Linking Source and Target Panes


Linking Source and Target Panes

  1. Save all the files in this application. From the BEA WorkSpace Studio menu bar, choose File > Save All.
Call the calculateTotalPrice User Method From the Query
  1. Select the Design tab.
  2. Select the link between the taxRate Java primitive and the quote/quoteResponse/totalCost node.
  3. In the Expression Functions Palette, find the User Functions folder.
  4. In the User Functions folder, select the calculateTotalPrice function, and drag-and-drop it into the General Expression pane.
  5. In the Source pane, select the taxRate node and drag-and-drop it onto the $float-var parameter of the General Expression pane.
  6. In the General Expression pane, the default argument: $float-var is replaced with the $taxRate argument and the next argument becomes selected.

    Select $int-var in the General Expression pane.

  7. In the Source pane, select availQuote1/availRequest/requestedQuantity and drag-and-drop it onto the selected $int-var argument in the General Expression pane.
  8. In the General Expression pane, the default argument: $int-var is replaced with the $availRequest/ns1:requestedQuantity argument and the next argument becomes selected.

    Select $float-var in the General Expression pane.

  9. In the Source pane, select priceQuote1/priceRequests/priceRequest/price and drag-and-drop it onto the selected $float-var argument in the General Expression pane.
  10. In the General Expression pane, the default argument: $float-var is replaced with the $priceRequest/ns0:price argument and the next argument becomes selected.

    Select $boolean-var in the General Expression pane.

  11. In the Source pane, select availQuote1/availRequest/quantityAvail and drag-and-drop it onto the selected $boolean-var argument in the General Expression pane.
  12. In the General Expression pane, the default argument: $boolean-var is replaced with the $availRequest/ns1:quantityAvail argument, as shown in Figure 5-5.

    Figure 5-5 General Expression pane


General Expression pane

  1. Click Apply.
  2. In the Design view, the following is displayed, as shown in Figure 5-6.

    Figure 5-6 Link Between Source and Target Panes


    Link Between Source and Target Panes

  3. Save all the files in this application. From the BEA WorkSpace Studio menu bar, choose File > Save All.
To View the Generated Query
  1. Select the Source tab.
  2. The following query is displayed in Source view:

    declare namespace xf = "http://tempuri.org/Tutorial_Process_Application_Web/src/requestquote/myJoin/";
    declare namespace ns0 = "http://www.example.org/price";
    declare namespace ns1 = "http://www.example.org/avail";
    declare namespace ns2 = "http://www.example.org/quote";
    declare function xf:myJoin($priceQuote1 as element(ns0:priceQuote),
        $availQuote1 as element(ns1:availQuote),
        $taxRate as xs:float) as element(ns2:quote)
        {
          <ns2:quote>
    {
    <name>{ data($priceQuote1/ns0:customerName) }</name>
                <address>{ concat($priceQuote1/ns0:shipAddress/@street ,",", $priceQuote1/ns0:shipAddress/@city ,",", fn:upper-case($priceQuote1/ns0:shipAddress/@state) , ",", $priceQuote1/ns0:shipAddress/@zip) }</address>
                {
                    for $priceRequest in                    $priceQuote1/ns0:priceRequests/ns0:priceRequest,
                        $availRequest in $availQuote1/ns1:availRequest
                    where data($priceRequest/ns0:widgetId) =                    data($availRequest/ns1:widgetId)
                    return
    <quoteResponse>
    <widgetId>{ data($priceRequest/ns0:widgetId) }</widgetId>
                     <unitPrice>{ data($priceRequest/ns0:price) }</unitPrice>
                     <requestedQuantity>{                  data($availRequest/ns1:requestedQuantity) }</requestedQuantity>
    <fillOrder>{ data($availRequest/ns1:quantityAvail) }</fillOrder>

    {
    for $shipDate in $availRequest/ns1:shipDate
    return
    <shipDate>{ data($shipDate) }</shipDate>
    }
    <taxRate>{ $taxRate }</taxRate>
    <totalCost>{ calculateTotalPrice($taxRate,
    $availRequest/ns1:requestedQuantity,
    $priceRequest/ns0:price,
    $availRequest/ns1:quantityAvail) }</totalCost>
    </quoteResponse>
    }
    </ns2:quote>

The links added in the preceding task generate the additional XQuery source code listed between the <quoteResponse> and </quoteResponse> tags highlighted in bold in the preceding query listing.

To Test the Query
  1. Select the Test View tab.
  2. There are three import parameters to the myJoin Transformation method: $priceQuote1, $availQuote1, and $taxRate. In the task: To Test a Simple Query, you imported PriceQuote.xml as source data for the $priceQuote1 parameter.

  3. Import AvailQuote.xml for the source parameter: $availQuote1:
    1. From the drop-down menu in the Source Data pane, select availQuote1.
    2. Click Import...
    3. The Import File to Test dialog box is displayed.

    4. Double-click the src folder.
    5. Double-click the testxml folder.
    6. Double-click the AvailQuote.xml file.
    7. A graphical representation of the AvailQuote.xml file appears in the Source Data pane.

  4. From the drop-down menu in the Source Data pane, select taxRate .
  5. In the Node Value field of the taxRate node, double-click the existing value, enter "0.08", and then press Enter on your keyboard.
  6. In the Result Data, pane click Test.
  7. The query is run with the test XML data. A graphical representation of the resulting XML data is shown in the XML Design View of the Result Data pane, as shown in Figure 5-7.

    Figure 5-7 Result Data Pane


    Result Data Pane

    This query joins the two sets of source repeating elements (availRequest and priceRequest) to a single repeating element (quoteResponse).

  8. To check that the resulting XML data from the query is valid against the associated XML Schema, in the Result Data pane click Validate.
  9. The Output tab will show whether the XML data is valid to the target XML Schema. In this example, the resulting XML data is checked against the XML Schema in the Quote.xsd file.

Create an Instance of the MyTutorialJoin Control

In this task, you create an instance of the MyTutorialJoin.java control.

Note: Switch from XQuery Transformation perspective to Process perspective to create a control. Click Window > Open Perspective > Other > Process Perspective.
  1. View the RequestQuote business process in the Design view:
    1. In the Package Explorer pane, navigate to Tutorial_Process_Application_Web\src\requestquote\RequestQuote.java and double-click the RequestQuote.java.
    2. Drag-and-drop the myTutorialJoin.java transformation file from the src/requestquote folder to the Controls folder in the DataPalette.
    3. An instance called MyTutorialJoin.java is created in your project and displayed in the Controls pane as shown by the following figure:

      Figure 5-8 Instance of MyTutorialJoin.java


      Instance of MyTutorialJoin.java

Edit the Node That Invokes the Transformation

In this task, you edit the Combine Price and Avail Quotes node in the RequestQuote business process and change the instance that gets invoked by this node from an instance of the TutorialJoin.java to an instance of the MyTutorialJoin.java. Additionally, you change the design of the Combine Price and Avail Quotes node to call the myJoin() method on the MyTutorialJoin control. The myJoin() method combines the data returned to your business process from different systems creating a single XML response document (quote) that is subsequently returned to the business process’s client.

  1. In the RequestQuote business process, double-click the Combine Price and Avail Quotes node to open its node builder.
  2. The node builder opens on the General Settings pane.

  3. From the drop-down menu in the Control field select myTutorialJoin.
  4. Select QuoteDocument myJoin() from the Method field.
  5. Click Send Data to open the second pane of the node builder.
  6. The Select variables to assign fields are populated with default variables. The data types match the data type expected in the source parameters to the myJoin() method as shown in the following list:

    priceQuote holds the price quote data, which is returned from the PriceProcessor service in the For Each loop in your business process.

    availQuote holds the availability quote data, which is returned from the AvailProcessor service in the For Each loop in your business process.

    taxRate holds the rate of sales tax applied to the quote, based on the shipping address, which is returned to your business process from the taxCalculation service.

    The Control Expects fields are populated with the data type expected by the myJoin() method on the MyTutorialJoin control, as shown in the following figure.


    Instance of MyTutorialJoin.java

  7. Click Receive Data to open the third pane of the node builder.
  8. On the Receive Data tab, the Select variables to assign field is populated with the default variable: Quote. The data type matches the data type expected in the target parameter to the myJoin() method. The Control Returns field is populated with the data type returned by the myJoin() method: QuoteDocument, as shown in the following figure.


    Instance of MyTutorialJoin.java

  9. In the node builder, click Close save your specifications and close the node builder.
  10. Save all the files in this application, including the RequestQuote business process. From the BEA WorkSpace Studio menu bar, choose File > Save All.
To Run the Business Process

Earlier in this tutorial, you entered the XML data that is run against the query. During run time, the business process builds the XML data and passes it to the query that was built in this tutorial. To run the business process and invoke the query, follow the instructions in Step 12: Run the RequestQuote Business Process in the Tutorial: Building Your First Business Process.


  Back to Top       Previous  Next