9. Mai 2012

SOAP-Zugang zu PL/SQL: Database Native Webservices

SOAP access to PL/SQL objects: Database Native Webservices

Heute möchte ich etwas über die Database Native Webservices schreiben, die zwar bereits mit der Datenbankversion 11g Release 1 eingeführt wurden, die aber immer noch kaum bekannt sind. Worum geht es? Mit den Database Native Webservices kann eine SQL-Query oder PL/SQL-Funktion ohne weiteren Aufwand als SOAP-Style Webservice bereitgestellt werden. Und das geht so:

  • Zunächst muss die XML DB in der Datenbank vorhanden sein. Das prüft man am einfachsten mit einer Abfrage auf die View DBA_REGISTRY.
    select comp_id, version from dba_registry where comp_id='XML'
    
    COMP_ID                        VERSION
    ------------------------------ ---------------------
    XML                            11.2.0.2.0
    
    Wenn hier keine Zeile zurückgegeben wird, ist die XML DB nicht vorhanden und es können auch keine Native Webservices genutzt werden. Mit dem PL/SQL Skript catqm.sql in $ORACLE_HOME/rdbms/admin kann die XML DB nachinstalliert werden.
  • In einer 11.2.0.4-Datenbank muss zusätzlich ein Patch eingespielt werden: 16199543. Dieser kann von der Oracle-Supportplattform support.oracle.com unter Patches & Updates heruntergeladen werden.
  • Der HTTP-Protokollserver der XML DB muss aktiviert sein. Normalerweise sind die FTP- und HTTP-Protokollserver deaktiviert. Nutzer des Embedded PL/SQL Gateway auf OracleXE arbeiten typischerweise mit bereits aktiviertem HTTP-Protokollserver, denn der wird hier für APEX genutzt. Wenn Ihr nicht auf OracleXE arbeitet oder euch generell nicht sicher seid, ob der HTTP-Protokollserver aktiv ist, könnt Ihr das mit einem lsnrctl status nachprüfen:
    $ lsnrctl status
    
    LSNRCTL for Linux: Version 11.2.0.2.0 - Production on 04-MAY-2012 10:58:27
    
    Copyright (c) 1991, 2010, Oracle.  All rights reserved.
    :
      (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=sccloud030.de.oracle.com)(PORT=1521)))
      (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=sccloud030.de.oracle.com)(PORT=8080))(Presentation=HTTP)(Session=RAW))
    :
    Services Summary...
    
    Ist diese Zeile nicht vorhanden, so ist der Protokollserver inaktiv. Dann aktiviert Ihr ihn einfach (als DBA) wie folgt. Verwendet nach Möglichkeit einen TCP/IP-Port über 1024. Für die Portnummern darunter muss auf UNIX-Systemen der Listener besonders eingerichtet werden.
    begin
      dbms_xdb.sethttport({http-port});
    end;
    
  • Die Database Native Webservices müssen (einmalig) innerhalb der XML DB aktiviert werden. Hierzu muss (als DBA) folgendes SQL-Kommando abgesetzt werden:
    DECLARE
      SERVLET_NAME VARCHAR2(32) := 'orawsv';
    BEGIN
      DBMS_XDB.deleteServletMapping(SERVLET_NAME);
      DBMS_XDB.deleteServlet(SERVLET_NAME);
      DBMS_XDB.addServlet(
        NAME     => SERVLET_NAME,
        LANGUAGE => 'C',
        DISPNAME => 'Oracle Query Web Service',
        DESCRIPT => 'Servlet for issuing queries as a Web Service',
        SCHEMA   => 'XDB'
      );
      DBMS_XDB.addServletSecRole(
        SERVNAME => SERVLET_NAME,
        ROLENAME => 'XDB_WEBSERVICES',
        ROLELINK => 'XDB_WEBSERVICES'
      );
      DBMS_XDB.addServletMapping(
        PATTERN => '/orawsv/*',
        NAME    => SERVLET_NAME
      );
    END;
    /
    

Damit sind die datenbankweiten Vorbereitungen erledigt. Zum Ausprobieren können wir den Datenbankuser SCOTT hernehmen. Zuerst bauen wir eine kleine PL/SQL-Funktion wie folgt:

create or replace function get_sal(
  p_empno in emp.empno%type
) return emp.sal%type is
  l_sal emp.sal%type := null;
begin
  begin
    select sal into l_sal from emp where empno = p_empno;
  exception
    when no_data_found then null;
    when others then raise;
  end;
  return l_sal;
end;

