Let IdDt clone without being heavyweight
This commit is contained in:
parent
1b7b141396
commit
28ebda71e1
|
@ -79,6 +79,10 @@ public interface IResource extends ICompositeElement {
|
|||
* of the URL) used to access this resource, and is not the same thing as any business
|
||||
* identifiers stored within the resource. For example, a Patient resource might
|
||||
* have any number of medical record numbers but these are not stored here.
|
||||
* <p>
|
||||
* This ID is specified as the "Logical ID" and "Version ID" in the FHIR specification, see
|
||||
* <a href="http://www.hl7.org/implement/standards/fhir/resources.html#metadata">here</a>
|
||||
* </p>
|
||||
*/
|
||||
IdDt getId();
|
||||
|
||||
|
@ -87,6 +91,10 @@ public interface IResource extends ICompositeElement {
|
|||
* of the URL) used to access this resource, and is not the same thing as any business
|
||||
* identifiers stored within the resource. For example, a Patient resource might
|
||||
* have any number of medical record numbers but these are not stored here.
|
||||
* <p>
|
||||
* This ID is specified as the "Logical ID" and "Version ID" in the FHIR specification, see
|
||||
* <a href="http://www.hl7.org/implement/standards/fhir/resources.html#metadata">here</a>
|
||||
* </p>
|
||||
*/
|
||||
void setId(IdDt theId);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.server.Constants;
|
|||
@DatatypeDef(name = "id")
|
||||
public class IdDt extends BasePrimitive<String> {
|
||||
|
||||
private boolean myHaveComponentParts;
|
||||
private String myResourceType;
|
||||
private String myUnqualifiedId;
|
||||
private String myUnqualifiedVersionId;
|
||||
|
@ -97,6 +98,18 @@ public class IdDt extends BasePrimitive<String> {
|
|||
setValue(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theResourceType
|
||||
* The resource type (e.g. "Patient")
|
||||
* @param theId
|
||||
* The ID (e.g. "123")
|
||||
*/
|
||||
public IdDt(String theResourceType, BigDecimal theIdPart) {
|
||||
this(theResourceType, theIdPart.toPlainString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
@ -126,11 +139,14 @@ public class IdDt extends BasePrimitive<String> {
|
|||
myResourceType = theResourceType;
|
||||
myUnqualifiedId = theId;
|
||||
myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
|
||||
if (myUnqualifiedVersionId != null) {
|
||||
myValue = myResourceType + '/' + myUnqualifiedId + '/' + Constants.PARAM_HISTORY + '/' + myUnqualifiedVersionId;
|
||||
} else {
|
||||
myValue = myResourceType + '/' + myUnqualifiedId;
|
||||
}
|
||||
myHaveComponentParts = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
|
||||
*/
|
||||
public BigDecimal asBigDecimal() {
|
||||
return getIdPartAsBigDecimal();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,12 +216,19 @@ public class IdDt extends BasePrimitive<String> {
|
|||
*/
|
||||
@Override
|
||||
public String getValue() {
|
||||
if (myValue == null && myHaveComponentParts) {
|
||||
if (myUnqualifiedVersionId != null) {
|
||||
myValue = myResourceType + '/' + myUnqualifiedId + '/' + Constants.PARAM_HISTORY + '/' + myUnqualifiedVersionId;
|
||||
} else {
|
||||
myValue = myResourceType + '/' + myUnqualifiedId;
|
||||
}
|
||||
}
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValueAsString() {
|
||||
return myValue;
|
||||
return getValue();
|
||||
}
|
||||
|
||||
public String getVersionIdPart() {
|
||||
|
@ -228,13 +251,6 @@ public class IdDt extends BasePrimitive<String> {
|
|||
return isNotBlank(getVersionIdPart());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the ID is a local reference (in other words, it begins with the '#' character)
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return myUnqualifiedId != null && myUnqualifiedId.isEmpty() == false && myUnqualifiedId.charAt(0) == '#';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
|
||||
* of digits)
|
||||
|
@ -252,6 +268,13 @@ public class IdDt extends BasePrimitive<String> {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the ID is a local reference (in other words, it begins with the '#' character)
|
||||
*/
|
||||
public boolean isLocal() {
|
||||
return myUnqualifiedId != null && myUnqualifiedId.isEmpty() == false && myUnqualifiedId.charAt(0) == '#';
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method
|
||||
* but it is provided for consistency with the rest of the API.
|
||||
|
@ -400,11 +423,4 @@ public class IdDt extends BasePrimitive<String> {
|
|||
return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
|
||||
*/
|
||||
public BigDecimal asBigDecimal() {
|
||||
return getIdPartAsBigDecimal();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -43,6 +44,8 @@ import org.thymeleaf.context.Context;
|
|||
import org.thymeleaf.dom.Document;
|
||||
import org.thymeleaf.dom.Element;
|
||||
import org.thymeleaf.dom.Node;
|
||||
import org.thymeleaf.exceptions.TemplateInputException;
|
||||
import org.thymeleaf.messageresolver.StandardMessageResolver;
|
||||
import org.thymeleaf.processor.IProcessor;
|
||||
import org.thymeleaf.processor.ProcessorResult;
|
||||
import org.thymeleaf.processor.attr.AbstractAttrProcessor;
|
||||
|
@ -51,8 +54,13 @@ import org.thymeleaf.standard.StandardDialect;
|
|||
import org.thymeleaf.standard.expression.IStandardExpression;
|
||||
import org.thymeleaf.standard.expression.IStandardExpressionParser;
|
||||
import org.thymeleaf.standard.expression.StandardExpressions;
|
||||
import org.thymeleaf.templatemode.StandardTemplateModeHandlers;
|
||||
import org.thymeleaf.templateparser.ITemplateParser;
|
||||
import org.thymeleaf.templateparser.xmlsax.XhtmlAndHtml5NonValidatingSAXTemplateParser;
|
||||
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
|
||||
import org.thymeleaf.templateresolver.TemplateResolver;
|
||||
import org.thymeleaf.util.DOMUtils;
|
||||
import org.thymeleaf.util.Validate;
|
||||
import org.w3c.dom.Text;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
|
@ -66,114 +74,37 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGenerator {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseThymeleafNarrativeGenerator.class);
|
||||
|
||||
private static final XhtmlAndHtml5NonValidatingSAXTemplateParser PARSER = new XhtmlAndHtml5NonValidatingSAXTemplateParser(1);
|
||||
|
||||
private Configuration configuration;
|
||||
private boolean myApplyDefaultDatatypeTemplates = true;
|
||||
private HashMap<Class<?>, String> myClassToName;
|
||||
|
||||
private boolean myCleanWhitespace = true;
|
||||
private boolean myIgnoreFailures = true;
|
||||
private boolean myIgnoreMissingTemplates = true;
|
||||
|
||||
private TemplateEngine myProfileTemplateEngine;
|
||||
private TemplateEngine myTitleTemplateEngine;
|
||||
private HashMap<String, String> myProfileToName;
|
||||
private HashMap<Class<?>, String> myClassToName;
|
||||
private HashMap<String, String> myNameToNarrativeTemplate;
|
||||
private boolean myApplyDefaultDatatypeTemplates = true;
|
||||
private volatile boolean myInitialized;
|
||||
|
||||
private HashMap<String, String> myNameToNarrativeTemplate;
|
||||
private HashMap<String, String> myNameToTitleTemplate;
|
||||
private TemplateEngine myProfileTemplateEngine;
|
||||
|
||||
private HashMap<String, String> myProfileToName;
|
||||
|
||||
private TemplateEngine myTitleTemplateEngine;
|
||||
|
||||
public BaseThymeleafNarrativeGenerator() {
|
||||
configuration = new Configuration();
|
||||
configuration.addTemplateResolver(new ClassLoaderTemplateResolver());
|
||||
configuration.addMessageResolver(new StandardMessageResolver());
|
||||
configuration.setTemplateModeHandlers(StandardTemplateModeHandlers.ALL_TEMPLATE_MODE_HANDLERS);
|
||||
configuration.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NarrativeDt generateNarrative(IResource theResource) {
|
||||
return generateNarrative(null, theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IResource theResource) {
|
||||
return generateTitle(null, theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IResource theResource) {
|
||||
if (!myInitialized) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
ourLog.trace("Generating resource title {}", theResource);
|
||||
|
||||
String name = null;
|
||||
if (StringUtils.isNotBlank(theProfile)) {
|
||||
name = myProfileToName.get(theProfile);
|
||||
}
|
||||
if (name == null) {
|
||||
name = myClassToName.get(theResource.getClass());
|
||||
}
|
||||
|
||||
ourLog.trace("Template name is {}", name);
|
||||
|
||||
if (name == null) {
|
||||
if (myIgnoreMissingTemplates) {
|
||||
ourLog.debug("No title template available for profile: {}", theProfile);
|
||||
return null;
|
||||
} else {
|
||||
throw new DataFormatException("No title template for class " + theResource.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Context context = new Context();
|
||||
context.setVariable("resource", theResource);
|
||||
|
||||
String result = myTitleTemplateEngine.process(name, context);
|
||||
|
||||
ourLog.trace("Produced {}", result);
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < result.length(); i++) {
|
||||
char nextChar = result.charAt(i);
|
||||
char prevChar = i > 0 ? result.charAt(i - 1) : '\n';
|
||||
if (nextChar == '<') {
|
||||
inTag = true;
|
||||
continue;
|
||||
} else if (inTag) {
|
||||
if (nextChar == '>') {
|
||||
inTag = false;
|
||||
}
|
||||
continue;
|
||||
} else if (nextChar <= ' ') {
|
||||
if (prevChar <= ' ' || prevChar == '>') {
|
||||
continue;
|
||||
} else {
|
||||
b.append(' ');
|
||||
}
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
|
||||
while (b.length() > 0 && b.charAt(b.length()-1) == ' ') {
|
||||
b.setLength(b.length() - 1);
|
||||
}
|
||||
|
||||
result = b.toString();
|
||||
if (result.startsWith("<") && result.contains(">")) {
|
||||
result = result.substring(result.indexOf('>') + 1);
|
||||
}
|
||||
if (result.endsWith(">") && result.contains("<")) {
|
||||
result = result.substring(0, result.lastIndexOf('<'));
|
||||
}
|
||||
|
||||
result = result.replace(">", ">").replace("<", "<").replace("&", "&");
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
if (myIgnoreFailures) {
|
||||
ourLog.error("Failed to generate narrative", e);
|
||||
return "No title available - Error: " + e.getMessage();
|
||||
} else {
|
||||
throw new DataFormatException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NarrativeDt generateNarrative(String theProfile, IResource theResource) {
|
||||
if (!myInitialized) {
|
||||
|
@ -221,13 +152,113 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IResource theResource) {
|
||||
return generateTitle(null, theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IResource theResource) {
|
||||
if (!myInitialized) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
ourLog.trace("Generating resource title {}", theResource);
|
||||
|
||||
String name = null;
|
||||
if (StringUtils.isNotBlank(theProfile)) {
|
||||
name = myProfileToName.get(theProfile);
|
||||
}
|
||||
if (name == null) {
|
||||
name = myClassToName.get(theResource.getClass());
|
||||
}
|
||||
|
||||
ourLog.trace("Template name is {}", name);
|
||||
|
||||
if (name == null) {
|
||||
if (myIgnoreMissingTemplates) {
|
||||
ourLog.debug("No title template available for profile: {}", theProfile);
|
||||
return null;
|
||||
} else {
|
||||
throw new DataFormatException("No title template for class " + theResource.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Context context = new Context();
|
||||
context.setVariable("resource", theResource);
|
||||
|
||||
String result = myTitleTemplateEngine.process(name, context);
|
||||
|
||||
ourLog.trace("Produced {}", result);
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < result.length(); i++) {
|
||||
char nextChar = result.charAt(i);
|
||||
char prevChar = i > 0 ? result.charAt(i - 1) : '\n';
|
||||
if (nextChar == '<') {
|
||||
inTag = true;
|
||||
continue;
|
||||
} else if (inTag) {
|
||||
if (nextChar == '>') {
|
||||
inTag = false;
|
||||
}
|
||||
continue;
|
||||
} else if (nextChar <= ' ') {
|
||||
if (prevChar <= ' ' || prevChar == '>') {
|
||||
continue;
|
||||
} else {
|
||||
b.append(' ');
|
||||
}
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
|
||||
while (b.length() > 0 && b.charAt(b.length() - 1) == ' ') {
|
||||
b.setLength(b.length() - 1);
|
||||
}
|
||||
|
||||
result = b.toString();
|
||||
if (result.startsWith("<") && result.contains(">")) {
|
||||
result = result.substring(result.indexOf('>') + 1);
|
||||
}
|
||||
if (result.endsWith(">") && result.contains("<")) {
|
||||
result = result.substring(0, result.lastIndexOf('<'));
|
||||
}
|
||||
|
||||
result = result.replace(">", ">").replace("<", "<").replace("&", "&");
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
if (myIgnoreFailures) {
|
||||
ourLog.error("Failed to generate narrative", e);
|
||||
return "No title available - Error: " + e.getMessage();
|
||||
} else {
|
||||
throw new DataFormatException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<String> getPropertyFile();
|
||||
|
||||
private Document getXhtmlDOMFor(final Reader source) {
|
||||
final Configuration configuration1 = configuration;
|
||||
try {
|
||||
return PARSER.parseTemplate(configuration1, "input", source);
|
||||
} catch (final Exception e) {
|
||||
throw new TemplateInputException("Exception during parsing of source", e);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void initialize() {
|
||||
if (myInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ourLog.info("Initializing narrative generator");
|
||||
|
||||
|
||||
myProfileToName = new HashMap<String, String>();
|
||||
myClassToName = new HashMap<Class<?>, String>();
|
||||
myNameToNarrativeTemplate = new HashMap<String, String>();
|
||||
|
@ -274,8 +305,6 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
myInitialized = true;
|
||||
}
|
||||
|
||||
protected abstract List<String> getPropertyFile();
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
|
||||
* <p>
|
||||
|
@ -301,31 +330,6 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
return myIgnoreMissingTemplates;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
|
||||
* <p>
|
||||
* Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g. "\n \n ") will be trimmed to a single space.
|
||||
* </p>
|
||||
*/
|
||||
public void setCleanWhitespace(boolean theCleanWhitespace) {
|
||||
myCleanWhitespace = theCleanWhitespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the generator will suppress any generated exceptions, and simply return a default narrative
|
||||
* indicating that no narrative is available.
|
||||
*/
|
||||
public void setIgnoreFailures(boolean theIgnoreFailures) {
|
||||
myIgnoreFailures = theIgnoreFailures;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, will return an empty narrative block for any profiles where no template is available
|
||||
*/
|
||||
public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) {
|
||||
myIgnoreMissingTemplates = theIgnoreMissingTemplates;
|
||||
}
|
||||
|
||||
private void loadProperties(String propFileName) throws IOException {
|
||||
ourLog.debug("Loading narrative properties file: {}", propFileName);
|
||||
|
||||
|
@ -429,6 +433,31 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
|
||||
* <p>
|
||||
* Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g. "\n \n ") will be trimmed to a single space.
|
||||
* </p>
|
||||
*/
|
||||
public void setCleanWhitespace(boolean theCleanWhitespace) {
|
||||
myCleanWhitespace = theCleanWhitespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the generator will suppress any generated exceptions, and simply return a default narrative
|
||||
* indicating that no narrative is available.
|
||||
*/
|
||||
public void setIgnoreFailures(boolean theIgnoreFailures) {
|
||||
myIgnoreFailures = theIgnoreFailures;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to true, will return an empty narrative block for any profiles where no template is available
|
||||
*/
|
||||
public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) {
|
||||
myIgnoreMissingTemplates = theIgnoreMissingTemplates;
|
||||
}
|
||||
|
||||
static String cleanWhitespace(String theResult) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean inWhitespace = false;
|
||||
|
@ -504,11 +533,13 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
context.setVariable("resource", value);
|
||||
|
||||
String name = null;
|
||||
Class<? extends Object> nextClass = value.getClass();
|
||||
do {
|
||||
name = myClassToName.get(nextClass);
|
||||
nextClass=nextClass.getSuperclass();
|
||||
} while (name == null && nextClass.equals(Object.class)==false);
|
||||
if (value != null) {
|
||||
Class<? extends Object> nextClass = value.getClass();
|
||||
do {
|
||||
name = myClassToName.get(nextClass);
|
||||
nextClass = nextClass.getSuperclass();
|
||||
} while (name == null && nextClass.equals(Object.class) == false);
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
if (myIgnoreMissingTemplates) {
|
||||
|
@ -521,7 +552,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
|
||||
String result = myProfileTemplateEngine.process(name, context);
|
||||
String trim = result.trim();
|
||||
Document dom = DOMUtils.getXhtmlDOMFor(new StringReader(trim));
|
||||
Document dom = getXhtmlDOMFor(new StringReader(trim));
|
||||
|
||||
Element firstChild = (Element) dom.getFirstChild();
|
||||
for (int i = 0; i < firstChild.getChildren().size(); i++) {
|
||||
|
|
|
@ -21,14 +21,14 @@ package ca.uhn.fhir.narrative;
|
|||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGenerator {
|
||||
|
||||
private String myPropertyFile;
|
||||
private List<String> myPropertyFile;
|
||||
|
||||
/**
|
||||
* Create a new narrative generator
|
||||
|
@ -42,7 +42,7 @@ public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGen
|
|||
* @throws IOException
|
||||
* If the file can not be found/read
|
||||
*/
|
||||
public CustomThymeleafNarrativeGenerator(String thePropertyFile) throws IOException {
|
||||
public CustomThymeleafNarrativeGenerator(String... thePropertyFile) throws IOException {
|
||||
setPropertyFile(thePropertyFile);
|
||||
}
|
||||
|
||||
|
@ -58,14 +58,14 @@ public class CustomThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGen
|
|||
* @throws IOException
|
||||
* If the file can not be found/read
|
||||
*/
|
||||
public void setPropertyFile(String thePropertyFile) {
|
||||
public void setPropertyFile(String... thePropertyFile) {
|
||||
Validate.notNull(thePropertyFile, "Property file can not be null");
|
||||
myPropertyFile = thePropertyFile;
|
||||
myPropertyFile = Arrays.asList(thePropertyFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPropertyFile() {
|
||||
return Collections.singletonList(myPropertyFile);
|
||||
return myPropertyFile;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
|
|||
|
||||
public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGenerator implements INarrativeGenerator {
|
||||
|
||||
static final String NARRATIVES_PROPERTIES = "classpath:ca/uhn/fhir/narrative/narratives.properties";
|
||||
public static final String NARRATIVES_PROPERTIES = "classpath:ca/uhn/fhir/narrative/narratives.properties";
|
||||
static final String HAPISERVER_NARRATIVES_PROPERTIES = "classpath:ca/uhn/fhir/narrative/narratives-hapiserver.properties";
|
||||
|
||||
private boolean myUseHapiServerConformanceNarrative;
|
||||
|
|
|
@ -36,6 +36,7 @@ import java.util.Map;
|
|||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
|
@ -57,13 +58,37 @@ public abstract class BaseClient {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
|
||||
|
||||
private final HttpClient myClient;
|
||||
private EncodingEnum myEncoding = null; // default unspecified (will be XML)
|
||||
private List<IClientInterceptor> myInterceptors = new ArrayList<IClientInterceptor>();
|
||||
private boolean myKeepResponses = false;
|
||||
private HttpResponse myLastResponse;
|
||||
private String myLastResponseBody;
|
||||
private final String myUrlBase;
|
||||
private EncodingEnum myEncoding = null; // default unspecified (will be XML)
|
||||
private boolean myPrettyPrint = false;
|
||||
|
||||
private final String myUrlBase;
|
||||
|
||||
BaseClient(HttpClient theClient, String theUrlBase) {
|
||||
super();
|
||||
myClient = theClient;
|
||||
myUrlBase = theUrlBase;
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> createExtraParams() {
|
||||
HashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
|
||||
|
||||
if (getEncoding() == EncodingEnum.XML) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("xml"));
|
||||
} else if (getEncoding() == EncodingEnum.JSON) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
|
||||
}
|
||||
|
||||
if (isPrettyPrint()) {
|
||||
retVal.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the
|
||||
* FHIR specification)
|
||||
|
@ -72,21 +97,6 @@ public abstract class BaseClient {
|
|||
return myEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the FHIR
|
||||
* specification)
|
||||
*/
|
||||
public BaseClient setEncoding(EncodingEnum theEncoding) {
|
||||
myEncoding = theEncoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
BaseClient(HttpClient theClient, String theUrlBase) {
|
||||
super();
|
||||
myClient = theClient;
|
||||
myUrlBase = theUrlBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
*/
|
||||
|
@ -105,6 +115,10 @@ public abstract class BaseClient {
|
|||
return myUrlBase;
|
||||
}
|
||||
|
||||
public String getUrlBase() {
|
||||
return myUrlBase;
|
||||
}
|
||||
|
||||
<T> T invokeClient(IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation) {
|
||||
return invokeClient(binding, clientInvocation, false);
|
||||
}
|
||||
|
@ -128,7 +142,16 @@ public abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -151,11 +174,10 @@ public abstract class BaseClient {
|
|||
list.add(next.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
|
||||
String body=null;
|
||||
Reader reader=null;
|
||||
String body = null;
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = createReaderFromResponse(response);
|
||||
body = IOUtils.toString(reader);
|
||||
|
@ -164,17 +186,17 @@ public abstract class BaseClient {
|
|||
} finally {
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
|
||||
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
|
||||
|
||||
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
|
||||
if (Constants.CT_TEXT.equals(mimeType)) {
|
||||
message = message+": " + body;
|
||||
message = message + ": " + body;
|
||||
}
|
||||
|
||||
|
||||
keepResponseAndLogIt(theLogRequestAndResponse, response, body);
|
||||
|
||||
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
|
||||
|
||||
if(body!=null) {
|
||||
if (body != null) {
|
||||
exception.setResponseBody(body);
|
||||
}
|
||||
|
||||
|
@ -185,21 +207,21 @@ public abstract class BaseClient {
|
|||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
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 {
|
||||
|
@ -207,7 +229,7 @@ public abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reader reader = createReaderFromResponse(response);
|
||||
|
||||
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
|
||||
|
@ -237,44 +259,50 @@ public abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
*/
|
||||
public boolean isKeepResponses() {
|
||||
return myKeepResponses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return myPrettyPrint;
|
||||
}
|
||||
|
||||
private void keepResponseAndLogIt(boolean theLogRequestAndResponse, HttpResponse response, String responseString) {
|
||||
if (myKeepResponses) {
|
||||
myLastResponse = response;
|
||||
myLastResponseBody = responseString;
|
||||
}
|
||||
if (theLogRequestAndResponse) {
|
||||
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
|
||||
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
|
||||
if (StringUtils.isNotBlank(responseString)) {
|
||||
ourLog.info("Client response: {}\n{}", message, responseString);
|
||||
}else {
|
||||
} else {
|
||||
ourLog.info("Client response: {}", message, responseString);
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> createExtraParams() {
|
||||
HashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
|
||||
|
||||
if (getEncoding() == EncodingEnum.XML) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("xml"));
|
||||
} else if (getEncoding() == EncodingEnum.JSON) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
|
||||
}
|
||||
|
||||
if (isPrettyPrint()) {
|
||||
retVal.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
public void registerInterceptor(IClientInterceptor theInterceptor) {
|
||||
Validate.notNull(theInterceptor, "Interceptor can not be null");
|
||||
myInterceptors.add(theInterceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the FHIR
|
||||
* specification)
|
||||
*/
|
||||
public boolean isKeepResponses() {
|
||||
return myKeepResponses;
|
||||
public BaseClient setEncoding(EncodingEnum theEncoding) {
|
||||
myEncoding = theEncoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -298,6 +326,20 @@ public abstract class BaseClient {
|
|||
myLastResponseBody = theLastResponseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public BaseClient setPrettyPrint(boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void unregisterInterceptor(IClientInterceptor theInterceptor) {
|
||||
Validate.notNull(theInterceptor, "Interceptor can not be null");
|
||||
myInterceptors.remove(theInterceptor);
|
||||
}
|
||||
|
||||
public static Reader createReaderFromResponse(HttpResponse theResponse) throws IllegalStateException, IOException {
|
||||
HttpEntity entity = theResponse.getEntity();
|
||||
if (entity == null) {
|
||||
|
@ -317,21 +359,4 @@ public abstract class BaseClient {
|
|||
return reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return myPrettyPrint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public BaseClient setPrettyPrint(boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -602,6 +602,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
@Override
|
||||
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (respType == null) {
|
||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
}
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseResource(myType, theResponseReader);
|
||||
}
|
||||
|
@ -653,6 +656,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
@Override
|
||||
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (respType == null) {
|
||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
}
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseTagList(theResponseReader);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
|
||||
public interface IClientInterceptor {
|
||||
|
||||
void interceptRequest(HttpRequestBase theRequest);
|
||||
|
||||
void interceptResponse(HttpResponse theRequest);
|
||||
|
||||
}
|
|
@ -27,7 +27,7 @@ import ca.uhn.fhir.parser.IParser;
|
|||
|
||||
public enum EncodingEnum {
|
||||
|
||||
XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML, Constants.CT_XML, Constants.FORMAT_XML) {
|
||||
XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML, "text/xml", Constants.FORMAT_XML) {
|
||||
@Override
|
||||
public IParser newParser(FhirContext theContext) {
|
||||
return theContext.newXmlParser();
|
||||
|
|
|
@ -91,13 +91,12 @@ public class RestfulServer extends HttpServlet {
|
|||
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
||||
private Collection<IResourceProvider> myResourceProviders;
|
||||
private ISecurityManager mySecurityManager;
|
||||
private IServerAddressStrategy myServerAddressStrategy= new IncomingRequestAddressStrategy();
|
||||
private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy();
|
||||
private BaseMethodBinding<?> myServerConformanceMethod;
|
||||
private Object myServerConformanceProvider;
|
||||
private String myServerName = "HAPI FHIR Server";
|
||||
/** This is configurable but by default we just use HAPI version */
|
||||
private String myServerVersion = VersionUtil.getVersion();
|
||||
|
||||
private boolean myStarted;
|
||||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
|
||||
|
@ -116,8 +115,7 @@ public class RestfulServer extends HttpServlet {
|
|||
/**
|
||||
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
||||
* <p>
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid
|
||||
* inadvertantly disabling functionality.
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling functionality.
|
||||
* </p>
|
||||
*/
|
||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||
|
@ -248,8 +246,8 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
|
||||
* providers should generally use this context if one is needed, as opposed to creating their own.
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||
* creating their own.
|
||||
*/
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
|
@ -289,21 +287,18 @@ public class RestfulServer extends HttpServlet {
|
|||
public ISecurityManager getSecurityManager() {
|
||||
return mySecurityManager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the server address strategy, which is used to determine what base URL to
|
||||
* provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return myServerAddressStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||
* if you do not wish to export a conformance statement.
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
|
@ -311,8 +306,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(StringDt)
|
||||
*/
|
||||
|
@ -325,8 +319,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||
* only, but can be helpful to set with something appropriate.
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
|
@ -363,8 +356,9 @@ public class RestfulServer extends HttpServlet {
|
|||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
||||
boolean respondGzip=theRequest.isRespondGzip();
|
||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction, respondGzip);
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
streamResponseAsBundle(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start,
|
||||
count, thePagingAction, respondGzip);
|
||||
|
||||
}
|
||||
|
||||
|
@ -489,16 +483,16 @@ public class RestfulServer extends HttpServlet {
|
|||
// TODO: look for more tokens for version, compartments, etc...
|
||||
|
||||
String acceptEncoding = theRequest.getHeader(Constants.HEADER_ACCEPT_ENCODING);
|
||||
boolean respondGzip=false;
|
||||
boolean respondGzip = false;
|
||||
if (acceptEncoding != null) {
|
||||
String[] parts = acceptEncoding.trim().split("\\s*,\\s*");
|
||||
for (String string : parts) {
|
||||
if (string.equals("gzip")) {
|
||||
respondGzip=true;
|
||||
respondGzip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Request r = new Request();
|
||||
r.setResourceName(resourceName);
|
||||
r.setId(id);
|
||||
|
@ -546,28 +540,27 @@ public class RestfulServer extends HttpServlet {
|
|||
theResponse.setContentType("text/plain");
|
||||
theResponse.setCharacterEncoding("UTF-8");
|
||||
theResponse.getWriter().write(e.getMessage());
|
||||
|
||||
|
||||
} catch (Throwable e) {
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
Issue issue = oo.addIssue();
|
||||
issue.getSeverity().setValueAsEnum(IssueSeverityEnum.ERROR);
|
||||
|
||||
|
||||
int statusCode = 500;
|
||||
if (e instanceof InternalErrorException) {
|
||||
ourLog.error("Failure during REST processing", e);
|
||||
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
||||
} else if (e instanceof BaseServerResponseException) {
|
||||
ourLog.warn("Failure during REST processing: {}", e.toString());
|
||||
statusCode=((BaseServerResponseException) e).getStatusCode();
|
||||
statusCode = ((BaseServerResponseException) e).getStatusCode();
|
||||
issue.getDetails().setValue(e.getMessage());
|
||||
} else {
|
||||
ourLog.error("Failure during REST processing", e);
|
||||
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
|
||||
|
||||
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, statusCode,false);
|
||||
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, statusCode, false);
|
||||
|
||||
theResponse.setStatus(statusCode);
|
||||
addHeadersToResponse(theResponse);
|
||||
|
@ -581,9 +574,8 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
|
||||
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
|
||||
* initialization of the restful server's internal init.
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
|
||||
* called immediately before beginning initialization of the restful server's internal init.
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
|
@ -637,8 +629,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
|
||||
* server being used.
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||
*/
|
||||
protected void initialize() throws ServletException {
|
||||
// nothing by default
|
||||
|
@ -653,6 +644,11 @@ public class RestfulServer extends HttpServlet {
|
|||
return userAgent != null && userAgent.contains("Mozilla");
|
||||
}
|
||||
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
Validate.notNull(theFhirContext, "FhirContext must not be null");
|
||||
myFhirContext = theFhirContext;
|
||||
}
|
||||
|
||||
public void setImplementationDescription(String theImplementationDescription) {
|
||||
myImplementationDescription = theImplementationDescription;
|
||||
}
|
||||
|
@ -730,8 +726,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provide a server address strategy, which is used to determine what base URL to
|
||||
* provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
||||
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
||||
|
@ -739,17 +734,14 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code>
|
||||
* if you do not wish to export a conformance statement.
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
|
||||
* {@link IllegalStateException} if called after that.
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myStarted) {
|
||||
|
@ -759,8 +751,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(StringDt)
|
||||
*/
|
||||
|
@ -769,16 +760,15 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||
* only, but can be helpful to set with something appropriate.
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of
|
||||
* standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
|
||||
* instead of a FHIR
|
||||
*/
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
|
@ -793,7 +783,7 @@ public class RestfulServer extends HttpServlet {
|
|||
bundle.getLinkSelf().setValue(theCompleteUrl);
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
|
@ -803,7 +793,7 @@ public class RestfulServer extends HttpServlet {
|
|||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
|
||||
bundle.addResource(next, theContext, theServerBase);
|
||||
}
|
||||
|
||||
|
@ -942,7 +932,7 @@ public class RestfulServer extends HttpServlet {
|
|||
if (theRespondGzip) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
||||
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
|
||||
}else {
|
||||
} else {
|
||||
writer = theHttpResponse.getWriter();
|
||||
}
|
||||
return writer;
|
||||
|
@ -973,8 +963,9 @@ public class RestfulServer extends HttpServlet {
|
|||
return prettyPrint;
|
||||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||
NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip) throws IOException {
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase,
|
||||
String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, boolean theRespondGzip)
|
||||
throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
@ -1059,13 +1050,14 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip) throws IOException {
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip) throws IOException {
|
||||
int stausCode = 200;
|
||||
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip);
|
||||
}
|
||||
|
||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip)
|
||||
throws IOException {
|
||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
if (theResource instanceof Binary) {
|
||||
|
|
|
@ -34,5 +34,7 @@ public class ExtensionConstants {
|
|||
public static final String CONF_ADDITIONAL_PARAM_TYPE = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamType";
|
||||
|
||||
public static final String CONF_ADDITIONAL_PARAM_REQUIRED = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamRequired";
|
||||
|
||||
public static final String CONF_RESOURCE_COUNT = "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount";
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,13 @@ TR.hapiTableOfValuesRowEven TD {
|
|||
********************************************************/
|
||||
|
||||
TABLE.hapiPropertyTable TBODY TR TD:FIRST-CHILD {
|
||||
background: #C0C0C0;
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
background: #C0C0C0;
|
||||
border-top: 1px solid #EEE;
|
||||
}
|
||||
|
||||
TABLE.hapiPropertyTable TBODY TR TD {
|
||||
vertical-align: top;
|
||||
padding: 4px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,14 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
|
||||
|
||||
<!-- Spring -->
|
||||
|
|
|
@ -47,6 +47,7 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -170,6 +171,30 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
}
|
||||
|
||||
private Set<Long> addPredicateId(String theParamName, Set<Long> theExistingPids, Set<Long> thePids) {
|
||||
if (thePids == null || thePids.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName);
|
||||
Predicate idPrecidate = from.get("myId").in(thePids);
|
||||
|
||||
cq.where(builder.and(typePredicate, idPrecidate));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
HashSet<Long> found = new HashSet<Long>(q.getResultList());
|
||||
if (!theExistingPids.isEmpty()) {
|
||||
theExistingPids.retainAll(found);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private Set<Long> addPredicateQuantity(String theParamName, Set<Long> thePids, List<IQueryParameterType> theOrParams) {
|
||||
if (theOrParams == null || theOrParams.isEmpty()) {
|
||||
return thePids;
|
||||
|
@ -475,32 +500,9 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
return new HashSet<Long>(q.getResultList());
|
||||
}
|
||||
|
||||
private Set<Long> addPredicateId(String theParamName, Set<Long> theExistingPids, Set<Long> thePids) {
|
||||
if (thePids == null || thePids.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName);
|
||||
Predicate idPrecidate = from.get("myId").in(thePids);
|
||||
|
||||
cq.where(builder.and(typePredicate, idPrecidate));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
HashSet<Long> found = new HashSet<Long>(q.getResultList());
|
||||
if (!theExistingPids.isEmpty()) {
|
||||
theExistingPids.retainAll(found);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTag(IdDt theId, String theScheme, String theTerm, String theLabel) {
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theId);
|
||||
if (entity == null) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
|
@ -518,10 +520,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
myEntityManager.persist(newEntity);
|
||||
myEntityManager.merge(entity);
|
||||
notifyWriteCompleted();
|
||||
ourLog.info("Processed addTag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId, w.getMillisAndRestart() });
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome create(final T theResource) {
|
||||
StopWatch w = new StopWatch();
|
||||
ResourceTable entity = new ResourceTable();
|
||||
entity.setResourceType(toResourceName(theResource));
|
||||
|
||||
|
@ -529,12 +533,32 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
MethodOutcome outcome = toMethodOutcome(entity);
|
||||
notifyWriteCompleted();
|
||||
ourLog.info("Processed create on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||
return outcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome delete(IdDt theId) {
|
||||
StopWatch w = new StopWatch();
|
||||
final ResourceTable entity = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart() && theId.getVersionIdPartAsLong().longValue() != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to update " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
ResourceTable savedEntity = updateEntity(null, entity, true, true);
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
||||
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
|
||||
return toMethodOutcome(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagList getAllResourceTags() {
|
||||
return super.getTags(myResourceType, null);
|
||||
StopWatch w = new StopWatch();
|
||||
TagList tags = super.getTags(myResourceType, null);
|
||||
ourLog.info("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||
return tags;
|
||||
}
|
||||
|
||||
public Class<T> getResourceType() {
|
||||
|
@ -543,16 +567,23 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
@Override
|
||||
public TagList getTags(IdDt theResourceId) {
|
||||
return super.getTags(myResourceType, theResourceId);
|
||||
StopWatch w = new StopWatch();
|
||||
TagList retVal = super.getTags(myResourceType, theResourceId);
|
||||
ourLog.info("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider history(Date theSince) {
|
||||
return super.history(myResourceName, null, theSince);
|
||||
StopWatch w = new StopWatch();
|
||||
IBundleProvider retVal = super.history(myResourceName, null, theSince);
|
||||
ourLog.info("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider history(final IdDt theId, final Date theSince) {
|
||||
StopWatch w = new StopWatch();
|
||||
final InstantDt end = createHistoryToTimestamp();
|
||||
final String resourceType = getContext().getResourceDefinition(myResourceType).getName();
|
||||
|
||||
|
@ -596,8 +627,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
return new IBundleProvider() {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return count;
|
||||
public InstantDt getPublished() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -632,8 +663,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
return end;
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -641,7 +672,30 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Long theId, Date theSince) {
|
||||
return super.history(myResourceName, theId, theSince);
|
||||
StopWatch w = new StopWatch();
|
||||
IBundleProvider retVal = super.history(myResourceName, theId, theSince);
|
||||
ourLog.info("Processed history on {} in {}ms", theId, w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IResource> theResourceListToPopulate) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(builder.equal(from.get("myResourceType"), getContext().getResourceDefinition(myResourceType).getName()));
|
||||
if (theIncludePids != null) {
|
||||
cq.where(from.get("myId").in(theIncludePids));
|
||||
}
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
||||
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
T resource = toResource(myResourceType, next);
|
||||
theResourceListToPopulate.add(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
@ -664,6 +718,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
@Override
|
||||
public T read(IdDt theId) {
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theId);
|
||||
|
||||
T retVal = toResource(myResourceType, entity);
|
||||
|
@ -673,6 +728,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
throw new ResourceGoneException("Resource was deleted at " + deleted.getValueAsString());
|
||||
}
|
||||
|
||||
ourLog.info("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -698,6 +754,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
@ -711,6 +768,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
@Override
|
||||
public void removeTag(IdDt theId, String theScheme, String theTerm) {
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theId);
|
||||
if (entity == null) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
|
@ -724,6 +782,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
myEntityManager.merge(entity);
|
||||
|
||||
ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId.getValue(), w.getMillisAndRestart() });
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -738,6 +798,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
@Override
|
||||
public IBundleProvider search(final SearchParameterMap theParams) {
|
||||
StopWatch w = new StopWatch();
|
||||
final InstantDt now = InstantDt.withCurrentTime();
|
||||
|
||||
Set<Long> loadPids;
|
||||
|
@ -762,11 +823,11 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
final ArrayList<Long> pids = new ArrayList<Long>(loadPids);
|
||||
|
||||
return new IBundleProvider() {
|
||||
IBundleProvider retVal = new IBundleProvider() {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return pids.size();
|
||||
public InstantDt getPublished() {
|
||||
return now;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -815,30 +876,14 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
return now;
|
||||
public int size() {
|
||||
return pids.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IResource> theResourceListToPopulate) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ourLog.info("Processed search for {} on {} in {}ms", new Object[] {myResourceName, theParams, w.getMillisAndRestart()});
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(builder.equal(from.get("myResourceType"), getContext().getResourceDefinition(myResourceType).getName()));
|
||||
if (theIncludePids != null) {
|
||||
cq.where(from.get("myId").in(theIncludePids));
|
||||
}
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
||||
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
T resource = toResource(myResourceType, next);
|
||||
theResourceListToPopulate.add(resource);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1006,6 +1051,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
@Override
|
||||
public MethodOutcome update(final T theResource, final IdDt theId) {
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
// TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
// ResourceTable savedEntity = template.execute(new TransactionCallback<ResourceTable>() {
|
||||
|
@ -1024,19 +1070,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
ResourceTable savedEntity = updateEntity(theResource, entity, true, false);
|
||||
|
||||
notifyWriteCompleted();
|
||||
return toMethodOutcome(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome delete(IdDt theId) {
|
||||
final ResourceTable entity = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart() && theId.getVersionIdPartAsLong().longValue() != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to update " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
ResourceTable savedEntity = updateEntity(null, entity, true, true);
|
||||
|
||||
notifyWriteCompleted();
|
||||
ourLog.info("Processed update on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
|
||||
return toMethodOutcome(savedEntity);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.springframework.transaction.annotation.Propagation;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
|
@ -134,12 +135,18 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Date theSince) {
|
||||
return super.history(null, null, theSince);
|
||||
StopWatch w = new StopWatch();
|
||||
IBundleProvider retVal = super.history(null, null, theSince);
|
||||
ourLog.info("Processed global history in {}ms", w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagList getAllTags() {
|
||||
return super.getTags(null, null);
|
||||
StopWatch w = new StopWatch();
|
||||
TagList retVal = super.getTags(null, null);
|
||||
ourLog.info("Processed getAllTags in {}ms", w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||
|
@ -75,7 +76,7 @@ public class SearchParameterMap extends HashMap<String, List<List<IQueryParamete
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this);
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
if (isEmpty() == false) {
|
||||
b.append("params", super.toString());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jboss.logging.MDC;
|
||||
|
||||
public class BaseJpaProvider {
|
||||
|
||||
public static final String REMOTE_ADDR = "req.remoteAddr";
|
||||
public static final String REMOTE_UA = "req.userAgent";
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaProvider.class);
|
||||
|
||||
public void startRequest(HttpServletRequest theRequest) {
|
||||
if (theRequest == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> headerNames = new TreeSet<String>();
|
||||
for (Enumeration<String> enums = theRequest.getHeaderNames(); enums.hasMoreElements();) {
|
||||
headerNames.add(enums.nextElement());
|
||||
}
|
||||
ourLog.debug("Request headers: {}", headerNames);
|
||||
|
||||
Enumeration<String> forwardedFors = theRequest.getHeaders("x-forwarded-for");
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (Enumeration<String> enums = forwardedFors; enums.hasMoreElements();) {
|
||||
if (b.length() > 0) {
|
||||
b.append(" / ");
|
||||
}
|
||||
b.append(enums.nextElement());
|
||||
}
|
||||
|
||||
String forwardedFor = b.toString();
|
||||
String ip = theRequest.getRemoteAddr();
|
||||
if (StringUtils.isBlank(forwardedFor)) {
|
||||
org.slf4j.MDC.put(REMOTE_ADDR, ip);
|
||||
ourLog.debug("Request is from address: {}", ip);
|
||||
} else {
|
||||
org.slf4j.MDC.put(REMOTE_ADDR, forwardedFor);
|
||||
ourLog.debug("Request is from forwarded address: {}", forwardedFor);
|
||||
}
|
||||
|
||||
String userAgent = StringUtils.defaultString(theRequest.getHeader("user-agent"));
|
||||
org.slf4j.MDC.put(REMOTE_UA, userAgent);
|
||||
|
||||
}
|
||||
|
||||
public void endRequest(HttpServletRequest theRequest) {
|
||||
MDC.remove(REMOTE_ADDR);
|
||||
MDC.remove(REMOTE_UA);
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,7 @@ import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
|||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
public class JpaConformanceProvider extends ServerConformanceProvider {
|
||||
|
||||
|
@ -19,27 +20,27 @@ public class JpaConformanceProvider extends ServerConformanceProvider {
|
|||
private IFhirSystemDao mySystemDao;
|
||||
private volatile Conformance myCachedValue;
|
||||
|
||||
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao, @SuppressWarnings("rawtypes") Collection<IFhirResourceDao> theResourceDaos) {
|
||||
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
||||
super(theRestfulServer);
|
||||
mySystemDao = theSystemDao;
|
||||
super.setCache(false);
|
||||
|
||||
for (IFhirResourceDao<?> nextResourceDao : theResourceDaos) {
|
||||
nextResourceDao.registerDaoListener(new IDaoListener() {
|
||||
@Override
|
||||
public void writeCompleted() {
|
||||
myCachedValue = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
// for (IFhirResourceDao<?> nextResourceDao : theResourceDaos) {
|
||||
// nextResourceDao.registerDaoListener(new IDaoListener() {
|
||||
// @Override
|
||||
// public void writeCompleted() {
|
||||
// myCachedValue = null;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public Conformance getServerConformance() {
|
||||
Conformance retVal = myCachedValue;
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
// if (retVal != null) {
|
||||
// return retVal;
|
||||
// }
|
||||
|
||||
Map<String, Long> counts = mySystemDao.getResourceCounts();
|
||||
|
||||
|
@ -48,7 +49,7 @@ public class JpaConformanceProvider extends ServerConformanceProvider {
|
|||
for (RestResource nextResource : nextRest.getResource()) {
|
||||
Long count = counts.get(nextResource.getType().getValueAsString());
|
||||
if (count != null) {
|
||||
nextResource.addUndeclaredExtension(false, "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount", new DecimalDt(count));
|
||||
nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.provider;
|
|||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
|
@ -26,11 +28,11 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
|||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
||||
public class JpaResourceProvider<T extends IResource> implements IResourceProvider {
|
||||
public class JpaResourceProvider<T extends IResource> extends BaseJpaProvider implements IResourceProvider {
|
||||
|
||||
@Autowired(required=true)
|
||||
@Autowired(required = true)
|
||||
private FhirContext myContext;
|
||||
|
||||
|
||||
private IFhirResourceDao<T> myDao;
|
||||
|
||||
public JpaResourceProvider() {
|
||||
|
@ -42,13 +44,23 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome create(@ResourceParam T theResource) {
|
||||
return myDao.create(theResource);
|
||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.create(theResource);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Delete
|
||||
public MethodOutcome delete(@IdParam IdDt theResource) {
|
||||
return myDao.delete(theResource);
|
||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.delete(theResource);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public FhirContext getContext() {
|
||||
|
@ -60,13 +72,23 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
}
|
||||
|
||||
@History
|
||||
public IBundleProvider getHistoryForResourceInstance(@IdParam IdDt theId, @Since Date theDate) {
|
||||
return myDao.history(theId.getIdPartAsLong(), theDate);
|
||||
public IBundleProvider getHistoryForResourceInstance(HttpServletRequest theRequest, @IdParam IdDt theId, @Since Date theDate) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.history(theId.getIdPartAsLong(), theDate);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@History
|
||||
public IBundleProvider getHistoryForResourceType(@Since Date theDate) {
|
||||
return myDao.history(theDate);
|
||||
public IBundleProvider getHistoryForResourceType(HttpServletRequest theRequest, @Since Date theDate) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.history(theDate);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,18 +97,33 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
}
|
||||
|
||||
@GetTags
|
||||
public TagList getTagsForResourceInstance(@IdParam IdDt theResourceId) {
|
||||
return myDao.getTags(theResourceId);
|
||||
public TagList getTagsForResourceInstance(HttpServletRequest theRequest, @IdParam IdDt theResourceId) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.getTags(theResourceId);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@GetTags
|
||||
public TagList getTagsForResourceType() {
|
||||
return myDao.getAllResourceTags();
|
||||
public TagList getTagsForResourceType(HttpServletRequest theRequest) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.getAllResourceTags();
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Read(version=true)
|
||||
public T read(@IdParam IdDt theId) {
|
||||
return myDao.read(theId);
|
||||
@Read(version = true)
|
||||
public T read(HttpServletRequest theRequest, @IdParam IdDt theId) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.read(theId);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContext(FhirContext theContext) {
|
||||
|
@ -99,16 +136,26 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
|
|||
}
|
||||
|
||||
@Update
|
||||
public MethodOutcome update(@ResourceParam T theResource, @IdParam IdDt theId) {
|
||||
return myDao.update(theResource, theId);
|
||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdDt theId) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.update(theResource, theId);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam T theResource) {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(new OperationOutcome());
|
||||
retVal.getOperationOutcome().addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Resource validates successfully");
|
||||
return retVal;
|
||||
public MethodOutcome validate(HttpServletRequest theRequest, @ResourceParam T theResource) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
retVal.setOperationOutcome(new OperationOutcome());
|
||||
retVal.getOperationOutcome().addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Resource validates successfully");
|
||||
return retVal;
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package ca.uhn.fhir.jpa.provider;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
|
@ -15,7 +17,7 @@ import ca.uhn.fhir.rest.annotation.Transaction;
|
|||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public class JpaSystemProvider {
|
||||
public class JpaSystemProvider extends BaseJpaProvider {
|
||||
|
||||
private IFhirSystemDao myDao;
|
||||
|
||||
|
@ -33,19 +35,34 @@ public class JpaSystemProvider {
|
|||
}
|
||||
|
||||
@Transaction
|
||||
public List<IResource> transaction(@TransactionParam List<IResource> theResources) {
|
||||
myDao.transaction(theResources);
|
||||
return theResources;
|
||||
public List<IResource> transaction(HttpServletRequest theRequest, @TransactionParam List<IResource> theResources) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
myDao.transaction(theResources);
|
||||
return theResources;
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@History
|
||||
public IBundleProvider historyServer(@Since Date theDate) {
|
||||
return myDao.history(theDate);
|
||||
public IBundleProvider historyServer(HttpServletRequest theRequest, @Since Date theDate) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.history(theDate);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@GetTags
|
||||
public TagList getAllTagsOnServer() {
|
||||
public TagList getAllTagsOnServer(HttpServletRequest theRequest) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.getAllTags();
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ package ca.uhn.fhir.jpa.test;
|
|||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
|
@ -13,17 +16,26 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
|
||||
import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.PatientResourceProvider;
|
||||
|
@ -38,6 +50,7 @@ public class CompleteResourceProviderTest {
|
|||
private static IFhirResourceDao<Questionnaire> questionnaireDao;
|
||||
private static IGenericClient ourClient;
|
||||
private static IFhirResourceDao<Observation> observationDao;
|
||||
// private static JpaConformanceProvider ourConfProvider;
|
||||
|
||||
// @Test
|
||||
// public void test01UploadTestResources() throws Exception {
|
||||
|
@ -123,7 +136,48 @@ public class CompleteResourceProviderTest {
|
|||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
|
||||
@Test
|
||||
public void testInsertUpdatesConformance() {
|
||||
// Conformance conf = ourConfProvider.getServerConformance();
|
||||
// ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conf));
|
||||
//
|
||||
// RestResource res=null;
|
||||
// for (Rest nextRest : conf.getRest()) {
|
||||
// for (RestResource nextRes : nextRest.getResource()) {
|
||||
// if (nextRes.getType().getValueAsEnum()==ResourceTypeEnum.PATIENT) {
|
||||
// res = nextRes;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// List<ExtensionDt> resCounts = res.getUndeclaredExtensionsByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
|
||||
//
|
||||
// int initial = 0;
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
|
||||
|
||||
ourClient.create(p1).getId();
|
||||
|
||||
// conf = ourConfProvider.getServerConformance();
|
||||
// res=null;
|
||||
// for (Rest nextRest : conf.getRest()) {
|
||||
// for (RestResource nextRes : nextRest.getResource()) {
|
||||
// if (nextRes.getType().getValueAsEnum()==ResourceTypeEnum.PATIENT) {
|
||||
// res = nextRes;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// resCounts = res.getUndeclaredExtensionsByUrl(ExtensionConstants.CONF_RESOURCE_COUNT);
|
||||
// assertNotNull(resCounts);
|
||||
// assertEquals(1, resCounts.size());
|
||||
// DecimalDt number = (DecimalDt) resCounts.get(0).getValue();
|
||||
// assertEquals(initial+1, number.getValueAsInteger());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testInsertBadReference() {
|
||||
Patient p1 = new Patient();
|
||||
|
@ -170,6 +224,10 @@ public class CompleteResourceProviderTest {
|
|||
RestfulServer restServer = new RestfulServer();
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class);
|
||||
|
||||
// ourConfProvider = new JpaConformanceProvider(restServer, systemDao, Collections.singletonList((IFhirResourceDao)patientDao));
|
||||
|
||||
int myPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(myPort);
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@
|
|||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaProvider;
|
||||
|
||||
public class RequestInterceptor implements WebRequestInterceptor {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RequestInterceptor.class);
|
||||
|
||||
@Override
|
||||
public void afterCompletion(WebRequest theArg0, Exception theArg1) throws Exception {
|
||||
org.slf4j.MDC.remove(BaseJpaProvider.REMOTE_ADDR);
|
||||
org.slf4j.MDC.remove(BaseJpaProvider.REMOTE_UA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postHandle(WebRequest theArg0, ModelMap theArg1) throws Exception {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preHandle(WebRequest theRequest) throws Exception {
|
||||
|
||||
String[] forwardedFors = theRequest.getHeaderValues("x-forwarded-for");
|
||||
StringBuilder b = new StringBuilder();
|
||||
if (forwardedFors != null) {
|
||||
for (String enums : forwardedFors) {
|
||||
if (b.length() > 0) {
|
||||
b.append(" / ");
|
||||
}
|
||||
b.append(enums);
|
||||
}
|
||||
}
|
||||
|
||||
String forwardedFor = b.toString();
|
||||
org.slf4j.MDC.put(BaseJpaProvider.REMOTE_ADDR, forwardedFor);
|
||||
|
||||
String userAgent = StringUtils.defaultString(theRequest.getHeader("user-agent"));
|
||||
org.slf4j.MDC.put(BaseJpaProvider.REMOTE_UA, userAgent);
|
||||
|
||||
ourLog.trace("User agent is: {}", userAgent);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -8,10 +8,12 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
@ -46,6 +48,10 @@ public class TestRestfulServer extends RestfulServer {
|
|||
|
||||
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
|
||||
FhirContext ctx = myAppCtx.getBean(FhirContext.class);
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
setFhirContext(ctx);
|
||||
|
||||
Collection<IResourceProvider> beans = myAppCtx.getBeansOfType(IResourceProvider.class).values();
|
||||
for (IResourceProvider nextResourceProvider : beans) {
|
||||
ourLog.info(" * Have resource provider for: {}", nextResourceProvider.getResourceType().getSimpleName());
|
||||
|
@ -58,10 +64,7 @@ public class TestRestfulServer extends RestfulServer {
|
|||
|
||||
String implDesc = getInitParameter("ImplementationDescription");
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Collection<IFhirResourceDao> resourceDaos = myAppCtx.getBeansOfType(IFhirResourceDao.class).values();
|
||||
|
||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao, resourceDaos);
|
||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao);
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
|
||||
|
|
|
@ -13,15 +13,15 @@
|
|||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<file>${fhir.logdir}/logFile.log</file>
|
||||
<file>${fhir.logdir}/fhirtest.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${fhir.logdir}/logFile.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
<fileNamePattern>${fhir.logdir}/fhirtest.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
|
||||
<encoder>
|
||||
<!-- [%file:%line] -->
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n</pattern>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{req.remoteAddr}] [%X{req.userAgent}] %-5level %logger{36} %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
|
||||
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||
|
@ -16,6 +17,7 @@
|
|||
<value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>
|
||||
<value>blaze , Blaze (Orion Health) , https://his-medicomp-gateway.orionhealth.com/blaze/fhir</value>
|
||||
<value>oridashi , Oridashi , http://demo.oridashi.com.au:8190</value>
|
||||
<value>fhirbase , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
@ -25,6 +27,11 @@
|
|||
<property name="socketTimeout" value="10000"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<mvc:interceptors>
|
||||
<mvc:interceptor>
|
||||
<mvc:mapping path="/**"/>
|
||||
<bean class="ca.uhn.fhirtest.RequestInterceptor"></bean>
|
||||
</mvc:interceptor>
|
||||
</mvc:interceptors>
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package ca.uhn.fhirtest;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
|
@ -25,11 +26,11 @@
|
|||
<version>${thymeleaf-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring4</artifactId>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring4</artifactId>
|
||||
<version>${thymeleaf-version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
|
@ -78,7 +79,8 @@
|
|||
<version>${derby_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
|
||||
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId>
|
||||
<version>2.3.2</version> </dependency> -->
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
|
@ -183,6 +185,30 @@
|
|||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>1.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copyNarrativeCss</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<copy todir="${project.build.directory}/${project.build.finalName}/css" flatten="true"
|
||||
failonerror="true">
|
||||
<resources>
|
||||
<file
|
||||
file="${basedir}/../hapi-fhir-base/src/main/resources/ca/uhn/fhir/narrative/hapi-narrative.css" />
|
||||
</resources>
|
||||
</copy>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||
import ca.uhn.fhir.rest.gclient.StringParam;
|
||||
|
@ -73,8 +74,6 @@ public class Controller {
|
|||
@RequestMapping(value = { "/about" })
|
||||
public String actionAbout(final HomeRequest theRequest, final ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
theModel.put("notHome", true);
|
||||
theModel.put("extraBreadcrumb", "About");
|
||||
|
@ -87,12 +86,17 @@ public class Controller {
|
|||
addCommonParams(theRequest, theModel);
|
||||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
ResultType returnsResource = ResultType.RESOURCE;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
try {
|
||||
client.conformance();
|
||||
} catch (Exception e) {
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
processAndAddLastClientInvocation(client, ResultType.RESOURCE, theModel, delay, "Loaded conformance");
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Loaded conformance");
|
||||
|
||||
return "result";
|
||||
}
|
||||
|
@ -109,8 +113,6 @@ public class Controller {
|
|||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
RuntimeResourceDefinition def;
|
||||
try {
|
||||
def = getResourceType(theReq);
|
||||
|
@ -132,8 +134,7 @@ public class Controller {
|
|||
try {
|
||||
client.delete(def.getImplementingClass(), new IdDt(id));
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
returnsResource = handleClientException(client,e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription);
|
||||
|
@ -141,14 +142,24 @@ public class Controller {
|
|||
return "result";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@RequestMapping(value = { "/get-tags" })
|
||||
public String actionGetTags(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
Class<? extends IResource> resType = null;
|
||||
ResultType returnsResource = ResultType.TAGLIST;
|
||||
String outcomeDescription = "Tag List";
|
||||
|
@ -180,8 +191,7 @@ public class Controller {
|
|||
client.getTags().execute();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
|
@ -205,10 +215,6 @@ public class Controller {
|
|||
@RequestMapping(value = { "/", "/home" })
|
||||
public String actionHome(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
return "home";
|
||||
}
|
||||
|
||||
|
@ -218,8 +224,6 @@ public class Controller {
|
|||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
String url = defaultString(theReq.getParameter("page-url"));
|
||||
if (!url.startsWith(theModel.get("base").toString())) {
|
||||
theModel.put("errorMsg", "Invalid page URL: " + url);
|
||||
|
@ -234,8 +238,7 @@ public class Controller {
|
|||
try {
|
||||
client.loadPage().url(url).execute();
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
|
@ -252,8 +255,6 @@ public class Controller {
|
|||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
RuntimeResourceDefinition def;
|
||||
try {
|
||||
def = getResourceType(theReq);
|
||||
|
@ -281,8 +282,7 @@ public class Controller {
|
|||
try {
|
||||
client.read(def.getImplementingClass(), new IdDt(def.getName(), id, versionId));
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
|
@ -293,10 +293,9 @@ public class Controller {
|
|||
|
||||
@RequestMapping({ "/resource" })
|
||||
public String actionResource(final ResourceRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
Conformance conformance = addCommonParams(theRequest, theModel);
|
||||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
Conformance conformance = loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
String resourceName = theRequest.getResource();
|
||||
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theRequest.getResource());
|
||||
|
@ -350,8 +349,6 @@ public class Controller {
|
|||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
IUntypedQuery search = client.search();
|
||||
IQuery query;
|
||||
if (isNotBlank(theReq.getParameter("resource"))) {
|
||||
|
@ -402,8 +399,7 @@ public class Controller {
|
|||
query.execute();
|
||||
returnsResource = ResultType.BUNDLE;
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
|
@ -417,7 +413,6 @@ public class Controller {
|
|||
addCommonParams(theRequest, theModel);
|
||||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
String body = preProcessMessageBody(theRequest.getTransactionBody());
|
||||
|
||||
|
@ -452,7 +447,7 @@ public class Controller {
|
|||
return "result";
|
||||
}
|
||||
|
||||
private void addCommonParams(final HomeRequest theRequest, final ModelMap theModel) {
|
||||
private Conformance addCommonParams(final HomeRequest theRequest, final ModelMap theModel) {
|
||||
if (myConfig.getDebugTemplatesMode()) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
@ -468,6 +463,7 @@ public class Controller {
|
|||
theModel.put("pretty", theRequest.getPretty());
|
||||
theModel.put("serverEntries", myConfig.getIdToServerName());
|
||||
|
||||
return loadAndAddConf(theRequest, theModel);
|
||||
}
|
||||
|
||||
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
|
||||
|
@ -490,8 +486,6 @@ public class Controller {
|
|||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
Class<? extends IResource> type = null; // def.getImplementingClass();
|
||||
if ("history-type".equals(theMethod)) {
|
||||
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theRequest.getResource());
|
||||
|
@ -542,8 +536,7 @@ public class Controller {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
|
@ -556,8 +549,6 @@ public class Controller {
|
|||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
String id = null;
|
||||
Class<? extends IResource> type = null; // def.getImplementingClass();
|
||||
if ("history-type".equals(theMethod)) {
|
||||
|
@ -697,22 +688,26 @@ public class Controller {
|
|||
return b.toString();
|
||||
}
|
||||
|
||||
private String formatUrl(String theResultBody) {
|
||||
private String formatUrl(String theUrlBase, String theResultBody) {
|
||||
String str = theResultBody;
|
||||
if (str == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!inParams) {
|
||||
if (nextChar == '?') {
|
||||
inParams = true;
|
||||
b.append("<wbr /><span class='hlControl'>?</span><span class='hlTagName'>");
|
||||
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 {
|
||||
|
@ -782,10 +777,12 @@ public class Controller {
|
|||
return true;
|
||||
}
|
||||
|
||||
private Conformance loadAndAddConformance(final HomeRequest theRequest, final ModelMap theModel, GenericClient theClient) {
|
||||
private Conformance loadAndAddConf(final HomeRequest theRequest, final ModelMap theModel) {
|
||||
IGenericClient client = myCtx.newRestfulGenericClient(theRequest.getServerBase(myConfig));
|
||||
|
||||
Conformance conformance;
|
||||
try {
|
||||
conformance = theClient.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());
|
||||
|
@ -876,7 +873,7 @@ public class Controller {
|
|||
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
|
||||
String action = theClient.getLastRequest() != null ? theClient.getLastRequest().getMethod() : null;
|
||||
String resultStatus = theClient.getLastResponse() != null ? theClient.getLastResponse().getStatusLine().toString() : null;
|
||||
String resultBody = theClient.getLastResponseBody();
|
||||
String resultBody = StringUtils.defaultString(theClient.getLastResponseBody());
|
||||
|
||||
if (lastRequest instanceof HttpEntityEnclosingRequest) {
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity();
|
||||
|
@ -932,7 +929,7 @@ public class Controller {
|
|||
theModelMap.put("resultStatus", resultStatus);
|
||||
|
||||
theModelMap.put("requestUrl", requestUrl);
|
||||
theModelMap.put("requestUrlText", formatUrl(requestUrl));
|
||||
theModelMap.put("requestUrlText", formatUrl(theClient.getUrlBase(), requestUrl));
|
||||
|
||||
String requestBodyText = format(requestBody, ctEnum);
|
||||
theModelMap.put("requestBody", requestBodyText);
|
||||
|
|
|
@ -2,11 +2,14 @@ package ca.uhn.fhir.to.model;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.to.TesterConfig;
|
||||
|
||||
|
@ -93,6 +96,23 @@ public class HomeRequest {
|
|||
retVal.setEncoding( EncodingEnum.JSON);
|
||||
}
|
||||
|
||||
final String remoteAddr = org.slf4j.MDC.get("req.remoteAddr");
|
||||
retVal.registerInterceptor(new IClientInterceptor() {
|
||||
|
||||
@Override
|
||||
public void interceptResponse(HttpResponse theRequest) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptRequest(HttpRequestBase theRequest) {
|
||||
if (isNotBlank(remoteAddr)) {
|
||||
theRequest.addHeader("x-forwarded-for", remoteAddr);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered table-striped resultTable" id="resultTable">
|
||||
<table class="table table-bordered table-striped resultTable" id="resultTable" th:if="${resultStatus} != null">
|
||||
<colgroup>
|
||||
<col class="col-xs-1" />
|
||||
<col class="col-xs-7" />
|
||||
|
@ -177,9 +177,11 @@
|
|||
</th:block>
|
||||
</td>
|
||||
<td>
|
||||
<th:block th:if="${entry.resource} != null" th:text="${entry.resource.id.toUnqualified()}"/>
|
||||
<small th:if="${entry.resource} != null" th:text="${entry.resource.id.toUnqualified()}"/>
|
||||
</td>
|
||||
<td>
|
||||
<small th:text="${entry.title}"/>
|
||||
</td>
|
||||
<td><small th:text="${entry.title}"/></td>
|
||||
<td th:if="${entry.updated.value} == null"></td>
|
||||
<td th:if="${entry.updated.value} != null and ${entry.updated.today} == true" th:text="${#dates.format(entry.updated.value, 'HH:mm:ss')}"></td>
|
||||
<td th:if="${entry.updated.value} != null and ${entry.updated.today} == false" th:text="${#dates.format(entry.updated.value, 'yyyy-MM-dd')}"></td>
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
BODY {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
DIV.hapiHeaderText {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* The following section is used for tables of values
|
||||
* with multiple columns (e.g. the table of Observations
|
||||
* in a Diagnostic Report for a lab test)
|
||||
********************************************************/
|
||||
TABLE.hapiTableOfValues THEAD TR TD {
|
||||
background: #A0A0F0;
|
||||
color: #000080;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
TABLE.hapiTableOfValues TBODY TR TD {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
/* Even and odd row formatting */
|
||||
TR.hapiTableOfValuesRowOdd TD {
|
||||
background: #C0C0C0;
|
||||
}
|
||||
TR.hapiTableOfValuesRowEven TD {
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
/********************************************************
|
||||
* The following section is used for property tables. These
|
||||
* tables have two columns, one for the property name, and
|
||||
* one for the property value
|
||||
********************************************************/
|
||||
|
||||
TABLE.hapiPropertyTable TBODY TR TD:FIRST-CHILD {
|
||||
background: #C0C0C0;
|
||||
text-align: right;
|
||||
}
|
||||
|
|
@ -47,6 +47,8 @@ SPAN.headerName {
|
|||
.hlText {
|
||||
color: #000000;
|
||||
}
|
||||
.hlUrlBase {
|
||||
}
|
||||
|
||||
SPAN.headerValue {
|
||||
color: #70A070;
|
||||
|
|
|
@ -101,7 +101,7 @@ public class OverlayTestApp {
|
|||
restServer.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
restServer.setImplementationDescription("This is a great server!!!!");
|
||||
|
||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(restServer, systemDao,resourceDaos);
|
||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(restServer, systemDao);
|
||||
restServer.setServerConformanceProvider(confProvider);
|
||||
|
||||
myPort = 8887;
|
||||
|
|
|
@ -24,6 +24,8 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
|
|||
|
||||
@Search()
|
||||
public ca.uhn.fhir.rest.server.IBundleProvider search(
|
||||
javax.servlet.http.HttpServletRequest theServletRequest,
|
||||
|
||||
@Description(shortDefinition="The resource identity")
|
||||
@OptionalParam(name="_id")
|
||||
StringParam theId,
|
||||
|
@ -58,16 +60,22 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
|
|||
})
|
||||
Set<Include> theIncludes
|
||||
) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
paramMap.add("_id", theId);
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
paramMap.add("_id", theId);
|
||||
#foreach ( $param in $searchParamsWithoutComposite )
|
||||
paramMap.add("${param.name}", the${param.nameCapitalized});
|
||||
paramMap.add("${param.name}", the${param.nameCapitalized});
|
||||
#end
|
||||
|
||||
paramMap.setIncludes(theIncludes);
|
||||
paramMap.setIncludes(theIncludes);
|
||||
|
||||
|
||||
|
||||
return getDao().search(paramMap);
|
||||
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap);
|
||||
return retVal;
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue