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
ランタイムの動作をカスタマイズできるコンポーネントである
・データ・バインディング
・例外のマッピング
・コンテキストの解決
データ・バインディング
ボディを任意のオブジェクトに変換する
例外のマッピング
コンテキストの解決
★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などの処理順番
ログ出力
[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
※他サイトを参照