
9fdf9720fdfd8b5eb778855d97ed9ffd.ppt
- Количество слайдов: 64
Contract-First Web Services with Spring-WS Craig Walls Gateway Software Symposium 2007 craig@habuma. com Blog: http: //www. springinaction. com Wiki: http: //wiki. habuma. com
Who are you? • Java: 6? 5? 1. 4? 1. 3? 1. 2? 1. 1? • Spring: 1. 2? 2. 0? 2. 1? • Web-Services: Axis? X-Fire? Glue? Spring-WS? • Favorite session so far? • What brings you to this session?
About me • Agile developer with Semantra – Natural language business intelligence • Developing software professionally for over 13 years – Telecom, Finance, Retail, Education – Java for most of that – Also some C/C++, C#/. NET, Ruby • Spring fanatic
Agenda • • Spring remoting and web services Contract-first Designing the contract Introducing Spring-WS Building service endpoints Wiring it all together Final thoughts Q&A
Spring remoting and web services Building a service using XFire
Spring Remoting Overview • Client side: – Proxy. Factory. Bean : Produces wire-able proxy to a remote service • Service side: – Remote exporter : Exports a Springconfigured POJO as a service • Available in RMI, Hessian, Burlap, Http. Invoker flavors • Spring doesn’t provide an exporter for SOAP web services…however…
XFire • Full-featured web-services stack • Comes with full Spring remoting support – XFire. Client. Factory. Bean : A client-side factory bean that produces a proxy for remote service – XFire. Exporter : A Spring remote exporter to export POJOs as web services • http: //xfire. codehaus. org
Exporting an XFire service 1. Configure the POJO as a Spring <bean> 2. Configure the XFire. Exporter to export the service 3. Configure Spring’s Dispatcher. Servlet in web. xml 4. Configure a handler-mapping to map URLs to the XFire. Exporter
Using JSR-181 annotations 1. 2. 3. 4. Annotate bean class and methods Declare as <bean> in Spring Configure Spring Dispatcher. Servlet Configure a Jsr 181 Handler. Mapping in Spring
What if the POJO changes? • The focus is on the POJO, not the contract. – The service is method-centric, not message -centric • The service’s contract is only a byproduct of the export. • Consequently, the contract is volatile. – (That’s a bad thing!)
Contract Last • The most important piece of a service is NOT the implementation--it’s the contract. • Nevertheless, many service developers treat the contract as a second-class citizen. – Some don’t even think about it at all.
Contract-first web services
The solution: Contract-first • If the contract is so important, then why not elevate it to the position it deserves. • Create the contract first – Then write code that satisfies the contract – Not necessarily implement the contract.
WSDL-first is NOT contract-first • In the WSDL-first approach, WSDL is used to generate service skeletons. – Not unlike using IDL compilers to generate CORBA service skeletons • Resulting skeletons are coupled to the contract. – Changes to the contract change the skeletons
It’s all in the message! • Contract-last services are operationcentric – A service is defined by its operations (which translate to methods) • Contract-first services are messagecentric – A service is defined by the messages it processes and returns.
The contract • WSDL + XSD • Operational contract: WSDL describes what the service can do. • Data contract: XSD describes the messages sent to the service.
Contact-first: Basic steps 1. Define the messages (XML) 2. Create the data contract (XSD) 3. Create the operational contract (WSDL) 4. Develop an endpoint to process the messages (Spring-WS) 5. Map messages to endpoints 6. Deploy
Defining the contract Don’t leave…it ain’t as bad as you think!!!
Create sample messages • Write out XML that resembles what you want passed in and out of your service. • Believe it or not, this is the hardest part.
Poker. Hand. Request <Evaluate. Hand. Request xmlns="http: //www. springinaction. com/poker/schemas"> <card> <suit>HEARTS</suit> <face>TEN</face> </card> <suit>SPADES</suit> <face>KING</face> </card> <suit>HEARTS</suit> <face>KING</face> </card> <suit>DIAMONDS</suit> <face>TEN</face> </card> <suit>CLUBS</suit> <face>TEN</face> </card> </Evaluate. Hand. Request>
Poker. Hand. Response <Evaluate. Hand. Response xmlns= "http: //www. springinaction. com/poker/schemas"> <hand. Name>Full House</hand. Name> </Evaluate. Hand. Response>
Create the data contract • Create XML Schema that can be used to validate sample messages • Options: – Write it by hand (not much fun) – Infer it • Inferred XSD is never perfect. Will require some fine-tuning by hand. – But at least you don’t have to write it all by hand.
XSD Inference tools • Microsoft’s XSD inference tool – http: //msdn 2. microsoft. com/en-us/xml/Bb 190622. aspx • Trang – http: //www. thaiopensource. com/relaxng/trang. html • Nocternity (Perl script) – http: //projects. nocternity. net/xsd-inference/
Create operational contract • Write WSDL • Options: – Write it by hand (not much fun) – Infer it • Generated WSDL is never perfect. Will require some fine-tuning by hand. – Again, you didn’t write it all by hand.
Introducing Spring-WS
What is Spring-WS • Web services framework that encourages contract-first development • Web services are implemented as service endpoints – Endpoints process XML messages – Conceptually similar to Spring MVC • Includes Object-XML Mapping framework • http: //static. springframework. org/springws/site – Current version 1. 0. 1 (released yesterday!!!)
Spring-WS and Spring MVC • Dispatcher. Servlet • Handler mapping • Controller – Command controller • Simple. Mapping Exception. Resolver Spring-WS • Message. Dispatcher • Endpoint mapping • Endpoint – Marshalling endpoint • Soap. Fault. Mapping Exception. Resolver
Spring OXM • Abstraction framework over several popular XML marshalling APIs. • Used by Spring-WS to (un)marshal objects for endpoints. – Can also be used for general purpose XML marshalling.
Supported marshalling APIs • • • Castor XML JAXB (v 1 and v 2) Ji. BX XMLBeans XStream
Building service endpoints
Service endpoints • Process XML message • Several Spring-WS base classes: – – – – Abstract. Dom 4 j. Payload. Endpoint Abstract. Dom. Payload. Endpoint Abstract. JDom. Payload. Endpoint Abstract. Marshalling. Payload. Endpoint Abstract. Sax. Payload. Endpoint Abstract. Stax. Event. Payload. Endpoint Abstract. Stax. Stream. Payload. Endpoint Abstract. Xom. Payload. Endpoint • Spring-WS 1. 0. 0 added support for annotation-based endpoints – @Endpoint, @Payload. Root, @XPath. Param
Abstract. JDom. Payload. Endpoint • Endpoint is given a JDom Element to process and must return a JDom Element: • Can use XPath queries to pull info out of Element objects. public class Evaluate. Hand. JDom. Endpoint extends Abstract. JDom. Payload. Endpoint implements Initializing. Bean { protected Element invoke. Internal(Element element) throws Exception { … // process message } }
Abstract. Marshalling. Payload. Endpoint • Given an Object and must return an Object. • Uses Spring’s OXM framework. – XML message is unmarshalled into Object. Returned object is marshalled into XML. public class Evaluate. Hand. Marshalling. Endpoint extends Abstract. Marshalling. Payload. Endpoint { protected Object invoke. Internal(Object object) throws Exception { Purchase. Order po = (Purchase. Order) object; … // Process purchase order } }
Annotation-based endpoint • Uses annotations to declare endpoints – Minimizes Spring XML configuration – Allows for more POJO(ish) endpoints – Helps with XML parsing • Three annotations… – @Endpoint - Declares a class as an endpoint – @Payload. Root - Specifies a method as the destination for a payload message – @XPath. Param - Defines how to parse message into method parameter
Annotation-based endpoint package com. springinaction. poker. webservice; import org. springframework. ws. server. endpoint. annotation. Endpoint; import org. springframework. ws. server. endpoint. annotation. Payload. Root; import com. springinaction. poker. Poker. Hand. Evaluator; import com. springinaction. poker. Poker. Hand. Type; @Endpoint public class Evaluate. Hand. Annotated. Endpoint { @Payload. Root(namespace = "http: //www. springinaction. com/poker/schemas", local. Part = "Evaluate. Hand. Request") public Evaluate. Hand. Response evaluate. Hand(Evaluate. Hand. Request request) { Poker. Hand. Type hand. Type = poker. Hand. Evaluator. evaluate. Hand(new Poker. Hand( request. get. Hand())); return new Evaluate. Hand. Response(hand. Type); } // injected private Poker. Hand. Evaluator poker. Hand. Evaluator; public void set. Poker. Hand. Evaluator(Poker. Hand. Evaluator poker. Hand. Evaluator) { this. poker. Hand. Evaluator = poker. Hand. Evaluator; } }
Wiring it all together in Spring
Spring-WS: The big picture Maps message payloads to endpoints Endpoint Implementation Business logic happens here Marshals XML messages to/from POJOs Converts exceptions to SOAP faults Magically generates WSDL from XSD
Message. Dispatcher. Servlet • Spring-WS is based on Spring MVC • Must configure a Dispatcher. Servlet in web. xml… – Actually, Message. Dispatcher. Servlet <servlet> <servlet-name>poker</servlet-name> <servlet-class> org. springframework. ws. transport. http. Message. Dispatcher. Servlet </servlet-class> <init-param> <param-name>transform. Wsdl. Locations</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>poker</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <servlet-name>poker</servlet-name> <url-pattern>*. wsdl</url-pattern> </servlet-mapping>
Endpoint mappings • Helps message dispatcher decide where messages should be sent • Two kinds: – Payload. Root. QName. Endpoint. Mapping – Soap. Action. Endpoint. Mapping When this kind of message arrives… <bean id="payload. Mapping" class="org. springframework. ws. server. endpoint. mapping. Payload. Root. QName. Endpoint. Mapping"> <property name="endpoint. Map"> <map> <entry key="{http: //www. springinaction. com/poker/schemas}Evaluate. Hand. Request" value-ref="evaluate. Hand. Endpoint" /> </map> </property> </bean> Send it to this endpoint
Annotation endpoint mappings • If using annotation-based endpoints… <bean class="o. sf. ws. server. endpoint. mapping. Payload. Root. Annotation. Method. Endpoint. Mapping"> <property name="order" value="1"/> </bean> – Note that namespace/local-name of message are specified in @Payload. Root • If marshaling, you’ll need a Marshalling. Method. Endpoint. Adapter: <bean class="o. sf. ws. server. endpoint. adapter. Marshalling. Method. Endpoint. Adapter"> <constructor-arg ref="marshaller"/> </bean>
Wiring the endpoint <bean id="evaluate. Hand. Endpoint" class= "com. springinaction. poker. webservice. Evaluate. Hand. JDom. Endpoint"> <property name="poker. Hand. Evaluator" ref="poker. Hand. Evaluator" /> </bean> <bean id="poker. Hand. Evaluator" class="com. springinaction. poker. Poker. Hand. Evaluator. Impl"/>
A marshalling endpoint <bean id="evaluate. Hand. Endpoint" class="com. springinaction. poker. webservice. Evaluate. Hand. Marshalling. Endpoint"> <property name="marshaller" ref="marshaller" /> Automatically maps XML to/from objects <property name="unmarshaller" ref="marshaller" /> <property name="poker. Hand. Evaluator" ref="poker. Hand. Evaluator" /> </bean>
An (un)marshaller: Castor <bean id="marshaller" class= "org. springframework. oxm. castor. Castor. Marshaller"> <property name="mapping. Location" value="classpath: mapping. xml" /> </bean>
Loose-end: Exceptions • What about exceptions? – How are they mapped to SOAP faults? – Soap. Fault. Mapping. Exception. Resolver <bean id="endpoint. Exception. Resolver” class= "org. springframework. ws. soap. server. endpoint. Soap. Fault. Mapping. Exception. Resolver"> <property name="default. Fault" value="RECEIVER, Server error" /> <property name="exception. Mappings"> <props> <prop key="org. springframework. oxm. Unmarshalling. Exception"> SENDER, Invalid request</prop> <prop key="org. springframework. oxm. Validation. Failure. Exception"> SENDER, Invalid request</prop> </props> </property> </bean>
Loose-end: Service location • The service location is hard-coded in the WSDL? – What if the service is deployed somewhere other than localhost? – Simple. Wsdl 11 Definition serves WSDL, transforming locations to match request’s server and context. <bean id="poker" class= "org. springframework. wsdl 11. Simple. Wsdl 11 Definition"> <property name="wsdl" value="/Poker. Service. wsdl"/> </bean>
Or…Dynamic. Wsdl 11 Definition • Dynamic. Wsdl 11 Definition automatically generates WSDL from the message XSD <bean id="poker" class= "org. springframework. wsdl 11. Dynamic. Wsdl 11 Definition"> <property name="builder"> <bean class="o. sf. wsdl 11. builder. Xsd. Based. Soap 11 Wsdl 4 j. Definition. Builder"> <property name="schema" value="/Poker. Types. xsd"/> <property name="port. Type. Name" value="Poker"/> <property name="location. Uri" value="http: //localhost: 8080/Poker-WS/services"/> </bean> </property> </bean>
Consuming Spring-WS services
A word about writing clients • Proxy-based client APIs typically won’t work with Spring-WS – Proxies are method-centric and very RPCish in nature. • What does work is… – WSDL 2 Java-generated client stubs – Anything that can wrap supplied XML in a SOAP wrapper – Spring-WS client-side templates
Using Spring-WS’ client API • Template-based – Much like Spring JDBC, Spring JMS, etc • Choices to make: – To marshal or not to marshal? – Basic template or gateway support?
Spring-WS Client API • Web. Service. Template - Centerpiece of Spring-WS client API (similar in concept to Spring’s JDBC Template) • Message factory - Produces message • Message sender - Sends message • (Un)Marshaler (optional)- Converts XML to/from POJO
Spring-WS Client API
Simple template-based client package com. springinaction. ws. client; import java. io. IOException; import org. jdom. Document; import org. jdom. Element; import org. jdom. Namespace; import org. jdom. transform. JDOMResult; import org. jdom. transform. JDOMSource; import org. springframework. ws. client. core. Web. Service. Template; import com. springinaction. poker. Card; import com. springinaction. poker. Poker. Hand. Type; public class Template. Based. Poker. Client implements Poker. Client { public Poker. Hand. Type evaluate. Hand(Card[] cards) throws IOException { // DETAILS OF THIS METHOD ARE ON THE NEXT SLIDE!!! } // INJECTED private Web. Service. Template web. Service. Template; public void set. Web. Service. Template(Web. Service. Template web. Service. Template) { this. web. Service. Template = web. Service. Template; } }
Simple template-based client (2) public Poker. Hand. Type evaluate. Hand(Card[] cards) throws IOException { Element request. Element = new Element("Evaluate. Hand. Request"); Namespace ns = Namespace. get. Namespace( "http: //www. springinaction. com/poker/schemas"); request. Element. set. Namespace(ns); Document doc = new Document(request. Element); for (int i = 0; i < cards. length; i++) { Element card. Element = new Element("card"); Element suit. Element = new Element("suit"); suit. Element. set. Text(cards[i]. get. Suit(). to. String()); Element face. Element = new Element("face"); face. Element. set. Text(cards[i]. get. Face(). to. String()); card. Element. add. Content(suit. Element); card. Element. add. Content(face. Element); doc. get. Root. Element(). add. Content(card. Element); } JDOMResult result = new JDOMResult(); web. Service. Template. send. Source. And. Receive. To. Result(new JDOMSource(doc), result); Document result. Document = result. get. Document(); Element response. Element = result. Document. get. Root. Element(); Element hand. Name. Element = response. Element. get. Child("hand. Name", ns); return Poker. Hand. Type. value. Of(hand. Name. Element. get. Text()); } Construct request message Send message Parse result
Simple template-based client (3) <bean id="web. Service. Template" class="org. springframework. ws. client. core. Web. Service. Template"> <property name="default. Uri" value="http: //localhost: 8080/Poker-WS/" /> </bean> <bean id="template. Based. Client" class="com. springinaction. ws. client. Template. Based. Poker. Client"> <property name="web. Service. Template" ref="web. Service. Template" /> </bean>
Marshaling template client package com. springinaction. ws. client; import java. io. IOException; import org. springframework. ws. client. core. Web. Service. Template; import com. springinaction. poker. Card; import com. springinaction. poker. Poker. Hand. Type; import com. springinaction. poker. webservice. Evaluate. Hand. Request; import com. springinaction. poker. webservice. Evaluate. Hand. Response; public class Marshalling. Poker. Client implements Poker. Client { public Poker. Hand. Type evaluate. Hand(Card[] cards) throws IOException { Evaluate. Hand. Request request = new Evaluate. Hand. Request(); request. set. Hand(cards); Evaluate. Hand. Response response = (Evaluate. Hand. Response) web. Service. Template. marshal. Send. And. Receive(request); return response. get. Poker. Hand(); Marshal and send } // INJECTED private Web. Service. Template web. Service. Template; public void set. Web. Service. Template(Web. Service. Template web. Service. Template) { this. web. Service. Template = web. Service. Template; } } Template is injected
Marshaling template client (2) <bean id="web. Service. Template" class="org. springframework. ws. client. core. Web. Service. Template"> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> <property name="default. Uri" value="http: //localhost: 8080/Poker-WS/" /> </bean> <bean id="marshaller" class="org. springframework. oxm. castor. Castor. Marshaller"> <property name="mapping. Location" value="classpath: mapping. xml" /> </bean> <bean id="marshalling. Client" class="com. springinaction. ws. client. Marshalling. Poker. Client"> <property name="web. Service. Template" ref="web. Service. Template" /> </bean>
Gateway-based client package com. springinaction. ws. client; import java. io. IOException; import org. springframework. ws. client. core. support. Web. Service. Gateway. Support; import com. springinaction. poker. Card; import com. springinaction. poker. Poker. Hand. Type; import com. springinaction. poker. webservice. Evaluate. Hand. Request; import com. springinaction. poker. webservice. Evaluate. Hand. Response; public class Poker. Client. Gateway extends Web. Service. Gateway. Support implements Poker. Client { public Poker. Hand. Type evaluate. Hand(Card[] cards) throws IOException { Evaluate. Hand. Request request = new Evaluate. Hand. Request(); request. set. Hand(cards); Evaluate. Hand. Response response = (Evaluate. Hand. Response) get. Web. Service. Template(). marshal. Send. And. Receive(request); return response. get. Poker. Hand(); } }
Gateway-based client (2) <bean id="poker. Client. Gateway" class="com. springinaction. ws. client. Poker. Client. Gateway"> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> <property name="default. Uri" value="http: //localhost: 8080/Poker-WS/" /> </bean> <bean id="marshaller" class="org. springframework. oxm. castor. Castor. Marshaller"> <property name="mapping. Location" value="classpath: mapping. xml" /> </bean>
Final thoughts
Creating a Spring-WS service • Create sample messages – Just XML • Create data contract (XSD) – Infer from XML • Create operational contract (WSDL) – Infer from XSD • Create endpoints • Wire it up in Spring
Yeah, but…That’s a lot of <bean>s! • Mostly boilerplate • Can be “pre-declared” in a common Spring configuration file – Include in all service applications • Or rolled into a Maven 2 archetype • Maybe some Spring 2. 0 -style namespaces could simplify matters further.
What I didn’t talk about • • • Spring-WS and WS-Security Sending POX messages RESTful Spring-WS (coming in 1. 1? ) JMS transports (coming in 1. 1) Mail transports (coming in 1. 1) WS-Addressing support (coming in 1. 1)
Recommended reading • Spring-WS homepage: – http: //www. springframework. org/spring-ws • Arjen Poutsma’s web-log – http: //blog. springframework. com/arjen • Spring in Action, 2 E; Chapter 9
Q&A Don’t forget to turn in evals!!! http: //www. springinaction. com craig@habuma. com
9fdf9720fdfd8b5eb778855d97ed9ffd.ppt