How Pl/Sql Applications Can Participate in a Service-Oriented Architecture

Total Page:16

File Type:pdf, Size:1020Kb

How Pl/Sql Applications Can Participate in a Service-Oriented Architecture

HOW PL/SQL APPLICATIONS CAN PARTICIPATE IN A SERVICE-ORIENTED ARCHITECTURE Aino Andriessen, AMIS

Introduction The title may be a bit posh, but the truth is, that all around us much effort is done to implement a service oriented architecture (SOA). Although everybody uses a different definition, the common denominator is the interaction of different applications with each other. This means that a Java application exchanges information with a .NET application or with a C, PHP or any other application. These applications can also be combined into new applications. Traditionally, the database could be considered to function as an isolated entity that only supports its applications. However, more and more features have been added to the database to enable communication with the outside world. This enables us to use the database as an application on its own and to participate in a service oriented architecture. In this paper I will demonstrate how we can use PL/SQL to achieve this.

Introduction to services and SOA According to Wikipedia (and OASIS) a Service Oriented Architecture is "A paradigm for organizing and utilizing distributed capabilities that may be under the control of different ownership domains. It provides a uniform means to offer, discover, interact with and use capabilities to produce desired effects consistent with measurable preconditions and expectations." Off course, more definitions are available, some of them focus on the utilization aspect, while others take a more technical approach like: "SOA is a style of design that strives to enable easy integration and flexible applications. In SOA, application functionality is designed as shared reusable services. A service is a piece of application functionality that exposes its functionality through an abstract interface, which hides the inner workings of the service implementation." The central aspect of a SOA is the service as a functional component that is available in a standard way, not bound to a certain (proprietary) technology. A service can function as a stand-alone entity and is not designed to be part of one application. Of course, the concept of a service is not new. It has been available for a long time, for example in Component Based Development. But in the past it has always been bounded to a certain technology, like Enterprise Java Beans or Microsoft DCOM objects and interaction between different technologies was a tedious task. Only since the availability of standards like SOAP and WSDL did the interfaces become technology agnostic and is it possible for different technologies to interact with each other in a easy and standardized way.

Service implementation Services are available in many different ways. One of the most popular implementations is the webservice, described by Wikipedia as: "According to the W3C a Web service[1] is a software system designed to support interoperable machine-to-machine interaction over a network. It has an interface that is described in a machine-processable format such as WSDL. Other systems interact with the Web service in a manner prescribed by its interface using messages, which may be enclosed in a SOAP envelope, or follow a REST approach. These messages are typically conveyed using HTTP, and normally comprise XML in conjunction with other Web-related standards." Although the transport mechanism is not dictated, the 'internet' transport protocol (HTTP) is by far the most widely used, thus making these services very popular for use on the internet or the intranet. One of the main characteristics of these services is www.odtug.com 1 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen the direct request-response mechanism; the consumer waits (on the same connection) until it receives a response from the service. Another very important implementation is based on a-synchronous transportation of messages to and from the services where the response is send via a different connection to the consumer. Because it requires the use of a specific transportation mechanism, also called message oriented middleware (MOM), it is mostly confined to intra-company environments. One of the problems with message oriented middleware has been the lack of standards governing its use. All the major vendors have their own implementations, each with its own API and management tools. However, Java Messaging Service (JMS) can be considered as a standard that has been implemented by the most of MOM vendors, thus hiding the particular implementations. Another standardization is the increasing use of XML messages, specifically with the SOAP format.

SOA participation For our application to participate in a SOA we can distinguish two roles: the service consumer and the service producer. As a consumer the application must be able to connect to the service. To function as a producer is a matter of both application design and connectivity. As we've seen before, the connectivity can be accomplished with webservices or with messaging. Let's evaluate the options for our PL/SQL applications.

