org.marketcetera.client.ClientImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.marketcetera.client.ClientImpl.java

Source

package org.marketcetera.client;

import java.beans.ExceptionListener;
import java.math.BigDecimal;
import java.util.*;

import javax.jms.JMSException;
import javax.xml.bind.JAXBException;

import org.apache.commons.lang.ObjectUtils;
import org.marketcetera.client.brokers.BrokerStatus;
import org.marketcetera.client.brokers.BrokersStatus;
import org.marketcetera.client.config.SpringConfig;
import org.marketcetera.client.jms.JmsManager;
import org.marketcetera.client.jms.JmsUtils;
import org.marketcetera.client.jms.OrderEnvelope;
import org.marketcetera.client.jms.ReceiveOnlyHandler;
import org.marketcetera.client.users.UserInfo;
import org.marketcetera.core.ApplicationBase;
import org.marketcetera.core.Util;
import org.marketcetera.core.position.PositionKey;
import org.marketcetera.metrics.ThreadedMetric;
import org.marketcetera.trade.*;
import org.marketcetera.util.except.ExceptUtils;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.util.log.I18NBoundMessage2P;
import org.marketcetera.util.log.I18NBoundMessage4P;
import org.marketcetera.util.log.SLF4JLoggerProxy;
import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.spring.SpringUtils;
import org.marketcetera.util.ws.stateful.ClientContext;
import org.marketcetera.util.ws.tags.SessionId;
import org.marketcetera.util.ws.wrappers.DateWrapper;
import org.marketcetera.util.ws.wrappers.RemoteException;
import org.marketcetera.util.ws.wrappers.RemoteProxyException;
import org.springframework.beans.BeansException;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.listener.SimpleMessageListenerContainer;

/* $License$ */
/**
 * The implementation of Client that connects to the server.
 *
 * @author anshul@marketcetera.com
 * @version $Id$
 * @since 1.0.0
 */
@ClassVersion("$Id$")
public class ClientImpl implements Client, javax.jms.ExceptionListener {

    @Override
    public void sendOrder(OrderSingle inOrderSingle) throws ConnectionException, OrderValidationException {
        Validations.validate(inOrderSingle);

        convertAndSend(inOrderSingle);
    }

    @Override
    public void sendOrder(OrderReplace inOrderReplace) throws ConnectionException, OrderValidationException {
        Validations.validate(inOrderReplace);

        convertAndSend(inOrderReplace);
    }

    @Override
    public void sendOrder(OrderCancel inOrderCancel) throws ConnectionException, OrderValidationException {
        Validations.validate(inOrderCancel);

        convertAndSend(inOrderCancel);
    }

    @Override
    public void sendOrderRaw(FIXOrder inFIXOrder) throws ConnectionException, OrderValidationException {
        Validations.validate(inFIXOrder);
        convertAndSend(inFIXOrder);
    }

    @Override
    public void addReportListener(ReportListener inListener) {
        failIfClosed();
        synchronized (mReportListeners) {
            mReportListeners.addFirst(inListener);
        }
    }

    @Override
    public void removeReportListener(ReportListener inListener) {
        failIfClosed();
        synchronized (mReportListeners) {
            mReportListeners.removeFirstOccurrence(inListener);
        }
    }

    @Override
    public void addBrokerStatusListener(BrokerStatusListener listener) {
        failIfClosed();
        synchronized (mBrokerStatusListeners) {
            mBrokerStatusListeners.addFirst(listener);
        }
    }

    @Override
    public void removeBrokerStatusListener(BrokerStatusListener listener) {
        failIfClosed();
        synchronized (mBrokerStatusListeners) {
            mBrokerStatusListeners.removeFirstOccurrence(listener);
        }
    }

    @Override
    public void addServerStatusListener(ServerStatusListener listener) {
        failIfClosed();
        synchronized (mServerStatusListeners) {
            mServerStatusListeners.addFirst(listener);
        }
    }

    @Override
    public void removeServerStatusListener(ServerStatusListener listener) {
        failIfClosed();
        synchronized (mServerStatusListeners) {
            mServerStatusListeners.removeFirstOccurrence(listener);
        }
    }

