JAXWS - help required to set WSDL request timeout - java

Im using Metro 2.0 and J2SE5. The application I have written does not know about the external WebService at compile time, it finds them at runtime based on a business logic XML file, therefore I perform a WSDL request.
The sample code I have written is as follows:
String wsdlServiceName = ...;
String wsdlURL = ...;
Document payload = ...;
final String nsURI = ...;
final QName serviceName = new QName(nsURI, wsdlServiceName + "Service");
final QName servicePort = new QName(nsURI, wsdlServiceName + "Port");
// Create service and the dispatcher for the SOAP message
Service service = Service.create(new URL(wsdlURL), serviceName);
dispatch = service.createDispatch(servicePort, SOAPMessage.class, Service.Mode.MESSAGE);
// Set timeouts
dispatch.getRequestContext().put("com.sun.xml.internal.ws.request.timeout", 3000);
dispatch.getRequestContext().put("com.sun.xml.internal.ws.connect.timeout", 3000);
// Create the outgoing SOAP request
SOAPBinding soapBinding = (SOAPBinding) dispatch.getBinding();
request = soapBinding.getMessageFactory().createMessage();
SOAPBody requestBody = request.getSOAPBody();
requestBody.addDocument(payload);
// Invoke web service operation
SOAPMessage response = dispatch.invoke(request);
The timeout works correctly when the Web Service is invoked ( dispatcher.invoke(request) )
HOWEVER the WSDL is requested before the timeouts are set, and if the Web Service is not responding it takes 90 seconds before the connection is timed-out.
Is it possible to set the timeouts before the WSDL is requested ? You need a dispatcher to set the timeouts, but that is done AFTER the Service is created that requests the WSDL?! (ie. Service.create() )

Try the setting system property
sun.net.client.defaultConnectTimeout
but from Networking Properties it says it may not be supported for in future releases
However I would suggest to cache the WSDL and not access it remotely.
It is better performance wise especially if you are working with a WSDL that is not expected to change frequently.

We just ran into this same issue, and tried all of the settings mentioned above - likewise, to no avail.
Our solution was to download the WSDL to a temporary file first, using URL.openConnection() (setting the timeouts on the connection with: URLConnection.setConnectionTimeout(), and URLConnection.setReadTimeout()). We then generate a url for this file with: File.toURI().toURL(), which we pass to the service constructor that takes a URL.
This approach lets you dynamically fetch the current WSDL, while explicitly controlling the timeout. We then set the timeout for subsequent calls to the service as you show in the original post.

Related

How to add a body to a GET request in JAX-RS