Webservices PL/SQL AS A WEBSERVICE PROVIDER With the introduction of the XMLDB the Oracle database provides http access to the database using an embedded http server. However, it was not possible to run stored procedures. But in Oracle 10g release 2 (including Oracle XE) the embedded PL/SQL gateway using mod_plsql can be enabled to run PL/SQL code without the need for an external webserver. It only takes a few steps to expose your stored procedure: 1. Create (as system) a database access descriptor (DAD) using dbms_epg. BEGIN DBMS_EPG.create_dad (dad_name => 'ws', path => '/ws/*'); END; / 2. Create a stored procedure (not a function) and use the htp package to produce the output. create or replace procedure myxml as xml xmltype; begin select xmlelement ("employees", xmlagg (xmlelement ("emp", ename))) into xml from emp; htp.p (xml.getstringval); end myxml; / 3. You can now execute this procedure via http, e.g. using a browser or from another application. http://localhost:8080/ws/myxml The embedded http server is part of the XMLDB. It is therefore subjected to the xmldb security policies and you need to provide user credentials. In addition, the pl/sql gateway also includes its own security mechanism that can be configured with dbms_epg. Of course, this is a very basic and home-grown implementation of a webservice (?!).To provide a full blown SOAP webservice with a WSDL based interface description you'd have to write them all by yourself, no wizards or frameworks are available. Also other webservice standards like WS-Addressing, WS-Security, WS-ReliableMessaging etc. are not provided and have to be implemented by home-grown code and/or frameworks. However, these standards are incorporated in the Java EE environment and also in other environments like .NET. It is therefore advisable to take advantage of the JEE platform and the available IDE's expose PL/SQL methods as a webservice. Especially JDeveloper is equipped with wizards to make this an easy task and to expose PL/SQL with the use of a JEE wrapper on an applicationserver. Since this has been described elsewhere it will not be a part of this paper1.

www.odtug.com 2 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen

PL/SQL AS A WEBSERVICE CONSUMER To consume webservices from pl/sql a two options are available: a fully pl/sql approach using either utl_http or utl_dbws, and a java approach. With the latter, you create a Java (JEE) webservice client, deploy that in the database as a Java stored procedure. This has been described elsewhere2. With Oracle 8i, 9i and Oracle XE utl_http is the only alternative. You have to write low-level http code to consume a webservice. It works, but it required a lot of work. To give you an idea: CREATE OR REPLACE PROCEDURE ws_helloworld_utl_http is soap_request varchar2(30000); soap_response varchar2(30000); http_req utl_http.req; http_resp utl_http.resp; response XMLType; content varchar2(100); BEGIN -- create soap request soap_request:= ' '; -- open http connection http_req:= utl_http.begin_request ( 'http://webservices.oracle.com:80/ws/mobile/oracle.ws.OTNHelloWorld' , 'POST' , 'HTTP/1.1' ); utl_http.set_header(http_req, 'Content-Type', 'text/xml'); utl_http.set_header(http_req, 'Content-Length', length(soap_request)); utl_http.set_header(http_req, 'SOAPAction', ''); -- send request utl_http.write_text(http_req, soap_request); -- get response http_resp:= utl_http.get_response(http_req); utl_http.read_text(http_resp, soap_response); utl_http.end_response(http_resp); -- use the response response:= XMLType.createXML(soap_response); response:= response.extract ('/soap:Envelope/soap:Body/child::node()' ,'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"' ); content := response.extract ('/ns1:getDefaultHelloWorldResponse/return/text()' ,'xmlns:ns1="oracle.ws.OTNHelloWorld"' ).getstringval; dbms_output.put_line(soap_response); dbms_output.put_line(response.getstringval); dbms_output.put_line(content); END ws_helloworld_utl_http;

The other fully pl/sql option, only available to Oracle 10g is to use utl_dbws. The same code would be: CREATE OR REPLACE PROCEDURE ws_helloworld_utl_dbws AS l_service UTL_DBWS.SERVICE; l_call UTL_DBWS.call; l_result ANYDATA;

l_wsdl_url VARCHAR2(1024); l_service_name VARCHAR2(200); l_operation_name VARCHAR2(200); l_input_params UTL_DBWS.anydata_list;

www.odtug.com 3 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen

BEGIN l_wsdl_url := 'http://webservices.oracle.com/ws/mobile/oracle.ws.OTNHelloWorld?WSDL'; l_service_name := 'OTNHelloWorld'; l_operation_name := 'getDefaultHelloWorld';

l_service := UTL_DBWS.create_service ( wsdl_document_location => URIFACTORY.getURI(l_wsdl_url), service_name => l_service_name);

l_call := UTL_DBWS.create_call ( service_handle => l_service, port_name => NULL, operation_name => l_operation_name);

l_result := UTL_DBWS.invoke ( call_handle => l_call, input_params => l_input_params);

UTL_DBWS.release_call (call_handle => l_call); UTL_DBWS.release_service (service_handle => l_service);

dbms_output.put_line (anydata.accessvarchar2(l_result));

END ws_helloworld_utl_dbws; / Make sure to create the grants and public synonyms and that the dbwsclient.jar has been loaded into the database3. But probably the best approach is to create java stored procedures. Modern Java IDE's like JDeveloper provide you with wizards to generate the required Java code and to deploy these code in the database. This is not only very productive, it also gives you easy access to the latest webservice technologies and standards.

