Wednesday, July 1, 2015

Java Restful Web Services



https://brandur.org/api-upgrades
Some readers may be aware already that at Stripe we take a somewhat unconventional “safety first” approach to API versioning. A new version is introduced every time a backwards incompatible change is made, and there’s enough of them that they’re named after dates like 2017-02-14. There tends to be on the order of 10s of new versions in a year.
The first time a user account makes a request to the API, their account is automatically locked to the current version of the API. They can override it by passing a Stripe-Version header, but any requests without will implicitly be assigned the version fixed to their account.
backwards incompatible change is one that could potentially break an existing integration. For example, removing a field from a response, or changing its JSON type

https://stackoverflow.com/questions/38834978/rest-api-design-add-remove-an-item-in-an-array-field-of-a-resource

  • If you to delete only a subset, you can leverage the PATCH method and the JSON PATCHformat (see http://jsonpatch.com/ and https://tools.ietf.org/html/rfc6902) to specify which elements to delete. Here is a sample:
    PATCH /contacts
    [
      { "op": "remove", "path": "/contacts/1" },
      { "op": "remove", "path": "/contacts/2" },
      { "op": "remove", "path": "/contacts/3" }
    ]
https://www.alexecollins.com/rest-resource-association-vs-resource-aggregation/
It's important when consider your API to determine if something in your model is an entity or not. In the meal example, the starters turned out not to be entities, so having an API to create them separately would be odd. Unlike the property API, when the identity of the people was key to the API.
I don't know who lives there, but my common use case is that I always want to list the people living there! The HAL specification provides the _embedded keyword for this:
GET /properties/1

HTTP/1.1 200 OK
{
 "address": "...",
 "_embedded": {
  "people": [{"id": 2, "name": "John Smith"}]
 }
}
This makes it clear that the people aren't part of the resource. In this case we want symmetry in the API, so we can use the _embeddedkeyword in the creation:
POST /properties
{
 "address": "...",
 "_embedded": {
  "people": [{"id": 2, "name": "John Smith"}]
 }
}


https://blogs.oracle.com/enterprisetechtips/entry/consuming_restful_web_services_with
If you're just using Jersey Client API, LoggingFilter (client filter) should help you:
Client client = Client.create();
client.addFilter(new LoggingFilter(System.out));
WebResource webResource = client.resource("http://localhost:9998/");
ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON)
                                         .get(ClientResponse.class);
Otherwise, you can again log both request and response on server using other LoggingFilter(container filter).
Here's how you do it in Jersey 2.x:
        ClientConfig config = new ClientConfig();

        Client client = ClientBuilder.newClient(config);
        client.register(new LoggingFilter());
This is irrelevant but I just have to complain: The new LoggingFilter is really annoying because it forces you to use Java Util Logging. It would be better if it gave me control over the logger. Seems like a step backwards in design.
http://jmchung.github.io/blog/2014/06/18/how-to-customise-the-jackson-json-objectmapper-in-java-ee-enterprise-application/
@Provider
public class JacksonObjectMapperProvider implements ContextResolver<ObjectMapper> {
    final ObjectMapper defaultObjectMapper;
    public JacksonObjectMapperProvider() {
        defaultObjectMapper = createDefaultMapper();
    }
    @Override
    public ObjectMapper getContext(Class<?> type) {
        return defaultObjectMapper;
    }
    private static ObjectMapper createDefaultMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new ObjectIdSerializerModule());
        return mapper;
    }
}
or must be registered in ApplicationConfig.


Developing RESTful Web Services with Jersey 2.0
@CookieParam("customerId") int custId

SecurityContext provides access to security-related information about the current request.

@DefaultValue("15") @QueryParam("age") String age

@MatrixParam("name") String name,
@DefaultValue("15") @MatrixParam("age") String age

@BeanParam

http://exampe.com/services/beanResource/getUserDetails/1;name=John;age=25?

@BeanParam UserBean userBean
public class UserBean {
    @PathParam("id")
    private String id;
    @MatrixParam("name")
    private String name;
    @MatrixParam("age")
    private String age;
    @DefaultValue("No address provided")
    @QueryParam("address")
    private String address;
    @HeaderParam("user-agent")
    private String userAgent;
}

