Chapter 12. A complete SCA application

This chapter covers

  • Running the TuscanySCATours application in a single Tuscany node
  • Building the application from separate SCA composites
  • Running the application in a distributed SCA domain
  • Hints and tips for building composite applications

We’ve used the TuscanySCATours travel-booking application throughout the book to demonstrate the various features of the Tuscany SCA Java runtime. In this chapter we’ll pull all the various parts together and run the complete application. The TuscanySCATours travel-booking application may not be much like your application, or even a realistic application as it stands, but showing all of the parts running together allows us to stand back from the details and see an SCA application doing something more than printing test output on the console.

On occasion we’ve presented different versions of the same component. For example, chapters 5 and 6 use different versions of the Payment component to demonstrate Java language, BPEL, Spring, and script implementation types. Here we’ll select one version of each component to build the full application. We’ll look at how the components in the application are organized and review some of the interesting features that we’ve included as the book has developed.

This chapter is also an opportunity for us to share some hints and tips about putting SCA applications together. These mostly focus on the nonfunctional issues of building SCA applications, such as how best to organize and test contributions.

To start off we’ll deploy and run the complete application in a single Tuscany node using the simple fullapp launcher. We’ll then look at how we brought the application together and describe each of the composites in the application. This will take up most of the space in this chapter because we’ll talk about the eight different composites that the application uses. After this we’ll run the same application in a distributed domain using multiple nodes. This uses the fullapp-domain and fullapp-nodes launchers. We’ll finish off by discussing some of the lessons we learned when building the application. Let’s get set up and get the application running.

12.1. Getting ready to run the application

The TuscanySCATours travel-booking application is a sample application that allows you to search for and book trips. The application features access by the user via a single web page that provides search input, search results, a shopping cart, and a checkout button. It’s not a real application in that many important online retail features are missing—a full security solution, for example. The application does, however, provide an example of how SCA can be used to partition and assemble a reasonably complex application.

The sample code comes with a single JVM launcher for the full TuscanySCATours application. In this chapter we’ll run the sample on Windows (although it will run on other platforms that the Java language supports). You can find it in the following file: /launchers/fullapp/src/main/java/scatours/FullAppLauncher.java. This file has everything in it to load all of the contributions that make up the full application. The following listing shows the launcher code..

Listing 12.1. Loading all of the contributions that form the full travel application
public class FullAppLauncher {

public static void main(String[] args) throws Exception {
SCANode node =
SCANodeFactory.newInstance().createSCANode(null,
locate("common"),
locate("currency"),
locate("hotel"),
locate("flight"),
locate("car"),
locate("trip"),
locate("tripbooking"),
locate("travelcatalog"),
locate("payment-spring-policy"),
locate("creditcard-payment-jaxb-policy"),
locate("shoppingcart"),
locate("scatours"),
locate("fullapp-ui"),
locate("fullapp-coordination"),
locate("fullapp-currency"),
locate("fullapp-packagedtrip"),
locate("fullapp-bespoketrip"),
locate("fullapp-shoppingcart"));
node.start();

System.out.println(
"Point your browser at - http://localhost:8080/scatours/ ");
System.out.println("Node started - Press enter to shutdown.");
try {
System.in.read();
} catch (IOException e) {
}
node.stop();
}
}

In listing 12.1 you can see that the locate() utility operation is used to find each of the required contributions instead of providing the full path to the contribution. The locate() utility isn’t part of Tuscany. It was written for this book sample to make the launcher code more compact.

The contributions shown here are a subset of the contributions you’ll see in the sample’s contributions directory. As we said in the introduction to this chapter, there are contributions that demonstrate particular SCA features that are used by other chapters but aren’t used in the full application. Also note that not all of the contributions loaded by this launcher contain a composite file. Some of the contributions just allow us to share artifacts such as Java class files or WSDL files between various parts of the application. For example, the common contribution contains the basic Java interfaces shared among other contributions.

Running the launcher shown in figure 12.1 is straightforward once your environment is set up to run Tuscany. Appendix A provides the necessary details. The README file provided with the travel-booking sample provides instructions on how to run each launcher. For example, if you have the source code distribution of the travel-booking sample, you can run the fullapp launcher using the Ant build.xml file provided in the /launchers/fullapp directory. To run the build.xml file enter the following:

cd launchers/fullapp
ant run
Figure 12.1. The front page of the TuscanySCATours travel-booking application

Once the travel-booking application is running, it prints messages to the console that indicate contributions are being loaded. The launcher prints the following message when initialization is complete:

Point your browser at - http://localhost:8080/scatours/
Node started - Press enter to shutdown.

Try the application now by pointing a web browser at http://localhost:8080/scatours/. The page you’ll see is shown in figure 12.1.

You can search for trip packages and trip components using the Search button. The trips showing in the search results list can be selected and added to the shopping cart. Finally, you can choose to buy the trips and check out. This will clear the shopping cart.

When you’ve finished, press a key in the window where the launcher was started, and the application will stop running. Let’s review all of the contributions you saw being loaded by the launcher.

12.2. Assembling the travel-booking application

