package edu.gatech.i3l.fhir.to;
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import javax.json.Json;
import javax.json.stream.JsonGenerator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.HttpEntityWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.thymeleaf.TemplateEngine;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestQuery;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Conformance.Rest;
//import ca.uhn.fhir.model.dstu2.resource.Conformance.RestQuery;
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam;
import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest;
import ca.uhn.fhir.rest.client.apache.ApacheHttpResponse;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.gclient.ICreateTyped;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.NumberClientParam.IMatches;
import ca.uhn.fhir.rest.gclient.QuantityClientParam;
import ca.uhn.fhir.rest.gclient.QuantityClientParam.IAndUnits;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.rest.gclient.TokenClientParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ExtensionConstants;
import edu.gatech.i3l.fhir.to.model.HomeRequest;
import edu.gatech.i3l.fhir.to.model.ResourceRequest;
import edu.gatech.i3l.fhir.to.model.TransactionRequest;
@org.springframework.stereotype.Controller()
public class Controller {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Controller.class);
private static final String PARAM_RESOURCE = "resource";
private static final String RESOURCE_COUNT_EXT_URL = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount";
@Autowired
private TesterConfig myConfig;
private Map<FhirVersionEnum, FhirContext> myContexts = new HashMap<FhirVersionEnum, FhirContext>();
private List<String> myFilterHeaders;
@Autowired
private TemplateEngine myTemplateEngine;
@RequestMapping(value = { "/about" })
public String actionAbout(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
addCommonParams(theServletRequest, theRequest, theModel);
theModel.put("notHome", true);
theModel.put("extraBreadcrumb", "About");
ourLog.info(logPrefix(theModel) + "Displayed about page");
return "about";
}
@RequestMapping(value = { "/conformance" })
public String actionConformance(HttpServletRequest theServletRequest, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
addCommonParams(theServletRequest, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
ResultType returnsResource = ResultType.RESOURCE;
long start = System.currentTimeMillis();
try {
client.conformance();
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Loaded conformance", interceptor, theRequest);
ourLog.info(logPrefix(theModel) + "Displayed conformance profile");
return "result";
}
@RequestMapping(value = { "/create" })
public String actionCreate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "create");
return "result";
}
@SuppressWarnings("unchecked")
@RequestMapping(value = { "/delete" })
public String actionDelete(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
RuntimeResourceDefinition def;
try {
def = getResourceType(theRequest, theReq);
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
return "resource";
}
String id = StringUtils.defaultString(theReq.getParameter("resource-delete-id"));
if (StringUtils.isBlank(id)) {
theModel.put("errorMsg", "No ID specified");
return "resource";
}
ResultType returnsResource = ResultType.BUNDLE;
String outcomeDescription = "Delete Resource";
long start = System.currentTimeMillis();
try {
client.delete((Class<? extends IResource>) def.getImplementingClass(), new IdDt(id));
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest);
ourLog.info(logPrefix(theModel) + "Deleted resource of type " + def.getName());
return "result";
}
@RequestMapping(value = { "/get-tags" })
public String actionGetTags(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
Class<? extends IResource> resType = null;
ResultType returnsResource = ResultType.TAGLIST;
String outcomeDescription = "Tag List";
long start = System.currentTimeMillis();
try {
if (isNotBlank(theReq.getParameter(PARAM_RESOURCE))) {
RuntimeResourceDefinition def;
try {
def = getResourceType(theRequest, theReq);
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
return "resource";
}
resType = (Class<? extends IResource>) def.getImplementingClass();
String id = theReq.getParameter("resource-tags-id");
if (isNotBlank(id)) {
String vid = theReq.getParameter("resource-tags-vid");
if (isNotBlank(vid)) {
client.getTags().forResource(resType, id, vid).execute();
ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName() + " ID " + id + " version" + vid);
} else {
client.getTags().forResource(resType, id).execute();
ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName() + " ID " + id);
}
} else {
client.getTags().forResource(resType).execute();
ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName());
}
} else {
client.getTags().execute();
ourLog.info(logPrefix(theModel) + "Got tags for server");
}
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest);
return "result";
}
@RequestMapping(value = { "/history-server" })
public String actionHistoryServer(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
doActionHistory(theReq, theRequest, theBindingResult, theModel, "history-server", "Server History");
return "result";
}
@RequestMapping(value = { "/history-type" })
public String actionHistoryType(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
doActionHistory(theReq, theRequest, theBindingResult, theModel, "history-type", "History");
return "result";
}
@RequestMapping(value = { "/", "/home" })
public String actionHome(HttpServletRequest theServletRequest, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
addCommonParams(theServletRequest, theRequest, theModel);
ourLog.info(theServletRequest.toString());
return "home";
}
@RequestMapping(value = { "/page" })
public String actionPage(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
String url = defaultString(theReq.getParameter("page-url"));
if (!url.startsWith(theModel.get("base").toString())) {
ourLog.warn(logPrefix(theModel) + "Refusing to load page URL: {}", url);
theModel.put("errorMsg", "Invalid page URL: " + url);
return "result";
}
url = url.replace("&", "&");
ResultType returnsResource = ResultType.BUNDLE;
long start = System.currentTimeMillis();
try {
ourLog.info(logPrefix(theModel) + "Loading paging URL: {}", url);
client.loadPage().url(url).execute();
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
String outcomeDescription = "Bundle Page";
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest);
return "result";
}
@RequestMapping(value = { "/read" })
public String actionRead(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
RuntimeResourceDefinition def;
try {
def = getResourceType(theRequest, theReq);
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
return "resource";
}
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theModel.put("errorMsg", "No ID specified");
return "resource";
}
ResultType returnsResource = ResultType.RESOURCE;
String versionId = StringUtils.defaultString(theReq.getParameter("vid"));
String outcomeDescription;
if (StringUtils.isBlank(versionId)) {
versionId = null;
outcomeDescription = "Read Resource";
} else {
outcomeDescription = "VRead Resource";
}
long start = System.currentTimeMillis();
try {
IdDt resid = new IdDt(def.getName(), id, versionId);
ourLog.info(logPrefix(theModel) + "Reading resource: {}", resid);
client.read(def.getImplementingClass(), resid);
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest);
return "result";
}
@RequestMapping({ "/resource" })
public String actionResource(HttpServletRequest theServletRequest, final ResourceRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
IResource conformance = addCommonParams(theServletRequest, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
String resourceName = theRequest.getResource();
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
TreeSet<String> includes = new TreeSet<String>();
TreeSet<String> revIncludes = new TreeSet<String>();
TreeSet<String> sortParams = new TreeSet<String>();
List<RestQuery> queries = new ArrayList<RestQuery>();
boolean haveSearchParams = false;
List<List<String>> queryIncludes = new ArrayList<List<String>>();
switch (theRequest.getFhirVersion(myConfig)) {
// case DEV:
case DSTU2:
haveSearchParams = extractSearchParamsDev(conformance, resourceName, includes, revIncludes, sortParams, queries, haveSearchParams, queryIncludes);
break;
// case DSTU1:
// haveSearchParams = extractSearchParamsDstu1(conformance, resourceName, includes, sortParams, queries, haveSearchParams, queryIncludes);
// break;
default:
throw new IllegalStateException("Unknown FHIR version: " + theRequest.getFhirVersion(myConfig));
}
theModel.put("includes", includes);
theModel.put("revincludes", revIncludes);
theModel.put("queries", queries);
theModel.put("haveSearchParams", haveSearchParams);
theModel.put("queryIncludes", queryIncludes);
theModel.put("sortParams", sortParams);
if (isNotBlank(theRequest.getUpdateId())) {
String updateId = theRequest.getUpdateId();
String updateVid = defaultIfEmpty(theRequest.getUpdateVid(), null);
IResource updateResource = (IResource) client.read(def.getImplementingClass(), new IdDt(resourceName, updateId, updateVid));
String updateResourceString = theRequest.newParser(getContext(theRequest)).setPrettyPrint(true).encodeResourceToString(updateResource);
theModel.put("updateResource", updateResourceString);
theModel.put("updateResourceId", updateId);
}
ourLog.info(logPrefix(theModel) + "Showing resource page: {}", resourceName);
return "resource";
}
@SuppressWarnings("unchecked")
@RequestMapping(value = { "/search" })
public String actionSearch(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
addCommonParams(theReq, theRequest, theModel);
StringWriter clientCodeJsonStringWriter = new StringWriter();
JsonGenerator clientCodeJsonWriter = Json.createGenerator(clientCodeJsonStringWriter);
clientCodeJsonWriter.writeStartObject();
clientCodeJsonWriter.write("action", "search");
clientCodeJsonWriter.write("base", (String) theModel.get("base"));
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
IUntypedQuery search = client.search();
IQuery query;
if (isNotBlank(theReq.getParameter("resource"))) {
try {
query = search.forResource((Class<? extends IResource>) getResourceType(theRequest, theReq).getImplementingClass());
} catch (ServletException e) {
theModel.put("errorMsg", e.toString());
return "resource";
}
clientCodeJsonWriter.write("resource", theReq.getParameter("resource"));
} else {
query = search.forAllResources();
clientCodeJsonWriter.writeNull("resource");
}
if (client.getPrettyPrint() != null) {
clientCodeJsonWriter.write("pretty", client.getPrettyPrint().toString());
} else {
clientCodeJsonWriter.writeNull("pretty");
}
if (client.getEncoding() != null) {
clientCodeJsonWriter.write("format", client.getEncoding().getRequestContentType());
} else {
clientCodeJsonWriter.writeNull("format");
}
String outcomeDescription = "Search for Resources";
clientCodeJsonWriter.writeStartArray("params");
int paramIdx = -1;
while (true) {
paramIdx++;
String paramIdxString = Integer.toString(paramIdx);
boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query, clientCodeJsonWriter);
if (!shouldContinue) {
break;
}
}
clientCodeJsonWriter.writeEnd();
clientCodeJsonWriter.writeStartArray("includes");
String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE);
if (incValues != null) {
for (String next : incValues) {
if (isNotBlank(next)) {
query.include(new Include(next));
clientCodeJsonWriter.write(next);
}
}
}
clientCodeJsonWriter.writeEnd();
clientCodeJsonWriter.writeStartArray("revincludes");
String[] revIncValues = theReq.getParameterValues(Constants.PARAM_REVINCLUDE);
if (revIncValues != null) {
for (String next : revIncValues) {
if (isNotBlank(next)) {
query.revInclude(new Include(next));
clientCodeJsonWriter.write(next);
}
}
}
clientCodeJsonWriter.writeEnd();
String limit = theReq.getParameter("resource-search-limit");
if (isNotBlank(limit)) {
if (!limit.matches("[0-9]+")) {
theModel.put("errorMsg", "Search limit must be a numeric value.");
return "resource";
}
int limitInt = Integer.parseInt(limit);
query.limitTo(limitInt);
clientCodeJsonWriter.write("limit", limit);
} else {
clientCodeJsonWriter.writeNull("limit");
}
String[] sort = theReq.getParameterValues("sort_by");
if (sort != null) {
for (String next : sort) {
if (isBlank(next)) {
continue;
}
String direction = theReq.getParameter("sort_direction");
if ("asc".equals(direction)) {
query.sort().ascending(new StringClientParam(next));
} else if ("desc".equals(direction)) {
query.sort().descending(new StringClientParam(next));
} else {
query.sort().defaultOrder(new StringClientParam(next));
}
}
}
long start = System.currentTimeMillis();
ResultType returnsResource;
try {
ourLog.info(logPrefix(theModel) + "Executing a search");
query.execute();
returnsResource = ResultType.BUNDLE;
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest);
clientCodeJsonWriter.writeEnd();
clientCodeJsonWriter.close();
String clientCodeJson = clientCodeJsonStringWriter.toString();
theModel.put("clientCodeJson", clientCodeJson);
return "result";
}
@RequestMapping(value = { "/transaction" })
public String actionTransaction(HttpServletRequest theServletRequest, final TransactionRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
addCommonParams(theServletRequest, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor);
String body = preProcessMessageBody(theRequest.getTransactionBody());
Bundle bundle;
try {
if (body.startsWith("{")) {
bundle = getContext(theRequest).newJsonParser().parseBundle(body);
} else if (body.startsWith("<")) {
bundle = getContext(theRequest).newXmlParser().parseBundle(body);
} else {
theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
return "home";
}
} catch (DataFormatException e) {
ourLog.warn("Failed to parse bundle", e);
theModel.put("errorMsg", "Failed to parse transaction bundle body. Error was: " + e.getMessage());
return "home";
}
ResultType returnsResource = ResultType.BUNDLE;
long start = System.currentTimeMillis();
try {
ourLog.info(logPrefix(theModel) + "Executing transaction with {} resources", bundle.size());
client.transaction().withBundle(bundle).execute();
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Transaction", interceptor, theRequest);
return "result";
}
@RequestMapping(value = { "/update" })
public String actionUpdate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "update");
return "result";
}
@RequestMapping(value = { "/validate" })
public String actionValidate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "validate");
return "result";
}
private IResource addCommonParams(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
if (myConfig.getDebugTemplatesMode()) {
myTemplateEngine.getCacheManager().clearAllCaches();
}
final String serverId = theRequest.getServerIdWithDefault(myConfig);
final String serverBase = theRequest.getServerBase(theServletRequest, myConfig);
final String serverName = theRequest.getServerName(myConfig);
theModel.put("serverId", serverId);
theModel.put("base", serverBase);
theModel.put("baseName", serverName);
theModel.put("resourceName", defaultString(theRequest.getResource()));
theModel.put("encoding", theRequest.getEncoding());
theModel.put("pretty", theRequest.getPretty());
theModel.put("serverEntries", myConfig.getIdToServerName());
return loadAndAddConf(theServletRequest, theRequest, theModel);
}
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
return theAllHeaders;
}
ArrayList<Header> retVal = new ArrayList<Header>();
for (Header next : theAllHeaders) {
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
retVal.add(next);
}
}
return retVal.toArray(new Header[retVal.size()]);
}
private void doActionCreateOrValidate(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod) {
boolean validate = "validate".equals(theMethod);
addCommonParams(theReq, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
Class<? extends IResource> type = null; // def.getImplementingClass();
if ("history-type".equals(theMethod)) {
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
type = (Class<? extends IResource>) def.getImplementingClass();
}
String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body");
if (isBlank(body)) {
theModel.put("errorMsg", "No message body specified");
return;
}
body = preProcessMessageBody(body);
IResource resource;
try {
if (body.startsWith("{")) {
resource = getContext(theRequest).newJsonParser().parseResource(type, body);
} else if (body.startsWith("<")) {
resource = getContext(theRequest).newXmlParser().parseResource(type, body);
} else {
theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
return;
}
} catch (DataFormatException e) {
ourLog.warn("Failed to parse resource", e);
theModel.put("errorMsg", "Failed to parse message body. Error was: " + e.getMessage());
return;
}
String outcomeDescription;
long start = System.currentTimeMillis();
ResultType returnsResource = ResultType.RESOURCE;
outcomeDescription = "";
boolean update = false;
try {
if (validate) {
outcomeDescription = "Validate Resource";
client.validate(resource);
} else {
String id = theReq.getParameter("resource-create-id");
if ("update".equals(theMethod)) {
outcomeDescription = "Update Resource";
client.update(id, resource);
update = true;
} else {
outcomeDescription = "Create Resource";
ICreateTyped create = client.create().resource(body);
if (isNotBlank(id)) {
create.withId(id);
}
create.execute();
}
}
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest);
try {
if (validate) {
ourLog.info(logPrefix(theModel) + "Validated resource of type " + getResourceType(theRequest, theReq).getName());
} else if (update) {
ourLog.info(logPrefix(theModel) + "Updated resource of type " + getResourceType(theRequest, theReq).getName());
} else {
ourLog.info(logPrefix(theModel) + "Created resource of type " + getResourceType(theRequest, theReq).getName());
}
} catch (Exception e) {
ourLog.warn("Failed to determine resource type from request", e);
}
}
private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) {
addCommonParams(theReq, theRequest, theModel);
CaptureInterceptor interceptor = new CaptureInterceptor();
GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor);
String id = null;
Class<? extends IResource> type = null; // def.getImplementingClass();
if ("history-type".equals(theMethod)) {
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource());
type = (Class<? extends IResource>) def.getImplementingClass();
id = StringUtils.defaultString(theReq.getParameter("resource-history-id"));
}
DateTimeDt since = null;
String sinceStr = theReq.getParameter("since");
if (isNotBlank(sinceStr)) {
since = new DateTimeDt(sinceStr);
}
Integer limit = null;
String limitStr = theReq.getParameter("limit");
if (isNotBlank(limitStr)) {
limit = Integer.parseInt(limitStr);
}
ResultType returnsResource = ResultType.BUNDLE;
long start = System.currentTimeMillis();
try {
ourLog.info(logPrefix(theModel) + "Retrieving history for type {} ID {} since {}", new Object[] { type, id, since });
client.history(type, id, since, limit);
} catch (Exception e) {
returnsResource = handleClientException(client, e, theModel);
}
long delay = System.currentTimeMillis() - start;
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, theMethodDescription, interceptor, theRequest);
}
private boolean extractSearchParamsDev(IResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams, List<RestQuery> queries, boolean haveSearchParams,
List<List<String>> queryIncludes) {
ca.uhn.fhir.model.dstu2.resource.Conformance conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) theConformance;
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextRes : nextRest.getResource()) {
if (nextRes.getTypeElement().getValue().equals(resourceName)) {
for (StringDt next : nextRes.getSearchInclude()) {
if (next.isEmpty() == false) {
includes.add(next.getValue());
}
}
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam next : nextRes.getSearchParam()) {
if (next.getTypeElement().getValueAsEnum() != ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum.COMPOSITE) {
sortParams.add(next.getNameElement().getValue());
}
}
if (nextRes.getSearchParam().size() > 0) {
haveSearchParams = true;
}
} else {
// It's a different resource from the one we're searching, so
// scan for revinclude candidates
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam next : nextRes.getSearchParam()) {
if (next.getTypeElement().getValueAsEnum() == ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum.REFERENCE) {
for (BoundCodeDt<ResourceTypeEnum> nextTargetType : next.getTarget()) {
if (nextTargetType.getValue().equals(resourceName)) {
theRevIncludes.add(nextRes.getTypeElement().getValue() + ":" + next.getName());
}
}
}
}
}
}
}
return haveSearchParams;
}
// private boolean extractSearchParamsDstu1(IResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> sortParams, List<RestQuery> queries, boolean haveSearchParams,
// List<List<String>> queryIncludes) {
// Conformance conformance = (Conformance) theConformance;
// for (Rest nextRest : conformance.getRest()) {
// for (RestResource nextRes : nextRest.getResource()) {
// if (nextRes.getType().getValue().equals(resourceName)) {
// for (StringDt next : nextRes.getSearchInclude()) {
// if (next.isEmpty() == false) {
// includes.add(next.getValue());
// }
// }
// for (RestResourceSearchParam next : nextRes.getSearchParam()) {
// if (next.getType().getValueAsEnum() != SearchParamTypeEnum.COMPOSITE) {
// sortParams.add(next.getName().getValue());
// }
// }
// if (nextRes.getSearchParam().size() > 0) {
// haveSearchParams = true;
// }
// }
// }
// for (RestQuery nextQuery : nextRest.getQuery()) {
// boolean queryMatchesResource = false;
// List<ExtensionDt> returnTypeExt = nextQuery.getUndeclaredExtensionsByUrl(ExtensionConstants.QUERY_RETURN_TYPE);
// if (returnTypeExt != null) {
// for (ExtensionDt nextExt : returnTypeExt) {
// if (resourceName.equals(nextExt.getValueAsPrimitive().getValueAsString())) {
// queries.add(nextQuery);
// queryMatchesResource = true;
// break;
// }
// }
// }
//
// if (queryMatchesResource) {
// ArrayList<String> nextQueryIncludes = new ArrayList<String>();
// queryIncludes.add(nextQueryIncludes);
// List<ExtensionDt> includesExt = nextQuery.getUndeclaredExtensionsByUrl(ExtensionConstants.QUERY_ALLOWED_INCLUDE);
// if (includesExt != null) {
// for (ExtensionDt nextExt : includesExt) {
// nextQueryIncludes.add(nextExt.getValueAsPrimitive().getValueAsString());
// }
// }
// }
// }
// }
// return haveSearchParams;
// }
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
String str = StringEscapeUtils.escapeHtml4(theResultBody);
if (str == null || theEncodingEnum == null) {
return str;
}
StringBuilder b = new StringBuilder();
if (theEncodingEnum == EncodingEnum.JSON) {
boolean inValue = false;
boolean inQuote = false;
for (int i = 0; i < str.length(); i++) {
char prevChar = (i > 0) ? str.charAt(i - 1) : ' ';
char nextChar = str.charAt(i);
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
if (inQuote) {
b.append(nextChar);
if (prevChar != '\\' && nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
b.append("quot;</span>");
i += 5;
inQuote = false;
} else if (nextChar == '\\' && nextChar2 == '"') {
b.append("quot;</span>");
i += 5;
inQuote = false;
}
} else {
if (nextChar == ':') {
inValue = true;
b.append(nextChar);
} else if (nextChar == '[' || nextChar == '{') {
b.append("<span class='hlControl'>");
b.append(nextChar);
b.append("</span>");
inValue = false;
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
b.append("<span class='hlControl'>");
b.append(nextChar);
b.append("</span>");
inValue = false;
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
if (inValue) {
b.append("<span class='hlQuot'>"");
} else {
b.append("<span class='hlTagName'>"");
}
inQuote = true;
i += 5;
} else if (nextChar == ':') {
b.append("<span class='hlControl'>");
b.append(nextChar);
b.append("</span>");
inValue = true;
} else {
b.append(nextChar);
}
}
}
} else {
boolean inQuote = false;
boolean inTag = false;
for (int i = 0; i < str.length(); i++) {
char nextChar = str.charAt(i);
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
if (inQuote) {
b.append(nextChar);
if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
b.append("quot;</span>");
i += 5;
inQuote = false;
}
} else if (inTag) {
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
b.append("</span><span class='hlControl'>></span>");
inTag = false;
i += 3;
} else if (nextChar == ' ') {
b.append("</span><span class='hlAttr'>");
b.append(nextChar);
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
b.append("<span class='hlQuot'>"");
inQuote = true;
i += 5;
} else {
b.append(nextChar);
}
} else {
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
inTag = true;
i += 3;
} else {
b.append(nextChar);
}
}
}
}
return b.toString();
}
private String formatUrl(String theUrlBase, String theResultBody) {
String str = theResultBody;
if (str == null) {
return str;
}
try {
str = URLDecoder.decode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
ourLog.error("Should not happen", e);
}
StringBuilder b = new StringBuilder();
b.append("<span class='hlUrlBase'>");
boolean inParams = false;
for (int i = 0; i < str.length(); i++) {
char nextChar = str.charAt(i);
// char nextChar2 = i < str.length()-2 ? str.charAt(i+1):' ';
// char nextChar3 = i < str.length()-2 ? str.charAt(i+2):' ';
if (!inParams) {
if (nextChar == '?') {
inParams = true;
b.append("</span><wbr /><span class='hlControl'>?</span><span class='hlTagName'>");
} else {
if (i == theUrlBase.length()) {
b.append("</span><wbr /><span class='hlText'>");
}
b.append(nextChar);
}
} else {
if (nextChar == '&') {
b.append("</span><wbr /><span class='hlControl'>&</span><span class='hlTagName'>");
} else if (nextChar == '=') {
b.append("</span><span class='hlControl'>=</span><span class='hlAttr'>");
// }else if (nextChar=='%' && Character.isLetterOrDigit(nextChar2)&& Character.isLetterOrDigit(nextChar3)) {
// URLDecoder.decode(s, enc)
} else {
b.append(nextChar);
}
}
}
if (inParams) {
b.append("</span>");
}
return b.toString();
}
private FhirContext getContext(HomeRequest theRequest) {
FhirVersionEnum version = theRequest.getFhirVersion(myConfig);
FhirContext retVal = myContexts.get(version);
if (retVal == null) {
retVal = new FhirContext(version);
myContexts.put(version, retVal);
}
return retVal;
}
private RuntimeResourceDefinition getResourceType(HomeRequest theRequest, HttpServletRequest theReq) throws ServletException {
String resourceName = StringUtils.defaultString(theReq.getParameter(PARAM_RESOURCE));
RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(resourceName);
if (def == null) {
throw new ServletException("Invalid resourceName: " + resourceName);
}
return def;
}
private ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
ResultType returnsResource;
returnsResource = ResultType.NONE;
ourLog.warn("Failed to invoke server", e);
if (theClient.getLastResponse() == null) {
theModel.put("errorMsg", "Error: " + e.getMessage());
}
return returnsResource;
}
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonGenerator theClientCodeJsonWriter) {
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
if (isBlank(nextName)) {
return false;
}
String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier"));
String nextType = theReq.getParameter("param." + paramIdxString + ".type");
List<String> parts = new ArrayList<String>();
for (int i = 0; i < 5; i++) {
parts.add(defaultString(theReq.getParameter("param." + paramIdxString + "." + i)));
}
List<String> values;
boolean addToWhere = true;
if ("token".equals(nextType)) {
if (isBlank(parts.get(1))) {
return true;
}
addToWhere = false;
if (isBlank(parts.get(0))) {
values = Collections.singletonList(parts.get(1));
theQuery.where(new TokenClientParam(nextName + nextQualifier).exactly().code(parts.get(1)));
} else {
values = Collections.singletonList(parts.get(0) + "|" + parts.get(1));
theQuery.where(new TokenClientParam(nextName + nextQualifier).exactly().systemAndCode(parts.get(0), parts.get(1)));
}
} else if ("date".equals(nextType)) {
values = new ArrayList<String>();
if (isNotBlank(parts.get(1))) {
values.add(StringUtils.join(parts.get(0), parts.get(1)));
}
if (isNotBlank(parts.get(3))) {
values.add(StringUtils.join(parts.get(2), parts.get(3)));
}
if (values.isEmpty()) {
return true;
}
} else if ("quantity".equals(nextType)) {
values = new ArrayList<String>();
addToWhere = false;
QuantityClientParam param = new QuantityClientParam(nextName);
IMatches<IAndUnits> matcher;
if ("~".equals(parts.get(0))) {
matcher = param.approximately();
} else if ("=".equals(parts.get(0))) {
matcher = param.exactly();
} else if (">=".equals(parts.get(0))) {
matcher = param.greaterThanOrEquals();
} else if ("<=".equals(parts.get(0))) {
matcher = param.lessThanOrEquals();
} else if (">".equals(parts.get(0))) {
matcher = param.greaterThan();
} else if ("<".equals(parts.get(0))) {
matcher = param.lessThan();
} else {
throw new Error("Unknown qualifier: " + parts.get(0));
}
IAndUnits number = matcher.number(parts.get(1));
if (isBlank(parts.get(3))) {
theQuery.where(number.andNoUnits());
} else if (isBlank(parts.get(2))) {
theQuery.where(number.andUnits(parts.get(3)));
} else {
theQuery.where(number.andUnits(parts.get(2), parts.get(3)));
}
values.add(parts.get(0) + parts.get(1) + "|" + parts.get(2) + "|" + parts.get(3));
if (values.isEmpty()) {
return true;
}
} else {
values = Collections.singletonList(StringUtils.join(parts, ""));
if (isBlank(values.get(0))) {
return true;
}
}
for (String nextValue : values) {
theClientCodeJsonWriter.writeStartObject();
theClientCodeJsonWriter.write("type", nextType);
theClientCodeJsonWriter.write("name", nextName);
theClientCodeJsonWriter.write("qualifier", nextQualifier);
theClientCodeJsonWriter.write("value", nextValue);
theClientCodeJsonWriter.writeEnd();
if (addToWhere) {
theQuery.where(new StringClientParam(nextName + nextQualifier).matches().value(nextValue));
}
}
if (StringUtils.isNotBlank(theReq.getParameter("param." + paramIdxString + ".0.name"))) {
handleSearchParam(paramIdxString + ".0", theReq, theQuery, theClientCodeJsonWriter);
}
return true;
}
private IResource loadAndAddConf(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
switch (theRequest.getFhirVersion(myConfig)) {
// case DEV:
// return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
// case DSTU1:
// return loadAndAddConfDstu1(theServletRequest, theRequest, theModel);
case DSTU2:
return loadAndAddConfDstu2(theServletRequest, theRequest, theModel);
}
throw new IllegalStateException("Unknown version: " + theRequest.getFhirVersion(myConfig));
}
// private Conformance loadAndAddConfDstu1(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
// IGenericClient client = getContext(theRequest).newRestfulGenericClient(theRequest.getServerBase(theServletRequest, myConfig));
//
// Conformance conformance;
// try {
// conformance = (Conformance) client.conformance();
// } catch (Exception e) {
// ourLog.warn("Failed to load conformance statement", e);
// theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
// conformance = new Conformance();
// }
//
// theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
//
// Map<String, Number> resourceCounts = new HashMap<String, Number>();
// long total = 0;
// for (Rest nextRest : conformance.getRest()) {
// for (RestResource nextResource : nextRest.getResource()) {
// List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
// if (exts != null && exts.size() > 0) {
// Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
// resourceCounts.put(nextResource.getType().getValue(), nextCount);
// total += nextCount.longValue();
// }
// }
// }
// theModel.put("resourceCounts", resourceCounts);
//
// if (total > 0) {
// for (Rest nextRest : conformance.getRest()) {
// Collections.sort(nextRest.getResource(), new Comparator<RestResource>() {
// @Override
// public int compare(RestResource theO1, RestResource theO2) {
// DecimalDt count1 = new DecimalDt();
// List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
// if (count1exts != null && count1exts.size() > 0) {
// count1 = (DecimalDt) count1exts.get(0).getValue();
// }
// DecimalDt count2 = new DecimalDt();
// List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
// if (count2exts != null && count2exts.size() > 0) {
// count2 = (DecimalDt) count2exts.get(0).getValue();
// }
// int retVal = count2.compareTo(count1);
// if (retVal == 0) {
// retVal = theO1.getType().getValue().compareTo(theO2.getType().getValue());
// }
// return retVal;
// }
// });
// }
// }
//
// theModel.put("conf", conformance);
// theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
//
// return conformance;
// }
private IResource loadAndAddConfDstu2(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) {
IGenericClient client = getContext(theRequest).newRestfulGenericClient(theRequest.getServerBase(theServletRequest, myConfig));
ca.uhn.fhir.model.dstu2.resource.Conformance conformance;
try {
conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) client.conformance();
} catch (Exception e) {
ourLog.warn("Failed to load conformance statement", e);
theModel.put("errorMsg", "Failed to load conformance statement, error was: " + e.toString());
conformance = new ca.uhn.fhir.model.dstu2.resource.Conformance();
}
theModel.put("jsonEncodedConf", getContext(theRequest).newJsonParser().encodeResourceToString(conformance));
Map<String, Number> resourceCounts = new HashMap<String, Number>();
long total = 0;
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextResource : nextRest.getResource()) {
List<ExtensionDt> exts = nextResource.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
if (exts != null && exts.size() > 0) {
Number nextCount = ((DecimalDt) (exts.get(0).getValue())).getValueAsNumber();
resourceCounts.put(nextResource.getTypeElement().getValue(), nextCount);
total += nextCount.longValue();
}
}
}
theModel.put("resourceCounts", resourceCounts);
if (total > 0) {
for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) {
Collections.sort(nextRest.getResource(), new Comparator<ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource>() {
@Override
public int compare(ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO1, ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource theO2) {
DecimalDt count1 = new DecimalDt();
List<ExtensionDt> count1exts = theO1.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
if (count1exts != null && count1exts.size() > 0) {
count1 = (DecimalDt) count1exts.get(0).getValue();
}
DecimalDt count2 = new DecimalDt();
List<ExtensionDt> count2exts = theO2.getUndeclaredExtensionsByUrl(RESOURCE_COUNT_EXT_URL);
if (count2exts != null && count2exts.size() > 0) {
count2 = (DecimalDt) count2exts.get(0).getValue();
}
int retVal = count2.compareTo(count1);
if (retVal == 0) {
retVal = theO1.getTypeElement().getValue().compareTo(theO2.getTypeElement().getValue());
}
return retVal;
}
});
}
}
theModel.put("conf", conformance);
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
return conformance;
}
private String logPrefix(ModelMap theModel) {
return "[server=" + theModel.get("serverId") + "] - ";
}
private String parseNarrative(IResource resource) {
try {
String retVal = resource.getText().getDiv().getValueAsString();
return StringUtils.defaultString(retVal);
} catch (Exception e) {
ourLog.error("Failed to parse resource", e);
return "";
}
}
private String preProcessMessageBody(String theBody) {
if (theBody == null) {
return "";
}
String retVal = theBody.trim();
StringBuilder b = new StringBuilder();
for (int i = 0; i < retVal.length(); i++) {
char nextChar = retVal.charAt(i);
int nextCharI = nextChar;
if (nextCharI == 65533) {
b.append(' ');
continue;
}
if (nextCharI == 160) {
b.append(' ');
continue;
}
if (nextCharI == 194) {
b.append(' ');
continue;
}
b.append(nextChar);
}
retVal = b.toString();
return retVal;
}
private void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription,
CaptureInterceptor theInterceptor, HomeRequest theRequest) {
try {
HttpRequestBase lastRequest = theInterceptor.getLastRequest();
HttpResponse lastResponse = theInterceptor.getLastResponse();
String requestBody = null;
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
String action = lastRequest != null ? lastRequest.getMethod() : null;
String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null;
String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody());
if (lastRequest instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
if (entity.isRepeatable()) {
requestBody = IOUtils.toString(entity.getContent());
}
}
ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null;
String mimeType = ct != null ? ct.getMimeType() : null;
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
String narrativeString = "";
StringBuilder resultDescription = new StringBuilder();
Bundle bundle = null;
IResource resource = null;
if (ctEnum == null) {
resultDescription.append("Non-FHIR response");
} else {
switch (ctEnum) {
case JSON:
if (theResultType == ResultType.RESOURCE) {
resource = (IResource) ctEnum.newParser(getContext(theRequest)).parseResource(resultBody);
narrativeString = parseNarrative(resource);
resultDescription.append("JSON resource");
} else if (theResultType == ResultType.BUNDLE) {
resultDescription.append("JSON bundle");
bundle = getContext(theRequest).newJsonParser().parseBundle(resultBody);
}
break;
case XML:
default:
if (theResultType == ResultType.RESOURCE) {
resource = (IResource) ctEnum.newParser(getContext(theRequest)).parseResource(resultBody);
narrativeString = parseNarrative(resource);
resultDescription.append("XML resource");
} else if (theResultType == ResultType.BUNDLE) {
resultDescription.append("XML bundle");
bundle = getContext(theRequest).newXmlParser().parseBundle(resultBody);
}
break;
}
}
/*
* DSTU2 no longer has a title in the bundle format, but it's still useful here..
*/
// if (bundle != null) {
// INarrativeGenerator gen = getContext(theRequest).getNarrativeGenerator();
// if (gen != null) {
// for (BundleEntry next : bundle.getEntries()) {
// if (next.getTitle().isEmpty() && next.getResource() != null) {
// String title = gen.generateTitle(next.getResource());
// next.getTitle().setValue(title);
// }
// }
// }
// }
resultDescription.append(" (").append(resultBody.length() + " bytes)");
// Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
// Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
theModelMap.put("outcomeDescription", outcomeDescription);
theModelMap.put("resultDescription", resultDescription.toString());
theModelMap.put("action", action);
theModelMap.put("bundle", bundle);
theModelMap.put("resultStatus", resultStatus);
theModelMap.put("requestUrl", requestUrl);
theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl));
String requestBodyText = format(requestBody, ctEnum);
theModelMap.put("requestBody", requestBodyText);
String resultBodyText = format(resultBody, ctEnum);
theModelMap.put("resultBody", resultBodyText);
theModelMap.put("resource", resource);
theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000);
theModelMap.put("requestHeaders", requestHeaders);
theModelMap.put("responseHeaders", responseHeaders);
if(narrativeString != null){
theModelMap.put("narrative", narrativeString);
} else {
theModelMap.put("resultMessage", "div");
}
theModelMap.put("latencyMs", theLatency);
} catch (Exception e) {
ourLog.error("Failure during processing", e);
theModelMap.put("errorMsg", "Error during processing: " + e.getMessage());
}
}
public static class CaptureInterceptor implements IClientInterceptor {
private HttpRequestBase myLastRequest;
private HttpResponse myLastResponse;
private String myResponseBody;
public HttpRequestBase getLastRequest() {
return myLastRequest;
}
public HttpResponse getLastResponse() {
return myLastResponse;
}
public String getLastResponseBody() {
return myResponseBody;
}
@Override
public void interceptRequest(IHttpRequest theRequest) {
assert myLastRequest == null;
myLastRequest = ((ApacheHttpRequest) theRequest).getApacheRequest();
}
@Override
public void interceptResponse(IHttpResponse myResponse) throws IOException {
assert myLastResponse == null;
HttpResponse theResponse = ((ApacheHttpResponse) myResponse).getResponse();
myLastResponse = theResponse;
HttpEntity respEntity = theResponse.getEntity();
if (respEntity != null) {
final byte[] bytes;
try {
bytes = IOUtils.toByteArray(respEntity.getContent());
} catch (IllegalStateException e) {
throw new InternalErrorException(e);
}
myResponseBody = new String(bytes, "UTF-8");
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
}
}
// @Override
// public void interceptRequest(HttpRequestBase theRequest) {
// assert myLastRequest == null;
// myLastRequest = theRequest;
// }
//
// @Override
// public void interceptResponse(HttpResponse theResponse) throws IOException {
// assert myLastResponse == null;
// myLastResponse = theResponse;
//
// HttpEntity respEntity = theResponse.getEntity();
// if (respEntity != null) {
// final byte[] bytes;
// try {
// bytes = IOUtils.toByteArray(respEntity.getContent());
// } catch (IllegalStateException e) {
// throw new InternalErrorException(e);
// }
//
// myResponseBody = new String(bytes, "UTF-8");
// theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
// }
// }
private static class MyEntityWrapper extends HttpEntityWrapper {
private byte[] myBytes;
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
super(theWrappedEntity);
myBytes = theBytes;
}
@Override
public InputStream getContent() throws IOException {
return new ByteArrayInputStream(myBytes);
}
@Override
public void writeTo(OutputStream theOutstream) throws IOException {
theOutstream.write(myBytes);
}
}
}
private enum ResultType {
BUNDLE, NONE, RESOURCE, TAGLIST
}
}