You are on page 1of 19

Chapter 6.

Trip Reservation Example Prev Next

Chapter 6. Trip Reservation Example
Trip Reservation is about a travel agency that offers its services via the web. Travelers pick their choice of flight, hotel and/or rental car. The agency receives orders from its customers and gives invoices in return. Invoices contain a trip locator. Travelers keep this locator for further reference. At any time before the trip begins, travelers are able to check the details of their trip. Should a traveler want to cancel, the travel agency assesses a cancellation fee and reimburses the remaining portion of the amount in the invoice. The figure below represents the top level activities of the trip reservation process.

Figure 6.1. Trip reservation process

6.1. Define the BPEL process
6.1.1. Create the BPEL document
This process has two partnerships. The first, traveler is the immediately noticeable link with the customers of the agency. The second is more of an implementation detail and consists of a service that generates the trip locators. In fact, this process reuses the ticket issuer introduced in the ATM example. The correlation set trip involves activities that exchange the trip locator with external entities. The trip locator helps distinguish reservations from each other.

org/ws/2003/03/businessprocess/" xmlns="http://schemas.w3.org/examples/trip" xmlns:tns="http://jbpm.. </sequence> </process> The trip purchase section of the process looks like this: ...org/examples/trip" xmlns:tic="http://jbpm.org/2001/XMLSchema" xmlns:xsi="http://www. </faultHandlers> <sequence name="Main"> .org/ws/2003/03/business-process/"> <partnerLinks> <partnerLink name="traveler" partnerLinkType="tns:TravelerAgent" myRole="Agent" /> <partnerLink name="ticket" partnerLinkType="tns:Agent-Ticket" partnerRole="TicketIssuer" /> </partnerLinks> <variables> <variable name="purchaseRequest" messageType="tns:purchaseRequest" /> <variable name="cost" type="xsd:double" /> <variable name="items" type="tns:ItemSet" /> <variable name="cancelRequest" messageType="tns:cancelRequest" /> <variable name="cancelResponse" messageType="tns:cancelResponse" /> <variable name="detailRequest" messageType="tns:detailRequest" /> <variable name="detailResponse" messageType="tns:detailResponse" /> <variable name="dateReached" type="xsd:boolean" /> </variables> <correlationSets> <correlationSet name="trip" properties="tns:tripLocator" /> </correlationSets> <faultHandlers> .org/examples/ticket" xmlns:xsd="http://www.xmlsoap.xmlsoap.w3.org/2001/XMLSchema-instance" xmlns:bpel="http://schemas..<process name="TripReservation" targetNamespace="http://jbpm.

The process examines the order and charges the flight. In parallel. an invoice is prepared and sent back to the traveler. <scope name="TripPurchase"> <variables> <variable name="purchaseResponse" messageType="tns:purchaseResponse" /> <variable name="ticketRequest" messageType="tic:ticketRequest" /> <variable name="ticketMessage" messageType="tic:ticketMessage" /> . Lastly. the process contacts the ticket service and obtains a trip locator. Trip purchase unit and behaves like this: after deciding on the desired trip options. hotel and rental car if requested. The invoice contains the trip locator for the customer's further reference.2.Figure 6. the traveler sends a purchase order which goes to the purchaseRequestvariable. The cost of the trip is maintained in variable cost and is initially set to zero.

</variables> <sequence name="PurchaseTrip"> <receive name="ReceiveTripOrder" operation="purchaseTrip" partnerLink="traveler" portType="tns:TravelAgent" variable="purchaseRequest" createInstance="yes" /> <flow> <sequence name="EvaluateCost"> <assign name="InitializeCost"> <copy> <from expression="0" /> <to variable="cost" /> </copy> <copy> <from variable="purchaseRequest" part="order" query="/tns:order/items" /> <to variable="items" /> </copy> </assign> <switch name="FlightDecision"> <case condition="bpel:getVariableData('items')/flight and string(bpel:getVariableData('items')/flight/@xsi:nil) != '1'"> <scope name="FlightReservation"> <compensationHandler> <assign name="ReimburseFlight"> <copy> <from expression="bpel:getVariableData('cost') 300 + 100" /> <to variable="cost" /> </copy> </assign> </compensationHandler> <assign name="ChargeFlight"> <copy> <from expression="bpel:getVariableData('cost') + 300" /> <to variable="cost" /> </copy> </assign> </scope> .