In various chapters of the book we’ve shown different versions of the same components. Here we’ll choose one version of each component in the application and wire them all together. Figure 12.2 shows the first part of the application and includes the name of the technology used to implement each component.

Figure 12.2. The first part of the TuscanySCATours travel-booking application up to and including the ShoppingCart component

In figure 12.2 the numbers in brackets indicate which HTTP port each composite occupies when using HTTP-based bindings such as the Web Services or JSON-RPC binding. Note also that the names of the composites in the diagram match the names of the contributions that can be found in the sample contributions directory.

Figure 12.2 shows how the travel-booking application is composed of smaller, functional components using SCA’s flexible configuration and wiring.

SCA components are collected into composites such as fullapp-shoppingcart and fullapp-ui. Each composite is a unit of deployment and can run on a separate Tuscany node. We’ve split the application diagram into two for readability. The second part of the application includes the payment components from the payment-spring-policy and creditcard-payment-jaxb-policy sample contributions, as shown in figure 12.3.

Figure 12.3. The payment part of the TuscanySCATours travel-booking application

The payment reference of the ShoppingCart component in figure 12.2 is connected, over Web Services, to the Payment service of the Payment component in figure 12.3.

Remember that these diagrams, and the diagrams in the rest of this chapter, show only the composites involved in the application. Running the sample requires the contributions that contain these composites, along with a number of other contributions. The full list of sample contributions required is shown in listing 12.1.

In the following subsections we’ll provide an overview of each composite in turn. The name in parentheses in each title is the name of the contribution being discussed. We’ll start by looking at how the web page is built.

12.2.1. The application user interface (fullapp-ui)

The Web 2.0–based TuscanySCATours user interface is displayed using the SCAToursUserInterface SCA component. This in turn is implemented using implementation. widget. This handles the various parts of the application user interface, which was shown in figure 12.1.

The user interface won’t win any prizes for interface design, but it provides enough features for us to demonstrate components behind the interface doing something interesting. If you look closer at the fullapp-ui contribution, as shown in figure 12.4, you’ll see two components defined.

Figure 12.4. The fullapp-ui contribution provides the user interface for the TuscanySCATours application.

Figure 12.4 shows that the SCAToursUserInterface component is wired to the three services provided by the SCATours component. The contents of the fullapp-ui.composite file are shown here.

Listing 12.2. The fullapp-ui.composite file from the fullapp-ui contribution

The SCAToursUserInterface component is implemented using an implementation.widget type . As you learned in chapter 8, the SCATours component runs as JavaScript in the user’s browser. JavaScript supports a limited number of reference bindings, one of which is binding.jsonrpc , which we’re using to communicate with the various services of the SCATours component.

The SCATours component allows us to control how the user interface communicates with the rest of the backend components. SCATours is a normal SCA component running on the server and is implemented using implementation.java . It provides services to the browser over the binding.jsonrpc. Because it’s running inside the Tuscany runtime, we’re free to configure its references with any of the bindings that Tuscany supports. In this case it uses the default binding to communicate with the TravelCatalog, TripBooking, and ShoppingCart components.

Let’s look at the services that the SCATours component is connected to.

12.2.2. Coordinating the application (fullapp-coordination)

TuscanySCATours uses a series of partner organizations to provide hotels, flights, cars, and even prepackaged trips. The user will initially search for available trips. The user may then select some of the trips that have been identified and proceed to book them. Both of these actions require interaction with TuscanySCATours partner organizations.

To coordinate this activity, the TuscanySCATours application has a fullapp-coordination contribution that acts as a typical middle tier, providing coordination between the frontend user interface and the backend services that provide search and booking functions. It does this using two components, TravelCatalog and TripBooking, as shown in figure 12.5.

Figure 12.5. The TravelCatalog and TripBooking components coordinate communication with the services provided by the TuscanySCATours company’s business partners.

The TravelCatalog component manages the process of searching for trip items, whereas the TripBooking component handles bookings on a user’s behalf. The Travel-Catalog component provides a TravelCatalogSearch interface with a search operation, which takes a TripLeg argument, as follows:

@Remotable
public interface TravelCatalogSearch {
TripItem[] search(TripLeg tripLeg);
}

The TripLeg argument holds the information gathered from the user interface regarding the details of the leg of the trip the user is searching for, for example, the start and end dates and details of where the user wants to travel to and from. The TripItem array that’s returned has one entry for each item found during the search. An item can be a trip, hotel, flight, or car.

This component determines what TuscanySCATours has to offer when a search is performed by the user. During the processing of the search, the TravelCatalog uses its tripSearch, hotelSearch, flightSearch, and carSearch references to search for trips, hotels, flights, and cars. The component services that are wired to these references represent gateways to the TuscanySCATours company’s business partner services that provide the actual trips, hotels, flights, and cars.

To ensure that the search happens as quickly as possible, the TravelCatalog component calls out to all the referenced partner components in parallel using the one-way interaction pattern. You’ll notice a delay of a few seconds when you perform a search because we added artificial delays in the partner services.

Once the results are returned using the callback interaction pattern, the quoted prices are converted into the currency specified via the quoteCurrencyCode component using the currencyConverter service. The results are then returned to the user interface.

