Skip to content

Commit

Permalink
#37 : java8 source level, cleanup async bean example (move out the ht…
Browse files Browse the repository at this point in the history
…tp client code for clarity)
  • Loading branch information
vankeisb committed Jan 13, 2016
1 parent 26fcaa4 commit 4ccf2a9
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,96 +2,98 @@

import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.examples.bugzooky.ext.Public;
import net.sourceforge.stripes.util.Base64;
import net.sourceforge.stripes.validation.Validate;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.nio.reactor.ConnectingIOReactor;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.nio.charset.Charset;

@Public
@UrlBinding("/async")
public class AsyncActionBean implements ActionBean {

@Validate(required = true)
private String someProp;

private ActionBeanContext context;

private Exception clientException;
private boolean cancelled;
private int status;
private String ghResponse;

public ActionBeanContext getContext() {
return context;
}

public void setContext(ActionBeanContext context) {
this.context = context;
}

// property to test binding/validation
@Validate(required = true)
private String someProp;

// those are set by the http client response, and
// used in the JSP...
private Exception clientException;
private boolean cancelled;
private int status;
private String ghResponse;

// display the test page
@DefaultHandler
@DontValidate
public Resolution display() {
return new ForwardResolution("/WEB-INF/async/async.jsp");
}

/**
* asynchronously fetch data from a remote web service (github)
* and set instance fields for use in the view.
*/
public Resolution asyncEvent() {

// we return an AsyncResolution : this triggers the asynchronous servlet mode...
return new AsyncResolution() {

// only this method to implement. you must complete() or dispatch() yourself.
public void execute(final HttpServletRequest request, final HttpServletResponse response) throws Exception {

final CloseableHttpAsyncClient asyncClient = createAsyncHttpClient();
HttpHost h = new HttpHost("api.github.com", 443, "https");
HttpRequest r = new BasicHttpRequest("GET", "/repos/StripesFramework/stripes/commits");
asyncClient.execute(h, r, new FutureCallback<HttpResponse>() {
public void completed(HttpResponse result) {
// deserialize result
// we use an Async Http Client in order to call the github web service as a demo.
// the async http client calls on of the lambdas when he's done, and
// then we dispatch to a JSP, completing the async request.

HttpHost host = new HttpHost("api.github.com", 443, "https");
new AsyncHttpClient(host)
.buildRequest("/repos/StripesFramework/stripes/commits")
.completed(result -> {

// response is returned, deserialize result
status = result.getStatusLine().getStatusCode();
if (status == 200) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
result.getEntity().writeTo(bos);
bos.close();
ghResponse = bos.toString("UTF-8");
dispatch(asyncClient, getAsyncContext());
} catch (Exception e) {
failed(e);
clientException = e;
}
dispatchToJsp(getAsyncContext());
} else {
ghResponse = result.getStatusLine().getReasonPhrase();
dispatch(asyncClient, getAsyncContext());
dispatchToJsp(getAsyncContext());
}
}

public void failed(Exception ex) {
// ouch !
})
.failed(ex -> {

// http client failure
clientException = ex;
dispatch(asyncClient, getAsyncContext());
}
dispatchToJsp(getAsyncContext());

public void cancelled() {
})
.cancelled(() -> {

// just for demo, we never call it...
cancelled = true;
dispatch(asyncClient, getAsyncContext());
}
});
dispatchToJsp(getAsyncContext());

})
.get(); // trigger async request
}
};
}
Expand All @@ -116,33 +118,12 @@ public void execute(final HttpServletRequest request, final HttpServletResponse
};
}

private void dispatch(CloseableHttpAsyncClient asyncClient, AsyncContext c) {
try {
asyncClient.close();
} catch (IOException e) {
e.printStackTrace();
}
c.dispatch("/WEB-INF/async/async.jsp");
// helper dispatch method
private void dispatchToJsp(AsyncContext asyncContext) {
asyncContext.dispatch("/WEB-INF/async/async.jsp");
}

protected CloseableHttpAsyncClient createAsyncHttpClient() {
try {
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(10000)
.setConnectTimeout(10000).build();
ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor();
PoolingNHttpClientConnectionManager cm =
new PoolingNHttpClientConnectionManager(ioReactor);
CloseableHttpAsyncClient res = HttpAsyncClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(cm)
.build();
res.start();
return res;
} catch(Exception e) {
throw new RuntimeException(e);
}
}
// getters for instance fields that have been set by event method

public boolean isCancelled() {
return cancelled;
Expand All @@ -156,11 +137,18 @@ public String getGhResponse() {
return ghResponse;
}

public Exception getClientException() {
return clientException;
}

// get/set for test binding prop

public String getSomeProp() {
return someProp;
}

public void setSomeProp(String someProp) {
this.someProp = someProp;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package net.sourceforge.stripes.examples.async;

import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.nio.reactor.ConnectingIOReactor;

import java.io.IOException;
import java.util.function.Consumer;

/**
* Wrapper for non-blocking http client example. Avoids cluttering the action bean's code...
*/
public class AsyncHttpClient {

private final CloseableHttpAsyncClient asyncClient;
private final HttpHost host;

public AsyncHttpClient(HttpHost host) {
this.host = host;
try {
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(10000)
.setConnectTimeout(10000).build();
ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor();
PoolingNHttpClientConnectionManager cm =
new PoolingNHttpClientConnectionManager(ioReactor);
asyncClient = HttpAsyncClients.custom()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(cm)
.build();
asyncClient.start();
} catch(Exception e) {
throw new RuntimeException(e);
}
}

private void close() {
try {
asyncClient.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public AsyncRequest buildRequest(String uri) {
return new AsyncRequest(uri);
}

public class AsyncRequest {

private final String uri;

private Consumer<HttpResponse> onCompleted;
private Consumer<Exception> onFailed;
private Runnable onCancelled;

private AsyncRequest(String uri) {
this.uri = uri;
}

public AsyncRequest completed(Consumer<HttpResponse> c) {
onCompleted = c;
return this;
}

public AsyncRequest failed(Consumer<Exception> e) {
onFailed = e;
return this;
}

public AsyncRequest cancelled(Runnable r) {
onCancelled = r;
return this;
}

public void get() {
HttpRequest r = new BasicHttpRequest("GET", uri);
asyncClient.execute(host, r, new FutureCallback<HttpResponse>() {
public void completed(HttpResponse result) {
if (onCompleted != null) {
onCompleted.accept(result);
}
close();
}

public void failed(Exception ex) {
if (onFailed != null) {
onFailed.accept(ex);
}
close();
}

public void cancelled() {
if (onCancelled != null) {
onCancelled.run();
}
close();
}
});
}
}

}
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
Expand Down

0 comments on commit 4ccf2a9

Please sign in to comment.