All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean Maven / Gradle / Ivy

There is a newer version: 4.17.37.Final
Show newest version
package com.arjuna.ats.arjuna.tools.osb.mbean;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
import com.arjuna.ats.arjuna.coordinator.BasicAction;
import com.arjuna.ats.arjuna.coordinator.RecordList;
import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome;
import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.objectstore.StoreManager;
import com.arjuna.ats.arjuna.tools.osb.util.JMXServer;

/**
 * MBean implementation of an ObjectStore entry that represents an AtomicAction
 */
public class ActionBean extends OSEntryBean implements ActionBeanMBean {
    // Basic properties this enty
    private StateManagerWrapper sminfo;
    // collection of participants belonging to this BasicAction
    private Collection participants = new ArrayList();
    // wrapper around the real AtomicAction
    protected ActionBeanWrapperInterface ra;

    public ActionBean(UidWrapper w) {
        super(w);

        boolean isJTS = JMXServer.isJTS() && w.getType().endsWith("ArjunaTransactionImple");
        // Participants in a JTS transaction are represented by entries in the ObjectStore
        List recuids = null;

        if (isJTS) {
            try {
                Class cl = (Class) Class.forName(JMXServer.AJT_WRAPPER_TYPE);
                Constructor constructor = cl.getConstructor(ActionBean.class, UidWrapper.class);
                ra = constructor.newInstance(this, w);
                ra.activate();
            } catch (Exception e) { // ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException
                if (tsLogger.logger.isTraceEnabled())
                    tsLogger.logger.trace("Error constructing " + JMXServer.AJT_WRAPPER_TYPE + ": " + e);
                ra = createWrapper(w, true);
            }

            /*
                * for JTS actions the participants will have entries in the ObjectStore.
                * these entries will be associated with the current MBean (refer to
                * the method findParticipants below for details)
                */
            recuids = w.probe(JMXServer.AJT_RECORD_TYPE);
        } else {
            ra = createWrapper(w, true);  // com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeManager.manager()
        }

        sminfo = new StateManagerWrapper(StoreManager.getRecoveryStore(), getUid(), getType());

        for (ParticipantStatus lt : ParticipantStatus.values()) {
            findParticipants(recuids, ra.getRecords(lt), lt);
        }
    }

    protected ActionBeanWrapperInterface createWrapper(UidWrapper w, boolean activate) {
        GenericAtomicActionWrapper action = new GenericAtomicActionWrapper(w.getClassName(), w);

        if (activate)
            action.activate();

        return action;
    }


    public StringBuilder toString(String prefix, StringBuilder sb) {
        ra.toString(prefix, sb);
        prefix += '\t';
        sb.append('\n').append(prefix).append(sminfo.getCreationTime());
        sb.append('\n').append(prefix).append(sminfo.getAgeInSeconds());

        for (LogRecordWrapper p : participants) {
            p.toString(prefix, sb);
        }

        return sb;
    }

    /**
     * return the Uid for given AbstractRecord
     * @param rec the record whose Uid is required
     * @return  the Uid of the requested record
     */
    public Uid getUid(AbstractRecord rec) {
        return ra.getUid(rec);
    }

    /**
     * Remove this AtomicAction from the ObjectStore
     * @return a textual indication of whether the remove operation succeeded
     */
    public String remove() {
        try {
            if (!StoreManager.getRecoveryStore().remove_committed(getUid(), getType()))
                return "Attempt to remove transaction failed";
            else
                w.probe();

            return "Transaction successfully removed";
        } catch (ObjectStoreException e) {
            return "Unable to remove transaction: " + e.getMessage();
        }
    }

