package org.mobicents.slee.container.profile;
import java.util.LinkedList;
import javax.slee.SLEEException;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.profile.UnrecognizedProfileTableNameException;
import javax.transaction.SystemException;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.runtime.facilities.MNotificationSource;
import org.mobicents.slee.runtime.transaction.SleeTransactionManager;
/**
*
* Start time:17:47:16 2009-03-25<br>
* Project: mobicents-jainslee-server-core<br>
* This is class that carefully records calls to profile classes. On calls
* within transaction this records profile name that has been called. In case of
* reentrant profile we look for:
* <ul>
* <li>loop call - if currently called profile is reentrant && call names
* contain this profile && last call name is not this profile</li>
* <li>calls from different thread</li> --- how should we do that ? lol
* </ul>
*
* @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
* @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
*
*/
public class ProfileCallRecorderTransactionData {
// FIXME: Shoudl we also look up threads ?
private final static SleeContainer sleeContainer = SleeContainer.lookupFromJndi();
private static Logger logger = Logger.getLogger(ProfileCallRecorderTransactionData.class);
private static final String TRANSACTION_CONTEXT_KEY = "pctd";
/**
* a linked list with the which contains string values representing profile
* table - profile pairs that has been called within transaction.
*/
private final LinkedList<String> invokedProfiles = new LinkedList<String>();
/**
* Stores profile table name. This is required for alarm factility, as
* source changes with call to different profile table+profile pair.
*/
private final LinkedList<String> invokedProfileTablesNames = new LinkedList<String>();
/**
* Adds call to this profile.
*
* @param po
* @throws SLEEException
*/
public static void addProfileCall(ProfileObject po) throws SLEEException
{
SleeTransactionManager sleeTransactionManager = sleeContainer.getTransactionManager();
try {
if(sleeTransactionManager.getTransaction() == null) {
return;
}
}
catch ( SystemException se ) {
throw new SLEEException("Unable to verify SLEE Transaction.", se);
}
String key = makeKey(po);
if (logger.isDebugEnabled()) {
logger.debug("Recording call to profile. Key[" + key + "]");
}
try
{
ProfileCallRecorderTransactionData data = (ProfileCallRecorderTransactionData) sleeTransactionManager.getTransactionContext().getData().get(TRANSACTION_CONTEXT_KEY);
// If data does not exist, create it
if (data == null) {
data = new ProfileCallRecorderTransactionData();
sleeTransactionManager.getTransactionContext().getData().put(TRANSACTION_CONTEXT_KEY, data);
}
if (!po.isProfileReentrant())
{
// we need to check
if (data.invokedProfiles.contains(key) && data.invokedProfiles.getLast().compareTo(key) != 0) {
throw new SLEEException("Detected loopback call. Call sequence: " + data.invokedProfiles);
}
data.invokedProfiles.add(key);
data.invokedProfileTablesNames.add(po.getProfileTable().getProfileTableName());
}
}
catch (SystemException e) {
throw new SLEEException("Failed to verify reentrancy due to some system level errror.", e);
}
}
public static void removeProfileCall(ProfileObject po) throws SLEEException
{
SleeTransactionManager sleeTransactionManager = sleeContainer.getTransactionManager();
try {
if(sleeTransactionManager.getTransaction() == null) {
return;
}
}
catch ( SystemException se ) {
throw new SLEEException("Unable to verify SLEE Transaction.", se);
}
String key = makeKey(po);
if (logger.isDebugEnabled()) {
logger.debug("Removing call to profile. Key[" + key + "]");
}
try
{
ProfileCallRecorderTransactionData data = (ProfileCallRecorderTransactionData) sleeTransactionManager.getTransactionContext().getData().get(TRANSACTION_CONTEXT_KEY);
if (data == null) {
throw new SLEEException("No Profile call recorder in memory, this is a bug.");
}
if (!po.isProfileReentrant())
{
// we need to check
String lastKey = data.invokedProfiles.getLast();
if (lastKey.compareTo(key) != 0)
{
// logger.error("Last called profile does not match current: " + key + ", last call: " + lastKey + ". Please report this, it is a bug.");
throw new SLEEException("Last called profile does not match current: " + key + ", last call: " + lastKey);
}
else
{
data.invokedProfiles.removeLast();
data.invokedProfileTablesNames.removeLast();
if (data.invokedProfiles.isEmpty())
{
sleeTransactionManager.getTransactionContext().getData().remove(TRANSACTION_CONTEXT_KEY);
}
}
}
}
catch (SystemException e) {
throw new SLEEException("Failed to verify reentrancy due to some system level errror.", e);
}
}
public static MNotificationSource getCurrentNotificationSource() throws TransactionRequiredLocalException, SLEEException
{
if(logger.isDebugEnabled()) {
logger.debug("Trying to get Notification source for profile table.");
}
SleeTransactionManager sleeTransactionManaget = sleeContainer.getTransactionManager();
ProfileCallRecorderTransactionData data;
try
{
data = (ProfileCallRecorderTransactionData) sleeTransactionManaget.getTransactionContext().getData().get(TRANSACTION_CONTEXT_KEY);
if (data == null) {
throw new SLEEException("No Profile call recorder in memory, this is a bug.");
}
//IF data is present, there is something in it.
String tableName = data.invokedProfileTablesNames.getLast();
//FIXME: should we create new object? or lookup table? Lets do lookup
ProfileTableImpl profileTable = sleeContainer.getSleeProfileTableManager().getProfileTable(tableName);
return profileTable.getProfileTableNotification();
}
catch (SystemException e) {
throw new SLEEException("Failed to fetch notification source due to some system level error.", e);
}
catch (UnrecognizedProfileTableNameException e) {
throw new SLEEException("Failed to fetch notification source due to some system level error.", e);
}
}
private static String makeKey(ProfileObject pc)
{
return pc.getProfileTable().getProfileTableName() + "-" + (pc.getProfileEntity() == null ? "NO_PROFILE_ENTITY" : pc.getProfileEntity().getProfileName());
}
}