Messaging Oracle has already introduced it's database messaging system, called Advanced Queuing, in Oracle 8. It basically provides a- synchronous message exchange between a producer and a consumer with the use of queues. Messaging offers advance features like single- and multi consumer queues, guaranteed delivery, subscriber functionality, delaying etc. etc. Figure 1 shows the two basic queue mechanisms.

Figure 1 Example of a single consumer and multi-consumer queue. Oracle AQ4 is a very efficient way to exchange information between different processes within or between database applications. It offers all the features that should be available by a modern MOM, including a JMS interface and the opportunity to connect to messaging systems outside the database, thus making it possible to participate in a SOA.

www.odtug.com 4 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen

The following example (Figure 2) demonstrates the use of AQ. B. is a registration office that handles conference registration requests and will also send a registration response. A. is a developer that wants to attend a conference and makes a registration.

Figure 2 Conference registration application

FULL PL/SQL IMPLEMENTATION Let's start with a full PL/SQL implementation. We need two queues, but for demonstration purposes I will only show the code for one. First we have to create the underlying queue table. BEGIN -- create the underlying queue table first dbms_aqadm.CREATE_queue_table( queue_table => 'registration_in_QT', queue_payload_type => 'SYS.XMLTYPE', comment => 'Creating incoming registrations queue table'); END; / The we can create the queue itself: BEGIN dbms_aqadm.CREATE_queue( queue_name => 'registration_in_Q', queue_table => 'registration_in_QT', comment => 'Incoming registrations Queue'); END; /

In addition we also get some default database objects, like an exception queue and a view. Note, always use the AQ packages (dbms_aq and dbms_aqadm) to dml actions. Direct manipulation of the underlying tables may result in database corruption. Let's use some simple, generic, PL/SQL procedures to enqueue and dequeue the messages: CREATE OR REPLACE PROCEDURE DO_ENQUEUE (queuename VARCHAR2, msg XMLTYPE) AS enq_msgid RAW(16); eopt dbms_aq.enqueue_options_t; mprop dbms_aq.message_properties_t; BEGIN dbms_aq.enqueue( queue_name => queuename, enqueue_options => eopt, message_properties => mprop, payload => msg, msgid => enq_msgid); commit; END do_enqueue; /

www.odtug.com 5 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen and CREATE OR REPLACE FUNCTION DO_DEQUEUE(queuename VARCHAR2) RETURN xmltype AS deq_msgid RAW(16); dopt dbms_aq.dequeue_options_t; mprop dbms_aq.message_properties_t; payload xmltype; no_messages exception; pragma exception_init(no_messages, -25228); BEGIN dopt.wait := 5; dbms_aq.dequeue( queue_name => queuename, dequeue_options => dopt, message_properties => mprop, payload => payload, msgid => deq_msgid); commit; return payload; EXCEPTION WHEN no_messages THEN commit; RETURN NULL; END do_dequeue; /

These procedures will be used by the developer and registration_office packages (the package body has been omitted for demonstration purposes): CREATE OR REPLACE package registration_office as procedure receive_registration; procedure send_confirmation (p_registration XMLTYPE); function get_registration return xmltype; end registration_office; / CREATE OR REPLACE package developer as procedure make_registration; procedure receive_confirmation; function get_confirmation RETURN xmltype; end developer; /

We can now run test/run the application: do_enqueue (registration_office. 'regconfirmation_out_Q' ,xmltype.createxml ('...')); developer.receive_confirmation; t_confirmation := developer.get_confirmation; utassert.eq ('Compare messages' ,'...' ,t_confirmation.getstringval);

PL/SQL AND EXTERNAL JAVA IMPLEMENTATION From the java code, we can directly access the advanced queues. It uses JDBC, JMS and the Oracle AQ libraries and provides the same operations as with PL/SQL. package nl.amis.demo.jms;

import java.sql.Connection; import javax.jms.DeliveryMode; import javax.jms.Message; import javax.jms.Queue; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueReceiver; import javax.jms.QueueSender; import javax.jms.QueueSession;

www.odtug.com 6 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen

import javax.jms.Session;

import oracle.jms.AQjmsConstants; import oracle.jms.AQjmsFactory; import oracle.jms.AQjmsQueueSender; import oracle.jms.AQjmsSession; import oracle.jms.AdtMessage;

import oracle.xdb.XMLType; import oracle.xml.parser.v2.XMLDocument;

