Skip to end of metadata
Go to start of metadata

The intent of this tutorial is to walk through the development of a rich Web service API on the Struts 2 development framework.

We're going to be using Enunciate to supply a rich Web service API to the struts showcase application included in the Struts 2 distribution bundle. When deploying the showcase application, you'll notice an employee manager interface that is used to demonstrate the CRUD capabilities of Struts 2. We're going to expose this interface via a Web service API. By the end of the tutorial the showcase application will include the following:

  • REST endpoints. The employee service and skills service will be exposed via REST resource endpoints in both XML and JSON data formats.
  • SOAP endpoints. The services will be exposed via SOAP and defined by a well-defined and consolidated WSDL.
  • Full API Documentation. The Web service API will be fully-documented.
  • Client-side code. The application will provide client-side Java code that will be able to access the Web service API remotely.

Enunciate makes it drop-dead easy to develop this kind of Web service API: a few minor tweaks to the project file, a configuration file, and two simple services with some annotations applied.

Step 1: Set up the Environment

First make sure you have Maven installed, since that's the build tool we'll be using to run the showcase application. You'll also need Subversion to check out the showcase from source code:

This will check-out the showcase application to the showcase directory, which we'll refer to as $SHOWCASE_HOME. Now to run the application:

cd showcase
mvn jetty:run-exploded

You should be able to open up a browser to http://localhost:8080/struts2-showcase to see the showcase application in all it's glory. You can see the CRUD demonstration by navigating to http://localhost:8080/struts2-showcase/empmanager/index.jsp.

We next need to integrate Enunciate with our project. We can do this by adding a dependency on Enunciate to our pom.xml file.


And we also add the maven-enunciate-spring-plugin to the list of plugins in our pom.xml file:


Now let's add a Web service API.

Step 2: Enunciate Configuration

Here is an Enunciate configuration file that Enunciate uses to expose the Web service API for the showcase services. Drop that in $SHOWCASE_HOME (next to the pom.xml file). Let's briefly go over the basic parts of this file.

The <api-classes> element simply tells Enunciate what classes are to be used to define the Web service API. By default, Enunciate assumes all classes in the project are a part of the Web service API, but the showcase has a lot of other classes that are used to drive the UI and were never intended to define the Web service API, so we have to tell Enunciate that only the classes in the org.apache.struts2.showcase.model package and the service classes in the org.apache.struts2.showcase.dao package are used to define our Web service API:

The balance of the configuration file is used to define behavior in a specific Enunciate module. Enunciate generates the documentation of our Web service API from the JavaDocs of our API classes. By configuring the docs module, we tell Enunciate to put the documentation in the /api directory of the application and assign the documentation a title.

Since we don't want to have the Enunciate-generated web.xml stomp on the existing web.xml file, we configure enunciate to merge the Enunciate-generated web.xml file with the application's web.xml with the "mergeWebXML" of the "webapp" element.

Enunciate assembles the Web service API in the form of a Spring application, and it needs to be merged with the showcase application. We do this by "importing" the bean definitions of the showcase application with the bean definitions of the Enunciate application. In order to do this reliably, we need to (1) move the web.xml file to $SHOWCASE_HOME (next to the pom.xml file) and (2) rename $SHOWCASE_HOME/src/main/webapp/WEB-INF/applicationContext.xml to $SHOWCASE_HOME/src/main/webapp/WEB-INF/struts-context.xml. This ensures that the files generated by Enunciate won't be stomped on by the existing files.

mv src/main/webapp/WEB-INF/web.xml .
mv src/main/webapp/WEB-INF/applicationContext.xml src/main/webapp/WEB-INF/struts-context.xml

Step 3: Create the Service Interfaces

If you take a look in the org.apache.struts2.showcase.dao package of the showcase application, you'll find a set of DAOs that Struts leverages to supply data to the Employee Manager UI. These DAOs don't conform very well to the JAX-WS and JAX-RS specs, so it's probably easier to just create our own service interfaces to expose the functionality of these DAOs as Web service endpoints.

