/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.jini.outrigger.snaplogstore; import com.sun.jini.outrigger.Recover; import com.sun.jini.outrigger.StoredObject; import java.io.IOException; import java.io.Serializable; import java.rmi.RemoteException; import java.util.Enumeration; import java.util.Hashtable; import net.jini.core.transaction.server.TransactionConstants; import net.jini.space.InternalSpaceException; /** * This object represents a pending transaction in a <code>BackEnd</code>. * As operations are performed under the transaction, they are logged into * this object. When the transaction is committed, each operation is * committed into the DB. * * @author Sun Microsystems, Inc. * */ class PendingTxn implements Serializable { /** A superclass for the objects that represent pending operations. */ static abstract class PendingOp implements Serializable { /** * Commit the operation by invoking the relevant method on the * <code>processor</code> object. */ abstract void commit(BackEnd processor); } /** An object that represents a pending write. */ static class WriteOp extends PendingOp implements Serializable { Resource entry; WriteOp(Resource entry) { this.entry = entry; } void commit(BackEnd processor) { processor.writeOp(entry, null); } } /** An object that represents a pending take. */ static class TakeOp extends PendingOp implements Serializable { byte cookie[]; TakeOp(byte cookie[]) { this.cookie = cookie; } void commit(BackEnd processor) { processor.takeOp(cookie, null); } } private long id; // the transaction ID private int state; // current state private Hashtable ops; // list of pending operations private StoredObject transaction; // the transaction object // itself /** * Create a new <code>PendingTxn</code> for the given <code>id</code>. */ PendingTxn(Long id) { this.id = id.longValue(); state = TransactionConstants.ACTIVE; ops = new Hashtable(); } /** * Add a new pending <code>write</code> operation. */ void addWrite(Resource entry) { ops.put(entry.getCookieAsWrapper(), new WriteOp(entry)); } /** * Add a new pending <code>take</code> operation. */ void addTake(byte cookie[]) { // Remove entry if the take is for a previous write in this // transaction. If it is for an entry written outside the transaction, // save the take for later. Note that this allows the pending elements // to be processed out of order (during a commit). // ByteArrayWrapper baw = new ByteArrayWrapper(cookie); if (ops.remove(baw) == null) ops.put(baw, new TakeOp(cookie)); } /** * Get a pending write resource. */ Resource get(ByteArrayWrapper cookie) { PendingOp po = (PendingOp)ops.get(cookie); // Both pending writes and pending takes are stored in the table. // We only interested in entries from pending writes. // if ((po != null) & (po instanceof WriteOp)) return ((WriteOp)po).entry; return null; } /** * Remove a pending write. */ Resource remove(ByteArrayWrapper cookie) { Resource entry = get(cookie); if (entry != null) ops.remove(cookie); return entry; } /** * Recover prepared transactions. This method returns true if this * pending transaction was recovered. */ boolean recover(Recover space) throws Exception { if (state != TransactionConstants.PREPARED) return false; space.recoverTransaction(new Long(id), transaction); Enumeration e = ops.elements(); while (e.hasMoreElements()) { PendingTxn.PendingOp op = (PendingTxn.PendingOp)e.nextElement(); if (op instanceof PendingTxn.WriteOp) { space.recoverWrite(((PendingTxn.WriteOp)op).entry, new Long(id)); } else if (op instanceof PendingTxn.TakeOp) { space.recoverTake( ByteArrayWrapper.toUuid(((PendingTxn.TakeOp)op).cookie), new Long(id)); } else { throw new InternalSpaceException("unknown operation type: " + op.getClass().getName()); } } return true; } /** * Set the <code>Transaction</code> object. */ void prepare(StoredObject tr) { transaction = tr; state = TransactionConstants.PREPARED; } /** * Commit all the operations by invoking the relevant method * on the <code>processor</code> object. */ void commit(BackEnd processor) { Enumeration e = ops.elements(); while (e.hasMoreElements()) ((PendingOp)e.nextElement()).commit(processor); state = TransactionConstants.COMMITTED; } public int hashCode() { return (int)id; } public boolean equals(Object o) { try { return ((PendingTxn)o).id == id; } catch (Exception e) {} return false; } }