/**
   * Invokes the given search Url and writes the results to a file in the specified job folder.
   *
   * @param searchUrl
   * @param serverBase
   * @param jobDir
   * @param fileName
   * @throws IOException
   */
  private int retrieveAndStoreData(
      String searchUrl, String serverBase, File jobDir, String fileName) throws IOException {

    int numRecords = 0;

    final String url = urlEncodeQueryParams(searchUrl);

    LOG.info(
        "retrieveAndStoreData, serverBase: {}  searchUrl: {} encoded query: {}",
        serverBase,
        searchUrl,
        url);

    final ServerAuthorization serverAuthorization =
        AuthorizationUtil.findServerAuthorization(serverAuthorizations, serverBase);

    BearerTokenAuthInterceptor authInterceptor = null;
    if (serverAuthorization != null) {
      // register authorization interceptor with the client
      LOG.info("assigning bearing token interceptor, {}", serverAuthorization.getAccessToken());
      authInterceptor = new BearerTokenAuthInterceptor(serverAuthorization.getAccessToken());
      fhirRestClient.registerInterceptor(authInterceptor);
    }
    try {
      // Perform a search
      final Bundle searchResults =
          fhirRestClient.search().byUrl(url).returnBundle(Bundle.class).execute();

      // Split the bundle into its component resources
      final SearchResultSplitter resultSplitter = new SearchResultSplitter();
      final List<Resource> resources = resultSplitter.splitBundle(searchResults);

      final File dataFile = createDataSourceFile(jobDir, fileName);
      writeData(dataFile, resources, serverBase, true);
      numRecords = resources.size();

      // retrieve other pages of search results
      Bundle nextResults = searchResults;
      while (nextResults.getLink(Bundle.LINK_NEXT) != null) {
        nextResults = fhirRestClient.loadPage().next(nextResults).execute();
        List<Resource> nextResources = resultSplitter.splitBundle(nextResults);
        writeData(dataFile, nextResources, serverBase, false);
        numRecords += nextResources.size();
      }
    } finally {
      if (authInterceptor != null) {
        // unregister authorization interceptor with the client
        fhirRestClient.unregisterInterceptor(authInterceptor);
      }
    }
    return numRecords;
  }
  public void process(Bundle bundle) {
    Bundle response = null;
    RecordMatchResultsBuilder respBuilder;

    if (BundleType.MESSAGE.equals(bundle.getType())) {
      // Create a CSV data source for FRIL
      final File jobDir = newRunDir(workDir);

      final List<BundleEntryComponent> bundleEntries = bundle.getEntry();
      try {
        // The first entry is supposed to be the MessageHeader
        // This will force an exception if not true.
        final MessageHeader msgHdr = (MessageHeader) bundleEntries.get(0).getResource();
        // This log statement keeps above from getting optimized out
        LOG.trace("msg hdr id {}", msgHdr.getId());

        Parameters masterQryParams = null;
        Parameters queryQryParams = null;
        String masterSearchUrl = null;
        String querySearchUrl = null;
        String masterServerBase = null;
        String queryServerBase = null;
        String resourceType = "Patient";
        boolean isDeduplication = true;

        // Find the Parameters resources that contain the search parameters
        // and use those to construct search Urls
        for (BundleEntryComponent entry : bundleEntries) {
          Resource r = entry.getResource();
          LOG.debug("Found Resource type: " + r.getResourceType().toString());
          if (ResourceType.Parameters.equals(r.getResourceType())) {
            Parameters params = (Parameters) r;
            List<ParametersParameterComponent> paramList = params.getParameter();
            // Now look for the parameter with name, resourceType.
            ParametersParameterComponent p = ParametersUtil.findByName(paramList, RESOURCE_TYPE);
            if (p != null) {
              resourceType = p.getValue().toString();
            }
            // Find parameter that distinguishes between master and query sets
            p = ParametersUtil.findByName(paramList, "type");
            if (p != null) {
              String val = p.getValue().toString();
              if (val.equalsIgnoreCase(MASTER)) {
                masterQryParams = params;
                masterSearchUrl = buildSearchUrl(params);
                masterServerBase = getServerBase(resourceType, masterQryParams);
              } else if (val.equalsIgnoreCase(QUERY)) {
                queryQryParams = params;
                querySearchUrl = buildSearchUrl(params);
                queryServerBase = getServerBase(resourceType, queryQryParams);
              }
            }
          }
        }

        if (masterSearchUrl == null) {
          final String errMsg =
              "Required Parameter for master record set is missing, bundle: " + bundle.getId();
          LOG.warn(errMsg);
          // Construct and return an error result
          respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.FATALERROR);
          respBuilder.outcomeIssueDiagnostics(errMsg);
          response = respBuilder.build();
          getProducer().sendBody(getProducerEndpointUri(), response);
          return;
        }

        LoggingInterceptor loggingInterceptor = null;
        if (LOG.isDebugEnabled()) {
          loggingInterceptor = new LoggingInterceptor(true);
          fhirRestClient.registerInterceptor(loggingInterceptor);
        }

        int numMasterRecs = 0;
        try {
          // Retrieve the data associated with the search urls
          numMasterRecs = retrieveAndStoreData(masterSearchUrl, masterServerBase, jobDir, "master");

          if (querySearchUrl != null) {
            isDeduplication = false;
            retrieveAndStoreData(querySearchUrl, queryServerBase, jobDir, "query");
          }

        } catch (BaseServerResponseException e) {
          final String errMsg =
              String.format(
                  "Error response from server.  code: %d, %s", e.getStatusCode(), e.getMessage());
          LOG.warn(errMsg);
          // Construct and return an error result
          respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.FATALERROR);
          respBuilder.outcomeIssueDiagnostics(errMsg);
          response = respBuilder.build();
          getProducer().sendBody(getProducerEndpointUri(), response);
          return;
        } catch (Exception e) {
          final String errMsg = String.format("Unable to retrieve messages: %s", e.getMessage());
          LOG.warn(errMsg, e);
          // Construct and return an error result
          respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.FATALERROR);
          respBuilder.outcomeIssueDiagnostics(errMsg);
          response = respBuilder.build();
          getProducer().sendBody(getProducerEndpointUri(), response);
          return;
        } finally {
          if (loggingInterceptor != null) {
            fhirRestClient.unregisterInterceptor(loggingInterceptor);
          }
        }

        // if no records were returned for the master record set query
        if (numMasterRecs == 0) {
          respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.OK);
          respBuilder.outcomeDetailText("No Records Found in Master Record Set");
          response = respBuilder.build();
        } else {
          final File configFile = prepareMatchingRuleConfiguration(isDeduplication, jobDir);

          // Perform the Match Operation
          LOG.debug("About to Start FRIL w/ config {}", configFile.getAbsolutePath());
          final int numMatches = findMatches(isDeduplication, configFile);
          LOG.info("FRIL Number of Matches: {}", numMatches);

          if (numMatches == 0) {
            respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.OK);
            respBuilder.outcomeDetailText("No Matches Found");
            response = respBuilder.build();

          } else if (numMatches > 0) {
            // Find the name of the file containing duplicates from the config
            // file
            final File dupsFile = getDuplicatesFile(configFile);
            // Ensure the duplicates file exists
            if (!dupsFile.exists()) {
              final String errMsg = "Unable to find duplicates file";
              LOG.error(errMsg + " at " + dupsFile.getAbsolutePath());
              throw new FileNotFoundException(errMsg);
            }

            // Construct results
            respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.OK);
            respBuilder.outcomeDetailText("Deduplication Complete");
            respBuilder.duplicates(dupsFile);
            response = respBuilder.build();

          } else {
            final String errMsg = "Unknown Processing Error";
            LOG.error("{} bundleId: {}", errMsg, bundle.getId());

            // Construct an error result
            respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.FATALERROR);
            respBuilder.outcomeIssueDiagnostics(errMsg);
            response = respBuilder.build();
          }
        }

      } catch (Exception e) {
        final String errMsg = "Unexpected Error";
        LOG.error("Processing bundle: {}", bundle.getId(), e);

        // Construct an error result
        respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.FATALERROR);
        respBuilder.outcomeIssueDiagnostics(errMsg);
        try {
          response = respBuilder.build();
        } catch (IOException ioe) {
          // only so many times we can attempt to send a response; log error
          LOG.error("Unable to Send Error Response. request bundle: {}", bundle.getId(), ioe);
        }
      }

      if (deleteJobResults) {
        // Delete the Job Results folder and content
        deleteFolder(jobDir);
      }
    } else {
      final String errMsg = "Unsupported Bundle type: " + bundle.getType().toString();
      LOG.info("{} msgId: {}", errMsg, bundle.getId());

      // Construct an error result
      respBuilder = new RecordMatchResultsBuilder(bundle, ResponseType.FATALERROR);
      respBuilder.outcomeIssueDiagnostics(errMsg);
      try {
        response = respBuilder.build();
      } catch (IOException e) {
        // only so many times we can attempt to send a response; log error
        LOG.error("Unable to Send Error Response. request bundle: {}", bundle.getId());
      }
    }

    // Send the response back to the requester
    if (response != null) {
      getProducer().sendBody(getProducerEndpointUri(), response);
    } else {
      LOG.error("Null Response for request! bundleId: {}", bundle.getId());
    }
  }