com.opengamma.bbg.referencedata.impl.BloombergReferenceDataProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.bbg.referencedata.impl.BloombergReferenceDataProvider.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.bbg.referencedata.impl;

import static com.opengamma.bbg.BloombergConstants.BLOOMBERG_FIELDS_REQUEST;
import static com.opengamma.bbg.BloombergConstants.BLOOMBERG_REFERENCE_DATA_REQUEST;
import static com.opengamma.bbg.BloombergConstants.BLOOMBERG_SECURITIES_REQUEST;
import static com.opengamma.bbg.BloombergConstants.EID_DATA;
import static com.opengamma.bbg.BloombergConstants.ERROR_INFO;
import static com.opengamma.bbg.BloombergConstants.FIELD_DATA;
import static com.opengamma.bbg.BloombergConstants.FIELD_EXCEPTIONS;
import static com.opengamma.bbg.BloombergConstants.FIELD_ID;
import static com.opengamma.bbg.BloombergConstants.RESPONSE_ERROR;
import static com.opengamma.bbg.BloombergConstants.SECURITY;
import static com.opengamma.bbg.BloombergConstants.SECURITY_DATA;
import static com.opengamma.bbg.BloombergConstants.SECURITY_ERROR;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;

import org.apache.commons.lang.StringUtils;
import org.fudgemsg.FudgeMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.context.Lifecycle;

import com.bloomberglp.blpapi.CorrelationID;
import com.bloomberglp.blpapi.Element;
import com.bloomberglp.blpapi.Request;
import com.google.common.base.CharMatcher;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.bbg.AbstractBloombergStaticDataProvider;
import com.opengamma.bbg.BloombergConnector;
import com.opengamma.bbg.BloombergConstants;
import com.opengamma.bbg.referencedata.ReferenceData;
import com.opengamma.bbg.referencedata.ReferenceDataError;
import com.opengamma.bbg.referencedata.ReferenceDataProviderGetRequest;
import com.opengamma.bbg.referencedata.ReferenceDataProviderGetResult;
import com.opengamma.bbg.referencedata.statistics.BloombergReferenceDataStatistics;
import com.opengamma.bbg.util.BloombergDataUtils;
import com.opengamma.util.ArgumentChecker;

/**
 * Provider of reference-data from the Bloomberg data source.
 */
public class BloombergReferenceDataProvider extends AbstractReferenceDataProvider implements Lifecycle {

    /** Logger. */
    private static final Logger s_logger = LoggerFactory.getLogger(BloombergReferenceDataProvider.class);

    /**
     * Implementation class.
     */
    private final BloombergReferenceDataProviderImpl _impl;

    /**
     * Creates an instance.
     * <p>
     * This will use the statistics tool in the connector.
     * 
     * @param bloombergConnector  the Bloomberg connector, not null
     */
    public BloombergReferenceDataProvider(BloombergConnector bloombergConnector) {
        this(bloombergConnector, bloombergConnector.getReferenceDataStatistics());
    }

    /**
     * Creates an instance with statistics gathering.
     * 
     * @param bloombergConnector  the Bloomberg connector, not null
     * @param statistics  the statistics to collect, not null
     */
    public BloombergReferenceDataProvider(BloombergConnector bloombergConnector,
            BloombergReferenceDataStatistics statistics) {
        _impl = new BloombergReferenceDataProviderImpl(bloombergConnector, statistics);
    }

    //-------------------------------------------------------------------------
    @Override
    protected ReferenceDataProviderGetResult doBulkGet(ReferenceDataProviderGetRequest request) {
        return _impl.doBulkGet(request);
    }

    //-------------------------------------------------------------------------
    @Override
    public void start() {
        _impl.start();
    }

    @Override
    public void stop() {
        _impl.stop();
    }

    @Override
    public boolean isRunning() {
        return _impl.isRunning();
    }

    //-------------------------------------------------------------------------
    /**
     * Loads reference-data from Bloomberg.
     */
    static class BloombergReferenceDataProviderImpl extends AbstractBloombergStaticDataProvider {

        /**
         * Bloomberg statistics.
         */
        private final BloombergReferenceDataStatistics _statistics;

        /**
         * Creates an instance.
         * 
         * @param provider  the provider, not null
         */
        public BloombergReferenceDataProviderImpl(BloombergConnector bloombergConnector,
                BloombergReferenceDataStatistics statistics) {
            super(bloombergConnector, BloombergConstants.REF_DATA_SVC_NAME);
            ArgumentChecker.notNull(statistics, "statistics");
            _statistics = statistics;
        }