Diese Funktion GET_SAL wollen wir nun als Native WebService aufrufen. Die Datenbank ist soweit vorbereitet. Allerdings kann man nicht mit jedem Datenbankuser einfach so Webservices aufrufen - es sind Rollen nötig. Für Database Native Webservices gibt es dero drei:

  • XDB_WEBSERVICES: Ein Datenbankbankuser, der diese Rolle hat, ist prinzipiell in der Lage, PL/SQL Objekte und SQL-Abfragen als SOAP-Webservices auszuführen.
  • XDB_WEBSERVICES_OVER_HTTP: Der Datenbankuser kann die Webservices mit dieser Rolle auch über ungesichertes HTTP aufrufen - es wird dann also kein HTTPS benötigt. Ohne dieses Privileg wird der Webservice-Call nur über SSL erlaubt.
  • HTTP_WEBSERVICES_WITH_PUBLIC: Der Datenbankuser kann auch auf Objekte zugreifen, die "nur" über einen "Public" Grant bereitstehen. Ohne diese Rolle muss der Datenbankuser Eigentümer der Objekte sein oder explizite Privilegien haben.

Also bekommt unser Datenbankuser SCOTT zwei Privilegien:

grant xdb_webservices to scott
/

grant xdb_webservices_over_http to scott
/

Damit sind alle Vorbereitungen gemacht. Um einen Webservice per SOAP aufrufen zu können, braucht es das WSDL-Dokument, also die "Schnittstellenbeschreibung" für den Web Service. Die Database Native Webservices generieren dieses automatisch. Probiert im Browser einfach folgende URL aus:

http://{host}:{port}/orawsv/SCOTT/GET_SAL?wsdl

Im Browser werdet Ihr dann zuerst nach Usernamen und Passwort gefragt. Hier loggt Ihr euch mit SCOTT und seinem Passwort ein. Wichtig ist: Der Login muss mit dem Datenbankaccount erfolgen, welcher die XDB_WEBSERVICES-Rollen hat - es muss nicht zwingend der Eigentümer der aufgerufenen Funktion sein. Nach dem Login zeigt euch der Browser das WSDL-Dokument für den Webservice an.

Mit Hilfe dieser URL kann nun jeder SOAP-Client mit der Datenbankfunktion GET_SAL arbeiten. Der folgende Screenshot zeigt ein Beispiel - typischerweise wird zunächst die URL zum WSDL-Dokument angegeben; das Werkzeug liest danach die Informationen aus und generiert einen SOAP-Request als Vorlage. Dort kann man dann eine der EMPNOs aus der Tabelle EMP eintragen und den Request zum Server senden - die Antwort kommt wiederum als SOAP-Response.

Hier habe ich noch einen Screenshot aus Application Express - es ist zwar eine seltsame Idee, eine PL/SQL-Funktion aus APEX heraus per Webservice-Schnittstelle aufzurufen, aber es funktioniert.

Es ist also sehr einfach, eine PL/SQL-Funktion oder SQL-Query als SOAP-Webservice verfügbar zu machen - ein paar Dinge gibt es aber noch zu sagen:

Die oben genannten Privilegien erlauben zunächst keine feingranulare Steuerung, welche PL/SQL-Funktionen oder -Prozeduren als Web Service verfügbar gemacht werden sollen. Man vergibt nur die Rollen an einen Datenbankuser - und alle Objekte, die diesem User gehören, können dann als Web Service aufgerufen werden. Das scheint erstmal sehr grob - aber nur auf den ersten Blick. Denn die XDB_WEBSERVICES-Rollen solle man niemals dem Eigentümer der Objekte geben. Vielmehr sollte man einen eigenen "Webservice-Connect-User" einrichten - dieser bekommt eine "Minimalausstattung" an Privilegien ...

create user webservice_connect identified by {password};

grant create session to webservice_connect;
grant xdb_webservices to webservice_connect;
grant xdb_webservices_over_http to webservice_connect;

Und an diesen User werden nun die EXECUTE-Rechte an den PL/SQL-Objekten, die man als Webservice bereitstellen möchte, explizit und einzeln vergeben. Als SCOTT wird also folgendes ausgeführt ...

grant execute on scott.get_sal to webservice_connect;

Zum Abrufen des WSDL wird nun die gleiche URL verwendet, wie vorhin; der Login muss allerdings nun als WEBSERVICE_CONNECT erfolgen und nicht mehr als SCOTT. Das WSDL wird danach ganz genauso aussehen und der Webservice lässt sich auch genauso nutzen - nur kann man nun auf Objektebene festlegen, welche Funktionen und Prozeduren als Webservice bereitstehen. Eben nur diejenigen, an denen WEBSERVICE_CONNECT Rechte hat.

