Recently I had to implement a SOAP webservice call from a af:commandButton action, but the requirement was to make it an asynchronous call. So that the user doesn't have to wait till the Webservice finish processing the request.
So I built :
So I built :
- Interface which extends java.util.concurrent.Callable.
- Interface so that I can use the same technique to call multiple Webservices, if required.
- A session scoped bean to invoke the above interface.
public interface WebserviceCallee<T> extends Callable<Boolean> {
// Base URL of the Webservice to call
public String getBaseUrl();
public void setBaseUrl(String baseUrl);
// To identify this object uniquely
public String getUniqueIdentifier();
}
#2. Implentation :
public class WebServiceA implements WebserviceCallee<Boolean> {
private static Log log = LogFactory.getLog(WebServiceA.class);
private String baseUrl;
private Thread thread;
private MyCustomObject myCustomObject;
public WebServiceA(MyCustomObject myCustomObject) {
this.myCustomObject = myCustomObject;
this.thread = Thread.currentThread();
}
public Boolean call() {
Boolean callResult = Boolean.FALSE;
// PLACE YOUR WEBSERVICE CALL HERE
// MY WEBSERVICE RETURNED TRUE/FALSE, THAT'S WHY THIS METHOD RETURNS A Boolean
return callResult;
}
public String getUniqueIdentifier() {
return "WebServiceA call for : " + myCustomObject.getUniqueID();
}
public void setBaseUrl(String baseUrl) {
this.baseUrl = baseUrl;
}
public String getBaseUrl() {
return baseUrl;
}
}
#3. Session Scoped Bean : ExecutionService
public class ExecutionService {
private static Log log = LogFactory.getLog(ExecutionService.class);
private final ExecutorService executorService = Executors.newCachedThreadPool();
Map<String, Future<Boolean>> submittedTasks = new SubmittedTaskMap<>();
public ExecutionService() {
super();
}
public static ExecutionService getInstance() {
return (ExecutionService) JsfUtils.getExpressionValue("#{ExecutionService}");
}
public void submitCallables(WebserviceCallee<Boolean> webserviceCallee) {
log.debug("thread spawnned for :" + webserviceCallee.getCalleeIdentity());
Future<Boolean> future = executorService.submit(webserviceCallee);
try {
log.debug("thread result :" + future.get());
} catch (ExecutionException e) {
log.fatal(e);
} catch (InterruptedException e) {
log.fatal(e);
}
submittedTasks.put(webserviceCallee.getWorkorderNumber(), future);
}
public Map<String, Future<Boolean>> getSubmittedTasks() {
return submittedTasks;
}
/**
* Custom HashMap implementation to track the threads spawnned by WebService calls
* @param <K>
* @param <V>
*/
public class SubmittedTaskMap<K,V> extends HashMap<String, Future<Boolean>> {
public SubmittedTaskMap() {
super();
}
/**
* @param key
* @return NULL if the thread has finished executing
*/
@Override
public Future<Boolean> get(Object key) {
Future<Boolean> submittedTask = super.get(key);
if(submittedTask != null) {
if(submittedTask.isDone()) {
return null;
}
}
return submittedTask;
}
}
}
adfc-config.xml :
<managed-bean id="es1">
<managed-bean-name id="es2">ExecutionService</managed-bean-name>
<managed-bean-class id="es3">my.bean.ExecutionService</managed-bean-class>
<managed-bean-scope id="es4">session</managed-bean-scope>
</managed-bean>
#4. To invoke from ADF managed bean :
WebServiceA webServiceA = new WebServiceA(myCustomObject);
String baseUrl = ContextUtil.getContextProperty(FacesContext.getCurrentInstance(), WSBASEURL);
webServiceA.setBaseUrl(baseUrl);
ExecutionService.getInstance().submitCallables(webServiceA);
NOTE :
This technique can also be used as a Singleton implementation. Just make the "ExecutionService" class Singleton.
Please be aware about the thread usage and implementation, I have used "newCachedThreadPool" of Executors, please consult the documentation of "java.util.concurrent.Executors" before implementing.
Comments
Post a Comment