    @Override
    public ReportBase[] getReportsSince(Date inDate) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            ReportBaseImpl[] reports = mService.getReportsSince(getServiceContext(), new DateWrapper(inDate));
            return reports == null ? new ReportBase[0] : reports;
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public BigDecimal getEquityPositionAsOf(Date inDate, Equity inEquity) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getEquityPositionAsOf(getServiceContext(), new DateWrapper(inDate), inEquity);
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public Map<PositionKey<Equity>, BigDecimal> getAllEquityPositionsAsOf(Date inDate) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getAllEquityPositionsAsOf(getServiceContext(), new DateWrapper(inDate)).getMap();
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    /* (non-Javadoc)
     * @see org.marketcetera.client.Client#getAllFuturePositionsAsOf(java.util.Date)
     */
    @Override
    public Map<PositionKey<Future>, BigDecimal> getAllFuturePositionsAsOf(Date inDate) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getAllFuturePositionsAsOf(getServiceContext(), new DateWrapper(inDate)).getMap();
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    /* (non-Javadoc)
     * @see org.marketcetera.client.Client#getFuturePositionAsOf(java.util.Date, org.marketcetera.trade.Future)
     */
    @Override
    public BigDecimal getFuturePositionAsOf(Date inDate, Future inFuture) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getFuturePositionAsOf(getServiceContext(), new DateWrapper(inDate), inFuture);
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public BigDecimal getOptionPositionAsOf(Date inDate, Option inOption) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getOptionPositionAsOf(getServiceContext(), new DateWrapper(inDate), inOption);
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public Map<PositionKey<Option>, BigDecimal> getAllOptionPositionsAsOf(Date inDate) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getAllOptionPositionsAsOf(getServiceContext(), new DateWrapper(inDate)).getMap();
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public Map<PositionKey<Option>, BigDecimal> getOptionPositionsAsOf(Date inDate, String... inSymbols)
            throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getOptionPositionsAsOf(getServiceContext(), new DateWrapper(inDate), inSymbols)
                    .getMap();
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public String getUnderlying(String inOptionRoot) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            String value;
            synchronized (mUnderlyingToRootCache) {
                if (mUnderlyingToRootCache.containsKey(inOptionRoot)) {
                    value = mUnderlyingToRootCache.get(inOptionRoot);
                } else {
                    //cache null return values too
                    value = mService.getUnderlying(getServiceContext(), inOptionRoot);
                    mUnderlyingToRootCache.put(inOptionRoot, value);
                }
            }
            return value;
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public Collection<String> getOptionRoots(String inUnderlying) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            Collection<String> value;
            synchronized (mRootToUnderlyingCache) {
                if (mRootToUnderlyingCache.containsKey(inUnderlying)) {
                    value = mRootToUnderlyingCache.get(inUnderlying);
                } else {
                    value = mService.getOptionRoots(getServiceContext(), inUnderlying);
                    mRootToUnderlyingCache.put(inUnderlying, value);
                }
            }
            return value;
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public BrokersStatus getBrokersStatus() throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return mService.getBrokersStatus(getServiceContext());
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    @Override
    public UserInfo getUserInfo(UserID id, boolean useCache) throws ConnectionException {
        failIfClosed();
        UserInfo result;
        synchronized (mUserInfoCache) {
            if (useCache) {
                result = mUserInfoCache.get(id);
                if (result != null) {
                    return result;
                }
            }
            failIfDisconnected();
            try {
                result = mService.getUserInfo(getServiceContext(), id);
            } catch (RemoteException ex) {
                throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
            }
            mUserInfoCache.put(id, result);
        }
        return result;
    }

    @Override
    public synchronized void close() {
        internalClose();
        ClientManager.reset();
        mClosed = true;
    }

    @Override
    public void reconnect() throws ConnectionException {
        reconnect(null);
    }

    @Override
    public synchronized void reconnect(ClientParameters inParameters) throws ConnectionException {
        failIfClosed();
        if (mContext != null) {
            internalClose();
        }
        if (inParameters != null) {
            setParameters(inParameters);
        }
        connect();
    }

    @Override
    public void addExceptionListener(ExceptionListener inListener) {
        failIfClosed();
        synchronized (mExceptionListeners) {
            mExceptionListeners.addFirst(inListener);
        }
    }

    @Override
    public void removeExceptionListener(ExceptionListener inListener) {
        failIfClosed();
        synchronized (mExceptionListeners) {
            mExceptionListeners.removeFirstOccurrence(inListener);
        }
    }