every time a new request is made, a new instance of the root-resource class is being created. The scope of the root-resource class, which is created at the time of request, is limited to that request only.

The @Singleton annotation allows us to create only a single instance throughout the application.

JAX-RS and Bean Validation
https://weblogs.java.net/blog/bhaktimehta/archive/2013/10/30/jax-rs-and-bean-validation
http://samaxes.com/2014/04/jaxrs-beanvalidation-javaee7-wildfly/

http://stackoverflow.com/questions/17143514/how-to-add-custom-response-and-abort-request-in-jersey-1-11-filters
In case of error, if you want to send a custom response then you need to throw WebApplicationException. Create a Response object and send it back using the following exception constructor:
WebApplicationException(Response response) 
          Construct a new instance using the supplied response
Try this:
@Override
public ContainerRequest filter(ContainerRequest request) {
    User user = Helper.getCurrentUser();
    if(user == null){
        ResponseBuilder builder = null;
        String response = "Custom message";
        builder = Response.status(Response.Status.UNAUTHORIZED).entity(response);
        throw new WebApplicationException(builder.build());

    }
    return request;
}
https://blog.codecentric.de/en/2012/05/writing-lightweight-rest-integration-tests-with-the-jersey-test-framework/
the MockTodoServiceProvider is defined as inner class and the mock-object is stored in a class variable of our test class.
https://blogs.oracle.com/naresh/entry/jersey_test_framework_re_visited
   public WebAppDescriptor configure() {
        return new WebAppDescriptor.Builder()
            .initParam(WebComponent.RESOURCE_CONFIG_CLASS,
                      ClassNamesResourceConfig.class.getName())
                .initParam(
                      ClassNamesResourceConfig.PROPERTY_CLASSNAMES,
                      TodoResource.class.getName() + ";"
                              + MockTodoServiceProvider.class.getName() + ";"
                              + NotFoundMapper.class.getName()).build();
    }


 public SpringAnnotationsWebAppTest() throws Exception {
      super(new WebAppDescriptor.Builder("com.sun.jersey.samples.springannotations.resources.jerseymanaged")
                .contextPath("spring")
                .contextParam("contextConfigLocation", "classpath:applicationContext.xml")
                .servletClass(SpringServlet.class)
                .contextListenerClass(ContextLoaderListener.class)
                .build());
    }
WppDescriptor is an abstract class which is extended by two classes - the LowLevelAppDescriptor and the WebAppDescriptor. These classes allow the definition of the various attributes of the application - like its context-path, url-pattern, root resource classes or packages, etc.

Programmatically setting the test container factory
protected TestContainerFactory getTestContainerFactory() {
   return new GrizzlyWebTestContainerFactory();
}

http://en.kodcu.com/2013/06/jax-rs-2-and-longpolling-based-chat-application/
http://allegro.tech/2014/10/async-rest.html
https://jersey.java.net/documentation/latest/async.html
@Path("/resource")
public class AsyncResource {
    @GET
    public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String result = veryExpensiveOperation();
                asyncResponse.resume(result);
            }
            private String veryExpensiveOperation() {
                // ... very expensive operation
            }
        }).start();
    }
}
The client API supports asynchronous processing too. Simple usage of asynchronous client API is shown in the following example:

Example 11.5. Simple client async invocation
1
2
3
4
5
6
7
final AsyncInvoker asyncInvoker = target().path("http://example.com/resource/")
        .request().async();
final Future<Response> responseFuture = asyncInvoker.get();
System.out.println("Request is being processed asynchronously.");
final Response response = responseFuture.get();
    // get() waits for the response to be ready
System.out.println("Response received.");
Client async callback
1
2
3
4
5
6
7
8
9
10
11
12
13
14
final Future<Response> responseFuture = target().path("http://example.com/resource/")
        .request().async().get(new InvocationCallback<Response>() {
            @Override
            public void completed(Response response) {
                System.out.println("Response status code "
                        + response.getStatus() + " received.");
            }
            @Override
            public void failed(Throwable throwable) {
                System.out.println("Invocation failed.");
                throwable.printStackTrace();
            }
        });
