All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.marketcetera.marketdata.manual.ManualFeedModule Maven / Gradle / Ivy

The newest version!
package org.marketcetera.marketdata.manual;

import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.marketcetera.core.PlatformServices;
import org.marketcetera.event.Event;
import org.marketcetera.marketdata.Capability;
import org.marketcetera.marketdata.FeedStatus;
import org.marketcetera.marketdata.FeedStatusRequest;
import org.marketcetera.marketdata.MarketDataCapabilityBroadcaster;
import org.marketcetera.marketdata.MarketDataRequest;
import org.marketcetera.marketdata.MarketDataRequestBuilder;
import org.marketcetera.marketdata.MarketDataStatus;
import org.marketcetera.marketdata.MarketDataStatusBroadcaster;
import org.marketcetera.marketdata.service.MarketDataService;
import org.marketcetera.module.AutowiredModule;
import org.marketcetera.module.DataEmitter;
import org.marketcetera.module.DataEmitterSupport;
import org.marketcetera.module.DataFlowID;
import org.marketcetera.module.DataRequest;
import org.marketcetera.module.Module;
import org.marketcetera.module.ModuleException;
import org.marketcetera.module.ModuleURN;
import org.marketcetera.module.RequestDataException;
import org.marketcetera.module.RequestID;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.util.log.I18NBoundMessage2P;
import org.marketcetera.util.log.SLF4JLoggerProxy;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;

/* $License$ */

/**
 * Supplies market data supplied by an upstream module.
 *
 * @author Colin DuPlantis
 * @version $Id$
 * @since $Release$
 */
