protected void streamOperationOutcome(
      BaseServerResponseException theE,
      RestfulServer theServer,
      EncodingEnum theEncodingNotNull,
      HttpServletResponse theResponse,
      RequestDetails theRequest)
      throws IOException {
    theResponse.setStatus(theE.getStatusCode());

    theServer.addHeadersToResponse(theResponse);

    if (theE.getOperationOutcome() != null) {
      theResponse.setContentType(theEncodingNotNull.getResourceContentType());
      IParser parser = theEncodingNotNull.newParser(theServer.getFhirContext());
      parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
      Writer writer = theResponse.getWriter();
      try {
        parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);
      } finally {
        writer.close();
      }
    } else {
      theResponse.setContentType(Constants.CT_TEXT);
      Writer writer = theResponse.getWriter();
      try {
        writer.append(theE.getMessage());
      } finally {
        writer.close();
      }
    }
  }
  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());
    }
  }
  private Bundle batch(final RequestDetails theRequestDetails, Bundle theRequest) {
    ourLog.info("Beginning batch with {} resources", theRequest.getEntry().size());
    long start = System.currentTimeMillis();

    TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
    txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

    Bundle resp = new Bundle();
    resp.setType(BundleType.BATCHRESPONSE);
    OperationOutcome ooResp = new OperationOutcome();
    resp.addEntry().setResource(ooResp);

    /*
     * For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it doesn't prevent others
     */

    for (final BundleEntryComponent nextRequestEntry : theRequest.getEntry()) {

      TransactionCallback<Bundle> callback =
          new TransactionCallback<Bundle>() {
            @Override
            public Bundle doInTransaction(TransactionStatus theStatus) {
              Bundle subRequestBundle = new Bundle();
              subRequestBundle.setType(BundleType.TRANSACTION);
              subRequestBundle.addEntry(nextRequestEntry);

              Bundle subResponseBundle =
                  transaction(
                      (ServletRequestDetails) theRequestDetails,
                      subRequestBundle,
                      "Batch sub-request");
              return subResponseBundle;
            }
          };

      BaseServerResponseException caughtEx;
      try {
        Bundle nextResponseBundle = txTemplate.execute(callback);
        caughtEx = null;

        BundleEntryComponent subResponseEntry = nextResponseBundle.getEntry().get(0);
        resp.addEntry(subResponseEntry);
        /*
         * If the individual entry didn't have a resource in its response, bring the sub-transaction's OperationOutcome across so the client can see it
         */
        if (subResponseEntry.getResource() == null) {
          subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource());
        }

      } catch (BaseServerResponseException e) {
        caughtEx = e;
      } catch (Throwable t) {
        ourLog.error("Failure during BATCH sub transaction processing", t);
        caughtEx = new InternalErrorException(t);
      }

      if (caughtEx != null) {
        BundleEntryComponent nextEntry = resp.addEntry();

        OperationOutcome oo = new OperationOutcome();
        oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage());
        nextEntry.setResource(oo);

        BundleEntryResponseComponent nextEntryResp = nextEntry.getResponse();
        nextEntryResp.setStatus(toStatusString(caughtEx.getStatusCode()));
      }
    }

    long delay = System.currentTimeMillis() - start;
    ourLog.info("Batch completed in {}ms", new Object[] {delay});
    ooResp
        .addIssue()
        .setSeverity(IssueSeverity.INFORMATION)
        .setDiagnostics("Batch completed in " + delay + "ms");

    return resp;
  }
