- they block the web container thread, which slows down user satisfaction;
- make the appearance of application to freeze;
- the task can take up time of no more then request timeout;
- you cannot cancel the task;
- you don't see the progress of your task.
You'd better do such operations in an asynchronous way in a separate thread. But it is prohibited to spawn your own threads. Among the reasons against this are:
- custom threads are not managed by application server;
- J2EE context is not propagated to custom managed threads. That means that you cannot get JNDI resources.
Here is the solution to execute the tasks:
- use AJAX to create and start an asynchronous task, save it in portlet session
- use AJAX to show to user the progress of task completion
- after completion of task get the results.
At first, you should define the work manager in WebSphere Application Server. You can look at the setup here.Take a look at parameter "Work request queue full action". Set it to FAIL, or the execute method will block.
In web.xml declare the pool:
<resource-ref>
<description>WorkManager</description>
<res-ref-name>wm/reportWorker</res-ref-name>
<res-type>commonj.work.WorkManager</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
In ibm-web-bnd.xml declare:
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd
xmlns="http://websphere.ibm.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_0.xsd" version="1.0">
<virtual-host name="default_host" />
<resource-ref name="wm/reportWorker" binding-name="wm/reportWorker" />
</web-bnd>
In applicationContext declare work manager:
<!-- workManager -->
<bean id="wmTaskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/reportWorker" />
<property name="resourceRef" value="true" />
</bean>
In controller define the work manager:
@Autowired
@Qualifier("wmTaskExecutor")
private AsyncTaskExecutor executor;
In resouremapping method like this:
@ResourceMapping(value = "exportSearchResultsAsXlsx")
public void exportSearchResultsAsXlsx(ResourceRequest request, ResourceResponse response, PortletSession porsession)
create you task, submit it to executor and store the reference to it in portlet session:
ExportDocResultThread exportDocResultThread = new ExportDocResultThread();
executor.execute(exportDocResultThread, AsyncTaskExecutor.TIMEOUT_IMMEDIATE);
porsession.setAttribute(EXPORT_DOC_RESULT_THREAD, exportDocResultThread);
After this you can query your portlet session for a thread and ask it for status, get results or cancel.
ExportDocResultThread has the following signature:
public class ExportDocResultThread implements java.lang.Runnable.
Unit testing such tasks is easy. You create executor and send task to it:
TaskExecutor executor = new ConcurrentTaskExecutor();
executor.execute(exportDocResultThread);
Also, note that execute method can throw TaskRejectedException if thread waiting queue executor is full.
Комментариев нет:
Отправить комментарий