http://developers.sun.com/docs/web/swdp/r2/rest-impl/docs/getting-started.html#addformats

RESTful 웹서비스 개발 시작하기#

이 문서는 RESTful 웹서비스 개발을 위한 자바 API의 앞선(early access) 구현체를 언급한다.

The goal of the API is to provide a programming model that enables developers to rapidly build web services in Java (or using the Java Virtual Machine) that are characteristic of the best designed parts of the web. The API and programming model encourage the use of the REST architectural style and the correct use of HTTP 1.1.

The implementation of the API is commonly referred to as a 'runtime'. The runtime is responsible for deploying Plain Old Java Objects (POJOs), which conform to the API and programming model, and dispatching HTTP requests to those Java objects.

API#

API를 위한 자바용 문서는 여기에서 볼수 있다.

RESTful 웹서비스 만들기#

Joe Gregorio 는 그의 글인 "REST 프로토콜을 만드는 방법" 에서 RESTful 웹서비스를 만들기 위한 방법을 제시했다. 이 방법은 문제를 메꾸는 효과적인 방법이다. 그래서 API를 사용하여 RESTful 웹서비스를 만들기 위해 글에 있는 간단한 employee 서비스 예제를 사용하는 것이 적절하다.

이 방법을 사용하면, 다음의 순서대로 의문점이 생긴다.

  1. URI는 무엇인가?
  2. 무슨 형태인가?
  3. 각각의 URI에 지원되는 메소드(method)는 무엇인가?
  4. 반환될수 있는 상태코드(status code)는 무엇인가?

URI는 무엇인가?#

이 질문은 정말 의미가 있다. URI가 구분하는 자원은 무엇인가? API를 사용하면 이 질문은 다음처럼 바뀌게 된다. URI가 구분하는 자원에 일치하는 자바 클래스는 무엇인가? 간단한 예제는 두개의 자원을 보여준다.

  1. Employee (employee마다 하나의 URI)
  2. 모든 employees

자원에 일치하는 자바 클래스는 UriTemplate 어노테이션으로 주석처리된다. 아래의 간단한 예제는 employees와 두개의 자바 클래스로 employees자원을 보여준다.

@UriTemplate("/employees")
public class Employees ... }

@UriTemplate("/employee/{id}")
public class Employee ... }

UriTemplate 어노테이션은 컨테이너의 기본 URI에 상대적인 URI로 URI template 을 정의한다. URI Template는 URI 템플릿을 지정하는 Internet-Draft 이다.

"내장된 변수가 대치된 후에 URI로 변형될수 있는 문자열. 이 문서는 URI Template의 구조와 문법을 정의한다."

API는 역으로 URI 템플릿을 사용한다. UriTemplate은 주석처리된 자바 클래스와 HTTP 요청의 URI로 주어진다. 런타임시 디플로이되는 자바 클래스는 HTTP 요청을 받고 HTTP 요청의 URI 경로와 일치하는 URI의 지정된 자바 클래스를 찾을것이다.

NOTE: The matching algorithm of an HTTP request to a Java class is more involved than just matching the URI template as described in this section. It also matches additional properties such as the HTTP method, the media type of the HTTP request, and the acceptable media types for the HTTP response.

자바 클래스가 기본 URI인 http://example.com/에 디플로이되었다고 가정하면 다음의 URI는 Employee 클래스의 "/employee/{id}" URI 템플릿에 일치할것이다.

http://example.com/employee/1234
http://example.com/employee/johnDoe
http://example.com/employee/john/doe

Where the id variable is substituted, respectively, with the strings "1234", "johnDoe" and "john/doe". So the Employee class matches many URIs, as many as there are employees. Whereas only the URI http://example.com/employees will match the URI template "/employees" of the Employees class.

무슨 형태인가?#

This question really means: What does a resource consume in terms of HTTP request entities and produce in terms of HTTP response entities, the latter of which are referred to as representations? Or more simply put: What is the information that can be sent and received?

