0

In order to allow a developer to make a SOAP call from an Oracle 11gR1 (11.1.0.7 PSU 24) instance using UTL_HTTP.

He created a stored procedure :

CREATE OR REPLACE PROCEDURE CALL_WS (URL IN VARCHAR2, REQUEST IN VARCHAR2, 
                                     RESPONSE OUT VARCHAR2, ERRLOG OUT VARCHAR2) IS

   L_HTTP_REQ      UTL_HTTP.REQ;
   L_HTTP_RESP     UTL_HTTP.RESP;

   L_HTTP_URL_V    VARCHAR2 (32767);
   IP_INPUTDATA_V  VARCHAR2 (32767);
   L_OUTPUTDATA_V  VARCHAR2 (32767);

BEGIN

   L_HTTP_URL_V := URL;
   IP_INPUTDATA_V := REQUEST;
   ERRLOG := NULL;

   -- Configuration
   UTL_HTTP.SET_DETAILED_EXCP_SUPPORT (TRUE);
   L_HTTP_REQ := UTL_HTTP.BEGIN_REQUEST (L_HTTP_URL_V, 'POST', 'HTTP/1.1');

   -- Authentification (not necessary at the moment)
   --UTL_HTTP.SET_AUTHENTICATION (L_HTTP_REQ, L_HTTP_USERNAME_V, L_HTTP_PASSWORD_V);
   UTL_HTTP.SET_PERSISTENT_CONN_SUPPORT (L_HTTP_REQ, FALSE);

   -- Headers
   UTL_HTTP.SET_HEADER (L_HTTP_REQ, 'Content-Type', 'text/xml');
   UTL_HTTP.SET_HEADER (L_HTTP_REQ, 'charset', 'UTF-8');
   UTL_HTTP.SET_HEADER (L_HTTP_REQ, 'Content-Length', LENGTH (IP_INPUTDATA_V));

   --WRITES SOME TEXT DATA IN THE HTTP REQUEST BODY
   UTL_HTTP.WRITE_TEXT (L_HTTP_REQ, IP_INPUTDATA_V);

   -- GET HTTP RESPONSE
   L_HTTP_RESP := UTL_HTTP.GET_RESPONSE (L_HTTP_REQ);

   -- GET THE RESPONSE TEXT VALUE
   UTL_HTTP.READ_TEXT (L_HTTP_RESP, L_OUTPUTDATA_V);

   -- Check HTTP status code
   IF (L_HTTP_RESP.STATUS_CODE <> 200) THEN               
       ERRLOG := 'HTTP REQUEST CALL FAILED. STATUS CODE IS ' || L_HTTP_RESP.STATUS_CODE;
   END IF;

   UTL_HTTP.END_RESPONSE (L_HTTP_RESP);
   -- Set Output parameter

   RESPONSE := L_OUTPUTDATA_V;

EXCEPTION
   WHEN OTHERS THEN
       RESPONSE := NULL;
       ERRLOG := SQLERRM;
END;

And a BEFORE INSERT trigger as follows:

CREATE OR REPLACE TRIGGER TR_kkk_AFTERIN
    BEFORE INSERT OR UPDATE ON CHU_kkk
    REFERENCING NEW AS N OLD AS O
FOR EACH ROW

DECLARE
   -- Variable declarations
   l_URL              VARCHAR2 (32767);
   l_REQUEST          VARCHAR2 (32767);
   l_RESPONSE         VARCHAR2 (32767);
   l_ERRLOG           VARCHAR2 (32767);

   v_def  definitions.compterendu%TYPE;
BEGIN

   IF :n.traite = 0 THEN
       -- Variable initializations
       l_URL := 'http://zz/uu/services.asmx';
       v_def := NULL;

       -- Get request SOAP model in a Omnipro definition
       SELECT VALUE INTO v_def
       FROM definitions
       WHERE cle LIKE 'kkk';

       IF v_def IS NOT NULL THEN
           -- Replace values in the request SOAP
           l_REQUEST := REPLACE(v_def, '@@DPTID@@', :n.dptid);
           l_REQUEST := REPLACE(l_REQUEST, '@@USERID@@', :n.userid);
           l_REQUEST := REPLACE(l_REQUEST, '@@APPTID@@', :n.apptid);
           l_REQUEST := REPLACE(l_REQUEST, '@@REFID@@', :n.refid);
           l_REQUEST := REPLACE(l_REQUEST, '@@REFAPP@@', :n.refapp);
           l_REQUEST := REPLACE(l_REQUEST, '@@DTEDEB@@', TO_CHAR(:n.dtedeb, 'yyyy-mm-dd'));
           l_REQUEST := REPLACE(l_REQUEST, '@@DTEFIN@@', TO_CHAR(:n.dtefin, 'yyyy-mm-dd'));
           l_REQUEST := REPLACE(l_REQUEST, '@@NUMNAT@@', :n.numnat);
           l_REQUEST := REPLACE(l_REQUEST, '@@NUMREF@@', :n.numref);

           -- Call
           CALL_WEB_SERVICE (URL       => l_URL,
                           REQUEST   => l_REQUEST,
                           RESPONSE  => l_RESPONSE,
                           ERRLOG    => l_ERRLOG);
           IF l_ERRLOG IS NOT NULL THEN
               :n.traite := 9;
               :n.reflog := l_ERRLOG || ' : ' || SUBSTR (l_RESPONSE, 0, 1900);
           ELSE
               :n.traite := 1;
           END IF;
           :n.dtetrt := SYSDATE;
       END IF;
   END IF;

