/* * 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, 2001, * * Hewlett Packard Arjuna Labs, * Newcastle upon Tyne, * Tyne and Wear, * UK. * * $Id: ServerTopLevelAction.java 2342 2006-03-30 13:06:17Z $ */ package com.arjuna.ats.internal.jts.orbspecific.interposition.resources.arjuna; import org.omg.CORBA.BAD_OPERATION; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.INVALID_TRANSACTION; import org.omg.CORBA.SystemException; import org.omg.CORBA.TRANSACTION_ROLLEDBACK; import org.omg.CORBA.UNKNOWN; import org.omg.CosTransactions.Coordinator; import org.omg.CosTransactions.HeuristicCommit; import org.omg.CosTransactions.HeuristicHazard; import org.omg.CosTransactions.HeuristicMixed; import org.omg.CosTransactions.HeuristicRollback; import org.omg.CosTransactions.Inactive; import org.omg.CosTransactions.NotPrepared; import org.omg.CosTransactions.RecoveryCoordinator; import org.omg.CosTransactions.Resource; import org.omg.CosTransactions.Vote; import com.arjuna.ats.arjuna.coordinator.ActionStatus; import com.arjuna.ats.arjuna.coordinator.TwoPhaseOutcome; import com.arjuna.ats.arjuna.coordinator.TxControl; import com.arjuna.ats.internal.arjuna.thread.ThreadActionData; import com.arjuna.ats.internal.jts.ORBManager; 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.interposition.ServerControl; import com.arjuna.ats.internal.jts.orbspecific.interposition.coordinator.ServerTransaction; import com.arjuna.ats.jts.exceptions.ExceptionCodes; import com.arjuna.ats.jts.logging.jtsLogger; /** * This looks like an atomic action, but is not actually derived from * BasicAction or Transaction. This is because of the way in which the * OTS creates and manipulates transactions. * * As with Transaction, we only create actions here, and do not associated * these contexts with any thread. We do the association later. * * If we were to do the creation via a suitably modified current interface * then the thread association would be done for us automatically, and we * would not have to call resume at all. * * This is a top-level action proxy. */ /* * NOTE: all calls to pushAction in this and related classes are to do * with AIT abstract records. Some of them rely on being able to call * BasicAction.Current to determine what the current transaction is and * if we do not do thread-to-transaction association here then this all * breaks. There is no other reason for this. So, what we need to do * is remove this dependency by fixing the AIT records. This dependency * has a knock-on effect on certain optimisations we can make (e.g., * local interpositon - see ServerControl.) */ public class ServerTopLevelAction extends ServerResource implements org.omg.CosTransactions.ResourceOperations { protected boolean _registered; public ServerTopLevelAction (ServerControl control) { super(control); if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::ServerTopLevelAction ( " + _theUid + " )"); } _theResource = null; _resourceRef = getReference(); /* * Now attempt to do interposition registration. */ if (_resourceRef != null) { /* * Would like to be able to attach a thread filter * to this object if process-filters aren't supported. * However, currently this won't work as we can't have * two different filter types working at the same * time. * * ATTACH_THREAD_FILTER_(_theResource); */ Coordinator realCoordinator = _theControl.originalCoordinator(); if (!(_valid = registerResource(realCoordinator))) { /* * Failed to register. Valid is set, and the interposition * controller will now deal with this. */ } realCoordinator = null; } else _valid = false; _registered = false; } public Resource getReference () { if ((_resourceRef == null) && _valid) { if (_theControl != null) { _theResource = new org.omg.CosTransactions.ResourcePOATie(this); ORBManager.getPOA().objectIsReady(_theResource); _resourceRef = org.omg.CosTransactions.ResourceHelper.narrow(ORBManager.getPOA().corbaReference(_theResource)); } else _valid = false; } return _resourceRef; } /* * Will only be called by the remote top-level transaction. */ public org.omg.CosTransactions.Vote prepare () throws HeuristicMixed, HeuristicHazard, SystemException { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::prepare for " + _theUid); } if (_theControl == null) { throw new INVALID_TRANSACTION(ExceptionCodes.SERVERAA_NO_CONTROL, CompletionStatus.COMPLETED_NO); } if (_theControl.isWrapper()) { destroyResource(); // won't necessarily get another invocation! return Vote.VoteReadOnly; } ServerTransaction theTransaction = (ServerTransaction) _theControl.getImplHandle(); //ThreadActionData.pushAction(theTransaction); // LockManager needs to know if there is a transaction int result = TwoPhaseOutcome.PREPARE_NOTOK; /* * Transaction may have locally timed out and been rolled back. */ int s = theTransaction.status(); if ((s == ActionStatus.RUNNING) || (s == ActionStatus.ABORT_ONLY)) result = theTransaction.doPrepare(); else { switch (s) { case ActionStatus.COMMITTING: case ActionStatus.COMMITTED: case ActionStatus.H_COMMIT: result = TwoPhaseOutcome.PREPARE_OK; break; case ActionStatus.H_MIXED: result = TwoPhaseOutcome.HEURISTIC_MIXED; break; case ActionStatus.H_HAZARD: result = TwoPhaseOutcome.HEURISTIC_HAZARD; break; } } ThreadActionData.popAction(); if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::prepare for " + _theUid + " : " + TwoPhaseOutcome.stringForm(result)); } /* * If prepare failed, then rollback now. */ if (result == TwoPhaseOutcome.PREPARE_NOTOK) { try { rollback(); } catch (HeuristicCommit ex1) { result = TwoPhaseOutcome.HEURISTIC_COMMIT; } catch (HeuristicMixed ex2) { result = TwoPhaseOutcome.HEURISTIC_MIXED; } catch (HeuristicHazard ex3) { result = TwoPhaseOutcome.HEURISTIC_HAZARD; } catch (SystemException ex4) { result = TwoPhaseOutcome.HEURISTIC_HAZARD; } } switch (result) { case TwoPhaseOutcome.INVALID_TRANSACTION: throw new INVALID_TRANSACTION(ExceptionCodes.INVALID_ACTION, CompletionStatus.COMPLETED_NO); case TwoPhaseOutcome.PREPARE_OK: return Vote.VoteCommit; case TwoPhaseOutcome.PREPARE_NOTOK: destroyResource(); // won't necessarily get another invocation! return Vote.VoteRollback; case TwoPhaseOutcome.PREPARE_READONLY: destroyResource(); // won't necessarily get another invocation! // what is we subsequently rollback? return Vote.VoteReadOnly; case TwoPhaseOutcome.HEURISTIC_MIXED: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicMixed(); // will eventually get forget case TwoPhaseOutcome.HEURISTIC_HAZARD: default: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicHazard(); } } public void rollback () throws HeuristicCommit, HeuristicMixed, HeuristicHazard, SystemException { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::rollback for " + _theUid); } if (_theControl == null) { throw new INVALID_TRANSACTION(ExceptionCodes.SERVERAA_NO_CONTROL, CompletionStatus.COMPLETED_NO); } if (_theControl.isWrapper()) { destroyResource(); return; } ServerTransaction theTransaction = (ServerTransaction) _theControl.getImplHandle(); ThreadActionData.pushAction(theTransaction); // LockManager needs to know if there is a transaction int actionStatus = theTransaction.status(); if (actionStatus == ActionStatus.PREPARED) { /* * This will also call any after_completions on * registered synchronizations. */ actionStatus = theTransaction.doPhase2Abort(); } else { if ((actionStatus == ActionStatus.RUNNING) || (actionStatus == ActionStatus.ABORT_ONLY)) { try { /* * Have to do this because of the way PI works * with thread-context association. */ if (!valid()) theTransaction.doPhase2Abort(); // must rollback else theTransaction.rollback(); actionStatus = ActionStatus.ABORTED; } catch (SystemException ex) { actionStatus = ActionStatus.ABORTED; throw ex; } finally { destroyResource(); } } } ThreadActionData.popAction(); if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::rollback for " + _theUid + " : " + ActionStatus.stringForm(actionStatus)); } switch (actionStatus) { case ActionStatus.PREPARED: throw new INVALID_TRANSACTION(ExceptionCodes.INVALID_ACTION, CompletionStatus.COMPLETED_NO); case ActionStatus.ABORTED: case ActionStatus.H_ROLLBACK: destroyResource(); break; case ActionStatus.COMMITTED: case ActionStatus.H_COMMIT: destroyResource(); throw new HeuristicCommit(); case ActionStatus.H_MIXED: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicMixed(); case ActionStatus.H_HAZARD: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicHazard(); default: destroyResource(); break; } } public void commit () throws NotPrepared, HeuristicRollback, HeuristicMixed, HeuristicHazard, SystemException { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::commit for " + _theUid); } if (_theControl == null) { throw new INVALID_TRANSACTION(ExceptionCodes.SERVERAA_NO_CONTROL, CompletionStatus.COMPLETED_NO); } if (_theControl.isWrapper()) { destroyResource(); return; } ServerTransaction theTransaction = (ServerTransaction) _theControl.getImplHandle(); ThreadActionData.pushAction(theTransaction); // LockManager needs to know if there is a transaction int actionStatus = theTransaction.status(); boolean notPrepared = false; if (actionStatus == ActionStatus.PREPARED) { /* * This will also call any after_completions on * registered synchronizations. */ actionStatus = theTransaction.doPhase2Commit(); } else { if (actionStatus == ActionStatus.RUNNING) { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::commit for " + _theUid + " : NotPrepared"); } notPrepared = true; } } ThreadActionData.popAction(); if (notPrepared) throw new NotPrepared(); if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::commit for " + _theUid + " : " + ActionStatus.stringForm(actionStatus)); } switch (actionStatus) { case ActionStatus.PREPARED: throw new INVALID_TRANSACTION(ExceptionCodes.SERVERAA_NO_CONTROL, CompletionStatus.COMPLETED_NO); case ActionStatus.COMMITTED: case ActionStatus.H_COMMIT: destroyResource(); break; case ActionStatus.ABORTED: case ActionStatus.H_ROLLBACK: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicRollback(); case ActionStatus.H_MIXED: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicMixed(); case ActionStatus.H_HAZARD: if (TxControl.getMaintainHeuristics()) destroyResource(); throw new HeuristicHazard(); default: destroyResource(); break; } } public void forget () throws SystemException { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::forget for " + _theUid); } boolean forgot = true; if (_theControl != null) forgot = _theControl.forgetHeuristics(); destroyResource(); // causes the removal of the transaction state. if (!forgot) throw new BAD_OPERATION(); } public void commit_one_phase () throws HeuristicHazard, SystemException { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::commit_one_phase for " + _theUid); } if (_theControl == null) { throw new INVALID_TRANSACTION(ExceptionCodes.SERVERAA_NO_CONTROL, CompletionStatus.COMPLETED_NO); } if (_theControl.isWrapper()) { destroyResource(); return; } ServerTransaction theTransaction = (ServerTransaction) _theControl.getImplHandle(); if (theTransaction == null) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_notx("ServerTopLevelAction.commit_one_phase"); throw new INVALID_TRANSACTION(ExceptionCodes.NO_TRANSACTION, CompletionStatus.COMPLETED_NO); } ThreadActionData.pushAction(theTransaction); // LockManager needs to know if there is a transaction try { /* * This will commit and do any before/after_completion calls * on registered synchronizations. */ theTransaction.doCommit(true); } catch (HeuristicHazard e1) { /* * Is a heuristic, then don't remove the * transaction information. */ ThreadActionData.popAction(); throw e1; } catch (TRANSACTION_ROLLEDBACK e4) { ThreadActionData.popAction(); throw e4; } catch (INVALID_TRANSACTION e5) { ThreadActionData.popAction(); throw e5; } catch (SystemException e6) { ThreadActionData.popAction(); throw e6; } catch (Exception e7) { ThreadActionData.popAction(); throw new UNKNOWN(e7.toString()); } finally { destroyResource(); } ThreadActionData.popAction(); destroyResource(); } public String type () { return "/Resources/Arjuna/ServerTopLevelAction"; } protected ServerTopLevelAction () { if (jtsLogger.logger.isTraceEnabled()) { jtsLogger.logger.trace("ServerTopLevelAction::ServerTopLevelAction ()"); } _theResource = null; _resourceRef = null; } protected synchronized void destroyResource () { if (!_destroyed) { try { if (Interposition.destroy(get_uid())) _destroyed = true; } catch (Exception e) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.destroyResource", e); } try { if (_theResource != null) { ORBManager.getPOA().shutdownObject(_theResource); _theResource = null; } } catch (Exception e) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.destroyResource", e); } } tidyup(); } protected boolean registerResource (Coordinator theCoordinator) { boolean result = false; if (theCoordinator != null) { if (_registered) return true; try { /* * Register resource and pass RecoveryCoordinator reference * to the interposed transaction to save and restore. */ RecoveryCoordinator recoveryCoord = theCoordinator.register_resource(_resourceRef); _registered = true; if (!_theControl.isWrapper()) { ServerTransaction tx = (ServerTransaction) _theControl.getImplHandle(); if (tx != null) { tx.setRecoveryCoordinator(recoveryCoord); result = true; } else { result = false; jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_ipfailed("ServerTopLevelAction"); } } else result = true; } catch (ClassCastException classCastException) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.registerResource", classCastException); } catch (Inactive ine) { jtsLogger.i18NLogger.warn_server_top_level_action_inactive(); jtsLogger.i18NLogger.debug_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.registerResource", ine); transactionInactive = true; } catch (TRANSACTION_ROLLEDBACK ex1) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.registerResource", ex1); } catch (INVALID_TRANSACTION ex2) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.registerResource", ex2); } catch (Exception e) { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_generror("ServerTopLevelAction.registerResource", e); } } else { jtsLogger.i18NLogger.warn_orbspecific_interposition_resources_arjuna_nocoord("ServerTopLevelAction.registerResource"); } return result; } public boolean isTransactionInactive() { return transactionInactive; } protected org.omg.CosTransactions.ResourcePOATie _theResource; protected Resource _resourceRef; private boolean transactionInactive; }