Exemple #4
0
  <T> T invokeClient(
      FhirContext theContext,
      IClientResponseHandler<T> binding,
      BaseHttpClientInvocation clientInvocation,
      EncodingEnum theEncoding,
      Boolean thePrettyPrint,
      boolean theLogRequestAndResponse) {

    if (!myDontValidateConformance) {
      myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
    }

    // TODO: handle non 2xx status codes by throwing the correct exception,
    // and ensure it's passed upwards
    HttpRequestBase httpRequest;
    HttpResponse response;
    try {
      Map<String, List<String>> params = createExtraParams();

      if (theEncoding == EncodingEnum.XML) {
        params.put(Constants.PARAM_FORMAT, Collections.singletonList("xml"));
      } else if (theEncoding == EncodingEnum.JSON) {
        params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
      }

      if (thePrettyPrint == Boolean.TRUE) {
        params.put(
            Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
      }

      EncodingEnum encoding = getEncoding();
      if (theEncoding != null) {
        encoding = theEncoding;
      }

      httpRequest = clientInvocation.asHttpRequest(myUrlBase, params, encoding, thePrettyPrint);

      if (theLogRequestAndResponse) {
        ourLog.info("Client invoking: {}", httpRequest);
        if (httpRequest instanceof HttpEntityEnclosingRequest) {
          HttpEntity entity = ((HttpEntityEnclosingRequest) httpRequest).getEntity();
          if (entity.isRepeatable()) {
            String content = IOUtils.toString(entity.getContent());
            ourLog.info("Client request body: {}", content);
          }
        }
      }

      for (IClientInterceptor nextInterceptor : myInterceptors) {
        nextInterceptor.interceptRequest(httpRequest);
      }

      response = myClient.execute(httpRequest);

      for (IClientInterceptor nextInterceptor : myInterceptors) {
        nextInterceptor.interceptResponse(response);
      }

    } catch (DataFormatException e) {
      throw new FhirClientConnectionException(e);
    } catch (IOException e) {
      throw new FhirClientConnectionException(e);
    }

    try {
      String mimeType;
      if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatusLine().getStatusCode()) {
        mimeType = null;
      } else {
        ContentType ct = ContentType.get(response.getEntity());
        mimeType = ct != null ? ct.getMimeType() : null;
      }

      Map<String, List<String>> headers = new HashMap<String, List<String>>();
      if (response.getAllHeaders() != null) {
        for (Header next : response.getAllHeaders()) {
          String name = next.getName().toLowerCase();
          List<String> list = headers.get(name);
          if (list == null) {
            list = new ArrayList<String>();
            headers.put(name, list);
          }
          list.add(next.getValue());
        }
      }

      if (response.getStatusLine().getStatusCode() < 200
          || response.getStatusLine().getStatusCode() > 299) {
        String body = null;
        Reader reader = null;
        try {
          reader = createReaderFromResponse(response);
          body = IOUtils.toString(reader);
        } catch (Exception e) {
          ourLog.debug("Failed to read input stream", e);
        } finally {
          IOUtils.closeQuietly(reader);
        }

        String message =
            "HTTP "
                + response.getStatusLine().getStatusCode()
                + " "
                + response.getStatusLine().getReasonPhrase();
        BaseOperationOutcome oo = null;
        if (Constants.CT_TEXT.equals(mimeType)) {
          message = message + ": " + body;
        } else {
          EncodingEnum enc = EncodingEnum.forContentType(mimeType);
          if (enc != null) {
            IParser p = enc.newParser(theContext);
            try {
              // TODO: handle if something other than OO comes back
              oo = (BaseOperationOutcome) p.parseResource(body);
              if (oo.getIssueFirstRep().getDetailsElement().isEmpty() == false) {
                message = message + ": " + oo.getIssueFirstRep().getDetailsElement().getValue();
              }
            } catch (Exception e) {
              ourLog.debug("Failed to process OperationOutcome response");
            }
          }
        }

        keepResponseAndLogIt(theLogRequestAndResponse, response, body);

        BaseServerResponseException exception =
            BaseServerResponseException.newInstance(
                response.getStatusLine().getStatusCode(), message);
        exception.setOperationOutcome(oo);

        if (body != null) {
          exception.setResponseBody(body);
        }

        throw exception;
      }
      if (binding instanceof IClientResponseHandlerHandlesBinary) {
        IClientResponseHandlerHandlesBinary<T> handlesBinary =
            (IClientResponseHandlerHandlesBinary<T>) binding;
        if (handlesBinary.isBinary()) {
          InputStream reader = response.getEntity().getContent();
          try {

            if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
              byte[] responseBytes = IOUtils.toByteArray(reader);
              if (myKeepResponses) {
                myLastResponse = response;
                myLastResponseBody = null;
              }
              String message =
                  "HTTP "
                      + response.getStatusLine().getStatusCode()
                      + " "
                      + response.getStatusLine().getReasonPhrase();
              if (theLogRequestAndResponse) {
                ourLog.info("Client response: {} - {} bytes", message, responseBytes.length);
              } else {
                ourLog.trace("Client response: {} - {} bytes", message, responseBytes.length);
              }
              reader = new ByteArrayInputStream(responseBytes);
            }

            return handlesBinary.invokeClient(
                mimeType, reader, response.getStatusLine().getStatusCode(), headers);
          } finally {
            IOUtils.closeQuietly(reader);
          }
        }
      }

      Reader reader = createReaderFromResponse(response);

      if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
        String responseString = IOUtils.toString(reader);
        keepResponseAndLogIt(theLogRequestAndResponse, response, responseString);
        reader = new StringReader(responseString);
      }

      try {
        return binding.invokeClient(
            mimeType, reader, response.getStatusLine().getStatusCode(), headers);
      } finally {
        IOUtils.closeQuietly(reader);
      }

    } catch (IllegalStateException e) {
      throw new FhirClientConnectionException(e);
    } catch (IOException e) {
      throw new FhirClientConnectionException(e);
    } finally {
      if (response instanceof CloseableHttpResponse) {
        try {
          ((CloseableHttpResponse) response).close();
        } catch (IOException e) {
          ourLog.debug("Failed to close response", e);
        }
      }
    }
  }