public LocatorDatasetReader read() throws IOException {
    selectedLocator = locatorWithFallbacks;

    DatasetWithFMI originalDatasetWithFMI = null;
    do {
      try {
        originalDatasetWithFMI = readFrom(selectedLocator);
      } catch (IOException e) {
        LOG.info(
            "Failed to read Data Set with iuid={} from {}@{}",
            selectedLocator.iuid,
            selectedLocator.getFilePath(),
            selectedLocator.getStorageSystem(),
            e);
        selectedLocator = selectedLocator.getFallbackLocator();
        if (selectedLocator == null) throw e;
        LOG.info("Try read Data Set from alternative location");
      }
    } while (originalDatasetWithFMI == null);

    Attributes dataset = originalDatasetWithFMI.getDataset();

    if (context.getRemoteAE() != null) {
      storescuService.coerceFileBeforeMerge(selectedLocator, dataset, context);
    }
    dataset = Utils.mergeAndNormalize(dataset, (Attributes) selectedLocator.getObject());
    if (context.getRemoteAE() != null) {
      storescuService.coerceAttributes(dataset, context);
    }

    datasetWithFMI = new DatasetWithFMI(originalDatasetWithFMI.getFileMetaInformation(), dataset);

    return this;
  }
  @Override
  protected String selectTransferSyntaxFor(Association storeas, ArchiveInstanceLocator inst)
      throws UnsupportedStoreSCUException {
    Set<String> acceptedTransferSyntax = storeas.getTransferSyntaxesFor(inst.cuid);
    // check for SOP classes elimination
    if (context
        .getArchiveAEExtension()
        .getRetrieveSuppressionCriteria()
        .isCheckTransferCapabilities()) {
      inst = service.eliminateUnSupportedSOPClasses(inst, context);

      // check if eliminated then throw exception
      if (inst == null)
        throw new UnsupportedStoreSCUException("Unable to send instance, SOP class not configured");

      if (isConfiguredAndAccepted(inst, storeas.getTransferSyntaxesFor(inst.cuid)))
        return inst.tsuid;
      else return getDefaultConfiguredTransferSyntax(inst);
    }

    if (acceptedTransferSyntax.contains(inst.tsuid)) return inst.tsuid;

    return storeas.getTransferSyntaxesFor(inst.cuid).contains(UID.ExplicitVRLittleEndian)
        ? UID.ExplicitVRLittleEndian
        : UID.ImplicitVRLittleEndian;
  }
  @Override
  protected DataWriter createDataWriter(ArchiveInstanceLocator inst, String tsuid)
      throws IOException, UnsupportedStoreSCUException {
    if (inst == null || !(inst instanceof ArchiveInstanceLocator))
      throw new UnsupportedStoreSCUException("Unable to send instance");

    ArchiveAEExtension arcAEExt = context.getLocalAE().getAEExtension(ArchiveAEExtension.class);

    Attributes attrs = null;
    do {
      try {
        attrs = readFrom(inst);
      } catch (IOException e) {
        LOG.info(
            "Failed to read Data Set with iuid={} from {}@{}",
            inst.iuid,
            inst.getFilePath(),
            inst.getStorageSystem(),
            e);
        inst = inst.getFallbackLocator();
        if (inst == null) {
          throw e;
        }
        LOG.info("Try to read Data Set from alternative location");
      }
    } while (attrs == null);

    // check for suppression criteria
    if (context.getRemoteAE() != null) {
      String templateURI =
          arcAEExt
              .getRetrieveSuppressionCriteria()
              .getSuppressionCriteriaMap()
              .get(context.getRemoteAE().getAETitle());
      if (templateURI != null)
        inst = service.applySuppressionCriteria(inst, attrs, templateURI, context);
    }

    service.coerceFileBeforeMerge(inst, attrs, context);

    // here we merge file attributes with attributes in the blob
    attrs = Utils.mergeAndNormalize(attrs, (Attributes) inst.getObject());

    service.coerceAttributes(attrs, context);
    if (!tsuid.equals(inst.tsuid)) Decompressor.decompress(attrs, inst.tsuid);
    return new DataWriterAdapter(attrs);
  }
  @Override
  public org.dcm4che3.net.service.BasicCStoreSCUResp cstore(
      final java.util.List<ArchiveInstanceLocator> instances,
      final Association storeas,
      final int priority) {

    ArrayList<ArchiveInstanceLocator> localyAvailable =
        (ArrayList<ArchiveInstanceLocator>) filterLocalOrExternalMatches(instances, true);
    ArrayList<ArchiveInstanceLocator> externallyAvailable =
        (ArrayList<ArchiveInstanceLocator>) filterLocalOrExternalMatches(instances, false);
    BasicCStoreSCUResp responseForLocalyAvailable = null;

    if (!localyAvailable.isEmpty())
      responseForLocalyAvailable = super.cstore(localyAvailable, storeas, priority);
    // initialize remaining response
    BasicCStoreSCUResp finalResponse = extendResponse(responseForLocalyAvailable);

    if (!externallyAvailable.isEmpty()) {
      FetchForwardCallBack moveCallBack =
          new FetchForwardCallBack() {

            @Override
            public void onFetch(
                Collection<ArchiveInstanceLocator> instances, BasicCStoreSCUResp resp) {
              pushInstances((ArrayList<ArchiveInstanceLocator>) instances, storeas, priority);
            }
          };
      FetchForwardCallBack wadoCallBack =
          new FetchForwardCallBack() {

            @Override
            public void onFetch(
                Collection<ArchiveInstanceLocator> instances, BasicCStoreSCUResp resp) {
              pushInstances((ArrayList<ArchiveInstanceLocator>) instances, storeas, priority);
            }
          };
      finalResponse =
          service
              .getFetchForwardService()
              .fetchForward(
                  instances.size(),
                  finalResponse,
                  externallyAvailable,
                  storeas,
                  priority,
                  wadoCallBack,
                  moveCallBack);
      if (failed.size() > 0) {
        if (failed.size() == nr_instances) status = Status.UnableToPerformSubOperations;
        else status = Status.OneOrMoreFailures;
      } else {
        status = Status.Success;
      }
      setChanged();
      notifyObservers();
    }
    return finalResponse;
  }
  private Attributes readFrom(ArchiveInstanceLocator inst) throws IOException {

    try (DicomInputStream din = new DicomInputStream(service.getFile(inst).toFile())) {
      IncludeBulkData includeBulkData = IncludeBulkData.URI;
      int stopTag = -1;
      if (withoutBulkData) {
        if (((ArchiveInstanceLocator) inst).isWithoutBulkdata()) {
          includeBulkData = IncludeBulkData.YES;
        } else {
          includeBulkData = IncludeBulkData.NO;
          stopTag = Tag.PixelData;
        }
      }
      din.setIncludeBulkData(includeBulkData);
      return din.readDataset(-1, stopTag);
    }
  }
 private DatasetWithFMI readFrom(ArchiveInstanceLocator inst) throws IOException {
   try (DicomInputStream din = new DicomInputStream(storescuService.getFile(inst).toFile())) {
     din.setIncludeBulkData(IncludeBulkData.URI);
     return din.readDatasetWithFMI();
   }
 }