(: This application implements a slightly simplified and adapted version of version 1.3 of the TPC-APP benchmark ( http://www.tpc.org/ ) using the Demaq Queue Language (DQL) :) (:--------------------------------------- Prolog ---------------------------------------- :) declare default errorqueue errorMessages; (:declare namespace soap = "http://schemas.xmlsoap.org/soap/envelope/";:) declare function local:soapify($message as node()) as node()+ { {$message} }; declare function local:desoapify($message as node()) as node()+ { $message (:TODO:) (: /soap:Envelope/soap:Body/*:) }; declare function local:shippingCosts($shippingZone as xs:integer, $volume as xs:double) as xs:double { (:TODO: use matrix:) 42.0 }; declare variable $taxRate as xs:decimal := 0.17; (:TODO dynamic lookup:) (:---------------------------------- Application Code ----------------------------------- :) (: -------------------------------- Error handling -------------------------------------- :) create queue errorMessages kind basic mode persistent; create rule logErrors for errorMessages ( qs:traceXDM(.), enqueue message . into outgoingMessages ) ; (:-------------------- Communication analysis / local message forwarding ---------------- :) create queue incomingMessages kind incoming interface "http" port "2342" response outgoingMessages mode persistent; create rule routeIncomingRequests for incomingMessages let $payload := local:desoapify(.) return if($payload//NewCustomer) then enqueue message $payload//NewCustomer into newCustomer else if($payload//ChangePaymentMethod) then enqueue message $payload//ChangePaymentMethod into changePayment else if($payload//CreateOrder) then enqueue message $payload//CreateOrder into createOrder else if($payload//OrderStatus) then enqueue message $payload//OrderStatus into orderStatus else if($payload//NewProducts) then enqueue message $payload//NewProducts into newProduct else if($payload//CheckProducts) then enqueue message $payload//CheckProducts into checkProducts else if($payload//ProductDetail) then enqueue message $payload//ProductDetail into productDetail else if($payload//firstMessage) then qs:traceMessage(concat("First message processed at ", xs:string(current-dateTime()))) else enqueue message Encountered unexpected message in routeIncomingRequests rule into outgoingMessages ; (: --------------------- Handle "new customer" requests --------------------:) create queue newCustomer kind basic mode transient; create queue createCustomer kind basic mode transient; create queue customerPovResponses kind basic mode transient; create queue customers kind basic mode persistent; create queue addresses kind basic mode persistent; create property businessName queue newCustomer fixed value /NewCustomer/BUSINESS_NAME/text(); create slicing customerRequestsBN on businessName require false(); create property addressCity queue addresses fixed value /address/city/text(); create slicing addressesByCity on addressCity require false(); create rule newCustomerRequest for newCustomer let $newCustomer := /NewCustomer let $paymentMethod as xs:string := string($newCustomer/PAYMENT_METHOD) return if($paymentMethod eq "PO") then (: we must check credit status of customer by invoking POV service:) let $businessName := qs:property("businessName") let $poid as xs:integer := xs:integer(data(/NewCustomer/PO_ID)) let $request := {$businessName} {$poid} return enqueue message local:soapify($request) into externalCustomerPOV else (: simply forward without checking credit status:) enqueue message . into createCustomer; create rule customerPovResponseRequest for customerPovResponses let $result as xs:integer := xs:integer(data(//POV_AuthorizationResult)) let $businessName := //Business_Name/text() return if($result gt 0) then let $correlatedRequest as node() := qs:slice($businessName,"customerRequestsBN")[last()] (:add PO ID:) let $message := {$correlatedRequest/NewCustomer/* , {$result}} return enqueue message $message into createCustomer else (:send error notification to user:) enqueue message POV Checking failed into errorMessages; create rule handleCreateCustomer for createCustomer let $newCustomer := /NewCustomer let $addr1 := $newCustomer/BILLING_ADDR1/text() let $addr2 := $newCustomer/BILLING_ADDR2/text() let $city := $newCustomer/BILLING_CITY/text() let $state := $newCustomer/BILLING_STATE/text() let $zip := $newCustomer/BILLING_ZIP/text() let $country := $newCustomer/BILL_COUNTRY/text() let $addresses := qs:slice($city, "addressesByCity") let $previousAddress as node()? := $addresses[/address/addr1/text() eq $addr1 and /address/addr2/text() eq $addr2 and /address/state/text() eq $state and /address/zip/text() eq $zip and /address/country/text() eq $country]//id let $customerID := $newCustomer/CUSTOMER_ID/text() let $partialCustomerRecord := {$customerID} {$newCustomer/BUSINESS_NAME/text()} {$newCustomer/BUSINESS_INFO/text()} {$newCustomer/PASSWORD/text()} {$newCustomer/CONTACT_F_NAME/text()} {$newCustomer/CONTACT_L_NAME/text()} {$newCustomer/CONTACT_PHONE/text()} {$newCustomer/CONTACT_EMAIL/text()} {$newCustomer/PAYMENT_METHOD/text()} {$newCustomer/CREDIT_INFO/text()} {$newCustomer/PO} 0> return ( if(not(empty($previousAddress))) then let $customerRecord := {$previousAddress/text()} {$partialCustomerRecord/*} return enqueue message $customerRecord into customers else (:address not found yet, create new:) let $newAddressID as xs:string := qs:uniqueID() let $address :=
{$newAddressID} {$addr1} {$addr2} {$city} {$state} {$zip} {$country}
let $customerRecord := {$newAddressID} {$partialCustomerRecord/*} return ( enqueue message $address into addresses, enqueue message $customerRecord into customers ) , let $customerReply := {$customerID} return enqueue message local:soapify($customerReply) into outgoingMessages ) ; (: ------------------------------- Change payment requests -----------------------------------:) create queue changePayment kind basic mode transient; create queue paymentPovResponses kind basic mode transient; create queue updatePayment kind basic mode transient; create slicing property customersByID queue customers fixed value /customer/customerID/text() require count(qs:history()) eq 1; create slicing property customersByBusinessName queue customers fixed value /customer/businessName/text() require count(qs:history()) eq 1; create slicing property changePaymentRequestsByCustomerID queue changePayment fixed value /ChangePaymentMethod/C_ID/text() require count(qs:history()) eq 1; create rule handleChangePaymentRequest for changePayment let $request := /ChangePaymentMethod let $customerID := $request/C_ID/text() let $paymentMethod := $request/PAYMENT_METHOD/text() let $creditInfo := $request/CREDIT_INFO/text() let $poid := $request/PO_ID/text() return if($paymentMethod eq "PO") then let $customerMasterdata := qs:slice($customerID, "customersByID")/customer let $businessName := $customerMasterdata/businessName/text() let $povRequest := {$businessName} {$poid} return enqueue message local:soapify($povRequest) into externalPaymentPOV else (: no check needed, directly proceed :) enqueue message . into updatePayment ; create rule handlePaymentPovResponse for paymentPovResponses let $result as xs:integer := xs:integer(data(//POV_AuthorizationResult)) let $businessName := //Business_Name/text() return if($result gt 0) then let $customer := qs:slice($businessName,"customersByBusinessName") let $customerID := $customer/customer/customerID/text() let $correlatedRequest := qs:slice($customerID,"changePaymentRequestsByCustomerID") let $message := {$correlatedRequest/ChangePaymentMethod/* , {$result}} return enqueue message $message into updatePayment else (:send error notification to user:) enqueue message POV Checking failed into errorMessages; create rule handleUpdatePaymentRequest for updatePayment let $request := /ChangePaymentMethod let $customerID := $request/C_ID/text() let $oldMasterData := qs:slice($customerID,"customersByID")/customer let $paymentMethod := $request/PAYMENT_METHOD/text() let $creditInfo := $request/CREDIT_INFO/text() let $newMasterData := {($oldMasterData/addressID, $oldMasterData/customerID, $oldMasterData/businessName, $oldMasterData/businessInfo, $oldMasterData/password, $oldMasterData/contactFirstname, $oldMasterData/contactLastname, $oldMasterData/contactPhone, $oldMasterData/contactEmail)} {$paymentMethod} {$creditInfo} {$request/PO} {$oldMasterData/discount} let $customerReply := {$paymentMethod} return ( enqueue message $newMasterData into customers , enqueue message local:soapify($customerReply) into outgoingMessages ) ; (: ------------------------------- Create order requests -----------------------------------:) create queue createOrder kind basic mode transient; create queue orderPGEResponses kind basic mode persistent; create queue orderAddressHandling kind basic mode transient; create queue costCalculation kind basic mode transient; create queue capacityCheck kind basic mode transient; create queue orders kind basic mode persistent; create queue items kind basic mode persistent; create queue stocks kind basic mode persistent; create queue shipping kind basic mode persistent; create slicing property itemByID queue items fixed value /item/id/text() require count(qs:history()) eq 1; create slicing property createOrderByOrderID queue createOrder fixed value /CreateOrder/ORDER_ID/text() require count(qs:history()) eq 1; create slicing property addressesByID queue addresses fixed value /address/id/text() require false(); create slicing property stocksByItemID queue stocks fixed value /stock/id/text() require count(qs:history()) eq 1; create rule handleCreateOrderRequests for createOrder let $request as node() := /CreateOrder let $customerID := $request/CUSTOMER_ID/text() let $orderID := $request/ORDER_ID/text() (:determine payment method:) let $customerMasterdata as node()? := qs:slice($customerID, "customersByID")/customer return if(empty($customerMasterdata)) then enqueue message Cannot find customer masterdata for customerID {$customerID} into errorMessages else let $paymentMethod := $customerMasterdata/paymentMethod/text() return if($paymentMethod eq "CC") then (:invoke external payment gateway to check credit card:) let $pgeRequest := {(//CC_TYPE, //CC_EXPIRY, //CC_NAME)} {$orderID} return enqueue message local:soapify($pgeRequest) into externalPGE else enqueue message . into orderAddressHandling (:simply forward:) ; create rule handleOrderPGEResponses for orderPGEResponses let $result as xs:integer := xs:integer(data(//PGE_Validate_CardResult)) return if($result gt 0) then let $orderID := //orderID/text() let $correlatedRequest as node():= qs:slice($orderID,"createOrderByOrderID")/CreateOrder let $message := {$correlatedRequest/*} {$result} return enqueue message $message into orderAddressHandling else enqueue message External PGE gateway failure into errorMessages ; (:TODO: factorize address creation code with newcustomer code above:) create rule handleOrderAddress for orderAddressHandling let $request as node() := /CreateOrder let $customerID := $request/CUSTOMER_ID/text() let $shippingAddress as node()? := $request/SHIPPING_STREET1 return if(not(empty($shippingAddress))) then (: we got a shipping address :) let $addr1 := $request/SHIPPING_STREET1/text() let $addr2 := $request/SHIPPING_STREET2/text() let $city := $request/SHIPPING_CITY/text() let $state := $request/SHIPPING_STATE/text() let $zip := $request/SHIPPING_ZIP/text() (: check whether shipping address already exists in database :) let $addresses := qs:slice($city, "addressesByCity") (: get country from customer master data default address (pretty strange!):) let $customerMasterdata as node() := qs:slice($customerID, "customersByID")/customer let $defaultAddressID := $customerMasterdata/addressID/text() let $defaultAddress as node() := qs:slice($defaultAddressID, "addressesByID")/address let $country as node() := $defaultAddress/country let $previousAddress as node()? := $addresses[/address/addr1/text() eq $addr1 and /address/addr2/text() eq $addr2 and /address/state/text() eq $state and /address/zip/text() eq $zip and /address/country/text() eq $country]//id return if(empty($previousAddress)) then (:add address to queue:) let $newAddressID as xs:string := qs:uniqueID() let $address :=
{$newAddressID} {$addr1} {$addr2} {$city} {$state} {$zip} {$country}
return ( enqueue message $address into addresses, let $result:={$request/*}{$newAddressID} return enqueue message $result into costCalculation ) else let $addressID := $previousAddress/text() let $result := {$request/*}{$addressID} return enqueue message $result into costCalculation else let $customerMasterdata as node() := qs:slice($customerID, "customersByID")/customer let $addressID := $customerMasterdata/addressID/text() let $address as node() := qs:slice($addressID, "addressesByID")/address let $result := {$request/*} {$address/addr1/text()} {$address/addr2/text()} {$address/city/text()} {$address/state/text()} {$address/zip/text()} {$address/country/text()} {$addressID} return enqueue message $result into costCalculation ; create rule calculateCosts for costCalculation let $request as node() := /CreateOrder (:obtain customer discount:) let $customerID := $request/CUSTOMER_ID/text() let $customerMasterdata as node() := qs:slice($customerID,"customersByID")/customer let $discountValue as xs:double := xs:double(data($customerMasterdata/discount)) (:determine line items of order:) let $orderLines:= for $itemID at $itemPosition in $request/ITEM_ID/text() let $quantity as xs:integer := xs:integer(data($request/QTY[$itemPosition])) let $currentItem as node() := qs:slice($itemID, "itemByID")/item let $cost as xs:double := xs:double(data($currentItem/cost)) let $dimensions := $currentItem/dimensions let $length as xs:double := xs:double($dimensions/length) let $width as xs:double := xs:double($dimensions/width) let $height as xs:double := xs:double($dimensions/height) let $volume as xs:double := $length * $width * $height return {$itemPosition} {$itemID} {$quantity} {$volume*$quantity} {$cost} let $subTotal as xs:double := sum($orderLines/(cost*quantity))*(1-$discountValue div 100) let $tax as xs:double := $subTotal * $taxRate let $shipVolume as xs:double := sum($orderLines/totalVolume) let $shippingCost as xs:double := local:shippingCosts(23, $shipVolume) (:TODO: zone:) let $shippingType := $request/SHIPPING_TYPE/text() let $shippingAddressID := $request/addressID/text() let $authentificationID := if(empty($request/authentificationID)) then "PO" else $request/authentificationID/text() let $totalCosts as xs:double := $subTotal + $tax + $shippingCost let $orderID := $request/ORDER_ID/text() let $result := {$orderID} {$customerID} {current-dateTime()} {$subTotal} {$tax} {$totalCosts} {$shippingCost} {$shippingType} {$shippingAddressID} {$authentificationID} {$discountValue} {$orderLines} { (:propagate shipping information:) $request/SHIPPING_STREET1, $request/SHIPPING_STREET2, $request/SHIPPING_CITY, $request/SHIPPING_STATE , $request/SHIPPING_ZIP, $request/COUNTRY_NAME } return enqueue message $result into capacityCheck ; create rule checkCapacity for capacityCheck let $request := /order (:check availability for all items:) let $orderLines as node()* := for $orderLine in $request/orderLines/orderLine let $itemID := $orderLine/itemID/text() let $orderQuantity as xs:integer := xs:integer($orderLine/quantity) let $stockItem as node()? := qs:slice($itemID, "stocksByItemID")/stock let $stockQuantity as xs:integer := if(empty($stockItem)) then 0 else xs:integer($stockItem/quantity) return { $orderLine/*, { if($stockQuantity ge $orderQuantity) then "PENDING" else "BACK_ORDERED" } } let $result := let $isBackorder as xs:boolean := not(empty($orderLines[status/text() eq "BACK_ORDERED"])) return {$request/id, $request/customerID, $request/date, $request/subTotal, $request/tax, $request/total, $request/shippingCost, $request/shippingType, $request/shippingAddressID, $request/authentificationID, $request/discount} {$orderLines} { (:propagate shipping information:) $request/SHIPPING_STREET1, $request/SHIPPING_STREET2, $request/SHIPPING_CITY, $request/SHIPPING_STATE , $request/SHIPPING_ZIP, $request/COUNTRY_NAME } { if($isBackorder) then "BACK_ORDERED" else "PENDING" } return enqueue message $result into orders ; create rule triggerShipping for orders let $request as node() := /order let $orderID := $request/id/text() let $requestedTime := "2009-12-12" let $status := $request/status/text() let $street1 := $request/SHIPPING_STREET1/text() let $street2 := $request/SHIPPING_STREET2/text() let $city := $request/SHIPPING_CITY/text() let $state := $request/SHIPPING_STATE/text() let $zip := $request/SHIPPING_ZIP/text() let $country := $request/SHIPPING_COUNTRY/text() let $result := {$orderID} {$requestedTime} {$status} {$street1} {$street2} {$city} {$state} {$zip} {$country} return enqueue message $result into shipping ; create rule customerConfirmation for orders let $request as node() := /order let $orderID := $request/id/text() let $status := $request/status/text() let $total := $request/total/text() let $items as node()*:= for $item as node() in $request/orderLines/orderLine return {$item/id, $item/quantity, $item/cost, $item/status} let $result := {$orderID} {$status} {$total} {$request/total, $request/tax, $request/shippingCost, $request/discount} {$items} return enqueue message $result into outgoingMessages ; (: ---------------------------------- Shipping process --------------------------------------:) create queue shippingSNEResponse kind basic mode persistent; create slicing property shippingsByOrderID queue shipping fixed value /RequestShipping/orderID/text() require count(qs:history()) eq 1; create slicing property ordersByOrderID queue orders fixed value /order/id/text() require count(qs:history()) eq 1; create rule handlePendingShippingRequest for shipping let $request as node() := /RequestShipping return if($request/status/text() eq "PENDING") then let $result := {$request/orderID/text()} {$request/street1/text()} {$request/street2/text()} {$request/city/text()} {$request/state/text()} {$request/zip/text()} {$request/country/text()} return enqueue message local:soapify($result) into externalSNE else () (:handled by another rule:) ; create rule handleshippingSNEResponse for shippingSNEResponse let $request as node() := local:desoapify(.)//SNE_ShippingResult let $orderID := $request/ORDER_ID/text() let $trackingNumber as xs:integer := xs:integer($request/SNE_TrackingNumber) let $correlatedShipping as node() := qs:slice($orderID, "shippingsByOrderID")/RequestShipping return if($trackingNumber gt 0) then (:everything worked, order is shipped, store in shipping queue (simulate inplace update):) let $result := {$orderID} SHIPPED {current-dateTime()} { (: include all data from correlatedShipping request :) $correlatedShipping/requestedTime, $correlatedShipping/street1, $correlatedShipping/street2 , $correlatedShipping/city, $correlatedShipping/state , $correlatedShipping/zip, $correlatedShipping/country } return enqueue message $result into shipping else enqueue message qs:slice($orderID, "shippingsByOrderID") into shipping (: try again :) ; create rule handleBackorderedShippingRequest for shipping let $request as node() := /RequestShipping return if($request/status/text() eq "BACK_ORDERED") then let $orderID := $request/orderID/text() (: NOTE we loose Vpartitioning here by accessing orders:) let $correlatedOrder as node() := qs:slice($orderID, "ordersByOrderID")/order let $result := {$request/requestedTime/text()} {$request/orderID/text()} { (: we can handle several backorders at once :) for $backorderedItem as node() in $correlatedOrder/orderLines/orderLine where $backorderedItem/status/text() eq "BACK_ORDERED" return ({$backorderedItem/itemID/text()}, {$backorderedItem/quantity/text()}) } {$request/street1/text()} {$request/street2/text()} {$request/city/text()} {$request/state/text()} {$request/zip/text()} {$request/country/text()} return enqueue message $result into stockManagement else () (:handled by another rule:) ; (: ---------------------------------- Stock management --------------------------------------:) create queue stockManagement kind basic mode persistent; create queue stockICEResponse kind basic mode persistent; create queue stockAndShippingUpdates kind basic mode transient; create slicing property stockManagementByOrderID queue stockManagement fixed value /BackorderedRequest/SHIPPING_O_ID/text() require count(qs:history()) eq 1; create rule handleBackorderedStock for stockManagement let $request as node() := /BackorderedRequest let $result := { $request/SHIPPING_REQUEST_TIME, $request/SHIPPING_O_ID, for $itemID as node() at $position in $request/REORDER_I_ID let $quantity as node() := $request/REORDER_QTY[$position] return ({$itemID/text()},{$quantity/text()}) } return enqueue message local:soapify($result) into externalICE ; create rule handleStockICEResponse for stockICEResponse let $request as node() := local:desoapify(.)//ICE_ReorderResponse let $result := $request/ICE_ReorderResult/text() let $orderID := $request/SHIPPING_O_ID/text() return if($result eq "OK") then enqueue message $request into stockAndShippingUpdates (:forward for processing:) else (:restart transaction by re-enqueuing initial request into stock management queue:) let $correlatedRequest as node() := qs:slice($orderID, "stockManagementByOrderID") return enqueue message $correlatedRequest into stockManagement ; create rule updateStock for stockAndShippingUpdates let $request as node() := /ICE_ReorderResponse let $orderID := $request/SHIPPING_O_ID/text() (: NOTE we loose Vpartitioning here by accessing orders:) let $correlatedOrder as node() := qs:slice($orderID, "ordersByOrderID")/order for $backorderedItem as node() in $correlatedOrder/orderLines/orderLine where $backorderedItem/status/text() eq "BACK_ORDERED" return (: no need to access stocks here as tpc-app blindly sets a new value :) (:TODO: should be random value between 20 and 30:) let $result := {$backorderedItem/itemID/text()} 23 return enqueue message $result into stocks ; create rule updateShipping for stockAndShippingUpdates let $request as node() := /ICE_ReorderResponse let $orderID := $request/SHIPPING_O_ID/text() let $correlatedShipping as node() := qs:slice($orderID, "shippingsByOrderID")/RequestShipping let $result := {$correlatedShipping/orderID, $correlatedShipping/requestedTime} PENDING {$correlatedShipping/street1, $correlatedShipping/street2, $correlatedShipping/city, $correlatedShipping/state, $correlatedShipping/zip, $correlatedShipping/country } return enqueue message $result into shipping (: try again :) ; (: ------------------------------------ Order status ----------------------------------------:) create queue orderStatus kind basic mode transient; create queue verifiedOrderStatus kind basic mode transient; create slicing property ordersByCustomerID queue orders fixed value /order/customerID/text() require false(); create rule checkOrderStatusPassword for orderStatus let $request as node() := /OrderStatus let $customerID := $request/CUSTOMER_ID/text() let $suppliedPassword := $request/PASSWD/text() let $businessName := $request/BUSINESS_NAME/text() let $orderCount := $request/ORDER_COUNT/text() let $customerMasterdata as node()? := qs:slice($customerID, "customersByID")/customer return if(empty($customerMasterdata)) then enqueue message Cannot access customer masterdata for customerID {$customerID} into errorMessages else if($suppliedPassword eq $customerMasterdata/password/text()) then (:password is OK, forward to verifiedOrderStatus queue:) let $result := {$customerMasterdata} {$orderCount} return enqueue message $result into verifiedOrderStatus else enqueue message Got wrong password for customer into errorMessages ; create rule compileOrderStatus for verifiedOrderStatus let $request as node() := /OrderStatus let $customer as node() := $request/customer let $customerID := $customer/customerID/text() let $addressID := $customer/addressID/text() let $customerAddress as node() := qs:slice($addressID, "addressesByID")/address let $orderCount as xs:integer := xs:integer($request/orderCount) let $orders as node()* := qs:slice($customerID, "ordersByCustomerID")/order let $relevantOrders as node()* := reverse($orders)[position() le $orderCount] let $result := {$customer/contactFirstname/text()} {$customer/contactLastname/text()} {$customer/contactPhone/text()} {$customer/contactEmail/text()} {$customerAddress/addr1/text()} {$customerAddress/addr2/text()} {$customerAddress/city/text()} {$customerAddress/state/text()} {$customerAddress/zip/text()} {$customerAddress/country/text()} { for $order as node() in $orders let $orderID := $order/id/text() let $shipping as node()? := qs:slice($orderID, "shippingsByOrderID")/RequestShipping let $processingDate := $shipping/processingDate/text() return ( {$orderID}, {$order/date/text()}, {$order/subTotal/text()}, {$order/tax/text()}, {$order/total/text()}, {$order/shippingType/text()}, {$processingDate}, {$order/status/text()}, {$order/shippingCost/text()}, {$order/discount/text()}, (:propagate shipping information:) $request/SHIPPING_STREET1, $request/SHIPPING_STREET2, $request/SHIPPING_CITY, $request/SHIPPING_STATE , $request/SHIPPING_ZIP, $request/COUNTRY_NAME, (:return all order lines with item details :) for $orderLine as node() in $order/orderLines/orderLine let $itemID := $orderLine/itemID/text() let $itemMasterdata as node() := qs:slice($itemID, "itemByID")/item return ( {$itemID}, {$itemMasterdata/title/text()}, {$itemMasterdata/publisher/text()}, {$orderLine/cost/text()}, {$orderLine/quantity/text()}, {$orderLine/status/text()} ) ) } return enqueue message local:soapify($result) into outgoingMessages ; (: ---------------------------------- New Item Service --------------------------------------:) (: The implementation of this part differs from TPC-app, which only updates the publication timestamp of an existing item. This service really adds a new item to the items queue (or overwrites an existing entry with new data. :) create queue newProduct kind basic mode persistent; create queue createProduct kind basic mode transient; create queue authors kind basic mode persistent; create slicing property authorsByLastname queue authors fixed value /author/lastname/text() require false(); create rule checkAuthor for newProduct let $request as node() := /NewProducts let $authorLastname := $request/AUTHOR_LNAME/text() let $authorFirstname := $request/AUTHOR_FNAME/text() let $author as node()? := qs:slice($authorLastname, "authorsByLastname")/author return ( if(empty($author)) then let $authorID as xs:string := qs:uniqueID() let $newAuthor := {$authorID} {$authorFirstname} {$authorLastname} return ( (:add master data for author, include authorID in NewProduct message:) enqueue message $newAuthor into authors, enqueue message {$request/*, $newAuthor/authorID} into createProduct) else (: we already have masterdata for this author, add ID:) enqueue message {$request/*, $author/authorID} into createProduct ) ; create rule createNewProduct for createProduct let $request as node() := /NewProducts let $itemID as xs:integer := xs:integer($request/ITEM_ID) let $item := {$itemID} {$request/ITEM_TITLE/text()} {current-dateTime()} {$request/ITEM_PUBLISHER/text()} {$request/ITEM_SUBJECT/text()} {$request/ITEM_DESC/text()} {$request/ITEM_SRP/text()} {$request/ITEM_COST/text()} {$request/ITEM_AVAIL/text()} {$request/I_ISBN/text()} {$request/ITEM_PAGE/text()} {$request/ITEM_BACKING/text()} {$request/ITEM_DIMENSIONS/ITEM_WIDTH/text()} {$request/ITEM_DIMENSIONS/ITEM_HEIGHT/text()} {$request/ITEM_DIMENSIONS/ITEM_LENGTH/text()} {$request/authorID} let $result := {$itemID} return ( enqueue message local:soapify($result) into outgoingMessages, enqueue message $item into items ) ; (: -------------------------------- Check Products Service ------------------------------------:) (: this service is called "new products web service" in tpc-app and has been renamed here for disambiguation with the new item web service :) create queue checkProducts kind basic mode persistent; create slicing property itemsBySubject queue items fixed value /item/subject/text() require false(); create slicing property authorsByID queue authors fixed value /author/authorID/text() require count(qs:history()) ge 1; create rule handleCheckProducts for checkProducts let $request as node() := /CheckProducts let $subject := $request/Subject/text() let $itemlimit as xs:integer := xs:integer($request/ItemLimit) let $cutoff as xs:integer := xs:integer($request/CutOff) let $formatString as xs:string := concat("PT",$cutoff,"M") (:OMG:) let $deadline as xs:dateTime := current-dateTime() - xs:dayTimeDuration($formatString) let $items as node()* := qs:slice($subject, "itemsBySubject") let $result as node() := {qs:hostName()} { for $item as node() in reverse($items)[position() le $itemlimit] where qs:timestamp($item) ge $deadline return let $authorID := $item//authorID/text() let $author as node() := qs:slice($authorID, "authorsByID")/author return ( {$item//id/text()}, {$item//title/text()}, {$author/firstname/text()}, {$author/lastname/text()} ) } return enqueue message $result into outgoingMessages ; (: ------------------------------- Product Details Service ----------------------------------:) create queue productDetail kind basic mode persistent; create rule handleProductDetail for productDetail let $request as node() := /ProductDetail let $result as node() := { for $itemID in $request/ITEM_ID/text() let $item as node() := qs:slice($itemID, "itemByID")/item let $authorID := $item/authorID/text() let $author as node() := qs:slice($authorID, "authorsByID")/author return ( {$itemID}, {$item/title/text()}, {$author/firstname/text()}, {$author/lastname/text()}, {$item/publishingDate/text()}, {$item/publisher/text()}, {$item/subject/text()}, {$item/description/text()}, {$item/cost/text()}, {$item/SRP/text()}, {$item/availability/text()}, {$item/ISBN/text()}, {$item/page/text()}, {$item/backing/text()}, {$item/dimensions/length/text()} {$item/dimensions/width/text()} {$item/dimensions/height/text()} , {$item/image/text()} ) } return enqueue message $result into outgoingMessages ; (: -------------------------------- "External" services -------------------------------------:) (: ------------------------------- "External" POV service -----------------------------------:) (: TODO: factorize! :) create queue externalCustomerPOV kind basic mode persistent; create queue externalPaymentPOV kind basic mode persistent; create rule externalCPOVRequest for externalCustomerPOV let $businessName := //Business_Name/text() let $poid as xs:integer := xs:integer(data(//PO_ID)) let $response := 1 {//Business_Name} (:Include business name for correlation:) return enqueue message local:soapify($response) into customerPovResponses; create rule externalPPOVRequest for externalPaymentPOV let $businessName := //Business_Name/text() let $poid as xs:integer := xs:integer(data(//PO_ID)) let $response := 1 {//Business_Name} (:Include business name for correlation:) return enqueue message local:soapify($response) into paymentPovResponses; (: ------------------------------- "External" PGE service -----------------------------------:) create queue externalPGE kind basic mode persistent; create rule handleExternalPGE for externalPGE let $orderID := //orderID let $response := 1 {$orderID} return enqueue message local:soapify($response) into orderPGEResponses; (: ------------------------------- "External" SNE service -----------------------------------:) create queue externalSNE kind basic mode persistent; (:TODO: add image data:) create rule handleExternalSNE for externalSNE let $response := {//ORDER_ID} 17 BASE64ENCODINBASE64ENCODINBASE64ENCODINGGGBASE64ENCODING return enqueue message local:soapify($response) into shippingSNEResponse ; (: ------------------------------- "External" ICE service -----------------------------------:) create queue externalICE kind basic mode persistent; (: must retain all messages :) create slicing property ICElog queue externalICE fixed value //SHIPPING_O_ID/text() require false(); create rule handleExternalICE for externalICE let $response := OK {//SHIPPING_O_ID} (:added for correlation to initial request:) return enqueue message $response into stockICEResponse ;