JAX-WSとJAX-RS

JAX-WS

Java API for XML-Based Web Service

サンプル1

確認URL:http://localhost:8080/mavenTest/service/CalculateService?wsdl

import javax.jws.WebMethod;

import javax.jws.WebService;

@WebService

public interface Calculate {

@WebMethod

int sum(String[] values);

}

import javax.jws.WebMethod;

import javax.jws.WebService;

import javax.jws.soap.SOAPBinding;

import javax.jws.soap.SOAPBinding.ParameterStyle;

import javax.jws.soap.SOAPBinding.Style;

import javax.jws.soap.SOAPBinding.Use;

@WebService(

endpointInterface="org.xxx.cxf.jaxws.Calculate",

serviceName="CalculateService",

portName="CalculatePort",

targetNamespace="http://sites.google.com/site/webdevelopart/")

@SOAPBinding(

style=Style.DOCUMENT,

use=Use.LITERAL,

parameterStyle=ParameterStyle.BARE)

@Service("calculate") // オプション

public class CalculateImpl implements Calculate {

@Override

@WebMethod

public int sum(String[] values) {

// do something

}

}

サンプル2

サーバ側

import javax.jws.WebMethod;

import javax.jws.WebService;

@WebService

public class MyWebService {

@WebMethod

public String getMessage() {

return "This is JAX-WS test!";

}

}

// 起動

Endpoint.publish("http://localhost:8080/message", new MyWebService());

http://localhost:8080/message?wsdl

<definitions targetNamespace="http://service.sample.co.jp/" name="MyWebServiceService">

...

</definitions>

クライアント側

JDKツール wsimport -keep http://localhost:8080/message?wsdl

下記のファイルが自動生成される

MyWebService.java インターフェース

MyWebServiceService.java

GetMessage.java

GetMessageResponse.java

ObjectFactory.java

// 方法1

MyWebServiceService service = new MyWebServiceService();

MyWebService servicePort = service.getMyWebServicePort();

String msg = servicePort.getMessage();

...

// 方法2

import java.net.URL;

import javax.xml.namespace.QName;

try {

URL url = new URL("http://localhost:8080/message?wsdl");

MyWebServiceService service

= new MyWebServiceService(url,

new QName("http://service.sample.co.jp/", "MyWebServiceService"));

MyWebService servicePort = serviceWithUrl.getMyWebServicePort();

String msg = servicePort.getMessage();

...

} catch (MalformedURLException e) {

e.printStackTrace();

}

JAX-RS(続く)

★JAX-RSのデプロイ

方法1

import javax.ws.rs.ApplicationPath;

import javax.ws.rs.core.Application;

@ApplicationPath("/app")

public class MyApplication extends Application {

// 通常は空でよい

}

方法2

<servlet>

<servlet-name>Jersey</servlet-name>

<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>Jersey</servlet-name>

<url-pattern>/app/*</url-pattern>

</servlet-mapping>

方法3

import javax.servlet.annotation.WebServlet;

import com.sun.jersey.spi.container.servlet.ServletContainer;

@WebServlet("/app/*")

public class MyServletContainer extends ServletContainer {

// 必ず空

}

★マッピング(他サイト参照)

リクエスト Consume

*/* byte[],

String,

java.io.InputStream,

java.io.Reader,

java.io.File,

javax.activation.DataSource,

javax.ws.rs.core.StreamingOutput

application/xml javax.xml.transform.Source,

javax.xml.bind.JAXBElement

application/x-www-form-urlencoded MultivaluedMap<String, String>

レスポンス Produce

★Provider

Provider Demo

ランタイムの動作をカスタマイズできるコンポーネントである

・データ・バインディング

・例外のマッピング

・コンテキストの解決

データ・バインディング

ボディを任意のオブジェクトに変換する

例外のマッピング

コンテキストの解決

★JAX-RS 2.0の主な新機能 (Java EE7)

・Client API (asynchronous API and callback API)

・Server-side Asynchronous HTTP

・Filters and Interceptors (authentication, caching, encoding etc.)

・Integrate with Bean Validation

・HATEOAS (Hypermedia as the Engine of Application State)

Client API

Client client = ClientFactory.newClient();

WebTarget target = client.target("http://example.com/shop");

Form form = new Form()

.param("customer", "Andy")

.param("product", "Android");

Response response = target.request().post(Entity.form(form));

if (response.getStatus() == 200) {

Order order = response.readEntity(Order.class);

...

}

InvocationCallback<Response>

callback = new InvocationCallback {

public void completed(Response res) {

...

}

public void failed(ClientException e) {

...

}

};

client.target("http://example.com/customers")

.queryParam("name", "Andy")

.request()

.async()

.get(callback);

Server-side Asynchronous HTTP

@Path("/chat")