The TripBooking component has a similarly simple interface, as follows:

@Remotable
public interface TripBooking {
TripItem bookTrip(String cartId, TripItem trip);
}

When the user selects and then books an item, the bookTrip operation is used to provisionally book the item with the appropriate partner and then add the item to the shopping cart. The cartId parameter here represents the user’s session. The user interface creates a new cart when the user connects and uses the cartId to identify the user as required. This isn’t a sophisticated user-tracking system but is sufficient for this sample.

Next we’ll look at the SCA services that represent the TuscanySCATours company’s business partners. The TravelCatalog and TripBooking components use these services to search for and book trip items, respectively.

12.2.3. Partner services (fullapp-packagedtrip and bespoketrip)

The TripPartner, HotelPartner, FlightPartner, and CarPartner components are all owned and run by the different departments of the TuscanySCATours company. Each department is responsible for managing the relationship with a different type of supplier. The services are organized to reflect this responsibility, as shown in figure 12.6.

Figure 12.6. The components in the TuscanySCATours application are aligned with the kinds of departments that could be part of a travel-booking company.

In figure 12.6 we’ve identified departments that are responsible for components in the application by adding appropriately labeled boxes. These boxes don’t represent any particular SCA syntax and are just for the purpose of this explanation. Using SCA, each department can build and package its part of the application in isolation. Only interface descriptions need be shared.

For example, the Offerings Department owns the TravelCatalog component and packages it alongside the TripBooking component in the fullapp-coordination contribution. The Packaged Trip Department owns the TripPartner component and packages it in the fullapp-packagedtrip contribution. Only the Search and Book interface descriptions are shared between the two departments.

The individual partner components are free to use whatever means are appropriate to communicate with the partner companies. This communication is shown by the jagged arrows in figure 12.6. It could involve telephone, fax, email, or even direct service calls using SCA references with bindings such as the Web Services binding.

In our sample application, for simplicity, no references are provided, and the partner components have travel information hardcoded internally.

Let’s look a little more closely at the Search and Book interfaces used by the partner services and review the way that the SCA one-way and callback interaction patterns have been used.

The Search Interface

The search-related references of the TravelCatalog component use the same Search interface as shown in the next code snippet. The different departments have agreed to use the same interface, and this interface has been constructed so that all of these departments’ search services can operate in parallel.

@Remotable
@Callback(SearchCallback.class)
public interface Search {
TripItem[] searchSynch(TripLeg tripLeg);
@OneWay
void searchAsynch(TripLeg tripLeg);
int getPercentComplete();
}

The Search interface provides operations for searching synchronously, searchSynch, and for searching asynchronously, searchAsynch. To support asynchronous searching, the Search service interface is defined with a callback interface called Search-Callback. The TripCatalog component uses the searchAsynch operation on the TripPartner, HotelPartner, FlightPartner, and CarPartner components to initiate four searches in parallel.

When the sample application is run, all searches performed will take several seconds to complete. This is because each of the partner services contains an artificial delay to simulate a real search being performed across trip-, hotel-, flight-, or car-booking information. For example, look at the implementation of the Flight component’s searchAsynch operation:

public void searchAsynch(TripLeg tripLeg) {

while ( percentComplete < 100 ){
try {
Thread.sleep(50);
} catch(Exception ex){}
percentComplete = percentComplete + 10;
searchCallback.setPercentComplete(componentName, percentComplete);
}
searchCallback.searchResults(searchSynch(tripLeg));
}

Note the call to Thread.sleep, which introduces a delay into the processing to simulate search processing. This operation makes calls to a callback service. The TravelCatalog component implements the SearchCallback interface. The callback interface is shown next:

@Remotable
public interface SearchCallback {
void searchResults(TripItem[] items);
void setPercentComplete(String searchComponent, int percentComplete);
}

The searchResults operation is called by each partner component when it has finished searching. This is how the results are returned to the TravelCatalog component. The configuration of the callback pattern is evident if you look at the composite descriptions of the TravelCatalog component and one of the partner components, such as HotelPartner. First, let’s look at the TravelCatalog component, which is defined in the fullapp-coordination.composite file:

<component name="TravelCatalog">
<implementation.java class=
"com.tuscanyscatours.travelcatalog.impl.TravelCatalogImpl"/>
<service name="TravelCatalogSearch"/>
<reference name="hotelSearch">
<binding.ws uri="http://localhost:8086/Hotel/Search"/>
<callback>
<binding.ws name="callback"
uri="http://localhost:8084/Hotel/SearchCallback"/>
</callback>
</reference>
</component>

The hotelSearch reference defines a forward binding.ws and a callback binding.ws. This reference will send messages out to http://localhost:8086/Hotel/Search and listen for messages arriving at http://localhost:8084/Hotel/SearchCallback.

If you now look at the definition of the HotelPartner component, which is defined in the fullapp-bespoketrip.composite file, you can see how the service is configured to support these callbacks:

