diff --git a/src/site/apt/tutorial/tutorial1.apt b/src/site/apt/tutorial/tutorial1.apt index 6c81022c..d22afbf9 100644 --- a/src/site/apt/tutorial/tutorial1.apt +++ b/src/site/apt/tutorial/tutorial1.apt @@ -4,9 +4,11 @@ Introduction - This is an overall tutorial on how to approach Web services development in contract-first style, i.e. starting with the XML Schema/WSDL contract instead of Java code. Spring Web Services focusses on this development style, and this -tutorial helps you get started. Note that this chapter contains almost no Spring-WS specific information: it is mostly -about XML, XSD, and WSDL. + This is the first part of an overall tutorial on how to approach Web services development in contract-first style, +i.e. starting with the XML Schema/WSDL contract instead of Java code. Spring Web Services focusses on this +development style, and this tutorial helps you get started. Note that this page contains almost no Spring-WS +specific information: it is mostly about XML, XSD, and WSDL. The {{{tutorial2.html}second page}} focusses on +implementing this contract using Spring-WS. In this tutorial, we will define a Web service that can be used for Human Resources. Clients can send holiday request forms to this service to book a holiday. It is based on a metaphor for Service Oriented Architectures @@ -15,7 +17,8 @@ North}}. The most important thing when doing contract-first Web service development is to try and think in terms of XML. This means that Java-language concepts are of lesser importance. It is the XML that is sent across the wire, and -you should focus on that. +you should focus on that. The fact that Java is used to implement the Web service is an implementation detail. An +important detail, but a detail nonetheless. The Messages @@ -27,7 +30,7 @@ start out by determining what these messages look like. In the scenario, we have to deal with holiday request, so it makes sense to determine what a holiday looks like: +------------------------------------- - + 2006-07-03 2006-07-07 @@ -42,7 +45,7 @@ parsing hassle. We also added a namespace to the element, to make sure our eleme There is also the notion of an employee in the scenario. Here's what it looks like: +------------------------------------- - + 42 Arjen Poutsma @@ -58,7 +61,7 @@ sense to use a different namespace, such as <<<"http://mycompany.com/employees/s Both the holiday and employee element can be put in a <<>>: +------------------------------------- - + 2006-07-03 2006-07-07 @@ -73,7 +76,7 @@ sense to use a different namespace, such as <<<"http://mycompany.com/employees/s The order of the two element does not matter: <<>> could have been the first element just as well. As long as all the data is there; that's what is important. In fact, the data is the only thing that is -important: we are taking a approach. +important: we are taking a <> approach. The Schema @@ -102,21 +105,21 @@ point. +------------------------------------- + targetNamespace="http://mycompany.com/hr/schemas" + xmlns:hr="http://mycompany.com/hr/schemas"> - - + + - - + + @@ -125,9 +128,9 @@ point. - - - + + + @@ -144,14 +147,14 @@ types), and inlining the results, we can accomplish this. +------------------------------------- + targetNamespace="http://mycompany.com/hr/schemas"> - /> - /> + + @@ -175,7 +178,7 @@ types), and inlining the results, we can accomplish this. validate: +------------------------------------- - + this is not a date neither is this @@ -191,52 +194,57 @@ validate: +------------------------------------- + targetNamespace="http://mycompany.com/hr/schemas"> - - + + - /> - /> + + - /> - /> + + +------------------------------------- - We can store this file with a convenient name such as <<>>. + We can store this file with a convenient name such as <<>>. The WSDL - Which leaves the WSDL. We start our WSDL with the standard preamble, and by importing our existing XSD. To + Which leaves the WSDL. Note that in Spring-WS, <>. Based on the XSD and +some conventions, Spring-WS can create the WSDL for you, as explained in the {{{tutorial2.html}next section}} of +this tutorial. The rest of this page will show you how to write your own WSDL, if you choose not to use this +functionality. + + We start our WSDL with the standard preamble, and by importing our existing XSD. To separate the schema from the definition, we will use a separate namespace for the WSDL definitions: -<<<"http://mycompany.com/holidays/definitions">>>. +<<<"http://mycompany.com/hr/definitions">>>. +------------------------------------- + targetNamespace="http://mycompany.com/hr/definitions" + xmlns:tns="http://mycompany.com/hr/definitions" + xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> - + @@ -247,15 +255,15 @@ separate the schema from the definition, we will use a separate namespace for th +------------------------------------- - + > @@ -268,15 +276,15 @@ separate the schema from the definition, we will use a separate namespace for th +------------------------------------- - + @@ -301,15 +309,15 @@ you use for the <<>> elements (anything else is +------------------------------------- - + @@ -337,4 +345,4 @@ you use for the <<>> elements (anything else is +------------------------------------- - This is the final WSDL. We will describe how to implement the resulting schema and WSDL in the next chapter. \ No newline at end of file + This is the final WSDL. We will describe how to implement the resulting schema and WSDL in the {{{tutorial2.html}next section}}. \ No newline at end of file diff --git a/src/site/apt/tutorial/tutorial2.apt b/src/site/apt/tutorial/tutorial2.apt new file mode 100644 index 00000000..3a1e581b --- /dev/null +++ b/src/site/apt/tutorial/tutorial2.apt @@ -0,0 +1,212 @@ + ------------------------------------------------------- + Implementing Contract-first Web Services with Spring-WS + ------------------------------------------------------- + + +Introduction + + This is the second part of a tutorial on how to write contract-first Web services. The first part can be found +{{{tutorial1.html}here}}. This second part focusses on implementing the Web service contract using Spring Web Services. + + +Setup + + In this tutorial, we will be using Maven2 to create the initial project structure for us. Doing so is not required, +but greatly reduces the amount of code we have to write to setup our HolidayService. + + The following command creates a Maven2 web application project for us, using the Spring-WS archetype (i.e. project +template): + ++------------------------------------- +> mvn archetype:create -DarchetypeGroupId=org.springframework.ws \ + -DarchetypeArtifactId=spring-ws-archetype \ + -DarchetypeVersion=1.0-m4-SNAPSHOT \ + -DgroupId=com.mycompany.hr \ + -DartifactId=holidayService ++------------------------------------- + + This command will create a new directory called <<>>. In this project, there is a +<<>> directory, which will contain the root of the WAR file. In this directory, you will find +the standard web application deployment descriptor <<>>, which basically defines a Spring-WS +<<>>, and maps all incoming requests to this servlet: + ++------------------------------------- + + + + MyCompany HR Holiday Service + + + spring-ws + org.springframework.ws.transport.http.MessageDispatcherServlet + + + + spring-ws + /* + + + ++------------------------------------- + + + Next to this file, there is <<>>, which is a Spring application context file that +will contain the Spring-WS bean definitions. + +Implementing the Endpoint + + In Spring-WS, you will implement <> to handle incoming XML messages. There are two flavors of endpoints: +{{{http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/server/endpoint/MessageEndpoint.html}<<>>}} and +{{{http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/server/endpoint/PayloadEndpoint.html}<<>>}}. +Message endpoint gives access to the entire XML message, including SOAP headers, etc. Typically, however, the +endpoint will only be interested in the <> of the message, i.e. the contents of the SOAP body. In that case, +creating a payload endpoint makes more sense. + +* Handling the XML Message + + In this sample application, we are going to use {{{http://www.jdom.org}JDom}} to handle XML message. We are also +using {{{http://www.w3schools.com/xpath/}XPath}}, because it allows us to select particular parts of the XML JDOM tree, +without requiring strict schema conformance. We extend +our endpoint from +{{{http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/server/endpoint/AbstractJDomPayloadEndpoint.html}<<>>}}, +because that will give us a JDOM element to execute the XPath queries on: + ++------------------------------------- +package com.mycompany.hr.ws; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.mycompany.hr.service.HumanResourceService; +import org.jdom.Element; +import org.jdom.JDOMException; +import org.jdom.Namespace; +import org.jdom.xpath.XPath; +import org.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint; + +public class HolidayEndpoint extends AbstractJDomPayloadEndpoint { + + private XPath startDateExpression; + + private XPath endDateExpression; + + private XPath nameExpression; + + private HumanResourceService humanResourceService; + + public HolidayEndpoint(HumanResourceService humanResourceService) { + this.humanResourceService = humanResourceService; + } + + public void init() throws JDOMException { + Namespace namespace = Namespace.getNamespace("hr", "http://mycompany.com/hr/schemas"); + startDateExpression = XPath.newInstance("//hr:StartDate"); + startDateExpression.addNamespace(namespace); + endDateExpression = XPath.newInstance("//hr:EndDate"); + endDateExpression.addNamespace(namespace); + nameExpression = XPath.newInstance("//hr:FirstName|//hr:LastName"); + nameExpression.addNamespace(namespace); + } + + protected Element invokeInternal(Element holidayRequest) throws Exception { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = dateFormat.parse(startDateExpression.valueOf(holidayRequest)); + Date endDate = dateFormat.parse(endDateExpression.valueOf(holidayRequest)); + String name = nameExpression.valueOf(holidayRequest); + + humanResourceService.bookHoliday(startDate, endDate, name); + return null; + } +} ++------------------------------------- + + Let's go over the class one method at a time. The HolidayEndpoint requires the HumanResourceService business service +to operate, so we use the constructor to inject it. Next, we have the initialization method <<>>, which sets up +the XPath expressions using the JDOM API. There are three expressions: <<>> for extracting the +<<<\>>> text value, <<>> for extracting the end date, and <<>> +for extracting the name of the employee. + + The <<>> method is a template method, which gets passed with the <<>> element +from the incoming XML message. Next, we use the XPath expressions to extract the String values from the XML messages, +and convert these values to <<>> using a <<>>. With these values, we invoke a method on the +business service. Typically, this will result in result in a database transaction being started, and some records being +altered in the database. Finally, we return <<>>, which indicates to Spring-WS that we don't want to send a +response message. If we wanted a response message, we could have returned a JDOM Element that represents the payload +of the response message. + + Using JDOM is just one of the options to handle the XML, other options include DOM, dom4j, XOM, SAX, and StAX, but +also marshalling techniques like JAXB, Castor, XMLBeans, JiBX, and XStream. Refer to the airline sample to see how +these are used. + + Here's how we would wire up these classes in our <<>> application context: + ++------------------------------------- + + + + + + + + + + ++------------------------------------- + +* Routing the Message to the Endpoint + + Now that we have written an endpoint that handles the message, we must define how incoming messages are routed to +that endpoint. In Spring-WS, this is the responsibility of an <<>>. In this tutorial, we will route +messages based on their content, by using a <<>>. Here's how we wire it up in +<<>>: + ++------------------------------------- + + + + holidayEndpoint + + + + + + ++------------------------------------- + + This means that whenever a XML message comes in with the namespace <<>> and the +<<>> local name, it will be routed to the holidayEndpoint. It also adds a <<>>, +which dumps incoming and outgoing messages to the log. + +Publishing the WSDL + + Finally, we need to publish the WSDL. As stated on the {{{tutorial1.html}previous page}}, we don't need to write a +WSDL ourselves; Spring-WS can generate one for us based on some conventions. Here's how we define it: + ++------------------------------------- + + + + + + + + + ++------------------------------------- + + The first property we set is the human resource schema we defined on the {{{tutorial1.html}first page}} of this +tutorial, <<>>: we simply placed the schema in the <<>> directory of the application. Next, we define +the WSDL port type to be <<>>. Finally, we set the location where the service can be reached: +<<>>. + + If you deploy the application, and point your browser at +{{{http://localhost:8080/holidayService/holiday.wsdl}<<>>}}, you will +see the generated WSDL. This WSDL is ready to be used by clients, such as {{{http://www.soapui.org/}soapUI}}, or other +SOAP frameworks. + + That concludes this tutorial. The next step would be to look at the echo sample application, that is part of the +distribution. After that, look at the airline sample, which is a bit more complicated, because it uses JAXB, +WS-Security, Hibernate, and a transactional service layer. Finally, refer to the {{{http://static.springframework.org/spring-ws/docs/1.0-m3/reference/html/index.html}reference documentation}}. \ No newline at end of file