/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* 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.apache.sandesha2.storage.inmemory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.sandesha2.i18n.SandeshaMessageHelper;
import org.apache.sandesha2.i18n.SandeshaMessageKeys;
import org.apache.sandesha2.storage.SandeshaStorageException;
import org.apache.sandesha2.storage.Transaction;
import org.apache.sandesha2.storage.beans.RMBean;
import org.apache.sandesha2.workers.SandeshaThread;
/**
* This class does not really implement transactions, but it is a good
* place to implement locking for the in memory storage manager.
*/
public class InMemoryTransaction implements Transaction {
private static final Log log = LogFactory.getLog(InMemoryTransaction.class);
private InMemoryStorageManager manager;
private String threadName;
private int threadId;
private ArrayList enlistedBeans = new ArrayList();
private InMemoryTransaction waitingForTran = null;
private boolean sentMessages = false;
private boolean receivedMessages = false;
private boolean active = true;
InMemoryTransaction(InMemoryStorageManager manager, String threadName, int id) {
if(log.isDebugEnabled()) log.debug("Entry: InMemoryTransaction::<init>");
this.manager = manager;
this.threadName = threadName;
this.threadId = id;
if(log.isDebugEnabled()) log.debug("Exit: InMemoryTransaction::<init>, " + this);
}
public void commit() {
releaseLocks();
if(sentMessages) manager.getSender().wakeThread();
if(receivedMessages) {
SandeshaThread invoker = manager.getInvoker();
if(invoker != null) invoker.wakeThread();
}
active = false;
}
public void rollback() {
releaseLocks();
active = false;
}
public boolean isActive () {
return active;
}
public void enlist(RMBean bean) throws SandeshaStorageException {
if(log.isDebugEnabled()) log.debug("Entry: InMemoryTransaction::enlist, " + bean);
if(bean != null) {
synchronized (bean) {
InMemoryTransaction other = (InMemoryTransaction) bean.getTransaction();
while(other != null && other != this) {
// Put ourselves into the list of waiters
waitingForTran = other;
// Look to see if there is a loop in the chain of waiters
if(!enlistedBeans.isEmpty()) {
HashSet set = new HashSet();
set.add(this);
while(other != null) {
if(set.contains(other)) {
String message = SandeshaMessageHelper.getMessage(SandeshaMessageKeys.deadlock, this.toString(), bean.toString());
SandeshaStorageException e = new SandeshaStorageException(message);
// Do our best to get out of the way of the other work in the system
waitingForTran = null;
releaseLocks();
if(log.isDebugEnabled()) log.debug(message, e);
throw e;
}
set.add(other);
other = other.waitingForTran;
}
}
try {
if(log.isDebugEnabled()) log.debug("This " + this + " waiting for " + waitingForTran);
bean.wait();
} catch(InterruptedException e) {
// Do nothing
}
other = (InMemoryTransaction) bean.getTransaction();
}
waitingForTran = null;
if(other == null) {
if(log.isDebugEnabled()) log.debug(this + " locking bean");
bean.setTransaction(this);
enlistedBeans.add(bean);
}
}
}
if(log.isDebugEnabled()) log.debug("Exit: InMemoryTransaction::enlist");
}
private void releaseLocks() {
if(log.isDebugEnabled()) log.debug("Entry: InMemoryTransaction::releaseLocks, " + this);
manager.removeTransaction(this);
Iterator beans = enlistedBeans.iterator();
while(beans.hasNext()) {
RMBean bean = (RMBean) beans.next();
synchronized (bean) {
bean.setTransaction(null);
bean.notifyAll();
}
}
enlistedBeans.clear();
if(log.isDebugEnabled()) log.debug("Exit: InMemoryTransaction::releaseLocks");
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("[InMemoryTransaction, id:");
result.append(threadId);
result.append(", name: ");
result.append(threadName);
result.append(", locks: ");
result.append(enlistedBeans.size());
result.append("]");
return result.toString();
}
public void setReceivedMessages(boolean receivedMessages) {
this.receivedMessages = receivedMessages;
}
public void setSentMessages(boolean sentMessages) {
this.sentMessages = sentMessages;
}
}