    @Override
    public ClientParameters getParameters() {
        failIfClosed();
        return new ClientParameters(mParameters.getUsername(),
                //hide the password value.
                "*****".toCharArray(), //$NON-NLS-1$
                mParameters.getURL(), mParameters.getHostname(), mParameters.getPort(), mParameters.getIDPrefix());
    }

    @Override
    public Date getLastConnectTime() {
        failIfClosed();
        return mLastConnectTime;
    }

    @Override
    public boolean isCredentialsMatch(String inUsername, char[] inPassword) {
        return (!mClosed) && ObjectUtils.equals(mParameters.getUsername(), inUsername)
                && Arrays.equals(mParameters.getPassword(), inPassword);
    }

    @Override
    public boolean isServerAlive() {
        return ((!mClosed) && mServerAlive);
    }

    /* (non-Javadoc)
     * @see org.marketcetera.client.Client#getUserData()
     */
    @Override
    public Properties getUserData() throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            return Util.propertiesFromString(mService.getUserData(getServiceContext()));
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    /* (non-Javadoc)
     * @see org.marketcetera.client.Client#setUserData(java.util.Properties)
     */
    @Override
    public void setUserData(Properties inProperties) throws ConnectionException {
        failIfClosed();
        failIfDisconnected();
        try {
            mService.setUserData(getServiceContext(), Util.propertiesToString(inProperties));
        } catch (RemoteException ex) {
            throw new ConnectionException(ex, Messages.ERROR_REMOTE_EXECUTION);
        }
    }

    /**
     * Creates an instance given the parameters and connects to the server.
     *
     * @param inParameters the parameters to connect to the server, cannot
     * be null.
     *
     * @throws ConnectionException if there were errors connecting
     * to the server.
     */
    public ClientImpl(ClientParameters inParameters) throws ConnectionException {
        setParameters(inParameters);
        connect();
    }

    // TradeMessage reception; public scope required by Spring.

    public class TradeMessageReceiver implements ReceiveOnlyHandler<TradeMessage> {
        @Override
        public void receiveMessage(TradeMessage inReport) {
            if (inReport instanceof ExecutionReport) {
                notifyExecutionReport((ExecutionReport) inReport);
            } else if (inReport instanceof OrderCancelReject) {
                notifyCancelReject((OrderCancelReject) inReport);
            } else {
                Messages.LOG_RECEIVED_FIX_REPORT.warn(this, ObjectUtils.toString(inReport));
            }
        }
    }

    void notifyExecutionReport(ExecutionReport inReport) {
        SLF4JLoggerProxy.debug(TRAFFIC, "Received Exec Report:{}", inReport); //$NON-NLS-1$
        synchronized (mReportListeners) {
            for (ReportListener listener : mReportListeners) {
                try {
                    listener.receiveExecutionReport(inReport);
                } catch (Throwable t) {
                    Messages.LOG_ERROR_RECEIVE_EXEC_REPORT.warn(this, t, ObjectUtils.toString(inReport));
                    ExceptUtils.interrupt(t);
                }
            }
        }
    }

    void notifyCancelReject(OrderCancelReject inReport) {
        SLF4JLoggerProxy.debug(TRAFFIC, "Received Cancel Reject:{}", inReport); //$NON-NLS-1$
        synchronized (mReportListeners) {
            for (ReportListener listener : mReportListeners) {
                try {
                    listener.receiveCancelReject(inReport);
                } catch (Throwable t) {
                    Messages.LOG_ERROR_RECEIVE_CANCEL_REJECT.warn(this, t, ObjectUtils.toString(inReport));
                    ExceptUtils.interrupt(t);
                }
            }
        }
    }

    // ReceiveOnlyHandler<BrokerStatus>; public scope required by Spring.

    public class BrokerStatusReceiver implements ReceiveOnlyHandler<BrokerStatus> {
        @Override
        public void receiveMessage(BrokerStatus status) {
            notifyBrokerStatus(status);
        }
    }

