Package com.opengamma.bbg.referencedata.cache

Source Code of com.opengamma.bbg.referencedata.cache.AbstractInvalidFieldCachingReferenceDataProvider

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

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.opengamma.bbg.referencedata.ReferenceData;
import com.opengamma.bbg.referencedata.ReferenceDataError;
import com.opengamma.bbg.referencedata.ReferenceDataProvider;
import com.opengamma.bbg.referencedata.ReferenceDataProviderGetRequest;
import com.opengamma.bbg.referencedata.ReferenceDataProviderGetResult;
import com.opengamma.bbg.referencedata.impl.AbstractReferenceDataProvider;
import com.opengamma.util.ArgumentChecker;

/**
* Abstract reference data provider decorator that caches permanent invalid field errors.
* <p>
* It is strongly recommended to always decorate the underlying provider with a permanent
* invalid field error caching provider. This avoids excess queries to Bloomberg.
*/
public abstract class AbstractInvalidFieldCachingReferenceDataProvider extends AbstractReferenceDataProvider {
  // See BBG-72

  /**
   * The underlying provider.
   */
  private final ReferenceDataProvider _underlying;

  /**
   * Creates an instance.
   *
   * @param underlying  the underlying provider, not null
   */
  protected AbstractInvalidFieldCachingReferenceDataProvider(ReferenceDataProvider underlying) {
    ArgumentChecker.notNull(underlying, "underlying");
    _underlying = underlying;
  }

  //-------------------------------------------------------------------------
  /**
   * Gets the underlying provider.
   *
   * @return the underlying provider, not null
   */
  public ReferenceDataProvider getUnderlying() {
    return _underlying;
  }

  //-------------------------------------------------------------------------
  @Override
  protected ReferenceDataProviderGetResult doBulkGet(ReferenceDataProviderGetRequest request) {
    // this implementation always caches and ignore the use-cache flag in the request
    // this is because the errors here really are permanent and there is no reason to avoid the cache
   
    // load invalid fields from cache
    final Map<String, Set<String>> cachedErrorsMap = loadInvalidFields(request.getIdentifiers());
   
    // filter the request removing known invalid fields
    final Map<Set<String>, Set<String>> requiredFields = buildUnderlyingRequestGroups(request, cachedErrorsMap);
   
    // process everything that remains
    final ReferenceDataProviderGetResult result = new ReferenceDataProviderGetResult();
    for (Entry<Set<String>, Set<String>> group : requiredFields.entrySet()) {
      if (group.getKey().isEmpty()) {
        // all fields for these identifiers are invalid
        for (String identifier : group.getValue()) {
          result.addReferenceData(new ReferenceData(identifier));
        }
      } else {
        // call the underlying with the filtered subset of identifiers and fields
        final ReferenceDataProviderGetRequest underlyingRequest = ReferenceDataProviderGetRequest.createGet(group.getValue(), group.getKey(), false);
        final ReferenceDataProviderGetResult underlyingResult = getUnderlying().getReferenceData(underlyingRequest);
        for (ReferenceData refData : underlyingResult.getReferenceData()) {
          String identifier = refData.getIdentifier();
          checkAndSaveInvalidFields(refData, cachedErrorsMap.get(identifier));
          result.addReferenceData(refData);
        }
      }
    }
    return result;
  }

  /**
   * Examines and groups the request using the known invalid fields.
   *
   * @param request  the request, not null
   * @param invalidFieldsByIdentifier  the invalid fields, keyed by identifier, not null
   * @return the map of field-set to identifier-set, not null
   */
  protected Map<Set<String>, Set<String>> buildUnderlyingRequestGroups(ReferenceDataProviderGetRequest request, Map<String, Set<String>> invalidFieldsByIdentifier) {
    Map<Set<String>, Set<String>> result = Maps.newHashMap();
    for (String identifier : request.getIdentifiers()) {
      // select known invalid fields for the identifier
      Set<String> invalidFields = invalidFieldsByIdentifier.get(identifier);
     
      // calculate the missing fields that must be queried from the underlying
      Set<String> missingFields = null;
      if (invalidFields == null) {
        missingFields = Sets.newHashSet(request.getFields());
      } else {
        missingFields = Sets.difference(request.getFields(), invalidFields);
      }
     
      // build the grouped result map, keyed from field-set to identifier-set
      Set<String> resultIdentifiers = result.get(missingFields);
      if (resultIdentifiers == null) {
        resultIdentifiers = Sets.newTreeSet();
        result.put(missingFields, resultIdentifiers);
      }
      resultIdentifiers.add(identifier);
    }
    return result;
  }

  /**
   * Checks the reference data and adds any extra permanent errors to the cache.
   *
   * @param refData  the reference data to check, not null
   * @param invalidFields  the previously cached invalid fields for the identifier, may be null
   */
  protected void checkAndSaveInvalidFields(ReferenceData refData, Set<String> invalidFields) {
    // find all the new invalid fields
    final Set<String> newPermanentErrors = Sets.newHashSet();
    for (ReferenceDataError error : refData.getErrors()) {
      if (isPermanent(error)) {
        newPermanentErrors.add(error.getField());
      }
    }
   
    // save the new invalid fields
    if (newPermanentErrors.size() > 0) {
      if (invalidFields != null) {
        newPermanentErrors.addAll(invalidFields);
      }
      saveInvalidFields(refData.getIdentifier(), newPermanentErrors);
    }
  }

  //-------------------------------------------------------------------------
  /**
   * Finds the fields which previously failed for the specified identifiers.
   * <p>
   * This loads from the cache.
   *
   * @param identifiers  the identifiers to find errors for, not null
   * @return the map of invalid fields keyed by identifier, not null
   */
  protected abstract Map<String, Set<String>> loadInvalidFields(Set<String> identifiers);

  /**
   * Saves the permanent errors into the cache.
   * <p>
   * This stores into the cache.
   *
   * @param identifier  the identifier to save errors for, not null
   * @param invalidFields  the invalid fields, not null
   */
  protected abstract void saveInvalidFields(String identifier, Set<String> invalidFields);

  //-------------------------------------------------------------------------
  /**
   * Checks whether the specified error is permanent or not.
   *
   * @param error  the error object, not null
   * @return true if error is permanent
   */
  protected boolean isPermanent(ReferenceDataError error) {
    return error.isFieldBased() && "BAD_FLD".equals(error.getCategory()) &&
          "NOT_APPLICABLE_TO_REF_DATA".equals(error.getSubcategory());
  }

}
TOP

Related Classes of com.opengamma.bbg.referencedata.cache.AbstractInvalidFieldCachingReferenceDataProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.