package org.marketcetera.modules.remote.emitter; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.log.I18NMessage0P; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.module.*; import javax.management.*; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.ConcurrentHashMap; /* $License$ */ /** * The remote emitter module that emits data received from the * remote receiver module. * <p> * Module Features * <table> * <tr><th>Capabilities</th><td>Data Emitter</td></tr> * <tr><th>DataFlow Request Parameters</th><td>None</td></tr> * <tr><th>Stops data flows</th><td>No</td></tr> * <tr><th>Start Operation</th><td>Connects to the remote receiver</td></tr> * <tr><th>Stop Operation</th><td>Disconnects from remote receiver</td></tr> * <tr><th>Management Interface</th><td>{@link EmitterModuleMXBean}</td></tr> * <tr><th>MX Notification</th><td>{@link AttributeChangeNotification} * whenever {@link #isConnected()} changes. </td></tr> * </table> * * @author anshul@marketcetera.com * @version $Id: EmitterModule.java 16154 2012-07-14 16:34:05Z colin $ * @since 1.5.0 */ @ClassVersion("$Id: EmitterModule.java 16154 2012-07-14 16:34:05Z colin $") class EmitterModule extends Module implements DataEmitter, EmitterModuleMXBean, NotificationEmitter, EmitterAdapter { @Override public void requestData(DataRequest inRequest, DataEmitterSupport inSupport) throws RequestDataException { mRequests.put(inSupport.getRequestID(), inSupport); } @Override public void cancel(DataFlowID inFlowID, RequestID inRequestID) { mRequests.remove(inRequestID); } @Override public String getURL() { return mURL; } @Override public void setURL(String inURL) { failIfStarted(Messages.ILLEGAL_STATE_CHANGE_URL); mURL = inURL; } @Override public String getUsername() { return mUsername; } @Override public void setUsername(String inUsername) { failIfStarted(Messages.ILLEGAL_STATE_CHANGE_USERNAME); mUsername = inUsername; } @Override public void setPassword(String inPassword) { failIfStarted(Messages.ILLEGAL_STATE_CHANGE_PASSWORD); mPassword = inPassword; } @Override public boolean isConnected() { return getState().isStarted() && mDataEmitter != null && mDataEmitter.isConnected(); } @Override public String getLastFailure() { if(mDataEmitter != null && mDataEmitter.getLastFailure() != null) { return mDataEmitter.getLastFailure().toString(); } return null; } @Override public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { mNotifySupport.removeNotificationListener(listener, filter, handback); } @Override public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { mNotifySupport.addNotificationListener(listener, filter, handback); } @Override public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { mNotifySupport.removeNotificationListener(listener); } @Override public MBeanNotificationInfo[] getNotificationInfo() { return mNotifySupport.getNotificationInfo(); } /** * Creates an instance. * * @param inURN the module instance's URN. */ protected EmitterModule(ModuleURN inURN) { super(inURN, true); } @Override protected void preStart() throws ModuleException { //Check if the broker URL is supplied String url = getURL(); if(url == null) { throw new ModuleException(Messages.START_FAIL_NO_URL); } try { mDataEmitter = new RemoteDataEmitter(url, getUsername(), mPassword, this); } catch(Exception e) { throw new ModuleException(e, Messages.ERROR_STARTING_MODULE); } } @Override protected void preStop() throws ModuleException { mDataEmitter.close(); mDataEmitter = null; } /** * This method is invoked when an object is received from the remote * receiver. * <p> * The received object is delivered to all the data flows sequentially. * * @param inObject the received object. */ @Override public void receiveData(Object inObject) { for(DataEmitterSupport support: mRequests.values()) { support.send(inObject); } } /** * Verifies if the module is not started. * * @param inMessage the message to use when the module is started. * * @throws IllegalStateException if the module is started. */ private void failIfStarted(I18NMessage0P inMessage) { if(getState().isStarted()) { throw new IllegalStateException(inMessage.getText()); } } /** * Sends an attribute change notification for change in the * {@link #isConnected()} value. * * @param inOldValue the old attribute value. * @param inNewValue the new attribute value. */ @Override public void connectionStatusChanged(boolean inOldValue, boolean inNewValue) { if (getState().isStarted() || getState() == ModuleState.STARTING || getState() == ModuleState.STOPPING) { SLF4JLoggerProxy.debug(this, "Sending attrib changed from {} to {}", //$NON-NLS-1$ inOldValue, inNewValue); mNotifySupport.sendNotification(new AttributeChangeNotification( getURN().toString(), mSequence.getAndIncrement(), System.currentTimeMillis(), Messages.ATTRIB_CHANGE_NOTIFICATION.getText(), "Connected", //$NON-NLS-1$ "boolean", //$NON-NLS-1$ inOldValue, inNewValue)); } } private volatile String mURL; private volatile String mUsername; private volatile String mPassword; private volatile RemoteDataEmitter mDataEmitter; private final AtomicLong mSequence = new AtomicLong(); private final NotificationBroadcasterSupport mNotifySupport = new NotificationBroadcasterSupport(new MBeanNotificationInfo( new String[]{AttributeChangeNotification.ATTRIBUTE_CHANGE}, AttributeChangeNotification.class.getName(), Messages.ATTRIB_CHANGE_NOTIFICATION.getText())); private final Map<RequestID, DataEmitterSupport> mRequests = new ConcurrentHashMap<RequestID, DataEmitterSupport>(); }