    void notifyBrokerStatus(BrokerStatus status) {
        SLF4JLoggerProxy.debug(TRAFFIC, "Received Broker Status:{}", status); //$NON-NLS-1$
        synchronized (mBrokerStatusListeners) {
            for (BrokerStatusListener listener : mBrokerStatusListeners) {
                try {
                    listener.receiveBrokerStatus(status);
                } catch (Throwable t) {
                    Messages.LOG_ERROR_RECEIVE_BROKER_STATUS.warn(this, t, ObjectUtils.toString(status));
                    ExceptUtils.interrupt(t);
                }
            }
        }
    }

    void notifyServerStatus(boolean status) {
        SLF4JLoggerProxy.debug(TRAFFIC, "Received Server Status:{}", status); //$NON-NLS-1$
        synchronized (mServerStatusListeners) {
            for (ServerStatusListener listener : mServerStatusListeners) {
                try {
                    listener.receiveServerStatus(status);
                } catch (Throwable t) {
                    Messages.LOG_ERROR_RECEIVE_SERVER_STATUS.warn(this, t, status);
                    ExceptUtils.interrupt(t);
                }
            }
        }
    }

    // javax.jms.ExceptionListener.

    @Override
    public void onException(JMSException e) {
        exceptionThrown(new ConnectionException(e, Messages.ERROR_RECEIVING_JMS_MESSAGE));
    }

    void exceptionThrown(ConnectionException inException) {
        synchronized (mExceptionListeners) {
            for (ExceptionListener l : mExceptionListeners) {
                try {
                    l.exceptionThrown(inException);
                } catch (Exception e) {
                    Messages.LOG_ERROR_NOTIFY_EXCEPTION.warn(this, e, ObjectUtils.toString(inException));
                    ExceptUtils.interrupt(e);
                }
            }
        }
    }

    /**
     * Fetches the next orderID base from server.
     *
     * @return the next orderID base from server.
     *
     * @throws RemoteException if there were communication errors.
     */
    String getNextServerID() throws RemoteException {
        failIfDisconnected();
        return mService.getNextOrderID(getServiceContext());
    }

    /**
     * The 'heart' that produces heartbeats, keeping the connection to
     * the ORS server alive.
     */

    private class Heart extends Thread {
        private volatile boolean mMarked;

        Heart() {
            super(Thread.currentThread().getThreadGroup(), Messages.HEARTBEAT_THREAD_NAME.getText());
            setDaemon(true);
        }

        void markExit() {
            mMarked = true;
        }

