/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. * See the copyright.txt in the distribution for a full listing * of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * (C) 2005-2006, * @author JBoss Inc. */ /* * Copyright (C) 1998, 1999, 2000, * * Arjuna Solutions Limited, * Newcastle upon Tyne, * Tyne and Wear, * UK. * * $Id: Interposition.java 2342 2006-03-30 13:06:17Z $ */ package com.arjuna.ats.internal.jts.interposition.resources.arjuna; import java.util.LinkedList; import java.util.List; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.SystemException; import org.omg.CORBA.TRANSACTION_ROLLEDBACK; import org.omg.CORBA.TRANSACTION_UNAVAILABLE; import org.omg.CosTransactions.Coordinator; import org.omg.CosTransactions.PropagationContext; import org.omg.CosTransactions.Terminator; import org.omg.CosTransactions.TransIdentity; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.internal.jts.interposition.ServerFactory; import com.arjuna.ats.internal.jts.orbspecific.ControlImple; import com.arjuna.ats.internal.jts.orbspecific.interposition.ServerControl; import com.arjuna.ats.internal.jts.orbspecific.interposition.resources.arjuna.ServerNestedAction; import com.arjuna.ats.internal.jts.orbspecific.interposition.resources.arjuna.ServerTopLevelAction; import com.arjuna.ats.jts.logging.jtsLogger; import com.arjuna.ats.jts.utils.Utility; public class Interposition { public Interposition () { _head = new LinkedList(); // not synchronized as the methods that access it all are synchronized. } public static ControlImple create (PropagationContext context) throws SystemException { if (__list != null) return __list.setupHierarchy(context); else return null; } public static boolean destroy (Uid act) { if (__list != null) return __list.removeHierarchy(act); else return false; } /* * Assume that all actions in the imported hierarchy are of the same * type, i.e., all JBoss transactions. * * Because of the way garbage collection works in the ORB we have to * run an explicit garbage collection phase for finished hierarchies. */ public synchronized ControlImple setupHierarchy (PropagationContext context) throws SystemException { ControlImple controlPtr = null; Uid theUid = null; ServerTopLevelAction proxyAction = null; if (context.parents.length == 0) theUid = Utility.otidToUid(context.current.otid); else theUid = Utility.otidToUid(context.parents[context.parents.length-1].otid); proxyAction = present(theUid); if (proxyAction == null) { /* * Create a new proxyAction element and return the * "current" transaction. */ controlPtr = createHierarchy(context, theUid); } else { /* * Check hierarchy of existing element. */ controlPtr = checkHierarchy(proxyAction, context); proxyAction = null; } return controlPtr; } protected final synchronized ServerTopLevelAction present (Uid actUid) { if(_head == null) { return null; } for(ServerTopLevelAction action : _head) { if(actUid.equals(action.get_uid())) { return action; } } return null; } protected synchronized ControlImple createHierarchy (PropagationContext ctx, Uid tlUid) throws SystemException { /* * Start at the parent and work our way down to "current". The current * transaction is not in the IDL sequence, but sent as separate field * of the propagation context. This tends to make the code more * complex than it would be if the entire hierarchy was represented in * one place. */ int depth = ctx.parents.length; ServerResource action = null; Coordinator tmpCoord = null; Terminator tmpTerm = null; /* * First deal with top-level transaction, which may be * the current transaction. */ if (depth == 0) { tmpCoord = ctx.current.coord; tmpTerm = ctx.current.term; } else { tmpCoord = ctx.parents[depth-1].coord; tmpTerm = ctx.parents[depth-1].term; } if (tmpCoord == null) // terminator may correctly be null { return null; } ServerControl control = ServerFactory.create_transaction(tlUid, null, null, tmpCoord, tmpTerm, ctx.timeout); action = new ServerTopLevelAction(control); if (!action.valid()) { try { ((ServerTopLevelAction) action).rollback(); // does dispose as well! } catch (Exception e) { } if (((ServerTopLevelAction)action).isTransactionInactive()) { throw new TRANSACTION_UNAVAILABLE(jtsLogger.i18NLogger.get_transaction_was_inactive(), 1, CompletionStatus.COMPLETED_NO); } else { throw new TRANSACTION_ROLLEDBACK(); } } ServerTopLevelAction newElement = (ServerTopLevelAction)action; _head.add(newElement); if (depth > 0) // current is a nested transaction { /* * Now deal with any nested transactions. * As we create, register with the original transactions. */ ServerResource nestedAction = null; for (int i = depth -2; i >= 0; i--) { tmpCoord = ctx.parents[i].coord; tmpTerm = ctx.parents[i].term; control = ServerFactory.create_subtransaction(Utility.otidToUid(ctx.parents[i].otid), tmpCoord, tmpTerm, control); nestedAction = new ServerNestedAction(control); if (!nestedAction.valid()) { /* * Just deal with current transaction. Others must have been * registered successfully, and will be deal with automatically * when the parent transaction terminates. */ try { ((ServerNestedAction) nestedAction).rollback_subtransaction(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } /* * Add transaction resource to list. */ action.addChild((ServerNestedAction) nestedAction); action = nestedAction; } /* * Now deal with current transaction. If there is * only one transaction we do nothing. */ tmpCoord = ctx.current.coord; tmpTerm = ctx.current.term; control = ServerFactory.create_subtransaction(Utility.otidToUid(ctx.current.otid), tmpCoord, tmpTerm, control); nestedAction = new ServerNestedAction(control); if (!nestedAction.valid()) { /* * Just deal with current transaction. Others must have been * registered successfully, and will be deal with automatically * when the parent transaction terminates. */ try { ((ServerNestedAction) nestedAction).rollback_subtransaction(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } action.addChild((ServerNestedAction) nestedAction); } if (jtsLogger.logger.isTraceEnabled()) compareHierarchies(ctx, newElement); return control; } /* * In a single threaded environment we could walk down the hierarchy, * aborting any actions which are no longer valid, and creating any new * ones. However, in a multi-threaded environment, a thread can make a * call from any point in the client's hierarchy, and multiple client * threads can invoke the same server object. So, in general we cannot do * this optimisation. We must maintain the entire tree until portions of * it have explicitly been termined. * * Once we find the point in the new hierarchy which deviates from our * current representation, we begin to assemble a new subtree in much the * same way as we did for creating a completely new hierarchy. */ protected synchronized ControlImple checkHierarchy (ServerTopLevelAction hier, PropagationContext context) throws SystemException { ServerControl control = null; ServerResource currentAction = hier; // top-level transaction int depth = context.parents.length; int differenceIndex = -1; // index of the new transactions in the hierarchy /* * Find the point at which our notion of the hierarchy deviates from * the one we have just received. * * To get here we have already checked the id of the parent * transaction, i.e., depth -1. * * Remember: the context hierarchy sequence *does not* include the * current transaction! */ if (depth == 0) { /* * There are no transactions in the context other than the current * transaction, which must therefore be top-level. We already have * the control to return. */ control = hier.control(); // top-level transaction's control } else { ServerResource nestedAction = null; /* * Start at -2 and work our way down the hierarchy. We * use -2 since the length gives us the *number* of elements, * which is 0 to n-1, and the n-1th element is the top-level * transaction, which we must deal with first! */ for (int i = depth -2; i >= 0; i--) // don't check depth-1 as it is current action! { nestedAction = currentAction.getChild(Utility.otidToUid(context.parents[i].otid)); if (nestedAction == null) // point of difference, so stop trawling hierarchy { differenceIndex = i; // remember for later so that we can add new actions. break; } else { /* * currentAction *always* points to the last known * good transaction in our hierarchy. */ currentAction = nestedAction; } } /* * Do we have anything left in the sent hierarchy (other than * current)? If so, add it now. */ if (differenceIndex != -1) { control = currentAction.control(); Coordinator tmpCoord; Terminator tmpTerm; for (int j = differenceIndex; j >= 0; j--) { tmpCoord = context.parents[j].coord; tmpTerm = context.parents[j].term; control = ServerFactory.create_subtransaction(Utility.otidToUid(context.parents[j].otid), tmpCoord, tmpTerm, control); nestedAction = new ServerNestedAction(control); if (!nestedAction.valid()) { /* * Just deal with current transaction. Others must have been * registered successfully, and will be deal with automatically * when the parent transaction terminates. */ try { ((ServerNestedAction) nestedAction).rollback(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } currentAction.addChild((ServerNestedAction) nestedAction); currentAction = nestedAction; } } else { /* * Hierarchies may be identical. * Remember to check! */ } Uid currentUid = Utility.otidToUid(context.current.otid); /* * currentAction points to the parent of the 'current' * transaction, i.e., the last element in the TransIdentity * structure. So, ask it if the sent hierarchy's child is * one of its children. */ nestedAction = currentAction.getChild(currentUid); if (nestedAction == null) { /* * Different notion of current in sent hierarchy. * So, add it to the hierarchy here. */ control = currentAction.control(); /* * Now deal with the current transaction. */ TransIdentity currentID = context.current; control = ServerFactory.create_subtransaction(currentUid, currentID.coord, currentID.term, control); nestedAction = new ServerNestedAction(control); if (!nestedAction.valid()) { /* * Just deal with current transaction. Others must have * been registered successfully, and will be deal with * automatically when the parent transaction terminates. */ try { ((ServerNestedAction) nestedAction).rollback(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } currentAction.addChild((ServerNestedAction) nestedAction); } else { /* * Same current, so get its control and return it. */ control = nestedAction.control(); } } if (jtsLogger.logger.isTraceEnabled()) compareHierarchies(context, hier); return control; } protected final synchronized boolean removeHierarchy (Uid theUid) { ServerTopLevelAction action = present(theUid); if (action != null) { _head.remove(action); return true; } else { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.i18NLogger.warn_interposition_resources_arjuna_ipfail("Interposition.removeHeirarchy", theUid); } } return false; } protected final void compareHierarchies (PropagationContext ctx, ServerTopLevelAction action) { int depth = ctx.parents.length; Uid[] ctxHierarchy = new Uid [depth+1]; boolean printHierarchies = false; for (int i = depth -1; i >= 0; i--) { ctxHierarchy[i+1] = new Uid(Utility.otidToUid(ctx.parents[i].otid)); } ctxHierarchy[0] = new Uid(Utility.otidToUid(ctx.current.otid)); boolean problem = false; if (action != null) { if (action.get_uid().notEquals(ctxHierarchy[depth])) { jtsLogger.i18NLogger.warn_interposition_resources_arjuna_iptl(action.get_uid(), ctxHierarchy[depth]); printHierarchies = true; } else { if (depth > 0) { ServerNestedAction child = action.getChild(ctxHierarchy[depth-1]); if (child != null) { int i = 0; for (i = depth -2; (i >= 0) && (child != null); i--) { child = child.getChild(ctxHierarchy[i]); if (child == null) { problem = true; break; } } if (i != -1) problem = true; } else problem = true; } if (problem) { jtsLogger.i18NLogger.warn_interposition_resources_arjuna_ipnt(); printHierarchies = true; } } } else { jtsLogger.i18NLogger.warn_interposition_resources_arjuna_ipnull(); printHierarchies = true; } if (!printHierarchies) printHierarchies = jtsLogger.logger.isTraceEnabled(); if (printHierarchies) { synchronized (jtsLogger.logger) { if (!problem) { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace(Utility.getHierarchy(ctx)); jtsLogger.logger.trace(hierarchytoString(action)); } } else { jtsLogger.i18NLogger.warn_interposition_resources_arjuna_problemhierarchy(Utility.getHierarchy(ctx)); jtsLogger.i18NLogger.warn_interposition_resources_arjuna_problemhierarchy(hierarchytoString(action)); } } } if (ctxHierarchy != null) { for (int i = 0; i < (int) depth; i++) { if (ctxHierarchy[i] != null) ctxHierarchy[i] = null; } ctxHierarchy = null; } } private final String hierarchytoString(ServerTopLevelAction action) { String hier = "InterposedHierarchy:"; if (action != null) { hier += action.get_uid(); List<ServerNestedAction> children = action.getChildren(); synchronized (children) { for(ServerNestedAction child : children) { hier += "\n"+child.get_uid(); hier += child.getChildren(2); } } } else hier += "EMPTY"; return hier; } protected List<ServerTopLevelAction> _head; private static Interposition __list = new Interposition(); }