http://www.mkyong.com/webservices/jax-rs/restful-java-client-with-jersey-client/
  Client client = Client.create();

  WebResource webResource = client
     .resource("http://localhost:8080/RESTfulExample/rest/json/metallica/post");

  String input = "{\"singer\":\"Metallica\",\"title\":\"Fade To Black\"}";

  ClientResponse response = webResource.type("application/json")
     .post(ClientResponse.class, input);

  if (response.getStatus() != 201) {
   throw new RuntimeException("Failed : HTTP error code : "
        + response.getStatus());
  }
http://juristr.com/blog/2015/05/jersey-webresource-ignores-headers/
You get a new Builder object each time! That's why it doesn't work. So instead of the wrong version above, you rather have to write it like this:
WebResource resource = Client.create(new DefaultClientConfig()).resource("http://myapp.org/api/v1/data");

WebResource.Builder builder = resource.accept(MediaType.APPLICATION_JSON);
builder.type(MediaType.APPLICATION_JSON);
builder.header(HttpHeaders.AUTHORIZATION, "Negotiate " + token);

return builder.get(String.class);
http://allegro.tech/2014/10/async-rest.html
CompletableFuture<T> is a new abstraction introduced in Java 8. It extends Future<T> and adds callbacks support to handle event-driven work. In other languages this concept is called a promise or deferred object
       @GET
        public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
            CompletableFuture
                .runAsync(() -> service.veryExpensiveOperation())
                .thenApply((result) -> asyncResponse.resume(result));
        }
http://stackoverflow.com/questions/27643558/java-rest-jersey-posting-multiple-types-of-data-file-and-json
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces({MediaType.APPLICATION_JSON})
public CreateTaskVO provideService(
               @FormDataParam("meta") String jsonMeta,
               @FormDataParam("data") InputStream file,
               @FormDataParam("data") FormDataContentDisposition fileDetail) {
You can see here an example of how the data can be sent from the client, and also the internal message body format of a multipart
http://stackoverflow.com/questions/27609569/file-upload-along-with-other-object-in-jersey-restful-web-service
http://tongqingqiu.iteye.com/blog/2171321
非阻塞
标准Jersey及其底层servlet实现最大的问题是每处理一个讲求,就需要一个相应的Socket线程处理。在成千上万的并发请求下,系统性能将会明显下降。

为了解决这个问题,JAX-RS提出了异步的解决方案。在这种模式下,请求线程和用户连接之间的联系被打破。I/O容器不再假设等待请求完成,在关闭连接

http://blog.ubone.com/blog/2015/04/12/rest-ke-hu-duan/
WebTarget接口为REST客户端实现资源定位,可以定义请求资源的地址、查询参数和媒体类型等信息。Jersey中的WebTarget接口实现类是JerseyWebTarget。
WebTarget对象接收配置参数的方法是通过方法链,采用不变模式完成。如果分开写每次都返回一个新的WebTarget对象,如果不将其覆值会造成信息丢失。
当WebTarget接口完成资源定位后,Invocation接口向REST服务端发起请求。请求包括同步和异步方式,由Invocation接口内部的Builder接口定义。Jersey中的Invocation接口实现类是JerseyInvocation。
WebTarget webTarget = client.target(BASE_URI);
webTarget.path("books").path("book").queryParam("bookId", "1");
final Invocation.Builder invocationBuilder = webTarget.request();

//以多种方式请求数据
final Book book = invocationBuilder.get(Book.class);
Response response = invocationBuilder.post(userEntity);
invocationBuilder.put(userEntity);
既然Client是一个重型组件,因此频繁地创建Client实例会影响总体性能。一种常见的解决方案是使用HTTP连接池来管理连接。下例使用ApacheConnector来实现HTTP连接池:


final ClientConfig clientConfig = new ClientConfig();
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
//设置最大连接数
cm.setMaxTotal(20000);
//设置每条路由的默认最大连接数
cm.setDefaultMaxPerRoute(10000);
clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, cm);

clientConfig.connectorProvider(new ApacheConnectorProvider());
client = ClientBuilder.newClient(clientConfig);

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts