package org.mobicents.slee.connector.adaptor;
import org.apache.log4j.Logger;
import org.mobicents.slee.connector.server.EventInvocation;
import org.mobicents.slee.connector.server.RemoteSleeService;
import java.io.PrintWriter;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import javax.slee.Address;
import javax.slee.EventTypeID;
import javax.slee.UnrecognizedEventException;
import javax.slee.connection.ExternalActivityHandle;
import javax.transaction.xa.XAResource;
/**
* Implementation of the ManagedConnection interface according to the JCA1.0
* contract
*
* @author Tim
*/
public class ManagedConnectionImpl implements ManagedConnection,
LocalTransaction {
private static Logger log = Logger.getLogger(ManagedConnectionImpl.class);
private static ConnectionMetaDataImpl metaData = new ConnectionMetaDataImpl();
private RemoteSleeService rmiStub;
private LinkedList<ConnectionEventListener> listeners = new LinkedList<ConnectionEventListener>();
private LinkedList<SleeConnectionImpl> connectionHandles = new LinkedList<SleeConnectionImpl>();
private ArrayList<EventInvocation> eventQueue = new ArrayList<EventInvocation>();
private boolean destroyed;
private PrintWriter printWriter;
private boolean inTransaction;
ManagedConnectionImpl(RemoteSleeService rmiStub) {
if (log.isDebugEnabled()) {
log.debug("Creating ManagedConnectionImpl");
}
this.rmiStub = rmiStub;
}
/*
* Called by the connection manager to register interest in a connection
*
* @see javax.resource.spi.ManagedConnection#addConnectionEventListener(javax.resource.spi.ConnectionEventListener)
*/
public void addConnectionEventListener(ConnectionEventListener listener) {
if (log.isDebugEnabled()) {
log.debug("addConnectionEventListener() called");
}
listeners.add(listener);
}
/*
* Remove a listener
*
* @see javax.resource.spi.ManagedConnection#removeConnectionEventListener(javax.resource.spi.ConnectionEventListener)
*/
public void removeConnectionEventListener(ConnectionEventListener listener) {
if (log.isDebugEnabled()) {
log.debug("removeConnectionEventListener() called");
}
listeners.remove(listener);
}
/*
* Move a handle from one physical connection to another
*
* @see javax.resource.spi.ManagedConnection#associateConnection(java.lang.Object)
*/
public void associateConnection(Object connection) throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("associateConnection() called");
}
SleeConnectionImpl conn = (SleeConnectionImpl) connection;
conn.getManagedConnection().removeConnectionHandle(conn);
connectionHandles.add(conn);
conn.setManagedConnection(this);
}
/*
* Client up client side state associated with the physical connection
*
* @see javax.resource.spi.ManagedConnection#cleanup()
*/
public void cleanup() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("cleanUp() called on " + this);
}
// This is called to clean up any client-specific state associated with
// this
// physical connection. It should call invalidate on all the associated
// connection handles
Iterator iter = connectionHandles.iterator();
while (iter.hasNext()) {
SleeConnectionImpl handle = (SleeConnectionImpl) iter.next();
handle.invalidate();
}
connectionHandles.clear();
}
/*
* Destroy the physical connection
*
* @see javax.resource.spi.ManagedConnection#destroy()
*/
public void destroy() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("destroy() called on " + this);
}
if (destroyed)
return;
cleanup();
destroyed = true;
}
/*
* Get a handle
*
* @see javax.resource.spi.ManagedConnection#getConnection(javax.security.auth.Subject,
* javax.resource.spi.ConnectionRequestInfo)
*/
public Object getConnection(Subject subject, ConnectionRequestInfo info)
throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("getConnection() called on " + this);
}
if (destroyed)
throw new IllegalStateException(
"This ManagedConnection has already been destroyed!");
// Create a new connection handle
SleeConnectionImpl conn = new SleeConnectionImpl(this);
connectionHandles.add(conn);
return conn;
}
/*
* Get the meta-data
*
* @see javax.resource.spi.ManagedConnection#getMetaData()
*/
public ManagedConnectionMetaData getMetaData() throws ResourceException {
return metaData;
}
/*
* We don't support XA transactions
*
* @see javax.resource.spi.ManagedConnection#getXAResource()
*/
public XAResource getXAResource() throws ResourceException {
// We don't support XA transactions
return null;
}
/*
* Get the local transaction
*
* @see javax.resource.spi.ManagedConnection#getLocalTransaction()
*/
public LocalTransaction getLocalTransaction() throws ResourceException {
log.debug("getLocalTransaction() called");
return this;
}
/*
* Not used
*
* @see javax.resource.spi.ManagedConnection#setLogWriter(java.io.PrintWriter)
*/
public void setLogWriter(PrintWriter pw) throws ResourceException {
printWriter = pw;
}
/*
* Not used
*
* @see javax.resource.spi.ManagedConnection#getLogWriter()
*/
public PrintWriter getLogWriter() throws ResourceException {
return printWriter;
}
/*
* Called by the transaction manager to start a local transaction
*
* @see javax.resource.spi.LocalTransaction#begin()
*/
public void begin() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("begin() called on local transaction");
}
if (inTransaction) {
throw new ResourceException(
"begin() called on transaction already in progress");
}
if (this.eventQueue.size() != 0) {
throw new IllegalStateException(
"begin() called on transaction but events already in queue!");
}
// do stuff
inTransaction = true;
// sendNotification(new ConnectionEvent(this,
// ConnectionEvent.LOCAL_TRANSACTION_STARTED));
}
/*
* Called by the transaction manager to commit a local tranaction
*
* @see javax.resource.spi.LocalTransaction#commit()
*/
public void commit() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("commit() called on local transaction");
}
if (!inTransaction) {
throw new ResourceException(
"commit() called on transaction but transaction not started");
}
// Send any queued events
sendQueuedEvents();
eventQueue.clear();
inTransaction = false;
// sendNotification(new ConnectionEvent(this,
// ConnectionEvent.LOCAL_TRANSACTION_COMMITTED));
}
/*
* Called by the transaction manager to rollback a local transaction
*
* @see javax.resource.spi.LocalTransaction#rollback()
*/
public void rollback() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("rollback() called on local transaction");
}
if (!inTransaction) {
throw new ResourceException(
"rollback() called on transaction but transaction not started");
}
// Clear the event queue
this.eventQueue.clear();
inTransaction = false;
}
private void removeConnectionHandle(SleeConnectionImpl connectionHandle) {
if (log.isDebugEnabled()) {
log.debug("removeConnectionHandle() called");
}
connectionHandles.remove(connectionHandle);
}
private void sendNotification(ConnectionEvent event) {
if (log.isDebugEnabled()) {
log.debug("sendNotification() called with " + event + " eventID = "
+ event.getId());
}
Iterator iter = listeners.iterator();
while (iter.hasNext()) {
ConnectionEventListener listener = (ConnectionEventListener) iter
.next();
switch (event.getId()) {
case ConnectionEvent.CONNECTION_CLOSED:
listener.connectionClosed(event);
break;
case ConnectionEvent.LOCAL_TRANSACTION_STARTED:
listener.localTransactionStarted(event);
break;
case ConnectionEvent.LOCAL_TRANSACTION_COMMITTED:
listener.localTransactionCommitted(event);
break;
case ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK:
listener.localTransactionRolledback(event);
break;
case ConnectionEvent.CONNECTION_ERROR_OCCURRED:
listener.connectionErrorOccurred(event);
break;
default:
throw new IllegalStateException("Invalid event id:"
+ event.getId());
}
}
}
void handleClosed(SleeConnectionImpl handle) {
if (log.isDebugEnabled()) {
log.debug("handleClosed() called");
}
if (destroyed)
throw new IllegalStateException(
"Attempt to close a handle on a destroyed connection!");
ConnectionEvent event = new ConnectionEvent(this,
ConnectionEvent.CONNECTION_CLOSED);
event.setConnectionHandle(handle);
sendNotification(event);
connectionHandles.remove(handle);
}
void connectionError(SleeConnectionImpl handle, Exception e) {
if (log.isDebugEnabled()) {
log.debug("connectionError() called");
}
if (destroyed)
throw new IllegalStateException(
"Attempt to signal a conection error on a destroyed connection!");
ConnectionEvent event = new ConnectionEvent(this,
ConnectionEvent.CONNECTION_ERROR_OCCURRED);
event.setConnectionHandle(handle);
sendNotification(event);
}
/* This method is non-transactional */
ExternalActivityHandle createActivityHandle() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("createActivityHandle() called");
}
if (destroyed)
throw new IllegalStateException("Connection is destroyed!");
try {
return rmiStub.createActivityHandle();
} catch (RemoteException e) {
String s = "Failed to invoke createActivityHandle";
log.error(s, e);
ResourceException ex = new ResourceException(s);
ex.setLinkedException(e);
throw ex;
}
}
/* This method is non-transactional */
EventTypeID getEventTypeID(String name, String vendor, String version)
throws ResourceException, UnrecognizedEventException {
if (log.isDebugEnabled()) {
log.debug("getEventTypeID called:" + name + "," + vendor + ","
+ version);
}
if (destroyed)
throw new IllegalStateException("Connection is destroyed!");
try {
return rmiStub.getEventTypeID(name, vendor, version);
} catch (RemoteException e) {
String s = "Failed to invoke getEventTypeID";
log.error(s, e);
ResourceException ex = new ResourceException(s);
ex.setLinkedException(e);
throw ex;
}
}
/*
* This method is transactional. If this method is invoked while a
* transaction is in progress, then the events aren't actually fired on the
* SLEE until the transaction commits. See JAIN SLEE 1.0 spec. section F.2.
* Therefore we need to queue the events internally and fire them all on the
* commit, if we're in a transaction. We don't queue them on the server side
* due to difficulty in clustering (we would have to cluster the queued
* events)
*/
void fireEvent(Object event, EventTypeID eventType,
ExternalActivityHandle activityHandle, Address address)
throws ResourceException, NullPointerException,
UnrecognizedEventException {
if (log.isDebugEnabled()) {
log.debug("fireEvent() called:" + event + "," + eventType + ","
+ activityHandle + "," + address);
}
if (destroyed)
throw new IllegalStateException("Connection is destroyed!");
if (!this.inTransaction) {
fireEventNow(event, eventType, activityHandle, address);
} else {
fireEventLater(event, eventType, activityHandle, address);
}
}
private void fireEventNow(Object event, EventTypeID eventType,
ExternalActivityHandle activityHandle, Address address)
throws ResourceException, NullPointerException,
UnrecognizedEventException {
if (log.isDebugEnabled()) {
log.debug("Firing single event on SLEE:" + event);
}
try {
this.rmiStub.fireEvent(event, eventType, activityHandle, address);
} catch (RemoteException e) {
String s = "Failed to invoke fireEvent";
log.error(s, e);
ResourceException ex = new ResourceException(s);
ex.setLinkedException(e);
throw ex;
}
}
private void fireEventLater(Object event, EventTypeID eventType,
ExternalActivityHandle activityHandle, Address address) {
// Just stick the invocation on the queue
if (log.isDebugEnabled()) {
log.debug("Adding event to event queue");
}
EventInvocation ei = new EventInvocation(event, eventType,
activityHandle, address);
this.eventQueue.add(ei);
}
/*
* Actually send any queued events to the SLEE.
*/
private void sendQueuedEvents() throws ResourceException {
if (log.isDebugEnabled()) {
log.debug("Firing queue of events on SLEE: "
+ this.eventQueue.size());
}
try {
for (EventInvocation ei : eventQueue) {
fireEventNow(ei.event, ei.eventTypeId,
ei.externalActivityHandle, ei.address);
}
} catch (NullPointerException e) {
throw new ResourceException(e.getMessage(), e);
} catch (UnrecognizedEventException e) {
throw new ResourceException(e.getMessage(), e);
}
}
}