TLDR; look at last paragrap.
A developer from our partner software company needs to call our WCF (basic http binding) service, and he asked us to turn it to asmx for themselves, cause he has trouble with calling it from Oracle. WCF service is being used on different platforms (.net, java, php) with no error.
His code gives him Status code: 500 - Internal Server Error. I assume its about sending wrong soap format or content.
So i learned you should use utl_dbws instead of utl_http as that developer did.
Ok, this seemed an easy task to me first. Find a working code sample from internet and send a e-mail like "Hi fellow developer friend you should use utl_dbws package not utl_http and the sample code at this link".
I'm not the only person in the world that needs to do this, right ?
Weird but i couldn't find any sample approved working piece of code that accomplishes calling a WCF service from Oracle.
Here is some of link i found about it;
https://forums.oracle.com/forums/thread.jspa?threadID=2354357
https://forums.oracle.com/forums/thread.jspa?threadID=1071996
http://steveracanovic.blogspot.com/2008/10/using-utldbws-package-to-call-web.html
https://forums.oracle.com/forums/thread.jspa?messageID=4205205&tstart=0#4205205
http://www.oracle-base.com/articles/10g/utl_dbws-10g.php
Noone writes any working code example or noone tells that this is not possible.
I would appreciate if anyone had a working code example that calling a WCF service from Oracle.
When you get a Http 500 error it is normally an internal error. For example that the developer is calling your service without setting all the input values, your code could then generate a divide by zero error, which when not caught is returned to the client as a http 500 error.
You can configure the soap version of a WCF service to be the same as an asmx service.
If you are getting response 500 (internal error) from WCF service, try set in web.config of WCF service includeexceptiondetailinfaults=true .
(http://msdn.microsoft.com/cs-cz/library/system.servicemodel.description.servicedebugbehavior.includeexceptiondetailinfaults(v=vs.110).aspx)
Then you will get detailed exception (wrong soap action, wrong format...)
Call WCF service from PL/SQL.
utl_http but it works.
/*
declare
p_request VARCHAR(32767);
p_plainResult VARCHAR2(32767);
begin
p_request := '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:Sum>
<tem:a>1</tem:a>
<tem:b>2</tem:b>
</tem:Sum>
</soapenv:Body>
</soapenv:Envelope>';
select callSOAPService(p_request,'http://tempuri.org/IMathService/Sum','http://localhost:51106/MathService.svc') into p_plainResult from dual;
end;
*/
create or replace function callSOAPService
(
p_plainRequest IN varchar2(20000),
p_actionName IN varchar2(1024), --SOAP Action ( in WCF, attribute [OperationContract(Action="ActionName")
p_url IN varchar2(1024),
p_userName varchar2(1024) := null,
p_password varchar2(1024) := null,
p_isAsynchronous boolean:= FALSE,
p_proxy varchar2(1024):=null,
p_transferTimeout number :=null,
)
RETURN VARCHAR2(32767)
IS
p_charset varchar2(1024) :='AL32UTF8'; --by default utf-8
p_request utl_http.req;
p_response utl_http.resp;
p_plainResponse varchar2(32767);
BEGIN
p_url := utl_url.escape(url => p_url); --escape url
if p_TransferTimeout > 0 THEN --set oracle timeout ( by defualt is 60 sec )
utl_http.set_transfer_timeout(timeout => p_transferTimeout);
END IF;
if p_proxy IS NOT NULL THEN --if proxy is provided, then set it
utl_http.set_proxy(proxy => p_proxy);
end if;
utl_http.set_response_error_check(enable => TRUE); --http status errorCheck ( 404 not found, 500 internal error...)
utl_http.set_detailed_excp_support(enable => TRUE); --detailed error stack
p_request := UTL_HTTP.begin_request(url => p_url,method => 'POST' /*u SOAP bude vzdy POST meotda*/ ,http_version => 'HTTP/1.1');
--pripravim si obalku
UTL_HTTP.set_header (r => p_request,name => 'Content-Type', value => 'text/xml');
UTL_HTTP.set_header (r => p_request,name => 'Content-Length',value => LENGTH (p_plainRequest));
UTL_HTTP.set_header (r => p_request, name => 'SOAPAction',value => p_actionName); --if status is 500 check SOAP action
UTL_HTTP.write_text(r => p_request,data => p_plainRequest);
p_response := UTL_HTTP.get_response (p_request);
if p_isAsynchronous THEN --one-way service
UTL_HTTP.end_response (p_response); --proto ukoncim request a vratim prazdno
RETURN '';
end if;
utl_http.read_text (p_response, p_plainResponse); --read response
utl_http.end_response (p_response); --close resposne
dbms_output.put_line ('Response from: ' || p_url || ' is ' || p_plainResponse); --vypisu odpoved pro kontrolu
return p_plainResponse;
EXCEPTION
when others then
dbms_output.put_line('Chyba ' || UTL_HTTP.get_detailed_sqlerrm()); --get error stack
utl_http.end_response (p_response);
END;
Related
Env: Oracle 12c
I'm looking at using Oracle DBMS_PIPE within a table trigger that will be used by many users. The trigger will fire only on a STATUS update as per below:
CREATE OR REPLACE TRIGGER MY_TRG
AFTER UPDATE OF STATUS ON "MY_TABLE"
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
declare
v_status INTEGER;
begin
if :OLD.status = 'ERROR' and (:NEW.status = 'OK' or :NEW.status = 'ERROR') then
DBMS_PIPE.PACK_MESSAGE(:OLD.id_key);
DBMS_PIPE.PACK_MESSAGE(:NEW.status);
v_status := DBMS_PIPE.SEND_MESSAGE('MY_PIPE');
if v_status != 0 THEN
raise_application_error(num => -20002,msg => 'error message on trigger!');
end if;
end if;
end;
The following call will be initiated from an Oracle APEX page process where this can be submitted again by multiple users.
DBMS_PIPE.receive_message(pipename => 'MY_PIPE', timeout => 10);
My question is, for each user here, do I need to ensure that the PIPE NAME is specific to each user, so that they only see their messages within their PIPE or can just the one 'MY_PIPE' pipe name handle all transactions for multiple users?
If in the case that each user needs their own designated PIPE NAME, how would I do this, if the SEND_MESSAGE('USER_1_PIPE') is triggered from a table trigger, which my receive_message_proc will be unaware of this 'USER_1_PIPE' name.
My create pipe is like this:
v_res := DBMS_PIPE.create_pipe(pipename => 'MY_PIPE', private => TRUE);
I assume I need to tag each user with their own private pipe name - is this correct?
Private pipes are private the username that created them. If you have multiple people logging on with the same user account, then they are all going to be able to see that pipe.
But perhaps a larger issue is that pipe are not transactional. So the moment that trigger fires, the message get put in the pipe...even if that transaction later rolls back, or fails or anything else that does not finally update the status. Moreover, the pipe message will be sent BEFORE the transaction commits. Another session (receiving that pipe message) will not be able to see changes done until the commit occurs, which can lead to timing inconsistencies.
Perhaps AQ (Advanced Queuing) is an alternative you might want to consider. Messages on a queue by default are transactional, so the message on the queue is then nicely bound to whether your changes to STATUS actually succeed.
The calling application just listens on a queue rather than on a pipe message.
I have a main application which calls a DLL. This DLL is where I do my HTTP requests using Indy's TIdHTTP component. It runs fine the first time, but then after closing the DLL and re-opening it, I get an HTTP/1.1 400 Bad Request exception:
var
idgetInfo : TIdHTTP;
begin
idgetInfo := TIdHTTP.Create(nil);
idgetInfo.AllowCookies := True;
idgetInfo.HandleRedirects := True;
try
result := idgetInfo.Get(GetString);
finally
if Assigned(idgetInfo) then
FreeAndNil(idgetInfo);
end;
end;
I have a requirement in which I need to update the existing records with new values/insert the new records by converting the data into JSON via Oracle backend(pl/sql). These JSON values need to be posted on URL.I am able to post sample values on the URL via Oracle but before updating/inserting any new values, It throws a mutating error in the trigger when I try to read the table..Can you please post some sample code for this?
Thanks in advance
CREATE OR REPLACE TRIGGER get_employees_trig before
INSERT ON emp_table REFERENCING OLD AS OLD NEW AS NEW FOR EACH
ROW DECLARE V_RET VARCHAR2(100);
BEGIN
dbms_output.put_line('fired-0');
IF :NEW.id <>:OLD.id THEN
V_RET := post_json_data_fnc(:NEW.id);
dbms_output.put_line('fired-1');
END IF;
dbms_output.put_line('fired-2');
END;
create or replace FUNCTION post_json_data_fnc(
p_id IN NUMBER)
RETURN CLOB
IS
req utl_http.req;
res utl_http.resp;
url varchar2(200);
l_clob CLOB;
l_xml CLOB;
l_txt CLOB;
--content varchar2(4000) := '{"name":"u14", "pass": "123","mail": "user#a.om"}';
BEGIN
URL := 'http://10.54.8.210:9200/temp/20';
l_xml := json_util_pkg.ref_cursor_to_json (emp_spec.get_employees(p_id));
req := utl_http.begin_request(URL, 'POST',' HTTP/1.1');
utl_http.set_header(req, 'user-agent', 'mozilla/4.0');
utl_http.set_header(req, 'content-type', 'application/json');
utl_http.set_header(req, 'Content-Length', LENGTH(l_xml));
l_txt := l_xml;
utl_http.write_text(req, l_txt);
res := utl_http.get_response(req);
utl_http.read_text(res,l_txt);
UTL_HTTP.END_RESPONSE(res);
dbms_output.put_line(l_txt);
return l_txt;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(res);
END;
create or replace FUNCTION post_json_data_fnc(
p_id IN NUMBER)
RETURN CLOB
IS
req utl_http.req;
res utl_http.resp;
url varchar2(200);
l_clob CLOB;
l_xml CLOB;
l_txt CLOB;
BEGIN
URL := 'http';
l_xml := json_util_pkg.ref_cursor_to_json (emp_spec.get_employees(p_id));
req := utl_http.begin_request(URL, 'POST',' HTTP/1.1');
utl_http.set_header(req, 'user-agent', 'mozilla/4.0');
utl_http.set_header(req, 'content-type', 'application/json');
utl_http.set_header(req, 'Content-Length', LENGTH(l_xml));
l_txt := l_xml;
utl_http.write_text(req, l_txt);
res := utl_http.get_response(req);
utl_http.read_text(res,l_txt);
UTL_HTTP.END_RESPONSE(res);
dbms_output.put_line(l_txt);
return l_txt;
EXCEPTION
WHEN UTL_HTTP.END_OF_BODY THEN
UTL_HTTP.END_RESPONSE(res);
END;
First, the mutating table exception is being thrown because you cannot in general query the table on which a row-level trigger is defined (emp_table in this case) from within the trigger or within code called by that trigger. My assumption is that something in the json_util_pkg.ref_cursor_to_json (emp_spec.get_employees(p_id)) call is querying emp_table. If you really, really wanted to do this from within a row-level trigger, you would need to remove any code that queries emp_table and recreate it using just the :new and :old pseudorecords.
However, you almost certainly don't want to do this or anything non-transactional in a trigger. What happens, for example, if your transaction is rolled back? Do you really want to have passed values to the web service that never actually existed in the table? What happens if Oracle does a partial or complete rollback and then re-executes the transaction for write consistency? Is your web service going to have a problem if you pass in duplicate data? In addition, do you really want to tie the availability of your system to the availability of the web service? So if the web server is down for a couple minutes, transactions on emp_table would fail?
It would make drastically more sense architecturally to build an asynchronous process. In the simplest case, you'd insert the primary key from emp_table into a separate table like emp_rows_to_be_processed. A background job (presumably using dbms_scheduler) would then periodically read that table, assemble the JSON, call the service, and then delete the row from emp_rows_to_be_processed. That allows the database to continue functioning if the web service is down briefly. The background job will only see rows that were actually committed. It won't see duplicates. And it doesn't have any problems with mutating tables.
I have the below function and I am quite frequently getting the below errors when front end ( ADF ) calls this function to get count.
Different errors comes at different times.
I think sometimes the listener is down so it might occur but other errors ?It happens when user from front end click on the link and that link in turn calls this database function.
I'm not able to find the issue in the function :-(
1) error "-12170 : ORA-12170: TNS:Connect timeout occurred"
2) error "-12571 : ORA-12571: TNS:packet writer failure"
3) error "-12541 : ORA-12541: TNS:no listener
4) error "-12514 : ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
5) error "-3113 : ORA-03113: end-of-file on communication channel
ORA-02063: preceding line from DBLINK"
6) error "-2051 : ORA-02051: another session or branch in same transaction failed or finalized
ORA-02063: preceding line from DBLINK"
FUNCTION GET_A (p_in IN VARCHAR2) RETURN NUMBER
AS
PRAGMA AUTONOMOUS_TRANSACTION;
PRE_COUNT NUMBER := NULL;
BEGIN
SELECT GET_B#DBLINK (p_in,NULL)INTO PRE_COUNT FROM dual;
COMMIT;
-- dbms_lock.sleep(2);
DBMS_SESSION.CLOSE_DATABASE_LINK('DBLINK');
COMMIT;
RETURN PRE_COUNT;
EXCEPTION
WHEN OTHERS
THEN
ROLLBACK;
LOG_ERR ( p_cd => SQLCODE,p_msg => SQLERRM);
RETURN -1;
END GET_A;
I am trying to send mail using utl_mail and for smtp server smtp.gmail.com with port 25 or 587.
If i use port 25,getting error, must use STARTTLS command first and for 587 its going in an infinite loop.
my code snippet are below
create or replace
PROCEDURE TEST_UTL_MAIL AS
BEGIN
utl_mail.send(
sender => 'xxx#gmail.com',
recipients => 'xxx#gmail.com',
subject => 'Testing utl_mail',
message => 'The receipt of this email means'
);
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'The following error has occured: ' || sqlerrm);
END;
Any help will be much appreciated.
You may have access control list issues. What is the error the code is reporting?
Also can you supply the output of this query (you'll need to supply the user parameter):
SELECT host, lower_port, upper_port, acl,
DECODE(
DBMS_NETWORK_ACL_ADMIN.CHECK_PRIVILEGE_ACLID(aclid, '&USER', 'connect'),
1, 'GRANTED', 0, 'DENIED', null) privilege
FROM dba_network_acls
;