Packages können auf den ersten Blick nur als Ganzes freigegeben werden - das ist zunächst auch richtig. Auch mit einem Grant des Execute-Privilegs an WEBSERVICE_CONNECT wird das ganze Package freigeschaltet. Allerdings würde ich für ein Webservice-Szenario ein eigenes "Wrapper-Paket" schreiben, welches nur die für den Webservice freigegebene Schnittstelle enthält. Hier kann man auch gleich PL/SQL-eigene Datentypen wie boolean und record, die von den Native Webservices nicht unterstützt werden, auf SQL-Typen abbilden.

Möchte man nicht nur einen skalaren Datentypen (wie NUMBER oder VARCHAR2), sondern ein ganzes Objekt als Parameter übergeben, so können Objekttypen eingesetzt werden. Objekttypen können als Input- oder als Output-Parameter verwendet werden. Eine Funktion CREATE_EMP könnte dann so aussehen ...

create type emp_t as object(
 EMPNO      NUMBER(4),
 ENAME      VARCHAR2(10),
 JOB        VARCHAR2(9),
 MGR        NUMBER(4),
 HIREDATE   DATE,
 SAL        NUMBER(7,2),
 COMM       NUMBER(7,2),
 DEPTNO     NUMBER(2)
)
/

create or replace procedure create_emp(
  p_emp emp_t 
) as
begin
 :
end;
/

Hierzu lässt sich wiederum direkt ein WSDL abrufen (wie oben). Und der Objekttyp EMP_T wird hier auch als komplexer XML-Datentyp erkannt ...

<xsd:element name="EMP_T">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="EMPNO" type="xsd:double"/>
      <xsd:element name="ENAME">
        <xsd:simpleType>
          <xsd:restriction base="xsd:string">
            <xsd:maxLength value="10"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:element>
      <xsd:element name="JOB">
        <xsd:simpleType>
          <xsd:restriction base="xsd:string">
            <xsd:maxLength value="9"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:element>
      <xsd:element name="MGR" type="xsd:double"/>
      <xsd:element name="HIREDATE" type="xsd:date"/>
      <xsd:element name="SAL" type="xsd:double"/>
      <xsd:element name="COMM" type="xsd:double"/>
      <xsd:element name="DEPTNO" type="xsd:double"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

Auch Arrays (VARRAY oder Nested Table-Types) können verwendet werden, aber nicht auf "oberster" Ebene. So führt folgendes bereits beim Versuch, das WSDL abzurufen, zu einer Fehlermeldung:

create type varchar_table_t as table of varchar2(4000)
/

create or replace procedure my_func(
  p_vtab varchar_table_t 
) as
begin
 :
end;
/

Die Lösung ist einfach: Auf der obersten Ebene (also als Funktionsparamater) darf kein VARRAY oder TABLE-Typ verwendet werden. Es ist aber durchaus erlaubt, solche in einem Objekttypen zu nutzen. Wir wandeln das Beispiel also ein wenig um ...

create type varchar_table_t as table of varchar2(4000)
/

create type param_t as object(
  varchar_table varchar_table_t
);
/

create or replace procedure my_func(
  p_vtab param_t 
) as
begin
  :
end;
/

Nun wird das WSDL korrekt generiert und der Webservice kann verwendet werden. Hier der Auszug aus dem WSDL, welcher den komplexen Typ PARAM_T beschreibt.

<xsd:element name="PARAM_T">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="VARCHAR_TABLE">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="VARCHAR2" type="xsd:string" maxOccurs="unbounded" minOccurs="0"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

Alles in allem sind die Native Webservices ein sehr eleganter und einfacher Weg, SQL-Abfragen und PL/SQL-Objekte als SOAP-Style Webservices im Unternehmen bereitzustellen. Macht man es mit Java- oder .NET-Mitteln, muss hierfür meist aufwändig Code geschrieben werden. Mehr dazu findet Ihr in der Oracle-Dokumentation:

Oracle XML DB Developers' Guide: 33 Using Native Oracle XML DB Web Services

