The ever-evolving Java programming language and Sun's J2EE specification have enabled software developers of various disciplines to create distributed computing applications that were previously possible only with relatively proprietary tools. Thus, while some development teams may choose to implement new systems in the Java platform, others will create, enhance, and maintain applications using other skills and then integrate them into an existing heterogeneous distributed application. This situation gives rise to an interoperability challenge. How can a new application interact with an old one? The answer: web services. Web services are the new holy grail of programming. They make it possible to share and coordinate dispersed, heterogeneous computing resources.
In this article, you'll learn one route to this goal. You'll see how to architect an application based on the open-source Struts framework that can integrate with web services. You should have some background in J2EE and web services before you begin; I'll briefly introduce the Struts framework and the Model-View-Controller (MVC) pattern here, but if you haven't encountered them before, you should check out the Resources section below for more information.
The MVC pattern: Separating development roles
The MVC design pattern clearly demarcates the roles of programmers and designers. In other words, it untangles data from business logic. This pattern allows designers to concentrate on the display portion of an application and developers to concentrate on developing the components required to drive the application's functions.
There are several variations to the MVC pattern, but they are all based on the same underpinning structure: an application's data models (the Model), presentation code (the View), and program control logic (the Controller) should exist as distinct but intercommunicating components. The Model component represents and processes application data. The View is the user interface; it reflects the Model data and presents it to the user. The Controller maps actions performed on the View (for example, pressing a Submit button) to operations on the Model (for example, retrieving user details). After the Model is updated, the View is refreshed and the user can perform more actions. The MVC pattern clarifies code and facilitates code reuse; in addition, in many projects the View is frequently updated, and the MVC pattern insulates the Model and Controller against those changes.
Figure 1 outlines the MVC pattern.
Figure 1. MVC design pattern
For more on the Model-View-Controller pattern, see Resources below.
Struts: A solid MVC-based framework
Struts is an open-source framework for building web applications based on the MVC pattern. Struts encourages application architectures based on the MVC pattern and provides services common to most web applications.
In a Struts application, you can architect the Model layer so that the business and data retrieval logic are easy to reuse. This layer is responsible for running the application's business logic and getting the relevant data (for example, running an SQL command or reading a flat file).
Struts encourages application architectures based on the Model-View-Controller design paradigm. Struts provides its own Controller component (the ActionController classes) and integrates with other technologies to provide the Model and the View. For the Model (Model classes), Struts can interact with any standard data access technology, including Enterprise Java Beans technology, JDBC, and the Object-Relational Bridge. For the View (ActionForm classes), Struts works well with the JavaServer Pages (JSP) environment and other presentation systems. Figure 2 illustrates the logical flow of a Struts-based application.
Figure 2. Logical flow of a Struts application
A simple -- and inelegant -- web services architecture
The simplest way to build web services is to associate a single operation with a single enterprise service, as shown in Figure 3. In this design, services that perform business logic and services that perform data retrieval are intermingled.
Figure 3. A simple, inelegant web services architecture
Such a web services architecture is easy to develop from existing business components. However, it has a number of weaknesses: there is no single sign-on point for the user, the coupling between the provider and subscriber isn't very tight, and business logic is not reused. In short, this isn't a very good architecture for a coherent solution.
It's a better idea to base a web services solution on the MVC pattern. In the coming sections, you'll see how you can use Struts to do just that. I'll expand the existing Struts framework with the WSManager layer, which exposes the Model service methods with web services.
Web services with Struts applications
You can augment a well-architected Struts application to support web services in future development. As noted, the Struts framework clearly demarcates a View, a Controller, and a Model. The Model contains all the business logic necessary to retrieve data from a persistent data store. You can build a simple web service layer -- call it the WSManager layer -- so that the Model can either provide or subscribe to a web service. An application that uses this architecture combines the best aspects of component-based development and the World Wide Web, as illustrated in Figure 4.
Figure 4. Struts app with web services
The following sections discuss in detail the different components used in this architecture, paying particularly close attention to the WSManager layer, since that is the truly new portion of this architecture.
The Controller portion of the MVC architecture is focused on receiving requests from the client (typically a user running a web browser), deciding what business logic function should be performed in response to those requests, and then delegating responsibility for producing the next phase of the user interface to an appropriate View component. In Struts, the primary component of the controller is a servlet of class ActionServlet.
The ActionServlet is responsible for mapping URI requests to specific actions through an XML file. This file contains a list of request URIs and tells the ActionServlet how it should dispatch each of them. There are several advantages to this approach:
- The entire logical flow of the application is in a hierarchical text file.
- The list in this format is easier to view and understand, especially for a large application.
The ActionServlet decides the flow of the application. A number of Action classes extend ActionServlet. Each Action class:
- Maps to a distinct process
- Interacts with Struts JSPs through the Struts
ActionController - Is implemented as a Java class that extends the Struts
Actionclass.
The Struts Action classes call the relevant methods in the WSManager classes to make use of a web service. The WSManager gets the required response -- or an exception, if one is raised -- and passes it back to the Struts Controller.
The WSManager receives requests from a JAX-RPC endpoint. Method calls in the WSManager classes are mapped to incoming client requests. These incoming client requests are in the form of SOAP messages. The WSManager should perform security validations, transform parameters, and preprocess the parameters for these requests before delegating the requests to the Model service. A request can contain parameters in the form of Java objects, Java primitive parameters, XML documents, or even SOAP document fragments (for example, SOAP Element objects). These types should be transformed to an internally-supported schema (for example, predefined Java Data Access Objects).
While the WSManager can handle parameters bound to Java objects in a straightforward manner, it might need to take additional steps to deal with XML documents. The following steps are recommended:
- The
WSManagerclasses should validate an incoming XML document against its schema. - The
WSManagerclasses should then transform the XML document to the internally supported schema. - Lastly, the
WSManagershould disassemble the document and potentially map it into domain objects.
It is important that the WSManager perform the following tasks:
- Authentication and authorization
- Handling errors
- Caching.
The WSManager also generates responses; this process consists of simply constructing the method call return values. By keeping this functionality in the WSManager, you can cache data to avoid extra trips to the Model service tier. You can also centralize response assembly and XML document transformation, which is particularly important if the document you'll return to the caller must conform to a schema different from the internal schema.
The WSManager handles all the incoming SOAP requests and delegates them to the business logic exposed in the Model service. If the Model service is implemented as an EJB tier, you could potentially do this through a Session Façade design pattern in the EJB tier. If you implement the WSManager using this pattern, you'll gain several advantages, since the WSManager will:
- Manage the handling of requests and serves as the initial point of contact
- Invoke security services, including authentication and authorization, to avoid any unnecessary layer trips
- Delegate business processing (by using the Model service used by the Struts application)
- Cache the data at the
WSManagerlayer to avoid any unnecessary database trips.
Publisher: Exposing a web service
Each public method implemented in the WSManager classes will be published as a web service. In other words, you'll be publishing a web service description for these classes. A web service description consists of the service's Web Services Description Language (WSDL) description and any XML schemas referenced by it. (WSDL is the standard language for describing a service.)
You can publish a web service description on a public registry or on a corporate registry within an enterprise. You can also publish XML schemas defined by the web service on the same corporate or public repository. A Java web service client uses the JAXR APIs to look up the service description on the corporate or public registry.
You do not need to use a registry if all of your clients are dedicated partners. Instead, you can publish your web service description (the WSDL and XML schemas) on the web tier of your application or at a well-known location with the proper protection. For example, imagine a client application of a reseller who has an agreement with a particular supplier. The client application has been statically bound at development time to the supplier's web service. Only authorized parties can reference the XML schemas or retrieve the service description from the web tier to generate the client code. You should implement such authentication and authorization of valid clients in WSManager layer.
Subscriber: Using a web service
A Struts application can make use of a web service that is already either in the public registry or within an enterprise. The WSManager can have methods that parse the necessary WSDL file and call the relevant operation to return a result. The Struts Controller calls the relevant methods in the WSManager classes in order to use a particular web service. Data is passed back and forth between the WSManager and Struts Controller as predefined Data Access Objects. Any exceptions that occur when accessing the web service are raised in the WSManager and propagated back to the Struts ActionController.
A service requester will search for web services by using the service broker; if it finds a web service that it wants to use, it will try to set up a contract with the service provider in order to consume the service, and thus do business.
To subscribe to a web service, WSManager uses a WSDL document, a server name, a port name, and an operation name, along with any necessary request parameters, which might include a Java primitive type, a Java array, a Java object, or an XML document.
If the target web service is published in a UDDI registry, any Struts-based app can subscribe to it using a broker service such as XMethods (see Resources). After executing the requested operation, the provider web service returns the expected results. The WSManager can change the returned result to match the schema that the application is expecting; it could also amend the result based on the application's requirements. After receiving the result from WSManager, the Struts ActionController can either process the result and forward it to the relevant View or call the relevant Model service to perform further processing.
Error handling
All error handling is taken care of in the WSManager layer; this eliminates unnecessary server trips. This gives a particularly marked performance boost if the Model service is implemented as EJB Layers.
When acting as a provider, the WSManager throws any exception as a SOAPFaultException. It can also check an incoming request and throw exceptions for any missing mandatory fields. You can create a class to track and log these errors in a data store for future reference.
When acting as a subscriber, the WSManager catches any SOAP exceptions thrown by the service provider and changes them to the format that WSManager requires. The thrown error can be logged for any future references. The response values can also be verified and thrown as exceptions if and when needed. You can create a class to log the exceptions for future reference. The WSManager can validate the response value and can throw it as an exception.
Auditing
When acting as a provider, the WSManager can log details for future auditing purposes. You can use this information for a number of purposes, such as:
- Billing a client based on the number of hits it receives
- Collecting data for marketing purposes
- Determining if an application needs to be upgraded
- Identifying and capturing rogue users.
Caching
Web service clients tend to be richer than clients in a typical client-server
architecture; thus, in a web services architecture, the client can do more work, such as caching. Web services can maximize performance by correctly using data caching. You should consider using caching in a web service when the service's requested information is primarily read-only or when that information changes at a slower rate than that at which it is requested.
Authentication and authorization
You can implement the authentication of any subscriber in the WSManager layer. Any client who wants to use the web service should go through this authentication logic. You can use basic user authentication or a digital certificate for this purpose.
You would build the View portion of a Struts-based application using JSP technology. JSP pages can contain static HTML plus dynamic content that changes based on the interpretation (at page request time) of special action tags. The JSP environment includes a set of standard action tags. In addition, there is a standard facility that developers can use to define their own tags, which are organized into custom tag libraries.
The Struts framework includes an extensive custom tag library that facilitates user interfaces that are fully internationalized and interact gracefully with ActionForm beans. The View layer is thin and supplies no business logic. The Struts View interacts with the Struts Controller through ActionForm.
ActionForms are nothing but Java classes extending the Struts-provided ActionForm classes, which have accessor and mutator methods. These methods are called either by JSP pages or by Action classes to populate or retrieve data.
The Model service is implemented as a set of Java classes. Each Model service component will offer a set of services, and the components collectively offer a set of common services as well. The ActionController and WSManager classes pass data back and forth as predefined Data Access Objects. In the course of processing, the ActionController or the WSManager can call the required methods in the relevant Model service components. These components pass the required data in the form of Data Access Objects to the Model service, and the Model service performs any necessary business logic processing and retrieves any necessary data from the persistent data store. The Model service components populate the relevant predefined Data Access Object, and pass it back to the ActionServlet or WSManager classes. Any error or validation messages are propagated back to the ActionServlet or WSManager layer.
You should apply the following design rules to your applications:
- The Model service should not contain any View-related code (i.e., session handling).
- All transactions should be committed in either the
Actionor theWSManagerlayer. - The Model services should only be called by other Model services within the same Model service component, or by higher-level
Actionclasses.
The data persistence layer comprises all the persistent data stores. For example, it might encompass a relational database, flat files, or even XML documents.
I've included a simple example that implements the architecture discussed here. All the sample code is included in a zip file (see Resources below). The example illustrates a simple news portal. The news content is delivered from a data source (here called DataSource) to a JSP page, and the news content is also published as a web service. The portal also retrieves the latest stock quotes from a StockQuote web service and displays it in the same JSP page.
The zip file contains all of the source code and the SQL scripts for DataSource. It also contains Struts Action and ActionForm classes, a Model service class, a JSP page to display the results, and source code for both a publisher and a subscriber. It also contains the WSDL file and the client source code to access the published NewsContent web service.
I won't go into all the details of the example code here; the best way for you to discover its intricacies is to experiment with it on your own. To get you started, I'll outline how the application delivers news content (which is stored in the DataSource) and stock details (which are obtained from a web service) to the JSP page.
When the JSP page is loaded, it calls the Action class, which acts as a Controller in the MVC design pattern. The Action class calls the getNews() method in the Model service class by passing a predefined Data Access Object. (A snippet of the Action class code shown in Listing 1 illustrates how the Model service is called from the Action class.)
The getNews() method implements all the necessary business and data retrieval logic. Once the relevant data is retrieved from the data source, the Model service populates the Data Access Object and sends it back to the Action class.
Listing 1. A portion of the Action class
/*
* Create a pre-defined Data Access Object
*/
newsSearchResultVOB = new NewsSearchResult();
/*
* Call Business layer NewsMs's searchNews method passing the NewsCOD
* and get back a Collection of News
*/
newsList = (Vector) newsMs.getNews(newsSearchResultVOB);
/*
* Put the NewsList in the request.
*
*/
request.setAttribute(SystemConstants.NEWS_LIST, newsList);
//Set the newslist in the form
newsFrm.setNewsDetails(newsList);
// Set the NewsForm in the request
request.setAttribute(mapping.getAttribute(), newsFrm);
//return actionForward;
return mapping.findForward("success");
|
After receiving the Data Access Object from the Model service, the Action class calls a getStocks() method in the WSManager class by passing the same Data Access Object that was passed to Model service. The getStocks() method in WSManager class implements JAX-RPC to subscribe to the stock web service.
The result from the Stock web service is converted back to the local schema and populated back in the predefined Data Access Object. This object is passed back to the action class from where it was called. Listing 2 shows how JAX-RPC is used in WSManager to subscribe to a web service, and how the result from the stock web service is converted to a local schema and populated back in the Data Access Object.
Listing 2. Subscribing to a web service and routing its responses
public Vector getStocks() throws Exception {
//Method level variables
Vector stockValues = new Vector();
try {
// create service factory
javax.xml.rpc.ServiceFactory factory =
javax.xml.rpc.ServiceFactory.newInstance();
// define targetNameSpace
String targetNamespace = "http://www.themindelectric.com/"
+ "wsdl/net.xmethods.services.stockquote.StockQuote/";
// define qname
QName serviceName =
new QName(targetNamespace,
"net.xmethods.services.stockquote.StockQuoteService");
// define portname
QName portName =
new QName(targetNamespace,
"net.xmethods.services.stockquote.StockQuotePort");
// define operation name
QName operationName = new QName("urn:xmethods-delayed-quotes",
"getQuote");
//Specify wsdl location
java.net.URL wsdlLocation =
new java.net.URL("http://services.xmethods.net/soap/urn:xmethods-
delayed-quotes.wsdl");
// create service
javax.xml.rpc.Service service =
factory.createService(wsdlLocation, serviceName);
// create call
javax.xml.rpc.Call call =
service.createCall(portName, operationName);
//Populate an array with list of stock names
Vector populatedStockNames = this.getPopulatedList();
//Loop through the populated list, and for each stock get a result
java.util.Iterator i = this.getPopulatedList().iterator();
String stockName = null;
Float result;
com.ddj.wsstruts.valueobject.StockValue stockValue = null;
while(i.hasNext()) {
stockName = (String) i.next();
// invoke the remote web service
result = (Float) call.invoke(new Object[] {stockName});
if(category.isDebugEnabled()) {
category.debug(" The quote for " + stockName + " is: " + result);
}
//Set stock name and stock values in stockValue bean
stockValue = new com.ddj.wsstruts.valueobject.StockValue();
stockValue.setStockName(stockName);
stockValue.setStockValue(result.toString());
//Add the stockValue bean to stockValues vector
stockValues.add(stockValue);
}
}
|
Once the Action class finishes calling Model service and WSManager, the Stock Value and New Content fields in the ActionForm are filled. Control is returned back to the JSP page, which gets all the necessary values from the ActionForm and paints the UI.
In this article, you saw how to integrate the Struts framework with web services. You also learned how to use Struts components to provide and subscribe to a web service. The simple application code that accompanies this article will help you understand in depth how all of this works.
Using the architecture outlined here, you can develop enterprise applications that are robust, easy to maintain, and easily integrated with any legacy applications. I hope that you will be ready to start exploring more about Struts and web services and see how this architecture can be useful to you in your own projects.
| Name | Size | Download method |
|---|---|---|
| ws-arcstruts.zip | 50KB | HTTP |
Information about download methods
- Download the zip file that accompanies this article to get started using the architecture outlined here.
- Find out more about the Struts framework at the project's home page, hosted by the Apache project.
- Check out "Struts, an open-source MVC implementation," by Malcolm Davis (developerWorks, February 2001). Though this view of the project is now somewhat outdated, it still provides a good introduction to project goals and the MVC pattern in general.
- To learn more about the Session Façade pattern, check out "Entity Bean protection," by Brett McLaughlin (developerWorks, October 2002).
-
XMethods is an example of a broker service.
Jerome Josephraj has recently taken on a new role as a developer at Ford Europe; previously, he worked as a senior consultant for GamCom Solutions. He has lived in the UK for more than five years, and his favorite pastimes are cricket, badminton, and chess. Contact Jerome at jerome_josephraj@hotmail.com.