<component name="HotelPartner">
<implementation.java class=
"com.tuscanyscatours.hotel.impl.HotelImpl"/>
<service name="Search">
<binding.ws name="searchws"
uri="http://localhost:8086/Hotel/Search"/>
<callback>
<binding.ws/>
</callback>
</service>
<service name="Book"/>
</component>

Using the same style of configuration as with the previous reference, the Search service is configured to listen to messages arriving at http://localhost:8086/Hotel/ Search. In this case the Web Services binding assigned to the callback has no URI configuration. This means callbacks will be directed to the callback service of the reference that sent the forward call.

The results from the partner services will arrive back at the TravelCatalog component at various times as each search process completes. Once the TravelCatalog component has received results from all of the partner components it has called, it collates all of the results, converts the quoted prices into the correct currency using the CurrencyConverter component, and returns the results to the user interface via the SCATours component.

This asynchronous interaction pattern using callbacks is described in detail in chapter 4.

The Book Interface

The Book interface is similarly straightforward and provides a single operation for booking trips, as follows:

@Remotable
public interface Book {
String book(TripItem tripItem);
}

The partner components implement this interface, and the TripBooking component uses this interface to notify the partners when the user has booked a trip. In the current application the partner components don’t do anything interesting with bookings, but this is where the TuscanySCATours company would ensure that the trips that the user wants will be reserved for them.

As we mentioned when we talked about the Search interface, the TravelCatalog component uses the CurrencyConverter component to ensure that quoted prices appear in the correct currency. We’ll look at that component next.

12.2.4. Currency conversion (fullapp-currency)

The CurrencyConverter component is a simple component that provides a single service with the following interface:

@Remotable
public interface CurrencyConverter {
double getExchangeRate(String fromCurrencyCode,
String toCurrencyCode);
double convert(String fromCurrencyCode,
String toCurrencyCode,
double amount);
}

The TravelCatalog component calls the currency converter, via a reference called currencyConverter, in the following way:

tripItem.setPrice(currencyConverter.convert(
tripItem.getCurrency(), quoteCurrencyCode, tripItem.getPrice()));

The currency that the trip item is originally quoted in is converted into the currency that the TravelCatalog is configured to quote prices in. This configuration takes place via an SCA property defined in the following way:

@Property
public String quoteCurrencyCode = "USD";

The default is USD, but the value is configured in the composite description of the TravelCatalog component as follows:

<component name="TravelCatalog">
<property name="quoteCurrencyCode">GBP</property>
</component>

The implementation, service, and reference elements have been left out in this case for clarity.

The next part of the application deals with how booked trip items are accumulated so that they may eventually be purchased. Let’s look at the shopping cart.

12.2.5. Constructing trips (fullapp-shoppingcart)

The travel-booking application stores the items that the user selects to buy. This state is managed by the ShoppingCart component and the CartStore component to which it’s wired. The fullapp-shoppingcart contribution contains the composite file and references the shoppingcart contribution, which contains the interfaces and implementations.

When the user uses the travel-booking application, a cart ID is created that identifies their shopping cart until checkout. At checkout, the shopping cart store is emptied and removed, and a new ID is generated. The two components are shown wired together in figure 12.7.

Figure 12.7. The Shopping Cart composite containing the ShoppingCart component and the CartStore component that stores items added to the shopping cart

In figure 12.7 you can see that the ShoppingCart component is wired to the CartStore component via the cartStore reference. Each instance of the CartStore component represents a shopping cart. In the current application the contents of the cart store are held in memory, but in a more robust implementation the contents of a cart store are likely to be saved to disc on each update.

As described back in chapter 4, the CartStore component implements a conversational interface, as shown in the following listing.

Listing 12.3. The conversational CartStore interface
@Remotable
@Conversational
public interface CartStore{
void addTrip(TripItem trip);
void removeTrip(TripItem trip);
TripItem[] getTrips();
@EndsConversation
void reset();
}

A new instance of the CartStore component is generated when the addTrip operation is called for a new conversation ID. Tuscany generates conversation IDs automatically. This works well if you don’t need to refer to the ID in your application code. We choose to generate conversation IDs manually, inside the newCart operation of the ShoppingCart component, so that we can use the ID elsewhere in the code, as follows.

Listing 12.4. The mechanics of generating a cart ID in the ShoppingCart component

In listing 12.4 you can see that the conversation ID is generated and returned as a result of the newCart call. From there the ID is passed back to the JavaScript running in the browser. In turn the JavaScript can pass it back in with any service calls that update the shopping cart. This is how the shopping cart for one user is distinguished from the shopping cart for another.

In SCA, it’s not possible to retrieve an automatic conversation ID before a call has been made to a conversational service. When a call arrives at the newCart operation, no calls have yet been made to the cart store, and so we’ll generate the conversation ID ourselves.

We hold a list of references to CartStore component instances in the cartStores map . This allows the right CartStore component reference to be found in subsequent calls given the incoming cartId.

The mechanism by which a CartStore component instance is subsequently found deserves a closer look. Remember that each instance of the CartStore component represents the state of a separate shopping cart. When a new cart is requested, the new-Cart operation creates the conversation ID (called cartId), gets a new service reference from the component context, and sets the conversation ID on the reference. The reference is stored away in the cartStores map for later use. When a request comes in later to add a trip item to the shopping cart, the cartStores reference map is used in the following way:

public void addTrip(String cartId, TripItem trip) {
cartStores.get(cartId).addTrip(trip);
}

Looking back at listing 12.3, you can see that the reset operation of the CartStore interface is marked with the @EndsConversation annotation, which is used when the shopping cart has finished with this cart store and the state that it holds can be removed. This operation is called when the user clicks the checkout button. This calls the checkout operation on the ShoppingCart, which, after processing the payment, removes the cart store, as shown here.

Listing 12.5. The ShoppingCart component checkout operation
public void checkout(String cartId, String customerName){
String customerId = customerName;
float amount = (float)0.0;
TripItem[] trips = getTrips(cartId);
for (TripItem trip : trips){
if (trip.getType().equals(TripItem.TRIP)){
amount += trip.getPrice();
} else {
for (TripItem tripItem : trip.getTripItems()){
amount += tripItem.getPrice();
}
}
}
payment.makePaymentMember(customerId, amount)
cartStores.get(cartId).reset();
cartStores.remove(cartId);
}

The main body of code in the checkout operation gets the trips from the cart store using the cartId as a key. It totals up the price for all the items in the cart store for this cartId and then calls the Payment component to make a payment on behalf of the user. Finally, the cart store is reset, which ends the conversation that’s ongoing between the ShoppingCart and CartStore components for this cartId. After this, the cart store reference is removed from the cartStores map.

If the user continues shopping, then a new cart is created, which results in a new conversation ID being generated, and the process starts again. Looking back at listing 12.5, you can see that during checkout processing the ShoppingCart component uses a reference called payment in order to take a payment from the user. Let’s look at that now.

12.2.6. Payment processing (payment and creditcard)

The payment-related components have been used numerous times already in this book to demonstrate a variety of implementation types, bindings, and databindings. In the full application we’re using the payment-spring-policy contribution to process the payments and the creditcard-payment-jaxb-policy contribution to process customer credit cards.

Payment processing isn’t the core business of the TuscanySCATours company, and so we’ve implemented a Payment component that orchestrates the payment process but offloads the real work to other components, as shown in figure 12.8.

Figure 12.8. The Payment and CreditCardPayment composites showing the components that are used during payment processing

A CreditCardPayment component handles the processing of customer credit cards. We’ve included a CustomerRegistry component that retrieves customer information, such as stored credit card details. There’s also an EmailGateway component, which, in real life, would send email back to the user confirming that a payment has successfully been taken.

Looking inside the implementations of the components here, you’ll see that they aren’t proficient from a payment-processing point of view. They don’t do any real payment processing or send any real emails. They do, however, demonstrate some interesting features of Tuscany.

First, as described in chapter 6, we’re mixing implementation types. Here the Payment component is implemented as a Spring context. The Payment component references out to a CustomerRegistry over the SCA binding and to the CreditCard component over the Web Services binding. The Spring implementation of the Payment component isn’t aware of how these other components are implemented. SCA allows us to combine multiple implementation technologies in a single composite application.

The CreditCard component could be implemented by another company, perhaps using non-SCA technologies. Here we’ve shown it implemented using SCA because it made it quick to develop. Because the web service communication between the Payment and CreditCardPayment components may pass between companies, we’ve added the authentication intent to the creditCardPayment reference of the Payment component as follows:

<component name="Payment">
<implementation.spring location="Payment-context.xml"/>
<service name="Payment">
<binding.ws uri="http://localhost:8081/Payment"/>
</service>
<reference name="creditCardPaymentReference" requires="authentication">
<binding.ws uri="http://localhost:8082/CreditCardPayment"/>
</reference>
<reference name="emailGateway" target="EmailGateway"/>
<reference name="customerRegistry" target="CustomerRegistry"/>
<property name="transactionFee">1.23</property>
</component>

The CreditCard interface of the CreditCardPayment component is also configured with a Web Services binding and authentication intent:

<component name="CreditCardPayment">
<implementation.java class="com.tuscanyscatours.payment.
creditcard.impl.CreditCardPaymentImpl" />
<service name="CreditCardPayment">
<interface.wsdl interface="http://www.tuscanyscatours.com/
CreditCardPayment/#wsdl.interface(CreditCardPayment)" />
<binding.ws uri="http://localhost:8082/CreditCardPayment"
requires="authentication"/>
<binding.sca/>
</service>
</component>

Looking back at listing 12.1, note that we’ve used the payment-spring-policy and creditcard-payment-jaxb-policy contributions directly without referencing them from another contribution, such as a fullapp-payment contribution, as we’ve done in other parts of the application. This has the benefit that the configuration required to process payments is entirely contained in these contributions. The drawback is that these contributions specify a deployable composite, which makes them more difficult to reuse in other composites. This is because the deployable composite provided will be deployed regardless of whether you write a new composite that uses the payment components in a slightly different way.