public class Developer { public final String CONFIRMATION_STR = "ODTUG 2006>Pietje Puk"; public final String RESERVATION_STR = "ODTUG 2006>Pietje Puk"; public final String QUEUE_OUT = "registration_in_Q"; public final String QUEUE_IN = "regconfirmation_out_Q";

private XMLDocument confirmation;

private final String USER = "SOA"; private final String PWD = "SOA"; private final String SID = "XE"; private final String HOST = "localhost";

public void receiveConfirmation () { try { QueueConnectionFactory qcfact = AQjmsFactory.getQueueConnectionFactory(HOST, SID, 1521, "thin"); QueueConnection qconn = qcfact.createQueueConnection( USER,PWD); QueueSession qsess = qconn.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE); qconn.start() ; Queue q1 = ((AQjmsSession)qsess).getQueue(USER, QUEUE_OUT) ;

QueueReceiver q_receiver = ((AQjmsSession)qsess).createReceiver(q1,XMLType.getORADataFactory()); Message jms_msg = (javax.jms.Message) q_receiver.receive(); if(jms_msg == null) { System.out.println("No message received"); } else { AdtMessage adt_msg2 = (AdtMessage)jms_msg; confirmation = (XMLDocument)((XMLType)adt_msg2.getAdtPayload()).getDOM(); } qsess.commit(); qsess.close(); qconn.close(); } catch (Exception e) {e.printStackTrace();}

}

public void makeReservation () { try { QueueConnectionFactory qcfact = AQjmsFactory.getQueueConnectionFactory(HOST, SID, 1521, "thin"); QueueConnection qconn = qcfact.createQueueConnection( USER,PWD); QueueSession qsess = qconn.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE); qconn.start() ; Connection db_conn = ((AQjmsSession)qsess).getDBConnection(); Queue q1 = ((AQjmsSession)qsess).getQueue(USER, QUEUE_OUT) ; // must queue_in QueueSender qsender = qsess.createSender(q1) ;

XMLType reservation = oracle.xdb.XMLType.createXML(db_conn, CONFIRMATION_STR); AdtMessage adt_msg = ((AQjmsSession)qsess).createAdtMessage(); adt_msg.setAdtPayload(reservation);

www.odtug.com 7 ODTUG Kaleidoscope 2006 HOW PL/SQL APPLICATIONS CAN PARTICIPATE … Andriessen

((AQjmsQueueSender)qsender).send(q1, adt_msg, DeliveryMode.PERSISTENT, 1, AQjmsConstants.EXPIRATION_NEVER);

qsess.commit(); qsess.close(); db_conn.close(); qconn.close(); } catch (Exception e) { e.printStackTrace(); } }

public XMLDocument getConfirmation() { return confirmation; } } This example demonstrates only enqueue-ing and dequeue-ing from the XMLType queues. As you can see, it is quite a lot of coding. Fortunately, libraries and tools are available that hide the complexity and allow you to use advanced queues in a more verbose way. In addition, many SOA environments like Oracle Interconnect, an Enterprise Service Bus (ESB) like Mule, the Oracle AQ servlet and many more provide access to advanced queue-ing.

Application design We have seen different ways for PL/SQL components to interact with services. How can these be applied to application design? Let's take the following example: Clients can place orders via a webstore. These orders are stored in the database. When the orderstatus changes, the user must be notified. If the notification fails, it must be tried again later. We have to make sure that the notification process functions independent from the orderstatus changes. In other words, a failure in the notification process must not result in a rollback of the orderstatus and sending the notification must not block the execution of the orderstatus change. Additionally, a notification service is already available as part of the webapplication, and must be reused. Preferably, a push mechanism is used instead of a polling mechanism. The solution is actually quite simple (Figure 3): an orderstatus change triggers, either by a trigger or by the database change notification5 mechanism, the creation of a notification message. This message is put in a an advanced queue. A notification process, in the database, reads these messages, and connects, via SOAP HTTP, to the notification service that sends the message to the client.

Figure 3 Application example

www.odtug.com 8 ODTUG Kaleidoscope 2006 1 Jason Price - Build a PL/SQL Web Service : http://www.oracle.com/technology/pub/articles/price_10gws.htm 2 Lucas jellema - Consuming Web Services from PL/SQL - Part I: Using Java Stored Procedures : http://technology.amis.nl/blog/?p=348 3 UTL_DBWS - Consuming Web Services in Oracle 10g : http://www.oracle-base.com/articles/10g/utl_dbws10g.php 4 Oracle Streams Advanced Queueing documentation : http://download- uk.oracle.com/docs/cd/B19306_01/server.102/b14257/toc.htm 5 Oracle Database Change Notification Documentation : http://download- uk.oracle.com/docs/cd/B19306_01/appdev.102/b14251/adfns_dcn.htm#ADFNS018

Recommended publications