Our service defines exactly one operation, namely the findPrice
operation. This is an operation which expects some
WKNs or
ISINs as input
and responds with the appropriate market prices or with a fault message if an error occurres. The operation follows therefore
the request/response pattern. Interface definitions are denoted with the wsdl:portType
tag in WSDL 1.1:
25 |
... |
26 |
<wsdl:portType name="SecurityPriceServicePortType"> |
27 |
<wsdl:operation name="findPrice"> |
28 |
<wsdl:input name="SecurityPriceServiceRequest" message="tns:SecurityPriceServiceRequest"/> |
29 |
<wsdl:output name="SecurityPriceServiceResponse" message="tns:SecurityPriceServiceResponse"/> |
30 |
<wsdl:fault name="SecurityPriceServiceException" message="tns:SecurityPriceServiceException"/> |
31 |
</wsdl:operation> |
32 |
</wsdl:portType> |
33 |
... |
The wsdl:input
, wsdl:output
and wsdl:fault
elements from line 28, 29 and 30 are in turn
referencing the relevant messages which are describing
the data being exchanged between the Web service and its clients during such a operation. For now we are following
the input message:
13 |
... |
14 |
<wsdl:message name="SecurityPriceServiceRequest"> |
15 |
<wsdl:part name="SecurityPriceServiceRequest" element="tns:SecurityPriceServiceRequest"/> |
16 |
</wsdl:message> |
17 |
... |
For every expected input parameter the wsdl:message
tag contains a wsdl:part
element.
In this case we are expecting only one parameter and this is the request itself, mainly consisting of a list of IDs identifying the securities.
The element
attribut
refers already to an xml element with the (qualified) name tns:SecurityPriceServiceRequest
. How this element
actually looks like is defined within the (external) XML schema:
7 |
... |
8 |
<wsdl:types> |
9 |
<xsd:schema targetNamespace="http://de.christofreichardt/SecurityPriceService"> |
10 |
<xsd:include schemaLocation="SecurityPriceServiceSchema.xsd"/> |
11 |
</xsd:schema> |
12 |
</wsdl:types> |
13 |
... |
The wsdl:output
and wsdl:fault
elements are connected in the exact same manner
with elements whose types are defined within the external schema. Before I discuss the types within the schema
another important section of the WSDL document remains to be shown, namely the binding:
33 |
... |
34 |
<wsdl:binding name="SecurityPriceServiceBinding" type="tns:SecurityPriceServicePortType"> |
35 |
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> |
36 |
<wsdl:operation name="findPrice"> |
37 |
<soap:operation soapAction="http://de.christofreichardt/SecurityPriceService/findPrice"/> |
38 |
<wsdl:input name="SecurityPriceServiceRequest"> |
39 |
<soap:body use="literal"/> |
40 |
</wsdl:input> |
41 |
... |
47 |
</wsdl:operation> |
48 |
</wsdl:binding> |
49 |
... |
The binding refers to the above presented interface definition, that is the wsdl:portType
and describes how
the findPrice
operation will be actually transmitted over the wire. Of course we are using SOAP messages on top of
the HTTP protocol here. Furthermore we are specifying the document/literal style which means that the payload contains only the
actual data to be passed to the service. Today the document/literal style is the recommended practise because the data going over the
wire can be validated against the employed XML schema without much ado. This is convenient when the client and the service
are managed by different platforms, e.g. by Java and .NET.
You may review the complete SecurityPriceService.wsdl.
[Top]
The namespace of all XML elements and attributs in connection with the SecurityPriceService
has been specified
with http://de.christofreichardt/SecurityPriceService
. Our schema document targets this
namespace in line 4. Additionally, we declare a prefix for this namespace in line 5 to distinguish our to be defined elements
from other elements (e.g. elements out of the XML schema namespace). Now we can continue with our exploration of the message connected with
the wsdl:input
of the findPrice
operation:
3 |
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
4 |
targetNamespace="http://de.christofreichardt/SecurityPriceService" |
5 |
xmlns:tns="http://de.christofreichardt/SecurityPriceService" |
6 |
elementFormDefault="qualified"> |
7 |
|
8 |
<xsd:element name="SecurityPriceServiceRequest" type="tns:SecurityPriceServiceRequest"/> |
9 |
<xsd:element name="SecurityPriceServiceResponse" type="tns:SecurityPriceServiceResponse"/> |
10 |
<xsd:element name="SecurityPriceServiceFault" type="tns:SecurityPriceServiceFault"/> |
11 |
|
12 |
<xsd:complexType name="SecurityPriceServiceRequest"> |
13 |
<xsd:sequence> |
14 |
<xsd:element name="UserId" type="xsd:string" /> |
15 |
<xsd:element name="TransactionId" type="xsd:unsignedInt" /> |
16 |
<xsd:choice maxOccurs="25"> |
17 |
<xsd:element name="WKN" type="xsd:string"/> |
18 |
<xsd:element name="ISIN" type="xsd:string"/> |
19 |
</xsd:choice> |
20 |
</xsd:sequence> |
21 |
<xsd:attribute name="provider" type="xsd:string" use="optional" default="OnVista"/> |
22 |
</xsd:complexType> |
23 |
... |
96 |
</xsd:schema> |
We see the declaration of a root element named SecurityPriceServiceRequest
of type tns:SecurityPriceServiceRequest
in line 8. Sometimes (complex) types are denoted
with the postfix 'Type' (that would be SecurityPriceServiceRequestType
) to differentiate between types and elements, but
in our case this would lead to Java classes with the same postfix and this would be rather unusual. Our SecurityPriceServiceRequest
element consists of an UserId
element, a TransactionId
element, a sequence of
maximal 25 WKN
or ISIN
elements and an provider
attribut with
the default value OnVista
. The decision to use an attribute to store the provider value results from the
XML Schema specification concerning the occurence constraints, see
2.2.1 Occurrence Constraints
within the XML schema recommendation of the W3C.
It is specified that if an attribut (with a default value) that does not appear at all within an instance document the schema processor must provide the attribut
together with its default value, whereas the handling of (missing) elements with default values is slightly different.
We will look how wsconsume
deals with this requirement later on.
How could such a SecurityPriceServiceRequest
actually look like? See the subsequent example:
1 |
<SecurityPriceServiceRequest xmlns="http://de.christofreichardt/SecurityPriceService" provider="Test"> |
2 |
<UserId>tester</UserId> |
3 |
<TransactionId>35</TransactionId> |
4 |
<WKN>623100</WKN> |
5 |
<WKN>716460</WKN> |
6 |
<WKN>514000</WKN> |
7 |
</SecurityPriceServiceRequest> |
The above XML fragment will be embedded within a SOAP envelope (and body) at runtime. Next we look at how the
SecurityPriceServiceResponse
is defined. This will be a bit more complicated, since there is
more than one type of security to consider. This demo takes shares, index certificates and options into account. Therefore we define
a AbstractSecurityPrice
type as starting point. This type consists of the actual security, that is
its name, WKN and ISIN as well as its current rate (bid and ask). The OptionPrice
type for example extends this
AbstractSecurityPrice
by adding the strike price, an expiration date and the way of the exercise (american or european).
3 |
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" |
4 |
targetNamespace="http://de.christofreichardt/SecurityPriceService" |
5 |
xmlns:tns="http://de.christofreichardt/SecurityPriceService" |
6 |
elementFormDefault="qualified"> |
7 |
|
8 |
<xsd:element name="SecurityPriceServiceRequest" type="tns:SecurityPriceServiceRequest"/> |
9 |
<xsd:element name="SecurityPriceServiceResponse" type="tns:SecurityPriceServiceResponse"/> |
10 |
<xsd:element name="SecurityPriceServiceFault" type="tns:SecurityPriceServiceFault"/> |
23 |
... |
24 |
<xsd:complexType name="SecurityPriceServiceResponse"> |
25 |
<xsd:sequence> |
26 |
<xsd:element name="Timestamp" type="xsd:dateTime"/> |
27 |
<xsd:choice maxOccurs="25"> |
28 |
<xsd:element name="SharePrice" type="tns:SharePrice"/> |
29 |
<xsd:element name="IndexPrice" type="tns:IndexPrice"/> |
30 |
<xsd:element name="OptionPrice" type="tns:OptionPrice"/> |
31 |
</xsd:choice> |
32 |
</xsd:sequence> |
33 |
</xsd:complexType> |
40 |
... |
41 |
<xsd:complexType name="Security"> |
42 |
<xsd:sequence> |
43 |
<xsd:element name="Name" type="xsd:string"/> |
44 |
<xsd:element name="WKN" type="xsd:string"/> |
45 |
<xsd:element name="ISIN" type="xsd:string"/> |
46 |
</xsd:sequence> |
47 |
</xsd:complexType> |
48 |
|
49 |
<xsd:complexType name="AbstractSecurityPrice"> |
50 |
<xsd:sequence> |
51 |
<xsd:element name="Security" type="tns:Security"/> |
52 |
<xsd:element name="Bid" type="xsd:decimal"/> |
53 |
<xsd:element name="Ask" type="xsd:decimal"/> |
54 |
</xsd:sequence> |
55 |
</xsd:complexType> |
76 |
... |
77 |
<xsd:simpleType name="Exercise"> |
78 |
<xsd:restriction base="xsd:string"> |
79 |
<xsd:enumeration value="european"/> |
80 |
<xsd:enumeration value="american"/> |
81 |
</xsd:restriction> |
82 |
</xsd:simpleType> |
83 |
|
84 |
<xsd:complexType name="OptionPrice"> |
85 |
<xsd:complexContent> |
86 |
<xsd:extension base="tns:AbstractSecurityPrice"> |
87 |
<xsd:sequence> |
88 |
<xsd:element name="StrikePrice" type="xsd:decimal"/> |
89 |
<xsd:element name="ExpirationDate" type="xsd:dateTime"/> |
90 |
<xsd:element name="Exercise" type="tns:Exercise"/> |
91 |
</xsd:sequence> |
92 |
</xsd:extension> |
93 |
</xsd:complexContent> |
94 |
</xsd:complexType> |
95 |
|
96 |
</xsd:schema> |
The definition of the SecurityPriceServiceResponse
type starts at line 24. Such a response consists of
a timestamp and the list of the requested market prices. Then comes the definition of the Security
type.
This is simply a sequence of the security's name, its WKN and ISIN. Any security price consists of the Security itself and its market
rate (bid and ask). We take the OptionPrice
as an example for a specialised security price. Such an
OptionPrice
simply adds the strike price, an expiration date and the way of the exercise of the option.
See below how an actual SecurityPriceServiceResponse
might look like:
1 |
<SecurityPriceServiceResponse xmlns="http://de.christofreichardt/SecurityPriceService"> |
2 |
<Timestamp>2014-10-13T19:09:20.734+02:00</Timestamp> |
3 |
<OptionPrice> |
4 |
<Security> |
5 |
<Name>CALL auf DAX Performance-Index</Name> |
6 |
<WKN>DE7LES</WKN> |
7 |
<ISIN>DE000DE7LES6</ISIN> |
8 |
</Security> |
9 |
<Bid>10.810</Bid> |
10 |
<Ask>10.820</Ask> |
11 |
<StrikePrice>7.600</StrikePrice> |
12 |
<ExpirationDate>2014-12-17T00:00:00.000+02:00</ExpirationDate> |
12 |
<Exercise>american</Exercise> |
13 |
</OptionPrice> |
14 |
</SecurityPriceServiceResponse> |
You may review the complete SecurityPriceServiceSchema.xsd.
[Top]
The wsdl:portType
definition translates directly into a Java (endpoint) interface. Apache CXF does that for us
during the generate-sources phase of the build lifecycle:
1 |
/** |
2 |
* This class was generated by Apache CXF 2.7.10 |
3 |
* 2014-10-17T16:51:02.879+02:00 |
4 |
* Generated source version: 2.7.10 |
5 |
* |
6 |
*/ |
7 |
@WebService(targetNamespace = "http://de.christofreichardt/SecurityPriceService", name = "SecurityPriceServicePortType") |
8 |
@XmlSeeAlso({ObjectFactory.class}) |
9 |
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) |
10 |
public interface SecurityPriceServicePortType { |
11 |
@WebMethod(action = "http://de.christofreichardt/SecurityPriceService/findPrice") |
12 |
@WebResult(name = "SecurityPriceServiceResponse", targetNamespace = "http://de.christofreichardt/SecurityPriceService", partName = "SecurityPriceServiceResponse") |
13 |
public SecurityPriceServiceResponse findPrice( |
14 |
@WebParam(partName = "SecurityPriceServiceRequest", name = "SecurityPriceServiceRequest", targetNamespace = "http://de.christofreichardt/SecurityPriceService") |
15 |
SecurityPriceServiceRequest securityPriceServiceRequest |
16 |
) throws SecurityPriceServiceException; |
17 |
} |
This boils down to the interface signature as shown below:
20 |
public interface SecurityPriceServicePortType { |
21 |
public SecurityPriceServiceResponse findPrice(SecurityPriceServiceRequest securityPriceServiceRequest) throws SecurityPriceServiceException; |
22 |
} |
Both SecurityPriceServiceRequest
and SecurityPriceServiceResponse
are JAXB value classes which have
been generated for us too. To get an idea I present the SecurityPriceServiceRequest
value class below:
1 |
@XmlAccessorType(XmlAccessType.FIELD) |
2 |
@XmlType(name = "SecurityPriceServiceRequest", propOrder = { |
3 |
"userId", |
4 |
"transactionId", |
5 |
"wknOrISIN" |
6 |
}) |
7 |
public class SecurityPriceServiceRequest { |
8 |
|
9 |
@XmlElement(name = "UserId", required = true) |
10 |
protected String userId; |
11 |
@XmlElement(name = "TransactionId") |
12 |
@XmlSchemaType(name = "unsignedInt") |
13 |
protected long transactionId; |
14 |
@XmlElementRefs({ |
15 |
@XmlElementRef(name = "ISIN", namespace = "http://de.christofreichardt/SecurityPriceService", type = JAXBElement.class, required = false), |
16 |
@XmlElementRef(name = "WKN", namespace = "http://de.christofreichardt/SecurityPriceService", type = JAXBElement.class, required = false) |
17 |
}) |
18 |
protected List<JAXBElement<String>> wknOrISIN; |
19 |
@XmlAttribute(name = "provider") |
20 |
protected String provider; |
21 |
|
22 |
public String getUserId() { |
23 |
return userId; |
24 |
} |
25 |
|
26 |
public void setUserId(String value) { |
27 |
this.userId = value; |
28 |
} |
29 |
|
30 |
public long getTransactionId() { |
31 |
return transactionId; |
32 |
} |
33 |
|
34 |
public void setTransactionId(long value) { |
35 |
this.transactionId = value; |
36 |
} |
37 |
|
38 |
public List<JAXBElement<String>> getWKNOrISIN() { |
39 |
if (wknOrISIN == null) { |
40 |
wknOrISIN = new ArrayList<JAXBElement<String>>(); |
41 |
} |
42 |
return this.wknOrISIN; |
43 |
} |
44 |
|
45 |
public String getProvider() { |
46 |
if (provider == null) { |
47 |
return "OnVista"; |
48 |
} else { |
49 |
return provider; |
50 |
} |
51 |
} |
52 |
|
53 |
public void setProvider(String value) { |
54 |
this.provider = value; |
55 |
} |
56 |
} |
The JAX-WS runtime uses these value classes to generate the appropriate XML at execution time. See e.g. the definition of the
provider property in the lines 19,20 and 45-50. Thus the code generation has taken the default value for the provider into account.
Of course the web service must provide an implementation of the SecurityPriceServicePortType
(endpoint) interface:
1 |
@WebService( |
2 |
endpointInterface = "de.christofreichardt.jaxws.securitypriceservice.SecurityPriceServicePortType", |
3 |
serviceName = "SecurityPriceService") |
4 |
@HandlerChain(file = "SecurityPriceService_handler.xml") |
5 |
public class SecurityPriceServicePortTypeImpl implements SecurityPriceServicePortType, Traceable { |
6 |
@Override |
7 |
public SecurityPriceServiceResponse findPrice(SecurityPriceServiceRequest securityPriceServiceRequest) throws SecurityPriceServiceException { |
8 |
... |
9 |
} |
10 |
} |
Since I didn't have a direct connection to the stock exchange, I'm checking the relevant online resources (like OnVista)
to get the market rates. When evaluating these resources I'm using the Jsoup html parser to retrieve
the desired pieces of information. This presupposes a particular and well known page layout. If OnVista changes this layout the price extraction
must be reworked.
The implementation should not be tight to a particular provider or to any certain price determination. Thus
the Strategy pattern is applied to abstract from the actual price extraction.
An offline provider for testing purposes has been made available too.
[Top]
It has to be mentioned that the provided service uses a web descriptor. The descriptor mainly maps endpoint implementations on url patterns.
The application server then turns it into an address at which the endpoint can be accessed. That is this overrides
the wsdl:port
section of the WSDL file (the server would do this anyway).
1 |
<?xml version="1.0" encoding="UTF-8"?> |
2 |
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"> |
3 |
<servlet> |
4 |
<servlet-name>WSServlet</servlet-name> |
5 |
<servlet-class>de.christofreichardt.jaxws.securitypriceservice.SecurityPriceServicePortTypeImpl</servlet-class> |
6 |
</servlet> |
7 |
<servlet-mapping> |
8 |
<servlet-name>WSServlet</servlet-name> |
9 |
<url-pattern>/securitypriceservice</url-pattern> |
10 |
</servlet-mapping> |
11 |
<session-config> |
12 |
<session-timeout> |
13 |
30 |
14 |
</session-timeout> |
15 |
</session-config> |
16 |
</web-app> |
[Top]