    /**
     * create MBean representations of the participants of this transaction
     * @param recuids some transaction participants are represented in the ObjectStore
     * - if this is the case then recuids contains a list of MBean wrappers representing them.
     * Otherwise this list will be empty.
     * @param list the records representing the participants
     * @param listType indicates the type of the records in list (PREPARED, PENDING, FAILED, READONLY, HEURISTIC)
     */
    private void findParticipants(List recuids, RecordList list, ParticipantStatus listType) {
        if (list != null) {
            for (AbstractRecord rec = list.peekFront(); rec != null; rec = list.peekNext(rec)) {
                LogRecordWrapper lw;
                int i = recuids == null ? -1 : recuids.indexOf(new UidWrapper(ra.getUid(rec)));

                if (i != -1) {
                    OSEntryBean p = recuids.get(i).getMBean();

                    if (p instanceof LogRecordWrapper) {
                        lw = (LogRecordWrapper) p;
                        lw.init(this, rec, listType);
                    } else {
                        if (tsLogger.logger.isTraceEnabled())
                            tsLogger.logger.trace("participant record is not a LogRecordWrapper");
                        lw = createParticipant(rec, listType, recuids.get(i));
                    }
                } else {
                    lw = createParticipant(rec, listType);
                }

                lw.activate();
                participants.add(lw);
            }
        }
    }

    /**
     * Extension point for other Bean implementations to provide an implementation bean for its participants.
     * For example @see com.arjuna.ats.internal.jta.tools.osb.mbean.jta.JTAActionBean
     * @param rec the record that should be represented by an MBean
     * @param listType the status of the record
     * @return the MBean implementation of the participant
     */
    protected LogRecordWrapper createParticipant(AbstractRecord rec, ParticipantStatus listType) {
        return new LogRecordWrapper(this, rec, listType);
    }

    protected LogRecordWrapper createParticipant(AbstractRecord rec, ParticipantStatus listType, UidWrapper wrapper) {
        return new LogRecordWrapper(this, rec, listType, wrapper);
    }
    /**
     * See if there is participant Bean corresponding to the given record
     * @param rec the record for the target participant
     * @return the bean corresponding to the requested record
     */
    public LogRecordWrapper getParticipant(AbstractRecord rec) {
        for (LogRecordWrapper w : participants)
            if (w.getRecord().equals(rec))
                return w;

        return null;
    }

    /**
     * register this bean (and its participants) with the MBeanServer
     */
    public void register() {
        super.register();

        for (LogRecordWrapper p : participants)
            JMXServer.getAgent().registerMBean(p.getName(), p);
    }

    /**
     * unregister this bean (and its participants) with the MBeanServer
     */
    public void unregister() {
        for (LogRecordWrapper p : participants)
            JMXServer.getAgent().unregisterMBean(p.getName());

        super.unregister();
    }

    public long getAgeInSeconds() {
        return sminfo.getAgeInSeconds();
    }

    public String getCreationTime() {
        return sminfo.getCreationTime();
    }

    public boolean isParticipant() {
        return false;
    }

    /**
     * Request a change in status of a participant. For example if a record has a
     * heuristic status then this method could be used to move it back into the
     * prepared state so that the recovery system can replay phase 2 of the
     * commitment protocol
     * @param logrec the record whose status is to be changed
     * @param newStatus the desired status
     * @return true if the status was changed
     */
    public boolean setStatus(LogRecordWrapper logrec, ParticipantStatus newStatus) {
        ParticipantStatus lt = logrec.getListType();
        AbstractRecord targRecord = logrec.getRecord();

        RecordList oldList = ra.getRecords(lt);
        RecordList newList = ra.getRecords(newStatus);

        // move the record from currList to targList
        if (oldList.remove(targRecord)) {

            if (newList.insert(targRecord)) {
                if (lt.equals(ParticipantStatus.HEURISTIC)) {
                    switch (newStatus) {
                        case FAILED:
                            ra.clearHeuristicDecision(TwoPhaseOutcome.FINISH_ERROR);
                            break;
                        case PENDING:
                            ra.clearHeuristicDecision(TwoPhaseOutcome.NOT_PREPARED);
                            break;
                        case PREPARED:
                            ra.clearHeuristicDecision(TwoPhaseOutcome.PREPARE_OK);
                            break;
                        case READONLY:
                            ra.clearHeuristicDecision(TwoPhaseOutcome.PREPARE_READONLY);
                            break;
                        default:
                            break;
                    }
                }

                ra.doUpdateState();

                return true;
            }
        }

        return false;
    }

    /**
     *
     * @return the MBeans corresponding to the participants within this action
     */
    public Collection getParticipants() {
        return Collections.unmodifiableCollection(participants);
    }

    /**
     * The ActionBean needs access to the participant lists maintained by an AtomicAction but these
     * lists are protected. Therefore define a simple extension class to get at these records:
     */
    public class GenericAtomicActionWrapper implements ActionBeanWrapperInterface {
        boolean activated;
        BasicAction action;
        Map recs;
        Method setHeuristicDecision;
        Method updateState = null;
        UidWrapper uidWrapper;

        public GenericAtomicActionWrapper(String classType, UidWrapper w) {
            uidWrapper = w;
            recs = new HashMap();

            if (classType == null)
                classType = "com.arjuna.ats.arjuna.AtomicAction";

            try {
                Class cls = Class.forName(classType);
                Class pTypes[] = new Class[1];
                pTypes[0] = Uid.class;
                Constructor ctor = cls.getConstructor(pTypes);
                Object args[] = new Object[1];
                args[0] = w.getUid();
                action = (BasicAction) ctor.newInstance(args);

                setHeuristicDecision = getMethod(action.getClass(), "setHeuristicDecision", int.class);
                updateState = getMethod(action.getClass(), "updateState");

                if (setHeuristicDecision != null)
                    setHeuristicDecision.setAccessible(true);

                if (updateState != null)
                    updateState.setAccessible(true);

            } catch (Exception e) {
                action = null;

                if (tsLogger.logger.isDebugEnabled())
                    tsLogger.logger.debug("unable to create log wrapper for type " + w.getType() + ": error: " + e.getMessage());
            }
        }

        public BasicAction getAction() {
            return action;
        }

        public boolean activate() {
            if (!activated && action != null) {
                activated = action.activate();
            }

            return activated;
        }

        public void doUpdateState() {
            if (updateState != null && action != null) {
                try {
                    updateState.invoke(action);
                } catch (IllegalAccessException e) {
                    if (tsLogger.logger.isDebugEnabled())
                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
                } catch (InvocationTargetException e) {
                    if (tsLogger.logger.isDebugEnabled())
                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
                }
            }
        }

        public Uid get_uid() {
            return action != null ? action.get_uid() : uidWrapper.getUid();
        }

        public Uid getUid(AbstractRecord rec) {
            return rec.order(); //get_uid();
        }

        public StringBuilder toString(String prefix, StringBuilder sb) {
            prefix += '\t';
            return sb.append('\n').append(prefix).append(get_uid());
        }

        public void clearHeuristicDecision(int newDecision) {
            RecordList rl = getRecords("heuristicList");

            if (setHeuristicDecision != null && rl != null && rl.size() == 0)
                try {
                    setHeuristicDecision.invoke(action, newDecision);
                } catch (IllegalAccessException e) {
                    if (tsLogger.logger.isDebugEnabled())
                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
                } catch (InvocationTargetException e) {
                    if (tsLogger.logger.isDebugEnabled())
                        tsLogger.logger.debug("failed to update heuristic for " + action.toString() + ": error: " + e.getMessage());
                }
        }

        private Field getField(Class cl, String fn) {
            try {
                return cl.getDeclaredField(fn);
            } catch (NoSuchFieldException e) {
                return getField(cl.getSuperclass(), fn);
            }
        }

        private Method getMethod(Class cl, String mn, Class... parameterTypes) {
            try {
                if (cl == null)
                    return null;
                return cl.getDeclaredMethod(mn, parameterTypes);
            } catch (NoSuchMethodException e) {
                return getMethod(cl.getSuperclass(), mn, parameterTypes);
            }
        }

        public RecordList getRecords(String ln) {
            if (action == null)
                return null;

            if (recs.containsKey(ln))
                return recs.get(ln);

            Field f = getField(action.getClass(), ln);
            f.setAccessible(true);
            try {
                RecordList rl = (RecordList) f.get(action);

                if (rl != null)
                    recs.put(ln, rl);

                return rl;
            } catch (IllegalAccessException e) {
                return null;
            }
        }

        public RecordList getRecords(ParticipantStatus type) {

            switch (type) {
                default:
                case PREPARED: return getRecords("preparedList");
                case FAILED: return getRecords("failedList");
                case HEURISTIC: return getRecords("heuristicList");
                case PENDING: return getRecords("pendingList");
                case READONLY: return getRecords("readonlyList");
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy