Skip to content

Getting Started (Version 1)

Ryan Heaton edited this page Oct 4, 2023 · 3 revisions

Note: The following is applicable to Enunciate 1.x. For getting started with Enunciate 2, see Getting Started.

This getting started guide will attempt to walk through a mock example of Web service development, without trying to hide the complexities of deployment descriptors, dependencies, configuration, and packaging. Of course, some attempt to be concise needs to be made, but at least you'll get the idea of the full development effort. And you'll see more than just "Hello, World".

We'll be writing a mock social networking app. For this tutorial, the name of our social networking app is IfYouWannaBeCool.com.

The source code for this application can be obtained in one of two ways:

Option 1: enunciate-simple-archetype

Enunciate provides a Maven archetype to seed a simple web service project. The project uses the basic modules and includes working sample service and model code. Here's how to invoke it:

mvn archetype:generate \
  -DarchetypeGroupId=org.codehaus.enunciate.archetypes \
  -DarchetypeArtifactId=enunciate-simple-archetype \
  -DarchetypeVersion=1.29 \
  -DgroupId=org.codehaus.enunciate \
  -DartifactId=wannabecool

Option 2: Distribution Bundle

The "wannabecool" sample is bundled with the distribution bundle that can be downloaded from the releases page.

Step 1: Write the Code

We'll start by defining the domain of our app. We'll define a Persona with an id, an alias, an email address, a name, and a picture. The Name is a complex structure, made up of a given name and a surname. We'll also define a Link between two personas and a SocialGroup consisting of an id, a group leader, the group members, and whether the group is exclusive.

Next, we'll define the services available for our domain data. The PersonaService will define the operations available on a persona. This includes operations for reading a persona, storing a persona, and deleting a persona. The SocialGroupService will carry the operations that deal with linking and grouping personas. These operations include creating a link between two personas, creating a social group, adding someone to a social group, and reading the social groups of a given persona.

We also define the possible exceptions that can get thrown, including a PermissionDeniedException when trying to create a link between two people, and an ExclusiveGroupException when trying to add a persona to an exclusive group.

After having defined our service interfaces, we create our implementation classes, PersonaServiceImpl and SocialGroupServiceImpl.

Apply the Metadata

In order for our code to be identified as a Web service API, we need to apply some metadata in the form of Java annotations. We'll start by defining an XML REST API using JAX-RS annotations. Our objects will be converted to XML using JAXB.

A JAX-RS endpoint is identified by mapping it's implementation to an URI path using the @jakarta.ws.rs.Path annotation. We'll apply the PersonaServiceImpl endpoint implementation to the "/persona" path and the SocialGroupServiceImpl to the "/group" path.