We'll create a EmployeeService class and a SkillService class and have Spring supply them with their own instances of EmployeeDao and SkillDao by leveraging the org.springframework.beans.factory.annotation.Autowired annotation. Then, for each service, we'll provide a get method, a create method, and a delete method. Then we'll apply some metadata to expose them as Web service endpoints.

SOAP Metadata

To expose a SOAP interface we just need to apply some JAX-WS metadata to our services. Actually, all we need is the javax.jws.WebService applied to each class.

REST Metadata

Of course we also want to apply a REST interface to our API. This can be done by applying JAX-RS annotations to our service classes, but it's a bit more complicated because of the additional constraints of a REST API.

First of all, you have to map the service to a URI path by applying the annotation. We'll apply the "/employees" path to the Employee endpoint and the "/skills" path to the Skills endpoint.

Next, since you're limited to a constrained set of operations, you have to annotate each specific method that is to be included in the REST API. You must specify the (1) HTTP method that is used to invoke the method and (2) the subpath that is used to locate it. We'll keep it simple by exposing just the get methods via the HTTP GET operation using the annotation and mounting the methods at the "/employee/{id}" and "/skill/{name}" paths for the employee endpoint and skills endpoint, respectively, using the annotation. The "{id}" on the path will specify the id of the employee that we want to get, and likewise for the "{name}" on the path for the skills service. This means that the method parameters must be annotated with the annotation which is also used to specify the name of each path parameter.

Of course, you can expose other methods using other annotations, but we'll refer you to the JAX-RS documentation to learn how to do that.

Here's what our services look like (you can also download them here and here):

One more thing is required in order to expose a REST API. Since by default the REST endpoints will expose XML data, we have to provide root XML elements for the XML responses. To do this, we simply annotate the org.apache.struts2.showcase.model.Employee and org.apache.struts2.showcase.model.Skill classes with @javax.xml.bind.annotation.XmlRootElement.

We also need to annotate the getId() methods on the Employee and Skill classes with @javax.xml.bind.annotation.XmlTransient because Serializable isn't a valid XML type.

Step 4: Build and Deploy

Back on the command-line:

mvn clean jetty:run-exploded

Behold the Glory

Your application is fully-functional at http://localhost:8080/struts2-showcase/.

Check out the documentation for your new Web service API at http://localhost:8080/struts2-showcase/api/ (note that the Struts filter is adding the header and footer; you can fix that by narrowing the filter if you want):

Everything is documented, scraped from the JavaDoc comments. Here's the documentation for the SOAP API:

And documentation for the REST API:

And you can download client-side libraries that Enunciate generated and can be used to invoke your Web service API:

What about your WSDL? http://localhost:8080/struts2-showcase/api/ns0.wsdl

What about your XML-Schema? http://localhost:8080/struts2-showcase/api/ns1.xsd

Want to see your API in action? Your SOAP endpoints are mounted at the /soap subcontext, and your REST endpoints are mounted at the /rest subcontext. To view an employee, just use the path we defined with JAX-RS annotations relative to the /rest subcontext. So to view the employee identified by id "1", we use http://localhost:8080/struts2-showcase/rest/employees/employee/1:

As a convenience, the same XML resource can also be found at http://localhost:8080/struts2-showcase/xml/employees/employee/1. And if you want to get that same resource as JSON, you can use http://localhost:8080/struts2-showcase/json/employees/employee/1.

And Beyond...

Well, that's how easy it is to add a Web service API to you Struts application. But we've only barely scratched the surface of what Enunciate can do. What about any of this:

  • Security (HTTP Auth, OAuth, form-based login, session management, etc.)
  • GWT RPC endpoints and client-side JavaScript for accessing them.
  • AMF endpoints and client-side ActionScript for accessing them.
  • Streaming API for large requests.
  • Etc.

At this point, it's only a matter of configuration....

  • No labels