[jboss-jira] [JBoss JIRA] (WFLY-3683) ScheduledFuture#cancel(boolean) failing to cancel tasks on bean PreDestroy

Aaron Cordova (JIRA) issues at jboss.org
Mon Jul 28 19:49:30 EDT 2014


     [ https://issues.jboss.org/browse/WFLY-3683?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Aaron Cordova updated WFLY-3683:
--------------------------------

    Description: 
Calls to ScheduledFuture#cancel on bean cleanup are failing to cancel subsequent tasks. 

*NOTE* This only occurs when using the Trigger functionality. I tested the same code with scheduleAtFixedRate, and it works as expected.

The following bean demonstrates the bug:
{code:title=Executor.java|borderStyle=solid}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.concurrent.LastExecution;
import javax.enterprise.concurrent.ManagedScheduledExecutorService;
import javax.enterprise.concurrent.Trigger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Session Bean implementation class DataRecorderManagerBean
 */
@Singleton
@Startup
public class ExecutorBug {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorBug.class);

    @Resource
    private ManagedScheduledExecutorService executorService;

    private List<ScheduledFuture<?>> scheduledTasks;

    /**
     * Default constructor.
     */
    public ExecutorBug() {
    }

    @PostConstruct
    void intialize() {
        this.scheduledTasks = Collections.synchronizedList(new ArrayList<ScheduledFuture<?>>());

        // Schedule the device task.
        PeriodicTrigger trigger = new PeriodicTrigger(0, 10, TimeUnit.SECONDS);
        ScheduledFuture<?> future = this.executorService.schedule(new LogRunner(), trigger);

        // Create the scheduled task and add it to the map.
        this.scheduledTasks.add(future);
    }

    @PreDestroy
    void cleanup() {
        // Cancel any scheduled tasks, ensuring that the map is locked.
        synchronized (this.scheduledTasks) {
            Iterator<ScheduledFuture<?>> i = this.scheduledTasks.iterator();

            while (i.hasNext()) {
                ScheduledFuture<?> future = i.next();
                // Cancel the task.
                future.cancel(true);
            }
        }
        this.scheduledTasks.clear();
        this.scheduledTasks = null;
    }

    private class LogRunner implements Runnable {
        public LogRunner() {
        }

        @Override
        public void run() {
            LOGGER.info("I am running");
        }
    }

    private class PeriodicTrigger implements Trigger {
        private final long periodMillis;
        private Date startTime;

        /**
         * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay
         * then initialDelay+period, then initialDelay + 2 * period, and so on.
         * 
         * @param initialDelay the time to delay first execution
         * @param period the period between successive executions
         * @param unit the time unit of the initialDelay and period parameters
         */
        public PeriodicTrigger(final long initialDelay, final long period, final TimeUnit unit) {
            this.periodMillis = TimeUnit.MILLISECONDS.convert(period, unit);

            // Calculate the start time.
            Date now = new Date();
            long millis = TimeUnit.MILLISECONDS.convert(initialDelay, unit);
            this.startTime = new Date(now.getTime() + millis);
        }


        @Override
        public Date getNextRunTime(final LastExecution lastExecutionInfo, final Date taskScheduledTime) {
            if (lastExecutionInfo == null) {
                return this.startTime;
            }
            else {
                return new Date(lastExecutionInfo.getScheduledStart().getTime() + this.periodMillis);
            }
        }

        @Override
        public boolean skipRun(final LastExecution lastExecutionInfo, final Date scheduledRunTime) {
            return false;
        }
    }
}
{code}

Steps:
* Deploy bean in Wildfly 8.1.0-Final using maven deploy goal.
* Verify log statement is output.
* Un-deploy bean from admin console.

*Expected*
Log statement is no longer called

*Actual*
Log statement continues to output after bean is destroyed.

  was:
Calls to ScheduledFuture#cancel on bean cleanup are failing to cancel subsequent tasks. 

*NOTE* This only occurs when using the Trigger functionality. I tested the same code with scheduleAtFixedRate, and it works as expected.

The following bean demonstrates the bug:
{code:title=Executor.java|borderStyle=solid}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.concurrent.LastExecution;
import javax.enterprise.concurrent.ManagedScheduledExecutorService;
import javax.enterprise.concurrent.Trigger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Session Bean implementation class DataRecorderManagerBean
 */
@Singleton
@Startup
public class ExecutorBug {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorBug.class);

    @Resource
    private ManagedScheduledExecutorService executorService;

    private List<ScheduledFuture<?>> scheduledTasks;

    /**
     * Default constructor.
     */
    public ExecutorBug() {
    }

    @PostConstruct
    void intialize() {
        this.scheduledTasks = Collections.synchronizedList(new ArrayList<ScheduledFuture<?>>());

        // Schedule the device task.
        PeriodicTrigger trigger = new PeriodicTrigger(0, 10, TimeUnit.SECONDS);
        ScheduledFuture<?> future = this.executorService.schedule(new LogRunner(), trigger);

        // Create the scheduled task and add it to the map.
        this.scheduledTasks.add(future);
    }

    @PreDestroy
    void cleanup() {
        // Cancel any scheduled tasks, ensuring that the map is locked.
        synchronized (this.scheduledTasks) {
            Iterator<ScheduledFuture<?>> i = this.scheduledTasks.iterator();

            while (i.hasNext()) {
                ScheduledFuture<?> future = i.next();
                // Cancel the task.
                future.cancel(true);
            }
        }
        this.scheduledTasks.clear();
        this.scheduledTasks = null;
    }

    private class LogRunner implements Runnable {
        public LogRunner() {
        }

        @Override
        public void run() {
            LOGGER.info("I am running");
        }
    }

    private class PeriodicTrigger implements Trigger {
        private final long periodMillis;
        private Date startTime;

        /**
         * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay
         * then initialDelay+period, then initialDelay + 2 * period, and so on.
         * 
         * @param initialDelay the time to delay first execution
         * @param period the period between successive executions
         * @param unit the time unit of the initialDelay and period parameters
         */
        public PeriodicTrigger(final long initialDelay, final long period, final TimeUnit unit) {
            this.periodMillis = TimeUnit.MILLISECONDS.convert(period, unit);

            // Calculate the start time.
            Date now = new Date();
            long millis = TimeUnit.MILLISECONDS.convert(initialDelay, unit);
            this.startTime = new Date(now.getTime() + millis);
        }


        @Override
        public Date getNextRunTime(final LastExecution lastExecutionInfo, final Date taskScheduledTime) {
            if (lastExecutionInfo == null) {
                return this.startTime;
            }
            else {
                return new Date(lastExecutionInfo.getScheduledStart().getTime() + this.periodMillis);
            }
        }

        @Override
        public boolean skipRun(final LastExecution lastExecutionInfo, final Date scheduledRunTime) {
            return false;
        }
    }
}
{code}

Steps:
* Deploy bean in Wildfly 8.1.0-Final using maven deploy goal.
* Verify log statement is output.
* Un-deploy bean from admin console.

**Expected**
Log statement is no longer called

**Actual**
Log statement continues to output after bean is destroyed.



