/**
*
* Copyright 2004 Hiram Chirino
*
* Licensed 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 org.activemq.store.journal;
import java.util.Iterator;
import javax.jms.JMSException;
import javax.transaction.xa.XAException;
import org.activeio.journal.RecordLocation;
import org.activemq.message.ActiveMQXid;
import org.activemq.store.TransactionStore;
import org.activemq.util.TransactionTemplate;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
/**
*/
public class JournalTransactionStore implements TransactionStore {
private final JournalPersistenceAdapter peristenceAdapter;
final TransactionStore checkpointStore;
ConcurrentHashMap inflightTransactions = new ConcurrentHashMap();
ConcurrentHashMap preparedTransactions = new ConcurrentHashMap();
private TransactionTemplate transactionTemplate;
public JournalTransactionStore(JournalPersistenceAdapter adapter, TransactionStore checkpointStore) {
this.peristenceAdapter = adapter;
this.checkpointStore = checkpointStore;
this.transactionTemplate = new TransactionTemplate(adapter);
}
public void transactionStarted(Object txid, RecordLocation txStartLocation) {
if( inflightTransactions.get(txid)==null ) {
inflightTransactions.put(txid, txStartLocation);
}
}
public void prepare(Object txid) throws XAException {
Object location = inflightTransactions.remove(txid);
if (location == null)
return;
peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.XA_PREPARE,txid,false), true);
preparedTransactions.put(txid, location);
checkpointStore.prepare(txid);
}
public void commit(Object txid, boolean wasPrepared) throws XAException {
Object location;
if( wasPrepared ) {
location = preparedTransactions.remove(txid);
} else {
location = inflightTransactions.remove(txid);
}
if( location == null )
return;
if(txid.getClass()==ActiveMQXid.class) {
peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.XA_COMMIT,txid,wasPrepared), true);
} else {
peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.LOCAL_COMMIT,txid,wasPrepared), true);
}
checkpointStore.commit(txid, wasPrepared);
}
public void rollback(Object txid) throws XAException {
if(txid.getClass()==ActiveMQXid.class) {
peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.XA_ROLLBACK,txid,false), true);
} else {
peristenceAdapter.writeTxCommand(new TxCommand(TxCommand.LOCAL_ROLLBACK,txid,false), true);
}
preparedTransactions.remove(txid);
inflightTransactions.remove(txid);
checkpointStore.rollback(txid);
}
public void recover(RecoveryListener listener) throws XAException {
checkpointStore.recover(listener);
}
public void start() throws JMSException {
}
public void stop() throws JMSException {
}
/**
* @return
* @throws JMSException
*/
public RecordLocation checkpoint() throws JMSException {
// Nothing really to checkpoint.. since, we don't
// buffer tx opperations in the journal to do a batch
// apply to the checkpointStore.
// But we keep track of the first location of an operation
// that was associated with an active tx. The journal can not
// roll over active tx records.
RecordLocation rc=null;
for (Iterator iter = inflightTransactions.values().iterator(); iter.hasNext();) {
RecordLocation location = (RecordLocation) iter.next();
if( rc==null || rc.compareTo(location)<0 ) {
rc = location;
}
}
for (Iterator iter = preparedTransactions.values().iterator(); iter.hasNext();) {
RecordLocation location = (RecordLocation) iter.next();
if( rc==null || rc.compareTo(location)<0 ) {
rc = location;
}
}
return rc;
}
}