        //-------------------------------------------------------------------------
        @Override
        protected Logger getLogger() {
            return s_logger;
        }

        //-------------------------------------------------------------------------
        /**
         * Get reference-data from Bloomberg.
         * 
         * @param request  the request, not null
         * @return the reference-data result, not null
         */
        ReferenceDataProviderGetResult doBulkGet(ReferenceDataProviderGetRequest request) {
            Set<String> identifiers = request.getIdentifiers();
            Set<String> dataFields = request.getFields();
            validateIdentifiers(identifiers);
            validateFields(dataFields);

            ensureStarted();
            s_logger.debug("Getting reference data for {}, fields {}", identifiers, dataFields);

            Request bbgRequest = createRequest(identifiers, dataFields);
            _statistics.recordStatistics(identifiers, dataFields);
            BlockingQueue<Element> resultElements = callBloomberg(bbgRequest);
            if (resultElements == null || resultElements.isEmpty()) {
                throw new OpenGammaRuntimeException(
                        "Unable to get a Bloomberg response for " + dataFields + " fields for " + identifiers);
            }
            return parse(identifiers, dataFields, resultElements);
        }

        //-------------------------------------------------------------------------
        /**
         * Checks that all the identifiers are valid.
         * 
         * @param identifiers  the set of identifiers, not null
         */
        private void validateIdentifiers(Set<String> identifiers) {
            Set<String> excluded = new HashSet<String>();
            for (String identifier : identifiers) {
                if (StringUtils.isEmpty(identifier)) {
                    throw new IllegalArgumentException("Must not have any null or empty identifiers");
                }
                if (CharMatcher.ASCII.matchesAllOf(identifier) == false) {
                    //[BBG-93] - The C++ interface is declared as UChar, so this just enforces that restriction   
                    excluded.add(identifier);
                }
            }
            if (excluded.size() > 0) {
                String message = MessageFormatter
                        .format("Request contains invalid identifiers {} from ({})", excluded, identifiers)
                        .getMessage();
                s_logger.error(message);
                throw new OpenGammaRuntimeException(message);
            }
        }

        /**
         * Checks that all the fields are valid.
         * 
         * @param fields  the set of fields, not null
         */
        private void validateFields(Set<String> fields) {
            Set<String> excluded = new HashSet<String>();
            for (String field : fields) {
                if (StringUtils.isEmpty(field)) {
                    throw new IllegalArgumentException("Must not have any null or empty fields");
                }
                if (CharMatcher.ASCII.matchesAllOf(field) == false) {
                    excluded.add(field);
                }
            }
            if (excluded.size() > 0) {
                String message = MessageFormatter
                        .format("Request contains invalid fields {} from ({})", excluded, fields).getMessage();
                s_logger.error(message);
                throw new OpenGammaRuntimeException(message);
            }
        }

        //-------------------------------------------------------------------------
        /**
         * Creates the Bloomberg request.
         */
        private Request createRequest(Set<String> identifiers, Set<String> dataFields) {
            // create request
            Request request = getService().createRequest(BLOOMBERG_REFERENCE_DATA_REQUEST);
            Element securitiesElem = request.getElement(BLOOMBERG_SECURITIES_REQUEST);

            // identifiers
            for (String identifier : identifiers) {
                if (StringUtils.isEmpty(identifier)) {
                    throw new IllegalArgumentException("Must not have any null or empty securities");
                }
                securitiesElem.appendValue(identifier);
            }

            // fields
            Element fieldElem = request.getElement(BLOOMBERG_FIELDS_REQUEST);
            for (String dataField : dataFields) {
                if (StringUtils.isEmpty(dataField)) {
                    throw new IllegalArgumentException("Must not have any null or empty fields");
                }
                if (dataField.equals(BloombergConstants.FIELD_EID_DATA) == false) {
                    fieldElem.appendValue(dataField);
                }
            }
            if (dataFields.contains(BloombergConstants.FIELD_EID_DATA)) {
                request.set("returnEids", true);
            }
            return request;
        }

        //-------------------------------------------------------------------------
        /**
         * Call Bloomberg.
         * 
         * @param request  the request, not null
         * @return the response, may be null
         */
        private BlockingQueue<Element> callBloomberg(Request request) {
            CorrelationID cid = submitBloombergRequest(request);
            return getResultElement(cid);
        }