EXCEPTION
   WHEN OTHERS THEN
       -- Consider logging the error and then re-raise
       :n.traite := 9;
       :n.reflog := SQLERRM;
       :n.dtetrt := SYSDATE;
       --RAISE;
END;

Everything works well for an INSERT query with a single value, but fails whenever he calls:

INSERT INTO ... VALUES ...

COMMIT;

INSERT INTO ... VALUES ...

COMMIT;

He gets an ORA-06502.

If he calls each insert separately, there is no error...

I suggested him to investigate using SQLDeveloper debugger, but we are not sure that it will lead to something...

What could cause that ?

3
  • 1
    What is the full error stack and what line is the error coming on? And for which of the two insert statements? As an architectural aside, doing something non-transactional in a trigger is rarely a great idea-- what happens if the insert statement gets rolled back and/or the trigger has to be rolled back and re-executed and the result of the HTTP call can't be rolled back? It normally makes more sense to have the trigger create a job that makes the HTTP call or write to a queue table and have a separate background job that makes all the HTTP calls. Jan 21, 2021 at 10:53
  • Use CLOB instead of VARCHAR2 (32767). For SOAP calls I would also recommend XMLTYE and related functions. Jan 21, 2021 at 11:46
  • @JustinCave - The second one... My colleage finally found out the reason why he gets the ORA-06502 message (adding too much data in column ERRLOG) and why he adds too much query: SOAP generates a uuid which is the same with two inserts. Jan 21, 2021 at 12:41

1 Answer 1

0

You should use proper data types, i.e. CLOB. For the SOAP request I would recommend XMLTYPE.

I have a similar use case and I use this code to generate the request:

CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);

FUNCTION GenerateRequest(nodes IN VARCHAR_TABLE_TYPE) RETURN XMLTYPE IS
    res XMLTYPE;
BEGIN
    
    SELECT  
        XMLELEMENT("soapenv:Envelope", 
            XMLATTRIBUTES(
                'http://schemas.xmlsoap.org/soap/envelope/' AS "xmlns:soapenv", 
                'http://xxx.yyy.zzz/' AS "xmlns:ser"),
            XMLELEMENT("soapenv:Header"),
            XMLELEMENT("soapenv:Body", 
                XMLELEMENT("ser:getAffectedServices",
                    XMLELEMENT("arg0",
                        XMLELEMENT("appName", 'com'),
                        XMLELEMENT("asmTicket", 'dummy'),
                        XMLELEMENT("principal", 'dummy')
                    ),
                    XMLELEMENT("arg1", 1),
                    XMLAGG(XMLELEMENT("arg2", COLUMN_VALUE)),                   
                    XMLELEMENT("arg3", 'basic')
                )
            )
        )
    INTO res
    FROM TABLE(nodes);
    
    RETURN res;

END GenerateRequest;

And this is the HTTP call. Both, request and response message are defined as CLOB/XMLTYPE:

PROCEDURE GetServices(node IN VARCHAR_TABLE_TYPE) IS

   l_http_request    UTL_HTTP.REQ;
   l_http_response   UTL_HTTP.RESP;
   BUFFER VARCHAR2(32767);
   l_response_clob   CLOB;
    p_soap_msg CLOB;
    msg_length INTEGER;
    p_url VARCHAR2(4000) := 'http://zz/uu/services.asmx'

    amount INTEGER := 10000;
    OFFSET PLS_INTEGER := 1;

BEGIN
    
    p_soap_msg := GenerateRequest(node).GetClobVal();
    msg_length := DBMS_LOB.GETLENGTH(p_soap_msg);
    
   l_http_request := UTL_HTTP.BEGIN_REQUEST(p_url, 'POST', UTL_HTTP.HTTP_VERSION_1_1);
   UTL_HTTP.SET_HEADER(l_http_request, 'Content-Type', 'text/xml; charset="utf-8"');
   UTL_HTTP.SET_HEADER(l_http_request, 'Content-Length', msg_length);

    LOOP
        EXIT WHEN OFFSET > msg_length;
        DBMS_LOB.READ(p_soap_msg, amount, OFFSET, BUFFER);
        UTL_HTTP.WRITE_TEXT(l_http_request, BUFFER);
        OFFSET := OFFSET + amount;
    END LOOP;
   l_http_response := UTL_HTTP.GET_RESPONSE(l_http_request);

   DBMS_LOB.CREATETEMPORARY(l_response_clob, TRUE, DBMS_LOB.CALL);
   DBMS_LOB.OPEN(l_response_clob, DBMS_LOB.LOB_READWRITE);
  
    BEGIN
    LOOP
        UTL_HTTP.READ_TEXT(l_http_response, BUFFER, 8192);
        DBMS_LOB.WRITEAPPEND(l_response_clob, LENGTH(BUFFER), BUFFER);
    END LOOP;
    EXCEPTION
        WHEN UTL_HTTP.END_OF_BODY THEN
            UTL_HTTP.END_RESPONSE(l_http_response);
    END;
   DBMS_LOB.CLOSE(l_response_clob);

    INSERT INTO T_TEMP_SOAP_RES VALUES (XMLTYPE(l_response_clob));
    COMMIT;
   DBMS_LOB.FREETEMPORARY(l_response_clob);

EXCEPTION
    WHEN OTHERS THEN
        Logging.put('Error while getting Service', ErrCode => SQLCODE, ErrMessage => SQLERRM ||CHR(13)|| SUBSTR(l_response_clob, 1, 900));
        RAISE;
END GetServices;

Not the answer you're looking for? Browse other questions tagged or ask your own question.