package org.marketcetera.saclient;
import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.marketcetera.modules.remote.emitter.EmitterAdapter;
import org.marketcetera.modules.remote.emitter.RemoteDataEmitter;
import org.marketcetera.util.except.ExceptUtils;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.util.log.I18NBoundMessage2P;
import org.marketcetera.util.log.SLF4JLoggerProxy;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.ws.wrappers.RemoteException;
import org.springframework.context.Lifecycle;
/* $License$ */
/**
* Provides common behavior for <code>SAClient</code> implementations.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: AbstractSAClient.java 16901 2014-05-11 16:14:11Z colin $
* @since 2.4.0
*/
@ClassVersion("$Id: AbstractSAClient.java 16901 2014-05-11 16:14:11Z colin $")
public abstract class AbstractSAClient
implements SAClient,EmitterAdapter,Lifecycle
{
/* (non-Javadoc)
* @see org.springframework.context.Lifecycle#isRunning()
*/
@Override
public boolean isRunning()
{
return running.get();
}
/* (non-Javadoc)
* @see org.springframework.context.Lifecycle#start()
*/
@Override
public synchronized final void start()
{
doStart();
if(parameters.getUseJms()) {
try {
emitter = new RemoteDataEmitter(parameters.getURL(),
parameters.getUsername(),
String.valueOf(parameters.getPassword()),
this);
} catch (Exception e) {
throw new ConnectionException(e,
new I18NBoundMessage2P(Messages.ERROR_JMS_CONNECT,
parameters.getURL(),
parameters.getUsername()));
}
} else {
emitter = null;
}
running.set(true);
}
/* (non-Javadoc)
* @see org.springframework.context.Lifecycle#stop()
*/
@Override
public synchronized final void stop()
{
SLF4JLoggerProxy.debug(this,
"Closing Strategy Agent Client"); //$NON-NLS-1$
try {
try {
doStop();
} catch (Exception ignored) {}
try {
if(emitter != null) {
emitter.close();
}
} catch (Exception ignored) {}
} finally {
emitter = null;
running.set(false);
SLF4JLoggerProxy.debug(this,
"Closed Strategy Agent Client"); //$NON-NLS-1$
}
}
/**
*
*
*
*/
protected void doStart()
{
}
/**
*
*
*
*/
protected void doStop()
{
}
/**
* Create a new AbstractSAClient instance.
*
* @param inParameters an <code>SAClientParameters</code> value
* @throws ConnectionException if a connection could not be made
*/
protected AbstractSAClient(SAClientParameters inParameters)
{
if(inParameters == null) {
throw new NullPointerException();
}
parameters = inParameters;
}
/* (non-Javadoc)
* @see org.marketcetera.saclient.SAClient#close()
*/
@Override
public synchronized final void close()
{
stop();
}
/* (non-Javadoc)
* @see org.marketcetera.saclient.SAClient#addDataReceiver(org.marketcetera.saclient.DataReceiver)
*/
@Override
public void addDataReceiver(DataReceiver inReceiver)
{
if(inReceiver == null) {
throw new NullPointerException();
}
synchronized (receivers) {
receivers.addFirst(inReceiver);
}
}
/* (non-Javadoc)
* @see org.marketcetera.saclient.SAClient#removeDataReciever(org.marketcetera.saclient.DataReceiver)
*/
@Override
public void removeDataReciever(DataReceiver inReceiver)
{
if(inReceiver == null) {
throw new NullPointerException();
}
synchronized (receivers) {
receivers.removeFirstOccurrence(inReceiver);
}
}
/* (non-Javadoc)
* @see org.marketcetera.saclient.SAClient#addConnectionStatusListener(org.marketcetera.saclient.ConnectionStatusListener)
*/
@Override
public void addConnectionStatusListener(ConnectionStatusListener inListener)
{
if(inListener == null) {
throw new NullPointerException();
}
synchronized (listeners) {
listeners.addFirst(inListener);
}
inListener.receiveConnectionStatus(isRunning());
}
/* (non-Javadoc)
* @see org.marketcetera.saclient.SAClient#removeConnectionStatusListener(org.marketcetera.saclient.ConnectionStatusListener)
*/
@Override
public void removeConnectionStatusListener(ConnectionStatusListener inListener)
{
if(inListener == null) {
throw new NullPointerException();
}
synchronized (listeners) {
listeners.removeFirstOccurrence(inListener);
}
}
/* (non-Javadoc)
* @see org.marketcetera.saclient.SAClient#getParameters()
*/
@Override
public SAClientParameters getParameters()
{
return new SAClientParameters(parameters.getUsername(),
"*****".toCharArray(), //$NON-NLS-1$
parameters.getURL(),
parameters.getHostname(),
parameters.getPort());
}
/* (non-Javadoc)
* @see org.marketcetera.modules.remote.emitter.EmitterAdapter#receiveData(java.lang.Object)
*/
@Override
public void receiveData(Object inObject)
{
synchronized (receivers) {
for(DataReceiver receiver: receivers) {
try {
receiver.receiveData(inObject);
} catch (Exception e) {
Messages.LOG_ERROR_RECEIVE_DATA.warn(this, e, inObject);
ExceptUtils.interrupt(e);
}
}
}
}
/* (non-Javadoc)
* @see org.marketcetera.modules.remote.emitter.EmitterAdapter#connectionStatusChanged(boolean, boolean)
*/
@Override
public void connectionStatusChanged(boolean inOldStatus,
boolean inNewStatus)
{
if(inOldStatus == inNewStatus) {
return;
}
running.set(inNewStatus);
synchronized(listeners) {
for(ConnectionStatusListener listener: listeners) {
try {
listener.receiveConnectionStatus(inNewStatus);
} catch (Exception e) {
Messages.LOG_ERROR_RECEIVE_CONNECT_STATUS.warn(this,
e,
inNewStatus);
ExceptUtils.interrupt(e);
}
}
}
}
/**
* Fails if the connection to the client is closed or disconnected.
*
* @throws ConnectionException if the connection to the client is closed or disconnected.
*/
protected void failIfDisconnected()
throws ConnectionException
{
if(!isRunning()) {
throw new ConnectionException(Messages.CLIENT_DISCONNECTED);
}
}
/**
* Fails if the connection to the client has been closed.
*
* @throws IllegalStateException if the connection to the client has been closed.
*/
protected void failIfClosed()
throws IllegalStateException
{
if(!isRunning()) {
throw new IllegalStateException(Messages.CLIENT_CLOSED.getText());
}
}
/**
* Creates a connection exception wrapping the supplied exception.
*
* <p>If the supplied exception is a <code>RemoteException</code>,
* the exception wrapped by it is extracted and wrapped into the
* returned exception.
*
* @param inFailure the exception that needs to be wrapped.
* @return the connection exception wrapping the failure.
*/
protected ConnectionException wrapRemoteFailure(Exception inFailure)
{
Throwable cause;
//if it's a remote server failure, extract the nested cause.
if(inFailure instanceof RemoteException) {
cause = inFailure.getCause() != null ? inFailure.getCause() : inFailure;
} else {
cause = inFailure;
}
return new ConnectionException(cause,
new I18NBoundMessage1P(Messages.ERROR_WS_OPERATION,
cause.getLocalizedMessage()));
}
/**
* remote emitter used to connect to external SA instances
*/
private RemoteDataEmitter emitter;
/**
* receivers of remove data
*/
private final Deque<DataReceiver> receivers = new LinkedList<DataReceiver>();
/**
* connection status listeners collection
*/
private final Deque<ConnectionStatusListener> listeners = new LinkedList<ConnectionStatusListener>();
/**
* SA connection parameters
*/
protected final SAClientParameters parameters;
/**
* indicates if the connection is active or not
*/
protected final AtomicBoolean running = new AtomicBoolean(false);
}