PA165 - Lab session – WS-* Webservices 2.12.2014 Goals a) set-up a WS-* webservice in Spring, b) understand "contract-first" development of web services. Prerequisites Netbeans 7.3.x, Tomcat 7, Java 7, Maven 3 Scenario The project for this seminar uses Spring-WS to expose an endpoint to manage a series of Book(s) resources. You will be requested to add new functionality and change the configuration of the project. The project uses the "contract-first" approach, so all the domain classes are generated from the xsd schema present in src/main/resources/books.xsd - all the domain classes will be recreated based on this file. There is one integration test class in package cz.muni.fi.pa165.soa.test, you can use it to test the functionality you are implementing. Task 1 In the IS you can find the initial application in the file PA165-Fall2014Seminar12spring-ws-seminar.zip. Have a look at the application, explore the different parts. Check also that the application is runnable. You should be able to import the project in Netbeans and run from it. Alternatively you can compile & run it from the command line from the root of the project: module add maven-3.0.5 mvn clean install && mvn spring-boot:run Upon successful execution you can find a published wsdl file at http://localhost:8080/ws/books.wsdl Look at this file and the parameters exposed in the WebServiceConfig class and the books.xsd schema. You can use curl to interrogate the endpoint. Create one file called request.xml in your current directory, containing The Hitchhiker's Guide to the Galaxy Use curl from the same directory with curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws You should get similar response as the following: The Hitchhiker's Guide to the Galaxy 0345391802 55 DouglasAdams ORDERED From now on, you can use the tests within the application to test requests & responses and their payloads. For this reason, look also that tests in cz.muni.fi.pa165.soa.test work as expected, there is one test that tests exactly the same request and response pair as the one given with the curl command. Task 2 It would be better to add logging to the application, so that we can log the different SOAP messages. Add a file called logback.xml in your src/main/resources folder with the following content: Try to play a bit with MessageTracing different logging levels and see the difference when passing from TRACE, DEBUG and INFO when the endpoint handler methods are invoked (you can do this either with curl or with the tests). Task 3 It is a good idea to validate the schema of the responses we are sending out. For this, you can add an interceptor. Spring-WS gives you the possibility of adding interceptors for different purposes (e.g. security). We will use in this case the PayloadValidatingInterceptor. Open your WebServiceConfig class and create a new interceptor adding the following method: @Bean public PayloadValidatingInterceptor myPayLoadInterceptor() { PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor(); interceptor.setXsdSchema(this.booksSchema()); interceptor.setValidateRequest(true); interceptor.setValidateResponse(true); return interceptor; } Then we add the interceptor to the list of endpoint interceptors. @Override public void addInterceptors(List interceptors) { interceptors.add(this.myPayLoadInterceptor()); } To give an example, without validation the following request will pass through to the endpoint (even if was not defined in the schema): The Hitchhiker's Guide to the Galaxy The Hitchhiker's Guide to the Galaxy After the addition of the interceptor, the schema will be validated, so in that case you will get a response with a SOAP Fault, like the following: SOAP-ENV:Client Validation error cvc-complex-type.2.4.d: Invalid content was found starting with element 'ga:name'. No child element is expected at this point. You can read more about different interceptors in Spring-WS documentation: http://docs.spring.io/spring-ws/docs/2.2.0.RELEASE/reference/htmlsingle/#server- endpoint-interceptor Task 4 In general, there is a problem with the current implementation: the method getBookByAuthorNameAndSurname(...) returns one book, but authors can have many books. Change the behaviour by returning a list of books. This implies that the schema needs to be changed so that more books are returned by the endpoint (hint: you can use minOccurs="0" maxOccurs="unbounded" in the element definition of book in the response – remember also the read comments in the new generated class to use it!). Have passing tests for your new implementation. Task 5 When a book that does not exist in the service is requested, we want to return a SOAP Fault to the requestor. Currently, if we ask for a book that does not exist in the catalogue, we will get the following: The first thing you could try is to throw a RuntimeException() in your endpoint handler method. If you change your public GetBookResponse getBook(...) method in BookEndpoint class as follows [...] Book book = bookRepository.getBookByTitle(request.getTitle()); if (book==null){ throw new RuntimeException("Book " + request.getTitle() + " not found.") ; } [...] The SOAP response will now look as follows: SOAP-ENV:Server Book Alice in Wonderland not found. This is similar to the response we would like to provide in such cases. We need a more proogrammatic way to handle exceptions. In our case, we will use the SoapFaultAnnotationExceptionResolver and using the @SoapFault annotation. So we need to create an Exception and then map to the soapfault: @SoapFault(faultCode = FaultCode.SERVER, faultStringOrReason = "Book not found." ) public class BookNotFoundException extends RuntimeException { public BookNotFoundException(String bookTitle) { super("Book not found '" + bookTitle ); } } now we will throw the exception with if (book==null){ throw new BookNotFoundException(request.getTitle()); } The response will be like the following: SOAP-ENV:Server Book not found. You should have created one test for the case in which there are no books to be returned. For this test, use serverOrReceiverFault() to test for the specific fault reason. Usually you will need to map also SoapFaultAnnotationExceptionResolver as a bean, but in Spring-boot standard configuration for Spring-WS. You can read about exceptions in Spring-WS at http://docs.spring.io/spring- ws/docs/2.2.0.RELEASE/reference/htmlsingle/#server-endpoint-exception-resolver Task 6 Similarly to books.xsd, create another schema customers.xsd in which you can report different properties such as name, surname, address for one customer. Each customer will have a list of books (defined in books.xsd). Expose another endpoint that returns the list of customers with the books that have been leased. Task (Extra) If you have completed all the tasks, you can look at how to consume a web service, you can adapt a client application from the tutorial: http://spring.io/guides/gs/consuming-web-service/