HTTP request entities and representations are identified by MIME media types. (Such MIME media types are used for the Content-Type HTTP header request/response field or the Accept HTTP header request field.) The API specifies annotations for declaring what MIME media types may be consumed (from an HTTP request entity) and produced (by a representation).

The example below shows the Employees and Employee classes annotated with a ConsumedMime and ProduceMime annotation:

@UriTemplate("/employees")
@ConsumeMime("application/employee+xml")
@ProduceMime("application/employee+xml")
public class Employees ... }

@UriTemplate("/employee/{id}")
@ConsumeMime("application/employee+xml")
@ProduceMime("application/employee+xml")
public class Employee ... }

These annotiations declare that the resources consume and produce information identified by the MIME media type application/employee+xml. Declaring such annotations on the Java class states that by default all HTTP methods (see next section) are declared to consume or produce the MIME media types. More than one MIME media type may be consumed or produced by declaring a comma separated list of MIME media types. It is also possible to specify the media type at the method level as shown below.

What methods are supported at each URI?#

HTTP methods are mapped to Java class methods using the HttpMethod annotation. The Java method arguments are populated with information from the HTTP request, and the Java method return value becomes the HTTP response. The example below shows the Employees class that supports the HTTP GET method to obtain a list of employees, and the HTTP POST method to add a new employee.

@UriTemplate("/employees")
@ConsumeMime("application/employee+xml")
@ProduceMime("application/employee+xml")
public class Employees {

  @HttpMethod
  public Employees getEmployeeList() {
    Employees es = ...
    return es;
  }

  @HttpMethod("POST")
  public Employee createEmployee(
      Employee employee) {
    ...
    return new employee;        
  }
}

The names of the Java methods annotated with HttpMethod are not significant when an argument is supplied with the annotation, otherwise the Java method must begin with a valid and known HTTP method. The Java method may return void, T, Representation<T> (or an extension of), HttpResponse (or an extension of) or Object that is any type of the former (except void).

Methods that expect an HTTP body (usually PUT or POST) should have a method argument of type T or Entity<T> to accept the HTTP body content, where T is the desired format. In this example, we are using JAXB to convert between the on-the-wire XML data and a Java class representation of the data.

The API provides support for a number of useful formats:

com.sun.ws.rest.api.representation This package contains a set of classes that implement Representation<T> for a variety of T. You can use these classes for returning data from a method, or develop your own custom class that implements Representation<T> and return an instance of that class instead. The AbstractRepresentation<T> class is provided to simplify the implementation of a custom representation. InputStream, String, byte, DataSource, File, MimeMultipart, FormURLEncodedProperties, Entry (ROME library), Feed (ROME library) and JAXB classes These classes can all be used as the T for a return type or a method parameter, Representation<T> for a return type, or Entity<T> for a method parameter. See com.sun.ws.rest.spi.streaming.TypeStreamingProvider for documentation that describes how to add your own custom streaming provider to API.

The example below shows the Employee class that supports the HTTP GET method to obtain an employee, the HTTP PUT method to update an employee, and the HTTP DELETE method to delete an employee:

@UriTemplate("/employee/{id}")
@ConsumeMime("application/employee+xml")
@ProduceMime("application/employee+xml")
public class Employee {