public class Chat {

private List<AsyncResponse> listeners = ...;

@GET

public void listen(@Suspended AsyncResponse res) {

listeners.add(res);

}

@POST

@Consumes("text/plain")

public void speak(String speech) {

for (AsyncResponse res : listeners) {

res.resume(Response.ok(speech, "text/plain").build());

}

}

}

Filters and Interceptors

Filters: modify or process incoming and outgoing request headers or response headers

Interceptors: marshall and unmarshall HTTP message bodies

Server Side Filters

ContainerRequestFilter ContainerResponseFilter @PreMatching

Client Side Filters

ClientRequestFilter ClientResponseFilter

Reader and Writer Interceptors

WriterInterceptor ReaderInterceptor

Resource Method Filters and Interceptors

DynamicFeature @NameBinding

★Filters、Interceptors、Providersなどの処理順番

Demo

ログ出力

[org.asuki.webservice.rs.filter.server.HttpHeaderFilter]

(default task-8) ContainerRequestFilter

[org.asuki.webservice.rs.filter.server.CustomContainerFilter]

(default task-8) ContainerRequestFilter

[org.asuki.webservice.rs.filter.server.CustomContainerFilter]

(default task-8)

[POST] /demo/list -> org.asuki.webservice.rs.resource.DemoResource$Proxy$_$$_WeldClientProxy

[org.asuki.webservice.rs.interceptor.CustomInterceptor]

(default task-8) ReaderInterceptor

[org.asuki.webservice.rs.provider.ListMessageBodyReader]

(default task-8) MessageBodyReader

[org.asuki.webservice.rs.interceptor.CustomInterceptor]

(default task-8)

aroundReadFrom [{Key1=Value1, Key2=Value2, Key3=Value3}, {KeyA=ValueA, KeyB=ValueB, KeyC=ValueC}]

[org.asuki.webservice.rs.resource.DemoResource]

(default task-8)

[{Key1=Value1, Key2=Value2, Key3=Value3}, {KeyA=ValueA, KeyB=ValueB, KeyC=ValueC}]

[org.asuki.webservice.rs.filter.server.CustomContainerFilter]

(default task-8) ContainerResponseFilter

[org.asuki.webservice.rs.filter.server.CustomContainerFilter]

(default task-8)

Response: Content-Type:[application/json] <- java.util.Arrays$ArrayList

[org.asuki.webservice.rs.interceptor.CustomInterceptor]

(default task-8) WriterInterceptor

[org.asuki.webservice.rs.interceptor.CustomInterceptor]

(default task-8)

aroundWriteTo [{key1=value1, key2=value2}, {key1=value1, key2=value2}]

[org.asuki.webservice.rs.provider.ListMessageBodyWriter]

(default task-8) MessageBodyWriter

★Retrieving a generic list

List<Work> works = target.request(MediaType.APPLICATION_XML).get(List.class);

import javax.ws.rs.core.GenericType;

List<Work> works = target.request(MediaType.APPLICATION_XML).get(new GenericType<List<Work>>() {});

JAX-RS

Java API for RESTful Web Service

サンプル1

確認URL:http://localhost:8080/mavenTest/service/customer/all

import javax.ws.rs.DELETE;

import javax.ws.rs.GET;

import javax.ws.rs.POST;

import javax.ws.rs.PUT;

import javax.ws.rs.Path;

import javax.ws.rs.PathParam;

import javax.ws.rs.Produces;

import javax.ws.rs.core.MediaType;

@Path("/customer")

@Produces({MediaType.APPLICATION_JSON}) // MediaType.APPLICATION_XML

public interface CustomerService {

@GET

@Path("/get/{id}")

Customer getCustomer(@PathParam("id") String id);

@GET

@Path("/all")

Customers getCustomers();

@POST

@Path("/create")

@Consumes(MediaType.APPLICATION_JSON)

Customer createCustomer(Customer customer);

@PUT

@Path("/update")

@Consumes(MediaType.APPLICATION_JSON)

void updateCustomer(Customer customer);

@DELETE

@Path("/delete/{id}")

void removeCustomer(@PathParam("id") String id);

@GET

@Path("/count")

int count(@QueryParam("field") String field);

}

public class CustomerServiceImpl implements CustomerService {

@Override

public Customer getCustomer(String id) {

// do something

}

@Override

public Customers getCustomers() {

// do something

}

@Override

public Customer createCustomer(Customer customer) {

// do something

}

@Override

public void updateCustomer(Customer customer) {

// do something

}

@Override

public void removeCustomer(String id) {

// do something

}

}

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="customer")

public class Customer {

private String id;

private String name;

// getterとsetter

public Customer() {}

public Customer(String id, String name) {

this.id = id;

this.name = name;

}

}

import java.util.List;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="customers")

public class Customers {

private List<Customer> customer;

// getterとsetter

public Customers() {}

public Customers(List<Customer> customer) {

this.customer = customer;

}

}

サンプル2

import org.json.JSONArray;

import java.util.Locale;

