001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    
019    package org.apache.geronimo.timer;
020    
021    import java.util.TimerTask;
022    
023    import javax.transaction.RollbackException;
024    import javax.transaction.SystemException;
025    import javax.transaction.Synchronization;
026    import javax.transaction.Status;
027    
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    
031    /**
032     *
033     *
034     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
035     *
036     * */
037    public class ExecutorFeedingTimerTask extends TimerTask {
038    
039        private static final Log log = LogFactory.getLog(ExecutorFeedingTimerTask.class);
040    
041        private final WorkInfo workInfo;
042        private final ThreadPooledTimer threadPooledTimer;
043        boolean cancelled = false;
044    
045        public ExecutorFeedingTimerTask(WorkInfo workInfo, ThreadPooledTimer threadPooledTimer) {
046            this.workInfo = workInfo;
047            this.threadPooledTimer = threadPooledTimer;
048        }
049    
050        public void run() {
051            threadPooledTimer.getExecutor().execute(workInfo.getExecutorTask());
052        }
053    
054        public boolean cancel() {
055            threadPooledTimer.removeWorkInfo(workInfo);
056            try {
057                threadPooledTimer.registerSynchronization(new CancelSynchronization(this));
058            } catch (RollbackException e) {
059                log.warn("Exception canceling task", e);
060                throw (IllegalStateException) new IllegalStateException("RollbackException when trying to register Cancel Synchronization").initCause(e);
061            } catch (SystemException e) {
062                log.warn("Exception canceling task", e);
063                throw (IllegalStateException) new IllegalStateException("SystemException when trying to register Cancel Synchronization").initCause(e);
064            }
065            // One cancels the task at this specific time. If the transaction is
066            // rolled-back, one will recreate it.
067            cancelled = true;
068            return super.cancel();
069        }
070    
071        public boolean isCancelled() {
072            return cancelled;
073        }
074    
075        private void doCancel() {
076            try {
077                // Impacts the timer storage only if the timer is cancelled
078                // in the scope of a committed transactions.
079                threadPooledTimer.getWorkerPersistence().cancel(workInfo.getId());
080            } catch (PersistenceException e) {
081                log.warn("Exception canceling task", e);
082            }
083        }
084    
085        private void rollbackCancel() {
086            threadPooledTimer.addWorkInfo(workInfo);
087            
088            // The transaction has been rolled-back, we need to restore the
089            // task as if cancel has been called.
090            if ( workInfo.isOneTime() ) {
091                threadPooledTimer.getTimer().schedule(
092                    new ExecutorFeedingTimerTask(workInfo, threadPooledTimer), 
093                    workInfo.getTime());
094            } else if ( workInfo.getAtFixedRate() ) {
095                threadPooledTimer.getTimer().scheduleAtFixedRate(
096                    new ExecutorFeedingTimerTask(workInfo, threadPooledTimer), 
097                    workInfo.getTime(), workInfo.getPeriod().longValue());
098            } else {
099                threadPooledTimer.getTimer().schedule(
100                    new ExecutorFeedingTimerTask(workInfo, threadPooledTimer),
101                    workInfo.getTime(), workInfo.getPeriod().longValue());
102            }
103        }
104        
105        private static class CancelSynchronization implements Synchronization {
106    
107            private final ExecutorFeedingTimerTask worker;
108    
109            public CancelSynchronization(ExecutorFeedingTimerTask worker) {
110                this.worker = worker;
111            }
112    
113            public void beforeCompletion() {
114            }
115    
116            public void afterCompletion(int status) {
117                if (status == Status.STATUS_COMMITTED) {
118                    worker.doCancel();
119                } else if (status == Status.STATUS_ROLLEDBACK) {
120                    worker.rollbackCancel();
121                }
122            }
123    
124        }
125    
126    }