package org.mobicents.slee.container.management.jmx;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.management.NotCompliantMBeanException;
import javax.slee.SbbID;
import javax.slee.facilities.TimerID;
import javax.slee.management.ManagementException;
import javax.slee.nullactivity.NullActivity;
import javax.transaction.SystemException;
import org.apache.log4j.Logger;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.management.jmx.editors.ComponentIDPropertyEditor;
import org.mobicents.slee.resource.ResourceAdaptorEntity;
import org.mobicents.slee.runtime.activity.ActivityContext;
import org.mobicents.slee.runtime.activity.ActivityContextFactoryImpl;
import org.mobicents.slee.runtime.activity.ActivityContextHandle;
import org.mobicents.slee.runtime.activity.ActivityType;
import org.mobicents.slee.runtime.facilities.ActivityContextNamingFacilityImpl;
import org.mobicents.slee.runtime.sbbentity.SbbEntity;
import org.mobicents.slee.runtime.sbbentity.SbbEntityFactory;
/**
* This class provides minimum information for management console. It also tries
* to clean container of dead activities
*
* @author Bartosz Baranowski
* @author Eduardo Martins
*
*/
@SuppressWarnings("unchecked")
public class ActivityManagementMBeanImpl extends MobicentsServiceMBeanSupport
implements ActivityManagementMBeanImplMBean {
// Name convention is to conform to other mbeans - also ServiceMBeanSupport
// is not StandadMBean which allows
// Passing MBean interface class which enables custom names this is why
// MBean interface has sufix - MBeanImplMbean
// 10 minutes interval between querries
private long querryInterval = 600 * 1000;
private long maxActivityIdleTime = 600 * 1000;
private final ActivityContextFactoryImpl acFactory;
private final org.mobicents.slee.runtime.transaction.SleeTransactionManager txMgr;
private TimerTask currentQuestioner = null;
private Timer queryRunner = new Timer("MOBICENTS_ACTIVITY_LIVELINESS_TIMER");
private static Logger logger = Logger
.getLogger(ActivityManagementMBeanImpl.class);
public ActivityManagementMBeanImpl(SleeContainer sleeContainer) throws NotCompliantMBeanException {
super(sleeContainer,ActivityManagementMBeanImpl.class);
this.acFactory = sleeContainer.getActivityContextFactory();
txMgr = sleeContainer.getTransactionManager();
}
// === PROPERTIES
public int getActivityContextCount() {
// prepareBean();
boolean newtx = txMgr.requireTransaction();
try {
return this.acFactory.getActivityContextCount();
} catch (Exception e) {
e.printStackTrace();
return -1;
}
finally {
if (newtx) {
try {
txMgr.rollback();
} catch (SystemException e) {
logger.error("Failed to rollback new tx for retreival of activity context count",e);
}
}
}
}
public long getQueryActivityContextLivelinessPeriod() {
return this.querryInterval;
}
public void setQueryActivityContextLivelinessPeriod(long set) {
long originalV = this.querryInterval;
if (set <= 100)
this.querryInterval = 15000; // 15 secs
else
this.querryInterval = set;
logger.info("Reinitiating liveliness query to run ["
+ !(querryInterval == 0) + "]");
if (currentQuestioner.cancel()) {
if (querryInterval == 0) {
return;
}
currentQuestioner = new PeriodicLivelinessScanner();
queryRunner.schedule(currentQuestioner, querryInterval);
} else if (originalV == 0) {
currentQuestioner = new PeriodicLivelinessScanner();
queryRunner.schedule(currentQuestioner, querryInterval);
}
logger.info("Reinitiated");
}
public void setActivityContextMaxIdleTime(long set) {
if (set <= 100 && set > 0)
this.maxActivityIdleTime = 15000;
else
this.maxActivityIdleTime = set;
}
public long getActivityContextMaxIdleTime() {
return this.maxActivityIdleTime;
}
// --- OPERATIONS
public void endActivity(ActivityContextHandle ach) throws ManagementException {
// Again this is tx method
logger.info("Trying to stop null activity[" + ach + "]!!");
// prepareBean();
boolean createdTx = false;
try {
createdTx = txMgr.requireTransaction();
ActivityContext ac = acFactory.getActivityContext(ach);
if (ac == null) {
logger.debug("There is no ac associated with given acID["
+ ach + "]!!");
throw new ManagementException("Can not find AC for given ID["
+ ach + "], try again!!!");
}
if (ac.getActivityContextHandle().getActivityType() == ActivityType.NULL) {
logger.debug("Scheduling activity end for acID[" + ach
+ "]");
NullActivity nullActivity = (NullActivity) ac.getActivityContextHandle().getActivity();
if (nullActivity != null) {
nullActivity.endActivity();
}
} else {
logger.debug("AC is not null activity context");
throw new IllegalArgumentException("Given ID[" + ach
+ "] does not point to NullActivity");
}
} catch (Throwable e) {
logger.error(e.getMessage(),e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(),e);
}
}
}
}
public void queryActivityContextLiveness() {
logger.info("Extorting liveliness query!!");
// prepareBean();
if (currentQuestioner.cancel()) {
currentQuestioner = new PeriodicLivelinessScanner();
currentQuestioner.run();
}
logger.info("Extortion complete");
// currentQuestioner=new PeriodicLivelinessScanner();
}
public String[] listActivityContextsFactories() {
logger.info("Listing AC factory");
boolean createdTx = false;
String[] ret = null;
try {
createdTx = txMgr.requireTransaction();
// Now we need to sort acs per factory type, raentity ?
Set factoriesSet = new HashSet();
Iterator<ActivityContextHandle> it = this.acFactory.getAllActivityContextsHandles()
.iterator();
logger.debug("Gathering information");
while (it.hasNext()) {
factoriesSet.add(it.next().getActivitySource());
}
logger.debug("Composing response");
// Now lets gather details
if (factoriesSet.size() == 0)
return null;
ret = new String[factoriesSet.size()];
ret = (String[]) factoriesSet.toArray(ret);
} catch (Throwable e) {
logger.error(e.getMessage(),e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(),e);
}
}
}
return ret;
}
public Object[] listActivityContexts(boolean inDetails) {
// prepareBean();
logger.info("Listing ACs with details[" + inDetails + "]");
boolean createdTx = false;
Object[] ret = null;
try {
createdTx = txMgr.requireTransaction();
ret = listWithCriteria(false, inDetails, LIST_BY_NO_CRITERIA, null);
} catch (Throwable e) {
logger.error(e.getMessage(),e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(),e);
}
}
}
return ret;
}
public Object[] retrieveActivityContextDetails(ActivityContextHandle ach)
throws ManagementException {
logger.info("Retrieving AC details for " + ach);
// prepareBean();
boolean createdTx = false;
Object[] o = null;
try {
createdTx = txMgr.requireTransaction();
ActivityContext ac = this.acFactory.getActivityContext(ach);
if (ac == null) {
logger.debug("Ac retrieval failed, no such ac[" + ach
+ "]!!!");
throw new ManagementException(
"Activity Context does not exist (ACID[" + ach
+ "]), try another one!!");
}
o = getDetails(ac);
} catch (Throwable e) {
logger.error(e.getMessage(),e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(),e);
}
}
}
return o;
}
/*
* This method should be run in tx
*/
/**
* This is main place where SLEE is accessed. This functions lists AC in
* various different ways. It can either return Object[] of arrays
* representing AC of simple Object[] that in fact contains String objects
* representing IDs of Activity Contexts
*
* @param listIDsOnly -
* tells to list only its, if this is true, second boolean flag
* is ignored. Return value is Object[]{String,String,....}
* @param inDetails -
* Tells whether sub-arrays containing name bindings, attached
* sbb entities should be passed, or only value representing
* their length. If true arrays are passed, if false, only
* values.
* @param criteria -
* Critteria to search by - if this is different than
* {@link ActivityManagementMBeanImplMBean.LIST_BY_NO_CRITERIA}
* next parameter has to have value.
* @param comparisonCriteria -
* this has to contain String representation of criteria ac
* should be looked by: SbbEID, RA Entity Name , SbbID or
* activity class name - like getClass().getName().
* @return
* <ul>
* <li>Object[]{Stirng, String}</li>
* <li>Object[] ={@link #retrieveActivityContextDetails()},{@link #retrieveActivityContextDetails()},....
* </li>
* </ul>
*/
private Object[] listWithCriteria(boolean listIDsOnly, boolean inDetails,
int criteria, String comparisonCriteria) {
logger.info("Listing with criteria[" + criteria + "] with details["
+ inDetails + "] only IDS[" + listIDsOnly + "]");
Iterator<ActivityContextHandle> it = this.acFactory.getAllActivityContextsHandles().iterator();
ArrayList lst = new ArrayList();
// Needed by LIST_BY_SBBID
HashMap sbbEntityIdToSbbID = new HashMap();
while (it.hasNext()) {
ActivityContextHandle ach = it.next();
ActivityContext ac = this.acFactory.getActivityContext(ach);
if (ac == null) {
continue;
}
Object activity = ach.getActivity();
if (activity != null) {
switch (criteria) {
case LIST_BY_ACTIVITY_CLASS:
if (!activity.getClass().getCanonicalName().equals(comparisonCriteria)) {
ac = null; // we dont want this one here
}
break;
case LIST_BY_RAENTITY:
if (ach.getActivityType() == ActivityType.RA) {
if (!ach.getActivitySource().equals(comparisonCriteria))
ac = null;
} else
ac = null;
break;
case LIST_BY_SBBENTITY:
// is this enough ?
if (comparisonCriteria.equals("")
|| !ac.getSbbAttachmentSet().contains(
comparisonCriteria)) {
ac = null;
}
break;
case LIST_BY_SBBID:
ComponentIDPropertyEditor propertyEditor = new ComponentIDPropertyEditor();
propertyEditor.setAsText(comparisonCriteria);
SbbID idBeingLookedUp = (SbbID) propertyEditor.getValue();
boolean match = false;
SbbID implSbbID = null;
for (Object obj : ac.getSbbAttachmentSet()) {
String sbbEID = (String) obj;
if (sbbEntityIdToSbbID.containsKey(sbbEID)) {
implSbbID = (SbbID) sbbEntityIdToSbbID.get(sbbEID);
} else {
SbbEntity sbbe = SbbEntityFactory.getSbbEntityWithoutLock(sbbEID);
implSbbID = sbbe.getSbbId();
sbbEntityIdToSbbID.put(sbbEID, implSbbID);
}
if (!implSbbID.equals(idBeingLookedUp)) {
match = false;
continue;
} else {
match = true;
break;
}
}
if (!match) {
ac = null;
}
break;
case LIST_BY_NO_CRITERIA:
break;
default:
continue;
}
if (ac == null)
continue;
// Now we have to check - if we want only IDS
Object singleResult = null;
if (!listIDsOnly) {
logger.debug("Adding AC[" + ach + "]");
Object[] o = getDetails(ac);
if (!inDetails) {
// This is stupid, but can save some bandwith, not sure if
// we
// should care. But Console is java script, and
// sometimes that can be pain, so lets ease it
o[SBB_ATTACHMENTS] = ((Object[]) o[SBB_ATTACHMENTS]).length
+ "";
o[NAMES_BOUND_TO] = ((Object[]) o[NAMES_BOUND_TO]).length
+ "";
o[TIMERS_ATTACHED] = ((Object[]) o[TIMERS_ATTACHED]).length
+ "";
o[DATA_PROPERTIES] = ((Object[]) o[DATA_PROPERTIES]).length
+ "";
}
singleResult = o;
} else {
singleResult = ach;
}
lst.add(singleResult);
}
}
if (lst.size() == 0)
return null;
logger.info("RETURN SIZE[" + lst.size() + "]");
Object[] ret = new Object[lst.size()];
ret = lst.toArray(ret);
return ret;
}
/*
* FIXME uncomment code when everything is commited
*
* This method should be run in tx
*/
private Object[] getDetails(ActivityContext ac) {
logger.debug("Retrieveing details for acID["
+ ac.getActivityContextHandle() + "]");
Object[] o = new Object[ARRAY_SIZE];
o[ActivityManagementMBeanImplMBean.AC_ID] = ac.getActivityContextHandle();
logger.debug("======[getDetails]["
+ o[ActivityManagementMBeanImplMBean.AC_ID] + "]["
+ ac.hashCode() + "]");
ActivityContextHandle ach = ac.getActivityContextHandle();
if (ach.getActivityType() == ActivityType.RA) {
o[RA] = ach.getActivitySource();
}
o[ACTIVITY_CLASS] = ach.getActivity().getClass().getName();
logger.debug("======[getDetails][ACTIVITY_CLASS][" + o[ACTIVITY_CLASS]
+ "]");
// Date d = new Date(ac.getLastAccessTime());
// o[LAST_ACCESS_TIME] = d;
o[LAST_ACCESS_TIME] = ac.getLastAccessTime() + "";
logger.debug("======[getDetails][LAST_ACCESS_TIME]["
+ o[LAST_ACCESS_TIME] + "]["
+ new Date(Long.parseLong((String) o[LAST_ACCESS_TIME])) + "]");
Set set = ac.getSbbAttachmentSet();
String[] tmp = new String[set.size()];
tmp = (String[]) set.toArray(tmp);
o[SBB_ATTACHMENTS] = tmp;
Set s = ac.getNamingBindingCopy();
tmp = new String[s.size()];
tmp = (String[]) s.toArray(tmp);
o[NAMES_BOUND_TO] = tmp;
s = ac.getAttachedTimersCopy();
tmp = new String[s.size()];
Iterator it = s.iterator();
int counter = 0;
while (it.hasNext()) {
tmp[counter++] = ((TimerID) it.next()).toString();
}
o[TIMERS_ATTACHED] = tmp;
Map m = ac.getDataAttributesCopy();
tmp = new String[m.size()];
it = m.keySet().iterator();
counter = 0;
while (it.hasNext()) {
Object k = it.next();
Object v = m.get(k);
tmp[counter++] = k + "=" + v;
}
o[DATA_PROPERTIES] = tmp;
return o;
}
// Should those methods throw something if sbbeid, classname or etity does
// not exist?
public Object[] retrieveActivityContextIDByActivityType(
String fullQualifiedActivityClassName) {
logger.info("Retrieving AC by activity class name["
+ fullQualifiedActivityClassName + "]");
// prepareBean();
boolean createdTx = false;
Object[] ret = null;
try {
createdTx = txMgr.requireTransaction();
ret = listWithCriteria(true, true, LIST_BY_ACTIVITY_CLASS,
fullQualifiedActivityClassName);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
return ret;
}
public Object[] retrieveActivityContextIDByResourceAdaptorEntityName(
String entityName) {
logger.info("Retrieving AC by entity name[" + entityName + "]");
// prepareBean();
boolean createdTx = false;
Object[] ret = null;
try {
createdTx = txMgr.requireTransaction();
ret = listWithCriteria(true, true, LIST_BY_RAENTITY, entityName);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
return ret;
}
public Object[] retrieveActivityContextIDBySbbEntityID(String sbbEID) {
logger.info("Retrieving ACs by sbb entity id [" + sbbEID + "]");
// prepareBean();
boolean createdTx = false;
Object[] ret = null;
try {
createdTx = txMgr.requireTransaction();
ret = listWithCriteria(true, true, LIST_BY_SBBENTITY, sbbEID);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
return ret;
}
public Object[] retrieveActivityContextIDBySbbID(String sbbID) {
logger.info("Retrieving ACs by sbb id [" + sbbID + "]");
// prepareBean();
boolean createdTx = false;
Object[] ret = null;
try {
createdTx = txMgr.requireTransaction();
ret = listWithCriteria(true, true, LIST_BY_SBBID, sbbID);
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
return ret;
}
public Map retrieveNamesToActivityContextIDMappings() {
ActivityContextNamingFacilityImpl naming = (ActivityContextNamingFacilityImpl) getSleeContainer()
.getActivityContextNamingFacility();
boolean createdTx = false;
Map ret = null;
try {
createdTx = txMgr.requireTransaction();
ret = naming.getBindings();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
} finally {
if (createdTx) {
try {
txMgr.commit();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
}
return ret;
}
// == Some helpers
private void prepareBean() {
if (currentQuestioner == null) {
this.currentQuestioner = new PeriodicLivelinessScanner();
this.queryRunner.schedule(this.currentQuestioner,
this.querryInterval);
}
}
// TimerClass to run periodic livelines querry - here is decided which ac
// are going to be querried, and possibly destroyed
// , depends on impl
/**
* FIXME uncomment code when everything is commited
*/
private class PeriodicLivelinessScanner extends TimerTask {
private Set<ActivityContextHandle> getActivityContextHandles() {
if(logger.isDebugEnabled())
logger
.debug("Periodic Liveliness Task is on the run, retrieveing all ACs");
try {
txMgr.begin();
return acFactory.getAllActivityContextsHandles();
}
catch (Exception e) {
logger.error("failed to retrieve all ACs to do periodic liveness scan", e);
return null;
}
finally {
try {
txMgr.rollback();
}
catch (Exception e) {
logger.error("failed to end tx to retrieve all ACs and do periodic liveness scan", e);
}
}
}
private void queryLiveness(ActivityContextHandle ach,long currentTime) {
if(logger.isDebugEnabled())
logger
.debug("Periodic Liveliness Task is on the run, processing AC "+ach);
try {
txMgr.begin();
ActivityContext ac = acFactory.getActivityContext(ach);
if (ac != null) {
if (ach.getActivityType() != ActivityType.RA)
return;
if ((currentTime - ac.getLastAccessTime()) < maxActivityIdleTime) {
// This one has been accessed in near past, so we dont
// want to query it
return;
}
final SleeContainer container = getSleeContainer();
ResourceAdaptorEntity raEntity = container.getResourceManagement()
.getResourceAdaptorEntity(
ach.getActivitySource());
if (raEntity.getResourceAdaptorObject().getActivity(ach.getActivityHandle()) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking ra entity "+raEntity.getName()+" queryLiveness() for activity handle "+ach.getActivityHandle());
}
raEntity.getResourceAdaptorObject().queryLiveness(
ach.getActivityHandle());
}
else {
logger.warn("Ending leaked activity with handle "+ach.getActivityHandle()+" of ra entity "+raEntity.getName()+" since the ra entity getActivity() returns null");
if (!ac.isEnding()) {
// end it
ac.endActivity();
}
else {
// force removal
container.getActivityContextFactory().removeActivityContext(ac);
}
}
}
}
catch (Exception e) {
logger.error("failed to query liveness on AC "+ach, e);
}
finally {
try {
txMgr.commit();
}
catch (Exception e) {
logger.error("failed to end tx to to query liveness on AC "+ach, e);
}
}
}
public void run() {
Set<ActivityContextHandle> achs = this.getActivityContextHandles();
if (achs != null) {
long currentTime = System.currentTimeMillis();
for (ActivityContextHandle ach : achs) {
this.queryLiveness(ach,currentTime);
}
}
if (querryInterval != 0) {
// Lets schedule again
currentQuestioner = new PeriodicLivelinessScanner();
queryRunner.schedule(currentQuestioner, querryInterval);
if(logger.isDebugEnabled())
logger.debug("Periodic Liveliness Task scheduled for next run");
}
}
}
// -------------------- JBOSS MBean LIFECYCLE METHODS
/**
*
* start MBean service lifecycle method
*
*/
public void startService() throws Exception {
if(logger.isDebugEnabled()) {
logger.debug("Starting Activity Manager MBean");
}
prepareBean();
logger.info("Activity Management MBean started");
}
/**
*
* stop MBean service lifecycle method
*
*/
protected void stopService() throws Exception {
this.queryRunner.cancel();
}
}