0

ZK Long operation with hibernate issue

asked 2019-11-06 22:32:40 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2621 3 8
http://emrpms.blogspot.in...

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

Long Operation

Long Operation Examples

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 ?

delete flag offensive retag edit

6 Answers

Sort by » oldest newest most voted
1

answered 2019-11-07 11:38:18 +0800

cor3000 gravatar image cor3000
4952 2 7
ZK Team

updated 2019-11-07 11:41:07 +0800

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.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/context/request/RequestContextHolder.html

also this sounds related: https://stackoverflow.com/questions/1528444/accessing-scoped-proxy-beans-within-threads-of

link publish delete flag offensive edit
0

answered 2019-11-07 10:09:34 +0800

cor3000 gravatar image cor3000
4952 2 7
ZK Team

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?

link publish delete flag offensive edit
0

answered 2019-11-07 10:56:52 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2621 3 8
http://emrpms.blogspot.in...

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="&lt;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>

link publish delete flag offensive edit

Comments

is it so difficult to get the formatting right?

cor3000 ( 2019-11-07 11:15:05 +0800 )edit
0

answered 2019-11-07 12:31:25 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2621 3 8
http://emrpms.blogspot.in...

Ok Thanks. I am not an expert in spring. Let me try

link publish delete flag offensive edit

Comments

yes spring has a lot to digest

cor3000 ( 2019-11-08 09:46:41 +0800 )edit
0

answered 2019-11-10 11:26:24 +0800

Senthilchettyin gravatar image Senthilchettyin flag of India
2621 3 8
http://emrpms.blogspot.in...

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();

    }
link publish delete flag offensive edit
0

answered 2019-11-11 10:23:01 +0800

cor3000 gravatar image cor3000
4952 2 7
ZK Team

updated 2019-11-11 10:44:54 +0800

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.

link publish delete flag offensive edit
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow
2 followers

RSS

Stats

Asked: 2019-11-06 22:32:40 +0800

Seen: 23 times

Last updated: Nov 11

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More