If i have Cucumber scenario like this :
Scenario: Correct response should be returned.
When User sent "xml example" request
Then Response should have some data "
And for this first step in ma TestStep class :
#When("^User sent \"(.*)\" request")
public void sendRequest(String requestName) throws Exception
{
//code for SOAP request
}
If service is not available, test will run for hours. I would like to stop it after some period of time. Any suggestions?
If your lib is not terminating request after provided timeout, you can use alternative approach with ExecutorService. The main idea is submit task with request to SOAP service to ExecutorService and then call get(time, TimeUnit.SECONDS) with timeout from Future.
Example:
Future<String> future = executor.submit(new SoapRequeset());
future.get(5L, TimeUnit.SECOND); //will throw exception if result not available after timeout
Solution with ExecutorService can be used but it was not acceptable for my project. I had to override method from my lib. This is what i did:
#Override
public SOAPMessage sendSOAPMessage(SOAPMessage request) throws Exception
{
SOAPConnection soapConnection = SOAPConnectionFactory.newInstance().createConnection();
URL endpoint =
new URL(new URL(serviceUrl), "endpoint spec.",
new URLStreamHandler()
{
#Override
protected URLConnection openConnection(URL url) throws IOException
{
URL target = new URL(url.toString());
URLConnection connection = target.openConnection();
// Connection settings
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
return (connection);
}
});
return soapConnection.call(request, endpoint);
}
so when i called sendSOAPMessage my test step will break down after 5 seconds if there was no response in that period of time.
Related
I'm using the new java.net.http.HttpClient and from my tests after about 378026 times of calling it, I start to get the following:
Caused by: java.net.ConnectException: Cannot assign requested address
at java.net.http/jdk.internal.net.http.common.Utils.toConnectException(Utils.java:964)
at java.net.http/jdk.internal.net.http.PlainHttpConnection.connectAsync(PlainHttpConnection.java:179)
at java.net.http/jdk.internal.net.http.AsyncSSLConnection.connectAsync(AsyncSSLConnection.java:56)
at java.net.http/jdk.internal.net.http.Http2Connection.createAsync(Http2Connection.java:369)
at java.net.http/jdk.internal.net.http.Http2ClientImpl.getConnectionFor(Http2ClientImpl.java:127)
at java.net.http/jdk.internal.net.http.ExchangeImpl.get(ExchangeImpl.java:88)
at java.net.http/jdk.internal.net.http.Exchange.establishExchange(Exchange.java:293)
at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl0(Exchange.java:425)
at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl(Exchange.java:330)
at java.net.http/jdk.internal.net.http.Exchange.responseAsync(Exchange.java:322)
at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:304)
at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsync0$2(MultiExchange.java:250)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1705)
at java.net.http/jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(HttpClientImpl.java:153)
at java.base/java.util.concurrent.CompletableFuture.completeAsync(CompletableFuture.java:2591)
at java.net.http/jdk.internal.net.http.MultiExchange.responseAsync(MultiExchange.java:204)
at java.net.http/jdk.internal.net.http.HttpClientImpl.sendAsync(HttpClientImpl.java:632)
Below is the class that I use:
public class JavaHttpClient {
HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30))
.followRedirects(HttpClient.Redirect.ALWAYS).build();
public String getRequest(String incomingUrl) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(incomingUrl)).GET().build();
HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
return response.body();
}
}
I don't understand why this is happening. I assume something is being left open that should have been closed?
The HttpClient will close all opened connections when it's no longer referenced and there's no operations in progress (all responses have been received).
I'm trying to create a websocket and dynamically recalculate its header in every message sent. Is it possible?
I was trying to use an interceptor but is only called once.
public void run() {
// only open a websocket if there aren't websockets already open
if (this.webSocket == null || !this.openingWS) {
this.openingWS = true;
wsBuilder = new OkHttpClient.Builder();
OkHttpClient client = wsBuilder.addInterceptor(this)
.readTimeout(0, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("wss://...")
.build();
client.newWebSocket(request, this);
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
client.dispatcher().executorService().shutdown();
}
}
#Override public void onOpen(WebSocket webSocket, Response response) {
this.openingWS = false; // already open
this.webSocket = webSocket; // storing websocket for future usages
if (listener != null) listener.onWSOpen();
}
public void sendCommand(String cmd) {
System.out.println("SEND " + cmd);
if (webSocket != null) webSocket.send(cmd);
}
This same class is implementing the interceptor
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (!isSpecial()) return chain.proceed(originalRequest);
okhttp3.Request.Builder builder = originalRequest.newBuilder()
.addHeader("text", "...")
.addHeader("dfds", "...");
Request compressedRequest = builder.build();
return chain.proceed(compressedRequest);
}
The authentication code sent in the header will change every X seconds/minutes.
If it's not possible to change dynamically the header, what is the best way to approach this kind of connection?
Thank you for your help.
I think the headers are send only first time when you request the connection, later is depends on frames between the client and the server.
So if you want to inform the server that you had changed the header then send message with your new header. Or you can close the connection and start a new one with the new header.
I am using:
Andorid Studio.
Okhttp 2.4.0 with AsyncTask. But I can't cancel a request.
Connected to server on wi-fi. And if server is off then okHttp keeps trying to connect, and I can't cancel it.
time outs is working but i want to cancel before timeouts
private OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(30000, TimeUnit.MILLISECONDS);
after execute I press special cancel button in my api
public void onCancelClick(View v){
BaseApplication.getInstance().server.cancel();
synchProducts.cancel(true);
}
first line must stoped okHttp, second line stoped class extended AsyncTask
private static final String myTag = "AsyncCall";
public void cancel(){
client.cancel(myTag);
}
backGround in AsyncTask class
#Override
protected String doInBackground(Void... params) {
publishProgress(1);
String responseStr = sendProductHeaders();
//this performed after timeouts, and spit to my cancel okHttp
publishProgress(2);
if (responseStr != null && !isCancelled()){
ProductsList productsForSend = parseProducts(responseStr);
//correctly stoped thread
I didn't forget to use .tag in the builder request
public Response post(String url, String json) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.tag(myTag)
.url(url)
.post(body)
.build();
return call(request);
}
"call" is my method which makes the okHttp call
private Response call(Request request){
try {
return client.newCall(request).execute();
} catch (IOException e) {
Log.e("Aync call", "IO exception " + e.getMessage());
}
return null;
}
"Tags" is true, code in okHttp Library realy fires call.cancel();
for (Call call : executedCalls) {
if (Util.equal(tag, call.tag())) {
call.cancel();
}
}
method which is running Async
public void onRefreshProducts(View v){
progressLayout.setVisibility(View.VISIBLE);
progressBar.setProgress(0);
synchProducts = new SynchProducts(activityCallBack);
synchProducts.execute();
}
"activityCallBack" is the interface I use when I call to my activity from AsyncTask class
i don't want to use okHttp enqueue, but I can't insert a friendly progress bar in my app.
Thanks!
Try updating the library to okhttp 2.5.0 and use the cancel() method. The ChangeLog for 2.5.0 mentions the following:
Call canceling is more reliable. We had a bug where a socket being connected wasn't being closed when the application used Call.cancel().
Hopefully it will fix the issue.
I'm in the midst of testing my application which is using an HTTP-server. Instead of mocking I decided to go with a HTTP server fixture. Meaning that I do not have to mock any productional code. To accomplish this goal I currently chose for a free to use 3rd party library fixd.
I was able to successfully create several unit tests - which are working by means of a GET request. Most are quite simple, i.e.:
#Test
public void verifyConnectionTest()
{
try
{
final String body = FileUtils.readFileToString(RESOURCE);
final String path = "/";
this.server.handle(Method.GET, path).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
But I now have to send a POST request with multipart/form-data. Which does not make much of a difference other than changing the method and content-type:
#Test
public void executeStepTest()
{
try
{
final String body = FileUtils.readFileToString(SERVICE_RESPONSE);
final String path = "/";
this.server.handle(Method.POST, path, "multipart/form-data").with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
However I get the following error: [ERROR] could not find a handler for POST - / - multipart/form-data; boundary=bqCBI7t-VW1xaJW7BADmTiGMg9w_YM2sHH8ukJYx and my guess is that fixd doesn't recognize the boundary-party. Since the documentation does not show an example I'm quite stuck on this part.
I tried using some wildcards such as '*', no succes. Thus; I need a way to either tell fixd to accept that boundary or use some wildcards I didn't yet discover. Any help would be great, thanks!
I've been making some debug and it seems to be that the problem is in the fixd core.
Basically, fixd indexes every RequestHandlerImpl by a HandlerKey (which includes ContentType as part of the key) in the map handlerMap. See method org.bigtesting.fixd.core.FixtureContainer#resolve.
...
HandlerKey key = new HandlerKey(method, route, contentType);
RequestHandlerImpl handler = handlerMap.get(key);
if (handler == null) {
// Error
}
...
Problem: When the request is multipart/form-data, boundary data (which it's generated dinamically every request) is part of the content type. So, any handler is found in handlerMap because the key changes with every running.
I've made a little test only to check that this is the cause of the problem, passing the contentType to fixd server.handle after the creation of the multipart request, and it works fine.
See the test below:
#Test
public void verifyConnectionTest_multipart() {
try {
// 1. Create multipart request (example with http-commons 3.1)
PostMethod filePost = new PostMethod(url);
Part[] parts = { new StringPart("param", "value") };
MultipartRequestEntity request = new MultipartRequestEntity(parts, filePost.getParams());
filePost.setRequestEntity(request);
// 2. fixd server handle (passing the request content type)
this.server.handle(Method.POST, "/", request.getContentType()).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response) {
response.setStatusCode(200);
response.setContentType("text/xml");
}
});
// 3. Execute multipart request
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
// 4. Assertions
Assert.assertEquals(200, status);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
Hope it helps you to clarify the problem. Cheers
This was a bug in fixd, and has been fixed in version 1.0.3. Your original code should work using this new version of fixd.
I try to understand the way asynchronous responses work with Jersey. I read chapter 10 of the Jersey documentation (https://jersey.java.net/documentation/latest/async.html) but it doesn't help with my problem. Also research here on stackoverflow didn't result in satisfying answers (that I can understand).
What I'm trying to do is similar to the one question in this post (Use http status 202 for asynchronous operations). I want to upload a large file to the server using a HTML form document. After the request is send to the server the web service should immediately response with status 202 and a URI where the file can be found after the request has finished.
After reading the post abive it seems possible but sadly no hints how to implement such a behavior where given.
I wrote a small web service to test the functionality:
#Path("/test/async/")
public class TestAsyncResponse {
#GET
#Path("get")
public Response asyncGet(#Suspended final AsyncResponse response) {
new Thread(new Runnable() {
#Override
public void run() {
DateFormat df = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
System.out.println("#### thread started: "
+ df.format(new Date()) + " ####");
String result = veryExpensiveOperation();
System.out.println("#### thread finished: "
+ df.format(new Date()) + " ####");
response.resume(result);
}
private String veryExpensiveOperation() {
try {
Thread.sleep(10000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
return "Woke up!";
}
}).start();
return Response.status(202).entity("Request accepted. " +
"Long running operation started")
.build();
}
}
The service works but as a response I get the "Woke Up!" message after the 10 second wait rather than the 202 response which seems logical because the AsyncResponse is the one that handles the response (as I understand it).
After reading the documentation I got the impression that this is suppose to happen because all Jersey does with the asynchronous server response is to outsource the thread from the response thread pool to another one to free processing time for more responses to the service.
So my two questions would be: Is my understanding correct and can I use the asynchronous server response to get the desired behavior?
I tried to start a new thread without the AsyncResponse and I get a NullPointerExceptionbecause Jersey already closed the response and thus closed the InputStream that contains the file data. Is this the expected behavior? This post (https://stackoverflow.com/a/17559684/1416602) seems to indicate that it might work.
Any response is greatly appreciated.
Greetings
Your question is mixing two topics.
From HTTP perspective, 202 is technically a completed request. And the result of the request is 202, server telling you it will do it on the side. You will have to make another HTTP request to get updated status.
From the perspective of your application, async means that you will execute the request in a separate thread (or other async way). But also, this means that you will not return a result, not even 202, until the other "veryExpensiveOperation" finishes. The whole point in jumping through this hoop is to free up the calling thread. Your web server has a limited number, e.g. 20, and if each of your requests took a very long time, all 20 would be hanging. Using #Suspended you transfer execution from the web server thread to some other means, (another thread in your case). This is really only the first step. The idea behind async servers is that even the veryExpensiveOperation is implemented in some async way so that waiting for a DB or a file does not occupy a whole thread.
I have been through the same pain recently. Jersey keeps claiming it supports Asynchronous REST calls, but I think it's being disingenuous.
And in fact, once I started to work out the correct way of doing this, Jersey actually got in the way.
private static ExecutorService executorService = Executors.newFixedThreadPool( Integer.valueOf( numberOfThreads ) );
#POST
#Path("async")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response async( #FormDataParam("file") InputStream inputStream,
#FormDataParam("file") FormDataContentDisposition des ) throws Throwable {
String uniqueID = UUID.randomUUID().toString();
executorService.execute( new Runnable() {
#Override
public void run() {
try {
// do long performing action
} catch (Exception ex) {
}
}
} );
return Response.accepted().location( getResultsURI( uniqueID ) ).build();
}
#GET
#Path("results/{uniqueID}")
#Produces("application/zip")
public Response results( #PathParam(value = "uniqueID ") String uniqueID ) {
// Check status of job
// If not finished...
if (notFinished) {
return Response.status( 202 ).location( getResultsURI( uniqueID ) )
.entity( status ).build();
}
return Response.ok( FileUtils.readFileToByteArray( zip.toFile() ) ).type( "application/zip" )
.header( "Content-Disposition", "attachment; filename=\"filename.zip\"" ).build();
}
protected URI getResultsURI( String uniqueID ) throws URISyntaxException {
return new URI( Constants.WS_VERSION + "/results/" + uniqueID );
}
The biggest pain was that when you set Response.location(), even if you set it to "./results" or "/results", Jersey expands it to the full URL. Which would be fine, except that it ignores any class-level #Path:
#Path(Constants.WS_VERSION)
public class MyEndpoint {
So instead of fighting it, I used the above code to at least make it correct. Ideally I'd like Jersey to leave the "Location" header alone.
Anyway - the above code is what I used (excluding the business logic bits ;) )