/* * 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; } }