DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Databases
  4. A Rich Web Service API For Your Favorite Framework, Part 3: Struts

A Rich Web Service API For Your Favorite Framework, Part 3: Struts

Ryan Heaton user avatar by
Ryan Heaton
·
Nov. 25, 08 · Interview
Like (0)
Save
Tweet
Share
21.81K Views

Join the DZone community and get the full member experience.

Join For Free

The intent of this how-to series is to demonstrate the development of a rich Web service API on a variety of popular development frameworks. Part 3 (this tutorial) targets Struts 2.

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:

svn co http://svn.apache.org/repos/asf/struts/struts2/tags/STRUTS_2_0_12/apps/showcase

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. There is also a version conflict of the Spring libraries between Enunciate and the Showcase demo that needs to be resolved by an explicit declaration to version 2.5.5 in the Spring dependencies. We're also going to be leveraging CGLIB for some Spring interceptors, so we add a dependency on that, too.

pom.xml

<dependencies>

<dependency>
<groupId>org.codehaus.enunciate</groupId>
<artifactId>enunciate-rt</artifactId>
<version>1.8.1</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>2.5.5</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>2.5.5</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.5</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>2.5.5</version>
</dependency>

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
</dependency>
...
</dependencies>

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

pom.xml

...
<plugins>
...
<plugin>
<groupId>org.codehaus.enunciate</groupId>
<artifactId>maven-enunciate-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<goals>

<goal>assemble</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...

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:

...
<api-classes>
<include pattern="org.apache.struts2.showcase.model.*"/>
<include pattern="org.apache.struts2.showcase.dao.*Service"/>
</api-classes>
...

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.

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 merging the Enunciate-generated web.xml file with the examples web.xml file and 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.

...
<modules>
<docs docsDir="api" title="Struts 2 Example API"/>
<spring-app>
<war mergeWebXML="web.xml"/>

<springImport file="src/main/webapp/WEB-INF/struts-context.xml"/>
</spring-app>
</modules>
...
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 @javax.ws.rs.Path 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 javax.ws.rs.GET annotation and mounting the methods at the "/employee/{id}" and "/skill/{name}" paths for the employee endpoint and skills endpoint, respectively, using the javax.ws.rs.Path 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 @javax.ws.rs.PathParam 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):

EmployeeService.java

package org.apache.struts2.showcase.dao;

import org.apache.struts2.showcase.model.Employee;
import org.apache.struts2.showcase.exception.CreateException;
import org.springframework.beans.factory.annotation.Autowired;

import javax.jws.WebService;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.GET;

/**
* Service for employees.
*/
@WebService
@Path ("/employees")
public class EmployeeService {

@Autowired
private EmployeeDao dao;

/**
* Get the employee.
*
* @param id The id of the employee.
* @return The employee.
*/
@GET
@Path("/employee/{id}")
public Employee getEmployee(@PathParam("id") long id) {
return (Employee) this.dao.get(id);
}

/**
* Create an employee.
*
* @param employee The employee to create.
*/
public void createEmployee(Employee employee) {
try {
this.dao.create(employee);
}
catch (CreateException e) {
throw new RuntimeException(e);
}
}

/**
* Delete an employee.
*
* @param id Id of the employee to delete.
*/
public void deleteEmployee(long id) {
try {
this.dao.delete(id);
}
catch (CreateException e) {
throw new RuntimeException(e);
}
}
}

SkillService.java

package org.apache.struts2.showcase.dao;

import org.apache.struts2.showcase.model.Skill;
import org.apache.struts2.showcase.exception.CreateException;
import org.springframework.beans.factory.annotation.Autowired;

import javax.jws.WebService;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.PathParam;

/**
* Service for skills.
*/
@WebService
@Path("/skills")
public class SkillService {

@Autowired
private SkillDao dao;

/**
* Get the skill.
*
* @param name The name of the skill.
* @return The skill.
*/
@GET
@Path("/skill/{name}")
public Skill getSkill(@PathParam("name") String name) {
return (Skill) this.dao.get(name);
}

/**
* Create an skill.
*
* @param skill The skill to create.
*/
public void createSkill(Skill skill) {
try {
this.dao.create(skill);
}
catch (CreateException e) {
throw new RuntimeException(e);
}
}

/**
* Delete an skill.
*
* @param name Name of the skill to delete.
*/
public void deleteSkill(String name) {
try {
this.dao.delete(name);
}
catch (CreateException e) {
throw new RuntimeException(e);
}
}
}

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.

Employee.java

@XmlRootElement
public class Employee {
...
@XmlTransient
public Serializable getId() {
return getName();
}
...
}

Skill.java

@XmlRootElement
public class Skill {
...
@XmlTransient
public Serializable getId() {
return getName();
}
...
}

 

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/.

Struts Home

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):

Struts API Home

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

Struts SOAP Docs

And documentation for the REST API:

Struts REST Docs

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

Struts Client Downloads

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

Struts WSDL

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

Struts Schema

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:

Struts Schema

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....

The previous parts of this tutorial are available on JavaLobby. Part 1 was based on Struts, while Part 2 dealt with JBoss Seam. Next week, we'll examine Wicket.
API Web Service application Framework

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 10 Most Popular Frameworks for Building RESTful APIs
  • Isolating Noisy Neighbors in Distributed Systems: The Power of Shuffle-Sharding
  • Data Stream Using Apache Kafka and Camel Application
  • Spring Boot vs Eclipse MicroProfile: Resident Set Size (RSS) and Time to First Request (TFR) Comparative

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: