/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2014, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* This file incorporates work covered by the following copyright contributed under the GNU LGPL : Copyright 2007-2011 Red Hat.
*/
package org.mobicents.slee.runtime.facilities;
import java.util.concurrent.CountDownLatch;
import javax.slee.ActivityContextInterface;
import javax.slee.Address;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.TransactionRolledbackLocalException;
import javax.slee.facilities.FacilityException;
import javax.slee.facilities.TimerID;
import javax.slee.facilities.TimerOptions;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.AbstractSleeContainerModule;
import org.mobicents.slee.container.activity.ActivityContext;
import org.mobicents.slee.container.facilities.TimerFacility;
import org.mobicents.slee.container.management.jmx.TimerFacilityConfiguration;
import org.mobicents.slee.container.transaction.SleeTransactionManager;
import org.mobicents.slee.container.transaction.TransactionContext;
import org.mobicents.slee.container.transaction.TransactionalAction;
import org.mobicents.slee.util.concurrent.SleeThreadFactory;
import org.restcomm.timers.FaultTolerantScheduler;
/**
* Implementation of the SLEE timer facility. timer is the timer object
* currently being examined. timer.scheduleTime is time that the Timer Event is
* scheduled to fire. timer.timeout is timeout for this timer (from
* TimerOptions). timer.numRepetitions is the total repetitions for this timer,
* 0 if infinite, 1 if non-periodic. timer.remainingRepetiton is the remaining
* repetition count, initially Long.MAX_VALUE for infinite periodic timers,
* timer.numRepetitions otherwise. timer.period is the timer period
* (Long.MAX_VALUE if non-periodic). timer.missed is the counter of undelivered
* late events.
*
* @author Tim
* @author M. Ranganathan
* @author Ivelin Ivanov
* @author martins
*
*/
public class TimerFacilityImpl extends AbstractSleeContainerModule implements TimerFacility {
private final static SleeThreadFactory SLEE_THREAD_FACTORY = new SleeThreadFactory("SLEE-TimerFacility");
private static Logger logger = Logger.getLogger(TimerFacilityImpl.class);
private static final int DEFAULT_TIMEOUT = 1000;
// this is supposed to be the timer resolution in ms of the hosting
// OS/hardware
private int timerResolution = 10;
private FaultTolerantScheduler scheduler;
private final TimerFacilityConfiguration configuration;
/**
*
*/
public TimerFacilityImpl(TimerFacilityConfiguration configuration) {
this.configuration = configuration;
}
@Override
public void sleeInitialization() {
}
@Override
public void sleeStarting() {
if (scheduler != null) {
scheduler.shutdownNow();
}
scheduler = new FaultTolerantScheduler("timer-facility",configuration.getTimerThreads(),sleeContainer.getCluster(),(byte)10, sleeContainer.getTransactionManager().getRealTransactionManager(),new TimerFacilityTimerTaskFactory(),configuration.getPurgePeriod(), SLEE_THREAD_FACTORY);
}
/**
* Retrieves
* @return the scheduler
*/
public FaultTolerantScheduler getScheduler() {
return scheduler;
}
/*
* (non-Javadoc)
* @see javax.slee.facilities.TimerFacility#setTimer(javax.slee.ActivityContextInterface, javax.slee.Address, long, javax.slee.facilities.TimerOptions)
*/
public TimerID setTimer(ActivityContextInterface aci, Address address,
long startTime, TimerOptions timerOptions)
throws NullPointerException, IllegalArgumentException,
FacilityException {
return setTimer(aci, address, startTime, Long.MAX_VALUE, 1,
timerOptions);
}
/*
* (non-Javadoc)
* @see javax.slee.facilities.TimerFacility#setTimer(javax.slee.ActivityContextInterface, javax.slee.Address, long, long, int, javax.slee.facilities.TimerOptions)
*/
public TimerID setTimer(ActivityContextInterface aci, Address address,
long startTime, long period, int numRepetitions,
TimerOptions timerOptions) throws NullPointerException,
IllegalArgumentException, TransactionRolledbackLocalException,
FacilityException {
if (aci == null)
throw new NullPointerException("Null ActivityContextInterface");
if (startTime < 0)
throw new IllegalArgumentException("startTime < 0");
if (period <= 0)
throw new IllegalArgumentException("period <= 0");
if (timerOptions == null)
throw new NullPointerException("Null TimerOptions");
if (timerOptions.getTimeout() > period)
throw new IllegalArgumentException("timeout > period");
if (timerOptions.getTimeout() < this.getResolution())
timerOptions.setTimeout(Math.min(period, this.getResolution()));
if (period == Long.MAX_VALUE && numRepetitions == 1) {
// non periodic value, the framework expects it to be negative instead
period = -1;
}
// when numRepetitions == 0 the timer repeats infinitely or until
// canceled
if (numRepetitions < 0)
throw new IllegalArgumentException("numRepetitions < 0");
SleeTransactionManager txMgr = sleeContainer.getTransactionManager();
boolean startedTx = txMgr.requireTransaction();
TimerIDImpl timerID = new TimerIDImpl(sleeContainer.getUuidGenerator().createUUID());
if (logger.isDebugEnabled()) {
logger.debug("setTimer: timerID = "+timerID+" , startTime = " + startTime + " period = "
+ period + " numRepetitions = " + numRepetitions
+ " timeroptions =" + timerOptions);
}
// Attach to activity context
org.mobicents.slee.container.activity.ActivityContextInterface aciImpl = (org.mobicents.slee.container.activity.ActivityContextInterface) aci;
aciImpl.getActivityContext().attachTimer(timerID);
// schedule timer task
TimerFacilityTimerTaskData taskData = new TimerFacilityTimerTaskData(timerID, aciImpl.getActivityContext().getActivityContextHandle(), address, startTime, period, numRepetitions, timerOptions);
final TimerFacilityTimerTask task = new TimerFacilityTimerTask(taskData);
if(configuration.getTaskExecutionWaitsForTxCommitConfirmation()) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
task.setCountDownLatch(countDownLatch);
TransactionalAction action = new TransactionalAction() {
@Override
public void execute() {
countDownLatch.countDown();
}
};
TransactionContext txContext = txMgr.getTransactionContext();
txContext.getAfterCommitActions().add(action);
txContext.getAfterRollbackActions().add(action);
}
scheduler.schedule(task);
// If we started a tx for this operation, we commit it now
if (startedTx) {
try {
txMgr.commit();
} catch (Exception e) {
throw new TransactionRolledbackLocalException(
"Failed to commit transaction");
}
}
return timerID;
}
/*
* (non-Javadoc)
* @see javax.slee.facilities.TimerFacility#cancelTimer(javax.slee.facilities.TimerID)
*/
public void cancelTimer(TimerID timerID) throws NullPointerException,
TransactionRolledbackLocalException, FacilityException {
if (logger.isDebugEnabled()) {
logger.debug("cancelTimer: timerID = "+timerID);
}
if (timerID == null)
throw new NullPointerException("Null TimerID");
SleeTransactionManager txMgr = sleeContainer.getTransactionManager();
boolean terminateTx = txMgr.requireTransaction();
boolean doRollback = true;
try {
cancelTimer(timerID,true);
doRollback = false;
} finally {
try {
txMgr.requireTransactionEnd(terminateTx, doRollback);
} catch (Throwable e) {
throw new TransactionRolledbackLocalException(e.getMessage(),e);
}
}
}
public void cancelTimer(TimerID timerID, boolean detachAC) {
// cancel task in scheduler
final TimerFacilityTimerTask task = (TimerFacilityTimerTask) scheduler.cancel(timerID);
if (detachAC && task != null) {
// detach this timer from the ac
ActivityContext ac = sleeContainer.getActivityContextFactory()
.getActivityContext(task.getTimerFacilityTimerTaskData().getActivityContextHandle());
if (ac != null) {
ac.detachTimer(timerID);
}
}
}
/*
* (non-Javadoc)
* @see javax.slee.facilities.TimerFacility#getResolution()
*/
public long getResolution() throws FacilityException {
return this.timerResolution;
}
/*
* (non-Javadoc)
* @see javax.slee.facilities.TimerFacility#getDefaultTimeout()
*/
public long getDefaultTimeout() throws FacilityException {
return DEFAULT_TIMEOUT;
}
/*
* (non-Javadoc)
* @see javax.slee.facilities.TimerFacility#getActivityContextInterface(javax.slee.facilities.TimerID)
*/
public ActivityContextInterface getActivityContextInterface(TimerID timerID)
throws NullPointerException, TransactionRequiredLocalException,
FacilityException {
if (timerID == null) {
throw new NullPointerException("null timerID");
}
sleeContainer.getTransactionManager().mandateTransaction();
TimerFacilityTimerTaskData taskData = (TimerFacilityTimerTaskData) scheduler.getTimerTaskData(timerID);
if (taskData != null) {
try {
return sleeContainer.getActivityContextFactory().getActivityContext(taskData.getActivityContextHandle()).getActivityContextInterface();
} catch (Exception e) {
throw new FacilityException(e.getMessage(),e);
}
}
else {
return null;
}
}
@Override
public String toString() {
return "Timer Facility: " +
"\n+-- " + scheduler.toDetailedString();
}
}