/** Returns the resourceUrl parameter from the Parameters resource. */
  private String getResourceUrl(Parameters params) {
    String resourceUrl = null;

    final List<ParametersParameterComponent> paramList = params.getParameter();

    final ParametersParameterComponent p = ParametersUtil.findByName(paramList, SEARCH_EXPR);
    if (p != null) {
      final Resource r = p.getResource();
      if (ResourceType.Parameters.equals(r.getResourceType())) {
        final Parameters searchExprParams = (Parameters) r;
        final List<ParametersParameterComponent> searchExprParamList =
            searchExprParams.getParameter();

        final ParametersParameterComponent ppc =
            ParametersUtil.findByName(searchExprParamList, RESOURCE_URL);
        resourceUrl = ppc.getValue().toString();

        if (resourceUrl == null) {
          LOG.warn("Required parameter, resourceUrl, is missing from record-match request!");
        }
      }
    } else {
      LOG.warn("Unable to find search expression in message parameters");
    }
    return resourceUrl;
  }
  /**
   * Constructs a search URL using the information in the Parameters resource.
   *
   * @param params Parameters resource containing a searchExpression parameter whose value is a
   *     Parameters resource containing a resourceUrl parameter and other parameters that comprise
   *     the query expression.
   * @return search url or an empty string if the url could not be formed
   */
  private String buildSearchUrl(Parameters params) {
    final StringBuilder searchUrl = new StringBuilder(200);

    final List<ParametersParameterComponent> paramList = params.getParameter();

    final ParametersParameterComponent p = ParametersUtil.findByName(paramList, SEARCH_EXPR);
    if (p != null) {
      final Resource r = p.getResource();
      if (ResourceType.Parameters.equals(r.getResourceType())) {
        final Parameters searchExprParams = (Parameters) r;
        final List<ParametersParameterComponent> searchExprParamList =
            searchExprParams.getParameter();

        String resourceUrl = null;
        final StringBuilder queryExpr = new StringBuilder(100);
        // all parameters except resourceUrl contribute to the query expression
        for (ParametersParameterComponent searchExprParam : searchExprParamList) {
          String name = searchExprParam.getName();
          try {
            String value = searchExprParam.getValue().toString();
            // resourceUrl is different than others
            if (RESOURCE_URL.equals(name)) {
              resourceUrl = value;
            } else {
              if (queryExpr.length() > 0) {
                queryExpr.append("&");
              }
              queryExpr.append(name);
              queryExpr.append("=");
              queryExpr.append(value);
            }
          } catch (NullPointerException e) {
            LOG.error("Null Value for search expression parameter, {}", name);
          }
        }

        if (resourceUrl == null) {
          LOG.warn("Required parameter, resourceUrl, is missing from record-match request!");
        } else {
          searchUrl.append(resourceUrl);
          searchUrl.append("?");
          searchUrl.append(queryExpr);
        }
        LOG.info("search Url: {}", searchUrl.toString());
      }
    } else {
      LOG.warn("Unable to find search expression in message parameters");
    }
    return searchUrl.toString();
  }
  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());
    }
  }