-
FEATURED COMPONENTS
First time here? Check out the FAQ!
Let us first see the requirement first. We need to create 1500 Claims from one screen. Let us assume to create the claim, we need to insert the record into 4 tables and update two tables.
So it is a long operation. I read the following long operation and understood how to implement
Now i tried to implement the real-world example. Here in the execute method we need to call method in dao implementation to insert 4 tables and two updates.
I understand people will ask runnable code to give answer. But not sure how to give runnable code. So i will try to give as clear as possible
public abstract class LongOperation implements Runnable {
private String desktopId;
private DesktopCache desktopCache;
private Thread thread;
private AtomicBoolean cancelled = new AtomicBoolean(false);
/**
* asynchronous callback for your long operation code
*
* @throws InterruptedException
*/
protected abstract void execute() throws InterruptedException;
/**
* optional callback method when the task has completed successfully
*/
protected void onFinish() {
};
/**
* optional callback method when the task has been cancelled or was interrupted
* otherwise
*/
protected void onCancel() {
};
/**
* optional callback method when the task has completed with an uncaught
* RuntimeException
*
* @param exception
*/
protected void onException(RuntimeException exception) {
exception.printStackTrace();
};
/**
* optional callback method when the task has completed (always called)
*/
protected void onCleanup() {
};
/**
* set the cancelled flag and try to interrupt the thread
*/
public final void cancel() {
cancelled.set(true);
thread.interrupt();
}
/**
* check the cancelled flag
*
* @return
*/
public final boolean isCancelled() {
return cancelled.get();
}
/**
* activate the thread (and cached desktop) for UI updates call
* {@link #deactivate()} once done updating the UI
*
* @throws InterruptedException
*/
protected final void activate() throws InterruptedException {
Executions.activate(getDesktop());
}
/**
* deactivate the current active (see: {@link #activate()}) thread/desktop after
* updates are done
*/
protected final void deactivate() {
Executions.deactivate(getDesktop());
}
/**
* Checks if the task thread has been interrupted. Use this to check whether or
* not to exit a busy operation in case.
*
* @throws InterruptedException when the current task has been
* cancelled/interrupted
*/
protected final void checkCancelled() throws InterruptedException {
if (Thread.currentThread() != this.thread) {
throw new IllegalStateException(
"this method can only be called in the worker thread (i.e. during execute)");
}
boolean interrupted = Thread.interrupted();
if (interrupted || cancelled.get()) {
cancelled.set(true);
throw new InterruptedException();
}
}
/**
* launch the long operation
*/
public final void start() {
// not caching the desktop directly to enable garbage collection, in case the
// desktop destroyed during the long operation
this.desktopId = Executions.getCurrent().getDesktop().getId();
this.desktopCache = ((WebAppCtrl) WebApps.getCurrent()).getDesktopCache(Sessions.getCurrent());
enableServerPushForThisTask();
thread = new Thread(this);
thread.start();
}
@Override
public final void run() {
try {
try {
checkCancelled(); // avoid unnecessary execution
execute();
checkCancelled(); // final cancelled check before calling onFinish
activate();
onFinish();
} catch (InterruptedException e) {
try {
cancelled.set(true);
activate();
onCancel();
} catch (InterruptedException e1) {
throw new RuntimeException("interrupted onCancel handling", e1);
} finally {
deactivate();
}
} catch (RuntimeException rte) {
try {
activate();
onException(rte);
} catch (InterruptedException e1) {
throw new RuntimeException("interrupted onException handling", e1);
} finally {
deactivate();
}
throw rte;
} finally {
deactivate();
}
} finally {
try {
activate();
onCleanup();
} catch (InterruptedException e1) {
throw new RuntimeException("interrupted onCleanup handling", e1);
} finally {
deactivate();
disableServerPushForThisTask();
}
}
}
private UUID taskId = UUID.randomUUID();
private void enableServerPushForThisTask() {
((DesktopCtrl) getDesktop()).enableServerPush(true, taskId);
}
private void disableServerPushForThisTask() {
((DesktopCtrl) getDesktop()).enableServerPush(false, taskId);
}
private Desktop getDesktop() {
return desktopCache.getDesktop(desktopId);
}
}
import org.zkoss.zk.ui.util.Clients; public abstract class BusyLongOperation extends LongOperation {
protected void showBusy(String busyMessage) throws InterruptedException {
activate();
Clients.showBusy(busyMessage);
deactivate();
}
@Override
protected void onCleanup() {
Clients.clearBusy();
}
}
public void startLongOperation() {
//Some initial Operations
LongOperation longOperation = new BusyLongOperation() {
Integer counter = 0;
@Override
protected void execute() throws InterruptedException {
for (Iterator<ClaimBatchDTO> i = claimBatchDTOList1.iterator(); i.hasNext();) {
ClaimBatchDTO tmp = i.next();
showBusy("Creating Claim " + counter);
BillingService billingService = (BillingService) SpringUtil.getBean("BillingService");
billingService.saveClaimBatchSingle(tmp);
counter++;
}
}
@Override
protected void onFinish() {
//Some Cleanup goes here
}
};
Clients.showBusy("Starting Operation...");
longOperation.start();
}
I got the following error
org.zkoss.zk.ui.UiException: SpringUtil can be called only under ZK environment!
Due to the following statement BillingService billingService = (BillingService) SpringUtil.getBean("BillingService");
Any help ?
Ok, now you're one step further. You successfully located your billingService from spring via ZK's SpringUtil.
Instead your code now fails when trying to access your database via Spring/Hibernate. Since this thread is not running in a servlet request, it doesn't go through your OpenSessionInViewFilter and it isn't aware of your Spring configuration either. So you have to initialize anything spring needs to function by yourself. ZK can't help here any more. Please refer to the spring community how to deal with non-servlet threads. The key class involved should be RequestContextHolder.
also this sounds related: https://stackoverflow.com/questions/1528444/accessing-scoped-proxy-beans-within-threads-of
Hi Senthil,
thanks for the details, making the explanation easy.
public final void run() {
try {
try {
checkCancelled(); // avoid unnecessary execution
execute(); //NOT running in a activated zk execution
checkCancelled(); // final cancelled check before calling onFinish
activate(); //activation happening here
onFinish();
The run()
method is running in a separate thread, which is not a ZK thread, so any operation requiring an activated ZK Execution is going to fail. The execute()
method of LongOperation is meant for background processes so it's not activated either. However inside it you can activate/deactivate (e.g. to show/clear busy).
Also your Call to SpringUtil fails unless running in an activated execution.
--> org.zkoss.zk.ui.UiException: SpringUtil can be called only under ZK environment!
At the end the onFinish()
method is called after activating. So in side onFinish you can use ZK functionality. However that's too late for your purpose.
So could activate/deactivate before/after calling SpringUtil (technically possible but unnecessarily expensive, since it performs a client side server push round trip).
Instead you can just initialize your service before the LongOperation.
//Some initial Operations !!!!!!!
//such as initializing required resources
final BillingService billingService = (BillingService) SpringUtil.getBean("BillingService");
LongOperation longOperation = new BusyLongOperation() {
Integer counter = 0;
@Override
protected void execute() throws InterruptedException {
for (Iterator<ClaimBatchDTO> i = claimBatchDTOList1.iterator(); i.hasNext();) {
ClaimBatchDTO tmp = i.next();
showBusy("Creating Claim " + counter);
//just use the service here
billingService.saveClaimBatchSingle(tmp);
Does this help in your scenario?
Thanks, Robert. I did earlier before creating the ZK Forum. Anyway, here is an error which I got
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) at com.product.webapp.apputility.FHSessionUtil.getSession(FHSessionUtil.java:43) at com.product.webapp.apputility.FHSessionUtil.getCurrentUser(FHSessionUtil.java:66) at com.product.hibernate.AutoColumnsPopulator.onSave(AutoColumnsPopulator.java:55) at org.hibernate.event.internal.AbstractSaveEventListener.substituteValuesIfNecessary(AbstractSaveEventListener.java:394) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:274) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:198) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:128) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:193) at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:178) at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:75) at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:697) at org.hibernate.internal.SessionImpl.save(SessionImpl.java:689) at org.hibernate.internal.SessionImpl.save(SessionImpl.java:684) at com.product.business.dao.BillingDaoImpl.saveClaimBatchSingle(BillingDaoImpl.java:2117) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy71.saveClaimBatchSingle(Unknown Source) at com.product.business.service.BillingServiceImpl.saveClaimBatchSingle(BillingServiceImpl.java:195) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy83.saveClaimBatchSingle(Unknown Source) at com.product.webapp.billing.ClaimBatchCreateClaimVM$1.execute(ClaimBatchCreateClaimVM.java:215) at com.product.webapp.apputility.LongOperation.run(LongOperation.java:128) at java.base/java.lang.Thread.run(Thread.java:835) Exception in thread "Thread-39" java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) at com.product.webapp.apputility.FHSessionUtil.getSession(FHSessionUtil.java:43) at com.product.webapp.apputility.FHSessionUtil.getCurrentUser(FHSessionUtil.java:66) at com.product.hibernate.AutoColumnsPopulator.onSave(AutoColumnsPopulator.java:55) at org.hibernate.event.internal.AbstractSaveEventListener.substituteValuesIfNecessary(AbstractSaveEventListener.java:394) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:274) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:198) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:128) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:193) at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:178) at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:75) at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:697) at org.hibernate.internal.SessionImpl.save(SessionImpl.java:689) at org.hibernate.internal.SessionImpl.save(SessionImpl.java:684) at com.product.business.dao.BillingDaoImpl.saveClaimBatchSingle(BillingDaoImpl.java:2117) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy71.saveClaimBatchSingle(Unknown Source) at com.product.business.service.BillingServiceImpl.saveClaimBatchSingle(BillingServiceImpl.java:195) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy83.saveClaimBatchSingle(Unknown Source) at com.product.webapp.billing.ClaimBatchCreateClaimVM$1.execute(ClaimBatchCreateClaimVM.java:215) at com.product.webapp.apputility.LongOperation.run(LongOperation.java:128) at java.base/java.lang.Thread.run(Thread.java:835)
After searching google, i find, we need add the following web.xml
<web-app ...>
<listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> </web-app>
I already added, but still same error. Here is my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="<a href=" http:="" java.sun.com="" xml="" ns="" javaee"="">http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<description><![CDATA[My ZK Application]]></description>
<display-name>hrm-admin</display-name>
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<!-- Spring can be easily integrated into any Java-based web framework.
All you need to do is to declare the ContextLoaderListener in your web.xml
and use a contextConfigLocation <context-param> to set which context files
to load. If you don't specify the contextConfigLocation context parameter,
the ContextLoaderListener will look for a /WEB-INF/applicationContext.xml
file to load. Once the context files are loaded, Spring creates a WebApplicationContext
object based on the bean definitions and puts it into the ServletContext. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
classpath:applicationContext_hib.xml
classpath:springOrmContext.xml
classpath:spring-security.xml
</param-value>
</context-param>
<!-- Loads the Spring web application context -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- ====================================================== -->
<!-- SPRING REQUEST LISTENER -->
<!-- ====================================================== -->
<listener>
<display-name>Spring Request Context Listener</display-name>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<!-- A servlet filter capturing every user requests and sending them to
the configured security filters to make sure access is authorized. -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<!-- //// -->
<!-- ZK -->
<servlet>
<servlet-name>dspLoader</servlet-name>
<servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dspLoader</servlet-name>
<url-pattern>*.dsp</url-pattern>
</servlet-mapping>
<listener>
<description>ZK listener for session cleanup</description>
<listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
</listener>
<servlet>
<description>ZK loader for ZUML pages</description>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<!-- Must. Specifies URI of the update engine (DHtmlUpdateServlet). It
must be the same as <url-pattern> for the update engine. -->
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
<!-- Optional. Specifies whether to compress the output of the ZK loader.
It speeds up the transmission over slow Internet. However, if you configure
a filter to post-processing the output, you might have to disable it. Default:
true <init-param> <param-name>compress</param-name> <param-value>true</param-value>
</init-param> -->
<!-- [Optional] Specifies the default log level: OFF, ERROR, WARNING, INFO,
DEBUG and FINER. If not specified, the system default is used. <init-param>
<param-name>log-level</param-name> <param-value>OFF</param-value> </init-param> -->
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zul</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zhtml</url-pattern>
</servlet-mapping>
<!-- [Optional] Uncomment it if you want to use richlets. <servlet-mapping>
<servlet-name>zkLoader</servlet-name> <url-pattern>/zk/*</url-pattern> </servlet-mapping> -->
<servlet>
<description>The asynchronous update engine for ZK</description>
<servlet-name>auEngine</servlet-name>
<servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
<!-- [Optional] Specifies whether to compress the output of the ZK loader.
It speeds up the transmission over slow Internet. However, if your server
will do the compression, you might have to disable it. Default: true <init-param>
<param-name>compress</param-name> <param-value>true</param-value> </init-param> -->
<!-- [Optional] Specifies the AU extension for particular prefix. <init-param>
<param-name>extension0</param-name> <param-value>/upload=com.my.MyUploader</param-value>
</init-param> -->
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>auEngine</servlet-name>
<url-pattern>/zkau/*</url-pattern>
</servlet-mapping>
<!-- [Optional] Uncomment if you want to use the ZK filter to post process
the HTML output generated by other technology, such as JSP and velocity.
<filter> <filter-name>zkFilter</filter-name> <filter-class>org.zkoss.zk.ui.http.DHtmlLayoutFilter</filter-class>
<init-param> <param-name>extension</param-name> <param-value>html</param-value>
</init-param> <init-param> <param-name>compress</param-name> <param-value>true</param-value>
</init-param> </filter> <filter-mapping> <filter-name>zkFilter</filter-name>
<url-pattern>your URI pattern</url-pattern> </filter-mapping> -->
<!-- //// -->
<!-- ///////////// -->
<!-- DSP (optional) Uncomment it if you want to use DSP However, it is turned
on since zksandbox uses DSP to generate CSS. <servlet> <servlet-name>dspLoader</servlet-name>
<servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class>
<init-param> <param-name>class-resource</param-name> <param-value>true</param-value>
</init-param> </servlet> <servlet-mapping> <servlet-name>dspLoader</servlet-name>
<url-pattern>*.dsp</url-pattern> </servlet-mapping> -->
<!-- /////////// -->
<!-- [Optional] Session timeout -->
<session-config>
<session-timeout>60</session-timeout>
</session-config>
<!-- [Optional] MIME mapping -->
<mime-mapping>
<extension>doc</extension>
<mime-type>application/vnd.ms-word</mime-type>
</mime-mapping>
<mime-mapping>
<extension>gif</extension>
<mime-type>image/gif</mime-type>
</mime-mapping>
<mime-mapping>
<extension>htm</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
<mime-mapping>
<extension>html</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jpeg</extension>
<mime-type>image/jpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jpg</extension>
<mime-type>image/jpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>js</extension>
<mime-type>text/javascript</mime-type>
</mime-mapping>
<mime-mapping>
<extension>pdf</extension>
<mime-type>application/pdf</mime-type>
</mime-mapping>
<mime-mapping>
<extension>png</extension>
<mime-type>image/png</mime-type>
</mime-mapping>
<mime-mapping>
<extension>txt</extension>
<mime-type>text/plain</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xls</extension>
<mime-type>application/vnd.ms-excel</mime-type>
</mime-mapping>
<mime-mapping>
<extension>xml</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
<mime-mapping>
<extension>zhtml</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
<mime-mapping>
<extension>zul</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
<!-- ====================================================== -->
<!-- Path to the own error page -->
<!-- ====================================================== -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/WEB-INF/sys/error.zul</location>
</error-page>
<welcome-file-list>
<welcome-file>index.zul</welcome-file>
<welcome-file>index.zhtml</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
</web-app>
Ok Thanks. I am not an expert in spring. Let me try
Yes. Finally Done. Thanks, Robert. Here are the changes I made. As you suggested, I followed the below link https://stackoverflow.com/questions/1528444/accessing-scoped-proxy-beans-within-threads-of and modify the Long operation file as below
package com.product.webapp.apputility;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Sessions;
import org.zkoss.zk.ui.WebApps;
import org.zkoss.zk.ui.sys.DesktopCache;
import org.zkoss.zk.ui.sys.DesktopCtrl;
import org.zkoss.zk.ui.sys.WebAppCtrl;
public abstract class LongOperation implements Runnable {
private String desktopId;
private DesktopCache desktopCache;
private Thread thread;
private AtomicBoolean cancelled = new AtomicBoolean(false);
private RequestAttributes requestAttributes;
/**
* asynchronous callback for your long operation code
*
* @throws InterruptedException
*/
protected abstract void execute() throws InterruptedException;
/**
* optional callback method when the task has completed successfully
*/
protected void onFinish() {
};
/**
* optional callback method when the task has been cancelled or was interrupted
* otherwise
*/
protected void onCancel() {
};
/**
* optional callback method when the task has completed with an uncaught
* RuntimeException
*
* @param exception
*/
protected void onException(RuntimeException exception) {
exception.printStackTrace();
};
/**
* optional callback method when the task has completed (always called)
*/
protected void onCleanup() {
};
/**
* set the cancelled flag and try to interrupt the thread
*/
public final void cancel() {
cancelled.set(true);
thread.interrupt();
}
/**
* check the cancelled flag
*
* @return
*/
public final boolean isCancelled() {
return cancelled.get();
}
/**
* activate the thread (and cached desktop) for UI updates call
* {@link #deactivate()} once done updating the UI
*
* @throws InterruptedException
*/
protected final void activate() throws InterruptedException {
Executions.activate(getDesktop());
}
/**
* deactivate the current active (see: {@link #activate()}) thread/desktop after
* updates are done
*/
protected final void deactivate() {
Executions.deactivate(getDesktop());
}
/**
* Checks if the task thread has been interrupted. Use this to check whether or
* not to exit a busy operation in case.
*
* @throws InterruptedException when the current task has been
* cancelled/interrupted
*/
protected final void checkCancelled() throws InterruptedException {
if (Thread.currentThread() != this.thread) {
throw new IllegalStateException(
"this method can only be called in the worker thread (i.e. during execute)");
}
boolean interrupted = Thread.interrupted();
if (interrupted || cancelled.get()) {
cancelled.set(true);
throw new InterruptedException();
}
}
/**
* launch the long operation
*/
public final void start() {
// not caching the desktop directly to enable garbage collection, in case the
// desktop destroyed during the long operation
this.requestAttributes = RequestContextHolder.getRequestAttributes();
this.desktopId = Executions.getCurrent().getDesktop().getId();
this.desktopCache = ((WebAppCtrl) WebApps.getCurrent()).getDesktopCache(Sessions.getCurrent());
RequestAttributes atr = RequestContextHolder.currentRequestAttributes();
HttpSession session = (HttpSession) atr.getSessionMutex();
enableServerPushForThisTask();
thread = new Thread(this);
thread.start();
}
@Override
public final void run() {
try {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
checkCancelled(); // avoid unnecessary execution
execute();
checkCancelled(); // final cancelled check before calling onFinish
activate();
onFinish();
} catch (InterruptedException e) {
try {
cancelled.set(true);
activate();
onCancel();
} catch (InterruptedException e1) {
throw new RuntimeException("interrupted onCancel handling", e1);
} finally {
deactivate();
}
} catch (RuntimeException rte) {
try {
activate();
onException(rte);
} catch (InterruptedException e1) {
throw new RuntimeException("interrupted onException handling", e1);
} finally {
deactivate();
}
throw rte;
} finally {
deactivate();
}
} finally {
try {
activate();
if (Thread.currentThread() != thread) {
RequestContextHolder.resetRequestAttributes();
}
thread = null;
onCleanup();
} catch (InterruptedException e1) {
throw new RuntimeException("interrupted onCleanup handling", e1);
} finally {
deactivate();
disableServerPushForThisTask();
}
}
}
private UUID taskId = UUID.randomUUID();
private void enableServerPushForThisTask() {
((DesktopCtrl) getDesktop()).enableServerPush(true, taskId);
}
private void disableServerPushForThisTask() {
((DesktopCtrl) getDesktop()).enableServerPush(false, taskId);
}
private Desktop getDesktop() {
return desktopCache.getDesktop(desktopId);
}
}
@Command
public void startLongOperation() {
//Some Initial Settings
LongOperation longOperation = new BusyLongOperation() {
Integer counter = 0;
BillingService billingService = (BillingService) SpringUtil.getBean("BillingService");
@Override
protected void execute() throws InterruptedException {
for (Iterator<ClaimBatchDTO> i = claimBatchDTOList.iterator(); i.hasNext();) {
ClaimBatchDTO tmp = i.next();
if (StringUtils.isBlank(tmp.getPatientClaimBatchDetails().getClaimNo())) {
step(counter);
billingService.saveClaimBatchSingleOnThread(tmp);
afterClaimCreate = true;
claimsCreated = counter;
counter++;
}
}
}
private void step(int progress) throws InterruptedException {
activate();
updateStatus(progress);
deactivate();
}
@Override
protected void onFinish() {
finalClaimCreationProcess();
}
};
longOperation.start();
}
Good you found something... one note just to be more save on the cleanup side. I am not sure about the purpose of this:
} finally {
try {
activate();
if (Thread.currentThread() != thread) { /* this condition */
RequestContextHolder.resetRequestAttributes();
}
thread = null;
onCleanup();
how would that ever be true, unless you're calling start() multiple times? And once that happens, why rese only in this particular case.
Another thing to keep in mind: activate()
can fail, e.g. if the desktop was destroyed during your long operation. In that case resetRequestAttributes would never be called, so my suggestion do it before calling activate()
- it doesn't mean I understand what that's supposed to do, but in case you care, don't rely on activate()
.
} finally {
try {
if (Thread.currentThread() != thread) { /* this condition */
RequestContextHolder.resetRequestAttributes();
}
activate();
thread = null;
onCleanup();
Another note, please avoid copying the whole code just to implement a few changes. The less invasive approach would be to subclass. The Long operation class, so that your code that doesn't require Spring can still use it.
e.g.:
public abstract class SpringLongOperation extends LongOperation {
private RequestAttributes requestAttributes;
public SpringLongOperation() {
requestAttributes = RequestContextHolder.getRequestAttributes();
}
@Override
protected void execute() throws InterruptedException {
RequestContextHolder.setRequestAttributes(requestAttributes);
}
@Override
protected void onCleanup() {
RequestContextHolder.resetRequestAttributes();
}
}
The remaining problem here is onCleanup being called in after activate, so you might add another callback to the LongOperation class that lets you do the cleanup without an active execution, which will be benefitial in case you are reusing threads.
Asked: 2019-11-06 22:32:40 +0800
Seen: 28 times
Last updated: Nov 11 '19
I need help to combine XYModel and BoxPlotModel into a single Chart
[Announcement] ZK 8.6 Introductory Session Open for Registration
how to set the global font-size
I configured iceblue_c theme, but it did not work. Please advise. Thank you
request hot fix for CE version for issue ZK-4127
Can you set min-width for columns in the grid that identify hflex=1?