Then, using JAX-RS, we map each method to an HTTP operation and, optionally, to a subpath. We decide to apply method-level metadata on the interface that defines the method. (Note that using interfaces isn't strictly necessary, but it may be convenient to do so if and/or when we apply some AOP or dependency injection.)

So for the PersonaService, we will use an HTTP GET to access the readPersona method and will pass in the persona id as a parameter on the path. We will use an HTTP POST operation to store a new persona, and an HTTP DELETE method to delete a person.

Then, for the SocialGroupService, we will use an HTTP GET to access the readGroup method and will pass in the group id as a parameter on the path. To store a new group, we will use the HTTP POST method and pass in the parameters as HTTP query parameters. To add a persona to a group, we'll use a POST to the group path and pass in the persona id as a query parameter.

Because we're returning objects from a few of our REST API calls, we need to also apply the @XmlRootElement annotation so that JAXB will know how to write out the root of our XML tree.

So, we've got our domain defined along with some services that operate on the domain. The source code structure looks like the following (you can see and browse it here):

src
|
|--com
   |
   |--ifyouwannabecool
      |
      |--api
      |  |
      |  |--ExclusiveGroupException.java
      |  |--SocialGroupService.java
      |  |--PermissionDeniedException.java
      |  |--PersonaService.java
      |
      |--domain
      |  |
      |  |--link
      |  |  |
      |  |  |--Link.java
      |  |  |--SocialGroup.java
      |  |
      |  |--persona
      |     |
      |     |--Name.java
      |     |--Persona.java
      |
      |--impl
         |
         |--SocialGroupServiceImpl.java
         |--PersonaServiceImpl.java

Step 2: Invoke Enunciate

For this example, we're going to invoke Enunciate using the command-line scripts. We do this because it's easy to demonstrate, but you'll probably want to use Ant or Maven. We make sure we have our environment set up, and invoke Enunciate on the source code (multi-line, single command, use the "-v" option to see more output of what Enunciate is doing):

user@localhost>enunciate -Espring.war.file ifyouwannabecool.war\
  src/com/ifyouwannabecool/api/ExclusiveGroupException.java\
  src/com/ifyouwannabecool/api/PermissionDeniedException.java\
  src/com/ifyouwannabecool/api/SocialGroupService.java\
  src/com/ifyouwannabecool/api/PersonaService.java\
  src/com/ifyouwannabecool/impl/PersonaServiceImpl.java\
  src/com/ifyouwannabecool/impl/SocialGroupServiceImpl.java\
  src/com/ifyouwannabecool/domain/persona/Name.java\
  src/com/ifyouwannabecool/domain/persona/Persona.java\
  src/com/ifyouwannabecool/domain/link/Link.java\
  src/com/ifyouwannabecool/domain/link/SocialGroup.java

Step 3: Take a Look

You'll notice that we exported the war artifact that was created by Enunciate to the file "ifyouwannabecool.war" (with the option "-Espring.war.file ifyouwannabecool.war"). Drop that into your favorite J2EE container and hit the app in a browser to see what you've got!

The first thing you'll notice is a nice-looking web page with a generic title divided into two sections. The first section describes the REST API, and it includes the two services we just wrote. The second section is the data section describing our domain. You'll notice the XML-Schema file that was generated that describe our domain.

As you continue to poke around, you'll notice that the documentation is quite sparse (although some information can be gleaned from the names of the methods and arguments).

The "downloads" page links to client-side binaries and source code that can be used to access your REST API. These client-side libraries were generated by Enunciate and packaged up (along with everything else) in the war. The client-side code is clean, intuitive, and powerful, handling all the complexities of the API.

Oh, and of course the endpoints are deployed as described in the documentation.

Step 4: Iterate

Even with no extra options or decorations, Enunciate does a pretty good job publishing your API. But there's so much more you can do with only a minimal amount of effort! Let's give our classes some extra love, then we'll talk about what we did.

You can see and browse this source code here.

src
|
|--enunciate.xml
|--LICENSE.txt
|--com
   |
   |--ifyouwannabecool
      |
      |--api
      |  |
      |  |--ExclusiveGroupException.java
      |  |--SocialGroupService.java
      |  |--PermissionDeniedException.java
      |  |--PersonaService.java
      |  |--package-info.java
      |--domain
      |  |
      |  |--link
      |  |  |
      |  |  |--Link.java
      |  |  |--SocialGroup.java
      |  |  |--package-info.java
      |  |
      |  |--persona
      |     |
      |     |--Name.java
      |     |--Persona.java
      |     |--package-info.java
      |
      |--impl
         |
         |--SocialGroupServiceImpl.java
         |--PersonaServiceImpl.java
         |--package-info.java

Add a SOAP API

Adding a SOAP API is as simple as applying the JAX-WS @WebService annotation to our service interfaces and to our service implementations. (Note that per the JAX-WS specification, the implementation must reference the interface using the endpointInterface method on the annotation.) We also apply the @WebFault annotation to our exceptions so they'll be translated to the client correctly.

There is, and always will be, debate around a SOAP API vs. a REST API. Whatever your opinion, there are advantages to defining a SOAP API, including the ability to define your API with a WSDL and provide a well-defined set of operations to your client. And when it's this easy to define a SOAP API, you may want to consider whether the advantages are worth it.

Removed the use of the default namespace

It is considered a "best practice" to namespace-qualify your domain API. We did this with the use of the package-info.java files for the link API and the persona API. Namespace-qualifying your domain API ensures maximum compatability as the use of the default namespace is confusing to implementation vendors. It also provides a tool to help with versioning your API.

Add a splash package

You'll notice we added a package-info.java file to the com.ifyouwannabecool.api package. This is where we added the introductory (i.e. "splash") documentation for our API. We want this documentation to show up on our index page for our documentation, so we specify this package as the splash packge in our enunciate.xml configuration file. For more information, check out the user guide.

Documentation

We've added javadocs to everything, including the endpoints, their methods and parameters, and the domain data. This documentation will show up in our generated documentation. You're free to use HTML tags as you want; they'll be applied in the resulting HTML. Javadoc block tags are recognized, but currently there is no support for javadoc inline tags (they'll show up unparsed in the documentation).

Specify a title and copyright

We can specify a title and copyright to the generated documentation in the enunciate.xml configuration file.

Add another file to download

We want to be able to add a downloadable file to the documentation. In this case, we add the license file that governs the use of the API. This extra download is specified in the enunciate.xml configuration file.

Specify the deployment configuration

We specify our hostname ("www.ifyouwannabecool.com") and the context at which the app will be deployed ("api") in the enunciate.xml configuration file. There are two advantages to this. (1) The generated WSDL will have an absolute reference to our SOAP endpoints, making the formal XML contract complete. (2) Consumers of our client code won't have to specify the URL of our endpoints if they don't want to.

Specify your namespace prefixes

Again, done in the enunciate.xml configuration file. Why would we want to do this? Just because we suffer from OCD and don't like the default "ns0", "ns1", etc. prefixes that are automatically assigned. It also gives a nice name to our wsdl and schema.

Change the package structure of the client-side classes

Done (where else?) in the enunciate.xml configuration file. This will distinguish the generated client classes from the original server-side classes. (Makes it easier to test, clearer that you're dealing with client-side classes, etc.)

And there are still a ton of other configuration options available to you as the API developer (which we won't go into here):

  • Specify your own CSS for the documentation, or even your own XSLT stylesheet if you don't like the structure.
  • Customize the web.xml file used to deploy your app.
  • Add AOP interceptors to your services.
  • Add your own API rules to be enforced at compile-time.
  • Etc.

Well, when we're done polishing things up, we enunciate our API again and deploy the war file as we did in steps 2 and 3. (Note that this time, we specify the config file on the command line.)

user@localhost>enunciate -f enunciate.xml -Espring.war.file ifyouwannabecool.war\
src/com/ifyouwannabecool/api/package-info.java\
src/com/ifyouwannabecool/api/ExclusiveGroupException.java\
src/com/ifyouwannabecool/api/PermissionDeniedException.java\
src/com/ifyouwannabecool/api/SocialGroupService.java\
src/com/ifyouwannabecool/api/PersonaService.java\
src/com/ifyouwannabecool/impl/PersonaServiceImpl.java\
src/com/ifyouwannabecool/impl/SocialGroupServiceImpl.java\
src/com/ifyouwannabecool/domain/persona/Name.java\
src/com/ifyouwannabecool/domain/persona/Persona.java\
src/com/ifyouwannabecool/domain/link/Link.java\
src/com/ifyouwannabecool/domain/link/SocialGroup.java

Take a look now at the generated documentation and you'll notice all the new enhancements!

Learn More

Clone this wiki locally