@AutowiredModule
public class ManualFeedModule
        extends Module
        implements DataEmitter
{
    /**
     * Get the instance value.
     *
     * @return a ManualFeedModule value
     */
    public static ManualFeedModule getInstance()
    {
        return instance;
    }
    /**
     * Emit the given events to the data flow for the given request id.
     *
     * @param inRequestId a String value or null to submit to all data flows
     * @param inEvents a Collection<Event> value
     */
    public void emit(String inRequestId,
                     Collection inEvents)
    {
        if(inRequestId == null) {
            SLF4JLoggerProxy.debug(this,
                                   "No request id specified, submitting to all data flows");
            for(MarketDataRequestData request : requestsByRequestId.asMap().values()) {
                for(Event event : inEvents) {
                    emit(request.getDataEmitterSupport(),
                         event);
                }
            }
        } else {
            MarketDataRequestData requestData = requestsByRequestId.getIfPresent(inRequestId);
            if(requestData == null) {
                SLF4JLoggerProxy.warn(this,
                                      "No request with id {}, cannot emit events",
                                      inRequestId);
            } else {
                for(Event event : inEvents) {
                    emit(requestData.getDataEmitterSupport(),
                         event);
                }
            }
        }
    }
    /**
     * Emit the given event to the data flow for the given request id.
     *
     * @param inRequestId a String value or null to submit to all data flows
     * @param inEvent a Event value
     */
    public void emit(String inRequestId,
                     Event inEvent)
    {
        if(inRequestId == null) {
            SLF4JLoggerProxy.debug(this,
                                   "No request id specified, submitting to all data flows");
            for(MarketDataRequestData request : requestsByRequestId.asMap().values()) {
                emit(request.getDataEmitterSupport(),
                     inEvent);
            }
        } else {
            MarketDataRequestData requestData = requestsByRequestId.getIfPresent(inRequestId);
            if(requestData == null) {
                SLF4JLoggerProxy.warn(this,
                                      "No request with id {}, cannot emit events",
                                      inRequestId);
            } else {
                emit(requestData.getDataEmitterSupport(),
                     inEvent);
            }
        }
    }
    /**
     * Get the known market data requests indexed by the request id associated with the request.
     *
     * @return a BiMap<String,MarketDataRequest> value
     */
    public BiMap getRequests()
    {
        BiMap requests = HashBiMap.create();
        for(Map.Entry entry : requestsByRequestId.asMap().entrySet()) {
            requests.put(entry.getKey(),
                         entry.getValue().getMarketDataRequest());
        }
        return requests;
    }
    /* (non-Javadoc)
     * @see org.marketcetera.module.DataEmitter#requestData(org.marketcetera.module.DataRequest, org.marketcetera.module.DataEmitterSupport)
     */
    @Override
    public void requestData(DataRequest inRequest,
                            DataEmitterSupport inSupport)
            throws RequestDataException
    {
        SLF4JLoggerProxy.debug(this,
                               "Received a data flow request: {}", //$NON-NLS-1$
                               inRequest);
        Object payload = inRequest.getData();
        try {
            if(payload == null) {
                throw new RequestDataException(Messages.DATA_REQUEST_PAYLOAD_REQUIRED);
            }
            if(payload instanceof String) {
                String stringPayload = (String)payload;
                try {
                    doMarketDataRequest(MarketDataRequestBuilder.newRequestFromString(stringPayload),
                                        inRequest,
                                        inSupport);
                } catch (Exception e) {
                    throw new RequestDataException(new I18NBoundMessage2P(Messages.INVALID_DATA_REQUEST_PAYLOAD,
                                                                          stringPayload,
                                                                          ExceptionUtils.getRootCause(e)));
                }
            } else if(payload instanceof MarketDataRequest) {
                doMarketDataRequest((MarketDataRequest)payload,
                                    inRequest,
                                    inSupport);
            } else if(payload instanceof FeedStatusRequest) {
                doFeedStatusRequest((FeedStatusRequest)payload,
                                    inRequest,
                                    inSupport);
            } else {
                throw new RequestDataException(new I18NBoundMessage1P(Messages.UNSUPPORTED_DATA_REQUEST_PAYLOAD,
                                                                      payload.getClass().getSimpleName()));
            }
        } catch (Exception e) {
            PlatformServices.handleException(this,
                                             "Market data request failed",
                                             e);
            throw new RequestDataException(e);
        }
    }
    /* (non-Javadoc)
     * @see org.marketcetera.module.DataEmitter#cancel(org.marketcetera.module.DataFlowID, org.marketcetera.module.RequestID)
     */
    @Override
    public void cancel(DataFlowID inFlowId,
                       RequestID inRequestID)
    {
        MarketDataRequestData requestData = requestsByDataFlowId.getIfPresent(inFlowId);
        requestsByDataFlowId.invalidate(inFlowId);
        if(requestData != null) {
            SLF4JLoggerProxy.debug(this,
                                   "Canceling data flow {} with market data request id {}", //$NON-NLS-1$
                                   inFlowId,
                                   requestData);
            requestsByRequestId.invalidate(requestData.requestId);
        }
    }
    /* (non-Javadoc)
     * @see org.marketcetera.module.Module#preStart()
     */
    @Override
    protected void preStart()
            throws ModuleException
    {
        for(MarketDataCapabilityBroadcaster broadcaster : capabilityBroadcasters) {
            broadcaster.reportCapability(EnumSet.allOf(Capability.class));
        }
        updateFeedStatus(FeedStatus.AVAILABLE);
    }
    /* (non-Javadoc)
     * @see org.marketcetera.module.Module#preStop()
     */
    @Override
    protected void preStop()
            throws ModuleException
    {
        requestsByDataFlowId.invalidateAll();
        requestsByRequestId.invalidateAll();
        updateFeedStatus(FeedStatus.OFFLINE);
    }
    /**
     * Create a new ManualFeedModule instance.
     *
     * @param inUrn a ModuleURN value
     */
    ManualFeedModule(ModuleURN inUrn)
    {
        super(inUrn,
              false);
        instance = this;
    }
    /**
     * Update the feed status to the new given value.
     *
     * @param inNewStatus a FeedStatus value
     */
    private void updateFeedStatus(FeedStatus inNewStatus)
    {
        if(inNewStatus == feedStatus) {
            return;
        }
        SLF4JLoggerProxy.debug(this,
                               "Updating feed status from {} to {}",
                               feedStatus,
                               inNewStatus);
        feedStatus = inNewStatus;
        MarketDataStatus marketDataStatus = new MarketDataStatus() {
            @Override
            public FeedStatus getFeedStatus()
            {
                return feedStatus;
            }
            @Override
            public String getProvider()
            {
                return ManualFeedModuleFactory.IDENTIFIER;
            }
        };
        marketDataService.reportMarketDataStatus(marketDataStatus);
        for(FeedStatusRequestData feedStatusRequestData : feedStatusRequestDataByDataFlowId.asMap().values()) {
            try {
                feedStatusRequestData.getDataEmitterSupport().send(marketDataStatus);
            } catch (Exception e) {
                SLF4JLoggerProxy.warn(this,
                                      e);
            }
        }
        for(MarketDataStatusBroadcaster broadcaster : statusBroadcasters) {
            broadcaster.reportMarketDataStatus(marketDataStatus);
        }
    }
    /**
     * Execute a feed status request with the given attributes.
     *
     * @param inPayload a FeedStatusRequest value
     * @param inRequest a DataRequest value
     * @param inSupport a DataEmitterSupport value
     */
    private void doFeedStatusRequest(FeedStatusRequest inPayload,
                                     DataRequest inRequest,
                                     DataEmitterSupport inSupport)
    {
        FeedStatusRequestData metaData = new FeedStatusRequestData(inSupport);
        feedStatusRequestDataByDataFlowId.put(inSupport.getFlowID(),
                                              metaData);
    }
    /**
     * Emit the given event to the given data flow.
     *
     * @param inDataEmitter a DataEmitterSupport value
     * @param inEvent an Event value
     */
    private void emit(DataEmitterSupport inDataEmitter,
                      Event inEvent)
    {
        SLF4JLoggerProxy.trace(this,
                               "Sending {} to {}",
                               inEvent,
                               inDataEmitter.getFlowID());
        inDataEmitter.send(inEvent);
    }
    /**
     * Perform the market data request
     *
     * @param inPayload a MarketDataRequest value
     * @param inRequest a DataRequest value
     * @param inSupport a DataEmitterSupport value
     */
    private void doMarketDataRequest(MarketDataRequest inPayload,
                                     DataRequest inRequest,
                                     DataEmitterSupport inSupport)
    {
        String id = inPayload.getRequestId();
        MarketDataRequestData requestData = new MarketDataRequestData(inSupport,
                                                  id,
                                                  inPayload);
        requestsByRequestId.put(id,
                                requestData);
        requestsByDataFlowId.put(inSupport.getFlowID(),
                                 requestData);
    }
    /**
     * Provides common behavior for data flow requests.
     *
     * @author Colin DuPlantis
     * @version $Id$
     * @since $Release$
     */
    private static abstract class AbstractRequestData
    {
        /**
         * Get the dataEmitterSupport value.
         *
         * @return a DataEmitterSupport value
         */
        protected DataEmitterSupport getDataEmitterSupport()
        {
            return dataEmitterSupport;
        }
        /**
         * Create a new AbstractRequestData instance.
         *
         * @param inDataEmitterSupport a DataEmitterSupport value
         */
        protected AbstractRequestData(DataEmitterSupport inDataEmitterSupport)
        {
            dataEmitterSupport = inDataEmitterSupport;
        }
        /**
         * data emitter support value
         */
        private final DataEmitterSupport dataEmitterSupport;
    }
    /**
     * Holds data relevant to a feed status request as part of a module data flow.
     *
     * @author Colin DuPlantis
     * @version $Id$
     * @since $Release$
     */
    private static class FeedStatusRequestData
            extends AbstractRequestData
    {
        /**
         * Create a new FeedStatusRequestData instance.
         *
         * @param inDataEmitterSupport a DataEmitterSupport value
         */
        private FeedStatusRequestData(DataEmitterSupport inDataEmitterSupport)
        {
            super(inDataEmitterSupport);
        }
    }
    /**
     * Holds data relevant to a market data request as part of a module data flow.
     *
     * @author Colin DuPlantis
     * @version $Id$
     * @since $Release$
     */
    private static class MarketDataRequestData
            extends AbstractRequestData
    {
        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString()
        {
            return description;
        }
        /**
         * Get the requestId value.
         *
         * @return a String value
         */
        @SuppressWarnings("unused")
        private String getRequestId()
        {
            return requestId;
        }
        /**
         * Get the marketDataRequest value.
         *
         * @return a MarketDataRequest value
         */
        private MarketDataRequest getMarketDataRequest()
        {
            return marketDataRequest;
        }
        /**
         * Create a new RequestData instance.
         *
         * @param inDataEmitterSupport a DataEmitterSupport value
         * @param inRequestId a String value
         * @param inMarketDataRequest a MarketDataRequest value
         */
        private MarketDataRequestData(DataEmitterSupport inDataEmitterSupport,
                                      String inRequestId,
                                      MarketDataRequest inMarketDataRequest)
        {
            super(inDataEmitterSupport);
            description = MarketDataRequestData.class.getSimpleName() + " [" + inDataEmitterSupport.getFlowID() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
            requestId = inRequestId;
            marketDataRequest = inMarketDataRequest;
        }
        /**
         * human-readable description of the object
         */
        private final String description;
        /**
         * request id of the request
         */
        private final String requestId;
        /**
         * original market data request
         */
        private final MarketDataRequest marketDataRequest;
    }
    /**
     * current status of the feed
     */
    private volatile FeedStatus feedStatus;
    /**
     * holds feed status requests by data flow id
     */
    private final Cache feedStatusRequestDataByDataFlowId = CacheBuilder.newBuilder().build();
    /**
     * receivers of capabilities of this module
     */
    @Autowired(required=false)
    private Collection capabilityBroadcasters = Lists.newArrayList();
    /**
     * receivers of status of this module
     */
    @Autowired(required=false)
    private Collection statusBroadcasters = Lists.newArrayList();
    /**
     * provides access to market data services
     */
    @Autowired
    private MarketDataService marketDataService;
    /**
     * holds data request info keyed by request id
     */
    private final Cache requestsByRequestId = CacheBuilder.newBuilder().build();
    /**
     * holds market data request info by data flow id
     */
    private final Cache requestsByDataFlowId = CacheBuilder.newBuilder().build();
    /**
     * singleton reference value
     */
    private static ManualFeedModule instance;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy