1. Native REST Web Services
with Oracle 11g
CLOUG 2009
Marcelo F. Ochoa - Fac. Cs. Exactas - UNICEN - Tandil - Arg.
2. Agenda
REST
Restlet framework
XDB Restlet adapter
XDB Restlet adapter architecture
My first REST application
Performance test
Comparison against SOAP
Data Service Architecture and SOA last mile
Data Service example
DataServiceApplication
UserResource
OrdersResource
OrderResource
3. What is REST?
REpresentational State Transfer
Formalized by Roy Fielding in his PhD Dissertation
Primarily applicable to distributed hypermedia systems
Think of it as resource-orientation
Resources represent the domain concepts
Championed by the Web community
Amazon, Yahoo, Google
Adopted by some of the major vendors (Microsoft:WCF,
Sun:Jersey)
Positioned as an alternative to WS-* implementations
High impact in the community (Ruby on Rails, RESTlet)
4. Motivation for REST
Take advantage of what the Web does well
Simplicity
Scalability
Performance
Ease of use
So much nicer than the alternatives
SOAP & WS - *
Unifies Web Sites and Web Services into consistent Web
Applications
5. A Style, Not a Standard
REST guides the use of standards
Examples:
HTTP (Connector)
URI (Resource)
XML, HTML, GIF, etc. (Representations)
text/xml, text/html, image/gif, etc. (Media types)
The Web is a REST system
6. REST Architectural Style
Composition of styles that gains their benefits:
Client-Server - separation of concerns, scalability
Layered – allows intermediaries (proxies, firewalls)
without affecting interfaces
Stateless – scalability
Cacheable – reduces payload & latency
Pipe-and-Filter – dynamic component connection
7. REST at glance
Cartoon by Eric Lemerdy (http://eric.lemerdy.free.fr)
Resources
A Resource should be a fixed target of a URI
The URI-to-Resource mapping shouldn't
change, but the representation can
Resources may map to multiple
representations, called variants
Example: png, gif, jpg are variant
representations of an image
Content negotiation selects the best
variant
Uniform interface
Resources are manipulated by HTTP
methods
GET – retrieve a resource
PUT – create a resource
POST – update (create if necessary) a
resource
DELETE – delete a resource
Cacheability
A cache can return copy in response to a GET,
therefore prefer GET over POST
8. What is Restlet?
An open source REST framework for Java
A good mapping of REST principles
Founded by Jérôme Louvel,
Noelios Consulting, Paris, France
http://www.restlet.org/
Built in response to:
Need for a simple, RESTful web application framework
Servlet limitations
9. Restlet Framework
Restlet API – Supports REST call
handling
Extensions – For integrating
external technologies (JDBC,
JSON, alternate containers,
connectors, template engines, etc.)
SPI – Plugin point for alternate
implementations
Restlet Implementation –
Currently just Noelios Engine
Application
Restlet API
SPI XDB
Restlet Implementation
Extensions
12. XDB Adapter
XdbServerServlet extends ServerServlet
A Servlet 2.2 compliant connector
XdbServletCall extends HttpServerCall
A Servlet 2.2 version of ServletCall class
XdbServletConverter extends HttpServerConverter
Converter of low-level HTTP server calls into high-level
uniform calls
XdbServletWarClient extends Client
Connector acting as a WAR client for a Servlet Application. Support
XMLDB repository browsing
XdbServletWarClientHelper extends ServletWarClientHelper
Local client connector based on a XMLDB repository. Translate:
URL: file:///$HOME/myPath/myFile.ext -> XDB: /home/SCOTT/myPath/myFile.ext
URL: war:///myPath/myFile.ext -> XDB: /home/SCOTT/wars/HelloRestlet/myPath/myFile.
ext
Servlet running with PUBLIC grants run with an effective user ANONYMOUS
13. XDB Servlet adapter architecture
HTTP presentation over
TCP/IP is used to
support native REST
WS
14. XDB Adapter application stack
Client alternatives:
an AJAX application
(GWT , DOJO Toolkit)
a RIA application
(Flex, Lazlo, etc.)
a middle tier
consuming REST WS
(REST client stack)
Client side:
Application
Context's client
dispatcher
HTTP client connector
HTTP protocol
TCP layer
IP layer
Server side:
Resource
Router
Services (decompression,
range, tunnel, etc.)
HTTP server connector
(XDB)
HTTP protocol
TCP layer
IP layer
15. Installing XDB Restlet Adapter
Using Reslet SVN 1.2M4 (SQLNet string test )
# cd reslet/build
# ant
# cd dist/classic/restlet-1.2snapshot/src/org.restlet.ext.xdb/resources/
# cat ~/build.properties
sqlnet.string=test
jdbc.username=RESTLET
jdbc.password=RESTLET
jdbc.sysusr=sys
jdbc.syspwd=change_on_install
# ant all
Buildfile: build.xml
create-schema:
.....
BUILD SUCCESSFUL
Total time: 1 minute 58 seconds
16. My first REST application
Extracted from Restlet tutorial 12 (automatically installed)
package org.restlet.example.tutorial ;
public class Part12 extends Application {
@Override
public Restlet createRoot() {
// Create a router
final Router router = new Router(getContext());
// Attach the resources to the router
router.attach("/users/{user }", UserResource .class);
router.attach("/users/{user }/orders", OrdersResource .class);
router.attach("/users/{user }/orders/{order }", OrderResource .class);
// Return the root router
return router;
}
}
17. Resources
UserResource
public class UserResource extends Resource {
String userName;
Object user;
public UserResource(Context context, Request request, Response response) {
super(context, request, response);
this.userName = (String) request.getAttributes().get("user ");
this.user = null; // Could be a lookup to a domain object.
// Here we add the representation variants exposed
getVariants().add(new Variant(MediaType.TEXT_PLAIN));
}
@Override
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
result = new StringRepresentation("Account of user ""
+ this.userName + """);
}
return result;
}
}
18. Resources - cont.
OrdersResource
public class OrdersResource extends UserResource {
public OrdersResource(Context context, Request request, Response response) {
super(context, request, response);
}
@Override
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
result = new StringRepresentation("Orders of user ""
+ this.userName + """);
}
return result;
}
}
19. Resources - cont.
OrderResource
public class OrderResource extends UserResource {
String orderId;
Object order;
public OrderResource(Context context, Request request, Response response) {
super(context, request, response);
this.orderId = (String) request.getAttributes().get("order ");
this.order = null; // Could be a lookup to a domain object.
}
@Override
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
result = new StringRepresentation("Order "" + this.orderId
+ "" for user "" + this.userName + """);
}
return result;
}
}
24. Adding caching behavior
Add an expiration time now plus 10 seconds
@Override
public Representation represent(Variant variant) throws
ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
result = new StringRepresentation("Order "" + this.orderId
+ "" for user "" + this.userName + """);
}
Date expirationDate = new Date(System.currentTimeMillis()+10000);
result.setExpirationDate(expirationDate);
return result;
}
26. Comparing against a SOAP application
Example application
create or replace and compile java source named "my.OrderCalculator" as
package my;
import java.util.logging.Level;
import java.util.logging.Logger;
public class OrderCalculator {
/**
* Java Util Logging variables and default values
*/
private static Logger logger = null;
/**
* Constant used to get Logger name
*/
static final String CLASS_NAME = OrderCalculator.class.getName();
static {
logger = Logger.getLogger(CLASS_NAME);
logger.setLevel(Level.ALL);
}
public static String getOrder(String userName, int orderId) {
logger.entering(CLASS_NAME,"getOrder",new Object [] {userName,new Integer(orderId)});
logger.exiting(CLASS_NAME,"getOrder","Order '"+orderId+"' for user '"+userName+"'");
return "Order '"+orderId+"' for user '"+userName+"'";
}
}
27. Defining a Call Spec
PLSQL Call Spec to provide execution as stored procedure
CREATE OR REPLACE PACKAGE orders_calculator AUTHID CURRENT_USER AS
FUNCTION getOrder(user_name IN VARCHAR2, order_id IN NUMBER) RETURN VARCHAR2 as
LANGUAGE JAVA NAME
'my.OrderCalculator.getOrder(java.lang.String, int) return java.lang.String';
END orders_calculator;
POST message to call above procedure with SOAP sintax
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://xmlns.
oracle.com/orawsv/SCOTT/ORDERS_CALCULATOR/GETORDER">
<env:Header/>
<env:Body>
<ns1:SVARCHAR2-GETORDERInput>
<ns1:USER_NAME-VARCHAR2-IN>scott</ns1:USER_NAME-VARCHAR2-IN>
<ns1:ORDER_ID-NUMBER-IN>300</ns1:ORDER_ID-NUMBER-IN>
</ns1:SVARCHAR2-GETORDERInput>
</env:Body>
</env:Envelope>
31. Data Service example
Key technologies used
XDB Restlet adapter
XMLDB repository as persistent layer
Java in the database
XdbRepresentation
Oracle AQ for faster insert
XQuery
Think Data Services as data abstraction layer, not as simple
CRUD services
32. Data Service Application
public class DataServiceApplication
extends Application {
@Override
public Restlet createRoot() {
// Create a router
final Router router = new Router(getContext());
// Attach the resources to the router
router.attach("/users/{user}", UserResource.class);
router.attach("/users/{user}/orders", OrdersResource.class);
router.attach("/users/{user}/orders/{order}", OrderResource.class);
// Return the root router
return router;
}
}
33. Data Service Application UserResource
public class UserResource extends Resource {
...
public UserResource(Context context, Request request, Response response) {
super(context, request, response);
this.userName = (String)request.getAttributes().get("user");
..
try {
..
preparedstatement =
this.conn.prepareStatement("select XMLElement("User"," +
"XMLAttributes(? as "UserName",? as "UserID",SYSDATE as "Created"))" +
" from dual");
...
if (resultset.next()) {
user = resultset.getObject(1);
..
setAvailable(false);
}
...
} finally {
XdbServerServlet.closeDbResources(preparedstatement, resultset);
}
// Here we add the representation variants exposed
getVariants().add(new Variant(MediaType.TEXT_XML));
}
}
34. UserResource - GET
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_XML)) {
if (user instanceof XMLType)
result =
new XdbRepresentation(variant.getMediaType(), (XMLType)this.user);
else
generateErrorRepresentation("Returned object is not XMLType",
"", this.getResponse());
}
Date expirationDate = new Date(System.currentTimeMillis()+10000);
result.setExpirationDate(expirationDate);
return result;
}
Sample Output
GET /ds/users/sbell HTTP/1.0
Authorization: Basic c2NvdHQ6dGlnZXI=
Host: localhost:8080
HTTP/1.1 200 OK
MS-Author-Via: DAV
DAV: 1,2,<http://www.oracle.com/xdb/webdav/props>
Date: Sat, 04 Apr 2009 17:59:59 GMT
Server: Noelios-Restlet-Engine/1.2snapshot
Accept-Ranges: bytes
Content-Type: text/xml
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Expires: Sat, 04 Apr 2009 18:00:09 GMT
<User UserName="sbell" UserID="SBELL" Created="2009-04-04"></User>
36. UserResource - Sample POST
POST /ds/users/sbell/orders HTTP/1.0
Host: localhost:8080
Authorization: Basic c2NvdHQ6dGlnZXI=
Content-Type: text/xml; charset=ISO8859_1
Content-Length: 844
<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http:
//localhost:8080/home/SCOTT/poSource/xsd/purchaseOrder.xsd">
<Reference>SBELL-20030409123336231PDT</Reference>
<Actions>
<Action>
<User>SVOLLMAN</User>
</Action>
</Actions>
<Reject/>
<Requestor>Sarah J. Bell</Requestor>
<User>SBELL</User>
<CostCenter>S30</CostCenter>
<ShippingInstructions>
<name>Sarah J. Bell</name>
<address>400 Oracle Parkway......</address>
<telephone>650 506 7400</telephone>
</ShippingInstructions>
<SpecialInstructions>COD</SpecialInstructions>
<LineItems>
<LineItem ItemNumber="1">
<Description>Juliet of the Spirits</Description>
<Part Id="37429165829" UnitPrice="29.95" Quantity="2"/>
</LineItem>
</LineItems>
</PurchaseOrder>
37. OrdersResoure
public class OrdersResource extends UserResource {
public OrdersResource(Context context, Request request,
Response response) {
super(context, request, response);
ResultSet resultset = null;
PreparedStatement preparedstatement = null;
try {
// Gets a list of Orders for given User
preparedstatement =
this.conn.prepareStatement("SELECT XMLQuery('<Orders>n" +
" {for $i in ora:view("PURCHASEORDER")/PurchaseOrdern" +
" where $i/User = $useridn" +
" returnn" +
" <PurchaseOrder>n" +
" {$i/Reference}n" +
" </PurchaseOrder>n" +
" }n" +
" </Orders>' PASSING ? as "userid"n" +
" RETURNING CONTENT) AS orders FROM DUAL");
preparedstatement.setString(1, this.userName.toUpperCase());
resultset = preparedstatement.executeQuery();
if (resultset.next()) {
orders = resultset.getObject(1);
...
} finally {
XdbServerServlet.closeDbResources(preparedstatement, resultset);
}
}
38. OrdersResource - Represent
public Representation represent(Variant variant) throws ResourceException {
Representation result = null;
if (variant.getMediaType().equals(MediaType.TEXT_XML)) {
if (this.orders instanceof XMLType)
result =
new XdbRepresentation(variant.getMediaType(), (XMLType)this.orders);
else
generateErrorRepresentation("Returned object is not XMLType",
"", this.getResponse());
}
return result;
}
Sample Output
GET /ds/users/sbell/orders HTTP/1.0
Authorization: Basic c2NvdHQ6dGlnZXI=
Host: localhost:8080
HTTP/1.1 200 OK
MS-Author-Via: DAV
DAV: 1,2,<http://www.oracle.com/xdb/webdav/props>
Date: Sat, 04 Apr 2009 18:17:09 GMT
Server: Noelios-Restlet-Engine/1.2snapshot
Accept-Ranges: bytes
Content-Type: text/xml
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
<Orders><PurchaseOrder><Reference>SBELL-20030409123336231PDT</Reference>
<Requestor>Sarah J. Bell</Requestor>
<CostCenter>S30</CostCenter>
</PurchaseOrder></Orders>
39. OrderResource
public class OrderResource extends UserResource {
public OrderResource(Context context, Request request, Response response) {
super(context, request, response);
this.orderId = (String)request.getAttributes().get("order");
..
try {
// Gets a list of Orders for given User
preparedstatement = this.conn.prepareStatement(selectStmt);
preparedstatement.setString(1,
this.userName.toUpperCase() + "-" + this.orderId);
resultset = preparedstatement.executeQuery();
if (resultset.next()) {
order = resultset.getObject(1);
...
} else {
setModifiable(true);
setAvailable(false);
}
} catch (SQLException sqe) {
..
} finally {
XdbServerServlet.closeDbResources(preparedstatement, resultset);
}
}
selectStmt:
public static final String selectStmt =
"select * from " + table + " where existsNode(object_value," +
"'/PurchaseOrder[Reference="'||?||'"]')=1";
40. OrderResource - DELETE handler
public void removeRepresentations() throws ResourceException {
int i = 0;
if (this.order != null) {
// Remove the item from the list.
PreparedStatement preparedstatement = null;
try {
// Gets a list of Orders for given User
preparedstatement = this.conn.prepareStatement(deleteStmt);
preparedstatement.setString(1,
this.userName.toUpperCase() + "-" +
this.orderId);
i = preparedstatement.executeUpdate();
this.conn.commit();
// Tells the client that the request has been successfully fulfilled.
getResponse().setStatus(Status.SUCCESS_MULTI_STATUS);
getResponse().setEntity("Deleted " + i + " record(s).",
MediaType.TEXT_PLAIN);
} catch (SQLException sqe) {
...
} finally {
XdbServerServlet.closeDbResources(preparedstatement, null);
}
...
}
}
deleteStmt:
public static final String deleteStmt =
"delete from " + table + " where existsNode(object_value," +
"'/PurchaseOrder[Reference="'||?||'"]')=1";
45. Need more information?
OracleJVM and Java Stored Procedures
http://www.oracle.com/technology/tech/java/jsp/index.html
Java Developer's Guide Java
http://tahiti.oracle.com/pls/db111/to_toc?pathname=java.111/b31225/toc.htm
XMLDB Developer's Guide
http://tahiti.oracle.com/pls/db111/to_toc?pathname=appdev.111/b28369/toc.htm
Restlet Framework
http://www.restlet.org/
XDB Restlet Adapter
http://wiki.restlet.org/docs_1.1/13-restlet/28-restlet/84-restlet.html
Data Service Example code
http://dbprism.cvs.sourceforge.net/viewvc/dbprism/DataService/
My blog
http://marceloochoa.blogspot.com/