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