Final, scheduled for Thurs afternoon at 3: OK?
Pa3 notes: Everyone used DOM. Posted solution uses DOM. I could also post a solution using JAXB, to show that approach.
Various approaches were used to analyze the DOM for the schema, which has a namespace. One way, also shown in the solution, sets up the NamespaceContext for a namepace-aware DOM so that full XPath can be used, then uses XPath
"//xs:simpleType[@name=\""+ key + "\"]/xs:restriction/xs:enumeration";
where key = “milk”, “size”, and “drink” and “location”.
The other two solutions avoided using full XPath, and used namespace-unaware DOMs. One used an element-name-free XPath //@value and checked out the resulting hits by climbing up the DOM tree, and the other used DOM Element’s getElementsByTagName to find xs:complexType elements, then one with name attribute “item”, then the children to get “milk”, etc. Then getElementsByTagName to find xs:simpleType elements whose name attribute is already known, and getElementsByTagName to find their descendent xs:enumeration elements, which finally yield the needed strings.
SOAP Web Services
Read REST book, Chap. 11 The Web and WS-*
If interested in WSDL, see tutorial at w3schools.com
But start now with Harold, pp. 96-99, 116-118, plus Appendix B, pp. 969-972,
firmly skipping all SOAP-ENC material (obsolete)
Basic scheme: use HTTP POST for all service request/responses, to a single service endpoint, i.e. one URL for the whole service. Use XML messages enclosed in SOAP “envelopes”.
So no idea of URI for each resource here. Orders and payments, etc, are still described in XML as in REST, but are all hidden inside the SOAP server. You ask, in XML, for what you want and the server returns it.
The basic rules of designing WS actions is the same for SOAP and REST. Invent actions that depend only on the message and persistent state held by the server—that is the basic “stateless” rule. In other words, don’t expect the server to remember what you were doing last, even if you have to authenticate yourself to get the service.
So it’s straightforward to convert a REST service to a SOAP service or vice versa. You can even reuse the schema for the “representations” of REST—now they are related to the public view of the persistent objects being manipulated by the service. We will be looking at Amazon S3 storage service. It uses one schema for both its SOAP and REST APIs.
In SOAP, there is a way to find out what requests the server can answer, via its WSDL. Unlike WADL, WSDL is widely supported and used. REST proponents would say that REST doesn’t really need WADL, since the service should give you what to do next at each step. But it would be great to know how to get started.
Pg. 97 Basic examples of SOAP messages
<?xml
version=”1.0”?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”> ßSOAP-ENV
is a prefix
<SOAP-ENV:Body>
<getQuote xmlns=”http://namespaces.cafeconleche.org/xmljava/ch2/”>
<symbol>RHAT</symbol>
</getQuote>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
and similar message for
response
Here we see local names Envelope and Body of the SOAP envelope NS.
Notes on this simple SOAP message:
- envelope in one standard NS
- body in an app-related NS
- no address
- interoperable (like REST)
o J2EE (TOMCAT, WEBSPHERE)
.NET solution
Internationalized: XML in UTF-8 (like REST)
Lack of address: keeping it free of transport mechanism: could be HTTP, SMTP, ..., as we saw for media types used in REST.
Schema for this
SOAP Message XML
Schema for the body: See pg. 103 for schema for the request message, trading.xsd. It has our usual setup: default NS = targetNS, elementFormDefault=”qualified”.
<xsd:element name=”symbol” type=”...>
This is introducing the local name “symbol” for the target NS.
type = “StockSymbol” refers to the StockSymbol in the default NS.
We now look at how the SOAP envelope schema can handle the message part inside, which is designed by the app developers. It’s similar to the XHTML example of class 11, except it tries to validate (processContents = “lax”) rather than skipping over the enclosed XML (processContents = “skip”).
Understanding the
SOAP Envelope Schema
Not covered in
class:
If we look at the SOAP Envelope schema in Appendix B, pg. 969, we see a different starting setup than we have been using for schemas:
<xsd:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/”
targetNamespace=”http://schemas.xmlsoap.org/soap/envelope/”
…
We see there is no elementFormDefault=”qualified”. It doesn’t matter, though, because all element definitions are global, i.e, their <element> definitions are direct children of <schema>.
Here “tns” means target NS, which it is by the third line.
So there are more prefixes in this schema than we usually see:
<xs:element name=”Envelope” type = “tns:Envelope”/>
^^^
This prefix makes it very clear that the type name is in the NS, and strangely, they have both the element name “Envelope” and the type name “Envelope”, both in the namespace. There is one localname “Envelope” in the NS, with double duty as an element name and as a type name. Similarly with Body.
It uses <any ...> to allow the app to fill in any message format in XML. The <any> element is on pg. 971, inside schema element name=”Body”. The processContents = “lax” means try to use a schema if you can find one. The XML document should have a NS declaration at this element that can be used to match up a schema.
Validating a whole
SOAP message: need to import one schema into another
This is another example of schema using two schemas together, here the SOAP envelope schema and the app schema for the contents of Body. Need to import one schema into another.
It’s a little different than our orderDap.xsd + Link.xsd because the outer schema doesn’t itself use the elements, etc., defined in the imported schema. Consequently, it doesn’t need to define a prefix for the imported NS. Also, we aren’t in control of the outer schema (we shouldn’t edit it), so we need to expand from it in our own document, and this is where <include> comes in to use.
Harold shows us the way here, on pg. 117-118. Drop the import of “.../soap/encoding”, which we’re not using, but keep the xsd:import of trading.xsd, and the xsd:include of the SOAP envelope schema.
The <include> brings in the SOAP envelope document the way #include does in C, i.e., verbatim, so at top level of the result is the SOAP envelope schema. The <import> allows another NS’s schema to be helping out with the full schema.
It’s better to avoid the local URL in the schemaLocation value (here or anywhere), unless you are sure the validator knows what the base URL is. Should be safe if in the same directory.
For more examples, if interested: see the W3C Schema Primer, linked to class web page.
Servlets for SOAP
We can do a lot with Servlets + SOAP -> basic SOAP Web Service
SOAP Client, pg. 142, like AdminTool of pa3: just uses JDK.
SOAP Servlet, pg. 146, also very primitive, but obviously we could do better.
The basic Web Service works OK between the two cooperating sites. (their developers in communication -> share DTDs, schemas etc).
Java EE support: SAAJ and JAX-WS
JAX-WS is layered on SAAJ, and does more for us, specifically it generates to and from WSDL, web service definition language, for standardized declaration of message formats, other things.
Let’s look at JAX-WS.
Like JAX-RS, JAX-WS provides a servlet, so we don’t have to code doPost ourselves, just put the right entries in our web.xml to get the servlet going.
We use annotation on server methods called on by SOAP services. We use JAXB markup on POJO classes related to the XML messages.
Unlike JAX-RS and WADL, it is easy and commonplace to generate almost a whole service or client from WSDL, so WSDL is much more important than WADL.
Like WADL, WSDL includes or points to XML schema(s) describing the XML messages needed for the service, that is, the payloads inside the SOAP envelope.
Almost the whole service, including JAXB POJOs for business objects, can be generated from the WSDL file. Of course we have to program the actual actions done by the service, by filling in code in provided stubs.
Similarly, client stubs can be generated from the WSDL.
WSDL doc contains
--XML Schemas for message bodies
--abstract SEI: service endpoint interface, i.e., what the calls look like
--spec. of SOAP, HTTP, endpoint address for services
Usually have one service URL for multiple WSs, usually discriminated by top-level element name in request body XML.
Once we have the WSDL doc, we can use tools to help build server-side and client code.
The tools allow the client and server to use the SEI as a Java API.
We have been working with 3 kinds of data: Draw
pic with three areas, arrows between representing translation of data between
them:
XML: triangles, Java objects: ovals, DB tables:
rectangles
All three are
considered very different from each other, with an “impedance mismatch” between
them, i.e. it’s hard to translate data from one form to another.
We have studied the
transition XML data ß> Java objects the most: parsing into our own objects with SAX,
parsing into a DOM tree, and back out (via XSL, recall trick), more recently
data binding with JAXB and its annotations in the POJOs.
CS636 studies Java
objects ß> DB data using JPA-2, Java Persistence Architecture. It uses
annotations in the POJOs too.
Web app layers:
·
Presentation:
handling XML to/from web services
·
Service:
doing core app programming with POJOs
·
Data
access: getting data to/from DB
Here we need the
POJOs to keep the programmers happy. We
can use JPA-2 to turn DB data into POJOs, and JAXB to turn POJOs into XML. It’s
even possibly to annotate the same POJO two ways, one for JAXB and one for
JPA-2, since each annotation processor only looks for its own annotations.
Data-driven Web Services
But what if we don’t
need Java programming for our web services?
In some pure data-driven cases, the desired web service data is determined
completely by the database data.
Publishing Database Data Directly in XML, with no intervening application
On pages 203-208 of the text there is code
that manually produces XML from database tables, but this is ugly
Today, many databases know about XML and
can generate XML from relational tables
The SQL/XML standard is supported Oracle
and DB2 UDB, among others, but not SqlServer, which has SQLXML, something
similar.
SQL/XML was supposed to be part of SQL 2003, but they
dropped it, apparently because it wasn’t integrated properly with XQuery yet.
Consider
the following table structure
Dept
(table
name)
-----------------------------------------
dname
| location (column names)
-----------------------------------------
Accounting | New York (row data)
-----------------------------------------
Operations | Boston
Easy to
set up on our Oracle running on dbs2:
SQL> create table dept(dname char(20), location
char(20));
Table created.
SQL> insert
into dept values ('Accounting', 'New York');
1 row created.
SQL> insert
into dept values ('Operations', 'Boston');
1 row created.
Here
is an example SQL/XML query;
select XMLElement ("Department",
XMLElement("DeptName", dname),
XMLElement("Location", location))
from dept
The above SQL statement would generate the following XML:
<Department>
<DeptName>Accounting</DeptName>
<Location>New York</Location>
</Department>
<Department>
<DeptName>Operations</DeptName>
<Location>Boston</Location>
</Department>
Actual output from SQLPlus—truncated, need better settings in SQLPlus to see everything
<Department><DeptName>Accounting </DeptName><Location>New
York
<Department><DeptName>Operations
</DeptName><Location>Boston
Of course there is more to this—see Oracle docs.
It is also possible to hold XML *in* the database, specifically in column values, using a new datatype for XML documents. Oracle calls this new datatype XMLType, DB2 just XML. We can have ordinary columns, like varchar and float and int, and then one or more columns that hold whole XML documents.
Then ordinary SQL can’t be used on the XMLType column value, but XPath can.
From notes15--
Another example of
XPath use: in databases
Databases can now handle XML along with their ordinary relational data. A very useful way is by defining an XML datatype, so that a single column value can hold a whole XML document. Then a certain order, for example, can have a row in the orders table with relational columns order_id, qty, price, etc. , and also (say in column “req”) the XML document that came in from the web to make the order.
Oracle has good XML support in this way (since v 9.2 I think). Once an XML document is held in a column value, it can be queried by the Oracle SQL function
extractValue(tab.col, ‘xpathexpr’)
where the xpathexpr selects a certain element or attribute node. There are other related functions as well.
For example the following could get the qty from the XML in the req column of orders:
select
extractValue(o.req, ‘/order/qty’) from orders o
where order_id = 100;
This kind of extraction of information from the XML can be done to populate relational columns in the database from the incoming XML, while also preserving that original XML.
Using Namespace case
Oracle case: just use an optional third argument for extractValue:
select
extractValue(o.req, ‘/ord:order/ord:qty’,’xmlns:ord=”http:/…”’) from orders o
where order_id = 100;
The third argument sets up ord: as the prefix for the xpath expression. Multiple prefixes can be set up by separating them by spaces in the string.
Now to flesh that example out, at least the no-namespace case. The following example is from
Available at http://www.oracle.com/technetwork/articles/quinlan-xml-095823.html
1. Create a table with an XML column.
create table
invoiceXML_col (
inv_id number primary
key,
inv_doc XMLType);
2. or, Create an XML table.
create table
invoiceXML_tbl of XMLtype;
We’ll stick to case 1. No reason to give up the option of having ordinary columns as well as XML ones. I’ve converted the examples from case 2 to case 1.
Add XML from a string:
Insert into invoicexml_col values (1, XMLType(‘<Invoice> … </Invoice>’)); --specify XML with string
Or add XML from a file (XMLDIR is a directory alias set up previously, see tutorial)
Insert into invoicexml_col values (1,
XMLType(bfilename('XMLDIR', 'invoicexml.txt'),
nls_charset_id('AL32UTF8') )); --I think this is the default charset, a UTF-8 descendent
Here is the file (no <?xml prolog, but it works)
<Invoice>
<MailAddressTo id="PA">
<Person>Joe Smith</Person>
<Street>10 Apple Tree Lane</Street>
<City>New York</City>
<State>NY</State>
<Zipcode>12345</Zipcode>
</MailAddressTo>
<MailAddressFrom id="PA">
<Person>Ed Jones</Person>
<Street>11 Cherry Lane</Street>
<City>Newark</City>
<State>NJ</State>
<Zipcode>67890</Zipcode>
</MailAddressFrom>
<Details id="2006Sept1to30PA">
<FromTo>Sept 1, 2006 to Sept 30, 2006</FromTo>
<Hours>70</Hours>
<Rate>30</Rate>
<Taxes>210</Taxes>
<TotalDue>2310</TotalDue>
<InvDate>Oct 1, 2006</InvDate>
<Contractor>Ed Jones</Contractor>
</Details>
</Invoice>
Using XPath on this data—
select extract(inv_doc, '/Invoice/MailAddressTo') from invoicexml_col;
EXTRACT(INV_DOC,'/INVOICE/MAILADDRESSTO')
<MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10
Apple Tree Lane</Street><City>New York</City><State>NY</Stat
e><Zipcode>12345</Zipcode></MailAddressTo>
Select count(*) from invoicexml_col
where existsNode(
inv_doc, '/Invoice/MailAddressTo[Person="Joe Smith"]') = 1;
COUNT(*)
1
With XML in the database, you can register XSD for it, that is, in our case associate a schema with invoicexml_col. This XML has no namespace, but it is possible to work with namespaces too, even multiple schemas and namespaces.
An XMLType column can only hold well-formed XML in each column value: single root element, etc. With a registered schema, it can only hold valid documents.
Oracle docs point out that XML Schema and database schema can work together to keep data conforming to rules. XML Schemas can’t by themselves enforce unique keys or relational integrity, but the database can.
DB2 and MS SQLServer have similar capabilities.
MySQL: behind the others for XML, but free and wildly popular. Can do boilerplate XML output:
sf08$ mysql -u eoneil1 -D eoneil1db -p --xml -e 'select * from customers'
Enter password:
<?xml version="1.0"?>
<resultset statement="select * from customers
" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<row>
<field name="cid">c001</field>
<field name="cname">Tiptop</field>
<field name="city">Duluth</field>
<field name="discnt">12</field>
</row>
<row>
<field name="cid">c002</field>
<field name="cname">Basics</field>
<field name="city">Dallas</field>
<field name="discnt">12</field>
</row>
</resultset>
mysql has an ExtractValue function that works on string data using XPath:
mysql> SELECT
ExtractValue('<a><b>cc</b></a>', '/a/b');
+------------------------------------------+
| ExtractValue('<a><b>cc</b></a>', '/a/b')
|
+------------------------------------------+
| cc |
+------------------------------------------+
1 row in set (0.00 sec)
Similarly, put this XML in a string column named col1 and
mysql> SELECT ExtractValue(col1, '/a/b') from T;
to see the same result
So you can store XML in string columns and query it with XPath in mysql too.
\
There is also an UpdateXML function that can replace parts of XML.
Oracle can update XML with its UPDATEXML function: example from Oracle docs--
SELECT warehouse_name,
EXTRACT(warehouse_spec, '/Warehouse/Docks')
"Number of Docks"
FROM warehouses
WHERE warehouse_name = 'San Francisco';
WAREHOUSE_NAME Number of Docks
-------------------- --------------------
San Francisco <Docks>1</Docks>
UPDATE warehouses SET warehouse_spec =
UPDATEXML(warehouse_spec,
'/Warehouse/Docks/text()',4)
WHERE warehouse_name = 'San Francisco';
1 row updated.
SELECT warehouse_name,
EXTRACT(warehouse_spec, '/Warehouse/Docks')
"Number of Docks"
FROM warehouses
WHERE warehouse_name = 'San Francisco';
WAREHOUSE_NAME Number of Docks
-------------------- --------------------
San Francisco <Docks>4</Docks>
Last topic: XML from DBs to Java via JDBC
Java 6 has SQLXML classes to help with newer JDBC (JDBC 4.0) accessing DBs that are storing XML in column values.
Supported in DB2 (DB2 v 9.5 (2007) or later), and MS SqlServer (2005 or later) for several years and more recently in Oracle 11.2 (2009)—we have v 10.1, unfortunately.
Mysql—can store XML in string types and use SQLXML in Java.