Java tutorial
/** * 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")); } } }