Статьи

Многофункциональный API веб-сервисов для вашей любимой платформы: Часть 1. Spring

Целью этой серии практических рекомендаций является демонстрация разработки API-интерфейса с широкими возможностями веб-служб на различных популярных средах разработки. Часть 1 (это руководство) предназначена для Spring Framework.

Мы собираемся использовать Enunciate для предоставления богатого API веб-службы для знакомого примера приложения petclinic, включенного в комплект поставки Spring. К концу урока заявка в петклинику будет включать следующее:

  • Конечные точки ОТДЫХА . Конечные точки REST petclinic будут предоставлять ресурсы в форматах данных XML и JSON.
  • SOAP конечные точки . API petclinic будет представлен через SOAP и определен четко определенным и консолидированным WSDL.
  • Полная документация по API . API веб-службы petclinic будет полностью документирован.
  • Код на стороне клиента . Приложение предоставит клиентский код Java, который сможет удаленно обращаться к API веб-службы.

Излагают делает это потрясающе легко развивать этот вид услуг API Web: файл сборки, файл конфигурации, несколько небольших правок к определению пружинные бобы и некоторые метаданные о интерфейсе сервиса.

Шаг 1: настройка среды

Мы собираемся использовать Maven для создания нашего приложения. Обратите внимание, что Maven был выбран для простоты, но вы также можете использовать Ant или другой инструмент для сборки.

Вот файл POM , созданный для поддержки новейшего приложения petclinic, поставляемого с пружинным дистрибутивом. На момент написания этой статьи последней была весна 2.5.5. Нажмите здесь, чтобы загрузить его , и убедитесь, что вы скачали файл spring-framework-2.5.5-with-docs.zip, чтобы включить приложение petclinic.

Когда вы распакуете пакет, вы найдете приложение petclinic в каталоге samples / petclinic . Мы будем ссылаться на этот каталог как $ PETCLINIC_HOME . Просто поместите файл POM в $ PETCLINIC_HOME . Поскольку для приложения petclinic требуется экземпляр базы данных, мы сначала запускаем экземпляр HSQL DB . Итак, откройте консоль на $ PETCLINIC_HOME и:

Mvn Exec: Java

Затем вы, вероятно, захотите заполнить базу данных некоторыми данными, чтобы вы могли увидеть, как она работает. Поскольку HSQL работает в последней консоли, откройте другую консоль в $ PETCLINIC_HOME и:

mvn sql: выполнить

Хорошо, теперь вы должны быть готовы запустить приложение petclinic:

Mvn причал: взорвалась

Предполагая, что все прошло хорошо, вы сможете открыть браузер по адресу http: // localhost: 8080 / petclinic, чтобы увидеть приложение petclinic во всей его красе.

Теперь давайте добавим API веб-сервиса.

Шаг 2: Создайте конфигурацию

Вот файл конфигурации Enunciate, который Enunciate использует для предоставления API веб-службы для примера petclinic. Удалите это в $ PETCLINIC_HOME (рядом с файлом POM). Давайте кратко рассмотрим основные части этого файла.

Элемент <api-classes> просто сообщает Enunciate, какие классы должны использоваться для определения API веб-службы. По умолчанию Enunciate предполагает, что все классы в проекте являются частью API веб-службы, но в приложении petclinic есть много классов, которые используются для управления веб-приложением и никогда не предназначались для того, чтобы быть частью API веб-службы. Поэтому мы должны сказать Enunciate, что только классы в пакете org.springframework.samples.petclinic и org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic используются для определения нашего API веб-службы:

enunciate.xml:

...
<api-classes>
<include pattern="org.springframework.samples.petclinic.*"/>
<include pattern="org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic"/>
</api-classes>
...

Баланс файла конфигурации используется для определения поведения в определенных модулях Enunciate. Enunciate генерирует документацию нашего API веб-сервиса из JavaDocs наших классов API. Настраивая модуль docs , мы говорим Enunciate поместить документацию в каталог / api приложения и назначить заголовок документации.

Enunciate собирает API веб-службы в форме приложения Spring, и его необходимо объединить с приложением petclinic. Мы делаем это путем слияний излагают сгенерированный web.xml файл с petclinic web.xml файлом и «импорт» определение боба в petclinic применения с бин приложений излагают.

enunciate.xml:

...
<modules>
<docs docsDir="api" title="Petclinic API"/>
<spring-app>
<war mergeWebXML="war/WEB-INF/web.xml"/>
<springImport file="war/WEB-INF/applicationContext-jdbc.xml"/>
</spring-app>
</modules>
...

Шаг 3. Аннотируйте классы API

The org.springframework.samples.petclinic.Clinic can be used as-is to publish a nice Web service API, so we don’t need to add any other classes to the application. All we have to do is to apply some annotations to the existing classes.

SOAP Metadata

The service interface of our Web service API is defined by the org.springframework.samples.petclinic.Clinic interface and its associated implementation, org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic. To expose a SOAP interface, we just apply some JAX-WS metadata to these classes. Specifically, we apply the @javax.jws.WebService annotation to the Clinic interface and the same annotation to the SimpleJdbcClinic class. The annotation on SimpleJdbcClinic, according to the JAX-WS spec, needs to specify the interface that it implements with the endpointInterface() annotation value.

