/* * 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: LockRecord.java 2342 2006-03-30 13:06:17Z $ */ package org.jboss.stm.internal.optimistic; import com.arjuna.ats.arjuna.ObjectStatus; import com.arjuna.ats.arjuna.ObjectType; import com.arjuna.ats.arjuna.state.*; import com.arjuna.ats.txoj.LockManager; import com.arjuna.ats.txoj.logging.txojLogger; import com.arjuna.ats.arjuna.common.Uid; import com.arjuna.ats.arjuna.coordinator.*; import com.arjuna.ats.arjuna.exceptions.ObjectStoreException; import com.arjuna.ats.internal.txoj.abstractrecords.LockRecord; /* * Optimistic cc in operation. Grab a copy of the current state so we can check against * the state again when we commit. * * During commit we check the current state on disk against what we had initially. If they * are not identical then we force the transaction to abort. * * Note, this can mean that multiple transactions may have committed during the interim but as * long as they keep the state the same then we are ok. */ class OptimisticLockRecord extends LockRecord { public OptimisticLockRecord (OptimisticLockManager lm, BasicAction currAct, boolean check) { super(lm, currAct); try { _state = lm.getStore().read_committed(lm.get_uid(), lm.type()); } catch (final ObjectStoreException ex) { _state = null; } _status = lm.status(); _check = check; } public OptimisticLockRecord (OptimisticLockManager lm, boolean rdOnly, BasicAction currAct, boolean check) { super(lm, rdOnly, currAct); try { _state = lm.getStore().read_committed(lm.get_uid(), lm.type()); } catch (final ObjectStoreException ex) { _state = null; } _status = lm.status(); _check = check; } public int typeIs () { return RecordType.USER_DEF_FIRST0; } public int nestedAbort () { if (txojLogger.logger.isTraceEnabled()) { txojLogger.logger.trace("OptimisticLockRecord::nestedAbort() for "+order()); } /* * Optimistic cc means we just throw away the state. */ _state = null; return super.nestedAbort(); } public int topLevelPrepare () { if (txojLogger.logger.isTraceEnabled()) { txojLogger.logger.trace("OptimisticLockRecord::nestedPrepare() for "+order()); } if (value() == null) return TwoPhaseOutcome.PREPARE_NOTOK; if (checkState()) return super.topLevelPrepare(); else { txojLogger.i18NLogger.warn_OptimisticLockRecord_1((LockManager) value()); return TwoPhaseOutcome.PREPARE_NOTOK; } } public int topLevelCommit () { boolean stateOK = checkState(); if (!stateOK) { txojLogger.i18NLogger.warn_OptimisticLockRecord_2((LockManager) value()); } if ((super.topLevelCommit() == TwoPhaseOutcome.FINISH_OK) && stateOK) return TwoPhaseOutcome.FINISH_OK; else return TwoPhaseOutcome.FINISH_ERROR; } public String type () { return "/StateManager/AbstractRecord/LockRecord/OptimisticLockRecord"; } public String toString () { return _myUid.stringForm(); } protected OptimisticLockRecord () { super(); } private boolean checkState () { if ((_status == ObjectStatus.ACTIVE_NEW) || (!_check)) return true; if (_state == null) return false; /* * If the object is recoverable then we can just check the local state. * If the object is persistent then we have to check the state on disk. */ OutputObjectState tempState = new OutputObjectState(); OptimisticLockManager man = (OptimisticLockManager) value(); int objectType = man.objectType(); synchronized (man) { /* * If we check the state now, it's possible that some other transactions may be * doing the same concurrently. We need to lock the object at this point. Or * suffer heuristic by checking during commit - though this still leaves a window * of vulnerability. */ /* * Assume initially that this will only work if the objects are all in the same * address space, since sharing across spaces will impose performance overhead * anyway. In that case, we can maintain a list of all objects that are being * managed optimistically and check them directly as well as lock them. * * Problem is that it's the state that needs to be checked and there may be * multiple instances of the state active in memory at the same time. So would * need to keep each instance per Uid. */ /* * Could even make this specific to STM and in which case we have even more control. */ if (objectType == ObjectType.RECOVERABLE) { if (man.save_state(tempState, objectType)) { boolean identical = true; if (tempState.length() == _state.length()) { for (int i = 0; (i < tempState.length()) && identical; i++) { if (tempState.buffer()[i] != _state.buffer()[i]) identical = false; } if (identical) return true; } } } if (objectType == ObjectType.ANDPERSISTENT) { /* * Don't need the state - could just check the time of the file update if we are using * a file based object store. */ try { InputObjectState s = man.getStore().read_committed(man.get_uid(), man.type()); if (s != null) { boolean identical = true; if (s.length() == _state.length()) { for (int i = 0; (i < s.length()) && identical; i++) { if (s.buffer()[i] != _state.buffer()[i]) identical = false; } if (identical) return true; } } else { return false; } } catch (final Exception ex) { ex.printStackTrace(); } } } return false; } public boolean shouldReplace (AbstractRecord ar) { if (!super.shouldReplace(ar)) { if ((order().equals(ar.order())) && typeIs() == ar.typeIs()) { if (!((OptimisticLockRecord) ar)._check && _check) return true; } return false; } else return true; } private InputObjectState _state = new InputObjectState(); private int _status = ObjectStatus.ACTIVE_NEW; private boolean _check = true; private Uid _myUid = new Uid(); }