The payment processing shows the use of a different implementation type and includes a policy intent. The ShoppingCart component calls the Payment service makePayment operation when the user checks out, so payment can be taken for the items in the shopping cart. The full application assumes that the user has already registered with TuscanySCATours using a separate web page, and so the Payment component uses the CustomerRegistry component to retrieve the preregistered customer details, including their credit card number. The Payment component then calls the authorize operation on the CreditCardPayment service to authorize the total amount against the credit card number. The EmailGateway component is then used to pretend to send an email detailing whether the payment was taken successfully or not. Once payment has been taken and an email has been sent, the shopping cart is emptied for this user, and the process can start again.

That brings us to the end of our walkthrough of the components used in the TuscanySCATours application. We’ve run the application in a single node using the launchers/fullapp module, so let’s now try running the TuscanySCATours application in a distributed domain.

12.3. The travel-booking application in a distributed domain

We started this chapter by running the application in a single JVM using the Full-AppLauncher class. This is a useful configuration because it allows us to run and debug the application easily when we’re assembling components.

Now let’s try the application running across several distributed nodes. You’ll need to do this if different parts of the application are to be run on different physical machines. This could be because the machines are owned by the parts of the organization that own the components or for performance reasons or even for security reasons. If you don’t need to run SCA applications across multiple JVMs yet, you can safely skip this section and go straight to section 12.4.

Nothing in the components, composites, or contributions changes when we run across multiple JVMs. We’re just going to assign the composites to Tuscany nodes running separately.

To help us do this, we’re going to use Tuscany’s domain manager. The domain manager is a separate program that manages contributions in a domain. Individual nodes running in the domain communicate with the domain manager to retrieve the details of the part of the distributed SCA application that’s to run. Chapter 3 gives a good overview of the features of the domain manager and how to use it, so we won’t repeat that here.

For the TuscanySCATours application, the domain manager is started using the launchers/fullapp-domain module. Once we’ve started the domain manager, we’ll start several nodes, one for each composite in the application, which will each read its configuration from the domain manager across the network. All the separate nodes are started using the launchers/fullapp-nodes module. Figure 12.9 gives an overview of the running sample.

Figure 12.9. Running the TuscanySCATours travel-booking application across distributed nodes configured using the Tuscany domain manager

From chapter 3 you may remember that the domain manager, box 1, is configured by a set of files shown in box 2. The details of the contents of the files shown in box 2 are described in chapter 11.

You need to set up your environment before running the sample, following the instructions in appendix A. Then, assuming you have the source code distribution of the travel-booking application, enter the following:

cd launchers/fullapp-domain
ant run

The ant command runs the run target in the build.xml Ant script. This in turn runs the Java launcher program, which, in this case, is called FullAppDomainLauncher.

When run, the domain manager will first read the names of all of the application contributions from the workspace.xml file. It will then read, from the domain. composite file, the names of all of the composites from these contributions that are to be deployed into the domain. Finally, it reads the configuration of all of the nodes that it’s expecting to be started from the cloud.composite file and from the individual node configuration files in the cloud subdirectory. Once it’s started, you’ll see the following message:

[java] INFO: Press 'q' to quit, 'r' to restart.

If you point your browser at the following URL, you’ll be able to see the domain manager’s management interface, similar to what’s shown in box 3 of figure 12.9: http:// localhost:9990/ui/workspace.

As described in chapter 3, this domain manager user interface allows you to adjust the configuration of the domain manager. You can also start nodes from this user interface, but in this case we’re going to use a program to start all the nodes for us.

The nodes, box 5, are started by running the FullAppNodesLauncher class in the launchers/fullapp-nodes module, box 4. From the travel-booking application source code distribution, you can run it by entering the following:

cd launchers/fullapp-nodes
ant run

The FullAppNodesLauncher starts several nodes, one for each composite in the application. Using this launcher, all of the nodes will be started in the same JVM.

We could have started each node in a separate JVM, and the effect would have been the same. This means that each of the nodes in box 5 could be running on a separate computer with little extra effort.

This is how the node that runs the creditcard composite is started:

SCANode nodeCreditcard = SCANodeFactory.newInstance().
createSCANodeFromURL("http://localhost:9990/node-config/creditcard");
nodeCreditcard.start();

The node is configured with the URL of the domain manager extended with a unique string that identifies this node’s configuration. The domain manager, box 1, makes the configuration available at this URL based on the configuration it’s been provided with, box 2. Once all the nodes are started, you’ll see the following:

[java] Point your browser at - http://localhost:8080/scatours/
[java] Nodes started - Press enter to shutdown.

If you point your browser at the URL described in this message, you should see the TuscanySCATours travel application front page, box 6, that you saw back in figure 12.1.

We’ve finished running the application now, but before we close out this chapter, let’s talk through some of the tricks and tips you learned while developing the application.

12.4. Hints and tips for building composite applications

Here we’ll present some hints and tips that may be useful to you as you build your applications. This isn’t an exhaustive collection of best practices for SCA or Tuscany, and we’d be glad to hear from you on the Tuscany mailing list if you have any hints or tips of your own to share.

12.4.1. Prototyping and then filling out

The Tuscany SCA Java runtime is helpful in the way that, with no extra configuration, it will automatically start up components and the bindings that are used to wire components together.

We started to build the application using a top-down approach by first sketching out the composites and components that served the basic business purpose. Once the basic building blocks were clear, we manually created simple Java implementations for the components. Finally, we described them in composite files and wired them together.

These artifacts were easy to combine in a single contribution constructed using a Maven module or Eclipse project along with some simple tests to exercise the component services.

At this stage, after little time and effort, we were able to run up the components and experiment with their construction and wiring. A good example of this is the set of partner services and the TravelCatalog component that uses them. The interactions between these components are relatively complex in that callbacks and remote bindings are involved. Building this set of components didn’t require any specific configuration of the underlying protocols; it was just a matter of writing the composite configuration, constructing the simple Java component implementations, and running the resulting composites.

This approach suits test-driven development and allows for rapid prototyping, after which the application model can be evolved to bring in new implementation types, new bindings, real and functional implementations, and reorganization into separate contributions.

12.4.2. Application organization

We previously pointed out in this chapter that the full application is made up of a combination of contributions that contain component implementation assets and others that exploit the assets contained in other contributions. This turned out to be quite interesting. Figure 12.10 shows the payment-java contribution, which contains everything required to run the Payment component.

Figure 12.10. The layout of the self-contained payment-java contribution

Notice that this combines the payment.composite file along with all of the assets that the composite requires to run, such as the Java classes and WSDL interface definitions. This is convenient for testing, but it means we’ll have to break open the contribution and change the composite file if we want to change the wiring or add another component.

We found it convenient to create a contribution to hold the composite file, which, in turn, references a separate contribution holding the implementation assets. This exploits a contribution’s ability to export Java packages and attribute namespaces for others to use. For example, the common contribution contains Java interfaces, which are shared by many of the other contributions in the application, and exports the com.tuscanyscatours.common package for other contributions to import.

All the contributions whose name starts with fullapp- use this approach. Figure 12.11 shows an example, the fullapp-bespoketrip contribution.

Figure 12.11. The layout of the fullapp-bespoketrip contribution

This then references the assets of the trip, hotel, flight, and car contributions, which hold the Java assets required to run the components defined in the fullapp-bespoketrip.composite file. Looking at the sca-contribution.xml file, you can see these relationships explicitly stated:

<contribution xmlns="http://www.osoa.org/xmlns/sca/1.0"
xmlns:scatours="http://tuscanyscatours.com/">
<deployable composite="scatours:bespoketrip" />
<import.java package="com.tuscanyscatours.common" />
<import.java package="com.tuscanyscatours.hotel" />
<import.java package="com.tuscanyscatours.hotel.impl" />
<import.java package="com.tuscanyscatours.flight.impl" />
<import.java package="com.tuscanyscatours.car.impl" />
</contribution>

When it comes time to update the composite file, we can edit the existing composite file and rebuild the fullapp-bespoketrip contribution without changing any of the referenced contributions. More likely though, we’ll create a fullapp-bespoketripv2 contribution to hold the changes, but this doesn’t involve copying any other assets because these remain in their separate contributions.

The object here is to be in a position to react to changes in the business process without needing to reconstruct all of the prebuilt and tested contributions.

None of this means that composite files won’t be placed inside contributions alongside the assets that they use. One good reason for doing this is contribution testing, where we test all of the components that a contribution contains in isolation before they’re used in a wide composite application. We’ve often done this. Looking back at the payment-java contribution in figure 12.10, you see a test package containing a JUnit4 test case. In Tuscany this can easily reference a local composite without declaring the composite as deployable for the contribution as a whole.

We should note that we’ve often made copies of contributions on purpose when writing this book to make the samples easier to follow. When concentrating on a particular aspect, such as policy or databinding, all the assets are available in a single contribution and aren’t spread about. But this approach would lead to a lot of unnecessary copying in real applications; combining shared assets into common contributions, as discussed here, reduces duplication.

12.4.3. Developing contributions in a team

Continuing from the previous point, the partitioning of an application into a number of contributions allows the work to be partitioned and assigned to different members of a team or even to different teams.

In the sample application is a contribution called common that’s shared by most of the other contributions. This contains a small number of assets that most other contributions need. Other than the common contribution, the rest of the contributions are relatively standalone and can be implemented in isolation. The service interfaces provide the point of control between the contributions.

By combining this thought with the previous idea of prototyping contributions, it’s easy for a high-level design team to mock up the system and then farm out the prototype contributions to separate teams who will write a fully functional implementation.

Contributions that you intend to share out across the team can be checked into a version control system or added to a registry or shared directory to allow physical access to them. When developing this application we hosted the source code for the modules that are used to generate the shared contributions in a version control system. People wanting to use them checked them out at the required version and built the contributions themselves.

12.4.4. Testing contributions in a single VM

In this chapter we’ve presented the travel-booking application, running it first in a single VM followed by running it across a distributed set of nodes. The flexibility and automation provided by the Tuscany runtime support these two modes of operation while requiring no changes to the application in order to switch between the two.

Starting with the single node of operation will help get applications up and running quickly. Start with single contributions and take a test-driven approach to building components and the service interfaces they provide. Combining components then allows you to build more complex compositions and work toward the full application. It’s easy to run in this mode regardless of whether you’re developing applications using command-line tools or from an IDE such as Eclipse. It’s easy to start, stop, and debug the single-node configuration.

Once you have code running to your satisfaction in a single node, you can run it across multiple nodes to distribute the workload if required. You can do this by using the Tuscany domain manager to configure your nodes, as we’ve done in this chapter when running the TuscanySCATours application.

If you don’t want to run a separate domain manager process to manage the domain, you can easily achieve a similar affect by manually configuring SCA bindings in your application composite files to represent the remote communications between the nodes running separate parts of the application.

12.4.5. Top-down and bottom-up development

Tuscany allows for both top-down and bottom-up development. In the bottom-up approach you build the component implementation and generate the service interface descriptions from there. In the top-down approach it’s the other way round. You build the service interface descriptions manually first and then build the service implementations based on these interfaces.

Which style you choose depends on the situation. We’ve found that the bottom-up approach works well when prototyping a single component. A component prototype can be evolved incrementally until a satisfactory service interface emerges. In this case Tuscany is able to generate WSDL service interface descriptions automatically if binding.ws is included on the services. To get the WSDL, add ?wsdl to the end of the service endpoint URL and point a browser at the resulting URL.

The top-down approach will come into play if there’s an existing service interface to work with. This happened in our application where components share an interface; for example, the Payment component calls the CreditCardPayment component over the Web Services binding. In this case we imagined that the CreditCardPayment component would be a service provided by a different company, and so we generated WSDL for the service interface. The TuscanySCATours company then generates a service interface from the WSDL in an appropriate programming language, in our case the Java language. In this case we used the wsimport tool that’s provided with the Java JDK to read in the CreditCardPayment WSDL and generate a Java interface. We could’ve done this from the command line, but we chose to configure the Maven build for the Payment contribution module to run wsimport for us. You can see this configuration if you look in the pom.xml file that’s at the root of the payment-java contribution.

Because these are general patterns that you’re likely to encounter, you may find the contents of the pom.xml (if you’re a Maven fan) or build.xml (if you’re an Ant fan) file useful when creating your own contribution modules.

12.4.6. Recursive composition

We’ll close this discussion of hints and tips with a word about recursive composites. Recursive composition is a term that describes the use of an SCA composite to implement an SCA component. In effect, composites are nested recursively one inside another. You can see when a composite is providing the implementation of a component because the definition of the component in the composite file will include the implementation.composite element.

We haven’t used any recursive composites in the TuscanySCATours application. This doesn’t mean that the technique isn’t useful for describing reusable functions. It’s just that the opportunity didn’t arise in this small application.

12.4.7. SCA and versioning

The SCA specifications don’t discuss how to version contributions, or any other SCA artifacts, and the Tuscany SCA Java runtime doesn’t provide any specific support for versioning.

From a development point of view, we relied on a version control system to version the source for all of the contribution modules in the sample application. This allowed us to collaboratively develop the contributions and keep track of all the incremental changes.

The simplest thing to do from a runtime point of view is to make sure that you give your contributions sensible names. Because there is no metadata associated with a contribution that indicates its version, a simple version number in the contribution archive name, as you would with a JAR file, works well. You’ll note that we didn’t do this in the sample to keep the contribution names as short as possible. Maven will generate target names with version numbers automatically, but we turned off this feature using the following section of each contribution’s pom.xml file:

<build>
<finalName>${artifactId}</finalName>
...
</build>

We’ve been demonstrating the sample application, running it in a single SCA domain, and there’s no support in the domain for managing different versions of a given contribution at the same time. To do this you’ll need to run the different versions in separate contributions.

That’s it for this section, so let’s now move on and wrap up this chapter.

12.5. Summary

Building an enterprise application will almost certainly involve many different people building many different cooperating services. SCA and Tuscany have been designed to help you build these kinds of applications and to control the complexity that naturally occurs. Here we’ve shown you how SCA and Tuscany can be used to construct an application that relies on multiple interconnected components.

The majority of this book so far has involved looking at specific parts of SCA and Tuscany using separate elements of our sample TuscanySCATours travel-booking application. In this chapter we’ve brought those parts together and shown you how easy it is to assemble a complete running application. First, we ran the application in a single node, a configuration that makes testing and incremental development straightforward. Following this, we showed that by restarting the application using a suitably configured Tuscany domain manager, we’re able to run the application using a distributed infrastructure without changing the application itself.

Beyond the idea of building a composite application out of assembled components, we’ve also described a few other ways that SCA and Tuscany can help speed your development process. This is based on leveraging the SCA model of assembled components in order to distribute work between a team of developers and define an incremental implementation strategy. The power of SCA is the model that it presents of an assembled application. Don’t be afraid to exploit it.

Now we’ve covered all aspects of developing, deploying, and managing applications using Tuscany SCA. We’re going to switch gears in the next part of the book and look at how the Tuscany runtime is architected and how we can create extensions to add additional capabilities such as new implementation types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.179.252