This blog posting is about Database native webservices; a feature, which was introduced with Oracle11g Release 1 (11.1). Database Native Webservices allow to publish a SQL Query or a PL/SQL unit (Function, Procedure, Package) as a SOAP Style Webservice without additional programming.

  • Database native Webservices are Part of the XML DB functionaliry. So make sure that XML DB is part of your database. The dictionary view DBA_REGISTRY allows to check this:
    select comp_id, version from dba_registry where comp_id='XML'
    
    COMP_ID                        VERSION
    ------------------------------ ---------------------
    XML                            11.2.0.2.0
    
    If this query returns no rows, then XML DB is not installed in your database - and you cannot use the webservice feature. XML DB can be installed into an existing database by starting the SQL script catqm.sql in $ORACLE_HOME/rdbms/admin. Of course, this must be done by a DBA.
  • On a 11.2.0.4 database, you also need to apply an additional oneoff-Patch: 16199543. It is available in the Patches & Updates Section on the Oracle Support Platform - support.oracle.com.
  • Nou need the XML DB protocol server for HTTP being activated. After a normal database creation these are disabled by default. OracleXE users probably will have an activated HTTP protocol server on port 8080 (it is being used for working with APEX). If you are not sure, simply check on the database server with the lsnrctl status command.
  • $ lsnrctl status
    
    LSNRCTL for Linux: Version 11.2.0.2.0 - Production on 04-MAY-2012 10:58:27
    
    Copyright (c) 1991, 2010, Oracle.  All rights reserved.
    :
      (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=sccloud030.de.oracle.com)(PORT=1521)))
      (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=sccloud030.de.oracle.com)(PORT=8080))(Presentation=HTTP)(Session=RAW))
    :
    Services Summary...
    
    If the red line is not present, the HTTP protocol server is disabled. It needs to be enabled with the following command (again: you need DBA privileges, at least the XDBADMIN role). On Linux or Unix systems choose a TCP/IP port above 1024; ports below require additional listener configuration.
    begin
      dbms_xdb.sethttport({http-port});
    end;
    
  • Now, having XML DB present and the protocol server enabled, you need to enable the webservice feature as such. The following command needs to be executed once - again with DBA privileges or the XDBADMIN role.
    DECLARE
      SERVLET_NAME VARCHAR2(32) := 'orawsv';
    BEGIN
      DBMS_XDB.deleteServletMapping(SERVLET_NAME);
      DBMS_XDB.deleteServlet(SERVLET_NAME);
      DBMS_XDB.addServlet(
        NAME     => SERVLET_NAME,
        LANGUAGE => 'C',
        DISPNAME => 'Oracle Query Web Service',
        DESCRIPT => 'Servlet for issuing queries as a Web Service',
        SCHEMA   => 'XDB'
      );
      DBMS_XDB.addServletSecRole(
        SERVNAME => SERVLET_NAME,
        ROLENAME => 'XDB_WEBSERVICES',
        ROLELINK => 'XDB_WEBSERVICES'
      );
      DBMS_XDB.addServletMapping(
        PATTERN => '/orawsv/*',
        NAME    => SERVLET_NAME
      );
    END;
    /
    

Now the database is ready to provide database native webservices. For testing we use a plain database account (say: SCOTT). First we create a little PL/SQL function as follows ...

create or replace function get_sal(
  p_empno in emp.empno%type
) return emp.sal%type is
  l_sal emp.sal%type := null;
begin
  begin
    select sal into l_sal from emp where empno = p_empno;
  exception
    when no_data_found then null;
    when others then raise;
  end;
  return l_sal;
end;

This function GET_SAL will now be executed as a native web service. The database has been prepared and the function is ready. Before actually doing it, we need some privileges (of course, this feature is being protected). There are three different roles.

  • XDB_WEBSERVICES: This role enables the database native web service feature in general. A user having this role can access own objects and objects with explicit privileges as SOAP style webservices. But for our test we will also need the next role.
  • XDB_WEBSERVICES_OVER_HTTP: With this role, the database user can access his webservices also over "plain" HTTP, i.e. SSL communication is not required. Without this role, webservice calls only work over HTTPS (the protocol server must be configured for HTTPS in that case).
  • .
  • HTTP_WEBSERVICES_WITH_PUBLIC: This role enables also executing public objects as web services. Without it, only own objects and objects with explicit privileges can be accessed.

So our database user SCOTT gets two additional roles ...

grant xdb_webservices to scott
/

grant xdb_webservices_over_http to scott
/

Now all requirements are met. The first thing a client needs in order to call the web service, is the WSDL document. The WSDL is the interface description in XML format. It contains information about the name of the webservice, its input and output parameters and so on. The Oracle database generates the WSDL automatically. Just open your browser and visit the URL as follows ...

http://{database-host}:{http-port}/orawsv/SCOTT/GET_SAL?wsdl

The browser not prompts you for username and password. Log in as wou would do in SQL*Plus. Keep in mind that you need the credentials of the database user having the XDB_WEBSERVICES roles. The object owner might be different. After logging in you'll see the WSDL as follows ...

Having that URL every SOAP client can call the PL/SQL function as a web service. Following you'll see some screen shots. The flow of operation is basically the same for all client: First, get the WSDL from the database, then send a SOAP request (which can be generated from the WSDL) and then receive the SOAP response. How each step is being performed, depends on the SOAP client you are actually using. The second screen shot is from Oracle Application Express (which also has a webservice interface).

