/* * 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: StrictInterposition.java 2342 2006-03-30 13:06:17Z $ */ package com.arjuna.ats.internal.jts.interposition.resources.strict; import org.omg.CORBA.SystemException; import org.omg.CORBA.TRANSACTION_ROLLEDBACK; 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.interposition.resources.arjuna.Interposition; import com.arjuna.ats.internal.jts.interposition.resources.arjuna.ServerResource; 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.ServerTopLevelAction; import com.arjuna.ats.internal.jts.orbspecific.interposition.resources.strict.ServerStrictNestedAction; import com.arjuna.ats.internal.jts.orbspecific.interposition.resources.strict.ServerStrictTopLevelAction; import com.arjuna.ats.jts.logging.jtsLogger; import com.arjuna.ats.jts.utils.Utility; /* * In this implementation, rather than create the entire hierarchy * we simply create a top-level transaction (if required) and a single * nested transaction for the "current" transaction received in the * propagation context. * * This is fine since there is nothing within the strict OTS interface * which allows a transaction to return its notion of its parent. */ public class StrictInterposition extends Interposition { public static ControlImple create (PropagationContext context) throws SystemException { if (__list != null) return __list.setupHierarchy(context); else return null; } protected StrictInterposition () { } protected synchronized ControlImple createHierarchy (PropagationContext ctx, Uid currentUid) 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. */ /* * We only ever register the current transaction with its parent, and * as each transaction commits, it registers its parent with the * "real" parent. */ 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 my correctory be null return null; ServerControl control = ServerFactory.create_transaction(currentUid, null, null, tmpCoord, tmpTerm, ctx.timeout); action = new ServerStrictTopLevelAction(control, ((depth == 0) ? true : false)); if (!action.valid()) { try { ((ServerStrictTopLevelAction) action).rollback(); // does dispose as well! action = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } ServerTopLevelAction newElement = (ServerStrictTopLevelAction)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 ServerStrictNestedAction(control, false); // not current, so don't register 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 { ((ServerStrictNestedAction) nestedAction).rollback_subtransaction(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } /* * Add transaction resource to list. */ action.addChild((ServerStrictNestedAction) 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 ServerStrictNestedAction(control, true); // current, so register 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 { ((ServerStrictNestedAction) nestedAction).rollback_subtransaction(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } action.addChild((ServerStrictNestedAction) nestedAction); } if (jtsLogger.logger.isTraceEnabled()) compareHierarchies(ctx, newElement); /* * Always return reference to 'current' transaction. */ 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. */ /* * Also we would like to just register one resource to represent the entire * hierarchy, but this has problems: since threads can invoke operations at * any point in a hierarchy, we end up with multiple resources at top-level * for the same transaction. Each will try to commit the top-level transaction! * In this implementation, the first to do so will garbage collect the root * of the hierarchy, and probably cause the subsequent ones to fail! There is * also the problem with many cross-process calls for the *same* transaction. * So, we register *one* resource for each level of the hierarchy *when it is * required*, i.e., as the previous transaction terminates, and remove * terminated transaction resources when they occur. This means that at * top-level we only have a single resource to commit. There are the same * number of cross-process invocations. However, we also maintain the entire * hierarchy at the server, so if it makes subsequent invocations, the right * hierarchy gets sent out! */ protected synchronized ControlImple checkHierarchy (ServerTopLevelAction hier, PropagationContext context) { 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. However, make sure it has registered * itself with the "real" transaction. */ ServerStrictTopLevelAction tx = (ServerStrictTopLevelAction) hier; tx.interposeResource(); control = tx.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 = (int) 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 = null; Terminator tmpTerm = null; 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 ServerStrictNestedAction(control, false); 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 { ((ServerStrictNestedAction) nestedAction).rollback(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } currentAction.addChild((ServerStrictNestedAction) 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 ServerStrictNestedAction(control, true); 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 { ((ServerStrictNestedAction) nestedAction).rollback(); // does dispose as well! nestedAction = null; } catch (Exception e) { } throw new TRANSACTION_ROLLEDBACK(); } currentAction.addChild((ServerStrictNestedAction) nestedAction); } else { /* * Same current, so get its control and return it. * Remember to make sure it has registered itself with * the "real" transaction. */ nestedAction.interposeResource(); control = nestedAction.control(); } } if (jtsLogger.logger.isTraceEnabled()) compareHierarchies(context, hier); return control; } private static StrictInterposition __list = new StrictInterposition(); }