I am having problems accessing a web service using Apache CXF generated classes (wsdl2java).
Everything was working fine until the web service provider suddenly changed a rule on their firewall and hosed us. What they are saying is they no longer allow http traffic, we have to start using https. The funny thing is, up until now we thought we were using https. This is inherited, legacy code. But we are pretty much using word for word the ssl example on the Apache CXF web site.
Here's a chunk of the wsdl:
...
<wsdl:portType name="GetMessagesSoap">
<wsdl:operation name="GetInfo">
<wsdl:input message="tns:GetInfoSoapIn" />
<wsdl:output message="tns:GetInfoSoapOut" />
</wsdl:operation>
</wsdl:portType>
...
<wsdl:service name="GetMessages">
<wsdl:port name="GetMessagesSoap" binding="tns:GetMessagesSoap">
<soap:address location="http://127.0.0.1/GetMessages.asmx" />
</wsdl:port>
</wsdl:service>
Basically we generate all the Java classes using wsdl2java. Then in our Gateway class we do the following:
private GetMessagesSoap getInstance() {
GetMessages getMessages = new GetMessages(wsdlUrl);
GetMessagesSoap getMessagesSoap = getMessages.getMessagesSoap();
setupTransportLayerSecurity(getMessagesSoap);
}
private void setupTransportLayerSecurity(final Object port) {
HTTPConduit httpConduit =
(HTTPConduit) ClientProxy.getClient(port).getConduit();
TLSClientParameters tlsCP = new TLSClientParameters();
KeyStore keyStore = ...
KeyManager[] myKeyManagers = getKeyManagers(keyStore ...
tlsCP.setKeyManagers(myKeyManagers);
KeyStore trustStore = ...
TrustManager[] myTrustStoreKeyManagers = getTrustManagers(trustStore);
tlsCP.setTrustManagers(myTrustStoreKeyManagers);
tlsCP.setDisableCNCheck(true);
tlsCP.setSecureSocketProtocol("SSL");
httpConduit.setTlsClientParameters(tlsCP);
}
Then we call getInfo(). This is where the error occurs.
public void getInfo() {
GetMessagesSoap getMessagesSoap = getInstance();
InfoResponse response = getMessagesSoap.getInfo()
}
The error we get is rather generic:
javax.xml.ws.WebServiceException: Could not send Message.
The ws provider says it's failing because we're trying to come in using http. All this despite all the fancy SSL stuff we're doing in the setupTransportLayerSecurity() method.
My fundamental doubt in all this is, is that really the reason it's failing? I mean, obviously it started failing when the rule was removed from the firewall. But, what I don't understand is, if the url in the GetMessages service in the wsdl is http, even if I say to use SSL, how in the world is it ever going to use https?
We've put a sniffer on the network and sure enough it's only using http. But ... isn't this normal?? I am by no means a SOAP expert, obviously. Can someone shed some light on this problem?
CXF HttpConduit only uses the TlsClientParameters when the address url is start with https.
Can you change the WSDL file?
If not, you can change service url at the runtime.
Related
I have this WSDL:
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://sei.esempio.it/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:ns2="http://schemas.xmlsoap.org/soap/http"
xmlns:ns1="http://esempio.it/" name="XServiziService"
targetNamespace="http://sei.esempio.it/">
<wsdl:import location="http://example.lan:8082/XServizi?wsdl=Sei.wsdl"
namespace="http://esempio.it/"> </wsdl:import>
<wsdl:binding name="XServiziServiceSoapBinding" type="ns1:Sei">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="HelloWorldOperation">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="HelloWorldOperation">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="HelloWorldOperationResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="XServiziService">
<wsdl:port binding="tns:XServiziServiceSoapBinding" name="XServiziPort">
<soap:address location="http://example.lan:8082/XServizi"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
I tried to import with SoapClient in PHP, but I get this error:
SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://example.lan:8083/XServices?wsdl' : failed to load external entity "https://example.lan:8083/XServices?wsdl"
I added also login and pass in SoapClient. I don't know why, with other WSDL, it works.
I can't import with SoapUI either. I get this error:
Error loading [http://example.lan:8082/XServices?wsdl=Sei.wsdl]: org.apache.xmlbeans.XmlException: org.apache.xmlbeans.XmlException: error: does not close tag .
I disabled also the proxy in SoapUI, but it doesn't work.
Any suggestions?
Based on the error messages, the problem is related to the <wsdl:import> inside the WSDL file you posted. Your WSDL is composed of two parts: the one you are diplaying and the one that can be found at the URL http://example.lan:8082/XServizi?wsdl=Sei.wsdl
Your error messages show two different things.
One has a problem with https://example.lan:8083/XServices?wsdl and the other with http://example.lan:8082/XServices?wsdl=Sei.wsdl. I'm not sure how you edited these URLs for privacy when posting this question and why one is on port 8083 and the other on port 8082, but the idea is that all parts of the WSDL need to be accessible:
the URL of the main WSDL needs to be accessible by your tooling;
the URL that the main WSDL imports also needs to be accessible by your tooling;
Only then will your tooling be able to read the WSDL and all its parts and generate a SOAP client.
Note also that some tools choke on WSDLs with imports (see here for details). Not familiar enough with PHP SoapClient though to know if that's the case here.
Since you can't control the service, your options are to somehow make both XMLs available to your tooling.
First make sure the URLs are accessible.
If that's not the case, then somehow get a hold of their content. When you have them, you could save the content of the two URLs in your local PHP server as main.wsdl (this is the WSDL you are showing in your question) and second.xml (this is what's imported). Then in your main.wsdl you change the import location. For example, if you expose the files locally as http://localhost:80/main.wsdl and http://localhost:80/second.xml then in main.wsdl you change the import to look like this:
<wsdl:import location="http://localhost:80/second.xml" namespace="http://esempio.it/"></wsdl:import>
then point the SoapClient or SoapUI to read http://localhost:80/main.wsdl instead.
You could try this on disk and place the files one next to each other in the same folder, then change the import to:
<wsdl:import location="second.xml" namespace="http://esempio.it/"></wsdl:import>
then point the tools to the main.wsdl file on disk. I'm not sure this will work though. Have never tried it. SoapUI might be able to fetch the files from the same folder, the PHP SoapClient I doubt so.
There is also the option of downloading both XML files and combine them into just one WSDL file (basically resolving the import manually) then use just this file for your tooling. You will have to know what you are doing though, in order to obtain a valid and correct WSDL file.
Or finally, you could ask the service provider to give you just one WSDL file you can use instead. There is no point of asking them to change the current WSDL because most likely they have other clients too and fixing this for you might break stuff for somebody else (as mentioned in the link above).
But this is probably just an accessibility issue, so make sure those URLs are accessible from your browser before using them with SOAP tools.
I have deployed a web service on OpenShift by annotating a class with #WebService and a public method with #WebMethod. Then, I deployed the class in a WildFly8 container and the server started the service. In order to be able to access the WSDL file I had to create a file "jboss-web.xml" inside the WEB-INF folder and change the context-root.
Now I am able to access the WSDL file, but not the service itself. The problem is in this line of the wsdl file:
<wsdl:service name="PlotterService">
<wsdl:port binding="tns:PlotterServiceSoapBinding" name="PlotterPort">
<soap:address location="http://127.8.173.1:8080/Plotter"/>
</wsdl:port>
</wsdl:service>
The address location is set to an internal IP and should be replaced with the URL of my application. How can I do that? The only file I am able to modify is the class that I annotated at the beginning.
I managed to solve this:
Openshift does not have Web services setup by default, so we need to modify the server configuration. To do this open .openshift/config/standalone.xml (this file may be hidden) in an editor and make the following additions:
If the webservices subsystem is not configured as below under the element, copy the following and replace the webservices subsystem to enable and configure Web Services:
<subsystem xmlns="urn:jboss:domain:webservices:1.1">
<modify-wsdl-address>true</modify-wsdl-address>
<wsdl-host>${env.OPENSHIFT_APP_DNS}</wsdl-host>
<wsdl-port>80</wsdl-port>
<endpoint-config name="Standard-Endpoint-Config"/>
<endpoint-config name="Recording-Endpoint-Config">
<pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
<handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
</pre-handler-chain>
</endpoint-config>
</subsystem>
This problem is present in mule 1.3.2-201212121943
WSDL soap:address location is not correctly rewritten by web-service-proxy with .NET WS
<mule ...>
<https:connector name="HTTP_HTTPS" sendBufferSize="0" receiveBufferSize="0" receiveBacklog="0" clientSoTimeout="10000" serverSoTimeout="10000" doc:name="HTTP\HTTPS">
<https:tls-client path="xyz\keystore.jks" storePassword="mulepassword"/>
</https:connector>
<pattern:web-service-proxy name="xyz-ws-proxy" >
<http:inbound-endpoint address="http://localhost:8081"/>
<https:outbound-endpoint followRedirects="true" address="https://xyz/g2g/BPM#[header:inbound:http.request.path]" connector-ref="HTTP_HTTPS" />
</pattern:web-service-proxy>
</mule>
http://localhost:8081/xyz.asmx?wsdl returns wsdl with
<wsdl:port name="XYZEndpointSoap" binding="tns:XYZEndpointSoap">
<soap:address location="http://xyz/g2g/BPM/xyz.asmx" />
</wsdl:port>
<wsdl:port name="XYZEndpointSoap12" binding="tns:XYZEndpointSoap12">
<soap12:address location="http://xyzg2g/BPM/xyz.asmx" />
</wsdl:port>
With another jax-ws service, this configuration works - there was only one soap:address hmm.
This is .NET WS.
Thank you
PS: I have read google results and closed mule issues but nothing works for this service.
I have checked and pattern:web-service-proxy has no problem rewrite all the addresses of a WSDL that contains both a soap:address and a soap12:address.
The issue could actually be due to the way you get the WSDL: with the HTTP inbound endpoint bound to http://localhost:8081 with no path (something I would advocate against: it's better to have a path), the WSDL is available at http://localhost:8081/?wsdl, not http://localhost:8081/xyz.asmx?wsdl.
I am getting the following error when using CXF to consume a web service. The exception message is:
No binding factory for namespace http://schemas.xmlsoap.org/wsdl/http/ registered.
The WSDL service which I am trying to consume is:
<wsdl:service name="MyService">
<wsdl:port name="MyServiceHttpport" binding="impl:MyServiceHttpBinding">
<http:address location="http://localhost:80/ip/services/MyService"/>
</wsdl:port>
</wsdl:service
As seen above, there us http:address instead of soap:address
CXF doesn't support the WSDL http binding. The cxf-rt-bindings-http module has been removed from the recent CXF releases as it was relatively proprietary and is now replaced with all the REST goodness of the JAX-RS frontend.
I generated a web-service client using JBoss utils (JAX-WS compatible)
using Eclipse 'web service client from a wsdl'.
So, the only thing I provided was a url to a web-service WSDL.
Now, the web service provider tells me to change the "url of client endpoint application access" of the web-service.
What is it and how to change it?
IMO, the provider is telling you to change the service endpoint (i.e. where to reach the web service), not the client endpoint (I don't understand what this could be). To change the service endpoint, you basically have two options.
Use the Binding Provider to set the endpoint URL
The first option is to change the BindingProvider.ENDPOINT_ADDRESS_PROPERTY property value of the BindingProvider (every proxy implements javax.xml.ws.BindingProvider interface):
...
EchoService service = new EchoService();
Echo port = service.getEchoPort();
/* Set NEW Endpoint Location */
String endpointURL = "http://NEW_ENDPOINT_URL";
BindingProvider bp = (BindingProvider)port;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);
System.out.println("Server said: " + echo.echo(args[0]));
...
The drawback is that this only works when the original WSDL is still accessible. Not recommended.
Use the WSDL to get the endpoint URL
The second option is to get the endpoint URL from the WSDL.
...
URL newEndpoint = new URL("NEW_ENDPOINT_URL");
QName qname = new QName("http://ws.mycompany.tld","EchoService");
EchoService service = new EchoService(newEndpoint, qname);
Echo port = service.getEchoPort();
System.out.println("Server said: " + echo.echo(args[0]));
...
To add some clarification here, when you create your service, the service class uses the default 'wsdlLocation', which was inserted into it when the class was built from the wsdl. So if you have a service class called SomeService, and you create an instance like this:
SomeService someService = new SomeService();
If you look inside SomeService, you will see that the constructor looks like this:
public SomeService() {
super(__getWsdlLocation(), SOMESERVICE_QNAME);
}
So if you want it to point to another URL, you just use the constructor that takes a URL argument (there are 6 constructors for setting qname and features as well). For example, if you have set up a local TCP/IP monitor that is listening on port 9999, and you want to redirect to that URL:
URL newWsdlLocation = new URL("http://theServerName:9999/somePath");
SomeService someService = new SomeService(newWsdlLocation);
and that will call this constructor inside the service:
public SomeService(URL wsdlLocation) {
super(wsdlLocation, SOMESERVICE_QNAME);
}
I wouldn't go so far as #Femi to change the existing address property. You can add new services to the definitions section easily.
<wsdl:service name="serviceMethodName_2">
<wsdl:port binding="tns:serviceMethodNameSoapBinding" name="serviceMethodName">
<soap:address location="http://new_end_point_adress"/>
</wsdl:port>
</wsdl:service>
This doesn't require a recompile of the WSDL to Java and making updates isn't any more difficult than if you used the BindingProvider option (which didn't work for me btw).
To change the end address property edit your wsdl file
<wsdl:definitions.......
<wsdl:service name="serviceMethodName">
<wsdl:port binding="tns:serviceMethodNameSoapBinding" name="serviceMethodName">
<soap:address location="http://service_end_point_adress"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>