This post is a quick outline on how to consume a Microsoft SharePoint web service from within the Oracle Database. The SharePoint instance I was dealing with had NTLM A enabled. The support for this type of authentication in PL/SQL is pretty much non-existent. The magnificent Alexandria PL/SQL Utilities library, however, includes the NTLM_HTTP_PKG package which does all the heavy lifting for you in this area. I simply installed the Alexandria library in a separate database schema and granted execute privileges to my APEX application’s parsing schema. A great example on how open source initiatives can make a big difference.
The next step for me was to actually call a SharePoint web service. I started off by trying to upload a (BLOB) file to a document library. For this functionality, I have used the Copy endpoint and CopyIntoItems SOAP action. An overview of all available actions under a specific endpoint is available by browsing to the following kind of URLs:
http://<sharepoint-server>/<customsite>/_vti_bin/Copy.asmx http://<sharepoint-server>/<customsite>/_vti_bin/Dws.asmx http://<sharepoint-server>/<customsite>/_vti_bin/Lists.asmx ...
Also, don’t forget to properly configure your database’s Access Control List (ACL) before invoking the web service. You’ll run into “ORA-24247: network access denied by access control list (ACL)” when something is missing in your ACL.
My plan was to use the APEX_WEB_SERVICE API package to invoke the SharePoint web service. Unfortunately, this technique didn’t work out for me. Using the APEX_WEB_SERVICE package, I constantly received a 401 Unauthorized response, telling me that there was something wrong with the user authentication. Now, I can’t tell you what the problem was, but calling the web service in SoapUI did give back a successful result. That’s why I stopped using the APEX_WEB_SERVICE package and started using UTL_HTTP instead, with success this time.
Here’s the code that calls the Copy SharePoint web service and uploads a file to a target document library.
set serveroutput on; declare l_file_location varchar2(400) := 'http://my.sharepoint.com/org/department/MY_DOC_LIBRARY/'; l_filename varchar2(256) := 'my-new-file.docx'; l_file_content blob; lco_ntlm_auth_domain constant varchar2(30) := 'MY.DOMAIN'; lco_ntlm_auth_username constant varchar2(30) := 'MY_USERNAME'; lco_ntlm_auth_password constant varchar2(30) := 'MY_PASSWORD'; l_ntlm_auth_string varchar2(4000); lco_web_service_url constant varchar2(400) := 'http://my.sharepoint.com/org/department/_vti_bin/Copy.asmx'; lco_web_service_soap_action constant varchar2(400) := '"http://schemas.microsoft.com/sharepoint/soap/CopyIntoItems"'; l_http_request utl_http.req; l_request_body clob; l_request_body_length number; l_http_response utl_http.resp; l_response_header_name varchar2(256); l_response_header_value varchar2(1024); l_response_body varchar2(32767); l_offset number := 1; l_amount number := 2000; l_buffer varchar2(2000); begin l_ntlm_auth_string := alexandria.ntlm_http_pkg.begin_request( p_url => lco_web_service_url, p_username => lco_ntlm_auth_domain || '\' || lco_ntlm_auth_username, p_password => lco_ntlm_auth_password ); select my_blob into l_file_content from my_file; l_request_body := '<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <CopyIntoItems xmlns="http://schemas.microsoft.com/sharepoint/soap/"> <SourceUrl>' || l_filename || '</SourceUrl> <DestinationUrls> <string>' || l_file_location || l_filename || '</string> </DestinationUrls> <Fields> <FieldInformation Type="File" DisplayName="' || l_filename || '" Value="' || l_filename || '" /> </Fields> <Stream>' || apex_web_service.blob2clobbase64(l_file_content) || '</Stream> </CopyIntoItems> </soap:Body> </soap:Envelope>'; l_request_body_length := dbms_lob.getlength(l_request_body); l_http_request := utl_http.begin_request( url => lco_web_service_url, method => 'POST', http_version => 'HTTP/1.1' ); utl_http.set_header(l_http_request, 'User-Agent', 'Mozilla/4.0'); utl_http.set_header(l_http_request, 'Content-Type', 'text/xml;charset=UTF-8'); utl_http.set_header(l_http_request, 'SOAPAction', lco_web_service_soap_action); utl_http.set_header(l_http_request, 'Content-Length', l_request_body_length); utl_http.set_header(l_http_request, 'Transfer-Encoding', 'chunked'); utl_http.set_header(l_http_request, 'Authorization', l_ntlm_auth_string); while l_offset < l_request_body_length loop dbms_lob.read(l_request_body, l_amount, l_offset, l_buffer); utl_http.write_text(l_http_request, l_buffer); l_offset := l_offset + l_amount; end loop; l_http_response := utl_http.get_response(l_http_request); dbms_output.put_line('Response> Status Code: ' || l_http_response.status_code); dbms_output.put_line('Response> Reason Phrase: ' || l_http_response.reason_phrase); dbms_output.put_line('Response> HTTP Version: ' || l_http_response.http_version); for i in 1 .. utl_http.get_header_count(l_http_response) loop utl_http.get_header(l_http_response, i, l_response_header_name, l_response_header_value); dbms_output.put_line('Response> ' || l_response_header_name || ': ' || l_response_header_value); end loop; utl_http.read_text(l_http_response, l_response_body, 32767); dbms_output.put_line(l_response_body); if l_http_request.private_hndl is not null then utl_http.end_request(l_http_request); end if; if l_http_response.private_hndl is not null then utl_http.end_response(l_http_response); end if; exception when others then if l_http_request.private_hndl is not null then utl_http.end_request(l_http_request); end if; if l_http_response.private_hndl is not null then utl_http.end_response(l_http_response); end if; raise; end; /
Executing the above code gives me back the following output.
PL/SQL procedure successfully completed.
Response> Status Code: 200 Response> Reason Phrase: OK Response> HTTP Version: HTTP/1.1 Response> Cache-Control: private, max-age=0 Response> Content-Type: text/xml; charset=utf-8 Response> Server: Microsoft-IIS/7.0 Response> SPRequestGuid: 890faafe-cfa4-43b1-8a74-ec2a4196b0d2 Response> X-SharePointHealthScore: 2 Response> X-AspNet-Version: 2.0.50727 Response> X-Powered-By: ASP.NET Response> MicrosoftSharePointTeamServices: 14.0.0.7116 Response> X-MS-InvokeApp: 1; RequireReadOnly Response> Date: Thu, 05 Nov 2015 08:15:18 GMT Response> Content-Length: 538
<?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soap:Body> <CopyIntoItemsResponse xmlns="http://schemas.microsoft.com/sharepoint/soap/"> <CopyIntoItemsResult>0</CopyIntoItemsResult> <Results> <CopyResult ErrorCode="Success" DestinationUrl="http://my.sharepoint.com/org/department/MY_DOC_LIBRARY/my-new-file.docx" /> </Results> </CopyIntoItemsResponse> </soap:Body> </soap:Envelope>
These are the products that I have used when writing the above code:
- Oracle Database 11g Express Edition 11.2.0.2.0 with Oracle Application Express 4.0.2
- Microsoft SharePoint 2010 without SSL, with NTLM authentication
The example was very useful, but I believe it doesn’t work when the file size increases.
I am trying to upload 6mb file and it fails with
length of request Body : 8719897
Response> Status Code: 401
Response> Reason Phrase: Unauthorized
Response> HTTP Version: HTTP/1.1
Response> Server: Microsoft-IIS/7.5
Response> SPRequestGuid: c307cd80-431e-411c-954a-67b70e4bd0c5
Response> WWW-Authenticate: NTLM
Response> X-Powered-By: ASP.NET
Response> MicrosoftSharePointTeamServices: 14.0.0.6106
Response> Date: Tue, 24 Nov 2015 01:10:34 GMT
Response> Content-Length: 0
With a smaller file :
length of request Body : 1803
Response> Status Code: 200
Response> Reason Phrase: OK
Response> HTTP Version: HTTP/1.1
Response> Cache-Control: private, max-age=0
Response> Content-Type: text/xml; charset=utf-8
Response> Server: Microsoft-IIS/7.5
Response> SPRequestGuid: 7a368425-26e1-4fc2-b566-cde30fc2d961
Response> Set-Cookie: WSS_KeepSessionAuthenticated={1452b94d-f563-4760-a6cf-e37bb92b8507}; path=/
Response> X-SharePointHealthScore: 0
Response> Set-Cookie: WSS_KeepSessionAuthenticated={1452b94d-f563-4760-a6cf-e37bb92b8507}; path=/
Response> X-AspNet-Version: 2.0.50727
Response> Persistent-Auth: true
Response> X-Powered-By: ASP.NET
Response> MicrosoftSharePointTeamServices: 14.0.0.6106
Response> Date: Tue, 24 Nov 2015 00:27:01 GMT
Response> Content-Length: 558
LikeLike
I have managed to upload an 8.6mb file. It takes some time, but it succeeds. I only had to increase the transfer timeout length by calling the UTL_HTTP.SET_TRANSFER_TIMEOUT procedure (http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/u_http.htm#i1027823).
Is it possible that the SharePoint environment is configured in a way to not accept files this large? 6mb isn’t much of course. I believe 50mb is the default file size limit.
The following article might help you: http://blogs.technet.com/b/sharepointcomic/archive/2010/02/14/sharepoint-large-file-upload-configuration.aspx
Nick
LikeLike
Thanks Nick. After increasing the timeout limits I was able to push a file succesfully but somehow Sharepoint takes long time to transfer a zip file compared to csv file.
LikeLiked by 1 person
hello nick,
You have a good write up there. I tried to follow the step and when i called the procedure from a trigger which fires after i insert into a table in oracle database. i got the error below:
“ORA-06502: PL/SQL: numeric or value error: raw variable length too long”
what do you feel the issue could be.
Thanks
LikeLike
That’s difficult to say. Can you call the procedure from an anonymous block? If you get the same error there, you should be able to specifically locate the line of code that throws the ORA-06502 error. This approach should make it easier to solve the problem.
Also, use the DBMS_UTILITY.FORMAT_ERROR_BACKTRACE function to get more detailed error information.
Nick
LikeLike
Hi Nick,
I need to contact you about using this script but with navision.
Could you please reply to my email? Info@arananet.net
LikeLike