import javax.xml.bind.JAXBElement;

import java.net.URI;

import javax.ws.rs.core.Context;

import javax.servlet.ServletConfig;

import javax.servlet.ServletContext;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.ws.rs.core.UriInfo;

import javax.ws.rs.core.Response;

import javax.ws.rs.core.MediaType;

import javax.ws.rs.WebApplicationException;

@Path("/books")

@Stateless

@Produces(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON)

@Consumes(MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON)

public class BookResource {

@Context

private ServletConfig config;

@Context

private ServletContext context;

@Context

private HttpServletRequest request;

@Context

private HttpServletResponse response;

@Context

private UriInfo uriInfo;


@EJB

private BookEJB bookEJB;


@PersistenceContext(unitName = "testU")

private EntityManager em;


@GET

public List<Book> getAllBooks() {

Query query = em.createNamedQuery("findAllBooks");

return query.getResultList();

}


@GET

@Produces(MediaType.APPLICATION_JSON)

public JSONArray getAllBooks() {

JSONArray array = mew JSONArray();

for (Book book : bookEJB.findAll()) {

UriBuilder ub = uriInfo.getAbsolutePathBuilder();

URI uri = ub.path(book.getId().toString()).build();

array.put(uri.toASCIIString());

}


// http://www.example.com/app/books?author=andy

URI uri = UriBuilder.fromUri("http://www.example.com/")

.path("{a}/{b}")

.queryParam("author", "{value}")

.build("app", "books", "andy");


return array;

}


@GET

@Produces(MediaType.TEXT_PLAIN)

public String getHeader(@Context HttpHeaders headers) {

List<Locale> locales = headers.getAcceptableLanguages();

List<Locale> locales = headers.getRequestHeader("accept-language");

List<Locale> locales =

headers.getRequestHeader(HttpHeaders.ACCEPT_LANGUAGE);

}

@GET

@Path("/users/{id}")

@Produces(MediaType.APPLICATION_JSON)

public Response getUserById(@PathParam("id") int id) {

User user = new User();

user.setId(id);

...

return Response.status(200).entity(user).build();

}


@POST

public Response createNewBook(JAXBElement<Book> bookJaxb) {

Book book = bookJaxb.getValue();

em.persist(book);


URI uri = uriInfo.getAbsolutePathBuilder()

.path(book.getId().toString()).build();

return Response.created(uri).build();

}


@POST

@Consumes(MediaType.APPLICATION_XML)

public Response createNewBook(InputStream is) {

...

}


@PUT

@Path("{bookid}")

@Consumes(MediaType.APPLICATION_XML)

public Response updateBook(

@PathParam("bookid") String bookId, InputStream is) {

...

}

@POST

@Path("/product")

@Consumes(MediaType.APPLICATION_JSON)

public Response createProduct(Product product) {

String result = product.toString();

return Response.status(201).entity(result).build();

}


@DELETE

@Path("{bookid}")

public void deleteBook(@PathParam("bookid") String bookId) {

Book book = em.find(Book.class, bookId);

em.remove(book);

}


// http://localhost:8080/project/app/books/2345

@GET

@Path("{bookid}")

public Book getBook(@PathParam("bookid") String bookId) {

if (bookId < 1) {

throw new WebApplicationException(

Response.status(400).entity("custom message").build());

}


Book book = em.find(Book.class, bookId);

if (book == null) {

throw new WebApplicationException(Response.Status.NOT_FOUND);

}


return book;

}


// http://localhost:8080/javaee6x/app/books?bookid=2345

@GET

@Consumes("application/x-www-form-urlencoded")

@Produces("text/plain")

public Book getBook(

@DefaultValue("1001") @QueryParam("bookid") String bookId) {

...

}


@GET

public Book getBook(@CookieParam("sessionid") int sessionId) {

...

}

}

@Entity

@XmlRootElement

@NamedQuery(name="findAllBooks", query="SELECT b FROM book b")

public class Book implements Serializable {

@Id

@GeneratedValue

private Long id;


@Column(nullable=false)

private String title;


@Column(length=1000)

private String description;


// コンストラクタ、getter、setter

}

★ExceptionMapperについて

@Provider

public class OptimisticLockExceptionMapper implements ExceptionMapper<OptimisticLockException> {

@Override

public Response toResponse(OptimisticLockException exception) {

return Response.status(400).entity("Failure").type("text/plain").build();

}

}

上記のOptimisticLockExceptionMapperを再利用

@Provider

public class RollbackExceptionMapper implements ExceptionMapper<RollbackException> {

@Context

private Providers p;

@Override

public Response toResponse(RollbackException exception) {

Throwable cause = exception.getCause();

Class<Throwable> type = (Class<Throwable>) cause.getClass();

return p.getExceptionMapper(type).toResponse(cause);

}

}

★Authentication

★JAX-RS 2.0

※他サイトを参照