        //-------------------------------------------------------------------------
        /**
         * Performs the main work to parse the result from Bloomberg.
         * <p>
         * This is part of {@link #getFields(Set, Set)}.
         * 
         * @param securityKeys  the set of securities, not null
         * @param fields  the set of fields, not null
         * @param resultElements  the result elements from Bloomberg, not null
         * @return the parsed result, not null
         */
        protected ReferenceDataProviderGetResult parse(Set<String> securityKeys, Set<String> fields,
                BlockingQueue<Element> resultElements) {
            ReferenceDataProviderGetResult result = new ReferenceDataProviderGetResult();
            for (Element resultElem : resultElements) {
                if (resultElem.hasElement(RESPONSE_ERROR)) {
                    Element responseError = resultElem.getElement(RESPONSE_ERROR);
                    String category = responseError.getElementAsString(BloombergConstants.CATEGORY);
                    if ("LIMIT".equals(category)) {
                        s_logger.error("Limit reached {}", responseError);
                    }
                    throw new OpenGammaRuntimeException("Unable to get a Bloomberg response for " + fields
                            + " fields for " + securityKeys + ": " + responseError);
                }

                Element securityDataArray = resultElem.getElement(SECURITY_DATA);
                int numSecurities = securityDataArray.numValues();
                for (int iSecurityElem = 0; iSecurityElem < numSecurities; iSecurityElem++) {
                    Element securityElem = securityDataArray.getValueAsElement(iSecurityElem);
                    String securityKey = securityElem.getElementAsString(SECURITY);
                    ReferenceData refData = new ReferenceData(securityKey);
                    if (securityElem.hasElement(SECURITY_ERROR)) {
                        parseIdentifierError(refData, securityElem.getElement(SECURITY_ERROR));
                    }
                    if (securityElem.hasElement(FIELD_DATA)) {
                        parseFieldData(refData, securityElem.getElement(FIELD_DATA));
                    }
                    if (securityElem.hasElement(FIELD_EXCEPTIONS)) {
                        parseFieldExceptions(refData, securityElem.getElement(FIELD_EXCEPTIONS));
                    }
                    if (securityElem.hasElement(EID_DATA)) {
                        parseEidData(refData, securityElem.getElement(FIELD_DATA));
                    }
                    result.addReferenceData(refData);
                }
            }
            return result;
        }

        /**
         * Processes an error affecting the whole identifier.
         * 
         * @param refData  the per identifier reference data result, not null
         * @param element  the bloomberg element, not null
         */
        protected void parseIdentifierError(ReferenceData refData, Element element) {
            ReferenceDataError error = buildError(null, element);
            refData.addError(error);
        }

        /**
         * Processes the field data.
         * 
         * @param refData  the per identifier reference data result, not null
         * @param element  the bloomberg element, not null
         */
        protected void parseFieldData(ReferenceData refData, Element element) {
            FudgeMsg fieldData = BloombergDataUtils.parseElement(element);
            refData.setFieldValues(fieldData);
        }

        /**
         * Processes the an error affecting a single field on a one identifier.
         * 
         * @param refData  the per identifier reference data result, not null
         * @param fieldExceptionArray  the bloomberg data, not null
         */
        protected void parseFieldExceptions(ReferenceData refData, Element fieldExceptionArray) {
            int numExceptions = fieldExceptionArray.numValues();
            for (int i = 0; i < numExceptions; i++) {
                Element exceptionElem = fieldExceptionArray.getValueAsElement(i);
                String fieldId = exceptionElem.getElementAsString(FIELD_ID);
                ReferenceDataError error = buildError(fieldId, exceptionElem.getElement(ERROR_INFO));
                refData.addError(error);
            }
        }

        /**
         * Processes the EID data.
         * 
         * @param refData  the per identifier reference data result, not null
         * @param element  the bloomberg element, not null
         */
        protected void parseEidData(ReferenceData refData, Element element) {
            refData.setEntitlementInfo(element);
        }

        /**
         * Creates an instance from a Bloomberg element.
         * 
         * @param field  the field, null if linked to the identifier rather than a field
         * @param element  the element, not null
         * @return the error, not null
         */
        public ReferenceDataError buildError(String field, Element element) {
            return new ReferenceDataError(field, element.getElementAsInt32("code"),
                    element.getElementAsString("category"), element.getElementAsString("subcategory"),
                    element.getElementAsString("message"));
        }
    }

}