Looking at the mentioned XDB_WEBSERVICES privileges it seems that only complete schemas can be enabled or disabled for webservices - the privileges can only be granted to database users. There is no PL/SQL package to work at the procedure or function level. But we can use standard database methods here. The XDB_WEBSERVICES roles should never be granted to the object owners directly. It's a better idea to create a separate "webservice connect user" with a limited set of privileges ...

create user webservice_connect identified by {password};

grant create session to webservice_connect;
grant xdb_webservices to webservice_connect;
grant xdb_webservices_over_http to webservice_connect;

Then the object (functiom procedure) owner does explicit and individual grants on the desired objects to the "webservice connect user". So, in our example, SCOTT issues the following GRANT statements ...

grant execute on scott.get_sal to webservice_connect;

We get the WSDL document we use the same URL as previously. But when the browser prompts for a login, we don't use SCOTT anymore - now we use the WEBSERVICE_CONNECT user. If the grants have been implemented correctly, we can just execute the one procedure SCOTT.GET_SAL as a native webservice.

When it's about packages there's a bit more of work to do: With the described approach we can enable or disable web service access only at the package level. When webservice access is being enabled for a particular package, all public functions and procedures in that package can be executed. If this should be limited to a reduced set of procedures and functions, we need a "wrapper package" on top of the functional one. The wrapper package contains just the functions and procedures to be enabled for webservice access. And execute privileges are then being granted on the wrapper package. Such a wrapper package can also be used to handle PL/SQL-only data types like boolean or record which are not supported by the webservice feature.

If object structures are needed as webservice arguments (in pure PL/SQL we might use records), then we need to create Object Types for this. Object types can be used as input or output arguments. A function CREATE_EMP with an object type argument could look like this:

create type emp_t as object(
 EMPNO      NUMBER(4),
 ENAME      VARCHAR2(10),
 JOB        VARCHAR2(9),
 MGR        NUMBER(4),
 HIREDATE   DATE,
 SAL        NUMBER(7,2),
 COMM       NUMBER(7,2),
 DEPTNO     NUMBER(2)
)
/

create or replace procedure create_emp(
  p_emp emp_t 
) as
begin
 :
end;
/

Now we can -again- retrieve the WSDL using the URL .../orawsv/SCOTT/GET_EMP?wsdl. Our input argument has been detected and "translated" to an XML structure. A SOAP client will now be able to contruct a matching SOAP request.

<xsd:element name="EMP_T">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="EMPNO" type="xsd:double"/>
      <xsd:element name="ENAME">
        <xsd:simpleType>
          <xsd:restriction base="xsd:string">
            <xsd:maxLength value="10"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:element>
      <xsd:element name="JOB">
        <xsd:simpleType>
          <xsd:restriction base="xsd:string">
            <xsd:maxLength value="9"/>
          </xsd:restriction>
        </xsd:simpleType>
      </xsd:element>
      <xsd:element name="MGR" type="xsd:double"/>
      <xsd:element name="HIREDATE" type="xsd:date"/>
      <xsd:element name="SAL" type="xsd:double"/>
      <xsd:element name="COMM" type="xsd:double"/>
      <xsd:element name="DEPTNO" type="xsd:double"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

We can also use VARRAY and nested table types to pass arrays or lists to the web service. But here we have one restriction. VARRAY or NESTED table types cannot be used at the "top-level", directly as procedure or function arguments. The database would then not be able to generate a WSDL and the web service cannot be called.

create type varchar_table_t as table of varchar2(4000)
/

create or replace procedure my_func(
  p_vtab varchar_table_t 
) as
begin
 :
end;
/

But there is a simple workaround: Just embed the VARRAY or table type into another object type - as illustrated in the following example:

create type varchar_table_t as table of varchar2(4000)
/

create type param_t as object(
  varchar_table varchar_table_t
);
/

create or replace procedure my_func(
  p_vtab param_t 
) as
begin
  :
end;
/

Now the WSDL can be generated and the webservice is callable again. Here is the WSDL part describing the input argument PARAM_T.

<xsd:element name="PARAM_T">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="VARCHAR_TABLE">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="VARCHAR2" type="xsd:string" maxOccurs="unbounded" minOccurs="0"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

As we have seen: It's very easy to publish PL/SQL objects as SOAP style webservices - just using the onboard utilities of the Oracle database: All we have to do is to enable the feature once and to grant privileges. In advanced scenarios we'll need additional wrapper packages or optimized data types for the procedure arguments.

Oracle XML DB Developers' Guide: 33 Using Native Oracle XML DB Web Services

Kommentare:

Peter Raganitsch hat gesagt…

Super Blogpost, ein kaum bekanntes Thema gut zusammengefasst und auch die Security nicht vergessen.

Carsten, danke wie immer !