The Clinic interface, as it is defined, explicitly declares org.springframework.dao.DataAccessException to be thrown by each of its methods. Even though this is an unchecked exception, declaring it as explicitly thrown causes a problem for JAX-WS because it will attempt to expose it as a SOAP fault. Since DataAccessException doesn’t conform to the JAX-WS constraints for SOAP faults, Enunciate will fail at compile-time with an error message unless we comment-out the explicit declaration of the DataAccessException. Since this is a runtime exception, this will not present a problem to any consumers of the Clinic interface.

Clinic.java:

@WebService
public interface Clinic {

/**
* Retrieve all <code>Vet</code>s from the data store.
* @return a <code>Collection</code> of <code>Vet</code>s
*/
Collection<Vet> getVets() /* throws DataAccessException */;

/**
* Retrieve all <code>PetType</code>s from the data store.
* @return a <code>Collection</code> of <code>PetType</code>s
*/
Collection<PetType> getPetTypes() /* throws DataAccessException */;

...

}

SimpleJdbcClinic.java:

@WebService (
endpointInterface = "org.springframework.samples.petclinic.Clinic"
)
public class SimpleJdbcClinic implements Clinic, SimpleJdbcClinicMBean {
...
}

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 added constraints imposed by REST.

You first have to map the service to a URI path by applying the @javax.ws.rs.Path annotation to the implementation class, SimpleJdbcClinic. We’ll mount the clinic at the «/clinic» path.

Next, since you’re limited to a constrained set of operations, you have to annotate the each method that is to be included in the REST API. The method must specify (1) the HTTP operation that is used to invoke the method and (2) the subpath that is used to locate it. We’ll keep it simple by exposing only the loadOwner method and the loadPet methods via the HTTP GET operation using the javax.ws.rs.GET annotation and mounting the methods at the «/owner/{id}» and «/pet/{id}» relative paths, respectively, using the javax.ws.rs.Path annotation. The «{id}» on the path will specify the id of the owner/pet that we want to get. This means that the method parameter must be annotated with the @javax.ws.rs.PathParam annotation that specifies the name of the path parameter to which it is to be mapped.

Of course, you can expose other methods via HTTP POST using other annotations, but we’ll refer you to the JAX-RS documentation to learn how to do that. Note also that the method-level JAX-RS annotations can be applied to either the interface or the implementation class, but in this case, we’ll apply them to the interface.

Here’s what our classes look like fully-annotated. You can also download them here and here.

Clinic.java:

@WebService
public interface Clinic {

...

@GET
@Path ("owner/{id}")
Owner loadOwner(@PathParam ("id") int id);

...

@GET
@Path("pet/{id}")
Pet loadPet(@PathParam("id") int id);

...
}

SimpleJdbcClinic.java:

@Path ( "/clinic" )
@WebService (
endpointInterface = "org.springframework.samples.petclinic.Clinic"
)
public class SimpleJdbcClinic implements Clinic, SimpleJdbcClinicMBean {
...
}

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.springframework.samples.petclinic.Pet and org.springframework.samples.petclinic.Owner classes with @javax.xml.bind.annotation.XmlRootElement.

Pet.java:

@XmlRootElement
public class Pet extends NamedEntity {
...
}

Owner.java:

@XmlRootElement
public class Owner extends Person {
...
}

Step 4: Tweak the Spring App XML

Since Enunciate generates documentation for the API, it would be nice to add index.html to the welcome-file-list of the web.xml file for the application (found in the war/WEB-INF directory):

web.xml:

...
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
...

The only thing left is a couple of minor changes to the Spring components definition file. In our case, the components definition is the applicationContext-jdbc.xml file in the war/WEB-INF directory.

In that file, you’ll see the declaration of the annotation-driven transaction manager with the <tx:annotation-driven/> XML element. Since the JAX-RS implementation must be tied to an implementation bean, we have to specify that the transaction manager instrument the implementation class (as opposed to instrumenting just its interfaces). We do this by adding proxy-target-class="true" to this XML element.

Finally, since Enunciate declares its own service interface components, we have to disable autowiring of the «clinic» bean. We do this by applying autowire-candidate="false" to the bean definition:

applicationContext-jdbc.xml:

...
<tx:annotation-driven proxy-target-class="true" />
...
<bean id="clinic" class="org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic"
autowire-candidate="false"/>
...

Step 5: Build and Deploy

Enunciate and Maven will do all the dirty work of building and deploying your application. All you have to do is add the Enunciate Maven Plugin to the POM file (it should be already there, you just have to uncomment it):

pom.xml:

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

Back on the command-line at $PETCLINIC_HOME, make sure your database is running:

mvn exec:java

Then make sure your database is populated:

mvn sql:execute

Then just run the application:

mvn jetty:run-exploded

Behold the Glory

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

Check out the documentation for your new Web service API at http://localhost:8080/petclinic/api/:

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/petclinic/api/ns1.wsdl

What about your XML-Schema? http://localhost:8080/petclinic/api/ns0.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 owner, just use the path we defined with JAX-RS annotations relative to the /rest subcontext. So to view the owner identified by id «1», we use http://localhost:8080/petclinic/rest/clinic/owner/1:

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

And Beyond…

Well, that’s how easy it is to add a Web service API to you Spring 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….