</case> </switch> <switch name="HotelDecision"> <case condition="bpel:getVariableData('items')/hotel and string(bpel:getVariableData('items')/hotel/@xsi:nil) != '1'"> <scope name="HotelReservation"> <compensationHandler> <assign name="ReimburseHotel"> <copy> <from expression="bpel:getVariableData('cost') 100 + 25" /> <to variable="cost" /> </copy> </assign> </compensationHandler> <assign name="ChargeHotel"> <copy> <from expression="bpel:getVariableData('cost') + 100" /> <to variable="cost" /> </copy> </assign> </scope> </case> </switch> <switch name="CarDecision"> <case condition="bpel:getVariableData('items')/rentalCar and string(bpel:getVariableData('items')/rentalCar/@xsi:nil) != '1'"> <scope name="CarReservation"> <compensationHandler> <assign name="ReimburseCar"> <copy> <from expression="bpel:getVariableData('cost') 50 + 5" /> <to variable="cost" /> </copy> </assign> </compensationHandler> .

<assign name="ChargeCar"> <copy> <from expression="bpel:getVariableData('cost') + 50" /> <to variable="cost" /> </copy> </assign> </scope> </case> </switch> </sequence> <invoke name="CreateTicket" operation="createTicket" partnerLink="ticket" portType="tic:TicketIssuer" inputVariable="ticketRequest" outputVariable="ticketMessage"> <correlations> <correlation set="trip" initiate="yes" pattern="in" /> </correlations> </invoke> </flow> <assign name="PrepareInvoice"> <copy> <from variable="ticketMessage" part="ticketNo" /> <to variable="purchaseResponse" part="invoice" query="/tns:invoice/@locator" /> </copy> <copy> <from variable="cost" /> <to variable="purchaseResponse" part="invoice" query="/tns:invoice/@cost" /> </copy> </assign> <reply name="SendInvoice" operation="purchaseTrip" partnerLink="traveler" portType="tns:TravelAgent" variable="purchaseResponse"> <correlations> <correlation set="trip" /> </correlations> </reply> </sequence> </scope> .

Arriving at the trip date just breaks the loop. <assign name="SetDateNotReached"> <copy> <from expression="false()" /> <to variable="dateReached" /> </copy> </assign> <while name="PredateLoop" condition="bpel:getVariableData('dateReached') = 'false'"> <pick name="PredateMenu"> <onMessage operation="getTripDetail" partnerLink="traveler" portType="tns:TravelAgent" variable="detailRequest"> . Figure 6.After the invoice is sent. the process expects one of the following three external events to occur: 1.3. the traveler cancels the trip 3. the event listening structure appears inside a loop. the trip date arrives These events appear as branches in the next model. the traveler reviews the trip details 2. For this reason. Loop before trip date Travelers can review the trip details as many times as they want before the trip date.

<correlations> <correlation set="trip" /> </correlations> <sequence name="DetailTrip"> <assign name="PrepareTripDetail"> <copy> <from variable="purchaseRequest" part="order" query="/tns:order/items" /> <to variable="detailResponse" part="detail" query="/tns:detail/items" /> </copy> <copy> <from variable="cost" /> <to variable="detailResponse" part="detail" query="/tns:detail/@cost" /> </copy> </assign> <reply name="SendTripDetail" operation="getTripDetail" partnerLink="traveler" portType="tns:TravelAgent" variable="detailResponse" /> </sequence> </onMessage> <onMessage operation="cancelTrip" partnerLink="traveler" portType="tns:TravelAgent" variable="cancelRequest"> <correlations> <correlation set="trip" /> </correlations> <throw name="CancelTrip" faultName="tns:cancelation" /> </onMessage> <onAlarm until="bpel:getVariableData('purchaseRequest'. 'order'. '/tns:order/date')"> <assign name="SetDateReached"> <copy> <from expression="true()" /> <to variable="dateReached" /> </copy> </assign> </onAlarm> </pick> .

the document characterizes the relationship between (a) the traveler and the agency (Traveler-Agent ) and (b) the agency and the trip locator issuer (Agent-Ticket ).org/examples/trip" xmlns:tns="http://jbpm.xmlsoap.</while> This example takes a distinct approach to handle the cancelation scenario. messages and operations. <definitions name="trip" targetNamespace="http://jbpm.wsdl describes the travel agency service. The WSDL definition also introduces the trip locator as a property and maps it to fields within the invoice. <catch faultName="tns:cancelation"> <sequence name="Cancel"> <compensate name="UndoPurchase" scope="tripPurchase" /> <assign name="PreparePenalty"> <copy> <from variable="cost" /> <to variable="cancelResponse" part="penalty" query="/tns:penalty/@fee" /> </copy> </assign> <reply name="SendPenalty" operation="cancelTrip" partnerLink="traveler" portType="tns:TravelAgent" variable="cancelResponse" /> </sequence> </catch> 6. Compensation handlers (installed only for inner scopes that got activated) reimburse charges and assess applicable fees. cancelation and query messages.w3.org/wsdl/"> .org/2001/XMLSchema" xmlns="http://schemas. In the end.org/ws/2003/05/partner-link/" xmlns:bpel="http://schemas.xmlsoap. Apart from schema types. A global fault handler compensates the trip purchase scope. Create/obtain the WSDL interface documents The WSDL file trip. a bill is sent back to report the forfeited amount.org/examples/trip" xmlns:tic="http://jbpm. Instead of branching to determine what options were ordered. this process throws a fault.1.xmlsoap.org/examples/ticket" xmlns:plt="http://schemas.org/ws/2003/03/businessprocess/" xmlns:xsd="http://www.2.

w3.org/examples/ticket" location="ticket.org/2001/XMLSchema" targetNamespace="http://jbpm.wsdl" /> <types> <xsd:schema xmlns:xsd="http://www.org/examples/trip"> <xsd:complexType name="Flight"> <xsd:attribute use="required" name="airline" type="xsd:string" /> <xsd:attribute use="required" name="number" type="xsd:int" /> </xsd:complexType> <xsd:complexType name="Hotel"> <xsd:attribute use="required" name="name" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="RentalCar"> <xsd:attribute use="required" name="company" type="xsd:string" /> </xsd:complexType> <xsd:complexType name="ItemSet"> <xsd:sequence> <xsd:element name="flight" minOccurs="0" type="tns:Flight" /> <xsd:element name="hotel" minOccurs="0" type="tns:Hotel" /> <xsd:element name="rentalCar" minOccurs="0" type="tns:RentalCar" /> </xsd:sequence> </xsd:complexType> <xsd:element name="order"> <xsd:complexType> <xsd:sequence> <xsd:element name="items" type="tns:ItemSet" /> <xsd:element name="date" type="xsd:dateTime" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="invoice"> <xsd:complexType> <xsd:sequence /> <xsd:attribute name="locator" type="xsd:int" use="required" /> <xsd:attribute name="cost" type="xsd:double" use="required" /> .<import namespace="http://jbpm.

</xsd:complexType> </xsd:element> <xsd:element name="cancelation"> <xsd:complexType> <xsd:sequence /> <xsd:attribute name="locator" type="xsd:int" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="penalty"> <xsd:complexType> <xsd:sequence /> <xsd:attribute name="fee" type="xsd:double" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="query"> <xsd:complexType> <xsd:sequence /> <xsd:attribute name="locator" type="xsd:int" use="required" /> </xsd:complexType> </xsd:element> <xsd:element name="detail"> <xsd:complexType> <xsd:sequence> <xsd:element name="items" type="tns:ItemSet" /> </xsd:sequence> <xsd:attribute name="cost" type="xsd:double" use="required" /> </xsd:complexType> </xsd:element> </xsd:schema> </types> <message name="purchaseRequest"> <part name="order" element="tns:order" /> </message> <message name="purchaseResponse"> <part name="invoice" element="tns:invoice" /> </message> <message name="cancelResponse"> <part name="penalty" element="tns:penalty" /> </message> .

<message name="detailResponse"> <part name="detail" element="tns:detail" /> </message> <message name="detailRequest"> <part name="query" element="tns:query" /> </message> <message name="cancelRequest"> <part name="cancelation" element="tns:cancelation" /> </message> <portType name="TravelAgent"> <operation name="purchaseTrip"> <input message="tns:purchaseRequest" /> <output message="tns:purchaseResponse" /> </operation> <operation name="cancelTrip"> <input message="tns:cancelRequest" /> <output message="tns:cancelResponse" /> </operation> <operation name="getTripDetail"> <input message="tns:detailRequest" /> <output message="tns:detailResponse" /> </operation> </portType> <plt:partnerLinkType name="Traveler-Agent"> <plt:role name="Agent"> <plt:portType name="tns:TravelAgent" /> </plt:role> </plt:partnerLinkType> <plt:partnerLinkType name="Agent-Ticket"> <plt:role name="TicketIssuer"> <plt:portType name="tic:TicketIssuer" /> </plt:role> </plt:partnerLinkType> <bpel:property name="tripLocator" type="xsd:int" /> <bpel:propertyAlias propertyName="tns:tripLocator" messageType="tns:purchaseResponse" part="invoice" query="/tns:invoice/@locator" /> <bpel:propertyAlias propertyName="tns:tripLocator" messageType="tns:cancelRequest" part="cancelation" query="/tns:cancelation/@locator" /> <bpel:propertyAlias propertyName="tns:tripLocator" messageType="tns:detailRequest" part="query" query="/tns:query/@locator" /> .

