I just want to call a web service from PL/SQL, so I create a web service :
http://localhost:64955/Service1.asmx?op=add
firstly, the web methods are very simple,like below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace Calculator
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public int add(int firstNum, int secondNum)
{
return firstNum + secondNum;
}
}
}
and I want to invoke the second method(add) in the web service, so I write my PL/SQL code like this:
declare
l_param_list varchar2(512);
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_response_text varchar2(32000);
begin
-- service's input parameters
l_param_list := 'firstNum=1'||'&'||'secondNum=2';
--http://localhost:64955/Service1.asmx?op=add
--16.158.161.7
-- prepareint Request...
l_http_request := UTL_HTTP.begin_request ('http://localhost:64955/Service1.asmx?op=add'
,'POST'
,'HTTP/1.1');
--...set header's attributes
UTL_HTTP.set_header(l_http_request,'Content-Type', 'application/x-www-form-urlencoded');
UTL_HTTP.set_header(l_http_request,'Content-Length', length(l_param_list));
--...set input parameters
UTL_HTTP.write_text(l_http_request, l_param_list);
-- get response and obtain received value
l_http_response := UTL_HTTP.get_response(l_http_request);
UTL_HTTP.read_text(l_http_response, l_response_text);
dbms_output.put_line(l_response_text);
dbms_output.put_line('test1');
--finalizing
UTL_HTTP.end_response(l_http_response);
exception
when UTL_HTTP.end_of_body then
UTL_HTTP.end_response(l_http_response);
dbms_output.put_line('test2');
end;
But when I run this PL/SQL code segment, I got some exceptions:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><soap:Fault><soap:Code><soap:Value>soap:Receiver</soap:Value></soap:Code><soap:Reason><soap:Text xml:lang="en">System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> System.Xml.XmlException: Root element is missing.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ThrowWithoutLineInfo(String res)
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlTextReader.Read()
at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.Read()
at System.Xml.XmlReader.MoveToContent()
at System.Web.Services.Protocols.SoapServerProtocol.SoapEnvelopeReader.MoveToContent()
at System.Web.Services.Protocols.SoapServerProtocolHelper.GetRequestElement()
at System.Web.Services.Protocols.Soap12ServerProtocolHelper.RouteRequest()
at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)
--- End of inner exception stack trace ---</soap:Text></soap:Reason><soap:Detail /></soap:Fault></soap:Body></soap:Envelope>
test1
I am a beginner on the PL/SQL, so can anybody tell me what's wrong with my code?
BTW, the web service can be invoked normally from a windows form application.
The problem is that your webservice is SOAP , but you are not sending a SOAP request.
To do this, check the wsdl of your webservice, by browsing this URL http://localhost:64955/Service1.asmx?wsdl ,from there you will know how to create the SOAP envelop to call your webmethod.
you would do something like :
soap_request :=
'<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:add xmlns:m="Some-URI">
<firstNum>1</firstNum>
<secondNum>2</secondNum>
</m:add>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
l_http_request := UTL_HTTP.begin_request ('http://localhost:64955/Service1.asmx?op=add'
,'POST'
,'HTTP/1.1');
--...set header's attributes
UTL_HTTP.set_header(l_http_request,'Content-Type', 'application/xml');
UTL_HTTP.set_header(l_http_request,'Content-Length', length(soap_request));
--...set input parameters
UTL_HTTP.write_text(l_http_request, soap_request);
-- get response and obtain received value
l_http_response := UTL_HTTP.get_response(l_http_request);
UTL_HTTP.read_text(l_http_response, l_response_text);
dbms_output.put_line(l_response_text);
dbms_output.put_line('test1');
--finalizing
UTL_HTTP.end_response(l_http_response);
Related
I'm invoking a web service that requires WS-Addressing SOAP headers. I'm using Apache Camel with CXF to invoke the web service. When I configure the CXF endpoint with the web service's WSDL, it's smart enough to automatically add WS-Adressing SOAP headers, but I need to set a custom MessageId.
Here is the message that is currently being sent:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<ws:international xmlns:ws="http://www.w3.org/2005/09/ws-i18n">
<ws:locale xmlns:ws="http://www.w3.org/2005/09/ws-i18n">en_CA</ws:locale>
</ws:international>
<fram:user wsa:IsReferenceParameter="true" xmlns:fram="http://wsbo.webservice.ephs.pdc.ibm.com/Framework/" xmlns:wsa="http://www.w3.org/2005/08/addressing">BESTSystem</fram:user>
<Action soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">http://webservice.ephs.pdc.ibm.com/Client/QueryHumanSubjects</Action>
<MessageID soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6</MessageID>
<To soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">https://panweb5.panorama.gov.bc.ca:8081/ClientWebServicesWeb/ClientProvider</To>
<ReplyTo soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
</soap:Header>
<soap:Body>
<ns2:queryHumanSubjectsRequest xmlns:ns2="http://wsbo.webservice.ephs.pdc.ibm.com/Client/" xmlns:ns3="http://wsbo.webservice.ephs.pdc.ibm.com/FamilyHealth/">
<!-- stuff -->
</ns2:queryHumanSubjectsRequest>
</soap:Body>
</soap:Envelope>
As you can see, the MessageId value is "urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6". I need to set a custom value.
I tried adding the MessageId header they way I add the other headers like "international" and "user", but some part of the framework overrides the value.
// Note this doesn't work! Something overrides the value. It works for other headers.
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
List<SoapHeader> headers = CastUtils.cast((List<?>) in.getHeader(Header.HEADER_LIST));
SOAPFactory sf = SOAPFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
QName MESSAGE_ID_HEADER = new QName("http://www.w3.org/2005/08/addressing", "MessageID", "wsa");
SOAPElement messageId = sf.createElement(MESSAGE_ID_HEADER);
messageId.setTextContent("customValue");
SoapHeader soapHeader = new SoapHeader(MESSAGE_ID_HEADER, messageId);
headers.add(soapHeader);
}
The CXF website has some documentation on how to set WS-Addressing headers, but I don't see how to apply it to Apache Camel. The Apache Camel CXF documentation doesn't specifically mention WS-Addressing either.
The documentation links you posted actually do have the information you need, although it's not immediately obvious how to apply it to Camel.
The CXF documentation says that:
The CXF org.apache.cxf.ws.addressing.impl.AddressingPropertiesImpl object can be used to control many aspects of WS-Addressing including the Reply-To:
AddressingProperties maps = new AddressingPropertiesImpl();
EndpointReferenceType ref = new EndpointReferenceType();
AttributedURIType add = new AttributedURIType();
add.setValue("http://localhost:9090/decoupled_endpoint");
ref.setAddress(add);
maps.setReplyTo(ref);
maps.setFaultTo(ref);
((BindingProvider)port).getRequestContext()
.put("javax.xml.ws.addressing.context", maps);
Note that it sets the addressing properties on the "RequestContext".
The Apache Camel documentation says that:
How to propagate a camel-cxf endpoint’s request and response context
CXF client API provides a way to invoke the operation with request and response context. If you are using a camel-cxf endpoint producer to invoke the outside web service, you can set the request context and get response context with the following code:
CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
public void process(final Exchange exchange) {
final List<String> params = new ArrayList<String>();
params.add(TEST_MESSAGE);
// Set the request context to the inMessage
Map<String, Object> requestContext = new HashMap<String, Object>();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
exchange.getIn().setBody(params);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
}
});
The above example has some stuff we don't need, but the important thing is that it shows us how to set the CXF Request Context.
Put them together and you get:
#Override
public void process(Exchange exchange) throws Exception {
AttributedURIType messageIDAttr = new AttributedURIType();
messageIDAttr.setValue("customValue");
AddressingProperties maps = new AddressingProperties();
maps.setMessageID(messageIDAttr);
Map<String, Object> requestContext = new HashMap<>();
requestContext.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT, requestContext);
}
// org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES = "javax.xml.ws.addressing.context"
// org.apache.cxf.endpoint.Client.REQUEST_CONTEXT = "RequestContext"
Warning: In my route, I invoke multiple different web services sequentially. I discovered that after setting the RequestContext as shown above, Camel started using the same RequestContext for all web services, which resulted in an error: "A header representing a Message Addressing Property is not valid and the message cannot be processed". This is because the incorrect "Action" header was used for all web service invocations after the first.
I traced this back to Apache Camel using a "RequestContext" Exchange property, separate from the header we set, which apparently takes priority over the header. If I remove this property prior to calling subsequent web services, CXF automatically fills in the correct Action header.
if your problem not solved, I suggest you to combine your cxf service with custom interceptor. it easy to work with your soap message. like this:
<bean id="TAXWSS4JOutInterceptorBean" name="TAXWSS4JOutInterceptorBean" class="com.javainuse.beans.SetDetailAnswerInterceptor " />
<cxf:cxfEndpoint id="CXFTest" address="/javainuse/learn"
endpointName="a:SOATestEndpoint" serviceName="a:SOATestEndpointService"
serviceClass="com.javainuse.SOATestEndpoint"
xmlns:a ="http://javainuse.com">
<cxf:binding>
<soap:soapBinding mtomEnabled="false" version="1.2" />
</cxf:binding>
<cxf:features>
<wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
</cxf:features>
<cxf:inInterceptors>
<ref bean="TAXWSS4JInInterceptorBean" />
</cxf:inInterceptors>
<cxf:inFaultInterceptors>
</cxf:inFaultInterceptors>
<cxf:outInterceptors>
<ref bean="TAXWSS4JOutInterceptorBean" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
</cxf:outFaultInterceptors>
</cxf:cxfEndpoint>
and in the interceptor you can set soap headers like this:
public class SetDetailAnswerInterceptor extends WSS4JOutInterceptor {
public SetDetailAnswerInterceptor() {
}
#Override
public void handleMessage(SoapMessage mc) {
AttributedURIType value = new AttributedURIType();
value.setValue("test");
((AddressingProperties) mc.get("javax.xml.ws.addressing.context.outbound")).setMessageID(value);
}
}
I'm working on custom axis2 module for wso2 esb. Right now I'm using code from https://docs.wso2.com/display/ESB490/Writing+an+Axis2+Module
and I have a problem with incoming requests. It doesn't matter what request I send because it always looks like this:
<?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body/></soapenv:Envelope>
On the other hand OutFlow works more or less as it should - response looks ok but instead of "out" its direction is set as "in". If I'm not mistaken invoke method will be called for requests and revoke for responses - am I right? In my case both are using invoke. Any ideas what I'm doing wrong?
Edit:
My handler code:
public class LogHandler extends AbstractHandler implements Handler {
private Logger log = Logger.getLogger(LogHandler.class.toString());
#Override
public void init(HandlerDescription handlerDescription) {
super.init(handlerDescription);
}
public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
System.out.println("invoked: " + msgContext.getEnvelope().toString() + "\n");
log.info("invoked: " + msgContext.getEnvelope().toString() + "\n");
return InvocationResponse.CONTINUE;
}
public void revoke(MessageContext msgContext) {
log.info("revoked: " + msgContext.getEnvelope().toString() + "\n");
}
}
Module:
public class LoggingModule implements Module {
private static final Log log = LogFactory.getLog(LoggingModule.class);
// initialize the module
public void init(ConfigurationContext configContext, AxisModule module) throws AxisFault {
}
public void engageNotify(AxisDescription axisDescription) throws AxisFault {
}
// shutdown the module
public void shutdown(ConfigurationContext configurationContext) throws AxisFault {
}
public String[] getPolicyNamespaces() {
return null;
}
public void applyPolicy(Policy policy, AxisDescription axisDescription) throws AxisFault {
}
public boolean canSupportAssertion(Assertion assertion) {
return true;
}
}
module.xml:
<module name="sample-logging" class="pl.wso2.logging.LoggingModule">
<InFlow>
<handler name="InFlowLogHandler" class="pl.wso2.logging.LogHandler">
<order phase="loggingPhase"/>
</handler>
</InFlow>
<OutFlow>
<handler name="OutFlowLogHandler" class="pl.wso2.logging.LogHandler">
<order phase="loggingPhase"/>
</handler>
</OutFlow>
</module>
In my wso2 proxy I use Payload Mediator to create response and then return it using Respond Mediator.
For given request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<aa>blahblah</aa>
</soapenv:Body>
</soapenv:Envelope>
there two thing logged:
request from InFlow
invoked: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlso
ap.org/soap/envelope/"><soapenv:Body/></soapenv:Envelope>
and response from OutFlow
invoked: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlso
ap.org/soap/envelope/"><soapenv:Body><m:checkpriceresponse xmlns:m="http://services.samples/xsd"><m:
code>dsadsa</m:code></m:checkpriceresponse></soapenv:Body></soapenv:Envelope>
As per https://axis.apache.org/axis2/java/core/docs/modules.html#Step2_:_LogHandler ...
"public void invoke(MessageContext ctx);" is the method that is called
by the Axis2 engine when the control is passed to the handler. "public
void revoke(MessageContext ctx);" is called when the handlers are
revoked by the Axis2 engine."
which means since you are calling same handler in both InFlow and OutFlow the same invoke() method should be getting triggered for both the request and the response. If you want different logics to be executed for requests and responses maybe you should write separate handlers for request and response.
After debugging everything I've found that while request was parsed in InFlow, instead of using its soap message, new one (empty) was created. Thankfully it's possible to access proper request using soap tracer handler (or just its code).
I've got a jax-ws webservice which I need to secure. When I start wildfly everything seems to load nicely, but when I try to send a request via SOAPUI tool, the server returns Error with body element - unauthorized. Relevant parts of code below.
Webservice itself:
#WebService
#Stateless
#RolesAllowed("testrole")
#SecurityDomain("test-domain")
public class Test {
#WebMethod
#WebResult(name = "HelloResponse")
public String sayHello(#WebParam(name = "username") String name) {
return "Hello " + name;
}
}
standalone.xml:
<security-domain name="test-domain" cache-type="default">
<authentication>
<login-module code="org.jboss.security.auth.spi.UserRolesLoginModule"
flag="required">
<module-option name="userProperties" value="test-domain-users.properties" relative-to="jboss.server.config.dir"/>
<module-option name="rolesProperties" value="test-domain-roles.properties" relative-to="jboss.server.config.dir"/>
</login-module>
</authentication>
</security-domain>
jboss-web.xml:
<jboss-web>
<security-domain>test-domain</security-domain>
</jboss-web>
test-domain-users.properties and test-domain-roles.properties look like this:
testuser=testpassword, testuser=testrole
I'm trying to create an inbound gateway for a SOAP service, that accepts SOAP requests like the following:
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<RequestHeader xmlns="http://test.com/">
<SecurityToken>mytoken</SecurityToken>
<RequestID>1234</RequestID>
</RequestHeader>
</S:Header>
<S:Body>
<BaseRequest xmlns="http://test.com/">
<RequestData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="BalanceRequest">
<CustomerID>1234</CustomerID>
</RequestData>
</BaseRequest>
</S:Body>
</S:Envelope>
I want to use JAXB for marshalling/unmarshalling the request/response. I have managed to configure Spring/Spring-integration with the following:
<oxm:jaxb2-marshaller id="soapMarshaller" context-path="com.test" />
<int:channel id="soap-channel"/>
<int-ws:inbound-gateway id="ws-inbound-gateway"
request-channel="soap-channel"
marshaller="soapMarshaller"/>
<int:service-activator input-channel="soap-channel">
<bean class="com.test.SoapServiceActivator"/>
</int:service-activator>
And I have tried to extract the SOAP header and body in the service activator.
#ServiceActivator
public BaseResponse issueResponseFor(BaseRequest body,
#Headers Map<String, Object> headerMap) {
return null;
}
BaseRequest is a JAXB annotated class.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"requestData"
})
#XmlRootElement(name = "BaseRequest")
public class BaseRequest {
#XmlElement(name = "RequestData")
protected BaseRequestBody requestData;
}
My problem is that in the variable body, I get the body of the SOAP request, but I didn't find anyway to extract the headers of the SOAP request. The headerMap variable holds only the standard Spring-Integration headers (replyChannel, errorChannel, id, timestamp). By headers, I mean the SecurityToken+RequestID, and also the HTTP header with the name of the requested action.
Any idea how to do that ?
Try to use mapped-request-headers="*".
By default the the DefaultSoapHeaderMapper maps only standard headers. And in this case it is only WebServiceHeaders.SOAP_ACTION
UPDATE
Quoting Omar :-)
Thanks ! The following code is working great :
#Autowired
#Qualifier("soapMarshaller")
Jaxb2Marshaller marshaller;
#ServiceActivator
public BaseResponse issueResponseFor(BaseRequest request, #Header("RequestHeader") SoapHeaderElement soapHeader) {
BaseRequestHeader requestHeader = (BaseRequestHeader) JAXBIntrospector.getValue(marshaller.unmarshal(soapHeader.getSource()));`
What I want to do:
Implement a Camel route from a CXF Endpoint to an JMS queue with schema validation in CXF endpoint.
Validation is enabled in CXF endpoint:
/* Set endpoint properties */
Map<String, Object> propertiesMap = new HashMap<String, Object>();
propertiesMap.put("schema-validation-enabled", "true");
/* Create endpoint */
CxfEndpoint cxfEndpoint = new CxfEndpoint();
cxfEndpoint.setWsdlURL("wsdl/input.wsdl");
cxfEndpoint.setDataFormat(DataFormat.CXF_MESSAGE);
cxfEndpoint.setProperties(propertiesMap);
cxfEndpoint.getInInterceptors().add(new FaultInterceptor());
The Camel route:
from(cxfEndpoint)
.routeId("INPUT_ROUTE")
.to("jms:foo.bar");
The CXF interceptor:
public class FaultInterceptor extends AbstractSoapInterceptor {
private static final Logger LOGGER = Logger.getLogger(FaultInterceptor.class);
public FaultInterceptor() {
super(Phase.UNMARSHAL);
}
public void handleMessage(SoapMessage message) throws Fault {
LOGGER.info("handleMessage=" + message.getExchange().getInMessage());
}
#Override
public void handleFault(SoapMessage message) {
Fault fault = (Fault) message.getContent(Exception.class);
LOGGER.info("handleFault='" + fault + "'");
/* Add some header property that says the message is invalid */
}
}
Problem:
The works ok if I send a valid SOAP message. If I send an invalid SOAP message, the handleFault method kicks in, logs the fault and that's all.
For the invalid SOAP message scenario, is it possible that I can log the fault with handleFault method and still route the invalid message to the JMS queue?
This is the only interceptor I've added to the endpoint.
I'm using:
Apache ServiceMix 5.0.0
Apache Camel 2.12.3
Apache CXF 2.7.10
You can't do a try/catch statement since the error happends in the "from" clause.
However, you can use a Dead Letter Channel.
errorHandler(deadLetterChannel("jms:foo.bar.invalid"));
from(cxfEndpoint)
.routeId("INPUT_ROUTE")
.to("jms:foo.bar");