        private boolean isMarked() {
            return mMarked;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(mParameters.getHeartbeatInterval());
                } catch (InterruptedException ex) {
                    SLF4JLoggerProxy.debug(HEARTBEATS, "Stopped (interrupted)"); //$NON-NLS-1$
                    markExit();
                    setServerAlive(false);
                    return;
                }
                if (isMarked()) {
                    SLF4JLoggerProxy.debug(HEARTBEATS, "Stopped (marked)"); //$NON-NLS-1$
                    setServerAlive(false);
                    return;
                }
                try {
                    mService.heartbeat(getServiceContext());
                    setServerAlive(true);
                } catch (Exception ex) {
                    setServerAlive(false);
                    if (ExceptUtils.isInterruptException(ex)) {
                        SLF4JLoggerProxy.debug(HEARTBEATS, "Stopped (interrupted)"); //$NON-NLS-1$
                        markExit();
                        return;
                    }
                    SLF4JLoggerProxy.debug(HEARTBEATS, "Failed", ex); //$NON-NLS-1$
                    exceptionThrown(new ConnectionException(ex, Messages.ERROR_HEARTBEAT_FAILED));
                    if (ex instanceof RemoteException) {
                        // We connected to the server, but the session
                        // may have expired: attempt to auto-reconnect
                        // after a short delay to let the server
                        // settle (if it has just restarted). The
                        // delay is random so that not all clients
                        // will try and contact the ORS at the same
                        // time.
                        long delay = (long) (RECONNECT_WAIT_INTERVAL * (0.75 + 1.25 * Math.random()));
                        SLF4JLoggerProxy.debug(HEARTBEATS, "Reconnecting in " + //$NON-NLS-1$
                                delay + "ms..."); //$NON-NLS-1$
                        try {
                            Thread.sleep(delay);
                        } catch (InterruptedException ex2) {
                            SLF4JLoggerProxy.debug(HEARTBEATS, "Stopped (interrupted)"); //$NON-NLS-1$
                            markExit();
                            return;
                        }
                        try {
                            mServiceClient.logout();
                            mServiceClient.login(mParameters.getUsername(), mParameters.getPassword());
                            setServerAlive(true);
                            SLF4JLoggerProxy.debug(HEARTBEATS, "...reconnect succeeded."); //$NON-NLS-1$
                        } catch (Exception ex2) {
                            setServerAlive(false);
                            if (ExceptUtils.isInterruptException(ex2)) {
                                SLF4JLoggerProxy.debug(HEARTBEATS, "Stopped (interrupted)"); //$NON-NLS-1$
                                markExit();
                                return;
                            }
                            SLF4JLoggerProxy.debug(HEARTBEATS, "...reconnect failed.", ex2); //$NON-NLS-1$
                            exceptionThrown(new ConnectionException(ex2, Messages.ERROR_HEARTBEAT_FAILED));
                        }
                    }
                }
                if (isMarked()) {
                    SLF4JLoggerProxy.debug(HEARTBEATS, "Stopped (marked)"); //$NON-NLS-1$
                    setServerAlive(false);
                    return;
                }
            }
        }
    }

    private void internalClose() {
        if (mContext == null) {
            return;
        }
        //Clear all the caches
        synchronized (mUnderlyingToRootCache) {
            mUnderlyingToRootCache.clear();
        }
        synchronized (mRootToUnderlyingCache) {
            mRootToUnderlyingCache.clear();
        }
        synchronized (mUserInfoCache) {
            mUserInfoCache.clear();
        }
        // Close the heartbeat generator first so that it won't
        // re-create a JMS connection during subsequent shutdown. In
        // fact, the generator will normally shut down the JMS
        // connection before it terminates.
        if (mHeart != null) {
            mHeart.markExit();
            mHeart.interrupt();
            try {
                mHeart.join();
            } catch (InterruptedException ex) {
                SLF4JLoggerProxy.debug(this, "Error when joining with heartbeat thread", ex); //$NON-NLS-1$
                ExceptUtils.interrupt(ex);
            }
        }
        setServerAlive(false);
        try {
            if (mServiceClient != null) {
                mServiceClient.logout();
            }
        } catch (Exception ex) {
            SLF4JLoggerProxy.debug(this, "Error when closing web service client", ex); //$NON-NLS-1$
            ExceptUtils.interrupt(ex);
        } finally {
            try {
                if (mContext != null) {
                    mContext.close();
                }
            } catch (Exception ex) {
                SLF4JLoggerProxy.debug(this, "Error when closing context", ex); //$NON-NLS-1$
                ExceptUtils.interrupt(ex);
            } finally {
                setContext(null);
            }
        }
    }

    private void connect() throws ConnectionException {
        if (mParameters.getURL() == null || mParameters.getURL().trim().isEmpty()) {
            throw new ConnectionException(Messages.CONNECT_ERROR_NO_URL);
        }
        if (mParameters.getUsername() == null || mParameters.getUsername().trim().isEmpty()) {
            throw new ConnectionException(Messages.CONNECT_ERROR_NO_USERNAME);
        }
        if (mParameters.getHostname() == null || mParameters.getHostname().trim().isEmpty()) {
            throw new ConnectionException(Messages.CONNECT_ERROR_NO_HOSTNAME);
        }
        if (mParameters.getPort() < 1 || mParameters.getPort() > 0xFFFF) {
            throw new ConnectionException(
                    new I18NBoundMessage1P(Messages.CONNECT_ERROR_INVALID_PORT, mParameters.getPort()));
        }
        try {
            StaticApplicationContext parentCtx = new StaticApplicationContext();
            SpringUtils.addStringBean(parentCtx, "brokerURL", //$NON-NLS-1$
                    mParameters.getURL());
            SpringUtils.addStringBean(parentCtx, "runtimeUsername", mParameters.getUsername()); //$NON-NLS-1$
            SpringUtils.addStringBean(parentCtx, "runtimePassword", mParameters == null //$NON-NLS-1$
                    ? null
                    : String.valueOf(mParameters.getPassword()));
            parentCtx.refresh();
            AbstractApplicationContext ctx;
            try {
                ctx = new FileSystemXmlApplicationContext(
                        new String[] { "file:" + ApplicationBase.CONF_DIR + "client.xml" }, //$NON-NLS-1$
                        parentCtx);
            } catch (BeansException e) {
                ctx = new ClassPathXmlApplicationContext(new String[] { "client.xml" }, //$NON-NLS-1$
                        parentCtx);
            }
            ctx.registerShutdownHook();
            ctx.start();
            setContext(ctx);
            SpringConfig cfg = SpringConfig.getSingleton();
            if (cfg == null) {
                throw new ConnectionException(Messages.CONNECT_ERROR_NO_CONFIGURATION);
            }

            mServiceClient = new org.marketcetera.util.ws.stateful.Client(mParameters.getHostname(),
                    mParameters.getPort(), ClientVersion.APP_ID);
            mServiceClient.login(mParameters.getUsername(), mParameters.getPassword());
            mService = mServiceClient.getService(Service.class);

            mJmsMgr = new JmsManager(cfg.getIncomingConnectionFactory(), cfg.getOutgoingConnectionFactory(), this);
            startJms();
            mServerAlive = true;
            notifyServerStatus(true);

            mHeart = new Heart();
            mHeart.start();

            ClientIDFactory idFactory = new ClientIDFactory(mParameters.getIDPrefix(), this);
            idFactory.init();
            Factory.getInstance().setOrderIDFactory(idFactory);
        } catch (Throwable t) {
            internalClose();
            ExceptUtils.interrupt(t);
            if (t.getCause() instanceof RemoteProxyException) {
                RemoteProxyException ex = (RemoteProxyException) t.getCause();
                if (IncompatibleComponentsException.class.getName().equals(ex.getServerName())) {
                    throw new ConnectionException(t,
                            new I18NBoundMessage1P(Messages.ERROR_CONNECT_INCOMPATIBLE_DEDUCED, ex.getMessage()));
                }
            } else if (t.getCause() instanceof IncompatibleComponentsException) {
                IncompatibleComponentsException ex = (IncompatibleComponentsException) t.getCause();
                throw new ConnectionException(t, new I18NBoundMessage2P(Messages.ERROR_CONNECT_INCOMPATIBLE_DIRECT,
                        ClientVersion.APP_ID, ex.getServerVersion()));
            }
            throw new ConnectionException(t,
                    new I18NBoundMessage4P(Messages.ERROR_CONNECT_TO_SERVER, mParameters.getURL(),
                            mParameters.getUsername(), mParameters.getHostname(), mParameters.getPort()));
        }
        mLastConnectTime = new Date();
    }

    private void setContext(AbstractApplicationContext inContext) {
        mContext = inContext;
    }

    private void convertAndSend(Order inOrder) throws ConnectionException {
        ThreadedMetric.event("client-OUT", //$NON-NLS-1$ 
                inOrder instanceof OrderBase ? ((OrderBase) inOrder).getOrderID() : null);
        failIfClosed();
        SLF4JLoggerProxy.debug(TRAFFIC, "Sending order:{}", inOrder); //$NON-NLS-1$
        try {
            if (mToServer == null) {
                throw new ClientInitException(Messages.NOT_CONNECTED_TO_SERVER);
            }
            failIfDisconnected();
            SpringConfig cfg = SpringConfig.getSingleton();
            Collection<OrderModifier> orderModifiers = cfg.getOrderModifiers();
            for (OrderModifier modifier : orderModifiers) {
                modifier.modify(inOrder);
            }
            mToServer.convertAndSend(new OrderEnvelope(inOrder, getSessionId()));
        } catch (Exception e) {
            ConnectionException exception;
            exception = new ConnectionException(e,
                    new I18NBoundMessage1P(Messages.ERROR_SEND_MESSAGE, ObjectUtils.toString(inOrder)));
            Messages.LOG_ERROR_SEND_EXCEPTION.warn(this, exception, ObjectUtils.toString(inOrder));
            ExceptUtils.interrupt(e);
            exceptionThrown(exception);
            throw exception;
        }
    }

    /**
     * Checks to see if the client is closed and fails if the client
     * is closed.
     *
     * @throws IllegalStateException if the client is closed.
     */
    private void failIfClosed() throws IllegalStateException {
        if (mClosed) {
            throw new IllegalStateException(Messages.CLIENT_CLOSED.getText());
        }
    }

    /**
     * Asserts that the client's connection to the server is alive;
     * fails otherwise.
     *
     * @throws IllegalStateException if the server connection is dead.
     */
    private void failIfDisconnected() throws IllegalStateException {
        if (!isServerAlive()) {
            throw new IllegalStateException(Messages.SERVER_CONNECTION_DEAD.getText());
        }
    }

    private ClientContext getServiceContext() {
        return mServiceClient.getContext();
    }

    SessionId getSessionId() {
        return getServiceContext().getSessionId();
    }

    /**
     * Sets the client parameters value.
     *
     * @param inParameters the client parameters, cannot be null.
     */
    private void setParameters(ClientParameters inParameters) {
        if (inParameters == null) {
            throw new NullPointerException();
        }
        mParameters = inParameters;
    }

    private void stopJms() {
        if (mToServer == null) {
            return;
        }
        try {
            if (mTradeMessageListener != null) {
                mTradeMessageListener.shutdown();
            }
        } catch (Exception ex) {
            SLF4JLoggerProxy.debug(this, "Error when closing trade message listener", ex); //$NON-NLS-1$
            ExceptUtils.interrupt(ex);
        } finally {
            try {
                if (mBrokerStatusListener != null) {
                    mBrokerStatusListener.shutdown();
                }
            } catch (Exception ex) {
                SLF4JLoggerProxy.debug(this, "Error when closing broker status listener", ex); //$NON-NLS-1$
                ExceptUtils.interrupt(ex);
            } finally {
                mToServer = null;
            }
        }
    }

    private void startJms() throws JAXBException {
        if (mToServer != null) {
            return;
        }
        mTradeMessageListener = mJmsMgr.getIncomingJmsFactory().registerHandlerTMX(new TradeMessageReceiver(),
                JmsUtils.getReplyTopicName(getSessionId()), true);
        mBrokerStatusListener = mJmsMgr.getIncomingJmsFactory().registerHandlerBSX(new BrokerStatusReceiver(),
                Service.BROKER_STATUS_TOPIC, true);
        mToServer = mJmsMgr.getOutgoingJmsFactory().createJmsTemplateX(Service.REQUEST_QUEUE, false);
    }

    /**
     * Sets the server connection status. If the status changed, the
     * registered callbacks are invoked.
     *
     * @param serverAlive True means the server connection is alive.
     */
    private void setServerAlive(boolean serverAlive) {
        if (mServerAlive == serverAlive) {
            return;
        }
        if (serverAlive) {
            try {
                startJms();
            } catch (JAXBException ex) {
                exceptionThrown(new ConnectionException(ex, Messages.ERROR_CREATING_JMS_CONNECTION));
                return;
            }
        } else {
            stopJms();
        }
        mServerAlive = serverAlive;
        notifyServerStatus(isServerAlive());
    }

    private volatile AbstractApplicationContext mContext;
    private volatile JmsManager mJmsMgr;
    private volatile SimpleMessageListenerContainer mTradeMessageListener;
    private volatile SimpleMessageListenerContainer mBrokerStatusListener;
    private volatile JmsOperations mToServer;
    private volatile ClientParameters mParameters;
    private volatile boolean mClosed = false;
    private volatile boolean mServerAlive = false;
    private final Deque<ReportListener> mReportListeners = new LinkedList<ReportListener>();
    private final Deque<BrokerStatusListener> mBrokerStatusListeners = new LinkedList<BrokerStatusListener>();
    private final Deque<ServerStatusListener> mServerStatusListeners = new LinkedList<ServerStatusListener>();
    private final Deque<ExceptionListener> mExceptionListeners = new LinkedList<ExceptionListener>();
    private Date mLastConnectTime;
    private final Map<UserID, UserInfo> mUserInfoCache = new HashMap<UserID, UserInfo>();
    private final Map<String, String> mUnderlyingToRootCache = new HashMap<String, String>();
    private final Map<String, Collection<String>> mRootToUnderlyingCache = new HashMap<String, Collection<String>>();

    private static final long RECONNECT_WAIT_INTERVAL = 30000;

    private volatile org.marketcetera.util.ws.stateful.Client mServiceClient;
    private Service mService;
    private Heart mHeart;

    private static final String TRAFFIC = ClientImpl.class.getPackage().getName() + ".traffic"; //$NON-NLS-1$
    private static final String HEARTBEATS = ClientImpl.class.getPackage().getName() + ".heartbeats"; //$NON-NLS-1$
}