Georg Friedrich hat gesagt…

Gelungene Zusammenfassung für ein interessantes Oracle Feature.

Besonders der Teil mit "Auch Arrays (VARRAY oder Nested Table-Types) können verwendet werden, aber nicht auf "oberster" Ebene." ist hier ein nützlicher Tipp, da ich vor einiger Zeit das gleiche Problem hatte. Danke nochmal für den Tipp!

Viele Grüße aus dem Saarland.

M. Schmidt hat gesagt…

Guten Morgen,

danke für das ausführliche Beispiel.
Ich habe aber noch ein Anliegen.
Auf meinem Testsystem, hat die Umsetzung an Hand des Beispielsources umgehend funktioniert. Eingespielt auf das Zielsystem erhalte ich die Meldung 404 - Seite nicht gefunden. Beide Datenbanken sind auf demselben Stand.
Ich weiss aktuell nicht, wo ich nachschauen soll, wo in dem Wirksystem das Handycap liegt. Wird die URL dort aufgerufen, wird die Anmeldung erfragt.
Anschließend erscheint die erwähnte Page. Lt. v$session erfolgt auch eine Anmeldung mit dem Account an die DB.
Was kann und sollte ich gegenprüfen. (ACL ist komplett freigeschaltet)
Wo finde ich die http-log zum prüfen bzw. habe ich noch andere Möglichkeiten zu prüfen an welcher Stelle das System "abbricht" oder verweigert?

Viele Fragen am frühen Morgen.
MfG
M. Schmidt

Carsten Czarski hat gesagt…

Hallo Herr Schmidt,

das ist aus der Ferne nur schwer zu beurteilen. So aus dem Stand würde ich nochmals überprüfen, ob der Schritt "Die Database Native Webservices müssen (einmalig) innerhalb der XML DB aktiviert werden." wirklich durchgeführt worden ist. Die Dokumentation enthält hier auch SQL-Abfragen, mit denen das geprüft werden kann ...
http://docs.oracle.com/cd/E11882_01/appdev.112/e23094/xdb_web_services.htm#CHDECACB

Beste Grüße

Carsten Czarski

Christian Klein hat gesagt…

Hallo,

die INIT.ORA Parameter local_listener und dispatchers müssen richtig sitzen. Ansonsten hört der Listener nicht auf den Port des Protokollservers.

Weitere INfos: Metalink [ID 1083991.1]

mfg Christian Klein




x hat gesagt…

Hallo,
Danke für dieses Posting, ich wurde dadurch erst auf dieses Thema aufmerksam. Allerdings war ein kleiner Fallstrick für mich enthalten:

Mein System ist eine Oracle XE 11G Datenbank mit Apex 4.2.

Da ich auf die beschriebenen Query


select comp_id, version from dba_registry where comp_id='XML';


nichts zurückbekommen habe, dachte ich müsste also die XML DB installieren. Dem ist aber nicht so. Im Gegenteil, nach Ausführen von catqm.sql war das Apex-Webinterface nicht mehr erreichbar.

Abgesehen davon konnte ich nun ein funktionierendes Soap-Webservice aufsetzen. Super!

Carsten Czarski hat gesagt…

Hallo,

das APEX Webinterface kann man mit dem Skript "apex_epg_config.sql" in $APEX_HOME wiederherstellen. Die Bilder und statischen Dateien werden mit "apxldimg.sql" neu geladen ...

Hoffe, das hilft ...

Beste Grüße

Carsten

stephan hat gesagt…

Hallo,
Danke für das Posting! Eine Frage: lässt sich so ein Webservice auch ohne Authentifizierung einrichten?
danke!

Carsten Czarski hat gesagt…

Hallo Stephan,

Nein; es ist immer eine Anmeldung nötig. Man könnte aber einen eigenen Connect User nur zum Aufruf von Webservices einrichten ...

Beste Gräße

Carsten

Peter hat gesagt…

Hallo,
ich bin ueber diesen gelungenen Blog-Post gestolpert auf der Suche nach einer Loesung fuer folgendes Problem: es wird ja bereits beschrieben, das table Typen auf oberster Ebene bei einem SOAP Service problematisch sind. Es scheint aber auch Probleme mit einem Typ auf unterer Ebene zu geben. Folgendes Bespiel:

PROCEDURE test_int_list (param_int_list IN INT_LST_CONT );

CREATE TYPE INT_LST_CONT AS OBJECT ( slot INT_LST );

CREATE TYPE INT_LST AS TABLE OF INTEGER;

Die Generierung des WSDL (Oracle 11) funktioniert mit diesem Typ nicht, obwohl auf oberster Ebene die Int-Liste durch einen Objekt-Typ gekapselt ist. Was genau uebersehe ich hier? Fuer etwas Hilfestellung waere ich sehr dankbar.

Viele Gruesse
Peter

Carsten Czarski hat gesagt…

Hallo Peter,

das habe ich soeben mal ausprobiert und kann es nachvollziehen. Fakt ist, dass das generierte XML-Dokument ungültig ist.

Die Ursache ist das INTEGER im Typ INT_LST - damit kommt die Webservice-Schnittstelle nicht klar. Aber es gibt einen einfachen Workaround:

CREATE TYPE TINT_LST AS TABLE OF number(38);
/

Intern bildet er INTEGER ohnehin auf NUMBER(38) ab - aber wenn man NUMBER(38) explizit verwendet, tritt das Problem (bei mir) nicht mehr auf ...

Probier' mal ...

Beste Grüße

Carsten

Peter hat gesagt…

Hallo Carsten,
ja, das mit dem Workaround funktioniert [natuerlich], es verwundert mich aber doch sehr, dass eine Liste von INT Probleme bereitet. Insbesondere moegen sich die Nutzer dieses SOAP Service doch mehr oder weniger stark darueber wundern, warum sie einen xsd:double angeben muessen, wenn ein einfacher Integer ausreichen wuerde (bspw bei einem Index). Wenn man einen SOAP-Client in einer streng typisierten Sprache verwendet, mag einem das vermutlich noch mehr missfallen.
Aber vielen Dank nochmal fuer Deine schnelle Antwort.
Viele Gruesse
Peter

Carsten Czarski hat gesagt…

Hallo Peter,

naja - ein Bug ist das wohl auf jeden Fall; ich gebe es mal
and die Kollegen in USA weiter. Allerdings müsste man, selbst wenn es funktionieren würde, wohl ein xs:double verwenden, denn ein "INTEGER" wird von Oracle intern automatisch auf ein NUMBER(38) abgebildet. Den SQL-Datentypen "INTEGER" gibt es in Oracle nicht - es versteht die Syntax, aber es mappt alles auf NUMBER, wie folgendes Beispiel zeigt ...

SQL> create table t12(
2 col1 integer
3 ,col2 decimal
4 ,col3 numeric
5* )

Tabelle wurde erstellt.

SQL> desc t12
Name Null? Typ
--------------------- -------- -----------------
COL1 NUMBER(38)
COL2 NUMBER(38)
COL3 NUMBER(38)

Beste Grüße

Carsten

Peter hat gesagt…

Hallo Carsten,
da haben wir wohl gerade aneinander vorbei geredet; wenn ich in das WSDL von meinem SOAP Service schaue, dann werden dort einzelne Parameter, die ich in Oracle als Integer angegeben habe, auch als xsd:integer im WSDL getypt. Dass Oracle diese intern dennoch als NUMBER behandelt, ist ja - aus der Perspektive der Nutzer/des Clients - erstmal unerheblich.
Aber wenn es sich um einen Bug handelt, dann wird er ja frueher oder spaeter sowieso behoben.
Viele Gruesse
Peter

Peter hat gesagt…

Hallo Carsten,
ich habe nochmal im Detail nachgeschaut; alle Parameter, die im WSDL als xsd:integer getypt sind, sind entweder IN Parameter oder die Rueckgabe von Funktionen mit "RETURN INTEGER". Sobald also etwas mal in der Datenbank gespeichert wurde, macht Oracle ein xsd:double draus. Die Aussage deines vorherigen Posts ist jetzt bei mir angekommen ;-)
Besten Dank nochmal fuer die schnellen Antworten.
Viele Gruesse
Peter

blauorange hat gesagt…

Danke für den Post!

Gibt es auch ein Beispiel wie Table-Typen als OUT-Parameter oder als RETURN-Wert zurückgegeben werden können?

Oder gibt es eine andere/bessere Variante um mehrere Datensätze als in der Antwort zurückzugeben (z.B. alle EMPs mit einer bestimmten DEPTNO)?

Beste Grüße
Paul

Carsten Czarski hat gesagt…

Hallo Paul,

ein Table-Type sollte ganz genauso wie jeder andere Objekttyp ebenfalls verwendbar sein. Gerade für solche Rückgaben gibt es aber noch eine Alternative. Man kann auch per Native Webservice eine SQL-Query ausführen lassen - das Ergebnis wird dann ebenfalls per SOAP zurückgegeben und es sind keine Types mehr nötig.

Die Doku enthält hierzu ein Beispiel: http://docs.oracle.com/cd/E11882_01/appdev.112/e23094/xdb_web_services.htm#ADXDB5684. Es muss eben ein anderer SOAP-Request abgesetzt werden.

Hilft das weiter ...?

Beste Grüße