I'm trying to consume a REST API that requires a body with a GET request. But as a GET usually doesn't have a body, I can't find a way to attach a body in my request. I am also building the REST API, but the professor won't allow us to change the method to POST (he gave us a list of the endpoints we are to create, no more, no less).
I'm trying to do it like this:
Response r = target.request().method(method, Entity.text(body));
Where I set the method to GET and the body to my get body. However, using this approach I get an exception:
javax.ws.rs.ProcessingException: RESTEASY004565: A GET request cannot have a body.
Is there any way to do this with JAX-RS? We learned to use JAX-RS so I would prefer a solution using this, as I'm not sure my professor would allow us to use any other REST client. I'm currently using RESTEasy, provided by the WildFly server.
(This is not a duplicate of HTTP GET with request body because I'm asking on how to create a GET request with body in JAX-RS, not if it should be done.)
This depends on what is your JAX-RS implementation. This check can be disabled in Jersey 2.25 using SUPPRESS_HTTP_COMPLIANCE_VALIDATION property:
ClientConfig config = new ClientConfig();
config.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
JerseyClient client = JerseyClientBuilder.createClient(config);
WebTarget target = client.target(URI.create("https://www.stackoverflow.com"));
Response response = target.request().method("GET", Entity.text("BODY HERE"));
Instead of exception you will get an INFO log
INFO: Detected non-empty entity on a HTTP GET request. The underlying HTTP transport connector may decide to change the request method to POST.
However in RESTEasy 3.5.0.Final there is a hardcoded check in both URLConnectionEngine and ApacheHttpClient4Engine:
if (request.getEntity() != null)
{
if (request.getMethod().equals("GET")) throw new ProcessingException(Messages.MESSAGES.getRequestCannotHaveBody());
You would have to create your own implementation of the ClientHttpEngine to skip this. Then you need to supply it when building the client:
ClientHttpEngine engine = new MyEngine();
ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();

Jetty on Heroku: how to tell in code if https was used

I have some servlets running in Jetty, deployed on Heroku, handling POST requests.
Some, but not all, POST requests MUST come over https. Whether or not a request should be forced to be on https depends on the http body of the POST request.
I need to figure out, from inside the servlet, whether the incoming request used https (SSL) or not, so that I can send the appropriate response. However, nothing I have tried seems to work.
I tried the obvious HttpServletRequest.getProtocol() but that apparently returns the same constant whether the protocol was http or https.
I tried HttpServletRequest.isSecure() however that is returning false even though my test request was sent to a url starting with https://
When I call HttpUtils.getRequestURL( HttpServletRequest ).toString(); I get an apparrently reconstructed url that starts with "http://" even though my test request was sent to a url starting with "https://"
According to the post "Enforce HTTPS with Embedded Jetty on Heroku" heroku has some load balancers, and I should get the value of the "x-forwarded-proto" header. That header is blank.
FYI I am using the default SSL endpoint provided by the heroku api -- I am not using their SSL Endpoint extension, because this url is not being loaded in a browser (so I don't need a custom domain in the url).
Can anyone tell me how to tell if HTTPS was used in the incoming request?
I know nothing about Heroku, but if you're programmatically configuring Jetty (as opposed to using the XML configuration), you likely need to add the SecureRequestCustomizer to your HttpConfiguration. It sets the secure flag on the requests, as well as setting the scheme to HTTPS. You can find examples here, but briefly:
final HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.setSecurePort(httpsPort);
final ServerConnector httpConnector = new ServerConnector(server,
new HttpConnectionFactory(httpConfig));
httpConnector.setPort(httpPort);
server.addConnector(httpConnector);
final HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.addCustomizer(new SecureRequestCustomizer()); // !!!
final HttpConnectionFactory httpsFactory = new HttpConnectionFactory(httpsConfig);
final SslConnectionFactory sslFactory = new SslConnectionFactory(sslCtx,
httpsFactory.getProtocol());
final ServerConnector httpsConnector = new ServerConnector(server,
sslFactory, httpsFactory);
httpsConnector.setPort(httpsPort);
server.addConnector(httpsConnector);
I too found it rather surprising that this poorly documented step was necessary.

Issue with recording from the Open ONVIF (Network Video Interface Forum ) device

I'm working on Open Network Video Interface Forum-Java project and following the steps described in the ONVIF Application Programmer's Guide.
I have generated sources from the wsdls provided in ONVIF site. I'm able to retrieve the live stream URI using the media.wsdl. Now I have an issue with recording. The codes that I have tried is given below:
RecordingService recording_ervice = new RecordingService();
RecordingPort record_port = recording_ervice.getRecordingPort();
BindingProvider bindingProvider = (BindingProvider) record_port;
// Add a security handler for the credentials
final Binding binding = bindingProvider.getBinding();
List<Handler> handlerList = binding.getHandlerChain();
if (handlerList == null) {
handlerList = new ArrayList<Handler>();
}
handlerList.add(new RecordStream.SecurityHandler());
// binding.setHandlerChain(handlerList);
// Set the actual web services address instead of the mock service
Map<String, Object> requestContext = bindingProvider.getRequestContext();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://" + deviceip + "/onvif/media_service");
requestContext.put(BindingProvider.USERNAME_PROPERTY, user);
requestContext.put(BindingProvider.PASSWORD_PROPERTY, pass);
Recordings recordings = record_port.getRecordings();
The above code on run gives an error as:
Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Method 'ns11:GetServiceCapabilities' not implemented: method name or namespace not recognized
I also tried with media service, then the error is:
Exception in thread "main" com.sun.xml.internal.ws.client.ClientTransportException: The server sent HTTP status code 405: Method Not Allowed
When you tried with the media source, you requested an unauthorized action apparently since the server returned Error code 405. Either the method is prohibited from use, or you need a credential to use the method.
As for Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Method 'ns11:GetServiceCapabilities' not implemented: method name or namespace not recognized, #Sigismondo is right about the fact that most ip cameras don't support it. You will need an alternative recording method(literal and pun) to record from an ip camera.
You are using http://" + deviceip + "/onvif/media_service to reach the Recording service, but this is a media.wsdl service. So when you try to call getRecordings on the media service it seems normal you receive an error.
The url for recording.wsdl service should be http://" + deviceip + "/onvif/recording_service.
In order to get the corect URL to reach the recording service you should request it from the GetCapabilities method of the devicemgmt.wsdl service.
HTTP 405 - Resource not allowed usually occurs in IIS.
This problem occurs if the following conditions are true:
You do not specify the file name. For example, you do not specify
http ://Server/Web/...
The Scripting Object Model (SOM) is enabled.
A DTC event is called.
So, when the SOM is enabled a < form > tag is inserted in the page the tag is invalid means it does not contain any action.

java WebService request timeout

My problem is that I call a remote web service that requires more than 60 seconds to respond and this causes a timeout exception. I do not want any timeout check: I just want the sender to wait until the web service ends. I tried to set:
HttpSession httpSession = getThreadLocalRequest().getSession();
httpSession.setMaxInactiveInterval(120000);
getThreadLocalRequest().setAttribute("session", httpSession);
to modify the web.xml session-timeout (even though I do not think that it is related with my problem) to create a custom HttpRequest. Timeout persists. Is there any way to shutdown this check?
Found the solution:
/* Connect to the service */
ClientProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
factoryBean.setServiceClass(MyService.class);
factoryBean.setAddress("service-url");
myService = (MyService) factoryBean.create();
/* Retrive HTTP client policy and set the receive timeout */
Client client = ClientProxy.getClient(myService);
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
HTTPClientPolicy httpClientPolicy = httpConduit.getClient();
httpClientPolicy.setReceiveTimeout(timeoutMilliseconds);
httpSession.setMaxInactiveInterval is not what you're after.
You probably want to set connectTimeout and readTimeout on URLConnection.
How to do that, depends on what tool you use to call the remote webservice.
Can you add some more details about the service, if it's a SOAP-service, REST-service etc, and what library you use to call the service?

SOAP web service calls from Javascript

I'm struggling to successfully make a web service call to a SOAP web service from a web page. The web service is a Java web service that uses JAX-WS.
Here is the web method that I'm trying to call:
#WebMethod
public String sayHi(#WebParam(name="name") String name)
{
System.out.println("Hello "+name+"!");
return "Hello "+name+"!";
}
I've tried doing the web service call using the JQuery library jqSOAPClient (http://plugins.jquery.com/project/jqSOAPClient).
Here is the code that I've used:
var processResponse = function(respObj)
{
alert("Response received: "+respObj);
};
SOAPClient.Proxy = url;
var body = new SOAPObject("sayHi");
body.ns = ns;
body.appendChild(new SOAPObject("name").val("Bernhard"));
var sr = new SOAPRequest(ns+"sayHi",body);
SOAPClient.SendRequest(sr,processResponse);
No response seems to be coming back. When in jqSOAPClient.js I log the xData.responseXML data member I get 'undefined'. In the web service I see the warning
24 Mar 2011 10:49:51 AM com.sun.xml.ws.transport.http.server.WSHttpHandler handleExchange
WARNING: Cannot handle HTTP method: OPTIONS
I've also tried using a javascript library, soapclient.js (http://www.codeproject.com/kb/Ajax/JavaScriptSOAPClient.aspx). The client side code that I use here is
var processResponse = function(respObj)
{
alert("Response received: "+respObj);
};
var paramaters = new SOAPClientParameters();
paramaters.add("name","Bernhard");
SOAPClient.invoke(url,"sayHi",paramaters,true,processResponse);
I've bypassed the part in soapclient.js that fetches the WSDL, since it doesn't work
(I get an: IOException: An established connection was aborted by the software in your host machine on the web service side). The WSDL is only retrieved for the appropriate name space to use, so I've just replaced the variable ns with the actual name space.
I get exactly the same warning on the web service as before (cannot handle HTTP method: OPTIONS) and in the browser's error console I get the error "document is null". When I log the value of req.responseXML in soapclient.js I see that it is null.
Could anyone advise on what might be going wrong and what I should do to get this to work?
I found out what was going on here. It is the same scenario as in this thread: jQuery $.ajax(), $.post sending "OPTIONS" as REQUEST_METHOD in Firefox.
Basically I'm using Firefox and when one is doing a cross domain call (domain of the address of the web service is not the same as the domain of the web page) from Firefox using AJAX, Firefox first sends an OPTIONS HTTP-message (before it transmits the POST message), to determine from the web service if the call should be allowed or not. The web service must then respond to this OPTIONS message to tell if it allows the request to come through.
Now, the warning from JAX-WS ("Cannot handle HTTP method: OPTIONS") suggests that it won't handle any OPTIONS HTTP-messages. That's ok - the web service will eventually run on Glassfish.
The question now is how I can configure Glassfish to respond to the OPTIONS message.
In the thread referenced above Juha says that he uses the following code in Django:
def send_data(request):
if request.method == "OPTIONS":
response = HttpResponse()
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
response['Access-Control-Max-Age'] = 1000
response['Access-Control-Allow-Headers'] = '*'
return response
if request.method == "POST":
# ...
Access-Control-Allow-Origin gives a pattern which indicates which origins (recipient addresses) will be accepted (mine might be a bit more strict than simply allowing any origin) and Access-Control-Max-Age tells after how many seconds the client will have to request permission again.
How do I do this in Glassfish?
Have you actually tested that ws is working properly?
You can use SoapUI for inspecting request/response etc.
When you confirm that ws is working from SoapUI, inspect what is format of raw Soap message. Then try to inspect how it looks before sending with .js method, and compare them.
It might help you understand what is wrong.
Check if this helps
http://bugs.jquery.com/attachment/ticket/6029/jquery-disable-firefox3-cross-domain-magic.patch
it's marked as invalid
http://bugs.jquery.com/ticket/6029
but it might give you some hint
On the other hand, instead to override proper settings for cross-domain scripting might be better if you can create and call local page that will do request to ws and return result.
Or even better, you can create page that will receive url as param and do request to that url and just return result. That way it will be more generic and reusable.

Categories