Let IdDt clone without being heavyweight

This commit is contained in:
James Agnew 2014-07-10 08:50:51 -04:00
parent 1b7b141396
commit 28ebda71e1
36 changed files with 924 additions and 527 deletions

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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("&gt;", ">").replace("&lt;", "<").replace("&amp;", "&");
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("&gt;", ">").replace("&lt;", "<").replace("&amp;", "&");
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++) {

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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) {

View File

@ -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";
}

View File

@ -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;
}

View File

@ -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 -->

View File

@ -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);
}

View File

@ -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

View File

@ -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());
}

View File

@ -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);
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -260,7 +260,7 @@
</configuration>
</execution>
</executions>
</plugin>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -1,6 +1,5 @@
package ca.uhn.fhirtest;
import java.io.File;
import java.util.List;
import org.apache.commons.io.IOUtils;

View File

@ -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>

View File

@ -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);

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}

View File

@ -47,6 +47,8 @@ SPAN.headerName {
.hlText {
color: #000000;
}
.hlUrlBase {
}
SPAN.headerValue {
color: #70A070;

View File

@ -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;

View File

@ -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);
}
}
}