> ScheduledFuture#cancel(boolean) failing to cancel tasks on bean PreDestroy
> --------------------------------------------------------------------------
>
>                 Key: WFLY-3683
>                 URL: https://issues.jboss.org/browse/WFLY-3683
>             Project: WildFly
>          Issue Type: Feature Request
>      Security Level: Public(Everyone can see) 
>          Components: EE
>    Affects Versions: 8.1.0.Final
>            Reporter: Aaron Cordova
>            Assignee: David Lloyd
>
> Calls to ScheduledFuture#cancel on bean cleanup are failing to cancel subsequent tasks. 
> *NOTE* This only occurs when using the Trigger functionality. I tested the same code with scheduleAtFixedRate, and it works as expected.
> The following bean demonstrates the bug:
> {code:title=Executor.java|borderStyle=solid}
> import java.util.ArrayList;
> import java.util.Collections;
> import java.util.Date;
> import java.util.Iterator;
> import java.util.List;
> import java.util.concurrent.ScheduledFuture;
> import java.util.concurrent.TimeUnit;
> import javax.annotation.PostConstruct;
> import javax.annotation.PreDestroy;
> import javax.annotation.Resource;
> import javax.ejb.Singleton;
> import javax.ejb.Startup;
> import javax.enterprise.concurrent.LastExecution;
> import javax.enterprise.concurrent.ManagedScheduledExecutorService;
> import javax.enterprise.concurrent.Trigger;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> /**
>  * Session Bean implementation class DataRecorderManagerBean
>  */
> @Singleton
> @Startup
> public class ExecutorBug {
>     private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorBug.class);
>     @Resource
>     private ManagedScheduledExecutorService executorService;
>     private List<ScheduledFuture<?>> scheduledTasks;
>     /**
>      * Default constructor.
>      */
>     public ExecutorBug() {
>     }
>     @PostConstruct
>     void intialize() {
>         this.scheduledTasks = Collections.synchronizedList(new ArrayList<ScheduledFuture<?>>());
>         // Schedule the device task.
>         PeriodicTrigger trigger = new PeriodicTrigger(0, 10, TimeUnit.SECONDS);
>         ScheduledFuture<?> future = this.executorService.schedule(new LogRunner(), trigger);
>         // Create the scheduled task and add it to the map.
>         this.scheduledTasks.add(future);
>     }
>     @PreDestroy
>     void cleanup() {
>         // Cancel any scheduled tasks, ensuring that the map is locked.
>         synchronized (this.scheduledTasks) {
>             Iterator<ScheduledFuture<?>> i = this.scheduledTasks.iterator();
>             while (i.hasNext()) {
>                 ScheduledFuture<?> future = i.next();
>                 // Cancel the task.
>                 future.cancel(true);
>             }
>         }
>         this.scheduledTasks.clear();
>         this.scheduledTasks = null;
>     }
>     private class LogRunner implements Runnable {
>         public LogRunner() {
>         }
>         @Override
>         public void run() {
>             LOGGER.info("I am running");
>         }
>     }
>     private class PeriodicTrigger implements Trigger {
>         private final long periodMillis;
>         private Date startTime;
>         /**
>          * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay
>          * then initialDelay+period, then initialDelay + 2 * period, and so on.
>          * 
>          * @param initialDelay the time to delay first execution
>          * @param period the period between successive executions
>          * @param unit the time unit of the initialDelay and period parameters
>          */
>         public PeriodicTrigger(final long initialDelay, final long period, final TimeUnit unit) {
>             this.periodMillis = TimeUnit.MILLISECONDS.convert(period, unit);
>             // Calculate the start time.
>             Date now = new Date();
>             long millis = TimeUnit.MILLISECONDS.convert(initialDelay, unit);
>             this.startTime = new Date(now.getTime() + millis);
>         }
>         @Override
>         public Date getNextRunTime(final LastExecution lastExecutionInfo, final Date taskScheduledTime) {
>             if (lastExecutionInfo == null) {
>                 return this.startTime;
>             }
>             else {
>                 return new Date(lastExecutionInfo.getScheduledStart().getTime() + this.periodMillis);
>             }
>         }
>         @Override
>         public boolean skipRun(final LastExecution lastExecutionInfo, final Date scheduledRunTime) {
>             return false;
>         }
>     }
> }
> {code}
> Steps:
> * Deploy bean in Wildfly 8.1.0-Final using maven deploy goal.
> * Verify log statement is output.
> * Un-deploy bean from admin console.
> *Expected*
> Log statement is no longer called
> *Actual*
> Log statement continues to output after bean is destroyed.



--
This message was sent by Atlassian JIRA
(v6.2.6#6264)


More information about the jboss-jira mailing list