6

I am fairly new to many of the concepts and technologies being used in this question so I would appreciate a little understanding and help for a beginner from the community. I am using the Play Framework version 2.1.3 and I need to POST data to a RESTful web service so that it can be inserted into a remote database. An XML response will be returned indicating either success or failure.

I am sure you are aware that the documentation for the Play Framework is quite lacking and is in no way helpful to beginners, therefore I am unsure of how to accomplish this task with best practices in mind. I am looking for a Java solution to this problem, I do not have the time at present to learn the Scala language. My experience with Web Services is fairly limited, normally I would implement a DAO design pattern (or use one of the many available ORM libraries depending on needs) within my application and use JDBC to connect directly to the database. That is not an option here.

My first question would have to be, is there a recommended design pattern for accessing web services? Then, considering the Play MVC framework, how would one best implement such a design pattern, package the data (assuming the application has already captured and validated data from the user), send it off and process the responses back to the user?

I know it is a fairly lengthy question however my intention behind this is to create a knowledge base of sorts for beginners who can easily come in with limited experience, read, understand and replicate what they find here to produce a working solution. Having searched the web quite extensively, I have found a few disjointed snippets but nothing concrete involving these technologies and no up-to-date tutorials. Thank you for your time.

Hegemon
  • 433
  • 7
  • 21
  • This post is very similar, although not a straight up duplicate I believe: http://stackoverflow.com/questions/4379485/restful-on-play-framework - combine this with the Play! ToDo List example and that should give you a solid starting point. – pfairbairn Sep 01 '13 at 11:14
  • Thank you for the response, since the question was asked in 2010 I assume it is referring to play framework version 1 since play 2 was only released in 2012. They are in no way the same. I could be wrong however and will double check the post. I am more focused on best practices here as well. – Hegemon Sep 01 '13 at 11:17
  • Fair point, however cross referencing that post with the Java Play 2.1 ToDo list, http://www.playframework.com/documentation/2.1.x/JavaTodoList, the information on Routing, using POST, GET and PUT for HTTP requests would make that SO post still seem useful, even if it is somewhat dated. – pfairbairn Sep 01 '13 at 11:23
  • A useful document, I have read it previously and it has helped me setting up my first apps. Although, the question in this case is how does one use Play 2.1 to access a web service and process responses not act as a web service. – Hegemon Sep 01 '13 at 11:35
  • Yeah that question is unrelated. @Hegemon What exactly is the problem with the [documentation about calling webservices](http://www.playframework.com/documentation/2.1.2/JavaWS)? I know it doesn't really explain anything but the code works, it shows you how to create a post request and it also shows you how to return a request's result to the user. I'm not sure if there is any widely used best practice yet. I think placing the code in a DAO would be good though, I did the same and it seemed fine. – kapex Sep 02 '13 at 13:52
  • True, I have looked at the WS API provided on the site and the code works. However, like you said, that doesn't go very far in explaining things very well. I like my solutions to be well thought out, standardized if possible and adhering to best practices... cutting and pasting is not exactly my style I guess. Likewise, I created a DAO implementation for web services, easy enough. I'll post a simplified version of my solution when I've completed it. – Hegemon Sep 03 '13 at 14:05

1 Answers1

11

Creating requests is straight-forward. First you provide a URL. There are various methods to add content types, query parameters, timeouts, etc. to the request. Then you choose a request type and optionally add some content to send. Examples:

WSRequestHolder request = WS.url("http://example.com");
request.setQueryParameter("page", "1");
Promise<Response> promise = request.get();
Promise<Response> promise = WS.url("http://example.com").post(content);

The complicated part is to send it and use the response of the request. I assume you have a controller that should return a Result to the user, based on the web service's response. The result is usually a rendered template or maybe just a status code.

Play avoids blocking by using Futures and Promises. The controller's async method takes a Promise<Result> and returns a result (the future value) at some point later. A simple to use promise is provided by the get and post methods shown above. You don't need to care about their implementation, you just need to know that they promise to provide a Response once the request is complete.

Notice the problem here: When creating a request with WS.url("...").get() it will give you a Promise<Response> even though async takes a Promise<Result>. Here you have to implement another promise yourself, which will convert the response to a result using the map method. If you follow the Play documentation, this will look a bit confusing, because Java doesn't has closures (yet) and everything has to be wrapped in a class. You don't have to use anonymous classes inside the method call though. If you prefer more clean code, you also can do it like this:

return async( 
  request                 
  .get()                   // returns a `Promise<Response>`
  .map(resultFromResponse) // map takes a `Function<Response, Result>` and
                           // returns the `Promise<Result>` we need
);

The object resultFromResponse may look like follows. It's actually just like a cumbersome definition of some kind of callback method that takes a Response as only argument and returns a Result.

Function<Response, List<T>> resultFromResponse = 
    new Function<Response /* 1st parameter type */, Result /* return type */>() {
        @Override
        public Result apply(Response response) {
            // example: read some json from the response
            String message = response.asJson().get("message");
            Result result = ok(message);
            return result;
        }
    };

As @itsjeyd pointed out in the comments, when calling webservices in Play 2.2.x you don't wrap the call in async any more. You simply return the Promise<Result>:

public static Promise<Result> index() {
    return request.get().map(resultFromResponse);
}
kapex
  • 26,163
  • 5
  • 97
  • 111
  • 1
    Thank you for the in-depth response, although most of this is available on the Play site, you have managed to clear up allot of the confusion. I will give it a try. – Hegemon Sep 03 '13 at 14:01
  • 1
    +1 for a great answer. @Hegemon since you wanted this to become a "knowledge base of sorts", I'd like to add that as of play! 2.2.x, returning `Result`s via `async` is deprecated; actions making calls to other web services should return a `Promise` instead. This can be achieved by changing the `return` statement above to `return request.get().map(resultFromResponse);`. – itsjeyd Dec 11 '13 at 14:14