Web Service Servers
Goal
Learn to create SOAP and RESTful web services. In this exercise we will be using libraries available for creation of this type of services. The goal is however that the student also understands what happens behind the scenes. Further, this task includes service composition, where two services are combined into one - transparently for the user of the service. Finally, you need to implement caching and optionally take measures which prevent rate limiting.
Prerequisites
The group should have made the assignment “Web service clients” and “RESTful web services exercise”. All prerequisite material for these exercises is prerequisite for this one as well. Further, the student should get acquainted with the Jersey libraries. You can read some practical info about RESTful servers from http://jersey.java.net/nonav/documentation/latest/getting-started.html. Please try out the examples from that webpage. Also check the section on JAX-RS Application, Resources and Sub-Resources, to figure out how to extract parts of a URL. Information on how to use the Jersey REST client libraries can be found from https://jersey.java.net/documentation/latest/user-guide.html#client.
Basic information about implementing SOAP services in Java using JAX-WS can be found from http://www.mkyong.com/webservices/jax-ws/jax-ws-hello-world-example/. (the parts about ruby web services are not relevant for this exercise.)
For the caching part of the exercise, read about google Guava’s CacheBuilder which provides basic in memory (and in process) caching. More information can be found from http://code.google.com/p/guava-libraries/wiki/CachesExplained.
For the optional part of this task, if you have no experience with Java thread pools, read http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html for more information.
Task
Two services need to be consumed and combined transparently for the user of the newly created service. The final set-up looks like this:
The first service to be used is the google geocoding API (more info from http://code.google.com/apis/maps/documentation/geocoding/ ). The second service is a service which converts a country code to a Gross Domestic Product. the WSDL file for that service can be found at http://ub1.ad.jyu.fi/GDP?wsdl. The self-created service, which will be called localGDP service will receive as a parameter a longitude and latitude. As a result, the service returns a GDP for the country at that location. Calling these web services can be done in a similar fashion as in previous exercises.
Caching is essential in many applications. In this case we could cache the GDP of countries. The GDP service updates the values of the GDP for countries every 5 minutes. An easy way to implement caching in this situation would be to query the service every five minutes for all known countries (or all countries ever asked for) and then cache these values. To keep the exercise interesting, you should imagine that there would be too many countries to keep all this data stored in memory. To implement the caching, you could use a Cache created by com.google.common.cache.CacheBuilder from the Google guava library. For the sake of this exercise, you should limit your cache size to 10 and expire entries in the cache after 2.5 minutes. When creating an actual application, its performance is affected greatly by a good choice of cache parameters.
Your localGDP service needs to have both a SOAP and a REST interface, which can be at different tcp ports (this is easier). The SOAP interface should be created using JAX-WS. For the REST service implementation, you should use the Jersey libraries and even groups use JSON to communicate and odd groups use XML. You should implement the REST services using streams (it is certainly disputable that this is the best solution - point is to learn the technique). To use streams, you can return an object of type StreamingOutput from your resource class’ method like this (other methods are allowed as well):
@GET
public StreamingOutput getIt() {
StreamingOutput op = new StreamingOutput() {
public void write(OutputStream out) throws IOException, WebApplicationException {
//write to the stream
}
};
return op;
}
You can use XML or JSON libraries for writing to the stream. Do not just write Strings.
Test whether your interfaces work by using ‘manual ‘testing’, i.e. write a main class which connects to the services you have created.
Then make a web interface for your application. You can use an extra server for hosting the web app. This web interface should show a map (free choice, could be for instance Google maps or you can get inspiration from here). When the user clicks on the map, you should perform a request to the localGDP service and display the result.
If using Vaadin, you can use plug-ins available like the GoogleMaps Add-on.
Answer the following reflective questions in your readme file.
-
How did you design your application?
-
How can you lower the memory consumption and CPU use of the LocalGDP service? Any ideas on how to test this?
-
What does the HTTP request which is send to your localGDP service look like, when the client want to know the GDP for latitude 62.23186 and longitude 25.73656 ? What does the HTTP response look like?
-
If you implemented some mechanism to prevent hitting the rate limit, how did you do it?
-
What should be improved in this exercise if it is given to students in the future?
Optional
An optional challenge is the implementation of basic rate limiting while making calls to the geocoding API. As per the Limits section of the API documentation there are limits on the amount of requests you can perform on the API. It seems like an easy solution would be to wait some time (say, a second or so) and then try to call the service again. The problem is, however, that also other calls which happen in other threads will hit the limit at the same time. This would result in all threads waiting a certain amount of time, and then all try again which might hit the limit again. People familiar with networking would think in the direction of exponential back-of approaches. However, since all the work is done inside the same VM, there is a more efficient way of solving this problem. You can for instance create a singleThreadExecutor (a thread pool with only one thread) which has the responsibility to execute all calls to the geocoding API. You then submit() Callables to the pool and get the result from the returned Future. Now, when you hit the rate limit, you can wait a fixed amount of time and try again. You can be sure that no other threads are doing the same thing at the same time.
Returning the task###
Put the parts you created yourself to your git repository. Add a readme.md file describing your project structure and the choices you made for the REST api (see hint 5 below).
Hints
-
Most hints from the previous exercises are still valid.
-
To consume the google geocoding API service, you should use the Jersey libraries (download from https://jersey.java.net/download.html or look at https://jersey.java.net/documentation/latest/modules-and-dependencies.html)
-
If you want to do JSON object marshalling, you can generate code stubs using http://jsongen.byingtondesign.com/. The generation is not perfect and most likely needs a few manual changes.
-
The wsimport tool introduced in an earlier exercise can be used to convert the WSDL file from the GDP service into classes.
-
The parts which should be included in the answer from the service is something which you should decide in your group. Also the way the information is structured is your choice. You can get inspiration from the mediafire API and the google geocoding API. Also look at the information from https://www.ibm.com/developerworks/webservices/library/ws-restful/. Document the choices in the readme file.
-
Use HTTP features correctly. Return correct response codes and set the MIME type correctly. (You can use @Produces, but this forces the client to set the encoding correctly as well!)
-
For information about transforming exceptions in responses, see https://jersey.java.net/documentation/latest/representations.html#d0e3586