package org.marketcetera.modules.remote.emitter;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.spring.SpringUtils;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/* $License$ */
/**
* A class that abstracts out receiving and publishing of data
* from the remote receiver so that it can be reused without having
* to go via the module framework.
*
* @author anshul@marketcetera.com
* @version $Id: RemoteDataEmitter.java 16154 2012-07-14 16:34:05Z colin $
* @since 2.0.0
*/
@ClassVersion("$Id: RemoteDataEmitter.java 16154 2012-07-14 16:34:05Z colin $")
public class RemoteDataEmitter {
/**
* Creates an instance that connects to the specified remote source of data
* and publishes the received data and notifications to the supplied
* adapter instance.
*
* @param inURL the URL of remote source of data. Cannot be null.
* @param inUsername the user name to authenticate to the remote source. Cannot be null.
* @param inPassword the password to authenticate to the remote source. Cannot be null.
* @param inAdapter the adapter instance. Cannot be null.
*
* @throws RuntimeException if there were errors connecting.
*/
public RemoteDataEmitter(String inURL, String inUsername, String inPassword,
EmitterAdapter inAdapter) {
if(inURL == null) {
throw new NullPointerException();
}
if(inAdapter == null) {
throw new NullPointerException();
}
if(inUsername == null) {
throw new NullPointerException();
}
if(inPassword == null) {
throw new NullPointerException();
}
mAdapter = inAdapter;
//Create spring contexts to initialize the broker and messaging topic
StaticApplicationContext parent = new StaticApplicationContext();
SpringUtils.addStringBean(parent, "brokerURI", inURL); //$NON-NLS-1$
SpringUtils.addStringBean(parent, "username", //$NON-NLS-1$
inUsername);
SpringUtils.addStringBean(parent, "password", //$NON-NLS-1$
inPassword);
parent.refresh();
mContext = new ClassPathXmlApplicationContext(new String[]{
"remote-emitter-jms.xml"}, parent); //$NON-NLS-1$
mContext.start();
MessagingDelegate delegate = (MessagingDelegate) mContext.getBean(
"delegate", MessagingDelegate.class); //$NON-NLS-1$
delegate.setDataEmitter(this);
//Reset last failure
setLastFailure(null);
//Send notification that the module is now connected.
sendConnectedChanged(false, true);
}
/**
* Closes the connection to the remote source of data.
* This instance is unusable after this method is invoked.
* <p>
* If one needs to reconnect to the remote source, a new instance
* of this class should be created.
* <p>
* This method does not fail if the attempt to close the connection fails.
* In case of failures, the failures are logged and the method returns
* silently.
*/
public synchronized void close() {
if(mContext == null) {
return;
}
boolean isConnected = getLastFailure() == null;
try {
mContext.close();
} catch (Exception e) {
//Swallow the exception as it prevents the module from stopping.
//If the receiver closed the connection from its end
//this method always fails.
Messages.LOG_ERROR_CLOSING_CONNECTION.warn(this, e);
}
mContext = null;
if(isConnected) {
sendConnectedChanged(isConnected, false);
}
}
/**
* The last failure encountered when receiving data.
*
* @return last failure encountered when receiving data.
*/
public Exception getLastFailure() {
return mLastFailure;
}
/**
* Returns true if no failures have been encountered when
* receiving data from the remote source.
*
* @return if no failures have been encountered when receiving data
* from the remote source.
*/
public boolean isConnected() {
return mContext != null && mLastFailure == null;
}
/**
* Receives the data received from the remote source.
* The received data is handed off to the adapter.
*
* @param inObject the data received from the remote source.
*/
void receive(Object inObject) {
mAdapter.receiveData(inObject);
}
/**
* This method is invoked when any failures are encountered receiving
* messages from the remote receiver.
* <p>
* The text of the failure is available via {@link #getLastFailure()}.
* <p>
* The a non-null parameter to this method causes {@link #isConnected()}
* to return false.
*
* @param inException the failure encountered.
*/
void onException(Exception inException) {
Messages.LOG_ERROR_RECEIVER_CONNECTION.warn(this, inException);
setLastFailure(inException);
}
/**
* Sets the value of the last failure exception and sends out an
* attribute change notification for {@link #isConnected()} if necessary.
* <p>
* It's assumed that this method can only be invoked when the module
* is started.
*
* @param inLastFailure the failure exception, can be null.
*/
private void setLastFailure(Exception inLastFailure) {
boolean oldConnected = mLastFailure == null;
mLastFailure = inLastFailure;
boolean newConnected = mLastFailure == null;
if(oldConnected != newConnected) {
sendConnectedChanged(oldConnected, newConnected);
}
}
/**
* Sends the connection change status to the adapter.
*
* @param inOldStatus old status.
* @param inNewStatus new status.
*/
private void sendConnectedChanged(boolean inOldStatus, boolean inNewStatus) {
mAdapter.connectionStatusChanged(inOldStatus, inNewStatus);
}
private volatile ClassPathXmlApplicationContext mContext;
private volatile Exception mLastFailure;
private final EmitterAdapter mAdapter;
}