• 沒有找到結果。

Developing Web Services with Apache CXF and Axis2

N/A
N/A
Protected

Academic year: 2022

Share "Developing Web Services with Apache CXF and Axis2"

Copied!
262
0
0

加載中.... (立即查看全文)

全文

(1)
(2)
(3)

Services with Apache CXF and

Axis2

By Kent Ka Iok Tong Copyright © 2005-2010

TipTec Development

Publisher: TipTec Development Author's email: [email protected] Book website: http://www.agileskills2.org

Notice: All rights reserved. No part of this publication may be reproduced, stored in a retrieval system or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of the publisher.

(4)

ISBN: 978-99937-929-1-8 Edition: Third edition Jan 2010

(5)
(6)

Foreword

Learn web services and Apache CXF and Axis2 easily

If you'd like to learn how to create web services (in particular, using Apache CXF or Axis2) and make some sense of various standards like JAX-WS, JAX- RS, JAXB, SOAP, WSDL, REST, MTOM, WS-Security, WS-Policy, XML Encryption and XML Signature, then this book is for you. Why?

It has a tutorial style that walks you through in a step-by-step manner.

It is concise. There is no lengthy, abstract description.

Many diagrams are used to show the flow of processing and high level concepts so that you get a whole picture of what's happening.

It contains working code.

The first two chapters are freely available on http://www.agileskills2.org. You can judge it yourself.

Content highlights in this book

This book covers the following topics not commonly found in other books on Java web services:

How to work with both Apache CXF 2.2.x and Axis2 1.5.x using standard API (JAX-WS, JAX-RS) as much as possible.

How to use caching to create scalable RESTful web services.

How to encrypt and sign SOAP messages using Rampart.

How to send user authentication information using Rampart.

How to send and receive binary files using MTOM.

How to unit test web services.

Target audience and prerequisites

This book is suitable for those who would like to learn how to develop web services in Java.

In order to understand what's in the book, you need to know Java and to have

(7)

edited XML files. However, you do NOT need to know the more advanced XML concepts (e.g., XML schema, XML namespace), servlet, Tomcat or PKI.

Acknowledgments

I'd like to thank:

The CXF developers for creating CXF.

The Axis2 developers for creating Axis2.

The WSS4J developers for creating WSS4J.

Anne Thomas Manes, an expert in web services, for reviewing the book (first edition).

Helena Lei for proofreading this book.

Eugenia Chan Peng U for doing book cover and layout design.

(8)

Table of Contents

Foreword...5

Learn web services and Apache CXF and Axis2 easily...5

Content highlights in this book...5

Target audience and prerequisites...5

Acknowledgments...6

Chapter 1 Designing the interface for a simple web service...11

What's in this chapter?...12

Providing cross platform operations across the Internet...12

RPC style web service...13

Document style web service...16

Determining the operation for a document style web service. .19 Port type...20

Binding...21

Port...22

Target namespace...24

WSDL...26

Summary...27

Chapter 2 Implementing a web service...29

What's in this chapter?...30

Installing Eclipse...30

Using a web service library...30

Downloading the jar files easily...31

Installing Apache CXF...33

WSDL file for the web service...36

RPC version of the web service...39

Creating the WSDL file visually...40

Validating the WSDL file...50

Generating the service code...51

Creating a client...57

Controlling the package name...59

Practical significance of the annotations...59

Creating the web service with Apache Axis2...62

Creating a client using Apache Axis2...65

Undeploying a web service from Axis2...67

Summary...67

Chapter 3 Viewing the SOAP messages...69

(9)

What's in this chapter?...70

Seeing the SOAP messages...70

Summary...74

Chapter 4 Accepting multiple parameters...75

What's in this chapter?...76

Splitting the XML element into multiple parameters...76

Using the wrapped style in Axis2...81

Interoperability...82

Summary...82

Chapter 5 Sending and receiving complex data structures...83

What's in this chapter?...84

Product query...84

Sending more data in a message...96

Returning faults...96

Referring to existing XML elements...105

Doing it in Axis2...108

Summary...110

Chapter 6 Sending binary files...111

What's in this chapter?...112

Providing the image of a product...112

Enabling MTOM in the service...119

Doing it in Axis2...120

Interoperability...122

Summary...122

Chapter 7 Invoking lengthy operations...123

What's in this chapter?...124

Invoking a time consuming operation...124

What if you can't modify the WSDL file?...128

Extremely lengthy processing...129

Specifying the reply address...134

Using an asynchronous client in Axis2...136

Summary...137

Chapter 8 Signing and encrypting SOAP messages...139

What's in this chapter?...140

Private key and public key...140

Digital signature...142

Signing and encrypting...143

Certificate and CA...144

Distinguished name...145

(10)

Performance issue with asymmetric encryption...146

Keeping key pair and certificates in Java...146

Generating a key pair...147

Setting up a CA...150

Importing the certificate into the keystore...152

Signing SOAP messages...156

Supporting digital signatures in the web service...164

Encrypting SOAP messages...168

Security issues when performing both signing and encrypting ...173

Sending login information...176

Installing Rampart into Axis2...182

Creating a secure client in Axis2...183

Creating a secure service in Axis2...188

Summary...192

Chapter 9 Creating scalable web services with REST...193

What's in this chapter?...194

Scalability difficulty with SOAP...194

Using a generic proxy...196

Creating a RESTful web service...198

Enabling caching by a proxy...201

Validating the cached response after expiry...203

Using other kinds of versions...209

What if books can be updated at any time?...211

Performing an update...211

Implementing add...213

Implementing delete...217

Listing the reviews on a book...218

Providing the full review text on demand...224

Implementing search...227

Doing it in Axis2...230

Summary...230

Chapter 10 Deploying your services and integrating them with Spring...231

What's in this chapter?...232

Deploying the simple service...232

Installing Tomcat...233

Invoking Spring beans from your implementation object...235

Deploying RESTful web services...237

(11)

Invoking Spring beans from your resource objects...238

Deploying Axis2 web services...240

Using Spring with Axis2...241

Summary...244

Chapter 11 Unit testing your web services...245

What's in this chapter?...246

Difficulties in testing a web service in a container...246

Testing a web service out of container, in isolation...246

Summary...251

References...253

Alphabetical Index...256

(12)

Chapter 1

Chapter 1

Designing the interface for

a simple web service

(13)

What's in this chapter?

In this chapter you'll learn how to design the interface for a simple web service.

Providing cross platform operations across the Internet

Suppose that you'd like to provide a service to the public or to some business partners: They can send you two strings and you will concatenate them and return the string. Of course, in the real world you provide a more useful service.

There are several major requirements: First, the users may be using different languages (Java, C# and etc.) and using different platforms (Windows, Linux and etc.). Your service must be accessible by different languages and platforms. Second, they will call your service across the Internet and there may be firewalls in between. Your service must be able to go through firewalls.

Given these requirements, the best solution is to provide a so-called "web service". For example, you may make a web service accessible on the host www.ttdev.com and accessible as /SimpleService (see the diagram below), so the full URL is http://www.ttdev.com/SimpleService. This is called the "endpoint"

of the web service. Your web service may support one or more operations. One operation may be named "concat":

However, you hope to provide a globally unique name to each operation so that you can have your "concat" operation while another person may have his

A web server at http://www.ttdev.com

A web service at the path /SimpleService

Name: concat An operation

Name: ...

An operation

Internet

Combined together, the full path of the web service is

http://www.ttdev.com/SimpleService.

(14)

"concat" operation. So, in addition to the name, you may declare that the

"concat" name above is in the "namespace" of http://ttdev.com/ss (see the diagram below). A namespace is just like a Java package, but it is not in a dot format like com.ttdev.foo; it is in the format of a URL. So, the full name of the operation will be "concat" in namespace http://ttdev.com/ss. The name "concat"

is called the "local name". The full name is called a "QName (qualified name)":

You may wonder what this http://ttdev.com/ss namespace means. The answer is that it has no particular meaning. Even though it is a URL, it does NOT mean that you can use a browser to access this URL to get a web page (if you do, you may get a file not found error). The only important thing is that it must be globally unique. As I have registered the domain name ttdev.com, it must be globally unique.

Note that the namespace is a completely different concept from the endpoint.

The endpoint really is the location, while the namespace is just a unique id. I could easily move the web service to another web server and thus it will have a different endpoint, but the namespaces of its operations will remain unchanged.

RPC style web service

Your concat operation may take two parameters. One is named "s1" and is a string. The other is named "s2" and is also a string. The return value is also a string:

A web server at http://www.ttdev.com

A web service at the path /SimpleService

Local name: concat

Namespace: http://ttdev.com/ss An operation

An operation

Internet

Local name: ...

Namespace: ...

(15)

However, what does the above "string" type mean? Is it the Java string type?

No, you can't say that because it must be language neutral. Fortunately, the XML schema specification defines some basic data types including a string type. Each of these data types has a QName as its id. For example:

Data type Local name namespace

string string http://www.w3.org/2001/XMLSchema

integer int http://www.w3.org/2001/XMLSchema

... ... ...

So, the interface of your operation should be written as:

Actually, in web services, a method call is called an "input message" and a parameter is called a "part". The return value is called an "output message" and may contain multiple parts. So, it is more correct to say:

When someone calls this operation, he can send you an XML element as the input message like:

An operation

Local name: concat

Namespace: http://ttdev.com/ss Parameters:

s1: string s2: string Return:

string

An operation

Local name: concat

Namespace: http://ttdev.com/ss Parameters:

s1: string in http://www.w3.org/2001/XMLSchema s2: string in http://www.w3.org/2001/XMLSchema Return:

string in http://www.w3.org/2001/XMLSchema

An operation

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: s1

Type: string in http://www.w3.org/2001/XMLSchema Part 2:

Name: s2

Type: string in http://www.w3.org/2001/XMLSchema Output message:

Part 1:

Name: return

Type: string in http://www.w3.org/2001/XMLSchema

(16)

When you return, the output message may be like:

This kind of web service is called "RPC style" web service (RPC stands for

<foo:concat xmlns:foo="http://ttdev.com/ss">

<s1>abc</s1>

<s2>123</s2>

</foo:concat>

foo is a "namespace prefix" representing the http://ttdev.com/ss in the rest of this element including its children.

The QName of this XML element is exactly that of the operation he is trying to call

There is a child element for each part. Each child element has the same name as that part ("s1" in this case).

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: s1

Type: string in http://www.w3.org/2001/XMLSchema Part 2:

Name: s2

Type: string in http://www.w3.org/2001/XMLSchema Output message:

Part 1:

Name: return

Type: string in http://www.w3.org/2001/XMLSchema

<foo:concat xmlns:foo="http://ttdev.com/ss">

<return>abc123</return>

</foo:concat>

The QName of this XML element is exactly that of the operation being called

Each child element has the same name as a part in the output message ("return" in this case).

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: s1

Type: string in http://www.w3.org/2001/XMLSchema Part 2:

Name: s2

Type: string in http://www.w3.org/2001/XMLSchema Output message:

Part 1:

Name: return

Type: string in http://www.w3.org/2001/XMLSchema

(17)

"Remote Procedure Call"). That is, the operation QName and the names of the parts are used to create the input and output messages.

Document style web service

The above way is not the only way you design the interface of your web service.

For example, you may say that its input message only contains a single part (see the diagram below) which is an element defined in a schema. In that schema, it is defined as an element named "concatRequest" that contains two child elements <s1> and <s2>:

Note that the schema is included in the interface of your web service:

An operation

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: concatRequest Element:

Output message:

...

<xsd:schema

targetNamespace="http://ttdev.com/ss"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="concatRequest">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="s1" type="xsd:string"/>

<xsd:element name="s2" type="xsd:string"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:schema>

<foo:concatRequest xmlns:foo="http://ttdev.com/ss">

<s1>abc</s1>

<s2>123</s2>

</foo:concatRequest>

<concatRequest> is a complext type because it contains child elements

It contains a sequence of child elements. The first is an <s1>

element, then is an

<s2> element.

The elements defined here are put into this namespace

(18)

As you can see above, a part may be declared as a particular element (<concatRequest> defined in your schema) or as any element having a particular type (string defined in XML schema specification). In either case it is identified using a QName.

When someone calls this operation, he will send you a <concatRequest>

element as the input message like:

Similarly, for the output message, you may specify that it contains only one part and that part is a <concatResponse> element:

<foo:concatRequest xmlns:foo="http://ttdev.com/ss">

<s1>abc</s1>

<s2>123</s2>

</foo:concatRequest>

An operation

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: concatRequest

Element: concatRequest in http://ttdev.com/ss Output message:

...

<xsd:schema

targetNamespace="http://ttdev.com/ss"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="concatRequest">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="s1" type="xsd:string"/>

<xsd:element name="s2" type="xsd:string"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:schema>

A schema A web service

(19)

This kind of web service is called "document style" web service. That is, the input message will contain a single part only which is well defined in a schema.

The same is true of the output message.

If you go back to check the input message for the RPC style service, it should be revised as:

An operation

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: concatRequest

Element: concatRequest in http://ttdev.com/ss Output message:

Part 1:

Name: concatResponse

Element: concatResponse in http://ttdev.com/ss

<xsd:schema

targetNamespace="http://ttdev.com/ss"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="concatRequest">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="s1" type="xsd:string"/>

<xsd:element name="s2" type="xsd:string"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

<xsd:element name="concatResponse" type="xsd:string"/>

</xsd:schema>

A schema A web service

<foo:concatResponse

xmlns:foo="http://ttdev.com/ss">abc123</foo:concatResponse>

This <concatResponse> element is a "simple type element", meaning that it has no attribute and can't have elements in its body (so only simple string or number in its body).

(20)

This is because <foo:concat>, <s1> and <s2> are not defined in any schema and therefore you must explicitly state the XML element types of the content of

<s1> and <s2>.

Now, let's compare the input messages of the RPC style web service and the document style web service:

RPC style

Document style

Not much difference, right? The significant difference is that the former can't be validated with a schema while the latter can. Therefore, document style web service is becoming the dominant style. According to an organization called

"WS-I (web services interoperability organization)", you should use document style web services only.

Determining the operation for a document style web service

To call an operation in a document style web service, one will send the single part of the input message only. Note that it does NOT send the operation name in any way. Then if there are more than one operations in the web service (see the diagram below), how can it determine which one is being called? In that

<foo:concat>

xmlns:foo="http://ttdev.com/ss"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance">

<s1 xsi:type="xsd:string">abc</s1>

<s2 xsi:type="xsd:string">123</s2>

</foo:concat>

This attribute is used to explicitly state the XML data type of the body of an element ("abc" here). This is useful when the element (<s1>) itself is not defined in a schema. This "type" attribute is defined in the http://www.w3.org/2001/XMLSchema-Instance namespace, so you need to introduce a prefix for it:

<foo:concatRequest xmlns:foo="http://ttdev.com/ss">

<s1>abc</s1>

<s2>123</s2>

</foo:concatRequest>

<foo:concat>

xmlns:foo="http://ttdev.com/ss"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance">

<s1 xsi:type="xsd:string">abc</s1>

<s2 xsi:type="xsd:string">123</s2>

</foo:concat>

(21)

case, it will see if the input message is a <concatRequest> or a

<someElement> to determine. What if both take a <someElement>? Then it is an error and it won't work:

Port type

Actually, a web service doesn't directly contain a list of operations. Instead (see the diagram below), operations are grouped into one or more "port types". A port type is like a Java class and each operation in it is like a static method. For example, in the web service above, you could have a port type named

"stringUtil" containing operations for strings, while having another port type named "dateUtil" containing operations for dates. The name of a port type must also be a QName:

An operation Local name: bar

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: barRequest

Element: someElement in http://ttdev.com/ss Output message:

...

...

A schema A web service

An operation

Local name: concat

Namespace: http://ttdev.com/ss Input message:

Part 1:

Name: concatRequest

Element: concatRequest in http://ttdev.com/ss Output message:

...

(22)

Binding

Actually, a port type may allow you to access it using different message formats.

The message format that you have seen is called the "Simple Object Access Protocol (SOAP)" format. It is possible that, say, the stringUtil port type may also support a plain text format:

In addition to the message format, a port type may allow the message to be carried (transported) in an HTTP POST request or in an email. Each supported combination is called a "binding":

An operation Local name: bar

Namespace: http://ttdev.com/ss ...

...

A schema A web service

An operation

Local name: concat

Namespace: http://ttdev.com/ss ...

A port type

Local name: stringUtil Namespace: http://ttdev.com/ss

A port type

Local name: dateUtil

Namespace: http://ttdev.com/ss An operation

Local name: ...

Namespace: http://ttdev.com/ss ...

An operation Local name: ...

Namespace: http://ttdev.com/ss ...

concat(s1='abc', s2='123')

(23)

What bindings should your port type support? SOAP+HTTP is the most common combination. So, you should probably use this binding in practice.

Port

Suppose that there are just too many people using your web service, you decide to make it available on more than one computers. For example (see the diagram below), you may deploy the above binding 1 on computers c1, c2 and c3 and deploy binding 2 on c3. In that case it is said that you have four ports.

Three ports are using binding 1 and one using binding 2:

concat ...

Port type: stringUtil

POST /DWSAA/test/ts.php

<concatRequest>

<s1>abc</s1>

<s2>123</s2>

</concatRequest>

FROM: [email protected] TO: ...

concat(s1='abc', s2='123') Name: binding1

Port type:

Format: SOAP Transport: HTTP Binding

Name: binding2 Port type:

Format: TEXT Transport: SMTP Binding

...

A schema A web service

For example For example

(24)

Note that it does NOT mean that the requests received by these three computers will be forwarded to a computer hiding behind for processing.

Instead, it means that there is some software implementing the port type installed on these three computers. There is no requirement that the same piece of software is installed onto the different computers. For example, on c1, port 1 may be written in Java, while on c2, port 2 may be written in C#. The important point is that they both support the operations specified in port type stringUtil and the message format and transport specified in the binding 1. Port 4 must also implement the same operations too (same port type) but the message format and transport are different.

To tell others about this arrangement, you include these ports in the interface of the web service:

concat ...

Port type: stringUtil

Name: binding1 Port type:

Format: SOAP Transport: HTTP Binding

Name: binding2 Port type:

Format: TEXT Transport: SMTP Binding

...

A schema A web service

c1 c2 c3

Port 1 Port 2 Port 3

Port 4 Deployed to

Deployed to Deployed to

Deployed to

(25)

Target namespace

You have been using the same namespace for the operation names, port type names and etc. in this web service. Do they have to be in the same namespace? By default, this is the case: There is a single namespace for a web service to put the names into. This is called the "target namespace" for the web service:

concat ...

Port type: stringUtil

Name: binding1 Port type:

Format: SOAP Transport: HTTP Binding

Name: binding2 Port type:

Format: TEXT Transport: SMTP Binding

...

A schema A web service

Name: port1 Binding:

Endpoint: ...

Port

Name: port2 Binding:

Endpoint: ...

Port

Name: port3 Binding:

Endpoint: ...

Port

Name: port4 Binding:

Endpoint: ...

Port

(26)

You've been using http://ttdev.com/ss as the target namespace. Is it a good choice? Basically a namespace is good as long as it is globally unique. So this one should be good. However, people may try to download a web page from this URL. When it doesn't work, they may suspect that your web service is out of order. To avoid this confusion, you may use something called URN (Uniform Resource Name) as the namespace.

A namespace must be a URI. URI stands for Uniform Resource Identifier.

There are two kinds of URI. One is URL such as http://www.foo.com/bar. The other is URN. A URN takes the format of urn:<some-object-type>:<some- object-id>. For example, International ISBN Agency has made a request to the IANA (International Assigned Numbers Association) that it would like to manage the object type named "isbn". After the request has been approved, the International ISBN Agency can declare that a URN urn:isbn:1-23-456789-0 will identify a book whose ISBN is 1-23-456789-0. It can determine the meaning of the object id without consulting IANA at all.

Similarly, you may submit a request to IANA to register your Internet domain name such as foo.com as the object type. Then on approval you can use URNs like urn:foo.com:xyz to identify an object xyz in your company. What xyz means or its format is completely up to you to decide. For example, you may use urn:foo.com:product:123 (so xyz is product:123) to mean the product #123 produced by your company, or urn:foo.com:patent/123 (so xyz is patent/123) to mean a patent coded 123 in your company.

concat ...

Port type: stringUtil

Name: binding1 Port type:

Format: SOAP Transport: HTTP Binding

Name: binding2 Port type:

Format: TEXT Transport: SMTP Binding

...

A schema A web service

Name: port1 Binding:

Endpoint: ...

Port

Name: port2 Binding:

Endpoint: ...

Port

Name: port3 Binding:

Endpoint: ...

Port

Name: port4 Binding:

Endpoint: ...

Port Target namespace: http://ttdev.com/ss

...

(27)

However, this will create a lot of workload on you and on IANA (one registration per company!). As you have already registered the domain name foo.com, it is unlikely that someone will use it in their URN's. So, you may want to go ahead and use foo.com, or, as many people do, foo-com as the object type without registration with IANA and hope that there won't be any collision.

An XML namespace must be a URI. You can use a URL or a URN. Functionally there is no difference at all. For example, you may use say urn:ttdev.com:ss as the target namespace for your web service instead of http://ttdev.com/ss without changing any functionality.

By the way, if you are going to lookup references on URN, do NOT try to find terms like "object type" or "object id". The official terms are:

WSDL

By now you have finished designing the interface for your web service:

urn:isbn:1-23-456789-0

URN namespace identifier (NID). This namespace is NOT the namespace in XML!

URN namespace specific string (NSS)

(28)

It fully describes your web service. This description language (terms and concepts) is called "WSDL (Web Services Description Language)".

Summary

A web service is platform neutral, language neutral and can be accessed across the Internet.

A web service has one or more ports. Each port is a binding deployed at a certain network address (endpoint). A binding is a port type using a particular message format and a particular transport protocol. A port type contains one or more operations. An operation has an input message and an output message.

Each message has one or more parts. Each part is either a certain element defined in the schema of the web service, or any element belonging to a certain element type in that schema. All this information is fully described in WSDL.

To call a RPC style web service, one will create an XML element with the name of the operation and a child element for each of its input message part. To call a document style web service, one will just send the one and only part of its input message. Because the XML element used to call a RPC style web service is not defined in any schema, for better interoperability, one should create document style web services.

The web service, and each of its ports, bindings, port types and operations, has

concat ...

Port type: stringUtil

Name: binding1 Port type:

Format: SOAP Transport: HTTP Binding

Name: binding2 Port type:

Format: TEXT Transport: SMTP Binding

...

A schema A web service

Name: port1 Binding:

Endpoint: ...

Port

Name: port2 Binding:

Endpoint: ...

Port

Name: port3 Binding:

Endpoint: ...

Port

Name: port4 Binding:

Endpoint: ...

Port Target namespace: http://ttdev.com/ss

...

(29)

a QName uniquely identifying it. A QName has a local part and an XML namespace. An XML namespace is a URI that is globally unique. By default the names of all these components are put into the target namespace of the web service.

There are two kinds of URI: URL and URN. URN takes the form of urn:<NID>:<NSS>. You can use either as an XML namespace. The only difference is that a URL is suggesting that it is the location of an object, while a URN is purely an id of the object.

(30)

Chapter 2

Chapter 2

Implementing a web service

(31)

What's in this chapter?

In this chapter you'll learn how to implement the web service interface designed in the previous chapter.

Installing Eclipse

You need to make sure you have a recent version Eclipse installed (in this book v3.4 is used) and it is the bundle for Java EE (the bundle for Java SE is NOT enough). If not, go to http://www.eclipse.org to download the Eclipse IDE for Java EE Developers (e.g., eclipse-jee-ganymede-SR2-win32.zip). Unzip it into a folder such as c:\eclipse. To see if it's working, run c:\eclipse\eclipse.exe and make sure you can switch to the Java EE perspective:

Using a web service library

How to create a web service? You can easily create a Java class that can concatenate two strings together, like:

Class Foo {

String m1(String s1, String s2) { } ...

}

However, when a client calls your web service, it will send a message (probably a SOAP message) as shown below. It will be great if there is a converter that can convert the incoming SOAP message into a Java object and then call a Java object you provide:

(32)

The good news is, there are libraries available that can act as such a converter.

The most popular ones are Apache CXF, Apache Axis2 and Metro from Sun Microsystems.

Downloading the jar files easily

You're about to download Apache CXF and add its jar files to your Eclipse project. However, CXF itself needs jar files from other 3rd parties so you must download those too.

To do that easily, you can use the Maven2 Eclipse plugin. Once it is installed, you can tell it download CXF. Then it will go to the Internet to download CXF and add its jar files to the classpath of your project. The cool thing is, it will download all jar files needed by CXF automatically. To install this Maven2 Eclipse plugin, choose Help | Install New Software. You'll see:

Converter 1: A request (a SOAP message) comes in.

Class Foo {

ConcatResponse m1(ConcatRequest) { ...

} }

3: Call a particular method on a class that you provided and pass that ConcatRequest object to as an argument.

<foo:concatRequest>

<s1>abc</s1>

<s2>123</s2>

</foo:concatRequest> 2: Convert the message into a Java object.

s1: "abc"

s2: "123"

Java Class: ConcatRequest

(33)

Click Add and define a new update site and input the data as shown below. The name is not really important; the location URL is:

Then choose this m2eclipse site and the available packages on that site will be listed (see below). Choose "Maven integration for Eclipse" and "Maven integration for WTP" as shown below:

(34)

Then continue until you finish the installation.

Installing Apache CXF

Note: Even if you are going to use Axis2, you should still follow the steps for working with CXF as many common and important concepts are introduced in the process.

Next, create a Java project as usual in Eclipse. Let's name it SimpleService.

Then right click the project and choose Maven | Enable Dependency Management. You'll see:

(35)

Accept the defaults and click Finish. Then, right click the project and choose Maven | Add Dependency. Enter the "cxf" as the keyword to search for the packages (see below). Then choose the cxf-bundle package:

(36)

Make sure you're connected to the Internet. Then the Maven2 plugin will download all the jar files in Apache CXF and those it needs from a central repository. However, it will fail to download some jar files as they aren't in the central repository. To solve the problem, modify the pom.xml file in the root of your project:

<project ...>

<modelVersion>4.0.0</modelVersion>

<groupId>SimpleService</groupId>

<artifactId>SimpleService</artifactId>

<version>0.0.1-SNAPSHOT</version>

<dependencies>

<dependency>

<groupId>org.apache.cxf</groupId>

<artifactId>cxf-bundle</artifactId>

<version>2.2.5</version>

</dependency>

</dependencies>

<repositories>

<repository>

<id>apache-incubating</id>

<name>Apache Incubating Repository</name>

<url>http://people.apache.org/repo/m2-incubating-repository/</url>

</repository>

</repositories>

</project>

What you did was to tell the Maven2 plugin to look for packages in an additional repository (http://people.apache.org/repo/m2-incubating-repository). Save the file and the plugin will finish the download shortly.

This is called the "artifact ID"

of the package. It identifies a specific product created by that organization. Think of it as the class name in Java.

The keyword This is called the "group

ID" of the package. It presents the

organization that created the package. It is like package name in Java.

(37)

WSDL file for the web service

Suppose that you'd like to create a web service described in the previous chapter:

To write it using the real WSDL language, it should be:

Name: ...

Port type:

Format: SOAP Transport: HTTP

Binding Name: ...

Operations:

Name: concat Input msg:

Part 1:

Name: concatRequest

Element: concatRequest element as defined in the schema Output msg:

Part 1:

Name: concatRequest

Element: concatResponse element as defined in the schema Port type

Name: ...

Binding:

Endpoint: ...

Port

<xsd:schema

targetNamespace="http://ttdev.com/ss xmlns:tns="http://ttdev.com/ss"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="concatRequest">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="s1" type="xsd:string"/>

<xsd:element name="s2" type="xsd:string"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

<xsd:element name="concatResponse" type="xsd:string"/>

</xsd:schema>

Schema

Target namespace: http://ttdev.com/ss

(38)

This defines the schema and the port type. To define the binding and the port:

<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

xmlns:tns="http://ttdev.com/ss"

xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SimpleService"

targetNamespace="http://ttdev.com/ss">

<wsdl:types>

<xsd:schema

targetNamespace="http://ttdev.com/ss"

xmlns:tns="http://ttdev.com/ss">

<xsd:element name="concatRequest">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="s1" type="xsd:string"/>

<xsd:element name="s2" type="xsd:string"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

<xsd:element name="concatResponse" type="xsd:string"/>

</xsd:schema>

</wsdl:types>

<wsdl:message name="concatRequest">

<wsdl:part name="concatRequest" element="tns:concatRequest" />

</wsdl:message>

<wsdl:message name="concatResponse">

<wsdl:part name="concatResponse" element="tns:concatResponse" />

</wsdl:message>

<wsdl:portType name="SimpleService">

<wsdl:operation name="concat">

<wsdl:input message="tns:concatRequest" />

<wsdl:output message="tns:concatResponse" />

</wsdl:operation>

</wsdl:portType>

...

</wsdl:definitions>

Put the schema into the <types>

section

The input message contains a single part.

The name of the part is unimportant.

concat operation The names of the port types, operations, bindings and ports will be put into this namespace

All the elements and element types defined in the schema will be put into this namespace

The output message contains a single part.

The name of the part is unimportant.

(39)

In fact, in a SOAP binding, you need to specify some more details:

<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

xmlns:tns="http://ttdev.com/ss"

xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SimpleService"

targetNamespace="http://ttdev.com/ss">

<wsdl:types>

...

</wsdl:types>

<wsdl:message name="concatRequest">

<wsdl:part name="concatRequest" element="tns:concatRequest" />

</wsdl:message>

<wsdl:message name="concatResponse">

<wsdl:part name="concatResponse" element="tns:concatResponse" />

</wsdl:message>

<wsdl:portType name="SimpleService">

<wsdl:operation name="concat">

<wsdl:input message="tns:concatRequest" />

<wsdl:output message="tns:concatResponse" />

</wsdl:operation>

</wsdl:portType>

<wsdl:binding name="SimpleServiceSOAP" type="tns:SimpleService">

<soap:binding style="document"

transport="http://schemas.xmlsoap.org/soap/http" />

</wsdl:binding>

<wsdl:service name="SimpleService">

<wsdl:port binding="tns:SimpleServiceSOAP"

name="p1">

<soap:address

location="http://localhost:8080/ss/p1" />

</wsdl:port>

</wsdl:service>

</wsdl:definitions>

This binding implements this port type

The port

The endpoint of the port

The port supports this binding

You'll deploy it on your own computer.

The binding uses the SOAP format and HTTP transport. SOAP supports RPC and document styles.

Here you use the document style.

You can use anything as the path, but it is a good convention to include the service name (here a shorthand "ss" is used) and the port name so that, for example, you could deploy another port p2 for the same service on the same host (/ss/p2) or deploy a p1 port for another service (/s2/p1).

(40)

RPC version of the web service

If the web service was a RPC style service, then the WSDL file would be like:

<wsdl:definitions ...>

...

<wsdl:message name="concatRequest">

<wsdl:part name="concatRequest" element="tns:concatRequest" />

</wsdl:message>

<wsdl:message name="concatResponse">

<wsdl:part name="concatResponse" element="tns:concatResponse " />

</wsdl:message>

...

<wsdl:binding name="SimpleServiceSOAP" type="tns:SimpleService">

<soap:binding style="document"

transport="http://schemas.xmlsoap.org/soap/http" />

<wsdl:operation name="concat">

<soap:operation

soapAction="http://ttdev.com/ss/concat" />

<wsdl:input>

<soap:body parts="concatRequest" use="literal" />

</wsdl:input>

<wsdl:output>

<soap:body parts="concatResponse" use="literal" />

</wsdl:output>

</wsdl:operation>

</wsdl:binding>

...

</wsdl:definitions>

The soap action is used to tell the HTTP server that it is a SOAP message and its purpose. It is up to the HTTP server to interpret the actual meaning. In your case, it is useless because Axis will handle the SOAP message, not Tomcat.

Literal means the message parts are already in XML. No need to convert (encode) it further.

Put the input message parts listed here (just one in this case: the

<concatRequest> element) into the body of the SOAP request message:

<soap-env:Envelope

xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">

<soap-env:Header>

<...>

</...>

<...>

</...>

</soap-env:Header>

<soap-env:Body>

<foo:concatRequest...>

<s1>...</s1>

<s2>...</s2>

</foo:concatRequest>

<...>

</...>

</soap-env:Body>

</soap-env:Envelope>

The <Header> is optional

It must have a <Body>. The real message content is put there.

This is called a "body entry" or "body element"

Another body element. However, in most cases you should have a single message part and thus a single body element only.

Otherwise interoperability will be affected.

A "header entry" or "header element". It is used like email headers.

Another header element A SOAP message is like a mail. The outermost is an <Envelope>. The main content is in a <Body>. One or more headers can be put into

<Header>.

The output message parts listed here will be put into the body of the SOAP response message.

(41)

As RPC style is not good for interoperability, you'll continue to use the document style version.

Creating the WSDL file visually

It may be error prone to manually create such a WSDL file. Instead, you may

<wsdl:definitions ...>

<wsdl:types>

<xsd:schema ...>

<xsd:element name="concatRequest">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="s1" type="xsd:string"/>

<xsd:element name="s2" type="xsd:string"/>

</xsd:sequence>

</xsd:complexType>

</xsd:element>

<xsd:element name="concatResponse" type="xsd:string"/>

</xsd:schema>

<wsdl:types/>

<wsdl:message name="concatRequest">

<wsdl:part name="s1" type="xsd:string" />

<wsdl:part name="s2" type="xsd:string" />

</wsdl:message>

<wsdl:message name="concatResponse">

<wsdl:part name="return" type="xsd:string" />

</wsdl:message>

<wsdl:portType name="SimpleService">

<wsdl:operation name="concat">

<wsdl:input message="tns:concatRequest" />

<wsdl:output message="tns:concatResponse" />

</wsdl:operation>

</wsdl:portType>

<wsdl:binding name="SimpleServiceSOAP" type="tns:SimpleService">

<soap:binding style="rpc"

transport="http://schemas.xmlsoap.org/soap/http" />

<wsdl:operation name="concat">

<soap:operation

soapAction="http://ttdev.com/ss/concat" />

<wsdl:input>

<soap:body parts="s1 s2" use="literal" />

</wsdl:input>

<wsdl:output>

<soap:body parts="return" use="literal" />

</wsdl:output>

</wsdl:operation>

</wsdl:binding>

...

</wsdl:definitions>

Don't need these any more

The input message has two parts.

Each part is of element type xsd:string (not elements).

RPC style

Two message parts are listed. So, they will be included into the <Body> (but not directly). As it is a RPC style service, the caller must create an element with the QName of the operation and then add each message part listed here as a child element. So it should still have a single element in the <Body>:

<soap-env:Envelope

xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">

<soap-env:Header>

...

</soap-env:Header>

<soap-env:Body>

<foo:concat ...>

<s1>...</s1>

<s2>...</s2>

</foo:concat>

</soap-env:Body>

</soap-env:Envelope>

No schema to validate it

The output message has one part.

It is of element type xsd:string (not elements).

(42)

use the Eclipse to do it. First, create a new folder src/main/resources in the root of your project. Next, right click on that folder and choose New | Other and then Web Services | WSDL:

If you don't see this option, it means that you haven't installed the Java EE version of Eclipse. If it is working, click Next and enter SimpleService.wsdl as the filename:

(43)

Click Next. Then input as shown below:

(44)

Click Finish. Then you will see something like:

Target namespace for the WSDL file

Remember, you're using the document style (the only input message part is the whole message) and literal use for that part.

Use the SOAP format

(45)

This is the WSDL code. To edit it visually, click the Design tab at the bottom of the editor window. Then you'll see:

Double click on the endpoint to change it to http://localhost:8080/ss/p1:

The service

A port. A service may contain one

or more ports. Endpoint of the port

A binding (SOAP

and HTTP) Port type

An operation. A port type may contain one or more operations.

Part name XML element name or element type for that part

(46)

Double click on the name of the port and change it to "p1":

Double click on the name of the operation and change it to "concat":

For the moment, the input part is an <concat> element. You'd like to change it to <concatRequest>. But for now, put the cursor on the arrow to its right first.

The arrow will turn into blue color. Wait a couple of seconds then a preview window will appear showing the definition of the <concat> element:

Set the name of the operation.

The XML element names for the input and output parts will be changed automatically:

(47)

Clicking anywhere else will make that preview window disappear. To edit the schema definition, click on the blue arrow. A new editor window will appear:

To edit it visually, click the Design tab at the bottom, you'll see:

(48)

Double click on "in" and change it to "s1":

Right click it and choose Insert Element | After and set the name to "s2":

By default the type is already set to string. If you wanted it to be, say, an int instead, you would double click on the type and it would become a combo box and then you could choose "int":

If you wanted s2 to appear before s1 in the sequence, you could drag it and drop it before s1:

But for now, make sure it is s1 first and then s2. Next, right click on the

<concat> element and choose Refactor | Rename, then change its name to concatRequest:

(49)

You're done with the <concatRequest> element. Now return to the WSDL editor to work on the response message. For the moment, the <concatResponse> is like:

That is, it is an element that contains a sequence of <out> element:

<foo:concatResponse>

<foo:out>abc</foo:out>

</foo:concatResponse>

However, in your design, the response is simple type element, not a complex type element:

(50)

To do that, go into the schema editor to edit the <concatResponse> element:

Right click it and choose Set Type | Browse:

Choose "string":

Then it will be like:

You can also type "s" so that only those starting with

"s" will be listed

(51)

That's it. To review the whole schema, click on the icon at the upper left corner:

Then you'll see:

This looks fine. Now, save the file.

Validating the WSDL file

The next step is to validate the WSDL file to make sure it conforms to the various web services standards. To do that, right click the SimpleService.wsdl file in Eclipse and choose Validate. If there were anything wrong, they would be reported in the Problems window. For example, here I had introduced an error into the file:

Click it to see the whole schema

(52)

Now, correct the error and validate it again. It should pass without any errors.

Generating the service code

As mentioned before, a web service library such as Apache CXF can create a converter to convert an incoming SOAP message into a Java object to be passed as a method argument. To generate this code, create a src/main/java folder and then right click the project root and choose Maven | Update Project Configuration. This will turn that java folder into a source folder in Eclipse (so the Java class files in it will be compiled).

Next, in that src/main/java folder, create a Java class as shown below:

The binding names no longer match.

(53)

Run it as a Java application. If you receive an error such as java.lang.AbstractMethodError in a class somewhere inside the org.apache.xerces package, it means that the Xerces library brought in by Apache CXF is too old for your computer (which has its own . To fix this problem, add a new Maven dependency:

Group ID xerces

Artifact ID xercesImpl

Version A recent version such as 2.9.1

After running it successfully, right click the project and choose Refresh. You should see a com.ttdev.ss package has been created and that there are some files in it:

package com.ttdev;

import org.apache.cxf.tools.wsdlto.WSDLToJava;

public class CodeGenerator {

public static void main(String[] args) { WSDLToJava.main(new String[] {

"-server",

"-d", "src/main/java",

"src/main/resources/SimpleService.wsdl" });

System.out.println("Done!");

} }

This class comes with Apache CXF. It can be used to convert a WSDL file into Java code.

It can be run as a Java application by a user. Here, call its main() method from your own program.

Generate Java code for the server (i.e., the service). If you specify -client, it will generate Java code for the client.

Tell it to put the files into the src/main/java folder. This is a relative path. When this program is run in Eclipse, the current folder will be the project root. So this relative path is correct.

The most important part: Tell the it the path to the WSDL file so that it can read that file. Again, this path is a relative path from the project root.

參考文獻

相關文件

The New Academic Structure for Senior Secondary Education and Higher Education – “334” Web Bulletin, Education Bureau, Hong

(A) File Transfer Protocol, FTP (B) Electronic Mail, E-Mail (C) World Wide Web, WWW (D) Word Wide Web,

 There are two types of background pages: persi stent background pages, and

RMI,及 DCOM 這些以專屬 binary 格式傳送資料所不及之處,那 就是對程式語言、作業平台的獨立性--由於是純文字 XML 格 式,

(Web Form、Web Service Mobile Form) Windows Form ADO.NET、XML. Base Class

¾ To fetch a Web page, browser establishes TCP connection to the machine where the page is and sends a message over the connection asking for the

• A simple look at a website can reveal many potential web accessibility issues for persons with disabilities.  Can the content be

• Information retrieval : Implementing and Evaluating Search Engines, by Stefan Büttcher, Charles L.A.