/*
* JBoss, Home of Professional Open Source.
* Copyright 2014, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.arjuna.ats.arjuna.tools.osb.mbean;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.management.MBeanException;
import com.arjuna.ats.arjuna.StateManager;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.common.arjPropertyManager;
import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.objectstore.ObjectStoreIterator;
import com.arjuna.ats.arjuna.objectstore.StoreManager;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.tools.osb.util.JMXServer;
/**
* An MBean implementation for walking an ObjectStore and creating/deleting MBeans
* that represent completing transactions (ie ones on which the user has called commit)
*
* @author Mike Musgrove
*/
/**
* @deprecated as of 5.0.5.Final In a subsequent release we will change packages names in order to
* provide a better separation between public and internal classes.
*/
@Deprecated // in order to provide a better separation between public and internal classes.
public class ObjStoreBrowser implements ObjStoreBrowserMBean {
private static final String SUBORDINATE_AA_TYPE =
"StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/SubordinateAtomicAction/JCA";
private static final String SUBORDINATE_ATI_TYPE =
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple/ServerTransaction/JCA";
private static OSBTypeHandler[] defaultOsbTypes = {
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.recovery.arjunacore.RecoverConnectableAtomicAction",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jta.RecoverConnectableAtomicActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/AtomicActionConnectable",
null
),
new OSBTypeHandler(
false,
"com.arjuna.ats.internal.jta.transaction.arjunacore.subordinate.jca.SubordinateAtomicAction",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jta.SubordinateActionBean",
SUBORDINATE_AA_TYPE,
null
),
new OSBTypeHandler(
true,
"com.arjuna.ats.arjuna.AtomicAction",
"com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction",
null
),
};
private static OSBTypeHandler[] defaultJTSOsbTypes = {
new OSBTypeHandler(
true,
false, // by default do not probe for this type
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSXAResourceRecordWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSXAResourceRecordWrapper",
"CosTransactions/XAResourceRecord",
null
),
new OSBTypeHandler(
false,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JCAServerTransactionWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
SUBORDINATE_ATI_TYPE,
"com.hp.mwtests.ts.jta.jts.tools.JCAServerTransactionHeaderReader"
),
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ArjunaTransactionImpleWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple",
null
),
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ServerTransactionWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple/ServerTransaction",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ServerTransactionHeaderReader"
),
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ServerTransactionWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple/AssumedCompleteServerTransaction",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ServerTransactionHeaderReader"
),
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.RecoveredTransactionWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple/AssumedCompleteHeuristicTransaction",
null
),
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ServerTransactionWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple/AssumedCompleteHeuristicServerTransaction",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.ServerTransactionHeaderReader"
),
new OSBTypeHandler(
true,
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.RecoveredTransactionWrapper",
"com.arjuna.ats.internal.jta.tools.osb.mbean.jts.JTSActionBean",
"StateManager/BasicAction/TwoPhaseCoordinator/ArjunaTransactionImple/AssumedCompleteTransaction",
null
)
};
private static Map<String, OSBTypeHandler> osbTypeMap = new HashMap<String, OSBTypeHandler>();
// A system property for defining extra bean types for instrumenting object store types
// The format is OSType1=BeanType1,OSType2=BeanType2,etc
public static final String OBJ_STORE_BROWSER_HANDLERS = "com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowserHandlers";
private static final String STORE_MBEAN_NAME = "jboss.jta:type=ObjectStore";
public static HeaderStateReader getHeaderStateUnpacker(String type) {
OSBTypeHandler osbType = osbTypeMap.get(type);
return (osbType != null) ? osbType.getHeaderStateReader() : null;
}
private Map<String, List<UidWrapper>> registeredMBeans = new HashMap<String, List<UidWrapper>> ();;
private boolean exposeAllLogs = false;
/**
* Initialise the MBean
*/
public void start()
{
JMXServer.getAgent().registerMBean(STORE_MBEAN_NAME, this);
}
/**
* Unregister all MBeans representing objects in the ObjectStore
* represented by this MBean
*/
public void stop()
{
unregisterMBeans();
JMXServer.getAgent().unregisterMBean(STORE_MBEAN_NAME);
}
private void unregisterMBeans(List<UidWrapper> beans) {
for (UidWrapper w : beans)
w.unregister();
beans.clear();
}
private void unregisterMBeans() {
for (List<UidWrapper> uids : registeredMBeans.values())
unregisterMBeans(uids);
registeredMBeans.clear();
}
private void registerMBeans() {
for (List<UidWrapper> uids : registeredMBeans.values()) {
for (UidWrapper w : uids)
w.register();
}
}
/**
* This method is deprecated in favour of @setType
* The issue with this method is there is no mechanism for determining which class
* is responsible for a given OS type.
*
* Define which object store types will registered as MBeans
* @param types the list of ObjectStore types that can be represented
* as MBeans
*/
@Deprecated
public void setTypes(Map<String, String> types) {
}
/**
* Tell the browser which beans to use for particular Object Store Action type
* @param osTypeClassName
* @param beanTypeClassName
* @return whether the type was set OK
*/
public boolean setType(String osTypeClassName, String beanTypeClassName) {
try {
Class cls = Class.forName(osTypeClassName);
StateManager sm = (StateManager) cls.getConstructor().newInstance();
String typeName = canonicalType(sm.type());
if (typeName == null || typeName.length() == 0)
return false;
osbTypeMap.put(typeName, new OSBTypeHandler(true, osTypeClassName, beanTypeClassName, typeName, null));
typeName = typeName.replaceAll("/", File.separator);
osbTypeMap.put(typeName, new OSBTypeHandler(true, osTypeClassName, beanTypeClassName, typeName, null));
return true;
} catch (Exception e) {
if (tsLogger.logger.isDebugEnabled())
tsLogger.logger.debug("Invalid class type in system property ObjStoreBrowserHandlers: " + osTypeClassName);
return false;
}
}
/**
* @param handler specification for handling object store types
* @return the previous value associated with type handler, or null if there was no previous handler.
*/
public OSBTypeHandler registerHandler(OSBTypeHandler handler) {
return osbTypeMap.put(handler.getTypeName(), handler);
}
private void initTypeHandlers(String handlers) {
for (String h : handlers.split(",")) {
String[] handler = h.split("=");
if (handler.length == 2) {
setType(handler[0], handler[1]);
}
}
}
private void init(String logDir) {
if (logDir != null)
arjPropertyManager.getObjectStoreEnvironmentBean().setObjectStoreDir(logDir);
if (tsLogger.logger.isTraceEnabled())
tsLogger.logger.trace("ObjectStoreDir: " + arjPropertyManager.getObjectStoreEnvironmentBean().getObjectStoreDir());
setExposeAllRecordsAsMBeans(arjPropertyManager.
getObjectStoreEnvironmentBean().getExposeAllLogRecordsAsMBeans());
for (OSBTypeHandler osbType : defaultOsbTypes)
osbTypeMap.put(osbType.getTypeName(), osbType);
for (OSBTypeHandler osbType : defaultJTSOsbTypes)
osbTypeMap.put(osbType.getTypeName(), osbType);
initTypeHandlers(System.getProperty(OBJ_STORE_BROWSER_HANDLERS, ""));
}
public ObjStoreBrowser() {
init(null);
}
public ObjStoreBrowser(String logDir) {
init(logDir);
}
/**
* Dump info about all registered MBeans
* @param sb a buffer to contain the result
* @return the passed in buffer
*/
public StringBuilder dump(StringBuilder sb) {
for (Map.Entry<String, List<UidWrapper>> typeEntry : registeredMBeans.entrySet()) {
sb.append(typeEntry.getKey()).append('\n');
for (UidWrapper uid : typeEntry.getValue())
uid.toString("\t", sb);
}
return sb;
}
/**
* See if the given uid has previously been registered as an MBean
* @param uid the unique id representing an ObjectStore entry
* @return the MBean wrapper corresponding to the requested Uid (or null
* if it hasn't been registered)
*/
public UidWrapper findUid(Uid uid) {
for (Map.Entry<String, List<UidWrapper>> typeEntry : registeredMBeans.entrySet())
for (UidWrapper w : typeEntry.getValue())
if (w.getUid().equals(uid))
return w;
return null;
}
/**
* Find the registered beand corresponding to a uid.
* @deprecated use {@link #findUid(com.arjuna.ats.arjuna.common.Uid)} ()} instead.
* @param uid the uid
* @return the registered bean or null if the Uid is not registered
*/
@Deprecated
public UidWrapper findUid(String uid) {
for (Map.Entry<String, List<UidWrapper>> typeEntry : registeredMBeans.entrySet())
for (UidWrapper w : typeEntry.getValue())
if (w.getUid().stringForm().equals(uid))
return w;
return null;
}
private boolean isRegistered(String type, Uid uid) {
List<UidWrapper> beans = registeredMBeans.get(type);
if (beans != null)
for (UidWrapper w : beans)
if (uid.equals(w.getUid()))
return true;
return false;
}
public void viewSubordinateAtomicActions(boolean enable) {
List<String> records = new ArrayList<>();
OSBTypeHandler subordinateAA = osbTypeMap.get(SUBORDINATE_AA_TYPE);
if (subordinateAA != null) {
subordinateAA.setEnabled(enable);
records.add(subordinateAA.getRecordClass());
}
OSBTypeHandler subordinateATI = osbTypeMap.get(SUBORDINATE_ATI_TYPE);
if (subordinateATI != null) {
subordinateATI.setEnabled(enable);
records.add(subordinateATI.getRecordClass());
}
if (!enable) {
for (List<UidWrapper> uids : registeredMBeans.values()) {
for (Iterator<UidWrapper> i = uids.iterator(); i.hasNext(); ) {
UidWrapper w = i.next();
if (records.contains(w.getClassName())) {
i.remove();
w.unregister();
}
}
}
}
}
public void setExposeAllRecordsAsMBeans(boolean exposeAllLogs) {
this.exposeAllLogs = exposeAllLogs;
}
/**
* Update registered MBeans based on the current set of Uids.
* @param allCurrUids any registered MBeans not in this collection will be deregistered
*/
private void unregisterRemovedUids(Map<String, Collection<Uid>> allCurrUids) {
for (Map.Entry<String, List<UidWrapper>> e : registeredMBeans.entrySet()) {
String type = e.getKey();
List<UidWrapper> registeredBeansOfType = e.getValue();
Collection<Uid> currUidsOfType = allCurrUids.get(type);
if (currUidsOfType != null) {
Iterator<UidWrapper> iterator = registeredBeansOfType.iterator();
while (iterator.hasNext()) {
UidWrapper w = iterator.next();
if (!currUidsOfType.contains(w.getUid())) {
w.unregister();
iterator.remove();
}
}
} else {
unregisterMBeans(registeredBeansOfType);
}
}
}
/**
* See if any new MBeans need to be registered or if any existing MBeans no longer exist
* as ObjectStore entries.
* @throws MBeanException
*/
public synchronized void probe() throws MBeanException {
Map<String, Collection<Uid>> currUidsForType = new HashMap<String, Collection<Uid>>();
for (String type : getTypes())
currUidsForType.put(type, getUids(type));
// if there are any beans in registeredMBeans that don't appear in new list and unregister them
unregisterRemovedUids(currUidsForType); //unregisterMBeans();
for (Map.Entry<String, Collection<Uid>> e : currUidsForType.entrySet()) {
String type = e.getKey();
List<UidWrapper> beans = registeredMBeans.get(type);
if (beans == null) {
beans = new ArrayList<UidWrapper>();
registeredMBeans.put(type, beans);
}
for (Uid uid : e.getValue()) {
if (!isRegistered(type, uid)) {
UidWrapper w = createBean(uid, type); // can return null if type isn't instrumented
if (w != null)
beans.add(w);
}
}
}
/*
* now create the actual MBeans - we create all the UidWrappers before registering because
* the process of creating a bean can call back into the browser to probe for a particular type
* (see for example com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean
*/
registerMBeans();
}
/**
* Register new MBeans of the requested type (or unregister ones whose
* corresponding ObjectStore entry has been removed)
* @param type the ObjectStore entry type
* @return the list of MBeans representing the requested ObjectStore type
*/
public List<UidWrapper> probe(String type) {
type = canonicalType(type);
return (type == null ? null : registeredMBeans.get(type));
}
private UidWrapper createBean(Uid uid, String type) {
OSBTypeHandler osbType = osbTypeMap.get(type);
//boolean enabled = osbType == null || osbType.isEnabled();
boolean registerBean = osbType != null && osbType.isAllowRegistration();
if (osbType == null && !exposeAllLogs)
return null;
if (exposeAllLogs)
registerBean = true;
// if (osbType != null && !osbType.enabled)
// return null;
String beanType = osbType == null ? OSEntryBean.class.getName() : osbType.getBeanClass();
String stateType = osbType == null ? null : osbType.getRecordClass();
UidWrapper w = new UidWrapper(this, beanType, type, stateType, uid, registerBean);
w.createMBean();
return w;
}
private Collection<Uid> getUids(String type) throws MBeanException {
Collection<Uid> uids = new ArrayList<Uid>();
try {
ObjectStoreIterator iter = new ObjectStoreIterator(StoreManager.getRecoveryStore(), type);
while (true) {
Uid u = iter.iterate();
if (u == null || Uid.nullUid().equals(u))
break;
uids.add(u);
}
} catch (ObjectStoreException | IOException e) {
throw new MBeanException(e);
}
return uids;
}
public static String canonicalType(String type) {
if (type == null)
return null;
type = type.replace(File.separator, "/");
while (type.startsWith("/"))
type = type.substring(1);
return type;
}
private Collection<String> getTypes() {
Collection<String> allTypes = new ArrayList<String>();
InputObjectState types = new InputObjectState();
try {
if (StoreManager.getRecoveryStore().allTypes(types)) {
while (true) {
try {
String typeName = canonicalType(types.unpackString());
if (typeName.length() == 0)
break;
allTypes.add(typeName);
} catch (IOException e1) {
break;
}
}
}
} catch (ObjectStoreException e) {
if (tsLogger.logger.isTraceEnabled())
tsLogger.logger.trace(e.toString()); }
return allTypes;
}
}