-Carsten

blauorange hat gesagt…

Hallo Carsten,

ich bin jetzt endlich dazugekommen die Sache zu probieren; ein Table-Type im RETURN scheint allerdings nicht zu funktionieren.

Die WSDL wird zwar richtig erstellt, das RETURN-Feld in der SOAP-Response bleibt aber leer.

create type emp_t as object(
EMPNO NUMBER(4),
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7,2),
COMM NUMBER(7,2),
DEPTNO NUMBER(2)
);
/

create type emp_table_t as table of emp_t;
/

create type emp_ret_t as object (
emp_table emp_table_t
);
/


CREATE OR REPLACE FUNCTION get_emps (deptno_in IN number) RETURN emp_ret_t AS
p_emp_ret emp_ret_t;
BEGIN

select emp_t(empno, ename, job, mgr, hiredate, sal, comm, deptno) bulk collect into p_emp_ret.emp_table
from emp
where deptno = deptno_in;

return p_emp_ret;

END get_emps;
/


Mache ich etwas falsch?

Danke & beste Grüße
Paul

Carsten Czarski hat gesagt…

Hallo Paul,

das Problem hat mit der SOAP-Schnittstelle nichts zu tun, denn die Ergebnismenge ist in SQL Plus auch leer.

SQL> select get_emps(10) from dual;

GET_EMPS(10)(EMP_TABLE(EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO))
--------------------------------------------------------------------------------


1 Zeile wurde ausgewählt.

Grund ist, dass Du die Rückgabevariable p_emp_ret nicht initialisiert hast. Dein SELECT INTO ist dann ins Leere gegangen.

Probier' mal diese Funktion hier:

CREATE OR REPLACE FUNCTION get_emps (deptno_in IN number) RETURN emp_ret_t AS
p_emp_tab emp_table_t;
BEGIN

select emp_t(empno, ename, job, mgr, hiredate, sal, comm, deptno) bulk collect into p_emp_tab
from emp
where deptno = deptno_in;

return emp_ret_t(p_emp_tab);

END get_emps;
/


Beste Grüße

Carsten

blauorange hat gesagt…

Stimmt, funktioniert gleich besser ;)

Tausend Dank!!

Georg Friedrich hat gesagt…

Hallo Herr Czarski,

wissen Sie ob es bei nativen Webservices für CLOBs eine Beschränkung auf 4000 Zeichen gibt?
Eine simple Stored Function die als Parameter einen CLOB erwartet funktioniert bis zu eine Länge von 4000 Zeichen einwandfrei, aber ab der Länge von 4001 bekomme ich eine Oracle Exception:
ORA-31011
XML parsing failed


Viele Grüße
Georg Friedrich

Carsten Czarski hat gesagt…

Hallo Herr Friedrich,

die Liste der unterstützten Datentypen ist hier:
https://docs.oracle.com/cd/E11882_01/appdev.112/e23094/xdb_web_services.htm#ADXDB5687

Da ist CLOB nicht dabei - bis 4.000 Zeichen wird das CLOB allerdings als VARCHAR2 behandelt; dehalb funktioniert es bis 4.000 Bytes. Alternativ könnten Sie den CLOB in einen BLOB umwandeln und dann mit einem BLOB arbeiten. Für den Konsumenten des Web Service wäre das allerdings eine Änderung.

Hilft Ihnen dies weiter?

Beste Grüße

Carsten Czarski

Anonym hat gesagt…

Ja das hilft mir weiter, auch wenn ich mir wünschen würde man könnte große Strings übertragen.

Herzlichen Dank.
Georg Friedrich

Anonym hat gesagt…

@Georg Friedrich
Auch wenn es schon ein wenig her ist, vielleicht hilft es ja trotzdem:

Man kann CLOBs versenden, wenn Sie als Object verpackt sind:

create or replace type OBJ_CLOB as object(
OBJ_CLOB CLOB
);

Wenn man nun eine Funktion schreibt, die einen OBJ_CLOB zurückgibt, kann dieser auch eine größere Menge als 4000 Chars enthalten (habe es mal mit 64k Chars probiert, ging problemlos).

Viele Grüße
Danny F.
@Georg Friedrich
Auch wenn es schon ein wenig her ist, vielleicht hilft es ja trotzdem:

Man kann CLOBs versenden, wenn Sie als Object verpackt sind:

create or replace type OBJ_CLOB as object(
OBJ_CLOB CLOB
);

Wenn man nun eine Funktion schreibt, die einen OBJ_CLOB zurückgibt, kann dieser auch eine größere Menge als 4000 Chars enthalten (habe es mal mit 64k Chars probiert, ging problemlos).

Viele Grüße
Danny F.

Beliebte Postings