  @HttpMethod
  public Employee getEmployee(
      @UriParam("id"int id) {
    Employee e = ...
    return e;
  }
  
  @HttpMethod
  public void putEmployee(
      @UriParam("id"int id,
      Employee employee) {
    ...
  }
  
  @HttpMethod
  public void deleteEmployee(
      @UriParam("id"int id) {
    ...
  }
}

Other request information can also be consumed by using method arguments. For example, note the use of the UriParam annotation in the example above. Before a method is invoked, the employee ID is extracted from request URI automatically and cast to the Java primitive type int. If the employee ID cannot be cast, then the runtime returns a 400 Bad Request. So in this example, the client is restricting employee IDs in the URI to strings that contain only digits. The same technique can be used for URI query parameters by use of the QueryParam annotation.

The following types may be annotated with a UriParam, QueryParam, HeaderParam or a MatrixParam annotation: all primitive types (except char), primitive wrapper classes (except Character), String, any class that has a static method with the signature valueOf(String), and any class that has a constructor that takes a String parameter. In addition, the QueryParam annotation supports List<T>, where T is a supported type as previously stated, for the case where multiple values for a query parameter are present.

It is possible to specify at the level of a Java method what MIME media types are consumed and produced. For example, the Employees class may be extended to support additional formats with the following additional methods:

  @HttpMethod
  @ProduceMime("text/xhtml")
  public DataSource getEmployeeListAsHTML() {
      ...
  }
    
  @HttpMethod
  @ConsumeMime("application/x-www-form-urlencoded")
  public Employee postEmployeeFromForm(
      FormURLEncodedProperties employee) {
    ...
  }

If a client sends an HTTP GET request to the employees URI stating that the MIME media type text/xhtml is preferable over application/employee+xml, then the getEmployeeListAsHTML method is invoked (instead of the getEmployee method) and returns a representation of the employees as an XHTML document. If a client sends an HTTP POST request with a HTTP request entity that was produced from an HTML form, then the createEmployeeFromForm method is invoked (instead of the createEmployee method).

NOTE: A client declares what MIME media types are preferable using the Accept HTTP request header field. In the above example, the HTTP request may, for example, include Accept: text/html;q=1.0, application/employees+xml;q=0.6 to indicate that text/html is preferable over application/employees+xml.

An application can also return a specific HTTP response. Below is an expanded version of the Employees class that uses the HTTP 201 Created status to indicate that a new resource has been created.

@UriTemplate("/employees")
@ConsumeMime("application/employee+xml")
@ProduceMime("application/employee+xml")
public class Employees {

  @HttpMethod
  public Created postEmployee(
      Employee employee) {
    Employee newEmployee = addEmployee(employee);
    return new Created(new Representation(newEmployee),  newEmployee.getUri());
  }
}

What Status Codes Could Be Returned?#

The runtime manages the returning of status codes for many common errors cases. If a client sends an HTTP POST request to the employee resource, the runtime returns a 405 Method Not Allowed response. If a client sends an HTTP PUT request to the employee resource where the media type of the request entity is anything other than application/employee+xml, then the runtime returns a 415 Unsupported Media Type response.

Error conditions can be signalled using an exception, for example, the following code can be used to inform the client that an employee resource has gone.

@HttpMethod("GET")
public Representation<Employee> getEmployee(
    @UriParam("id"int id) {
  if (!doesEmployeeExist(id)) {
    throw new WebApplicationException(410);
  }
  ...
}

Deploying a RESTful Web Service#

The first step in deploying a RESTful Web Service is to compile your Java classes with APT. When you compile your resources with APT, the Annotation Processor is run, which generates some handy artifacts that make deploying your application easier. One of the artifacts generated is a RESTBeansResources class. This class is used by the runtime to load your resources into the application. The annotation processor can also generate a web.xml file that can be used to deploy into a servlet container, a application.wadl file, which is a WADL description of your resources, and a WadlResource class that can be used to access the WADL description.

APT can be invoked through an APT ant task supplied with this release. For APT to find the annotation processor, make sure that the restbeans-impl.jar is on the classpath passed to APT.

The annotation processor can accept the following options:

OptionDescriptionCommand-Line ExampleDefault Value
urlpatternSpecify the url-pattern to be placed in the generated web.xml.-Aurlpattern=/myapp/*/restbean/*
servletnameSpecify the servlet-name to be placed in the generated web.xml.-Aservletname="My APP"RESTBeans Application
restbeanpkgSpecify the package to generate the RESTBeansResources class into.-Arestbeanpkg=com.sun.rest.samplerestbeans
noservletTurn off the generation of web.xml.-Anoservletweb.xml is generated.
nowadlTurn off the generation of the application.wadl and the WadlResource class.-Anowadlapplication.wadl and WadlResource class are generated.
restbeansdestdirSpecify the destination directory for the generated /WEB-INF/web.xml relative to APT's destination directory.-Arestbeansdestdir=../..APT destination directory

All of the examples utilize the APT ant task, so you can refer to them for concrete examples of how to invoke APT

The following is taken from the SimpleServlet example. For the APT ant task to run, it must have the restbeans-impl.jar and the tools.jar from Java SE 5 or higher. To execute the APT ant task in Java SE 6 requires that the xEnsorsed="true" be set on the task. (Note that if there is a runtime dependency on JAXB 2.1 or JAX-WS 2.1 jars then these jars must be placed in the endorsed directory of the Java SE 6 distribution, or by setting the endorsed directory using the system property "java.endorsed.dirs".)

Notice that the options are specified using the <option> element.

<taskdef name="rbpt" classname="com.sun.ws.rest.tools.ant.RestBeansProcessorTask">
    <classpath location="${file.reference.restbeans-impl.jar}"/>
</taskdef>

<target name="-pre-compile">
    <echo message="Processing resource classes"/>
    <rbpt fork="true" debug="true" verbose="false" xEndorsed="true"
   nocompile="false"
   destdir="${build.classes.dir.real}"
   sourcedestdir="gen-src"  
   sourcePath="${src.dir}">
  <classpath>
      <path path="${javac.classpath}"/>
      <pathelement location="${build.web.dir}/WEB-INF/classes"/>
  </classpath>
  <option key="restbeansdestdir" value="../.."/>
  <option key="restbeanpkg" value="com.sun.ws.rest.samples.servlet.resources"/>
  <option key="noservlet"/>
  <source dir="${src.dir}">
      <include name="**/*.java"/>
  </source>
    </rbpt>    
    <copy todir="${build.classes.dir}">
  <fileset dir="${src.dir}" excludes="**/*.java"/>
    </copy>
</target>

Java EE Servlet Container#

To deploy to a Java EE servlet container, you must make sure that the restbeans-impl.jar and all of the other jars upon which it is dependent are in the servlet container's classpath. This can be done by either copying the jar files to one of the servlet container's library directories, or by simply WARing the jar files into the application WAR file. The application WAR file should contain your Java classes, the generated RESTBeansResource class, and the generated WEB-INF/web.xml file. This WAR file can then be deployed to your servlet container.

The following shows the WEB-INF/web.xml file generated for the SimpleServlet example. The web.xml specifies the com.sun.ws.rest.impl.container.servlet.ServletAdaptor as the <servlet-class>. This adapter uses the resourcebean <init-param> to get the set of Java classes to include in the application.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>RESTBeans Application</servlet-name>
    <servlet-class>com.sun.ws.rest.impl.container.servlet.ServletAdaptor</servlet-class>
    <init-param>
      <param-name>resourcebean</param-name>
      <param-value>com.sun.ws.rest.samples.servlet.resources.RESTBeansResources</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>RESTBeans Application</servlet-name>
    <url-pattern>/restbean/*</url-pattern>
  </servlet-mapping>
</web-app>

The following shows the contents of the SimpleServlet sample WAR file.

META-INF/context.xml
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/MasterResourceBean.class
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/RESTBeansResources.class
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean1.class
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean2.class
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean3.class
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/ResourceBean4.class
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/index.html
WEB-INF/classes/com/sun/ws/rest/samples/servlet/resources/java.jpg
WEB-INF/classes/com/sun/ws/rest/wadl/resource/WadlResource.class
WEB-INF/classes/com/sun/ws/rest/wadl/resource/application.wadl
WEB-INF/lib/activation.jar
WEB-INF/lib/jaxb-api.jar
WEB-INF/lib/jaxb-impl.jar
WEB-INF/lib/jaxws-api.jar
WEB-INF/lib/jdom-1.0.jar
WEB-INF/lib/jsr173_api.jar
WEB-INF/lib/jsr250-api.jar
WEB-INF/lib/localizer.jar
WEB-INF/lib/mail.jar
WEB-INF/lib/persistence-api-1.0.jar
WEB-INF/lib/restbeans-api.jar
WEB-INF/lib/restbeans-impl.jar
WEB-INF/lib/rome-0.9.jar
WEB-INF/web.xml
index.html

JAX-WS Endpoint Container#

When deploying to a JAX-WS Endpoint container, you do not need the generated WEB-INF/web.xml, because a servlet container is not used. To prevent the generation of this file, you can use the noservlet option for APT. The following is taken from the SimpleJAXWSEndpoint example. It shows how the APT ant task is used with this option.

<target name="-pre-compile">
    <echo message="Processing resource classes"/>
    <rbpt fork="true" destdir="${build.classes.dir}" xEndorsed="true"
   sourcedestdir="gen-src" sourcePath="${src.dir}">
  <classpath>
      <path path="${javac.classpath}"/>
      <pathelement location="${build.dir}"/>
  </classpath>                 
  <option key="restbeansdestdir" value="."/>
  <option key="restbeanpkg" value="com.sun.ws.rest.samples.jaxws.resources"/>
        <option key="noservlet"/>
  <source dir="${src.dir}">
      <include name="**/*.java"/>
  </source>
    </rbpt>    
    <copy todir="${build.classes.dir}">
  <fileset dir="${src.dir}" excludes="**/*.java"/>
    </copy>
</target>

To deploy to a JAX-WS endpoint, you must write a simple Main class that can be used to publish the endpoint. The following is the Main class taken from the SimpleJAXWSEndpoint example.

import com.sun.ws.rest.api.container.ContainerFactory;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Provider;
import javax.xml.ws.http.HTTPBinding;

public class Main {
    
    /** Creates a new instance of Main */
    public Main() {
    }
    
    /**
     @param args the command line arguments
     */
    public static void main(String[] argsthrows Exception {
        /** Create a JAX-WS Provider */
        Provider provider = ContainerFactory.createContainer(Provider.class,
                "com.sun.ws.rest.samples.jaxws.resources");
        
        /** Create a JAX-WS Endpoint with the provider */
        Endpoint endpoint = Endpoint.create(HTTPBinding.HTTP_BINDING,provider);
        /** publish the endpoint */
        endpoint.publish("http://localhost:9998/endpoint");
        
        System.out.println("JAX-WS endpoint running, visit: http://127.0.0.1:9998/endpoint/start, hit return to stop...");
        /** wait of for a CR */
        System.in.read();
        System.out.println("Stopping JAX-WS endpoint");
        
        /* stop the endpoint */
        endpoint.stop();
        System.out.println("Server stopped");        
    }
    
}

The following line uses the ProviderFactory to create a JAX-WS Provider:

Provider provider = ContainerFactory.createContainer(Provider.class,
                "com.sun.ws.rest.samples.jaxws.resources");

The ContainerFactory.createContainer method takes two parameters, the type of container which to create, and package name that contains the RESTBeansResource class. An alternative create is also available that can be passed a ResourceConfig should you wish to avoid the apt step.

The following line creates a JAX-WS Endpoint:

Endpoint endpoint = Endpoint.create(HTTPBinding.HTTP_BINDING,provider);

It is passed the HTTPBinding.HTTP_BINDING, which specifies that the XML/HTTP binding should be used and the provider created in the previous line.

The following line does the actual publishing of the endpoint:

endpoint.publish("http://localhost:9998/endpoint");

It takes one argument, which is the URL at which the endpoint should be published.

The following line is simply there to wait for the user to press the Return key:

System.in.read();

... before stopping the endpoint with the following line:

endpoint.stop();

When running a JAX-WS Endpoint-based application, the the API, runtime and dependent jar files, the JAX-WS runtime jar files, the generated RESTBeansResources, and your Java classes must all be in the classpath.

Lightweight HTTP Server Container#

The SimpleConsole example application shows how to use the API with the lightweight HTTP server. Note in particular the -pre-compile target in the build.xml file that generates the classes (primarily RESTBeansResources) (as described above). Note also the use of the noservlet option to prevent generation of a web.xml file as in the previous example. Samples

The distribution contains the following examples that utilize the features of the RESTful API (some of which have been described in this document):

Add new attachment

Only authorized users are allowed to upload new attachments.
« This page (revision-4) was last changed on 22-Jul-2007 03:39 by DongGukLee