This characterizes the document style. This means support for the document style and enhancements for the RPC style.1.250 INFO [IntegrationConfigurator] message reception enabled for process: « TripReservation 21:10:28. Deploy the process definition to the jBPM database calling: ant deploy.250 INFO [DeploymentServlet] deployed web module: trip. Please jump to the Create/obtain the WSDL interface documents section of the ATM example if you want to examine this document.250 INFO [WebModuleBuilder] packaged web module: trip. ticket. It imports ticket. « endpoint=AgentServlet 21:10:27.. There is another document.war 21:10:25.process The deploy-process target builds the process archive trip. 6.war 21:10:27. that describes the ticket issuer interface. jBPM BPEL parses incoming messages and formats outgoing messages using full WSDL binding information. Unlike other examples./tripservice. the TravelAgent port type refers only to wsdl:parts that have been defined using the element attribute.437 INFO [TomcatDeployer] deploy.wsdl contains most WSDL definitions.wsdl Back in the description of the process. ctxPath=/trip.<bpel:propertyAlias propertyName="tns:tripLocator" messageType="tic:ticketMessage" part="ticketNo" /> </definitions> Starting from version 1.203 INFO [DeploymentServlet] deployed process definition: TripReservation 21:10:25.wsdl to access the definitions specific to the ticket issuer.546 INFO [WSDLFilePublisher] WSDL published to: .zip and submits it to the deployment servlet. we mentioned a partner service that generates unique trip locators..3.war/ 21:10:28..1. Make sure you deploy that service before any . Deploy the process definition The master WSDL document trip.GA./tmp41529trip-exp.wsdl..ws:context=trip. In a typical deployment the server prints these messages: 21:10:23. warUrl=.359 INFO [DefaultEndpointRegistry] register: jboss.

. ant deploy.4.437 INFO [TomcatDeployer] deploy.wsdl The JBossWS service endpoints page should be showing a picture similar to the next one.ws:context=ticket. Figure 6.. « endpoint=ticketIssuerServlet 18:12:44. the host address and port shown may vary. warUrl=. depending on your server bind address.reservation is placed.421 INFO [DefaultEndpointRegistry] register: jboss./ticket-impl. Change to the ticketdirectory and trigger the following target. 18:12:44.843 INFO [WSDLFilePublisher] WSDL published to: ..webservice The next few lines confirm a successful deployment.. Endpoints page . or the process will malfunction and your virtual travelers will be very angry. ctxPath=/ticket. Of course. 18:12:44.

partners The web console allows you to browse the catalog. Figure 6. The target below is provided for this purpose.5. ant register. You can register new partner services manually as well. Its WSDL description must be registered in the service catalog. BPEL endpoints are no different. For all intents and purposes. Service catalog .Note It is worth pointing BPEL endpoints are intermixed with Java and EJB endpoints in the list. Deploying a partner service is not enough for the jBPM BPEL to know of its existence.

xml . Test the process .jbpm.1.2.4" xmlns="http://java.bpel.3. Application client deployment descriptor Reference your full WSDL description and Java mapping artifacts from the application-client.2.published WSDL document --> <wsdl-file>META-INF/wsdl/trip-service.JNDI name of service interface in client environment context --> <service-ref-name>service/Trip</service-ref-name> <!-.com/xml/ns/j2ee"> <display-name>Trip Reservation Client</display-name> <service-ref> <!-.service interface --> <service-interface> org. Just make sure you give different service-ref-names to each client in the respective application-client.wsdl</wsdl-file> <!-. <jboss-client> <jndi-name>jbpmbpel-client</jndi-name> </jboss-client> Tip The jndi-name above is shared among all examples. 6.6.sun.trip.xml</jaxrpc-mappingfile> </service-ref> </application-client> 6. <application-client version="1.tutorial.xml file. You can share a single JNDI name among multiple application clients to keep them organized and reduce the number of top-level entries in the global JNDI context of the server.TripReservationService </service-interface> <!-. Environment context Allocate a JNDI name for the client environment context in jboss-client. Build the WSEE application client 6.2.2.xml descriptor.Java<->XML mapping file --> <jaxrpc-mapping-file>META-INF/trip-mapping.

