package org.marketcetera.photon.actions; import java.beans.ExceptionListener; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.preferences.ScopedPreferenceStore; import org.eclipse.ui.progress.UIJob; import org.marketcetera.client.BrokerStatusListener; import org.marketcetera.client.Client; import org.marketcetera.client.ClientInitException; import org.marketcetera.client.ClientManager; import org.marketcetera.client.ConnectionException; import org.marketcetera.client.brokers.BrokerStatus; import org.marketcetera.client.brokers.BrokersStatus; import org.marketcetera.client.rpc.RpcClientFactory; import org.marketcetera.client.rpc.RpcClientParameters; import org.marketcetera.core.notifications.Notification; import org.marketcetera.core.notifications.NotificationManager; import org.marketcetera.core.notifications.ServerStatusListener; import org.marketcetera.photon.BrokerManager; import org.marketcetera.photon.Messages; import org.marketcetera.photon.PhotonPlugin; import org.marketcetera.photon.PhotonPreferences; import org.marketcetera.photon.core.ICredentials; import org.marketcetera.photon.core.ICredentialsService; import org.marketcetera.photon.core.ICredentialsService.IAuthenticationHelper; import org.marketcetera.photon.core.ILogoutService; import org.marketcetera.photon.ui.ServerStatusIndicator; import org.marketcetera.util.log.I18NBoundMessage; import org.marketcetera.util.log.I18NMessage; import org.marketcetera.util.log.I18NMessage0P; import org.marketcetera.util.log.I18NMessage1P; import org.marketcetera.util.misc.ClassVersion; /* $License$ */ /** * Collects user name and password and reconnects Photon to ORS. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: ReconnectServerJob.java 16882 2014-04-17 01:52:17Z colin $ * @since 1.5.0 */ @ClassVersion("$Id: ReconnectServerJob.java 16882 2014-04-17 01:52:17Z colin $") public class ReconnectServerJob extends UIJob { private static final AtomicBoolean sScheduled = new AtomicBoolean(); /** * Constructor. */ public ReconnectServerJob() { super(Messages.RECONNECT_SERVER_JOB_NAME.getText()); setUser(true); // don't visualize progress for this job since it's modal setSystem(true); } @Override public boolean shouldSchedule() { // fails if already scheduled return sScheduled.compareAndSet(false, true); } private final static Runnable sClientCloser = new Runnable() { @Override public void run() { if (sScheduled.compareAndSet(false, true)) { try { ClientManager.getInstance().close(); } catch (ClientInitException e) { // ignore } finally { sScheduled.set(false); } } } }; @Override public IStatus runInUIThread(IProgressMonitor monitor) { try { // load connection properties ScopedPreferenceStore prefs = PhotonPlugin.getDefault().getPreferenceStore(); final String url = prefs.getString(PhotonPreferences.JMS_URL); final String hostname = prefs.getString(PhotonPreferences.WEB_SERVICE_HOST); final int port = prefs.getInt(PhotonPreferences.WEB_SERVICE_PORT); final String idPrefix = prefs.getString(PhotonPreferences.ORDER_ID_PREFIX); // try to login ICredentialsService credentialsService = PhotonPlugin.getDefault().getCredentialsService(); ILogoutService logoutService = PhotonPlugin.getDefault().getLogoutService(); logoutService.addLogoutRunnable(sClientCloser); boolean success = credentialsService.authenticateWithCredentials(new IAuthenticationHelper() { @Override public boolean authenticate(ICredentials credentials) { final RpcClientParameters parameters = new RpcClientParameters(credentials.getUsername(), credentials.getPassword() == null ? null : credentials.getPassword().toCharArray(), url, hostname, port, idPrefix); IRunnableWithProgress op = new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException,InterruptedException { // Invalidate position engine, it will be recreated if trading history is retrieved. PhotonPlugin.getDefault().disposePositionEngine(); ServerStatusIndicator.setDisconnected(); PhotonPlugin.getDefault().setSessionStartTime(null); // connect try { Client client; // if already initialized, reconnect if(ClientManager.isInitialized()) { ClientManager.getInstance().reconnect(parameters); client = ClientManager.getInstance(); } else { // switch us to use the RPC client instead of the WS client ClientManager.setClientFactory(new RpcClientFactory()); // first time initialization ClientManager.init(parameters); client = ClientManager.getInstance(); // add listeners client.addExceptionListener(new ExceptionListener() { @Override public void exceptionThrown(Exception e) { // When disconnected, client sends continual notifications, so we want to avoid cluttering the console. if(getMessage(e) != org.marketcetera.client.Messages.ERROR_HEARTBEAT_FAILED) { PhotonPlugin.getMainConsoleLogger().error(Messages.CLIENT_EXCEPTION.getText(), e); } } private I18NMessage getMessage(Exception e) { if(e instanceof ConnectionException) { I18NBoundMessage bound = ((ConnectionException)e).getI18NBoundMessage(); if(bound != null) { return bound.getMessage(); } } return null; } }); ServerNotificationListener serverNotificationListener = new ServerNotificationListener(); // Simulate initial connection notification that we missed because it was issued during initialization, above. serverNotificationListener.receiveServerStatus(true); client.addServerStatusListener(serverNotificationListener); client.addReportListener(PhotonPlugin.getDefault().getPhotonController()); client.addBrokerStatusListener(new BrokerNotificationListener(client)); } // Refresh Broker Status try { asyncUpdateBrokers(client.getBrokersStatus()); } catch (ConnectionException e) { throw new InvocationTargetException(e); } } catch (ConnectionException e) { throw new InvocationTargetException(e); } catch (ClientInitException e) { throw new InvocationTargetException(e); } } }; try { new ProgressMonitorDialog(getDisplay().getActiveShell()).run(true, false, op); // CD 20130820 commented out in lieu of replacing the call below in receiveServerStatus (one or the other, not both) // CD 20140416 restored new RetrieveTradingHistoryJob().schedule(); return true; } catch (InterruptedException e) { // Intentionally not restoring the interrupt status since this is the main UI thread where it will be ignored. Messages.RECONNECT_SERVER_JOB_CONNECTION_FAILED.error(ReconnectServerJob.this, e); return false; } catch (InvocationTargetException e) { Throwable realException = e.getTargetException(); String message = realException.getLocalizedMessage(); if (message == null) { message = Messages.RECONNECT_SERVER_JOB_CONNECTION_FAILED.getText(); } MessageDialog.openError(getDisplay().getActiveShell(), Messages.RECONNECT_SERVER_JOB_ERROR_DIALOG_TITLE.getText(), message); Messages.RECONNECT_SERVER_JOB_CONNECTION_FAILED.error(ReconnectServerJob.this, realException); return false; } } }); // reconnect market data, too, if so requested if(reconnectMarketData) { PhotonPlugin.getDefault().reconnectMarketDataFeed(); } return success ? Status.OK_STATUS : Status.CANCEL_STATUS; } finally { sScheduled.set(false); } } /** * Get the reconnectMarketData value. * * @return a <code>boolean</code> value */ public boolean getReconnectMarketData() { return reconnectMarketData; } /** * Sets the reconnectMarketData value. * * @param inReconnectMarketData a <code>boolean</code> value */ public void setReconnectMarketData(boolean inReconnectMarketData) { reconnectMarketData = inReconnectMarketData; } private static void asyncUpdateBrokers(final BrokersStatus brokersStatus) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { BrokerManager.getCurrent().setBrokersStatus(brokersStatus); } }); } /** * Handles broker status updates. */ @ClassVersion("$Id: ReconnectServerJob.java 16882 2014-04-17 01:52:17Z colin $") static final class BrokerNotificationListener implements BrokerStatusListener { private final Client mClient; /** * @param client * Client instance to use to refresh brokers status */ public BrokerNotificationListener(Client client) { mClient = client; } @Override public void receiveBrokerStatus(final BrokerStatus status) { try { I18NMessage0P subject; I18NMessage1P details; if (status.getLoggedOn()) { subject = Messages.BROKER_NOTIFICATION_BROKER_AVAILABLE; details = Messages.BROKER_NOTIFICATION_BROKER_AVAILABLE_DETAILS; } else { subject = Messages.BROKER_NOTIFICATION_BROKER_UNAVAILABLE; details = Messages.BROKER_NOTIFICATION_BROKER_UNAVAILABLE_DETAILS; } NotificationManager.getNotificationManager().publish( Notification.high(subject.getText(), details .getText(Messages.BROKER_LABEL_PATTERN.getText( status.getName(), status.getId())), getClass().getName())); asyncUpdateBrokers(mClient.getBrokersStatus()); } catch (Exception e) { Messages.BROKER_NOTIFICATION_BROKER_ERROR_OCCURRED.error(this, e, status); } } } /** * Handles server status updates. */ @ClassVersion("$Id: ReconnectServerJob.java 16882 2014-04-17 01:52:17Z colin $") private class ServerNotificationListener implements ServerStatusListener { @Override public void receiveServerStatus(boolean status) { try { String text; if (status) { ServerStatusIndicator.setConnected(); text = Messages.SERVER_NOTIFICATION_SERVER_ALIVE.getText(); // CD 20120915 Fix rolled back due to performance problems for high // volume installations // CD 20130820 Replaced (still vulnerable to high-volume problems) // CD 20140416 Removed // PhotonPlugin.getDefault().disposePositionEngine(); // new RetrieveTradingHistoryJob().schedule(); } else { ServerStatusIndicator.setDisconnected(); text = Messages.SERVER_NOTIFICATION_SERVER_DEAD.getText(); } // notifications are not necessary if the reconnect job is // running if (!sScheduled.get()) { NotificationManager.getNotificationManager().publish(Notification.high(text, text, getClass().getName())); } } catch (Exception e) { Messages.SERVER_NOTIFICATION_SERVER_ERROR_OCCURRED.error(this, e); } } } /** * */ private boolean reconnectMarketData = false; }