1.setName("Maria Isabel"). // cost: 300 items.setAirline("AM"). private TravelAgent agent. Calendar tripDate = Calendar.lookup( "java:comp/env/service/Trip").setCompany("Alamo"). Remote web service access The test setup code looks up the service instance tripService. protected void setUp() throws Exception { /* * "service/Trip" is the JNDI name of the service interface instance relative to * the client environment context.To ensure the process works as expected. Submit the reservation.getInstance(). order. 6. 8. 6. This name matches the <serviceref-name> in * application-client. RentalCar car = new RentalCar(). tripDate. 5. 9. This object is a factory from which clients get service endpoint proxies. hotel and car entities are also initialized. flight. 7. } There are three test scenarios. private private private private Flight flight = new Flight().setDate(tripDate).setFlight(flight).xml */ InitialContext iniCtx = new InitialContext(). Ensure the cost in the invoice matches the price of the selected items. 2. the test case TravelAgentTest is provided. hotel. agent = tripService. order.setNumber(637). TripReservationService tripService = (TripReservationService) iniCtx. 4. Hotel hotel = new Hotel(). 3.add(Calendar.setItems(items). 1. flight. 10). .getAgentPort(). items. Sample flight.SECOND. public void testPurchaseTrip() throws RemoteException { ItemSet items = new ItemSet(). // cost: 100 Order order = new Order().3. car.setHotel(hotel). testPurchaseTrip: create a trip reservation that includes a flight and a hotel.

33. 36.setFlight(flight).getCost(). // fee: 100 42. 12. Detail detail = agent.setFlight(flight). invoice. assertEquals(flight. including all offered items. 0). 29.getHotel(). items. // fee: 5 43. 31. Use the trip locator in the returned invoice to query about the details. items.getFlight(). 32. detail. items. Order order = new Order().getName()). 26.setHotel(hotel). 13.setRentalCar(car).getNumber(). Query query = new Query().setRentalCar(car). testGetTripDetails: reserve a trip. 0). order. 46. 22. items = detail.getRentalCar(). Verify the details match the original order.getLocator()). assertEquals(hotel. public void testGetTripDetails() throws RemoteException { 16. 23.setLocator(invoice. 47. 45. assertEquals(300 + 100. 44. 39. testCancelTrip: make a reservation for a flight and a rental car. items. order. 15. 11. 30. 35. 21.setItems(items). Order order = new Order(). items. 34. ItemSet items = new ItemSet(). 41.getName().getCompany()).purchaseTrip(order). assertEquals(flight. 27. items. query.getItems().getCompany(). 24. 14. assertEquals(car.getFlight(). 25.getTripDetail(query). .getCost().setDate(tripDate).getAirline(). assertEquals(invoice. } 38. 19. 28.getCost(). items. Cancel the trip using the locator in the invoice. public void testCancelTrip() throws Exception { 40. items. items. order. order. 17. 20.purchaseTrip(order). Invoice invoice = agent. 18. } Invoice invoice = agent.getAirline()). Check the penalty corresponds to the ordered items. 37.setItems(items). ItemSet items = new ItemSet().10.getNumber()).setDate(tripDate).

3. } Invoice invoice = agent. Failures: 0. assertEquals(100 + 5.setLocator(invoice.xml descriptors. 53. call: ant test The results you should expect follow.getLocator()). Refer to the Client JNDI properties section in the first example for a listing of the properties. Errors: 0.3. Test execution To execute the JUnit test. In particular. test: [junit] Running org.cancelTrip(reference). 50. Time elapsed: 4. penalty.tutorial. 6. 49.jbpm.trip. 0).purchaseTrip(order).875 sec .bpel. Note The process instances created to serve the above requests will end automatically.getFee().clientName property does not change because all examples share the <jndi-name> in their respective jboss-client.3. Penalty penalty = agent. reference. Client JNDI properties The JNDI properties are exactly the same for all examples. the j2ee.48.TravelAgentTest [junit] Tests run: 3.2. Cancelation reference = new Cancelation(). 6. 54. 51. on or shortly after the stated trip date and time. 52. 55.