Merge branch 'master' of ssh://nacharya@git.code.sf.net/p/hl7api/fhircode

This commit is contained in:
t106uhn 2014-06-02 19:34:01 -04:00
commit 51d16ea92f
214 changed files with 17918 additions and 3074 deletions

View File

@ -149,8 +149,8 @@
<version>9.1.1.v20140108</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
@ -290,7 +290,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-linkcheck-plugin</artifactId>
<version>1.1</version>
</plugin>
</plugin>
</plugins>
</reporting>
@ -303,35 +303,14 @@
<plugins>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<skip>false</skip>
<skipDeploy>false</skipDeploy>
</configuration>
<configuration>
<skip>false</skip>
<skipDeploy>false</skipDeploy>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<executions>
<execution>
<id>first</id>
<goals>
<goal>update-file-header</goal>
</goals>
<phase>process-sources</phase>
<configuration>
<licenseName>apache_v2</licenseName>
<canUpdateDescription>true</canUpdateDescription>
<canUpdateCopyright>true</canUpdateCopyright>
<roots>
<root>src/main/java</root>
</roots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
@ -461,57 +440,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<minmemory>128m</minmemory>
<maxmemory>1g</maxmemory>
<linksource>true</linksource>
<verbose>false</verbose>
<debug>false</debug>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<attach>false</attach>
<descriptors>
<descriptor>${project.basedir}/src/assembly/hapi-fhir-all.xml</descriptor>
<!-- <descriptor>src/assembly/hapi-jdk14.xml</descriptor> -->
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
@ -521,4 +449,86 @@
</resources>
</build>
<profiles>
<profile>
<id>DIST</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<minmemory>128m</minmemory>
<maxmemory>1g</maxmemory>
<linksource>true</linksource>
<verbose>false</verbose>
<debug>false</debug>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<attach>false</attach>
<descriptors>
<descriptor>${project.basedir}/src/assembly/hapi-fhir-all.xml</descriptor>
<!-- <descriptor>src/assembly/hapi-jdk14.xml</descriptor> -->
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<executions>
<execution>
<id>first</id>
<goals>
<goal>update-file-header</goal>
</goals>
<phase>process-sources</phase>
<configuration>
<licenseName>apache_v2</licenseName>
<canUpdateDescription>true</canUpdateDescription>
<canUpdateCopyright>true</canUpdateCopyright>
<roots>
<root>src/main/java</root>
</roots>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -18,6 +18,18 @@
Introduce StringParameter type which can be used as a RESTful operation search parameter
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
</action>
<action type="add">
Parsers (XML/JSON) now support deleted entries in bundles
</action>
<action type="add">
Transaction method now supported in servers
</action>
<action type="add">
Support for Binary resources added (in servers, clients, parsers, etc.)
</action>
<action type="fix">
Support for Query resources fixed (in parser)
</action>
</release>
</body>
</document>

View File

@ -42,6 +42,7 @@ import java.util.TreeSet;
import ca.uhn.fhir.model.api.BaseResourceReference;
import ca.uhn.fhir.model.api.CodeableConceptElement;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.ICodeEnum;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ICompositeElement;
@ -164,10 +165,10 @@ class ModelScanner {
@SuppressWarnings("unchecked")
Class<? extends IElement> nextClass = (Class<? extends IElement>) Class.forName((String) nextValue);
if (!IElement.class.isAssignableFrom(nextClass)) {
ourLog.warn("Class is not assignable from " + IElement.class.getSimpleName()+": " + nextValue);
ourLog.warn("Class is not assignable from " + IElement.class.getSimpleName() + ": " + nextValue);
continue;
}
toScan.add(nextClass);
} catch (ClassNotFoundException e) {
ourLog.warn("Unknown class exception: " + nextValue, e);
@ -182,7 +183,7 @@ class ModelScanner {
// toScan.add(DecimalDt.class);
// toScan.add(AttachmentDt.class);
// toScan.add(ResourceReferenceDt.class);
// toScan.add(QuantityDt.class);
// toScan.add(QuantityDt.class);
do {
for (Class<? extends IElement> nextClass : toScan) {
@ -289,7 +290,12 @@ class ModelScanner {
private void scanCompositeDatatype(Class<? extends ICompositeDatatype> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
RuntimeCompositeDatatypeDefinition resourceDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass);
RuntimeCompositeDatatypeDefinition resourceDef;
if (theClass.equals(ExtensionDt.class)) {
resourceDef = new RuntimeExtensionDtDefinition(theDatatypeDefinition, theClass);
} else {
resourceDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass);
}
myClassToElementDefinitions.put(theClass, resourceDef);
scanCompositeElementForChildren(theClass, resourceDef);
}
@ -376,9 +382,8 @@ class ModelScanner {
}
/*
* Anything that's marked as unknown is given a new ID that is <0 so
* that it doesn't conflict wityh any given IDs and can be figured
* out later
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict wityh any
* given IDs and can be figured out later
*/
while (order == Child.ORDER_UNKNOWN && orderMap.containsKey(order)) {
order--;
@ -416,6 +421,14 @@ class ModelScanner {
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
orderMap.put(order, def);
} else if (next.getType().equals(ExtensionDt.class)) {
RuntimeChildExtensionDt def = new RuntimeChildExtensionDt(next, elementName, childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
if (IElement.class.isAssignableFrom(nextElementType)) {
addScanAlso((Class<? extends IElement>) nextElementType);
}
} else if (extensionAttr != null) {
/*
* Child is an extension
@ -443,8 +456,8 @@ class ModelScanner {
} else if (IResourceBlock.class.isAssignableFrom(nextElementType)) {
/*
* Child is a resource block (i.e. a sub-tag within a resource)
* TODO: do these have a better name according to HL7?
* Child is a resource block (i.e. a sub-tag within a resource) TODO: do these have a better name
* according to HL7?
*/
Class<? extends IResourceBlock> blockDef = (Class<? extends IResourceBlock>) nextElementType;
@ -452,7 +465,7 @@ class ModelScanner {
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef);
orderMap.put(order, def);
} else if (IDatatype.class.equals(nextElementType)) {
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType)) {
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
orderMap.put(order, def);
@ -575,8 +588,8 @@ class ModelScanner {
SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class);
if (searchParam != null) {
SearchParamTypeEnum paramType = SearchParamTypeEnum.valueOf(searchParam.type().toUpperCase());
if (paramType ==null) {
throw new ConfigurationException("Searc param "+searchParam.name()+" has an invalid type: "+searchParam.type());
if (paramType == null) {
throw new ConfigurationException("Searc param " + searchParam.name() + " has an invalid type: " + searchParam.type());
}
RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType);
theResourceDef.addSearchParam(param);

View File

@ -44,8 +44,8 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
public RuntimeChildChoiceDefinition(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation, List<Class<? extends IElement>> theChoiceTypes) {
super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName);
myChoiceTypes= Collections.unmodifiableList(theChoiceTypes);
myChoiceTypes = Collections.unmodifiableList(theChoiceTypes);
}
/**
@ -71,7 +71,7 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
@Override
public BaseRuntimeElementDefinition<?> getChildByName(String theName) {
assert myNameToChildDefinition.containsKey(theName);
return myNameToChildDefinition.get(theName);
}
@ -80,37 +80,37 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
myNameToChildDefinition = new HashMap<String, BaseRuntimeElementDefinition<?>>();
myDatatypeToElementName = new HashMap<Class<? extends IElement>, String>();
myDatatypeToElementDefinition =new HashMap<Class<? extends IElement>, BaseRuntimeElementDefinition<?>>();
myDatatypeToElementDefinition = new HashMap<Class<? extends IElement>, BaseRuntimeElementDefinition<?>>();
for (Class<? extends IElement> next : myChoiceTypes) {
String elementName;
String alternateElementName=null;
String alternateElementName = null;
BaseRuntimeElementDefinition<?> nextDef;
if (IResource.class.isAssignableFrom(next)) {
elementName = getElementName() + StringUtils.capitalize(next.getSimpleName());
alternateElementName = getElementName() + "Resource";
List<Class<? extends IResource>> types = new ArrayList<Class<? extends IResource>>();
types.add((Class<? extends IResource>)next);
types.add((Class<? extends IResource>) next);
nextDef = new RuntimeResourceReferenceDefinition(elementName, types);
nextDef.sealAndInitialize(theClassToElementDefinitions);
} else {
nextDef = theClassToElementDefinitions.get(next);
elementName = getElementName() + StringUtils.capitalize(nextDef.getName());
}
myNameToChildDefinition.put(elementName, nextDef);
if (alternateElementName!=null) {
if (alternateElementName != null) {
myNameToChildDefinition.put(alternateElementName, nextDef);
}
myDatatypeToElementDefinition.put(next, nextDef);
myDatatypeToElementName.put(next, elementName);
}
myNameToChildDefinition = Collections.unmodifiableMap(myNameToChildDefinition);
myDatatypeToElementName=Collections.unmodifiableMap(myDatatypeToElementName);
myDatatypeToElementDefinition=Collections.unmodifiableMap(myDatatypeToElementDefinition);
myDatatypeToElementName = Collections.unmodifiableMap(myDatatypeToElementName);
myDatatypeToElementDefinition = Collections.unmodifiableMap(myDatatypeToElementDefinition);
}
@Override
@ -127,5 +127,4 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
return Collections.unmodifiableSet((myDatatypeToElementDefinition.keySet()));
}
}

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.reflect.Field;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
public class RuntimeChildExtensionDt extends RuntimeChildAny {
public RuntimeChildExtensionDt(Field theField, String theElementName, Child theChildAnnotation, Description theDescriptionAnnotation) {
super(theField, theElementName, theChildAnnotation, theDescriptionAnnotation);
}
public IElement newInstance() {
return new ExtensionDt();
}
}

View File

@ -41,6 +41,7 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos
}
@Override
public boolean isSpecialization() {
return mySpecialization;
}

View File

@ -0,0 +1,66 @@
package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
public class RuntimeExtensionDtDefinition extends RuntimeCompositeDatatypeDefinition {
private List<BaseRuntimeChildDefinition> myChildren;
public RuntimeExtensionDtDefinition(DatatypeDef theDef, Class<? extends ICompositeDatatype> theImplementingClass) {
super(theDef, theImplementingClass);
}
@Override
public List<BaseRuntimeChildDefinition> getChildren() {
return myChildren;
}
@Override
public void sealAndInitialize(Map<Class<? extends IElement>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
super.sealAndInitialize(theClassToElementDefinitions);
/*
* The "url" child is a weird child because it is not parsed and encoded in the normal way,
* so we exclude it here
*/
List<BaseRuntimeChildDefinition> superChildren = super.getChildren();
ArrayList<BaseRuntimeChildDefinition> children = new ArrayList<BaseRuntimeChildDefinition>();
for (BaseRuntimeChildDefinition baseRuntimeChildDefinition : superChildren) {
if (baseRuntimeChildDefinition.getValidChildNames().contains("url")) {
continue;
}
children.add(baseRuntimeChildDefinition);
}
myChildren = Collections.unmodifiableList(children);
}
}

View File

@ -57,10 +57,12 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
private Profile myProfileDef;
private String myResourceProfile;
private List<RuntimeSearchParam> mySearchParams;
private String myId;
public RuntimeResourceDefinition(Class<? extends IResource> theClass, ResourceDef theResourceAnnotation) {
super(theResourceAnnotation.name(), theClass);
myResourceProfile = theResourceAnnotation.profile();
myId = theResourceAnnotation.id();
}
public void addSearchParam(RuntimeSearchParam theParam) {
@ -313,8 +315,15 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
}
Profile retVal = new Profile();
RuntimeResourceDefinition def = this;
if (StringUtils.isNotBlank(myId)) {
retVal.setId(myId);
}else {
throw new ConfigurationException("Resource class " + getImplementingClass().getCanonicalName() + " has no ID specified");
}
// Scan for extensions
scanForExtensions(retVal, def);
Collections.sort(retVal.getExtensionDefn(), new Comparator<ExtensionDefn>() {

View File

@ -116,6 +116,11 @@ public abstract class BaseElement implements IIdentifiableElement, ISupportsUnde
myId = theId;
}
@Override
public void setId(String theId) {
myId = new IdDt(theId);
}
/**
* Intended to be called by extending classes {@link #isEmpty()}
* implementations, returns <code>true</code> if all content in this

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.model.api;
import java.io.IOException;
import java.io.Reader;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
@ -31,7 +30,6 @@ import org.apache.http.client.methods.HttpGet;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
@ -40,34 +38,23 @@ public abstract class BaseResourceReference extends BaseElement {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReference.class);
private IResource myResource;
private String myResourceId;
private Class<? extends IResource> myResourceType;
/**
* Constructor
*/
* @param theResource
*/
public BaseResourceReference() {
// nothing
}
/**
* Constructor
*/
public BaseResourceReference(Class<? extends IResource> theResourceType, IdDt theResourceId) {
myResourceType = theResourceType;
myResourceId = theResourceId.getValue();
}
/**
* Constructor
*
* @param theResource The loaded resource itself
*/
public BaseResourceReference(Class<? extends IResource> theResourceType, String theResourceId) {
myResourceType = theResourceType;
myResourceId = theResourceId;
}
public BaseResourceReference(IResource theResource) {
myResource=theResource;
myResource = theResource;
setResourceId(theResource.getId());
}
/**
@ -79,7 +66,18 @@ public abstract class BaseResourceReference extends BaseElement {
* version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*/
public abstract StringDt getReference();
public abstract IdDt getResourceId();
/**
* Sets the resource ID
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute
* URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be
* version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*/
public abstract void setResourceId(IdDt theResourceId);
/**
* Gets the actual loaded and parsed resource instance, <b>if it is already present</b>. This
@ -94,21 +92,10 @@ public abstract class BaseResourceReference extends BaseElement {
return myResource;
}
public String getResourceId() {
return myResourceId;
}
public Class<? extends IResource> getResourceType() {
return myResourceType;
}
public String getResourceUrl() {
return getReference().getValue();
}
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && myResource == null && myResourceType == null && StringUtils.isBlank(myResourceId);
return super.isBaseEmpty() && myResource == null;
}
/**
@ -120,12 +107,18 @@ public abstract class BaseResourceReference extends BaseElement {
return myResource;
}
ourLog.debug("Loading resource at URL: {}", getResourceUrl());
IdDt resourceId = getResourceId();
if (resourceId == null) {
throw new IllegalStateException("Reference has no resource ID defined");
}
String resourceUrl = resourceId.getValue();
ourLog.debug("Loading resource at URL: {}", resourceUrl);
HttpClient httpClient = theClient.getHttpClient();
FhirContext context = theClient.getFhirContext();
String resourceUrl = getResourceUrl();
if (!resourceUrl.startsWith("http")) {
resourceUrl = theClient.getServerBase() + resourceUrl;
}
@ -152,12 +145,4 @@ public abstract class BaseResourceReference extends BaseElement {
myResource = theResource;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public void setResourceType(Class<? extends IResource> theResourceType) {
myResourceType = theResourceType;
}
}

View File

@ -20,16 +20,26 @@ package ca.uhn.fhir.model.api;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.ElementUtil;
public class Bundle extends BaseBundle /*implements IElement*/ {
public class Bundle extends BaseBundle /* implements IElement */{
//@formatter:off
/* ****************************************************
* NB: add any new fields to the isEmpty() method!!!
@ -73,6 +83,14 @@ public class Bundle extends BaseBundle /*implements IElement*/ {
return myEntries;
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append(getEntries().size() + " entries");
b.append("id", getId());
return b.toString();
}
public StringDt getBundleId() {
if (myBundleId == null) {
myBundleId = new StringDt();
@ -179,7 +197,7 @@ public class Bundle extends BaseBundle /*implements IElement*/ {
public <T extends IResource> List<T> getResources(Class<T> theClass) {
ArrayList<T> retVal = new ArrayList<T>();
for (BundleEntry next : getEntries()) {
if (next.getResource()!=null && theClass.isAssignableFrom(next.getResource().getClass())) {
if (next.getResource() != null && theClass.isAssignableFrom(next.getResource().getClass())) {
@SuppressWarnings("unchecked")
T resource = (T) next.getResource();
retVal.add(resource);
@ -188,4 +206,98 @@ public class Bundle extends BaseBundle /*implements IElement*/ {
return retVal;
}
/**
* Creates a new entry using the given resource and populates it accordingly
*
* @param theResource
* The resource to add
*/
public void addResource(IResource theResource, FhirContext theContext, String theServerBase) {
BundleEntry entry = addEntry();
entry.setResource(theResource);
entry.setResource(theResource);
TagList list = (TagList) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
for (Tag tag : list) {
if (StringUtils.isNotBlank(tag.getTerm())) {
entry.addCategory().setTerm(tag.getTerm()).setLabel(tag.getLabel()).setScheme(tag.getScheme());
}
}
}
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) {
entry.getTitle().setValue(def.getName() + " " + theResource.getId().getValue());
StringBuilder b = new StringBuilder();
b.append(theServerBase);
if (b.length() > 0 && b.charAt(b.length() - 1) != '/') {
b.append('/');
}
b.append(def.getName());
b.append('/');
String resId = theResource.getId().getUnqualifiedId();
b.append(resId);
entry.getId().setValue(b.toString());
if (isNotBlank(theResource.getId().getUnqualifiedVersionId())) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(theResource.getId().getUnqualifiedVersionId());
} else {
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(theResource);
if (versionId != null) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(versionId.getValue());
}
}
entry.getLinkSelf().setValue(b.toString());
}
InstantDt published = (InstantDt) ResourceMetadataKeyEnum.PUBLISHED.get(theResource);
if (published == null) {
entry.getPublished().setToCurrentTimeInLocalTimeZone();
} else {
entry.setPublished(published);
}
InstantDt updated = (InstantDt) ResourceMetadataKeyEnum.UPDATED.get(theResource);
if (updated != null) {
entry.setUpdated(updated);
}
InstantDt deleted = (InstantDt) ResourceMetadataKeyEnum.DELETED_AT.get(theResource);
if (deleted != null) {
entry.setDeleted(deleted);
}
IdDt previous = (IdDt) ResourceMetadataKeyEnum.PREVIOUS_ID.get(theResource);
if (previous != null) {
entry.getLinkAlternate().setValue(previous.toQualifiedUrl(theServerBase, def.getName()));
}
TagList tagList = (TagList) ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
if (tagList != null) {
for (Tag nextTag : tagList) {
entry.addCategory(nextTag);
}
}
}
public static Bundle withResources(ArrayList<IResource> theUploadBundle, FhirContext theContext, String theServerBase) {
Bundle retVal = new Bundle();
for (IResource next : theUploadBundle) {
retVal.addResource(next, theContext, theServerBase);
}
return retVal;
}
}

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.model.api;
* #L%
*/
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -34,21 +36,60 @@ public class BundleEntry extends BaseBundle {
* NB: add any new fields to the isEmpty() method!!!
*****************************************************/
//@formatter:on
private TagList myCategories;
private InstantDt myDeletedAt;
private StringDt myLinkAlternate;
private StringDt myLinkSelf;
private InstantDt myPublished;
private IResource myResource;
private XhtmlDt mySummary;
private StringDt myTitle;
private InstantDt myUpdated;
private XhtmlDt mySummary;
private TagList myCategories;
public Tag addCategory() {
Tag retVal = new Tag();
getCategories().add(retVal);
return retVal;
}
@Override
public boolean isEmpty() {
//@formatter:off
return super.isEmpty() &&
ElementUtil.isEmpty(myLinkSelf, myPublished, myResource, myTitle, myUpdated, mySummary) &&
ElementUtil.isEmpty(myCategories);
//@formatter:on
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
if (getResource() != null) {
b.append("type", getResource().getClass().getSimpleName());
} else {
b.append("No resource");
}
b.append("id", getId());
return b.toString();
}
public void addCategory(Tag theTag) {
getCategories().add(theTag);
}
public TagList getCategories() {
if (myCategories == null) {
myCategories = new TagList();
}
return myCategories;
}
/**
* Gets the date/time that thius entry was deleted.
*/
public InstantDt getDeletedAt() {
if (myDeletedAt == null) {
myDeletedAt = new InstantDt();
}
return myDeletedAt;
}
public StringDt getLinkAlternate() {
if (myLinkAlternate == null) {
myLinkAlternate = new StringDt();
}
return myLinkAlternate;
}
public StringDt getLinkSelf() {
@ -69,6 +110,13 @@ public class BundleEntry extends BaseBundle {
return myResource;
}
public XhtmlDt getSummary() {
if (mySummary == null) {
mySummary = new XhtmlDt();
}
return mySummary;
}
public StringDt getTitle() {
if (myTitle == null) {
myTitle = new StringDt();
@ -83,6 +131,25 @@ public class BundleEntry extends BaseBundle {
return myUpdated;
}
@Override
public boolean isEmpty() {
//@formatter:off
return super.isEmpty() &&
ElementUtil.isEmpty(myCategories, myDeletedAt, myLinkAlternate, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated);
//@formatter:on
}
/**
* Sets the date/time that this entry was deleted.
*/
public void setDeleted(InstantDt theDeletedAt) {
myDeletedAt = theDeletedAt;
}
public void setLinkAlternate(StringDt theLinkAlternate) {
myLinkAlternate = theLinkAlternate;
}
public void setLinkSelf(StringDt theLinkSelf) {
if (myLinkSelf == null) {
myLinkSelf = new StringDt();
@ -90,42 +157,18 @@ public class BundleEntry extends BaseBundle {
myLinkSelf = theLinkSelf;
}
public void setResource(IResource theResource) {
myResource = theResource;
}
public XhtmlDt getSummary() {
if (mySummary == null) {
mySummary = new XhtmlDt();
}
return mySummary;
}
public Tag addCategory() {
Tag retVal = new Tag();
getCategories().add(retVal);
return retVal;
}
public TagList getCategories() {
if (myCategories == null) {
myCategories = new TagList();
}
return myCategories;
}
public void setPublished(InstantDt thePublished) {
Validate.notNull(thePublished, "Published may not be null");
myPublished = thePublished;
}
public void setResource(IResource theResource) {
myResource = theResource;
}
public void setUpdated(InstantDt theUpdated) {
Validate.notNull(theUpdated, "Updated may not be null");
myUpdated = theUpdated;
}
public void addCategory(Tag theTag) {
getCategories().add(theTag);
}
}

View File

@ -20,6 +20,9 @@ package ca.uhn.fhir.model.api;
* #L%
*/
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.api.annotation.Child;
@ -27,11 +30,11 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.primitive.StringDt;
@DatatypeDef(name="Extension")
public class ExtensionDt extends BaseElement {
public class ExtensionDt extends BaseElement implements ICompositeDatatype {
private boolean myModifier;
@Child(name="use", type=StringDt.class, order=0, min=1, max=1)
@Child(name="url", type=StringDt.class, order=0, min=1, max=1)
private StringDt myUrl;
@Child(name="value", type=IDatatype.class, order=1, min=0, max=1)
@ -68,7 +71,7 @@ public class ExtensionDt extends BaseElement {
}
public String getUrlAsString() {
return myUrl.getValue();
return getUrl().getValue();
}
/**
@ -110,16 +113,23 @@ public class ExtensionDt extends BaseElement {
myModifier = theModifier;
}
public void setUrl(String theUrl) {
public ExtensionDt setUrl(String theUrl) {
myUrl = new StringDt(theUrl);
return this;
}
public void setUrl(StringDt theUrl) {
public ExtensionDt setUrl(StringDt theUrl) {
myUrl = theUrl;
return this;
}
public void setValue(IElement theValue) {
myValue = theValue;
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return new ArrayList<T>();
}
}

View File

@ -25,7 +25,6 @@ package ca.uhn.fhir.model.api;
public interface IElement {
boolean isEmpty();
}

View File

@ -24,9 +24,16 @@ import ca.uhn.fhir.model.primitive.IdDt;
public interface IIdentifiableElement extends IElement {
public void setId(IdDt theId);
void setId(IdDt theId);
IdDt getId();
/**
* Convenience method for {@link #setId(IdDt)} which creates a new IdDt and provides the
* given string as the ID.
*
* @param theId The ID string. Can be a complete URL, a partial URL or even a simple identifier.
*/
void setId(String theId);
public IdDt getId();
}

View File

@ -20,11 +20,61 @@ package ca.uhn.fhir.model.api;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.util.Date;
import java.util.Map;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public enum ResourceMetadataKeyEnum {
/**
* If present and populated with a date/time (as an instance of {@link InstantDt}),
* this value is an indication that the resource is in the deleted state. This key
* is only used in a limited number of scenarios, such as POSTing transaction bundles
* to a server, or returning resource history.
* <p>
* Values for this key are of type <b>{@link InstantDt}</b>
* </p>
*/
DELETED_AT {
@Override
public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
}
@Override
public void put(IResource theResource, Object theObject) {
InstantDt obj = (InstantDt) theObject;
theResource.getResourceMetadata().put(DELETED_AT, obj);
}
},
/**
* The value for this key represents a previous ID used to identify
* this resource. This key is currently only used internally during
* transaction method processing.
* <p>
* Values for this key are of type <b>{@link IdDt}</b>
* </p>
*/
PREVIOUS_ID {
@Override
public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
}
@Override
public void put(IResource theResource, Object theObject) {
IdDt obj = (IdDt) theObject;
theResource.getResourceMetadata().put(PREVIOUS_ID, obj);
}
},
/**
* The value for this key is the bundle entry <b>Published</b> time. This is
* defined by FHIR as "Time resource copied into the feed", which is
@ -40,8 +90,20 @@ public enum ResourceMetadataKeyEnum {
*
* @see InstantDt
*/
PUBLISHED,
PUBLISHED {
@Override
public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED);
}
@Override
public void put(IResource theResource, Object theObject) {
InstantDt obj = (InstantDt) theObject;
theResource.getResourceMetadata().put(PUBLISHED, obj);
}
},
/**
* The value for this key is the list of tags associated with this resource
* <p>
@ -50,8 +112,30 @@ public enum ResourceMetadataKeyEnum {
*
* @see TagList
*/
TAG_LIST,
TAG_LIST {
@Override
public TagList get(IResource theResource) {
Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
if (retValObj == null) {
return null;
} else if (retValObj instanceof TagList) {
if (((TagList) retValObj).isEmpty()) {
return null;
} else {
return (TagList) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected " + TagList.class.getCanonicalName());
}
@Override
public void put(IResource theResource, Object theObject) {
TagList obj = (TagList) theObject;
theResource.getResourceMetadata().put(TAG_LIST, obj);
}
},
/**
* The value for this key is the bundle entry <b>Updated</b> time. This is
* defined by FHIR as "Last Updated for resource". This value is also used
@ -63,14 +147,81 @@ public enum ResourceMetadataKeyEnum {
*
* @see InstantDt
*/
UPDATED,
UPDATED {
@Override
public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
}
@Override
public void put(IResource theResource, Object theObject) {
InstantDt obj = (InstantDt) theObject;
theResource.getResourceMetadata().put(UPDATED, obj);
}
},
/**
* The value for this key is the version ID of the resource object.
* <p>
* Values for this key are of type <b>{@link IdDt}</b>
* </p>
*
* @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getUnqualifiedVersionId()} method
*/
VERSION_ID;
@Deprecated
VERSION_ID {
@Override
public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
}
@Override
public void put(IResource theResource, Object theObject) {
IdDt obj = (IdDt) theObject;
theResource.getResourceMetadata().put(VERSION_ID, obj);
}
};
public abstract Object get(IResource theResource);
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof String) {
if (isNotBlank((String) retValObj)) {
return new IdDt((String) retValObj);
} else {
return null;
}
} else if (retValObj instanceof IdDt) {
if (((IdDt) retValObj).isEmpty()) {
return null;
} else {
return (IdDt) retValObj;
}
} else if (retValObj instanceof Number) {
return new IdDt(((Number)retValObj).toString());
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
}
private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof Date) {
return new InstantDt((Date) retValObj);
} else if (retValObj instanceof InstantDt) {
if (((InstantDt) retValObj).isEmpty()) {
return null;
} else {
return (InstantDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
}
public abstract void put(IResource theResource, Object theObject);
}

View File

@ -29,7 +29,7 @@ public class TagList extends ArrayList<Tag> {
public static final String ELEMENT_NAME = "TagList";
public static final String ELEMENT_NAME_LC = ELEMENT_NAME.toLowerCase();
public Tag addTag(String theTerm, String theLabel, String theScheme) {
public Tag addTag(String theScheme, String theTerm, String theLabel) {
Tag retVal = new Tag(theTerm, theLabel, theScheme);
add(retVal);
return retVal;
@ -38,5 +38,14 @@ public class TagList extends ArrayList<Tag> {
public Tag addTag() {
return addTag(null, null, null);
}
public Tag getTag(String theScheme, String theTerm) {
for (Tag next : this) {
if (theScheme.equals(next.getScheme()) && theTerm.equals(next.getTerm())) {
return next;
}
}
return null;
}
}

View File

@ -60,4 +60,9 @@ public class ContainedDt implements IDatatype {
public IdDt getId() {
throw new UnsupportedOperationException();
}
@Override
public void setId(String theId) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,19 +1,3 @@
package ca.uhn.fhir.model.dstu.composite;
/*
@ -49,22 +33,19 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
/**
* HAPI/FHIR <b>ResourceReferenceDt</b> Datatype
* (A reference from one resource to another)
*
* HAPI/FHIR <b>ResourceReferenceDt</b> Datatype (A reference from one resource to another)
*
* <p>
* <b>Definition:</b>
* A reference from one resource to another
* </p>
*
* <b>Definition:</b> A reference from one resource to another
* </p>
*
* <p>
* <b>Requirements:</b>
*
* </p>
* </p>
*/
@DatatypeDef(name="ResourceReferenceDt")
public class ResourceReferenceDt
extends BaseResourceReference implements ICompositeDatatype {
@DatatypeDef(name = "ResourceReferenceDt")
public class ResourceReferenceDt extends BaseResourceReference implements ICompositeDatatype {
/**
* Constructor
@ -74,79 +55,74 @@ public class ResourceReferenceDt
}
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, String theResourceId) {
super(theResourceType, theResourceId);
}
/**
* Constructor which creates a normal resource reference
*
* @param theResourceType The resource type
* @param theResourceId The resource ID
*/
public ResourceReferenceDt(Class<? extends IResource> theResourceType, IdDt theResourceId) {
super(theResourceType, theResourceId);
}
/**
* Constructor which creates a resource reference containing the actual
* resource in question.
* Constructor which creates a resource reference containing the actual resource in question.
* <p>
* <b>
* When using this in a server:</b> Generally if this is serialized, it will be serialized
* as a contained resource, so this should not be used if the intent is not to actually
* supply the referenced resource. This is not a hard-and-fast rule however, as the
* server can be configured to not serialized this resource, or to load an ID and contain
* even if this constructor is not used.
* <b> When using this in a server:</b> Generally if this is serialized, it will be serialized as a contained
* resource, so this should not be used if the intent is not to actually supply the referenced resource. This is not
* a hard-and-fast rule however, as the server can be configured to not serialized this resource, or to load an ID
* and contain even if this constructor is not used.
* </p>
*
* @param theResource The resource instance
* @param theResource
* The resource instance
*/
public ResourceReferenceDt(IResource theResource) {
super(theResource);
}
@Child(name="reference", type=StringDt.class, order=0, min=0, max=1)
@Description(
shortDefinition="Relative, internal or absolute URL reference",
formalDefinition="A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources"
)
/**
* Constructor which accepts a reference directly (this can be an ID, a partial/relative URL or a complete/absolute
* URL)
*
* @param theId
* The reference itself
*/
public ResourceReferenceDt(String theId) {
setResourceId(new IdDt(theId));
}
/**
* Constructor which accepts a reference directly (this can be an ID, a partial/relative URL or a complete/absolute
* URL)
*
* @param theId
* The reference itself
*/
public ResourceReferenceDt(IdDt theResourceId) {
setResourceId(theResourceId);
}
@Child(name = "reference", type = StringDt.class, order = 0, min = 0, max = 1)
@Description(shortDefinition = "Relative, internal or absolute URL reference", formalDefinition = "A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources")
private StringDt myReference;
@Child(name="display", type=StringDt.class, order=1, min=0, max=1)
@Description(
shortDefinition="Text alternative for the resource",
formalDefinition="Plain text narrative that identifies the resource in addition to the resource reference"
)
@Child(name = "display", type = StringDt.class, order = 1, min = 0, max = 1)
@Description(shortDefinition = "Text alternative for the resource", formalDefinition = "Plain text narrative that identifies the resource in addition to the resource reference")
private StringDt myDisplay;
@Override
public boolean isEmpty() {
return super.isBaseEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty( myReference, myDisplay);
return super.isBaseEmpty() && ca.uhn.fhir.util.ElementUtil.isEmpty(myReference, myDisplay);
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType, myReference, myDisplay);
}
/**
* Gets the value(s) for <b>reference</b> (Relative, internal or absolute URL reference).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
* Gets the value(s) for <b>reference</b> (Relative, internal or absolute URL reference). creating it if it does not
* exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative
* reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location
* where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR
* RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#')
* refer to contained resources
* </p>
*/
public StringDt getReference() {
public StringDt getReference() {
if (myReference == null) {
myReference = new StringDt();
}
@ -155,41 +131,44 @@ public class ResourceReferenceDt
/**
* Sets the value(s) for <b>reference</b> (Relative, internal or absolute URL reference)
*
* <p>
* <b>Definition:</b>
* A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative
* reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location
* where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR
* RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#')
* refer to contained resources
* </p>
*/
public ResourceReferenceDt setReference(StringDt theValue) {
myReference = theValue;
return this;
}
/**
/**
* Sets the value for <b>reference</b> (Relative, internal or absolute URL reference)
*
* <p>
* <b>Definition:</b>
* A reference to a location at which the other resource is found. The reference may a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources
* </p>
*
* <p>
* <b>Definition:</b> A reference to a location at which the other resource is found. The reference may a relative
* reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location
* where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR
* RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#')
* refer to contained resources
* </p>
*/
public ResourceReferenceDt setReference( String theString) {
return setReference(new StringDt(theString));
public ResourceReferenceDt setReference(String theString) {
return setReference(new StringDt(theString));
}
/**
* Gets the value(s) for <b>display</b> (Text alternative for the resource).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* Plain text narrative that identifies the resource in addition to the resource reference
* </p>
* Gets the value(s) for <b>display</b> (Text alternative for the resource). creating it if it does not exist. Will
* not return <code>null</code>.
*
* <p>
* <b>Definition:</b> Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*/
public StringDt getDisplay() {
public StringDt getDisplay() {
if (myDisplay == null) {
myDisplay = new StringDt();
}
@ -198,32 +177,36 @@ public class ResourceReferenceDt
/**
* Sets the value(s) for <b>display</b> (Text alternative for the resource)
*
* <p>
* <b>Definition:</b>
* Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*
* <p>
* <b>Definition:</b> Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*/
public ResourceReferenceDt setDisplay(StringDt theValue) {
myDisplay = theValue;
return this;
}
/**
/**
* Sets the value for <b>display</b> (Text alternative for the resource)
*
* <p>
* <b>Definition:</b>
* Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*
* <p>
* <b>Definition:</b> Plain text narrative that identifies the resource in addition to the resource reference
* </p>
*/
public ResourceReferenceDt setDisplay( String theString) {
myDisplay = new StringDt(theString);
return this;
public ResourceReferenceDt setDisplay(String theString) {
myDisplay = new StringDt(theString);
return this;
}
@Override
public IdDt getResourceId() {
return new IdDt(myReference.getValue());
}
@Override
public void setResourceId(IdDt theResourceId) {
myReference = new StringDt(theResourceId.getValue());
}
}

View File

@ -20,38 +20,84 @@ package ca.uhn.fhir.model.dstu.resource;
* #L%
*/
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.model.api.BaseResource;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.Base64BinaryDt;
@ResourceDef(name="Binary", profile="http://hl7.org/fhir/profiles/Binary", id="binary")
public class Binary extends BaseResource implements IResource {
@ResourceDef(name = "Binary", profile = "http://hl7.org/fhir/profiles/Binary", id = "binary")
public class Binary extends BaseResource implements IResource {
// TODO: implement binary
@Override
public boolean isEmpty() {
return true;
private Base64BinaryDt myContent = new Base64BinaryDt();
private String myContentType;
/**
* Constructor
*/
public Binary() {
// nothing
}
/**
* Constructor
*
* @param theContentType
* The content type
* @param theContent
* The binary contents
*/
public Binary(String theContentType, byte[] theContent) {
setContentType(theContentType);
setContent(theContent);
}
@Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return null;
return Collections.emptyList();
}
@Override
public ContainedDt getContained() {
return null;
public byte[] getContent() {
return myContent.getValue();
}
public String getContentAsBase64() {
return myContent.getValueAsString();
}
public String getContentType() {
return myContentType;
}
/**
* Do not call - throws {@link UnsupportedOperationException}
*/
@Override
public NarrativeDt getText() {
throw new IllegalStateException();
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return (myContent.isEmpty()) && StringUtils.isBlank(myContentType);
}
public void setContent(byte[] theContent) {
myContent.setValue(theContent);
}
public void setContentAsBase64(String theContent) {
myContent.setValueAsString(theContent);
}
public void setContentType(String theContentType) {
myContentType = theContentType;
}
}

View File

@ -20,19 +20,26 @@ package ca.uhn.fhir.model.primitive;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.Constants;
/**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs,
* Resource References, etc. to represent a specific instance of a resource.
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any
* other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -41,6 +48,9 @@ import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "id")
public class IdDt extends BasePrimitive<String> {
private String myResourceType;
private String myUnqualifiedId;
private String myUnqualifiedVersionId;
private String myValue;
/**
@ -51,7 +61,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string
* representation.
*/
public IdDt(BigDecimal thePid) {
if (thePid != null) {
@ -60,7 +71,7 @@ public class IdDt extends BasePrimitive<String> {
setValue(null);
}
}
/**
* Create a new ID using a long
*/
@ -69,12 +80,12 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234")
* or it may contain a complete URL (http://example.com/fhir/Patient/1234).
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete
* URL (http://example.com/fhir/Patient/1234).
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -85,44 +96,90 @@ public class IdDt extends BasePrimitive<String> {
setValue(theValue);
}
/**
* Returns the value of this ID as a big decimal, or <code>null</code> if the value is null
* Constructor
*
* @param theResourceType The resource type (e.g. "Patient")
* @param theId The ID (e.g. "123")
* @param theVersionId The version ID ("e.g. "456")
*/
public IdDt(String theResourceType, String theId, String theVersionId) {
Validate.notBlank(theResourceType, "Resource type must not be blank");
Validate.notBlank(theId, "ID must not be blank");
myResourceType = theResourceType;
myUnqualifiedId = theId;
myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
if (myUnqualifiedVersionId != null) {
myValue = myResourceType + '/' + myUnqualifiedId + '/' + Constants.PARAM_HISTORY + '/' + myUnqualifiedVersionId;
} else {
myValue = myResourceType + '/' + myUnqualifiedId;
}
}
/**
* Returns the unqualified portion of this ID as a big decimal, or <code>null</code> if the value is null
*
* @throws NumberFormatException
* If the value is not a valid BigDecimal
*/
public BigDecimal asBigDecimal() {
if (getValue() == null) {
String val = getUnqualifiedId();
if (isBlank(val)) {
return null;
}
return new BigDecimal(getValueAsString());
return new BigDecimal(val);
}
/**
* Returns the value of this ID as a {@link Long}, or <code>null</code> if the value is null
* Returns the unqualified portion of this ID as a {@link Long}, or <code>null</code> if the value is null
*
* @throws NumberFormatException
* If the value is not a valid Long
*/
public Long asLong() {
if (getValue() == null) {
String val = getUnqualifiedId();
if (isBlank(val)) {
return null;
}
return Long.parseLong(getValueAsString());
return Long.parseLong(val);
}
/**
* Returns a reference 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.
* Returns a reference 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.
*/
@Override
public IdDt getId() {
return this;
}
public String getResourceType() {
return myResourceType;
}
public String getUnqualifiedId() {
return myUnqualifiedId;
}
public String getUnqualifiedVersionId() {
return myUnqualifiedVersionId;
}
public Long getUnqualifiedVersionIdAsLong() {
if (!hasUnqualifiedVersionId()) {
return null;
}else {
return Long.parseLong(getUnqualifiedVersionId());
}
}
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a
* simple ID. Use {@link #getUnqualifiedId()} to get just the ID portion.
*
* @see #getUnqualifiedId()
*/
@Override
public String getValue() {
return myValue;
@ -133,8 +190,34 @@ public class IdDt extends BasePrimitive<String> {
return myValue;
}
public boolean hasUnqualifiedId() {
return isNotBlank(getUnqualifiedId());
}
public boolean hasUnqualifiedVersionId() {
return isNotBlank(getUnqualifiedVersionId());
}
/**
* 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.
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
* of digits)
*/
public boolean isValidLong() {
String id = getUnqualifiedId();
if (StringUtils.isBlank(id)) {
return false;
}
for (int i = 0; i < id.length(); i++) {
if (Character.isDigit(id.charAt(i)) == false) {
return false;
}
}
return true;
}
/**
* 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.
*/
@Override
public void setId(IdDt theId) {
@ -145,8 +228,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -156,14 +239,44 @@ public class IdDt extends BasePrimitive<String> {
public void setValue(String theValue) throws DataFormatException {
// TODO: add validation
myValue = theValue;
if (StringUtils.isBlank(theValue)) {
myValue = null;
myUnqualifiedId = null;
myUnqualifiedVersionId = null;
myResourceType = null;
} else {
int vidIndex = theValue.indexOf("/_history/");
int idIndex;
if (vidIndex != -1) {
myUnqualifiedVersionId = theValue.substring(vidIndex + "/_history/".length());
idIndex = theValue.lastIndexOf('/', vidIndex - 1);
myUnqualifiedId = theValue.substring(idIndex + 1, vidIndex);
} else {
idIndex = theValue.lastIndexOf('/');
myUnqualifiedId = theValue.substring(idIndex + 1);
myUnqualifiedVersionId = null;
}
if (idIndex <= 0) {
myResourceType = null;
} else {
int typeIndex = theValue.lastIndexOf('/', idIndex - 1);
if (typeIndex == -1) {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
}
}
}
}
/**
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -174,9 +287,49 @@ public class IdDt extends BasePrimitive<String> {
setValue(theValue);
}
/**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name
* (which will only be used if the ID does not already contain those respective parts). Essentially,
* because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this
* method may be used to translate into a complete URL.
*
* @param theServerBase The server base (e.g. "http://example.com/fhir")
* @param theResourceType The resource name (e.g. "Patient")
* @return A fully qualified URL for this ID (e.g. "http://example.com/fhir/Patient/1")
*/
public String toQualifiedUrl(String theServerBase, String theResourceType) {
if (getValue().startsWith("http")) {
return getValue();
}
StringBuilder retVal = new StringBuilder();
retVal.append(theServerBase);
if (retVal.charAt(retVal.length()-1) != '/') {
retVal.append('/');
}
if (isNotBlank(getResourceType())) {
retVal.append(getResourceType());
}else {
retVal.append(theResourceType);
}
retVal.append('/');
retVal.append(getUnqualifiedId());
return retVal.toString();
}
@Override
public String toString() {
return myValue;
}
public IdDt withoutVersion() {
int i = myValue.indexOf(Constants.PARAM_HISTORY);
if (i > 1) {
return new IdDt(myValue.substring(0, i-1));
}else {
return this;
}
}
}

View File

@ -22,8 +22,10 @@ package ca.uhn.fhir.model.primitive;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "idref")
public class IdrefDt extends BasePrimitive<String> {
private IElement myTarget;

View File

@ -76,6 +76,7 @@ import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
@ -160,14 +161,16 @@ public class JsonParser extends BaseParser implements IParser {
writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
eventWriter.writeStartArray("link");
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
eventWriter.writeEnd();
boolean linkStarted = false;
linkStarted = writeAtomLink(eventWriter, "self", theBundle.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "first", theBundle.getLinkFirst(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "next", theBundle.getLinkNext(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();
}
writeOptionalTagWithTextNode(eventWriter, "totalResults", theBundle.getTotalResults());
@ -177,17 +180,21 @@ public class JsonParser extends BaseParser implements IParser {
for (BundleEntry nextEntry : theBundle.getEntries()) {
eventWriter.writeStartObject();
writeTagWithTextNode(eventWriter, "deleted", nextEntry.getDeletedAt());
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
eventWriter.writeStartArray("link");
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
eventWriter.writeEnd();
linkStarted = false;
linkStarted = writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
if (nextEntry.getCategories() != null) {
if (nextEntry.getCategories() != null && nextEntry.getCategories().size() > 0) {
eventWriter.writeStartArray("category");
for (Tag next : nextEntry.getCategories()) {
eventWriter.writeStartObject();
@ -202,8 +209,10 @@ public class JsonParser extends BaseParser implements IParser {
writeAuthor(nextEntry, eventWriter);
IResource resource = nextEntry.getResource();
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content");
if (resource != null && !resource.isEmpty()) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content");
}
eventWriter.writeEnd(); // entry object
}
@ -213,8 +222,7 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.close();
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
String theChildName) throws IOException {
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
@ -255,30 +263,34 @@ public class JsonParser extends BaseParser implements IParser {
} else {
theWriter.writeStartObject();
}
if (theValue instanceof ExtensionDt) {
theWriter.write("url", ((ExtensionDt) theValue).getUrlAsString());
}
encodeCompositeElementToStreamWriter(theResDef, theResource, theValue, theWriter, childCompositeDef);
theWriter.writeEnd();
break;
}
case RESOURCE_REF: {
ResourceReferenceDt value = (ResourceReferenceDt) theValue;
ResourceReferenceDt referenceDt = (ResourceReferenceDt) theValue;
IdDt value = referenceDt.getResourceId();
if (theChildName != null) {
theWriter.writeStartObject(theChildName);
} else {
theWriter.writeStartObject();
}
String reference = value.getReference().getValue();
String reference = value.getValue();
if (StringUtils.isBlank(reference)) {
if (value.getResourceType() != null && StringUtils.isNotBlank(value.getResourceId())) {
reference = myContext.getResourceDefinition(value.getResourceType()).getName() + '/' + value.getResourceId();
if (value.getResourceType() != null && StringUtils.isNotBlank(value.getUnqualifiedId())) {
reference = myContext.getResourceDefinition(value.getResourceType()).getName() + '/' + value.getUnqualifiedId();
}
}
if (StringUtils.isNotBlank(reference)) {
theWriter.write("resource", reference);
}
if (value.getDisplay().isEmpty() == false) {
theWriter.write("display", value.getDisplay().getValueAsString());
if (referenceDt.getDisplay().isEmpty() == false) {
theWriter.write("display", referenceDt.getDisplay().getValueAsString());
}
theWriter.writeEnd();
break;
@ -316,8 +328,7 @@ public class JsonParser extends BaseParser implements IParser {
}
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
@ -449,8 +460,7 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions());
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
}
@ -471,10 +481,14 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.write("id", theResource.getId().getValue());
}
extractAndWriteExtensionsAsDirectChild(theResource, theEventWriter, resDef, theResDef, theResource);
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef);
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
theEventWriter.write("contentType", bin.getContentType());
theEventWriter.write("content",bin.getContentAsBase64());
} else {
extractAndWriteExtensionsAsDirectChild(theResource, theEventWriter, resDef, theResDef, theResource);
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef);
}
theEventWriter.writeEnd();
}
@ -520,10 +534,10 @@ public class JsonParser extends BaseParser implements IParser {
}
/**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions
*/
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IResource theResource) throws IOException {
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IResource theResource) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -833,13 +847,20 @@ public class JsonParser extends BaseParser implements IParser {
return this;
}
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
private boolean writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) {
boolean retVal = theStarted;
if (isNotBlank(theLink.getValue())) {
if (theStarted == false) {
theEventWriter.writeStartArray("link");
retVal = true;
}
theEventWriter.writeStartObject();
theEventWriter.write("rel", theRel);
theEventWriter.write("href", theLink.getValue());
theEventWriter.writeEnd();
}
return retVal;
}
private void writeAuthor(BaseBundle theBundle, JsonGenerator eventWriter) {
@ -853,8 +874,7 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
List<HeldExtension> modifierExtensions) throws IOException {
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException {
if (extensions.isEmpty() == false) {
theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) {
@ -878,9 +898,9 @@ public class JsonParser extends BaseParser implements IParser {
}
}
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IdDt theIdDt) {
if (StringUtils.isNotBlank(theIdDt.getValue())) {
theEventWriter.write(theElementName, theIdDt.getValue());
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, IPrimitiveDatatype<?> theIdDt) {
if (theIdDt != null && !theIdDt.isEmpty()) {
theEventWriter.write(theElementName, theIdDt.getValueAsString());
} else {
theEventWriter.writeNull(theElementName);
}
@ -889,9 +909,10 @@ public class JsonParser extends BaseParser implements IParser {
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue());
} else {
theEventWriter.writeNull(theElementName);
}
// else {
// theEventWriter.writeNull(theElementName);
// }
}
private class HeldExtension {

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.parser;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -40,7 +39,6 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeElemContainedResources;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
@ -63,7 +61,9 @@ import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.rest.server.Constants;
@ -121,9 +121,8 @@ class ParserState<T> {
}
/**
* Invoked after any new XML event is individually processed, containing a
* copy of the XML event. This is basically intended for embedded XHTML
* content
* Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically
* intended for embedded XHTML content
*/
public void xmlEvent(XMLEvent theNextEvent) {
myState.xmlEvent(theNextEvent);
@ -216,8 +215,8 @@ class ParserState<T> {
myInstance.setScheme(theValue);
} else if ("value".equals(theName)) {
/*
* This handles XML parsing, which is odd for this quasi-resource type,
* since the tag has three values instead of one like everything else.
* This handles XML parsing, which is odd for this quasi-resource type, since the tag has three values
* instead of one like everything else.
*/
switch (myCatState) {
case STATE_LABEL:
@ -262,10 +261,32 @@ class ParserState<T> {
}
private void putPlacerResourceInDeletedEntry(BundleEntry entry) {
IdDt id = null;
if (entry.getLinkSelf() != null && entry.getLinkSelf().isEmpty() == false) {
id = new IdDt(entry.getLinkSelf().getValue());
} else {
id = entry.getId();
}
IResource resource = entry.getResource();
if (resource == null && id != null && isNotBlank(id.getResourceType())) {
resource = myContext.getResourceDefinition(id.getResourceType()).newInstance();
resource.setId(id);
entry.setResource(resource);
}
if (resource != null) {
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt());
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id);
}
}
public class AtomEntryState extends BaseState {
private BundleEntry myEntry;
private Class<? extends IResource> myResourceType;
private boolean myDeleted;
public AtomEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(null);
@ -274,10 +295,18 @@ class ParserState<T> {
theInstance.getEntries().add(myEntry);
}
protected BundleEntry getEntry() {
return myEntry;
}
@Override
public void endingElement() throws DataFormatException {
populateResourceMetadata();
pop();
if (myDeleted) {
putPlacerResourceInDeletedEntry(myEntry);
}
}
@Override
@ -300,6 +329,10 @@ class ParserState<T> {
push(new XhtmlState(getPreResourceState(), myEntry.getSummary(), false));
} else if ("category".equals(theLocalPart)) {
push(new AtomCategoryState(myEntry.addCategory()));
} else if ("deleted".equals(theLocalPart) && myJsonMode) {
// JSON and XML deleted entries are completely different for some reason
myDeleted = true;
push(new AtomDeletedJsonWhenState(myEntry.getDeletedAt()));
} else {
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
}
@ -307,11 +340,17 @@ class ParserState<T> {
// TODO: handle category
}
@SuppressWarnings("deprecation")
private void populateResourceMetadata() {
if (myEntry.getResource() == null) {
return;
}
IdDt id = myEntry.getId();
if (id != null && id.isEmpty() == false) {
myEntry.getResource().setId(id);
}
Map<ResourceMetadataKeyEnum, Object> metadata = myEntry.getResource().getResourceMetadata();
if (myEntry.getPublished().isEmpty() == false) {
metadata.put(ResourceMetadataKeyEnum.PUBLISHED, myEntry.getPublished());
@ -328,45 +367,10 @@ class ParserState<T> {
}
if (!myEntry.getLinkSelf().isEmpty()) {
String linkSelfValue = myEntry.getLinkSelf().getValue();
/*
* Find resource ID if it is there
*/
String resNameLc = myContext.getResourceDefinition(myEntry.getResource()).getName().toLowerCase();
String subStrId = "/" + resNameLc + "/";
int idIdx = linkSelfValue.toLowerCase().lastIndexOf(subStrId);
if (idIdx != -1) {
int endIndex = linkSelfValue.indexOf('/', idIdx + subStrId.length());
String id;
if (endIndex == -1) {
id = linkSelfValue.substring(idIdx + subStrId.length());
} else {
id = linkSelfValue.substring(idIdx + subStrId.length(), endIndex);
}
myEntry.getResource().setId(new IdDt(id));
}
/*
* Find resource version ID if it is there
*/
String subStrVid = "/" + Constants.PARAM_HISTORY + "/";
int startIndex = linkSelfValue.indexOf(subStrVid);
if (startIndex > 0) {
startIndex = startIndex + subStrVid.length();
int endIndex = linkSelfValue.indexOf('?', startIndex);
if (endIndex == -1) {
endIndex = linkSelfValue.length();
}
String versionId = linkSelfValue.substring(startIndex, endIndex);
if (isNotBlank(versionId)) {
int idx = versionId.indexOf('/');
if (idx != -1) {
// Just in case
ourLog.warn("Bundle entry link-self contains path information beyond version (this will be ignored): {}", versionId);
versionId = versionId.substring(0, idx);
}
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt(versionId));
}
IdDt linkSelf = new IdDt(linkSelfValue);
myEntry.getResource().setId(linkSelf);
if (isNotBlank(linkSelf.getUnqualifiedVersionId())) {
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, linkSelf);
}
}
@ -374,6 +378,29 @@ class ParserState<T> {
}
public class AtomDeletedEntryState extends AtomEntryState {
public AtomDeletedEntryState(Bundle theInstance, Class<? extends IResource> theResourceType) {
super(theInstance, theResourceType);
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("ref".equals(theName)) {
getEntry().setId(new IdDt(theValue));
} else if ("when".equals(theName)) {
getEntry().setDeleted(new InstantDt(theValue));
}
}
@Override
public void endingElement() throws DataFormatException {
putPlacerResourceInDeletedEntry(getEntry());
super.endingElement();
}
}
private class AtomLinkState extends BaseState {
private BundleEntry myEntry;
@ -417,7 +444,11 @@ class ParserState<T> {
myInstance.getLinkBase().setValueAsString(myHref);
}
} else {
myEntry.getLinkSelf().setValueAsString(myHref);
if ("self".equals(myRel)) {
myEntry.getLinkSelf().setValueAsString(myHref);
} else if ("alternate".equals(myRel)) {
myEntry.getLinkAlternate().setValueAsString(myHref);
}
}
pop();
}
@ -429,6 +460,77 @@ class ParserState<T> {
}
private class BinaryResourceState extends BaseState {
private static final int SUBSTATE_CT = 1;
private static final int SUBSTATE_CONTENT = 2;
private Binary myInstance;
private String myData;
private int mySubState = 0;
public BinaryResourceState(PreResourceState thePreResourceState, Binary theInstance) {
super(thePreResourceState);
myInstance = theInstance;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("contentType".equals(theName)) {
myInstance.setContentType(theValue);
} else if (myJsonMode && "value".equals(theName)) {
string(theValue);
}
}
@Override
public void endingElement() throws DataFormatException {
if (mySubState == SUBSTATE_CT) {
myInstance.setContentType(myData);
mySubState = 0;
myData=null;
return;
} else if (mySubState == SUBSTATE_CONTENT) {
myInstance.setContentAsBase64(myData);
mySubState = 0;
myData=null;
return;
} else {
if (!myJsonMode) {
myInstance.setContentAsBase64(myData);
}
pop();
}
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (myJsonMode && "contentType".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CT;
} else if (myJsonMode && "content".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CONTENT;
} else {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
}
@Override
public void string(String theData) {
if (myData == null) {
myData = theData;
} else {
// this shouldn't generally happen so it's ok that it's
// inefficient
myData = myData + theData;
}
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
private class AtomPrimitiveState extends BaseState {
private String myData;
@ -440,6 +542,14 @@ class ParserState<T> {
myPrimitive = thePrimitive;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if (myJsonMode) {
string(theValue);
}
super.attributeValue(theName, theValue);
}
@Override
public void endingElement() throws DataFormatException {
myPrimitive.setValueAsString(myData);
@ -469,6 +579,40 @@ class ParserState<T> {
}
private class AtomDeletedJsonWhenState extends BaseState {
private String myData;
private IPrimitiveDatatype<?> myPrimitive;
public AtomDeletedJsonWhenState(IPrimitiveDatatype<?> thePrimitive) {
super(null);
Validate.notNull(thePrimitive, "thePrimitive");
myPrimitive = thePrimitive;
}
@Override
public void endingElement() throws DataFormatException {
myPrimitive.setValueAsString(myData);
pop();
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
myData = theValue;
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
private class AtomState extends BaseState {
private Bundle myInstance;
@ -503,6 +647,8 @@ class ParserState<T> {
push(new AtomPrimitiveState(myInstance.getUpdated()));
} else if ("author".equals(theLocalPart)) {
push(new AtomAuthorState(myInstance));
} else if ("deleted-entry".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS, theNamespaceURI)) {
push(new AtomDeletedEntryState(myInstance, myResourceType));
} else {
if (theNamespaceURI != null) {
throw new DataFormatException("Unexpected element: {" + theNamespaceURI + "}" + theLocalPart);
@ -665,7 +811,7 @@ class ParserState<T> {
case RESOURCE_REF: {
ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), (RuntimeResourceReferenceDefinition)target, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), (RuntimeResourceReferenceDefinition) target, newChildInstance);
push(newState);
return;
}
@ -716,6 +862,8 @@ class ParserState<T> {
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("id".equals(theName)) {
myInstance.setId(new IdDt(theValue));
} else if ("url".equals(theName) && myInstance instanceof ExtensionDt) {
((ExtensionDt)myInstance).setUrl(theValue);
}
}
@ -987,7 +1135,11 @@ class ParserState<T> {
myEntry.setResource(myInstance);
}
push(new ElementCompositeState(this, def, myInstance));
if ("Binary".equals(def.getName())) {
push(new BinaryResourceState(this, (Binary) myInstance));
} else {
push(new ElementCompositeState(this, def, myInstance));
}
}
public Map<String, IResource> getContainedResources() {
@ -1146,25 +1298,25 @@ class ParserState<T> {
case INITIAL:
throw new DataFormatException("Unexpected attribute: " + theValue);
case REFERENCE:
int lastSlash = theValue.lastIndexOf('/');
if (lastSlash==-1) {
myInstance.setResourceId(theValue);
} else if (lastSlash==0) {
myInstance.setResourceId(theValue.substring(1));
}else {
int secondLastSlash=theValue.lastIndexOf('/', lastSlash-1);
String resourceTypeName;
if (secondLastSlash==-1) {
resourceTypeName=theValue.substring(0,lastSlash);
}else {
resourceTypeName=theValue.substring(secondLastSlash+1,lastSlash);
}
myInstance.setResourceId(theValue.substring(lastSlash+1));
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceTypeName);
if(def!=null) {
myInstance.setResourceType(def.getImplementingClass());
}
}
// int lastSlash = theValue.lastIndexOf('/');
// if (lastSlash==-1) {
// myInstance.setResourceId(theValue);
// } else if (lastSlash==0) {
// myInstance.setResourceId(theValue.substring(1));
// }else {
// int secondLastSlash=theValue.lastIndexOf('/', lastSlash-1);
// String resourceTypeName;
// if (secondLastSlash==-1) {
// resourceTypeName=theValue.substring(0,lastSlash);
// }else {
// resourceTypeName=theValue.substring(secondLastSlash+1,lastSlash);
// }
// myInstance.setResourceId(theValue.substring(lastSlash+1));
// RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceTypeName);
// if(def!=null) {
// myInstance.setResourceType(def.getImplementingClass());
// }
// }
myInstance.setReference(theValue);
break;
}

View File

@ -70,11 +70,13 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
public class XmlParser extends BaseParser implements IParser {
@ -82,6 +84,7 @@ public class XmlParser extends BaseParser implements IParser {
static final String ATOM_NS = "http://www.w3.org/2005/Atom";
static final String FHIR_NS = "http://hl7.org/fhir";
static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/";
static final String TOMBSTONES_NS = "http://purl.org/atompub/tombstones/1.0";
static final String XHTML_NS = "http://www.w3.org/1999/xhtml";
// private static final Set<String> RESOURCE_NAMESPACES;
@ -141,10 +144,21 @@ public class XmlParser extends BaseParser implements IParser {
}
for (BundleEntry nextEntry : theBundle.getEntries()) {
eventWriter.writeStartElement("entry");
boolean deleted=false;
if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty()==false) {
deleted=true;
eventWriter.writeStartElement("at","deleted-entry",TOMBSTONES_NS);
eventWriter.writeNamespace("at", TOMBSTONES_NS);
eventWriter.writeAttribute("ref", nextEntry.getId().getValueAsString());
eventWriter.writeAttribute("when", nextEntry.getDeletedAt().getValueAsString());
} else {
eventWriter.writeStartElement("entry");
}
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
writeOptionalTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
if (!deleted) {
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
@ -162,17 +176,20 @@ public class XmlParser extends BaseParser implements IParser {
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
}
eventWriter.writeStartElement("content");
eventWriter.writeAttribute("type", "text/xml");
IResource resource = nextEntry.getResource();
if (resource != null) {
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
} else {
ourLog.warn("Bundle entry contains null resource");
if (!nextEntry.getLinkAlternate().isEmpty()) {
writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate());
}
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty()) {
eventWriter.writeStartElement("content");
eventWriter.writeAttribute("type", "text/xml");
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
eventWriter.writeEndElement(); // content
} else {
ourLog.debug("Bundle entry contains null resource");
}
eventWriter.writeEndElement(); // content
eventWriter.writeEndElement(); // entry
}
@ -290,7 +307,8 @@ public class XmlParser extends BaseParser implements IParser {
PrettyPrintWriterWrapper retVal = new PrettyPrintWriterWrapper(eventWriter);
return retVal;
} else {
return eventWriter;
NonPrettyPrintWriterWrapper retVal = new NonPrettyPrintWriterWrapper(eventWriter);
return retVal;
}
}
@ -458,8 +476,13 @@ public class XmlParser extends BaseParser implements IParser {
if (childDef == null) {
super.throwExceptionForUnknownChildType(nextChild, type);
}
if (extensionUrl != null && childName.equals("extension") == false) {
if (nextValue instanceof ExtensionDt) {
extensionUrl = ((ExtensionDt) nextValue).getUrlAsString();
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
} else if (extensionUrl != null && childName.equals("extension") == false) {
RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild;
if (extDef.isModifier()) {
theEventWriter.writeStartElement("modifierExtension");
@ -494,11 +517,11 @@ public class XmlParser extends BaseParser implements IParser {
private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, ResourceReferenceDt theRef) throws XMLStreamException {
String reference = theRef.getReference().getValue();
if (StringUtils.isBlank(reference)) {
if (theRef.getResourceType() != null && StringUtils.isNotBlank(theRef.getResourceId())) {
reference = myContext.getResourceDefinition(theRef.getResourceType()).getName() + '/' + theRef.getResourceId();
}
}
// if (StringUtils.isBlank(reference)) {
// if (theRef.getResourceType() != null && StringUtils.isNotBlank(theRef.getResourceId())) {
// reference = myContext.getResourceDefinition(theRef.getResourceType()).getName() + '/' + theRef.getResourceId();
// }
// }
if (!(theRef.getDisplay().isEmpty())) {
theEventWriter.writeStartElement("display");
@ -534,7 +557,13 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeAttribute("id", theResource.getId().getValue());
}
encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource);
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
theEventWriter.writeAttribute("contentType", bin.getContentType());
theEventWriter.writeCharacters(bin.getContentAsBase64());
} else {
encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource);
}
theEventWriter.writeEndElement();
}

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.rest.annotation;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import ca.uhn.fhir.model.api.Bundle;
/**
* RESTful method annotation to be used for the FHIR <a href="http://hl7.org/implement/standards/fhir/http.html#transaction">transaction</a> method.
*
* <p>
* This method should have a parameter of type {@link Bundle} annotated with the {@link TransactionParam} annotation.
* </p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transaction {
// nothing
}

View File

@ -0,0 +1,32 @@
package ca.uhn.fhir.rest.annotation;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value=ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionParam {
}

View File

@ -62,6 +62,9 @@ public class MethodOutcome {
return myOperationOutcome;
}
/**
* @deprecated {@link MethodOutcome#getId()} should return the complete ID including version if it is available
*/
public IdDt getVersionId() {
return myVersionId;
}

View File

@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client;
* #L%
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
@ -33,6 +35,7 @@ import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@ -44,6 +47,7 @@ import org.apache.http.entity.ContentType;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.IClientResponseHandlerHandlesBinary;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@ -100,11 +104,11 @@ public abstract class BaseClient {
return myUrlBase;
}
<T> T invokeClient(IClientResponseHandler<T> binding, BaseClientInvocation clientInvocation) {
<T> T invokeClient(IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation) {
return invokeClient(binding, clientInvocation, false);
}
<T> T invokeClient(IClientResponseHandler<T> binding, BaseClientInvocation clientInvocation, boolean theLogRequestAndResponse) {
<T> T invokeClient(IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, boolean theLogRequestAndResponse) {
// TODO: handle non 2xx status codes by throwing the correct exception,
// and ensure it's passed upwards
HttpRequestBase httpRequest;
@ -124,19 +128,6 @@ public abstract class BaseClient {
}
try {
Reader reader = createReaderFromResponse(response);
if (ourLog.isTraceEnabled() || myKeepResponses) {
String responseString = IOUtils.toString(reader);
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = responseString;
}
ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
reader = new StringReader(responseString);
}
ContentType ct = ContentType.get(response.getEntity());
String mimeType = ct != null ? ct.getMimeType() : null;
@ -155,7 +146,9 @@ public abstract class BaseClient {
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
String body=null;
Reader reader=null;
try {
reader = createReaderFromResponse(response);
body = IOUtils.toString(reader);
} catch (Exception e) {
ourLog.debug("Failed to read input stream", e);
@ -176,6 +169,54 @@ public abstract class BaseClient {
throw exception;
}
if (binding instanceof IClientResponseHandlerHandlesBinary) {
IClientResponseHandlerHandlesBinary<T> handlesBinary = (IClientResponseHandlerHandlesBinary<T>) binding;
if (handlesBinary.isBinary()) {
InputStream reader = response.getEntity().getContent();
try {
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
byte[] responseBytes = IOUtils.toByteArray(reader);
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = null;
}
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
if (theLogRequestAndResponse) {
ourLog.info("Client response: {} - {} bytes", message, responseBytes.length);
}else {
ourLog.trace("Client response: {} - {} bytes", message, responseBytes.length);
}
reader = new ByteArrayInputStream(responseBytes);
}
return handlesBinary.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} finally {
IOUtils.closeQuietly(reader);
}
}
}
Reader reader = createReaderFromResponse(response);
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
String responseString = IOUtils.toString(reader);
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = responseString;
}
if (theLogRequestAndResponse) {
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
if (StringUtils.isNotBlank(responseString)) {
ourLog.info("Client response: {}\n{}", message, responseString);
}else {
ourLog.info("Client response: {}", message, responseString);
}
}else {
ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
}
reader = new StringReader(responseString);
}
try {
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);

View File

@ -33,7 +33,7 @@ import org.apache.http.message.BasicHeader;
import ca.uhn.fhir.rest.server.EncodingEnum;
public abstract class BaseClientInvocation {
public abstract class BaseHttpClientInvocation {
private List<Header> myHeaders;

View File

@ -72,7 +72,7 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
BaseMethodBinding<?> binding = myBindings.get(theMethod);
if (binding != null) {
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
BaseHttpClientInvocation clientInvocation = binding.invokeClient(theArgs);
return invokeClient(binding, clientInvocation);
}

View File

@ -54,9 +54,12 @@ import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
import ca.uhn.fhir.rest.method.CreateMethodBinding;
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation;
import ca.uhn.fhir.rest.method.HttpGetClientInvocation;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.ReadMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.TransactionMethodBinding;
import ca.uhn.fhir.rest.method.UpdateMethodBinding;
import ca.uhn.fhir.rest.method.ValidateMethodBinding;
import ca.uhn.fhir.rest.server.Constants;
@ -69,6 +72,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private HttpRequestBase myLastRequest;
private boolean myLogRequestAndResponse;
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
@ -79,19 +84,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public Conformance conformance() {
GetClientInvocation invocation = ConformanceMethodBinding.createConformanceInvocation();
HttpGetClientInvocation invocation = ConformanceMethodBinding.createConformanceInvocation();
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
ResourceResponseHandler<Conformance> binding = new ResourceResponseHandler<Conformance>(Conformance.class);
Conformance resp = invokeClient(binding, invocation);
Conformance resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@Override
public MethodOutcome create(IResource theResource) {
BaseClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
BaseHttpClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
@ -101,21 +106,21 @@ public class GenericClient extends BaseClient implements IGenericClient {
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
MethodOutcome resp = invokeClient(binding, invocation);
MethodOutcome resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@Override
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
DeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(toResourceName(theType), theId);
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(toResourceName(theType), theId);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
final String resourceName = myContext.getResourceDefinition(theType).getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
MethodOutcome resp = invokeClient(binding, invocation);
MethodOutcome resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -130,13 +135,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public <T extends IResource> Bundle history(final Class<T> theType, IdDt theIdDt) {
GetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(toResourceName(theType), theIdDt);
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(toResourceName(theType), theIdDt);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
BundleResponseHandler binding = new BundleResponseHandler(theType);
Bundle resp = invokeClient(binding, invocation);
Bundle resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -146,15 +151,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
return history(theType, new IdDt(theId));
}
public boolean isLogRequestAndResponse() {
return myLogRequestAndResponse;
}
@Override
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
GetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
HttpGetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType);
T resp = invokeClient(binding, invocation);
T resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -179,16 +188,16 @@ public class GenericClient extends BaseClient implements IGenericClient {
qualifier = nextValue.getQueryParameterQualifier();
}
qualifier = StringUtils.defaultString(qualifier);
params.put(nextEntry.getKey()+qualifier, valueList);
params.put(nextEntry.getKey() + qualifier, valueList);
}
GetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
HttpGetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
BundleResponseHandler binding = new BundleResponseHandler(theType);
Bundle resp = invokeClient(binding, invocation);
Bundle resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -199,9 +208,30 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = theLastRequest;
}
@Override
public void setLogRequestAndResponse(boolean theLogRequestAndResponse) {
myLogRequestAndResponse = theLogRequestAndResponse;
}
private String toResourceName(Class<? extends IResource> theType) {
return myContext.getResourceDefinition(theType).getName();
}
@Override
public List<IResource> transaction(List<IResource> theResources) {
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
Bundle resp = invokeClient(new BundleResponseHandler(null), invocation, myLogRequestAndResponse);
return resp.toListOfResources();
}
@Override
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
BaseClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
BaseHttpClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
@ -210,7 +240,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
MethodOutcome resp = invokeClient(binding, invocation);
MethodOutcome resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -221,7 +251,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public MethodOutcome validate(IResource theResource) {
BaseClientInvocation invocation = ValidateMethodBinding.createValidateInvocation(theResource, null, myContext);
BaseHttpClientInvocation invocation = ValidateMethodBinding.createValidateInvocation(theResource, null, myContext);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
@ -230,19 +260,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
MethodOutcome resp = invokeClient(binding, invocation);
MethodOutcome resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@Override
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
GetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
HttpGetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType);
T resp = invokeClient(binding, invocation);
T resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp;
}
@ -251,41 +281,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return vread(theType, new IdDt(theId), new IdDt(theVersionId));
}
private String toResourceName(Class<? extends IResource> theType) {
return myContext.getResourceDefinition(theType).getName();
}
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
private final String myResourceName;
private OutcomeResponseHandler(String theResourceName) {
myResourceName = theResourceName;
}
@Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
}
private final class ResourceResponseHandler<T extends IResource> implements IClientResponseHandler<T> {
private Class<T> myType;
public ResourceResponseHandler(Class<T> theType) {
myType = theType;
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(myType, theResponseReader);
}
}
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
private Class<? extends IResource> myType;
@ -295,8 +290,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
public Bundle 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);
@ -310,9 +304,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>();
private List<Include> myInclude = new ArrayList<Include>();
private boolean myLogRequestAndResponse;
private EncodingEnum myParamEncoding;
private Integer myParamLimit;
private boolean myQueryLogRequestAndResponse;
private String myResourceName;
private Class<? extends IResource> myResourceType;
private List<SortInternal> mySort = new ArrayList<SortInternal>();
@ -327,6 +321,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
myResourceName = theResourceName;
}
private void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
@Override
public IQuery and(ICriterion theCriterion) {
myCriterion.add((ICriterionInternal) theCriterion);
@ -335,7 +336,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public IQuery andLogRequestAndResponse(boolean theLogRequestAndResponse) {
myLogRequestAndResponse = theLogRequestAndResponse;
myQueryLogRequestAndResponse = theLogRequestAndResponse;
return this;
}
@ -383,14 +384,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit));
}
GetClientInvocation invocation = new GetClientInvocation(params, myResourceName);
HttpGetClientInvocation invocation = new HttpGetClientInvocation(params, myResourceName);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
BundleResponseHandler binding = new BundleResponseHandler(myResourceType);
Bundle resp = invokeClient(binding, invocation, myLogRequestAndResponse);
Bundle resp = invokeClient(binding, invocation, myQueryLogRequestAndResponse || myLogRequestAndResponse);
return resp;
}
@ -411,6 +412,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public IQuery prettyPrint() {
setPrettyPrint(true);
return this;
}
@Override
public ISort sort() {
SortInternal retVal = new SortInternal(this);
@ -424,19 +431,20 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
private void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
private final String myResourceName;
private OutcomeResponseHandler(String theResourceName) {
myResourceName = theResourceName;
}
@Override
public IQuery prettyPrint() {
setPrettyPrint(true);
return this;
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
}
private class QueryInternal implements IUntypedQuery {
@ -453,6 +461,22 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private final class ResourceResponseHandler<T extends IResource> implements IClientResponseHandler<T> {
private Class<T> myType;
public ResourceResponseHandler(Class<T> theType) {
myType = theType;
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(myType, theResponseReader);
}
}
private class SortInternal implements ISort {
private ForInternal myFor;

View File

@ -47,6 +47,16 @@ public interface IGenericClient {
*/
MethodOutcome create(IResource theResource);
/**
* Implementation of the "transaction" method.
*
* @param theResources
* The resources to create/update in a single transaction
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other {@link IResource#getResourceMetadata() metadata}
*/
List<IResource> transaction(List<IResource> theResources);
/**
* Implementation of the "delete instance" method.
*
@ -184,4 +194,13 @@ public interface IGenericClient {
*/
<T extends IResource> T vread(Class<T> theType, String theId, String theVersionId);
/**
* If set to <code>true</code>, the client will log all requests and all responses. This
* is probably not a good production setting since it will result in a lot of extra logging, but
* it can be useful for troubleshooting.
*
* @param theLogRequestAndResponse Should requests and responses be logged
*/
void setLogRequestAndResponse(boolean theLogRequestAndResponse);
}

View File

@ -45,7 +45,7 @@ public class ReferenceParam implements IParam {
* the logical ID or the absolute URL of the resource)
*/
public ICriterion hasId(IdDt theId) {
return new StringCriterion(getParamName(), theId.getValueAsString());
return new StringCriterion(getParamName(), theId.getValue());
}
/**

View File

@ -39,8 +39,7 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.PostClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -119,8 +118,8 @@ public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
protected abstract boolean isDelete();
@Override
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
PostClientInvocation retVal;
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
HttpPostClientInvocation retVal;
IdDt id = (IdDt) theArgs[myIdParamIndex];
if (id == null || id.isEmpty()) {
@ -139,15 +138,15 @@ public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
if (isDelete()) {
if (versionId != null) {
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS, Constants.PARAM_DELETE);
retVal = new HttpPostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS, Constants.PARAM_DELETE);
} else {
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_TAGS, Constants.PARAM_DELETE);
retVal = new HttpPostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_TAGS, Constants.PARAM_DELETE);
}
} else {
if (versionId != null) {
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
retVal = new HttpPostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
} else {
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_TAGS);
retVal = new HttpPostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_TAGS);
}
}
for (int idx = 0; idx < theArgs.length; idx++) {
@ -169,7 +168,7 @@ public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
}
IParser parser = createAppropriateParserForParsingServerRequest(theRequest);
Reader reader = theRequest.getInputReader();
Reader reader = theRequest.getServletRequest().getReader();
try {
TagList tagList = parser.parseTagList(reader);
params[myTagListParamIndex] = tagList;
@ -178,7 +177,7 @@ public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
}
invokeServerMethod(params);
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest);
theResponse.setContentType(responseEncoding.getResourceContentType());
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.client;
package ca.uhn.fhir.rest.method;
/*
* #%L
@ -25,33 +25,41 @@ import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
public abstract class BaseClientInvocationWithContents extends BaseClientInvocation {
public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvocation {
private final FhirContext myContext;
private final IResource myResource;
private final String myUrlExtension;
private final TagList myTagList;
private final List<IResource> myResources;
public BaseClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
super();
myContext = theContext;
myResource = theResource;
myUrlExtension = theUrlExtension;
myTagList = null;
myResources=null;
}
public BaseClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
super();
if (theTagList == null) {
throw new NullPointerException("Tag list must not be null");
@ -60,10 +68,19 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
myResource = null;
myContext = theContext;
myTagList = theTagList;
myResources=null;
myUrlExtension = StringUtils.join(theUrlExtension, '/');
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, List<IResource> theResources) {
myContext=theContext;
myResource=null;
myTagList=null;
myUrlExtension=null;
myResources = theResources;
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
StringBuilder b = new StringBuilder();
@ -74,7 +91,15 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
b.append(StringUtils.defaultString(myUrlExtension));
appendExtraParamsWithQuestionMark(theExtraParams, b, true);
String url = b.toString();
if (myResource != null && Binary.class.isAssignableFrom(myResource.getClass())) {
Binary binary = (Binary)myResource;
ByteArrayEntity entity = new ByteArrayEntity(binary.getContent(), ContentType.parse(binary.getContentType()));
HttpRequestBase retVal = createRequest(url, entity);
return retVal;
}
IParser parser;
String contentType;
if (theEncoding == EncodingEnum.JSON) {
@ -88,17 +113,20 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
String contents;
if (myTagList != null) {
contents = parser.encodeTagListToString(myTagList);
} else if (myResources != null) {
Bundle bundle = BaseResourceReturningMethodBinding.createBundleFromResourceList(myContext, "", myResources, theEncoding, "", "", false, NarrativeModeEnum.NORMAL);
contents = parser.encodeBundleToString(bundle);
} else {
contents = parser.encodeResourceToString(myResource);
}
StringEntity entity = new StringEntity(contents, ContentType.create(contentType, "UTF-8"));
HttpRequestBase retVal = createRequest(b.toString(), entity);
HttpRequestBase retVal = createRequest(url, entity);
super.addHeadersToRequest(retVal);
return retVal;
}
protected abstract HttpRequestBase createRequest(String url, StringEntity theEntity);
protected abstract HttpRequestBase createRequest(String url, AbstractHttpEntity theEntity);
}

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.Reader;
@ -30,9 +30,7 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
@ -56,10 +54,11 @@ import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -80,9 +79,11 @@ import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
private FhirContext myContext;
private Method myMethod;
private List<IParameter> myParameters;
private Object myProvider;
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
@ -103,13 +104,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return myMethod;
}
public List<IParameter> getParameters() {
return myParameters;
}
public Object getProvider() {
return myProvider;
}
/**
* Returns the name of the resource this method handles, or
* <code>null</code> if this method is not resource specific
* Returns the name of the resource this method handles, or <code>null</code> if this method is not resource
* specific
*/
public abstract String getResourceName();
@ -117,26 +122,60 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
public abstract RestfulOperationSystemEnum getSystemOperationType();
public abstract BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
public abstract boolean incomingServerRequestMatchesMethod(Request theRequest);
public abstract BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
public abstract void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException;
public abstract boolean incomingServerRequestMatchesMethod(Request theRequest);
/** For unit tests only */
public void setParameters(List<IParameter> theParameters) {
myParameters = theParameters;
}
protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) {
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
if (encoding==null) {
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
if (encoding == null) {
NonFhirResponseException ex = NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
populateException(ex, theResponseReader);
throw ex;
}
IParser parser=encoding.newParser(getContext());
IParser parser = encoding.newParser(getContext());
return parser;
}
public List<IParameter> getParameters() {
return myParameters;
protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) {
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
EncodingEnum encoding;
if (isBlank(contentTypeHeader)) {
encoding = EncodingEnum.XML;
} else {
int semicolon = contentTypeHeader.indexOf(';');
if (semicolon != -1) {
contentTypeHeader = contentTypeHeader.substring(0, semicolon);
}
encoding = EncodingEnum.forContentType(contentTypeHeader);
}
if (encoding == null) {
throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader);
}
IParser parser = encoding.newParser(getContext());
return parser;
}
protected Object[] createParametersForServerRequest(Request theRequest, IResource theResource) {
Object[] params = new Object[getParameters().size()];
for (int i = 0; i < getParameters().size(); i++) {
IParameter param = getParameters().get(i);
if (param == null) {
continue;
}
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theResource);
}
return params;
}
protected Object invokeServerMethod(Object[] theMethodParams) {
@ -154,9 +193,36 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
}
/** For unit tests only */
public void setParameters(List<IParameter> theParameters) {
myParameters = theParameters;
protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
BaseServerResponseException ex;
switch (theStatusCode) {
case Constants.STATUS_HTTP_400_BAD_REQUEST:
ex = new InvalidRequestException("Server responded with HTTP 400");
break;
case Constants.STATUS_HTTP_404_NOT_FOUND:
ex = new ResourceNotFoundException("Server responded with HTTP 404");
break;
case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
ex = new MethodNotAllowedException("Server responded with HTTP 405");
break;
case Constants.STATUS_HTTP_409_CONFLICT:
ex = new ResourceVersionConflictException("Server responded with HTTP 409");
break;
case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
ex = new ResourceVersionNotSpecifiedException("Server responded with HTTP 412");
break;
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
OperationOutcome operationOutcome = parser.parseResource(OperationOutcome.class, theResponseReader);
ex = new UnprocessableEntityException(operationOutcome);
break;
default:
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
break;
}
populateException(ex, theResponseReader);
return ex;
}
@SuppressWarnings("unchecked")
@ -172,8 +238,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
GetTags getTags = theMethod.getAnnotation(GetTags.class);
AddTags addTags = theMethod.getAnnotation(AddTags.class);
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
Transaction transaction = theMethod.getAnnotation(Transaction.class);
// ** if you add another annotation above, also add it to the next line:
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags)) {
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction)) {
return null;
}
@ -283,6 +350,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return new AddTagsMethodBinding(theMethod, theContext, theProvider, addTags);
} else if (deleteTags != null) {
return new DeleteTagsMethodBinding(theMethod, theContext, theProvider, deleteTags);
} else if (transaction != null) {
return new TransactionMethodBinding(theMethod, theContext, theProvider);
} else {
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
}
@ -309,50 +378,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
// return sm;
}
public static EncodingEnum determineResponseEncoding(Request theReq) {
String[] format = theReq.getParameters().remove(Constants.PARAM_FORMAT);
if (format != null) {
for (String nextFormat : format) {
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
if (retVal != null) {
return retVal;
}
}
}
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders("Accept");
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
if (retVal != null) {
return retVal;
}
}
}
return EncodingEnum.XML;
}
protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) {
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
EncodingEnum encoding;
if (isBlank(contentTypeHeader)) {
encoding = EncodingEnum.XML;
} else {
int semicolon = contentTypeHeader.indexOf(';');
if (semicolon!=-1) {
contentTypeHeader=contentTypeHeader.substring(0,semicolon);
}
encoding = EncodingEnum.forContentType(contentTypeHeader);
}
if (encoding==null) {
throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader);
}
IParser parser=encoding.newParser(getContext());
return parser;
}
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
Object obj1 = null;
for (Object object : theAnnotations) {
@ -376,6 +401,15 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return true;
}
private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
try {
String responseText = IOUtils.toString(theResponseReader);
theEx.setResponseBody(responseText);
} catch (IOException e) {
ourLog.debug("Failed to read response", e);
}
}
private static String toLogString(Class<?> theType) {
if (theType == null) {
return null;
@ -394,18 +428,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return retVal;
}
protected Object[] createParametersForServerRequest(Request theRequest, IResource theResource) {
Object[] params = new Object[getParameters().size()];
for (int i = 0; i < getParameters().size(); i++) {
IParameter param = getParameters().get(i);
if (param == null) {
continue;
}
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theResource);
}
return params;
}
protected static List<IResource> toResourceList(Object response) throws InternalErrorException {
if (response == null) {
return Collections.emptyList();
@ -422,59 +444,4 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
}
protected static boolean prettyPrintResponse(Request theRequest) {
Map<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
boolean prettyPrint = false;
if (pretty != null && pretty.length > 0) {
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
prettyPrint = true;
}
}
return prettyPrint;
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
BaseServerResponseException ex;
switch (theStatusCode) {
case Constants.STATUS_HTTP_400_BAD_REQUEST:
ex = new InvalidRequestException("Server responded with HTTP 400");
break;
case Constants.STATUS_HTTP_404_NOT_FOUND:
ex = new ResourceNotFoundException("Server responded with HTTP 404");
break;
case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
ex = new MethodNotAllowedException("Server responded with HTTP 405");
break;
case Constants.STATUS_HTTP_409_CONFLICT:
ex = new ResourceVersionConflictException("Server responded with HTTP 409");
break;
case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
ex = new ResourceVersionNotSpecifiedException("Server responded with HTTP 412");
break;
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
OperationOutcome operationOutcome = parser.parseResource(OperationOutcome.class, theResponseReader);
ex = new UnprocessableEntityException(operationOutcome);
break;
default:
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
break;
}
populateException(ex, theResponseReader);
return ex;
}
private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
try {
String responseText = IOUtils.toString(theResponseReader);
theEx.setResponseBody(responseText);
} catch (IOException e) {
ourLog.debug("Failed to read response", e);
}
}
}

View File

@ -47,7 +47,7 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -112,11 +112,9 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
@Override
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
EncodingEnum encoding = determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
IResource resource;
if (requestContainsResource()) {
resource = parser.parseResource(theRequest.getInputReader());
resource = parseIncomingServerResource(theRequest);
TagList tagList = new TagList();
for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) {
String nextTagComplete = enumeration.nextElement();
@ -138,10 +136,12 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
response = (MethodOutcome) invokeServerMethod(params);
} catch (InternalErrorException e) {
ourLog.error("Internal error during method invocation", e);
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
return;
} catch (BaseServerResponseException e) {
ourLog.info("Exception during method invocation: " + e.getMessage());
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
return;
}
@ -172,9 +172,11 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
theServer.addHeadersToResponse(theResponse);
if (response != null && response.getOperationOutcome() != null) {
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
theResponse.setContentType(encoding.getResourceContentType());
Writer writer = theResponse.getWriter();
parser.setPrettyPrint(prettyPrintResponse(theRequest));
IParser parser = encoding.newParser(getContext());
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
try {
parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
} finally {
@ -189,6 +191,16 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
// getMethod().in
}
/**
* @throws IOException
*/
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
EncodingEnum encoding = RestfulServer.determineRequestEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
IResource resource = parser.parseResource(theRequest.getServletRequest().getReader());
return resource;
}
/*
* @Override public void invokeServer(RestfulServer theServer, Request
* theRequest, HttpServletResponse theResponse) throws
@ -334,7 +346,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
return false;
}
protected abstract BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
protected abstract BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
/**
* For servers, this method will match only incoming requests that match the
@ -361,7 +373,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
if (theE.getOperationOutcome() != null) {
theResponse.setContentType(theEncoding.getResourceContentType());
IParser parser = theEncoding.newParser(theServer.getFhirContext());
parser.setPrettyPrint(prettyPrintResponse(theRequest));
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
Writer writer = theResponse.getWriter();
try {
parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);
@ -420,15 +432,20 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
}
protected static void parseContentLocation(MethodOutcome theOutcomeToPopulate, String theResourceName, String theLocationHeader) {
if (StringUtils.isBlank(theLocationHeader)) {
return;
}
theOutcomeToPopulate.setId(new IdDt(theLocationHeader));
String resourceNamePart = "/" + theResourceName + "/";
int resourceIndex = theLocationHeader.lastIndexOf(resourceNamePart);
if (resourceIndex > -1) {
int idIndexStart = resourceIndex + resourceNamePart.length();
int idIndexEnd = theLocationHeader.indexOf('/', idIndexStart);
if (idIndexEnd == -1) {
theOutcomeToPopulate.setId(new IdDt(theLocationHeader.substring(idIndexStart)));
// nothing
} else {
theOutcomeToPopulate.setId(new IdDt(theLocationHeader.substring(idIndexStart, idIndexEnd)));
String versionIdPart = "/_history/";
int historyIdStart = theLocationHeader.indexOf(versionIdPart, idIndexEnd);
if (historyIdStart != -1) {

View File

@ -20,8 +20,10 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import java.io.IOException;
import java.lang.reflect.Method;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException;
@ -30,8 +32,11 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.Constants;
@ -41,6 +46,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding {
private int myResourceParameterIndex;
private String myResourceName;
private boolean myBinary;
public BaseOutcomeReturningMethodBindingWithResourceParam(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
super(theMethod, theContext, theMethodAnnotation, theProvider);
@ -57,6 +63,10 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
resourceType=((IResourceProvider) theProvider).getResourceType();
}
if (resourceType.isAssignableFrom(Binary.class)) {
myBinary = true;
}
myResourceName = theContext.getResourceDefinition(resourceType).getName();
myResourceParameterIndex = index;
@ -70,6 +80,29 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
}
@Override
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
if (myBinary) {
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
byte[] contents = IOUtils.toByteArray(theRequest.getServletRequest().getInputStream());
return new Binary(ct, contents);
}else {
return super.parseIncomingServerResource(theRequest);
}
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
// TODO Auto-generated method stub
return null;
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
// TODO Auto-generated method stub
return null;
}
/**
* For subclasses to override
*/
@ -88,13 +121,13 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
}
@Override
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
IResource resource = (IResource) theArgs[myResourceParameterIndex];
if (resource == null) {
throw new NullPointerException("Resource can not be null");
}
BaseClientInvocation retVal = createClientInvocation(theArgs, resource);
BaseHttpClientInvocation retVal = createClientInvocation(theArgs, resource);
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.PrintWriter;
@ -35,8 +35,10 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.DateUtils;
@ -50,8 +52,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser;
@ -187,7 +188,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
// Pretty print
boolean prettyPrint = prettyPrintResponse(theRequest);
boolean prettyPrint = RestfulServer.prettyPrintResponse(theRequest);
// Narrative mode
Map<String, String[]> requestParams = theRequest.getParameters();
@ -201,7 +202,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
// Determine response encoding
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest);
// Is this request coming from a browser
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
@ -210,12 +211,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
requestIsBrowser = true;
}
Object requestObject = parseRequestObject(theRequest);
// Method params
Object[] params = new Object[getParameters().size()];
for (int i = 0; i < getParameters().size(); i++) {
IParameter param = getParameters().get(i);
if (param != null) {
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, null);
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, requestObject);
}
}
@ -235,56 +238,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
}
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof String) {
if (isNotBlank((String) retValObj)) {
return new IdDt((String) retValObj);
} else {
return null;
}
} else if (retValObj instanceof IdDt) {
if (((IdDt) retValObj).isEmpty()) {
return null;
} else {
return (IdDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
}
private InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof Date) {
return new InstantDt((Date) retValObj);
} else if (retValObj instanceof InstantDt) {
if (((InstantDt) retValObj).isEmpty()) {
return null;
} else {
return (InstantDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
}
private TagList getTagListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof TagList) {
if (((TagList) retValObj).isEmpty()) {
return null;
} else {
return (TagList) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + TagList.class.getCanonicalName());
}
private IParser getNewParser(EncodingEnum theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
IParser parser;
switch (theResponseEncoding) {
@ -303,6 +256,12 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
NarrativeModeEnum theNarrativeMode) throws IOException {
assert !theServerBase.endsWith("/");
for (IResource next : theResult) {
if (next.getId() == null || next.getId().isEmpty()) {
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
}
}
theHttpResponse.setStatus(200);
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
@ -317,98 +276,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
theServer.addHeadersToResponse(theHttpResponse);
Bundle bundle = new Bundle();
bundle.getAuthorName().setValue(getClass().getCanonicalName());
bundle.getBundleId().setValue(UUID.randomUUID().toString());
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
bundle.getLinkBase().setValue(theServerBase);
bundle.getLinkSelf().setValue(theCompleteUrl);
for (IResource next : theResult) {
BundleEntry entry = new BundleEntry();
bundle.getEntries().add(entry);
entry.setResource(next);
TagList list = (TagList) next.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
for (Tag tag : list) {
if (StringUtils.isNotBlank(tag.getTerm())) {
entry.addCategory().setTerm(tag.getTerm()).setLabel(tag.getLabel()).setScheme(tag.getScheme());
}
}
}
RuntimeResourceDefinition def = getContext().getResourceDefinition(next);
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
entry.getId().setValue(next.getId().getValue());
entry.getTitle().setValue(def.getName() + " " + next.getId().getValue());
StringBuilder b = new StringBuilder();
b.append(theServerBase);
b.append('/');
b.append(def.getName());
b.append('/');
String resId = next.getId().getValue();
b.append(resId);
/*
* If this is a history operation, we add the version of the
* resource to the self link to indicate the version
*/
if (getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_INSTANCE || getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_TYPE || getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
if (versionId != null) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(versionId.getValue());
} else {
throw new InternalErrorException("Server did not provide a VERSION_ID in the resource metadata for resource with ID " + resId);
}
}
InstantDt published = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.PUBLISHED);
if (published == null) {
entry.getPublished().setToCurrentTimeInLocalTimeZone();
} else {
entry.setPublished(published);
}
InstantDt updated = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
entry.setUpdated(updated);
}
TagList tagList = getTagListFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.TAG_LIST);
if (tagList != null) {
for (Tag nextTag : tagList) {
entry.addCategory(nextTag);
}
}
boolean haveQ = false;
if (thePrettyPrint) {
b.append('?').append(Constants.PARAM_PRETTY).append("=true");
haveQ = true;
}
if (theResponseEncoding == EncodingEnum.JSON) {
if (!haveQ) {
b.append('?');
haveQ = true;
} else {
b.append('&');
}
b.append(Constants.PARAM_FORMAT).append("=json");
}
if (theNarrativeMode != NarrativeModeEnum.NORMAL) {
b.append(Constants.PARAM_NARRATIVE).append("=").append(theNarrativeMode.name().toLowerCase());
}
entry.getLinkSelf().setValue(b.toString());
}
}
bundle.getTotalResults().setValue(theResult.size());
Bundle bundle = createBundleFromResourceList(getContext(), getClass().getCanonicalName(), theResult, theResponseEncoding, theServerBase, theCompleteUrl, thePrettyPrint, theNarrativeMode);
PrintWriter writer = theHttpResponse.getWriter();
try {
@ -428,6 +296,24 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
theHttpResponse.setStatus(200);
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
if (isNotBlank(bin.getContentType())) {
theHttpResponse.setContentType(bin.getContentType());
}else {
theHttpResponse.setContentType(Constants.CT_OCTET_STREAM);
}
if (bin.getContent()==null || bin.getContent().length==0) {
return;
}
theHttpResponse.setContentLength(bin.getContent().length);
ServletOutputStream oos = theHttpResponse.getOutputStream();
oos.write(bin.getContent());
oos.close();
return;
}
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType());
} else if (theNarrativeMode == NarrativeModeEnum.ONLY) {
@ -439,7 +325,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
theServer.addHeadersToResponse(theHttpResponse);
InstantDt lastUpdated = getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
InstantDt lastUpdated = (InstantDt) ResourceMetadataKeyEnum.UPDATED.get(theResource);
if (lastUpdated != null) {
theHttpResponse.addHeader(Constants.HEADER_LAST_MODIFIED, lastUpdated.getValueAsString());
}
@ -466,6 +352,31 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
/**
* Subclasses may override
*/
protected Object parseRequestObject(@SuppressWarnings("unused") Request theRequest) throws IOException {
return null;
}
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
Bundle bundle = new Bundle();
bundle.getAuthorName().setValue(theAuthor);
bundle.getBundleId().setValue(UUID.randomUUID().toString());
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
bundle.getLinkBase().setValue(theServerBase);
bundle.getLinkSelf().setValue(theCompleteUrl);
for (IResource next : theResult) {
bundle.addResource(next, theContext, theServerBase);
}
bundle.getTotalResults().setValue(theResult.size());
return bundle;
}
public enum MethodReturnTypeEnum {
BUNDLE, LIST_OF_RESOURCES, RESOURCE
}

View File

@ -30,7 +30,6 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@ -53,8 +52,8 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
}
@Override
public GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
GetClientInvocation retVal = createConformanceInvocation();
public HttpGetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
HttpGetClientInvocation retVal = createConformanceInvocation();
if (theArgs != null) {
for (int idx = 0; idx < theArgs.length; idx++) {
@ -66,8 +65,8 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
return retVal;
}
public static GetClientInvocation createConformanceInvocation() {
return new GetClientInvocation("metadata");
public static HttpGetClientInvocation createConformanceInvocation() {
return new HttpGetClientInvocation("metadata");
}
@Override

View File

@ -30,8 +30,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.PostClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
@ -57,10 +56,10 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
}
@Override
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource) {
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource resource) {
FhirContext context = getContext();
BaseClientInvocation retVal = createCreateInvocation(resource, context);
BaseHttpClientInvocation retVal = createCreateInvocation(resource, context);
if (theArgs != null) {
for (int idx = 0; idx < theArgs.length; idx++) {
@ -72,14 +71,14 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return retVal;
}
public static PostClientInvocation createCreateInvocation(IResource resource, FhirContext context) {
public static HttpPostClientInvocation createCreateInvocation(IResource resource, FhirContext context) {
RuntimeResourceDefinition def = context.getResourceDefinition(resource);
String resourceName = def.getName();
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(resourceName);
return new PostClientInvocation(context, resource, urlExtension.toString());
return new HttpPostClientInvocation(context, resource, urlExtension.toString());
}
@Override

View File

@ -34,9 +34,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.DeleteClientInvocation;
import ca.uhn.fhir.rest.client.PostClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -106,11 +104,11 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
}
@Override
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(getContext().getResourceDefinition(theResource).getName());
return new PostClientInvocation(getContext(), theResource, urlExtension.toString());
return new HttpPostClientInvocation(getContext(), theResource, urlExtension.toString());
}
@Override
@ -119,14 +117,14 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
}
@Override
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
if (idDt == null) {
throw new NullPointerException("ID can not be null");
}
String resourceName = getResourceName();
DeleteClientInvocation retVal = createDeleteInvocation(resourceName, idDt);
HttpDeleteClientInvocation retVal = createDeleteInvocation(resourceName, idDt);
for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx);
@ -136,9 +134,9 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
return retVal;
}
public static DeleteClientInvocation createDeleteInvocation(String theResourceName, IdDt idDt) {
public static HttpDeleteClientInvocation createDeleteInvocation(String theResourceName, IdDt idDt) {
String id = idDt.getValue();
DeleteClientInvocation retVal = new DeleteClientInvocation(theResourceName, id);
HttpDeleteClientInvocation retVal = new HttpDeleteClientInvocation(theResourceName, id);
return retVal;
}

View File

@ -39,8 +39,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -107,8 +106,8 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
}
@Override
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
GetClientInvocation retVal;
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
HttpGetClientInvocation retVal;
IdDt id = null;
IdDt versionId = null;
@ -122,15 +121,17 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
if (myType != IResource.class) {
if (id != null) {
if (versionId != null) {
retVal = new GetClientInvocation(getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getResourceName(), id.getUnqualifiedId(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
} else if (id.hasUnqualifiedVersionId()){
retVal = new HttpGetClientInvocation(getResourceName(), id.getUnqualifiedId(), Constants.PARAM_HISTORY, id.getUnqualifiedVersionId(), Constants.PARAM_TAGS);
} else {
retVal = new GetClientInvocation(getResourceName(), id.getValue(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getResourceName(), id.getUnqualifiedId(), Constants.PARAM_TAGS);
}
} else {
retVal = new GetClientInvocation(getResourceName(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getResourceName(), Constants.PARAM_TAGS);
}
} else {
retVal = new GetClientInvocation(Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(Constants.PARAM_TAGS);
}
if (theArgs != null) {
@ -156,7 +157,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
TagList resp = (TagList) invokeServerMethod(params);
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest);
theResponse.setContentType(responseEncoding.getResourceContentType());
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);
@ -165,7 +166,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
theServer.addHeadersToResponse(theResponse);
IParser parser = responseEncoding.newParser(getContext());
parser.setPrettyPrint(prettyPrintResponse(theRequest));
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
PrintWriter writer = theResponse.getWriter();
try {
parser.encodeTagListToWriter(resp, writer);
@ -193,9 +194,9 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
if ((myIdParamIndex != null) != (theRequest.getId() != null)) {
return false;
}
if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
return false;
}
// if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
// return false;
// }
return true;
}

View File

@ -34,8 +34,7 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants;
@ -103,7 +102,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
IdDt id = null;
String resourceName = myResourceName;
if (myIdParamIndex != null) {
@ -112,20 +111,20 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
throw new NullPointerException("ID can not be null");
}
}
GetClientInvocation retVal = createHistoryInvocation(resourceName, id);
HttpGetClientInvocation retVal = createHistoryInvocation(resourceName, id);
if (theArgs != null) {
for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx);
nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], retVal.getParameters(),retVal);
nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], retVal.getParameters(), retVal);
}
}
return retVal;
}
public static GetClientInvocation createHistoryInvocation(String theResourceName, IdDt theId) {
public static HttpGetClientInvocation createHistoryInvocation(String theResourceName, IdDt theId) {
StringBuilder b = new StringBuilder();
if (theResourceName != null) {
b.append(theResourceName);
@ -138,7 +137,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
b.append('/');
}
b.append(Constants.PARAM_HISTORY);
GetClientInvocation retVal = new GetClientInvocation(b.toString());
HttpGetClientInvocation retVal = new HttpGetClientInvocation(b.toString());
return retVal;
}
@ -151,18 +150,20 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
Object response = invokeServerMethod(theMethodParams);
List<IResource> resources = toResourceList(response);
int index=0;
int index = 0;
for (IResource nextResource : resources) {
if (nextResource.getId() == null || nextResource.getId().isEmpty()) {
throw new InternalErrorException("Server provided resource at index " + index + " with no ID set (using IResource#setId(IdDt))");
}
IdDt versionId = getIdFromMetadataOrNullIfNone(nextResource.getResourceMetadata(),ResourceMetadataKeyEnum.VERSION_ID);
if (versionId == null||versionId.isEmpty()) {
throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#Resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, Object))");
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(nextResource);
if (versionId == null || versionId.isEmpty()) {
if (nextResource.getId().getUnqualifiedVersionId() == null) {
throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#setId(IdDt))");
}
}
index++;
}
return resources;
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.client;
package ca.uhn.fhir.rest.method;
/*
* #%L
@ -27,13 +27,14 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class DeleteClientInvocation extends BaseClientInvocation {
public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
private String myUrlPath;
public DeleteClientInvocation(String... theUrlFragments) {
public HttpDeleteClientInvocation(String... theUrlFragments) {
super();
myUrlPath = StringUtils.join(theUrlFragments, '/');
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.client;
package ca.uhn.fhir.rest.method;
/*
* #%L
@ -32,24 +32,25 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class GetClientInvocation extends BaseClientInvocation {
public class HttpGetClientInvocation extends BaseHttpClientInvocation {
private final Map<String, List<String>> myParameters;
private final String myUrlPath;
public GetClientInvocation(Map<String, List<String>> theParameters, String... theUrlFragments) {
public HttpGetClientInvocation(Map<String, List<String>> theParameters, String... theUrlFragments) {
myParameters = theParameters;
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public GetClientInvocation(String theUrlPath) {
public HttpGetClientInvocation(String theUrlPath) {
myParameters = new HashMap<String, List<String>>();
myUrlPath = theUrlPath;
}
public GetClientInvocation(String... theUrlFragments) {
public HttpGetClientInvocation(String... theUrlFragments) {
myParameters = new HashMap<String, List<String>>();
myUrlPath = StringUtils.join(theUrlFragments, '/');
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.client;
package ca.uhn.fhir.rest.method;
/*
* #%L
@ -20,27 +20,34 @@ package ca.uhn.fhir.rest.client;
* #L%
*/
import java.util.List;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.AbstractHttpEntity;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
public class PostClientInvocation extends BaseClientInvocationWithContents {
public class HttpPostClientInvocation extends BaseHttpClientInvocationWithContents {
public PostClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
public HttpPostClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
super(theContext, theResource, theUrlExtension);
}
public PostClientInvocation(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
public HttpPostClientInvocation(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
super(theContext, theTagList, theUrlExtension);
}
public HttpPostClientInvocation(FhirContext theContext, List<IResource> theResources) {
super(theContext, theResources);
}
@Override
protected HttpPost createRequest(String url, StringEntity theEntity) {
protected HttpPost createRequest(String url, AbstractHttpEntity theEntity) {
HttpPost retVal = new HttpPost(url);
retVal.setEntity(theEntity);
return retVal;

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.client;
package ca.uhn.fhir.rest.method;
/*
* #%L
@ -22,19 +22,19 @@ package ca.uhn.fhir.rest.client;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.AbstractHttpEntity;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
public class PutClientInvocation extends BaseClientInvocationWithContents {
public class HttpPutClientInvocation extends BaseHttpClientInvocationWithContents {
public PutClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
public HttpPutClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
super(theContext, theResource, theUrlExtension);
}
@Override
protected HttpRequestBase createRequest(String url, StringEntity theEntity) {
protected HttpRequestBase createRequest(String url, AbstractHttpEntity theEntity) {
HttpPut retVal = new HttpPut(url);
retVal.setEntity(theEntity);
return retVal;

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public interface IClientResponseHandlerHandlesBinary<T> extends IClientResponseHandler<T> {
/**
* If this method returns true, {@link #invokeClient(String, InputStream, int, Map)} should be invoked instead of {@link #invokeClient(String, Reader, int, Map)}
*/
boolean isBinary();
T invokeClient(String theResponseMimeType, InputStream theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException;
}

View File

@ -0,0 +1,25 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class MethodUtil {
}

View File

@ -20,27 +20,34 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
public class ReadMethodBinding extends BaseResourceReturningMethodBinding implements IClientResponseHandlerHandlesBinary<Object> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadMethodBinding.class);
private Integer myIdIndex;
@ -110,7 +117,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
theMethodParams[myIdIndex] = theRequest.getId();
if (myVersionIdIndex != null) {
theMethodParams[myVersionIdIndex] = theRequest.getVersionId();
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getVersionId().getUnqualifiedVersionId());
}
Object response = invokeServerMethod(theMethodParams);
@ -119,8 +126,8 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public GetClientInvocation invokeClient(Object[] theArgs) {
GetClientInvocation retVal;
public HttpGetClientInvocation invokeClient(Object[] theArgs) {
HttpGetClientInvocation retVal;
IdDt id = ((IdDt) theArgs[myIdIndex]);
if (myVersionIdIndex == null) {
String resourceName = getResourceName();
@ -139,12 +146,12 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
return retVal;
}
public static GetClientInvocation createVReadInvocation(IdDt theId, IdDt vid, String resourceName) {
return new GetClientInvocation(resourceName, theId.getValue(), Constants.URL_TOKEN_HISTORY, vid.getValue());
public static HttpGetClientInvocation createVReadInvocation(IdDt theId, IdDt vid, String resourceName) {
return new HttpGetClientInvocation(resourceName, theId.getUnqualifiedId(), Constants.URL_TOKEN_HISTORY, vid.getUnqualifiedId());
}
public static GetClientInvocation createReadInvocation(IdDt theId, String resourceName) {
return new GetClientInvocation(resourceName, theId.getValue());
public static HttpGetClientInvocation createReadInvocation(IdDt theId, String resourceName) {
return new HttpGetClientInvocation(resourceName, theId.getUnqualifiedId());
}
@Override
@ -157,4 +164,26 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
return null;
}
@Override
public boolean isBinary() {
return "Binary".equals(getResourceName());
}
@Override
public Object invokeClient(String theResponseMimeType, InputStream theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
byte[] contents = IOUtils.toByteArray(theResponseReader);
Binary resource = new Binary(theResponseMimeType, contents);
switch (getMethodReturnType()) {
case BUNDLE:
return Bundle.withSingleResource(resource);
case LIST_OF_RESOURCES:
return Collections.singletonList(resource);
case RESOURCE:
return resource;
}
throw new IllegalStateException(""+getMethodReturnType()); // should not happen
}
}

View File

@ -39,7 +39,6 @@ public class Request {
private String myCompleteUrl;
private String myFhirServerBase;
private IdDt myId;
private Reader myInputReader;
private String myOperation;
private Map<String, String[]> myParameters;
private RequestType myRequestType;
@ -62,10 +61,6 @@ public class Request {
return myId;
}
public Reader getInputReader() {
return myInputReader;
}
public String getOperation() {
return myOperation;
}
@ -111,10 +106,6 @@ public class Request {
myId = theId;
}
public void setInputReader(Reader theReader) {
myInputReader = theReader;
}
public void setOperation(String theOperation) {
myOperation = theOperation;
}

View File

@ -34,7 +34,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.server.Constants;
@ -76,7 +75,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
public HttpGetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
assert (myQueryName == null || ((theArgs != null ? theArgs.length : 0) == getParameters().size())) : "Wrong number of arguments: " + (theArgs != null ? theArgs.length : "null");
Map<String, List<String>> queryStringArgs = new LinkedHashMap<String, List<String>>();
@ -86,7 +85,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
String resourceName = getResourceName();
GetClientInvocation retVal = createSearchInvocation(resourceName, queryStringArgs);
HttpGetClientInvocation retVal = createSearchInvocation(resourceName, queryStringArgs);
if (theArgs != null) {
for (int idx = 0; idx < theArgs.length; idx++) {
@ -98,8 +97,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
return retVal;
}
public static GetClientInvocation createSearchInvocation(String theResourceName, Map<String, List<String>> theParameters) {
return new GetClientInvocation(theParameters, theResourceName);
public static HttpGetClientInvocation createSearchInvocation(String theResourceName, Map<String, List<String>> theParameters) {
return new HttpGetClientInvocation(theParameters, theResourceName);
}
@Override

View File

@ -0,0 +1,154 @@
package ca.uhn.fhir.rest.method;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.TransactionParameter;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class TransactionMethodBinding extends BaseResourceReturningMethodBinding {
private int myTransactionParamIndex;
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
super(null, theMethod, theConetxt, theProvider);
myTransactionParamIndex = -1;
int index=0;
for (IParameter next : getParameters()) {
if (next instanceof TransactionParameter) {
myTransactionParamIndex = index;
}
index++;
}
if (myTransactionParamIndex==-1) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with the @" + TransactionParam.class + " annotation");
}
}
@Override
public RestfulOperationSystemEnum getSystemOperationType() {
return RestfulOperationSystemEnum.TRANSACTION;
}
@Override
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
if (theRequest.getRequestType() != RequestType.POST) {
return false;
}
if (isNotBlank(theRequest.getOperation())) {
return false;
}
if (isNotBlank(theRequest.getResourceName())) {
return false;
}
return true;
}
@Override
public ReturnTypeEnum getReturnType() {
return ReturnTypeEnum.BUNDLE;
}
@SuppressWarnings("unchecked")
@Override
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
List<IResource> resources = (List<IResource>) theMethodParams[myTransactionParamIndex];
List<IdDt> oldIds= new ArrayList<IdDt>();
for (IResource next : resources) {
oldIds.add(next.getId());
}
List<IResource> retVal=(List<IResource>) invokeServerMethod(theMethodParams);
if (retVal.size() != resources.size()) {
throw new InternalErrorException("Transaction bundle contained " + resources.size() + " entries, but server method response contained " + retVal.size() + " entries (must be the same)");
}
for (int i =0; i < resources.size(); i++) {
IdDt oldId = oldIds.get(i);
IResource newRes = retVal.get(i);
if (newRes.getId() == null || newRes.getId().isEmpty()) {
throw new InternalErrorException("Transaction method returned resource at index " + i + " with no id specified - IResource#setId(IdDt)");
}
if (oldId != null && !oldId.isEmpty()) {
if (!oldId.getId().equals(newRes.getId())) {
newRes.getResourceMetadata().put(ResourceMetadataKeyEnum.PREVIOUS_ID, oldId.getId());
}
}
}
return retVal;
}
@Override
protected Object parseRequestObject(Request theRequest) throws IOException {
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
Bundle bundle = parser.parseBundle(theRequest.getServletRequest().getReader());
return bundle;
}
@Override
public RestfulOperationTypeEnum getResourceOperationType() {
return null;
}
@Override
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
@SuppressWarnings("unchecked")
List<IResource> resources = (List<IResource>) theArgs[myTransactionParamIndex];
FhirContext context = getContext();
return createTransactionInvocation(resources, context);
}
public static BaseHttpClientInvocation createTransactionInvocation(List<IResource> resources, FhirContext theContext) {
return new HttpPostClientInvocation(theContext, resources);
}
}

View File

@ -37,8 +37,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.PutClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -79,6 +78,13 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
* we allow it in the PUT URL as well..
*/
String locationHeader = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_LOCATION);
IdDt id = new IdDt(locationHeader);
if (isNotBlank(id.getResourceType())) {
if (!getResourceName().equals(id.getResourceType())) {
throw new InvalidRequestException("Attempting to update '"+getResourceName()+ "' but content-location header specifies different resource type '"+id.getResourceType()+"' - header value: " + locationHeader);
}
}
if (isNotBlank(locationHeader)) {
MethodOutcome mo = new MethodOutcome();
parseContentLocation(mo, getResourceName(), locationHeader);
@ -97,7 +103,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
}
@Override
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
if (idDt == null) {
throw new NullPointerException("ID can not be null");
@ -109,7 +115,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
}
FhirContext context = getContext();
PutClientInvocation retVal = createUpdateInvocation(theResource, idDt, versionIdDt, context);
HttpPutClientInvocation retVal = createUpdateInvocation(theResource, idDt, versionIdDt, context);
for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx);
@ -119,14 +125,14 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
return retVal;
}
public static PutClientInvocation createUpdateInvocation(IResource theResource, IdDt idDt, IdDt versionIdDt, FhirContext context) {
public static HttpPutClientInvocation createUpdateInvocation(IResource theResource, IdDt idDt, IdDt versionIdDt, FhirContext context) {
String id = idDt.getValue();
String resourceName = context.getResourceDefinition(theResource).getName();
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(resourceName);
urlExtension.append('/');
urlExtension.append(id);
PutClientInvocation retVal = new PutClientInvocation(context, theResource, urlExtension.toString());
HttpPutClientInvocation retVal = new HttpPutClientInvocation(context, theResource, urlExtension.toString());
if (versionIdDt != null) {
String versionId = versionIdDt.getValue();

View File

@ -30,8 +30,7 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.PostClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -65,7 +64,7 @@ public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWith
}
@Override
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
FhirContext context = getContext();
IdDt idDt=null;
@ -73,7 +72,7 @@ public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWith
idDt = (IdDt) theArgs[myIdParameterIndex];
}
PostClientInvocation retVal = createValidateInvocation(theResource, idDt, context);
HttpPostClientInvocation retVal = createValidateInvocation(theResource, idDt, context);
for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx);
@ -83,7 +82,7 @@ public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWith
return retVal;
}
public static PostClientInvocation createValidateInvocation(IResource theResource, IdDt theId, FhirContext theContext) {
public static HttpPostClientInvocation createValidateInvocation(IResource theResource, IdDt theId, FhirContext theContext) {
StringBuilder urlExtension = new StringBuilder();
urlExtension.append(theContext.getResourceDefinition(theResource).getName());
urlExtension.append('/');
@ -95,7 +94,7 @@ public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWith
urlExtension.append(id);
}
// TODO: is post correct here?
PostClientInvocation retVal = new PostClientInvocation(theContext, theResource, urlExtension.toString());
HttpPostClientInvocation retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
return retVal;
}

View File

@ -30,7 +30,7 @@ import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -55,7 +55,7 @@ public abstract class BaseQueryParameter implements IParameter {
public abstract SearchParamTypeEnum getParamType();
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
if (theSourceClientArgument == null) {
if (isRequired()) {
throw new NullPointerException("SearchParameter '" + getName() + "' is required and may not be null");

View File

@ -33,7 +33,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -44,7 +44,7 @@ public class CountParameter implements IParameter {
private Class<?> myType;
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
if (theSourceClientArgument != null) {
IntegerDt since = ParameterUtil.toInteger(theSourceClientArgument);
if (since.isEmpty() == false) {
@ -74,10 +74,10 @@ public class CountParameter implements IParameter {
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but can not be of collection type");
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Since.class.getName() + " but can not be of collection type");
}
if (!ParameterUtil.getBindableIntegerTypes().contains(theParameterType)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but type '" + theParameterType + "' is an invalid type, must be one of: " + ParameterUtil.getBindableInstantTypes());
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Since.class.getName() + " but type '" + theParameterType + "' is an invalid type, must be one of: " + ParameterUtil.getBindableIntegerTypes());
}
myType = theParameterType;
}

View File

@ -27,7 +27,7 @@ import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -35,7 +35,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public interface IParameter {
void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException;
void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException;
/**
* This <b>server method</b> method takes the data received by the server in an incoming request, and translates that data into a single argument for a server method invocation. Note that all

View File

@ -26,7 +26,7 @@ import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -34,7 +34,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class NullParameter implements IParameter {
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
//nothing
}

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.TagListParam;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam;
@ -225,6 +226,8 @@ public class ParameterUtil {
param = new CountParameter();
} else if (nextAnnotation instanceof Sort) {
param = new SortParameter();
} else if (nextAnnotation instanceof TransactionParam) {
param = new TransactionParameter();
} else {
continue;
}

View File

@ -27,7 +27,7 @@ import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -41,7 +41,7 @@ public class ResourceParameter implements IParameter {
}
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
// TODO Auto-generated method stub
}

View File

@ -26,7 +26,7 @@ import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -35,7 +35,7 @@ class ServerBaseParameter implements IParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerBaseParameter.class);
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
/*
* Does nothing, since we just ignore serverbase arguments
*/

View File

@ -26,7 +26,7 @@ import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -35,7 +35,7 @@ class ServletRequestParameter implements IParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class);
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
/*
* Does nothing, since we just ignore HttpServletRequest arguments
*/

View File

@ -26,7 +26,7 @@ import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -35,7 +35,7 @@ class ServletResponseParameter implements IParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
/*
* Does nothing, since we just ignore HttpServletResponse arguments
*/

View File

@ -33,7 +33,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -44,7 +44,7 @@ public class SinceParameter implements IParameter {
private Class<?> myType;
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
if (theSourceClientArgument != null) {
InstantDt since = ParameterUtil.toInstant(theSourceClientArgument);
if (since.isEmpty() == false) {

View File

@ -33,7 +33,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.client.BaseClientInvocation;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -42,7 +42,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class SortParameter implements IParameter {
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
SortSpec ss = (SortSpec) theSourceClientArgument;
while (ss != null) {
String name;

View File

@ -0,0 +1,78 @@
package ca.uhn.fhir.rest.param;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.Request;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class TransactionParameter implements IParameter {
public TransactionParameter() {
}
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
// TODO Auto-generated method stub
}
@Override
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
Bundle resource = (Bundle) theRequestContents;
ArrayList<IResource> retVal = new ArrayList<IResource>();
for (BundleEntry next : resource.getEntries()) {
if (next.getResource() != null) {
retVal.add(next.getResource());
}
}
return retVal;
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but can not be a collection of collections");
}
if (theInnerCollectionType.equals(List.class)==false) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()+">");
}
if (theParameterType.equals(IResource.class)==false) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()+">");
}
}
}

View File

@ -28,53 +28,56 @@ import java.util.Set;
public class Constants {
public static final String CHARSET_UTF_8 = "UTF-8";
public static final String CT_ATOM_XML = "application/atom+xml";
public static final String CT_FHIR_JSON = "application/json+fhir";
public static final String CT_FHIR_XML = "application/xml+fhir";
public static final String PARAM_FORMAT = "_format";
public static final String URL_TOKEN_HISTORY = "_history";
public static final String CT_ATOM_XML = "application/atom+xml";
public static final Set<String> FORMAT_VAL_XML;
public static final String CT_HTML = "text/html";
public static final String CT_JSON = "application/json";
public static final String CT_OCTET_STREAM = "application/octet-stream";
public static final String CT_TEXT = "text/plain";
public static final String CT_XML = "application/xml";
public static final String FORMAT_JSON = "json";
public static final Set<String> FORMAT_VAL_JSON;
public static final Map<String, EncodingEnum> FORMAT_VAL_TO_ENCODING;
public static final String CT_XML = "application/xml";
public static final String CT_JSON = "application/json";
public static final String CT_HTML = "text/html";
public static final String PARAM_NARRATIVE = "_narrative";
public static final String PARAM_HISTORY = "_history";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_QUERY = "_query";
public static final int STATUS_HTTP_201_CREATED = 201;
public static final String CT_TEXT = "text/plain";
public static final int STATUS_HTTP_200_OK = 200;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_400_BAD_REQUEST = 400;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final Set<String> FORMAT_VAL_XML;
public static final String FORMAT_XML = "xml";
public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_CATEGORY = "Category";
public static final String HEADER_CONTENT_LOCATION = "Content-Location";
public static final int STATUS_HTTP_204_NO_CONTENT = 204;
public static final String PARAM_COUNT = "_count";
public static final String PARAM_SINCE = "_since";
public static final String PARAM_SEARCH = "_search";
public static final String HEADER_LAST_MODIFIED = "Last-Modified";
public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase();
public static final String PARAM_VALIDATE = "_validate";
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
public static final String PARAM_COUNT = "_count";
public static final String PARAM_DELETE = "_delete";
public static final String PARAM_FORMAT = "_format";
public static final String PARAM_HISTORY = "_history";
public static final String PARAM_INCLUDE = "_include";
public static final String PARAM_NARRATIVE = "_narrative";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_PRETTY_VALUE_TRUE = "true";
public static final String PARAM_QUERY = "_query";
public static final String PARAM_SEARCH = "_search";
public static final String PARAM_SINCE = "_since";
public static final String PARAM_SORT = "_sort";
public static final String PARAM_SORT_ASC = "_sort:asc";
public static final String PARAM_SORT_DESC = "_sort:desc";
public static final String HEADER_CATEGORY = "Category";
public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
public static final String PARAM_TAGS = "_tags";
public static final String CHARSET_UTF_8 = "UTF-8";
public static final String PARAM_DELETE = "_delete";
public static final String FORMAT_XML = "xml";
public static final String FORMAT_JSON = "json";
public static final String PARAM_INCLUDE = "_include";
public static final String PARAM_VALIDATE = "_validate";
public static final String PARAMQUALIFIER_STRING_EXACT = ":exact";
public static final int STATUS_HTTP_200_OK = 200;
public static final int STATUS_HTTP_201_CREATED = 201;
public static final int STATUS_HTTP_204_NO_CONTENT = 204;
public static final int STATUS_HTTP_400_BAD_REQUEST = 400;
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final String URL_TOKEN_HISTORY = "_history";
public static final String HEADER_CONTENT_TYPE = "Content-Type";
static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -0,0 +1,16 @@
package ca.uhn.fhir.rest.server;
import java.util.List;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.InstantDt;
public interface IBundleProvider {
List<IResource> getResources(int theFromIndex, int theToIndex);
int size();
InstantDt getPublished();
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.server;
public interface IPagingProvider {
}

View File

@ -20,12 +20,14 @@ package ca.uhn.fhir.rest.server;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
@ -51,7 +53,6 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
import ca.uhn.fhir.util.VersionUtil;
@ -89,12 +90,10 @@ public class RestfulServer extends HttpServlet {
}
/**
* This method is called prior to sending a response to incoming requests.
* It is used to add custom headers.
* 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) {
@ -102,17 +101,15 @@ 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;
}
/**
* Provides the non-resource specific providers which implement method calls
* on this server
* Provides the non-resource specific providers which implement method calls on this server
*
* @see #getResourceProviders()
*/
@ -139,12 +136,11 @@ 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>
*/
public Object getServerConformanceProvider() {
@ -152,9 +148,8 @@ 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)
*/
@ -167,19 +162,17 @@ 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;
}
/**
* 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 {
@ -237,8 +230,7 @@ public class RestfulServer extends HttpServlet {
}
/**
* Sets the non-resource specific providers which implement method calls on
* this server.
* Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
@ -247,8 +239,7 @@ public class RestfulServer extends HttpServlet {
}
/**
* Sets the non-resource specific providers which implement method calls on
* this server.
* Sets the non-resource specific providers which implement method calls on this server.
*
* @see #setResourceProviders(Collection)
*/
@ -257,8 +248,7 @@ public class RestfulServer extends HttpServlet {
}
/**
* Sets the non-resource specific providers which implement method calls on
* this server
* Sets the non-resource specific providers which implement method calls on this server
*
* @see #setResourceProviders(Collection)
*/
@ -288,19 +278,16 @@ 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.
* 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
* 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) {
@ -311,9 +298,8 @@ 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)
*/
@ -322,18 +308,16 @@ 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;
@ -348,50 +332,63 @@ public class RestfulServer extends HttpServlet {
private void findResourceMethods(Object theProvider) throws Exception {
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
int count = 0;
Class<?> clazz = theProvider.getClass();
Class<?> supertype = clazz.getSuperclass();
if (!Object.class.equals(supertype)) {
findResourceMethods(theProvider, supertype);
while (!Object.class.equals(supertype)) {
count += findResourceMethods(theProvider, supertype);
supertype = supertype.getSuperclass();
}
findResourceMethods(theProvider, clazz);
count += findResourceMethods(theProvider, clazz);
if (count == 0) {
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
}
}
private void findResourceMethods(Object theProvider, Class<?> clazz) {
private int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
int count = 0;
for (Method m : clazz.getDeclaredMethods()) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
if (foundMethodBinding == null) {
continue;
}
count++;
if (!Modifier.isPublic(m.getModifiers())) {
ourLog.debug("Ignoring non-public method: {}",m);
throw new ConfigurationException("Method '" + m.getName() + "' is not public, FHIR RESTful methods must be public");
} else {
if (!Modifier.isStatic(m.getModifiers())) {
if (Modifier.isStatic(m.getModifiers())) {
throw new ConfigurationException("Method '" + m.getName() + "' is static, FHIR RESTful methods must not be static");
} else {
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
if (foundMethodBinding != null) {
String resourceName = foundMethodBinding.getResourceName();
ResourceBinding resourceBinding;
if (resourceName == null) {
resourceBinding = myNullResourceBinding;
} else {
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceName);
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToProvider.put(resourceName, resourceBinding);
}
}
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
String resourceName = foundMethodBinding.getResourceName();
ResourceBinding resourceBinding;
if (resourceName == null) {
resourceBinding = myNullResourceBinding;
} else {
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceName);
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToProvider.put(resourceName, resourceBinding);
}
}
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
}
}
}
return count;
}
private void findSystemMethods(Object theSystemProvider) {
@ -466,6 +463,13 @@ public class RestfulServer extends HttpServlet {
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
}
private static String getBaseUrl(HttpServletRequest request) {
if (("http".equals(request.getScheme()) && request.getServerPort() == 80) || ("https".equals(request.getScheme()) && request.getServerPort() == 443))
return request.getScheme() + "://" + request.getServerName() + request.getContextPath();
else
return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
}
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
try {
@ -475,15 +479,13 @@ public class RestfulServer extends HttpServlet {
String resourceName = null;
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
// String contextPath =
// StringUtils.defaultString(request.getContextPath());
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
if (theRequest.getServletContext() != null) {
servletContextPath = StringUtils.defaultIfBlank(theRequest.getServletContext().getContextPath(), servletPath);
} else {
servletContextPath = servletPath;
servletContextPath = StringUtils.defaultString(theRequest.getServletContext().getContextPath());
// } else {
// servletContextPath = servletPath;
}
if (ourLog.isTraceEnabled()) {
@ -493,25 +495,30 @@ public class RestfulServer extends HttpServlet {
ourLog.trace("Context Path: {}", servletContextPath);
}
servletPath = servletContextPath;
IdDt id = null;
IdDt versionId = null;
String operation = null;
String requestPath = requestFullPath.substring(servletPath.length());
String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
requestPath = requestPath.substring(1);
}
int contextIndex;
if (servletPath.length() == 0) {
contextIndex = requestUrl.indexOf(requestPath);
if (requestPath.length() == 0) {
contextIndex = requestUrl.length();
} else {
contextIndex = requestUrl.indexOf(requestPath);
}
} else {
contextIndex = requestUrl.indexOf(servletPath);
}
String fhirServerBase = requestUrl.substring(0, contextIndex + servletPath.length());
String fhirServerBase;
int length = contextIndex + servletPath.length();
fhirServerBase = requestUrl.substring(0, length);
if (fhirServerBase.endsWith("/")) {
fhirServerBase = fhirServerBase.substring(0, fhirServerBase.length() - 1);
}
@ -521,13 +528,12 @@ public class RestfulServer extends HttpServlet {
Map<String, String[]> params = new HashMap<String, String[]>(theRequest.getParameterMap());
StringTokenizer tok = new StringTokenizer(requestPath, "/");
if (!tok.hasMoreTokens()) {
throw new ResourceNotFoundException("No resource name specified");
}
resourceName = tok.nextToken();
if (resourceName.startsWith("_")) {
operation = resourceName;
resourceName = null;
if (tok.hasMoreTokens()) {
resourceName = tok.nextToken();
if (resourceName.startsWith("_")) {
operation = resourceName;
resourceName = null;
}
}
ResourceBinding resourceBinding = null;
@ -557,7 +563,11 @@ public class RestfulServer extends HttpServlet {
if (nextString.equals(Constants.PARAM_HISTORY)) {
if (tok.hasMoreTokens()) {
String versionString = tok.nextToken();
versionId = new IdDt(versionString);
if (id == null) {
throw new InvalidRequestException("Don't know how to handle request path: " + requestPath);
}
id = new IdDt(resourceName + "/" + id.getUnqualifiedId() + "/_history/" + versionString);
versionId = id;
} else {
operation = Constants.PARAM_HISTORY;
}
@ -570,27 +580,23 @@ public class RestfulServer extends HttpServlet {
}
// Secondary is for things like ..../_tags/_delete
String secondaryOperation=null;
String secondaryOperation = null;
while (tok.hasMoreTokens()) {
String nextString = tok.nextToken();
if (operation == null) {
operation = nextString;
}else if (secondaryOperation==null) {
secondaryOperation=nextString;
}else {
throw new InvalidRequestException("URL path has unexpected token '"+nextString + "' at the end: " + requestPath);
} else if (secondaryOperation == null) {
secondaryOperation = nextString;
} else {
throw new InvalidRequestException("URL path has unexpected token '" + nextString + "' at the end: " + requestPath);
}
}
if (theRequestType == RequestType.PUT && versionId == null) {
String contentLocation = theRequest.getHeader("Content-Location");
if (contentLocation != null) {
int idx = contentLocation.indexOf("/_history/");
if (idx != -1) {
String versionIdString = contentLocation.substring(idx + "/_history/".length());
versionId = new IdDt(versionIdString);
}
versionId = new IdDt(contentLocation);
}
}
@ -604,11 +610,6 @@ public class RestfulServer extends HttpServlet {
r.setSecondaryOperation(secondaryOperation);
r.setParameters(params);
r.setRequestType(theRequestType);
if ("application/x-www-form-urlencoded".equals(theRequest.getContentType())) {
r.setInputReader(new StringReader(""));
} else {
r.setInputReader(theRequest.getReader());
}
r.setFhirServerBase(fhirServerBase);
r.setCompleteUrl(completeUrl);
r.setServletRequest(theRequest);
@ -653,13 +654,100 @@ 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() {
// nothing by default
}
public static boolean prettyPrintResponse(Request theRequest) {
Map<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
boolean prettyPrint;
if (pretty != null && pretty.length > 0) {
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
prettyPrint = true;
} else {
prettyPrint = false;
}
} else {
prettyPrint = false;
Enumeration<String> acceptValues = theRequest.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
String nextAcceptHeaderValue = acceptValues.nextElement();
if (nextAcceptHeaderValue.contains("pretty=true")) {
prettyPrint = true;
}
}
}
}
return prettyPrint;
}
public static EncodingEnum determineResponseEncoding(Request theReq) {
String[] format = theReq.getParameters().remove(Constants.PARAM_FORMAT);
if (format != null) {
for (String nextFormat : format) {
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
if (retVal != null) {
return retVal;
}
}
}
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
String nextAcceptHeaderValue = acceptValues.nextElement();
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
for (String nextPart : nextAcceptHeaderValue.split(",")) {
int scIdx = nextPart.indexOf(';');
if (scIdx == 0) {
continue;
}
if (scIdx != -1) {
nextPart = nextPart.substring(0, scIdx);
}
nextPart = nextPart.trim();
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
if (retVal != null) {
return retVal;
}
}
}
}
}
return EncodingEnum.XML;
}
public static EncodingEnum determineRequestEncoding(Request theReq) {
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
String nextAcceptHeaderValue = acceptValues.nextElement();
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
for (String nextPart : nextAcceptHeaderValue.split(",")) {
int scIdx = nextPart.indexOf(';');
if (scIdx == 0) {
continue;
}
if (scIdx != -1) {
nextPart = nextPart.substring(0, scIdx);
}
nextPart = nextPart.trim();
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
if (retVal != null) {
return retVal;
}
}
}
}
}
return EncodingEnum.XML;
}
public enum NarrativeModeEnum {
NORMAL, ONLY, SUPPRESS;

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.server.provider;
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -79,7 +80,14 @@ public class ServerConformanceProvider {
Set<RestfulOperationSystemEnum> systemOps = new HashSet<RestfulOperationSystemEnum>();
for (ResourceBinding next : myRestfulServer.getResourceBindings()) {
List<ResourceBinding> bindings = new ArrayList<ResourceBinding>(myRestfulServer.getResourceBindings());
Collections.sort(bindings, new Comparator<ResourceBinding>() {
@Override
public int compare(ResourceBinding theArg0, ResourceBinding theArg1) {
return theArg0.getResourceName().compareToIgnoreCase(theArg1.getResourceName());
}});
for (ResourceBinding next : bindings) {
Set<RestfulOperationTypeEnum> resourceOps = new HashSet<RestfulOperationTypeEnum>();
RestResource resource = rest.addResource();

View File

@ -61,12 +61,12 @@ import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
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.annotation.Metadata;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public class RestfulServerTesterServlet extends HttpServlet {
@ -94,6 +94,8 @@ public class RestfulServerTesterServlet extends HttpServlet {
myStaticResources.put("shBrushPlain.js", "text/javascript");
myStaticResources.put("shCore.css", "text/css");
myStaticResources.put("shThemeDefault.css", "text/css");
myStaticResources.put("json2.js", "text/javascript");
myStaticResources.put("minify.json.js", "text/javascript");
myCtx = new FhirContext();
}
@ -154,6 +156,11 @@ public class RestfulServerTesterServlet extends HttpServlet {
ctx.setVariable("conf", conformance);
ctx.setVariable("base", myServerBase);
ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance));
addStandardVariables(ctx, theReq.getParameterMap());
theResp.setContentType("text/html");
theResp.setCharacterEncoding("UTF-8");
myTemplateEngine.process(theReq.getPathInfo(), ctx, theResp.getWriter());
} catch (Exception e) {
ourLog.error("Failed to respond", e);
@ -161,15 +168,29 @@ public class RestfulServerTesterServlet extends HttpServlet {
}
}
private void addStandardVariables(WebContext theCtx, Map<String, String[]> theParameterMap) {
addStandardVariable(theCtx, theParameterMap, "configEncoding");
addStandardVariable(theCtx, theParameterMap, "configPretty");
}
private void addStandardVariable(WebContext theCtx, Map<String, String[]> theParameterMap, String key) {
if (theParameterMap.containsKey(key) && theParameterMap.get(key).length > 0) {
theCtx.setVariable(key, theParameterMap.get(key)[0]);
}
}
@Override
protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
if (DEBUGMODE) {
myTemplateEngine.getCacheManager().clearAllCaches();
}
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
client.setKeepResponses(true);
boolean returnsResource;
long latency=0;
try {
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
client.setKeepResponses(true);
String method = theReq.getParameter("method");
String prettyParam = theReq.getParameter("configPretty");
@ -182,172 +203,138 @@ public class RestfulServerTesterServlet extends HttpServlet {
client.setEncoding(EncodingEnum.JSON);
}
String requestUrl;
String action;
String resultStatus;
String resultBody;
String resultSyntaxHighlighterClass;
boolean returnsResource;
try {
if ("conformance".equals(method)) {
returnsResource = true;
client.conformance();
} else if ("read".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
returnsResource = true;
client.read(def.getImplementingClass(), new IdDt(id));
} else if ("vread".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
String versionId = StringUtils.defaultString(theReq.getParameter("versionid"));
if (StringUtils.isBlank(versionId)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No Version ID specified");
}
returnsResource = true;
client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId));
} else if ("delete".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
returnsResource = false;
client.delete(def.getImplementingClass(), new IdDt(id));
} else if ("history-instance".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
returnsResource = false;
client.history(def.getImplementingClass(), new IdDt(id));
} else if ("create".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
if (StringUtils.isBlank(resourceText)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
}
IResource resource;
if (client.getEncoding() == null || client.getEncoding() == EncodingEnum.XML) {
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
} else {
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
}
returnsResource = false;
client.create(resource);
} else if ("validate".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
if (StringUtils.isBlank(resourceText)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
}
IResource resource;
if (client.getEncoding() == null || client.getEncoding() == EncodingEnum.XML) {
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
} else {
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
}
returnsResource = false;
client.validate(resource);
} else if ("update".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
if (StringUtils.isBlank(resourceText)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
}
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
IResource resource;
if (client.getEncoding() == null || client.getEncoding() == EncodingEnum.XML) {
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
} else {
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
}
returnsResource = false;
client.update(new IdDt(id), resource);
} else if ("searchType".equals(method)) {
Map<String, List<IQueryParameterType>> params = new HashMap<String, List<IQueryParameterType>>();
HashSet<String> hashSet = new HashSet<String>(theReq.getParameterMap().keySet());
String paramName = null;
IQueryParameterType paramValue = null;
while (hashSet.isEmpty() == false) {
String nextKey = hashSet.iterator().next();
String nextValue = theReq.getParameter(nextKey);
paramName = null;
paramValue = null;
if (nextKey.startsWith("param.token.")) {
int prefixLength = "param.token.".length();
paramName = nextKey.substring(prefixLength + 2);
String systemKey = "param.token." + "1." + paramName;
String valueKey = "param.token." + "2." + paramName;
String system = theReq.getParameter(systemKey);
String value = theReq.getParameter(valueKey);
paramValue = new IdentifierDt(system, value);
hashSet.remove(systemKey);
hashSet.remove(valueKey);
} else if (nextKey.startsWith("param.string.")) {
paramName = nextKey.substring("param.string.".length());
paramValue = new StringDt(nextValue);
}
if (paramName != null) {
if (params.containsKey(paramName) == false) {
params.put(paramName, new ArrayList<IQueryParameterType>());
}
params.get(paramName).add(paramValue);
}
hashSet.remove(nextKey);
}
RuntimeResourceDefinition def = getResourceType(theReq);
returnsResource = false;
client.search(def.getImplementingClass(), params);
} else {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method);
return;
long start = System.currentTimeMillis();
if ("conformance".equals(method)) {
returnsResource = true;
client.conformance();
} else if ("read".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
} catch (BaseServerResponseException e) {
ourLog.error("Failed to invoke method", e);
returnsResource = true;
client.read(def.getImplementingClass(), new IdDt(id));
} else if ("vread".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
String versionId = StringUtils.defaultString(theReq.getParameter("versionid"));
if (StringUtils.isBlank(versionId)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No Version ID specified");
}
returnsResource = true;
client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId));
} else if ("delete".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
returnsResource = false;
client.delete(def.getImplementingClass(), new IdDt(id));
} else if ("history-instance".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
returnsResource = false;
client.history(def.getImplementingClass(), new IdDt(id));
} else if ("create".equals(method)) {
IResource resource = parseIncomingResource(theReq, theResp, client);
returnsResource = false;
client.create(resource);
} else if ("validate".equals(method)) {
IResource resource = parseIncomingResource(theReq, theResp, client);
returnsResource = false;
client.validate(resource);
} else if ("update".equals(method)) {
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
IResource resource = parseIncomingResource(theReq, theResp, client);
returnsResource = false;
client.update(new IdDt(id), resource);
} else if ("searchType".equals(method)) {
Map<String, List<IQueryParameterType>> params = new HashMap<String, List<IQueryParameterType>>();
HashSet<String> hashSet = new HashSet<String>(theReq.getParameterMap().keySet());
String paramName = null;
IQueryParameterType paramValue = null;
while (hashSet.isEmpty() == false) {
String nextKey = hashSet.iterator().next();
String nextValue = theReq.getParameter(nextKey);
paramName = null;
paramValue = null;
if (nextKey.startsWith("param.token.")) {
int prefixLength = "param.token.".length();
paramName = nextKey.substring(prefixLength + 2);
String systemKey = "param.token." + "1." + paramName;
String valueKey = "param.token." + "2." + paramName;
String system = theReq.getParameter(systemKey);
String value = theReq.getParameter(valueKey);
paramValue = new IdentifierDt(system, value);
hashSet.remove(systemKey);
hashSet.remove(valueKey);
} else if (nextKey.startsWith("param.string.")) {
paramName = nextKey.substring("param.string.".length());
paramValue = new StringDt(nextValue);
}
if (paramName != null) {
if (params.containsKey(paramName) == false) {
params.put(paramName, new ArrayList<IQueryParameterType>());
}
params.get(paramName).add(paramValue);
}
hashSet.remove(nextKey);
}
RuntimeResourceDefinition def = getResourceType(theReq);
returnsResource = false;
client.search(def.getImplementingClass(), params);
} else {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method);
return;
}
latency = System.currentTimeMillis() - start;
} catch (DataFormatException e) {
ourLog.error("Failed to invoke method", e);
returnsResource = false;
} catch (Exception e) {
ourLog.error("Failure during processing", e);
returnsResource = false;
}
try {
HttpRequestBase lastRequest = client.getLastRequest();
String requestBody = null;
String requestSyntaxHighlighterClass = null;
@ -377,13 +364,14 @@ public class RestfulServerTesterServlet extends HttpServlet {
}
}
}
requestUrl = lastRequest.getURI().toASCIIString();
action = client.getLastRequest().getMethod();
resultStatus = client.getLastResponse().getStatusLine().toString();
resultBody = client.getLastResponseBody();
String resultSyntaxHighlighterClass;
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
String action = client.getLastRequest() != null ? client.getLastRequest().getMethod() : null;
String resultStatus = client.getLastResponse() != null ? client.getLastResponse().getStatusLine().toString() : null;
String resultBody = client.getLastResponseBody();
HttpResponse lastResponse = client.getLastResponse();
ContentType ct = ContentType.get(lastResponse.getEntity());
ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null;
String mimeType = ct != null ? ct.getMimeType() : null;
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
String narrativeString = "";
@ -408,8 +396,8 @@ public class RestfulServerTesterServlet extends HttpServlet {
}
}
Header[] requestHeaders = applyHeaderFilters(lastRequest.getAllHeaders());
Header[] responseHeaders = applyHeaderFilters(lastResponse.getAllHeaders());
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
Header[] responseHeaders = lastResponse != null ? applyHeaderFilters(lastResponse.getAllHeaders()) : new Header[0];
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
ctx.setVariable("base", myServerBase);
@ -423,6 +411,7 @@ public class RestfulServerTesterServlet extends HttpServlet {
ctx.setVariable("requestHeaders", requestHeaders);
ctx.setVariable("responseHeaders", responseHeaders);
ctx.setVariable("narrative", narrativeString);
ctx.setVariable("latencyMs", latency);
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
} catch (Exception e) {
@ -431,6 +420,28 @@ public class RestfulServerTesterServlet extends HttpServlet {
}
}
private IResource parseIncomingResource(HttpServletRequest theReq, HttpServletResponse theResp, GenericClient theClient) throws ServletException, IOException {
RuntimeResourceDefinition def = getResourceType(theReq);
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
if (StringUtils.isBlank(resourceText)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
}
IResource resource;
if (theClient.getEncoding() == null) {
if (resourceText.trim().startsWith("{")) {
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
} else {
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
}
} else if (theClient.getEncoding() == EncodingEnum.XML) {
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
} else {
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
}
return resource;
}
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
return theAllHeaders;
@ -445,8 +456,8 @@ public class RestfulServerTesterServlet extends HttpServlet {
}
/**
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user. This can be used, for instance, to filter out "Authorization" headers. Note that
* names are not case sensitive.
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user.
* This can be used, for instance, to filter out "Authorization" headers. Note that names are not case sensitive.
*/
public void setFilterHeaders(String... theHeaderNames) {
myFilterHeaders = new HashSet<String>();

View File

@ -26,11 +26,16 @@ import java.util.List;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.primitive.StringDt;
public class FhirTerser {
@ -47,23 +52,22 @@ public class FhirTerser {
BaseRuntimeElementCompositeDefinition<?> currentDef = def;
List<String> parts = Arrays.asList(thePath.split("\\."));
List<String> subList = parts.subList(1, parts.size() );
if (subList.size()< 1) {
List<String> subList = parts.subList(1, parts.size());
if (subList.size() < 1) {
throw new ConfigurationException("Invalid path: " + thePath);
}
return getDefinition(currentDef, subList);
}
private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, List<String> theSubList) {
BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0));
if (theSubList.size() == 1) {
return nextDef;
} else {
BaseRuntimeElementCompositeDefinition<?> cmp=(BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0));
return getDefinition(cmp, theSubList.subList(1, theSubList.size() ));
BaseRuntimeElementCompositeDefinition<?> cmp = (BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0));
return getDefinition(cmp, theSubList.subList(1, theSubList.size()));
}
}
@ -74,8 +78,8 @@ public class FhirTerser {
Object currentObj = theResource;
List<String> parts = Arrays.asList(thePath.split("\\."));
List<String> subList = parts.subList(1, parts.size() );
if (subList.size()< 1) {
List<String> subList = parts.subList(1, parts.size());
if (subList.size() < 1) {
throw new ConfigurationException("Invalid path: " + thePath);
}
return getValues(currentDef, currentObj, subList);
@ -93,11 +97,94 @@ public class FhirTerser {
} else {
for (IElement nextElement : values) {
BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass());
List<?> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size() ));
List<?> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()));
retVal.addAll(foundValues);
}
}
return retVal;
}
/**
* Returns a list containing all child elements (including the resource itself) which are <b>non-empty</b>
* and are either of the exact type specified, or are a subclass of that type.
* <p>
* For example, specifying a type of {@link StringDt} would return all non-empty string instances within
* the message. Specifying a type of {@link IResource} would return the resource itself, as well as any contained resources.
* </p>
* @param theResourceT The resource instance to search. Must not be null.
* @param theType The type to search for. Must not be null.
* @return
*/
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(IResource theResource, Class<T> theType) {
ArrayList<T> retVal = new ArrayList<T>();
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource);
getAllChildElementsOfType(theResource, def, theType, retVal);
return retVal;
}
private <T extends IElement> void getAllChildElementsOfType(IElement theElement, BaseRuntimeElementDefinition<?> theDefinition, Class<T> theType, ArrayList<T> theList) {
if (theElement.isEmpty()) {
return;
}
addIfCorrectType(theElement, theType, theList);
addUndeclaredExtensions(theElement, theType, theList);
switch (theDefinition.getChildType()) {
case PRIMITIVE_XHTML:
case PRIMITIVE_DATATYPE:
case RESOURCE_REF:
// These are primitive types
break;
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE:
case RESOURCE: {
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition;
for (BaseRuntimeChildDefinition nextChild : childDef.getChildren()) {
List<? extends IElement> values = nextChild.getAccessor().getValues(theElement);
if (values != null) {
for (IElement nextValue : values) {
if (nextValue == null) {
continue;
}
BaseRuntimeElementDefinition<?> childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
getAllChildElementsOfType(nextValue, childElementDef, theType, theList);
}
}
}
break;
}
case CONTAINED_RESOURCES: {
ContainedDt value = (ContainedDt) theElement;
for (IResource next : value.getContainedResources()) {
BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(next);
getAllChildElementsOfType(next, def, theType, theList);
}
break;
}
case EXTENSION_DECLARED:
case UNDECL_EXT: {
throw new IllegalStateException("state should not happen: " + theDefinition.getChildType());
}
}
}
private <T extends IElement> void addUndeclaredExtensions(IElement theElement, Class<T> theType, ArrayList<T> theList) {
if (theElement instanceof ISupportsUndeclaredExtensions) {
ISupportsUndeclaredExtensions elem = (ISupportsUndeclaredExtensions) theElement;
for (ExtensionDt nextExt : elem.getUndeclaredExtensions()) {
addIfCorrectType(nextExt, theType, theList);
addIfCorrectType(nextExt.getValue(), theType, theList);
addUndeclaredExtensions(nextExt, theType, theList);
}
}
}
@SuppressWarnings("unchecked")
private <T extends IElement> void addIfCorrectType(IElement theElement, Class<T> theType, ArrayList<T> theList) {
if (theElement != null && theType.isAssignableFrom(theElement.getClass())) {
theList.add((T) theElement);
}
}
}

View File

@ -0,0 +1,247 @@
package ca.uhn.fhir.util;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
public class NonPrettyPrintWriterWrapper implements XMLStreamWriter {
private static final String PRE = "pre";
private XMLStreamWriter myTarget;
private int myInsidePre = 0;
public NonPrettyPrintWriterWrapper(XMLStreamWriter target) {
myTarget = target;
}
@Override
public void flush() throws XMLStreamException {
myTarget.flush();
}
@Override
public void close() throws XMLStreamException {
myTarget.close();
}
@Override
public String getPrefix(String theUri) throws XMLStreamException {
return myTarget.getPrefix(theUri);
}
@Override
public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
myTarget.setPrefix(thePrefix, theUri);
}
@Override
public void setDefaultNamespace(String theUri) throws XMLStreamException {
myTarget.setDefaultNamespace(theUri);
}
@Override
public void setNamespaceContext(NamespaceContext theContext) throws XMLStreamException {
myTarget.setNamespaceContext(theContext);
}
@Override
public NamespaceContext getNamespaceContext() {
return myTarget.getNamespaceContext();
}
@Override
public void writeStartElement(String theLocalName) throws XMLStreamException {
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
myTarget.writeStartElement(theLocalName);
}
@Override
public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
myTarget.writeStartElement(theNamespaceURI, theLocalName);
}
@Override
public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
}
@Override
public void writeEmptyElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
myTarget.writeEmptyElement(theNamespaceURI, theLocalName);
}
@Override
public void writeEmptyElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI);
}
@Override
public void writeEmptyElement(String theLocalName) throws XMLStreamException {
myTarget.writeEmptyElement(theLocalName);
}
@Override
public void writeEndElement() throws XMLStreamException {
if (myInsidePre > 0) {
myInsidePre--;
}
myTarget.writeEndElement();
}
@Override
public void writeEndDocument() throws XMLStreamException {
myTarget.writeEndDocument();
}
@Override
public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theLocalName, theValue);
}
@Override
public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeNamespace(String thePrefix, String theNamespaceURI) throws XMLStreamException {
myTarget.writeNamespace(thePrefix, theNamespaceURI);
}
@Override
public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
myTarget.writeDefaultNamespace(theNamespaceURI);
}
@Override
public void writeComment(String theData) throws XMLStreamException {
myTarget.writeComment(theData);
}
@Override
public void writeProcessingInstruction(String theTarget) throws XMLStreamException {
myTarget.writeProcessingInstruction(theTarget);
}
@Override
public void writeProcessingInstruction(String theTarget, String theData) throws XMLStreamException {
myTarget.writeProcessingInstruction(theTarget, theData);
}
@Override
public void writeCData(String theData) throws XMLStreamException {
myTarget.writeCData(theData);
}
@Override
public void writeDTD(String theDtd) throws XMLStreamException {
myTarget.writeDTD(theDtd);
}
@Override
public void writeEntityRef(String theName) throws XMLStreamException {
myTarget.writeEntityRef(theName);
}
@Override
public void writeStartDocument() throws XMLStreamException {
myTarget.writeStartDocument();
}
@Override
public void writeStartDocument(String theVersion) throws XMLStreamException {
myTarget.writeStartDocument(theVersion);
}
@Override
public void writeStartDocument(String theEncoding, String theVersion) throws XMLStreamException {
myTarget.writeStartDocument(theEncoding, theVersion);
}
@Override
public void writeCharacters(String theText) throws XMLStreamException {
if (myInsidePre > 0) {
myTarget.writeCharacters(theText);
} else {
writeCharacters(theText.toCharArray(), 0, theText.length());
}
}
@Override
public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
writeCharacters(theText, theStart, theLen, myTarget, myInsidePre);
}
static void writeCharacters(char[] theText, int theStart, int theLen, XMLStreamWriter target, int insidePre) throws XMLStreamException {
if (theLen == 0) {
return;
} else {
if (insidePre > 0) {
target.writeCharacters(theText, theStart, theLen);
} else {
int initialEnd = theStart + (theLen - 1);
int start = theStart;
int end = initialEnd;
while (Character.isWhitespace(theText[start]) && start < end) {
start++;
}
while (Character.isWhitespace(theText[end]) && end > start) {
end--;
}
if (start == end) {
if (Character.isWhitespace(theText[start])) {
target.writeCharacters(" ");
return;
}
}
if (start > theStart) {
target.writeCharacters(" ");
}
target.writeCharacters(theText, start, (end - start) + 1);
if (end < initialEnd) {
target.writeCharacters(" ");
}
}
}
}
@Override
public Object getProperty(String theName) throws IllegalArgumentException {
return myTarget.getProperty(theName);
}
}

View File

@ -31,39 +31,43 @@ import org.apache.commons.lang3.StringUtils;
public class PrettyPrintWriterWrapper implements XMLStreamWriter {
private XMLStreamWriter myTarget;
private static final String INDENT_CHAR = " ";
private static final String LINEFEED_CHAR = "\n";
private static final String PRE = "pre";
private int depth = 0;
private Map<Integer, Boolean> hasChildElement = new HashMap<Integer, Boolean>();
private static final String INDENT_CHAR = " ";
private static final String LINEFEED_CHAR = "\n";
private int myInsidePre = 0;
private XMLStreamWriter myTarget;
private boolean myFirstIndent=true;
public PrettyPrintWriterWrapper(XMLStreamWriter target) {
myTarget = target;
}
private String repeat(int d, String s) {
return StringUtils.repeat(s, d * 3);
}
@Override
public void flush() throws XMLStreamException {
myTarget.flush();
}
@Override
public void close() throws XMLStreamException {
myTarget.close();
}
@Override
public void flush() throws XMLStreamException {
myTarget.flush();
}
@Override
public NamespaceContext getNamespaceContext() {
return myTarget.getNamespaceContext();
}
@Override
public String getPrefix(String theUri) throws XMLStreamException {
return myTarget.getPrefix(theUri);
}
@Override
public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
myTarget.setPrefix(thePrefix, theUri);
public Object getProperty(String theName) throws IllegalArgumentException {
return myTarget.getProperty(theName);
}
@Override
@ -77,44 +81,63 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
}
@Override
public NamespaceContext getNamespaceContext() {
return myTarget.getNamespaceContext();
public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
myTarget.setPrefix(thePrefix, theUri);
}
@Override
public void writeStartElement(String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theLocalName);
public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theLocalName, theValue);
}
private void indentAndAdd() throws XMLStreamException {
indent();
@Override
public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
}
// update state of parent node
if (depth > 0) {
hasChildElement.put(depth - 1, true);
@Override
public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeCData(String theData) throws XMLStreamException {
myTarget.writeCData(theData);
}
@Override
public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
NonPrettyPrintWriterWrapper.writeCharacters(theText, theStart, theLen, myTarget, myInsidePre);
}
@Override
public void writeCharacters(String theText) throws XMLStreamException {
if (myInsidePre > 0) {
myTarget.writeCharacters(theText);
} else {
writeCharacters(theText.toCharArray(), 0, theText.length());
}
// reset state of current node
hasChildElement.put(depth, false);
depth++;
}
private void indent() throws XMLStreamException {
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
@Override
public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theNamespaceURI, theLocalName);
public void writeComment(String theData) throws XMLStreamException {
myTarget.writeComment(theData);
}
@Override
public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
myTarget.writeDefaultNamespace(theNamespaceURI);
}
@Override
public void writeDTD(String theDtd) throws XMLStreamException {
myTarget.writeDTD(theDtd);
}
@Override
public void writeEmptyElement(String theLocalName) throws XMLStreamException {
indent();
myTarget.writeEmptyElement(theLocalName);
}
@Override
@ -129,28 +152,6 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI);
}
@Override
public void writeEmptyElement(String theLocalName) throws XMLStreamException {
indent();
myTarget.writeEmptyElement(theLocalName);
}
@Override
public void writeEndElement() throws XMLStreamException {
decrementAndIndent();
myTarget.writeEndElement();
}
private void decrementAndIndent() throws XMLStreamException {
depth--;
if (hasChildElement.get(depth) == true) {
// indent for current depth
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
}
@Override
public void writeEndDocument() throws XMLStreamException {
decrementAndIndent();
@ -158,18 +159,19 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
}
@Override
public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theLocalName, theValue);
public void writeEndElement() throws XMLStreamException {
if (myInsidePre > 0) {
myInsidePre--;
}
decrementAndIndent();
myTarget.writeEndElement();
}
@Override
public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
public void writeEntityRef(String theName) throws XMLStreamException {
myTarget.writeEntityRef(theName);
}
@Override
@ -177,16 +179,6 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
myTarget.writeNamespace(thePrefix, theNamespaceURI);
}
@Override
public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
myTarget.writeDefaultNamespace(theNamespaceURI);
}
@Override
public void writeComment(String theData) throws XMLStreamException {
myTarget.writeComment(theData);
}
@Override
public void writeProcessingInstruction(String theTarget) throws XMLStreamException {
myTarget.writeProcessingInstruction(theTarget);
@ -197,49 +189,90 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
myTarget.writeProcessingInstruction(theTarget, theData);
}
@Override
public void writeCData(String theData) throws XMLStreamException {
myTarget.writeCData(theData);
}
@Override
public void writeDTD(String theDtd) throws XMLStreamException {
myTarget.writeDTD(theDtd);
}
@Override
public void writeEntityRef(String theName) throws XMLStreamException {
myTarget.writeEntityRef(theName);
}
@Override
public void writeStartDocument() throws XMLStreamException {
myFirstIndent=true;
myTarget.writeStartDocument();
}
@Override
public void writeStartDocument(String theVersion) throws XMLStreamException {
myFirstIndent=true;
myTarget.writeStartDocument(theVersion);
}
@Override
public void writeStartDocument(String theEncoding, String theVersion) throws XMLStreamException {
myFirstIndent=true;
myTarget.writeStartDocument(theEncoding, theVersion);
}
@Override
public void writeCharacters(String theText) throws XMLStreamException {
myTarget.writeCharacters(theText);
public void writeStartElement(String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theLocalName);
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
}
@Override
public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
myTarget.writeCharacters(theText, theStart, theLen);
public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theNamespaceURI, theLocalName);
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
}
@Override
public Object getProperty(String theName) throws IllegalArgumentException {
return myTarget.getProperty(theName);
public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
}
private void decrementAndIndent() throws XMLStreamException {
if (myInsidePre > 0) {
return;
}
depth--;
if (hasChildElement.get(depth) == true) {
// indent for current depth
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
}
private void indent() throws XMLStreamException {
if (myFirstIndent) {
myFirstIndent = false;
return;
}
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
private void indentAndAdd() throws XMLStreamException {
if (myInsidePre > 0) {
return;
}
indent();
// update state of parent node
if (depth > 0) {
hasChildElement.put(depth - 1, true);
}
// reset state of current node
hasChildElement.put(depth, false);
depth++;
}
private String repeat(int d, String s) {
return StringUtils.repeat(s, d * 3);
}
}

View File

@ -55,4 +55,6 @@ TD.testerNameCell {
.syntaxhighlighter {
font-size: 0.85em !important;
min-height: 1.4em;
max-width: 800px;
}

View File

@ -14,8 +14,11 @@ This file is a Thymeleaf template for the
<script type="text/javascript" src="jquery-2.1.0.min.js"></script>
<script type="text/javascript" src="PublicTester.js"></script>
<link rel="stylesheet" type="text/css" href="PublicTester.css"/>
<meta charset="UTF-8"/>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="minify.json.js"></script>
</head>
<body>
<body onload="selectResourceType();">
<table border="0" width="100%">
<tr>
<td align="left"><img src="hapi_fhir_banner.png"/></td>
@ -53,17 +56,30 @@ This file is a Thymeleaf template for the
<tr>
<td class="propertyKeyCell">Encoding</td>
<td>
<select id="configEncoding">
<option value="" selected="true">(default)</option>
<option value="xml">XML</option>
<option value="json">JSON</option>
<select id="configEncoding" th:switch="${configEncoding}">
<th:block th:case="'xml'">
<option value="">(default)</option>
<option value="xml" selected="selected">XML</option>
<option value="json">JSON</option>
</th:block>
<th:block th:case="'json'">
<option value="">(default)</option>
<option value="xml">XML</option>
<option value="json" selected="selected">JSON</option>
</th:block>
<th:block th:case="*">
<option value="" selected="selected">(default)</option>
<option value="xml">XML</option>
<option value="json">JSON</option>
</th:block>
</select>
</td>
</tr>
<tr>
<td class="propertyKeyCell">Pretty Printing</td>
<td>
<input type="checkbox" id="configPretty"/>
<td th:switch="${configPretty}">
<input th:case="'false'" type="checkbox" id="configPretty"/>
<input th:case="*" type="checkbox" id="configPretty" checked="checked"/>
</td>
</tr>
</table>
@ -91,7 +107,24 @@ This file is a Thymeleaf template for the
</tr>
</table>
<th:block th:each="resource, resIterStat : ${rest.resource}" th:with="expandoId='resExpando'+${resIterStat.count}">
<div class="bodyHeaderBlock">
Resources
</div>
<table border="0" th:id="systemExpando" class="propertyTable">
<tr>
<td valign="top" class="propertyKeyCell">Select Resource:</td>
<td valign="top">
<select id="configResource" onchange="selectResourceType();">
<th:block th:each="resource, resIterStat : ${rest.resource}">
<option th:value="${resource.type.valueAsString}" th:text="${resource.type.valueAsString}"/>
</th:block>
</select>
</td>
</tr>
</table>
<div th:each="resource, resIterStat : ${rest.resource}" th:with="expandoId='resExpando'+${resIterStat.count}" th:id="'res' + ${resource.type.valueAsString}" class="resourceTypeContainer" style="display:none;">
<div class="bodyHeaderBlock">
Resource: <th:block th:text="${resource.type.valueAsString}"/>
</div>
@ -115,9 +148,8 @@ This file is a Thymeleaf template for the
</td>
</tr>
</table>
</th:block>
</div>
</th:block>
</td>
</tr>
</table>

View File

@ -81,6 +81,14 @@ function displayConformance(button, expandoTr) {
clearCurrentForm(postCompleteFunction);
}
function minifyTextarea(self) {
var value = value;
value = value.replace(/[\u00A0\u1680\u180e\u2000-\u2009\u200a\u200b\u202f\u205f\u3000]/g,' ');
//value = JSON.minify(value);
$('#textarea').val(value);
return addConfigElementsToForm(self);
}
/** Create a tester form for the 'read' method */
function displayCreate(button, expandoTr, resourceName) {
highlightSelectedLink(button);
@ -89,11 +97,11 @@ function displayCreate(button, expandoTr, resourceName) {
$('<tr class="testerNameRow" style="display: none;" />').append(
$('<td class="testerNameCell">Create</td>'),
$('<td />').append(
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "minifyTextarea(this);" }).append(
$('<input />', { name: 'method', value: 'create', type: 'hidden' }),
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
$('<div class="textareaWrapper">').append(
$('<textarea />', { name: 'resource', rows: 10, style: 'white-space: nowrap;' })
$('<textarea />', { id: 'textarea', name: 'resource', rows: 10, style: 'white-space: nowrap;' })
),
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })
@ -137,11 +145,11 @@ function displayUpdate(button, expandoTr, resourceName) {
$('<tr class="testerNameRow" style="display: none;" />').append(
$('<td class="testerNameCell">Update</td>'),
$('<td />').append(
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "minifyTextarea(this);" }).append(
$('<input />', { name: 'method', value: 'update', type: 'hidden' }),
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
$('<textarea />', { name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
$('<textarea />', { id: 'textarea', name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })
)
@ -282,10 +290,10 @@ function displayValidate(button, expandoTr, resourceName) {
$('<tr class="testerNameRow" style="display: none;" />').append(
$('<td class="testerNameCell">Validate</td>'),
$('<td />').append(
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "minifyTextarea(this);" }).append(
$('<input />', { name: 'method', value: 'validate', type: 'hidden' }),
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
$('<textarea />', { name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
$('<textarea />', { id: 'textarea', name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })
)
@ -333,6 +341,15 @@ function newUniqueId() {
return "uid" + uniqueIdSeed++;
}
function selectResourceType() {
$('.resourceTypeContainer').each(function() {
$(this).hide();
});
var cr = document.getElementById('configResource');
var selected = cr.options[cr.selectedIndex].value;
$('#res' + selected).show();
}
/** Show a newly created tester form */
function showNewForm() {
var time = 0;

View File

@ -28,7 +28,8 @@ This file is a Thymeleaf template for the
</tr>
</table>
<div class="bodyHeaderBlock">
Executed invocation against FHIR RESTful Server
Executed invocation against FHIR RESTful Server in
<th:block th:text="${latencyMs} + 'ms'"/>
</div>
<table border="0" width="100%" cellpadding="0" cellspacing="0" style="margin-top: 4px;">

View File

@ -0,0 +1,11 @@
/*!
http://www.JSON.org/json2.js
2009-09-29
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
*/
if(!this.JSON){this.JSON={}}(function(){function l(c){return c<10?'0'+c:c}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(c){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+l(this.getUTCMonth()+1)+'-'+l(this.getUTCDate())+'T'+l(this.getUTCHours())+':'+l(this.getUTCMinutes())+':'+l(this.getUTCSeconds())+'Z':null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(c){return this.valueOf()}}var o=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,p=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,h,m,r={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},j;function q(a){p.lastIndex=0;return p.test(a)?'"'+a.replace(p,function(c){var f=r[c];return typeof f==='string'?f:'\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function n(c,f){var a,e,d,i,k=h,g,b=f[c];if(b&&typeof b==='object'&&typeof b.toJSON==='function'){b=b.toJSON(c)}if(typeof j==='function'){b=j.call(f,c,b)}switch(typeof b){case'string':return q(b);case'number':return isFinite(b)?String(b):'null';case'boolean':case'null':return String(b);case'object':if(!b){return'null'}h+=m;g=[];if(Object.prototype.toString.apply(b)==='[object Array]'){i=b.length;for(a=0;a<i;a+=1){g[a]=n(a,b)||'null'}d=g.length===0?'[]':h?'[\n'+h+g.join(',\n'+h)+'\n'+k+']':'['+g.join(',')+']';h=k;return d}if(j&&typeof j==='object'){i=j.length;for(a=0;a<i;a+=1){e=j[a];if(typeof e==='string'){d=n(e,b);if(d){g.push(q(e)+(h?': ':':')+d)}}}}else{for(e in b){if(Object.hasOwnProperty.call(b,e)){d=n(e,b);if(d){g.push(q(e)+(h?': ':':')+d)}}}}d=g.length===0?'{}':h?'{\n'+h+g.join(',\n'+h)+'\n'+k+'}':'{'+g.join(',')+'}';h=k;return d}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(c,f,a){var e;h='';m='';if(typeof a==='number'){for(e=0;e<a;e+=1){m+=' '}}else if(typeof a==='string'){m=a}j=f;if(f&&typeof f!=='function'&&(typeof f!=='object'||typeof f.length!=='number')){throw new Error('JSON.stringify');}return n('',{'':c})}}if(typeof JSON.parse!=='function'){JSON.parse=function(i,k){var g;function b(c,f){var a,e,d=c[f];if(d&&typeof d==='object'){for(a in d){if(Object.hasOwnProperty.call(d,a)){e=b(d,a);if(e!==undefined){d[a]=e}else{delete d[a]}}}}return k.call(c,f,d)}o.lastIndex=0;if(o.test(i)){i=i.replace(o,function(c){return'\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(i.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){g=eval('('+i+')');return typeof k==='function'?b({'':g},''):g}throw new SyntaxError('JSON.parse');}}}());

View File

@ -0,0 +1,61 @@
/*! JSON.minify()
v0.1 (c) Kyle Simpson
MIT License
*/
(function(global){
if (typeof global.JSON == "undefined" || !global.JSON) {
global.JSON = {};
}
global.JSON.minify = function(json) {
var tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
in_string = false,
in_multiline_comment = false,
in_singleline_comment = false,
tmp, tmp2, new_str = [], ns = 0, from = 0, lc, rc
;
tokenizer.lastIndex = 0;
while (tmp = tokenizer.exec(json)) {
lc = RegExp.leftContext;
rc = RegExp.rightContext;
if (!in_multiline_comment && !in_singleline_comment) {
tmp2 = lc.substring(from);
if (!in_string) {
tmp2 = tmp2.replace(/(\n|\r|\s)*/g,"");
}
new_str[ns++] = tmp2;
}
from = tokenizer.lastIndex;
if (tmp[0] == "\"" && !in_multiline_comment && !in_singleline_comment) {
tmp2 = lc.match(/(\\)*$/);
if (!in_string || !tmp2 || (tmp2[0].length % 2) == 0) { // start of string with ", or unescaped " character found to end string
in_string = !in_string;
}
from--; // include " character in next catch
rc = json.substring(from);
}
else if (tmp[0] == "/*" && !in_string && !in_multiline_comment && !in_singleline_comment) {
in_multiline_comment = true;
}
else if (tmp[0] == "*/" && !in_string && in_multiline_comment && !in_singleline_comment) {
in_multiline_comment = false;
}
else if (tmp[0] == "//" && !in_string && !in_multiline_comment && !in_singleline_comment) {
in_singleline_comment = true;
}
else if ((tmp[0] == "\n" || tmp[0] == "\r") && !in_string && !in_multiline_comment && in_singleline_comment) {
in_singleline_comment = false;
}
else if (!in_multiline_comment && !in_singleline_comment && !(/\n|\r|\s/.test(tmp[0]))) {
new_str[ns++] = tmp[0];
}
}
new_str[ns++] = rc;
return new_str.join("");
};
})(this);

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.AddTags;
@ -50,6 +51,8 @@ import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
@ -586,7 +589,7 @@ public interface HistoryClient extends IBasicClient {
* also be included in any of the methods above.
*/
@History
Bundle getHistoryServerWithCriteria(@Since Date theDate, @Count int theCount);
Bundle getHistoryServerWithCriteria(@Since Date theDate, @Count Integer theCount);
}
//END SNIPPET: historyClient
@ -610,8 +613,8 @@ public Patient readPatient(@IdParam IdDt theId) {
// Create a TagList and place a complete list of the patient's tags inside
TagList tags = new TagList();
tags.addTag("Dog", "Canine Patient", "http://animals"); // TODO: more realistic example
tags.addTag("Friendly", "Friendly", "http://personality"); // TODO: more realistic example
tags.addTag("http://animals", "Dog", "Canine Patient"); // TODO: more realistic example
tags.addTag("http://personality", "Friendly", "Friendly"); // TODO: more realistic example
// The tags are then stored in the Patient resource instance
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags);
@ -653,8 +656,8 @@ newPatient.addName().addFamily("Jones").addGiven("Frank");
// Populate tags
TagList tags = new TagList();
tags.addTag("Dog", "Canine Patient", "http://animals"); // TODO: more realistic example
tags.addTag("Friendly", "Friendly", "http://personality"); // TODO: more realistic example
tags.addTag("http://animals", "Dog", "Canine Patient"); // TODO: more realistic example
tags.addTag("http://personality", "Friendly", "Friendly"); // TODO: more realistic example
newPatient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags);
// ...invoke the create method on the client...
@ -742,6 +745,42 @@ public class TagMethodProvider
}
//END SNIPPET: tagMethodProvider
//START SNIPPET: transaction
@Transaction
public List<IResource> transaction(@TransactionParam List<IResource> theResources) {
// theResources will contain a complete bundle of all resources to persist
// in a single transaction
for (IResource next : theResources) {
InstantDt deleted = (InstantDt) next.getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT);
if (deleted != null && deleted.isEmpty() == false) {
// delete this resource
} else {
// create or update this resource
}
}
/*
* According to the specification, a bundle must be returned. This bundle will contain
* all of the created/updated/deleted resources, including their new/updated identities.
*
* The returned list must be the exact same size as the list of resources
* passed in, and it is acceptable to return the same list instance that was
* passed in.
*/
List<IResource> retVal = theResources;
for (IResource next : theResources) {
/*
* Populate each returned resource with the new ID for that resource,
* including the new version if the server supports versioning.
*/
IdDt newId = new IdDt("Patient", "1", "2");
next.setId(newId);
}
return retVal;
}
//END SNIPPET: transaction
}

View File

@ -1090,9 +1090,31 @@
<section name="System Level - Transaction">
<p>
Not yet implemented - Get in touch if you would like to help!
The
<a href="http://hl7.org/implement/standards/fhir/http.html#transaction">transaction</a>
action is among the most challenging parts of the FHIR specification to implement. It allows the
user to submit a bundle containing a number of resources to be created/updated/deleted as a single
atomic transaction.
</p>
<p>
HAPI provides a skeleton for implementing this action, although most of the effort
will depend on the underlying implementation. The following example shows
how to define a <i>transaction</i> method.
</p>
<macro name="snippet">
<param name="id" value="transaction" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URL to invoke this method:
<br />
<code>POST http://fhir.example.com/</code><br/>
<i>(note that the content of this POST will be a bundle)</i>
</p>
<a name="system_search" />
</section>

View File

@ -4,10 +4,18 @@ import static org.junit.Assert.*;
import org.junit.Test;
import ca.uhn.fhir.model.dstu.resource.CarePlan;
import ca.uhn.fhir.parser.DataFormatException;
public class ModelScannerTest {
/** This failed at one point */
@SuppressWarnings("unused")
@Test
public void testCarePlan() throws DataFormatException {
new ModelScanner(CarePlan.class);
}
@Test
public void testScanExtensionTypes() throws DataFormatException {

View File

@ -190,6 +190,11 @@ public class ResourceWithExtensionsA extends BaseResource {
return myId;
}
@Override
public void setId(String theId) {
myId=new IdDt(theId);
}
}

View File

@ -1,100 +0,0 @@
package ca.uhn.fhir.model.dstu.composite;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
public class ResourceReferenceDtTest {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceReferenceDtTest.class);
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("http://foo/fhir/Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(null, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueMissingType2() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(null, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueRelative1() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("/Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
private Patient parseAndEncode(Patient patient) {
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
ourLog.info("\n" + encoded);
return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -0,0 +1,130 @@
package ca.uhn.fhir.model.primitive;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Patient;
public class IdDtTest {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class);
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("http://foo/fhir/Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueAbsoluteWithVersion() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("http://foo/fhir/Organization/123/_history/999");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
assertEquals("999", ref.getResourceId().getUnqualifiedVersionId());
}
@Test
public void testParseValueWithVersion() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("/123/_history/999");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals(null, ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
assertEquals("999", ref.getResourceId().getUnqualifiedVersionId());
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals(null, ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueMissingType2() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals(null, ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueRelative1() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("/Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
ResourceReferenceDt ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getResourceId().getResourceType());
assertEquals("123", ref.getResourceId().getUnqualifiedId());
}
private Patient parseAndEncode(Patient patient) {
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
ourLog.info("\n" + encoded);
return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -1,14 +1,17 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.sf.json.JSON;
@ -17,12 +20,15 @@ import net.sf.json.JSONSerializer;
import org.apache.commons.io.IOUtils;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Extension;
@ -33,10 +39,12 @@ import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
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.Query;
import ca.uhn.fhir.model.dstu.resource.Specimen;
import ca.uhn.fhir.model.dstu.resource.ValueSet;
import ca.uhn.fhir.model.dstu.resource.ValueSet.Define;
@ -44,12 +52,15 @@ import ca.uhn.fhir.model.dstu.resource.ValueSet.DefineConcept;
import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum;
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
public class JsonParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
private static FhirContext ourCtx;
@Test
public void testEncodingNullExtension() {
@ -60,6 +71,80 @@ public class JsonParserTest {
assertEquals("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#bar\"}]}", str);
}
@Test
public void testEncodeBinaryResource() {
Binary patient = new Binary();
patient.setContentType("foo");
patient.setContent(new byte[] {1,2,3,4});
String val = ourCtx.newJsonParser().encodeResourceToString(patient);
assertEquals("{\"resourceType\":\"Binary\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}",val);
}
@Test
public void testParseQuery() {
String msg = "{\n" +
" \"resourceType\": \"Query\",\n" +
" \"text\": {\n" +
" \"status\": \"generated\",\n" +
" \"div\": \"<div>[Put rendering here]</div>\"\n" +
" },\n" +
" \"identifier\": \"urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376\",\n" +
" \"parameter\": [\n" +
" {\n" +
" \"url\": \"http://hl7.org/fhir/query#_query\",\n" +
" \"valueString\": \"example\"\n" +
" }\n" +
" ]\n" +
"}";
Query query = ourCtx.newJsonParser().parseResource(Query.class, msg);
assertEquals("urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376", query.getIdentifier().getValueAsString());
assertEquals("http://hl7.org/fhir/query#_query", query.getParameterFirstRep().getUrlAsString());
assertEquals("example", query.getParameterFirstRep().getValueAsPrimitive().getValueAsString());
}
@Test
public void testEncodeQuery() {
Query q = new Query();
ExtensionDt parameter = q.addParameter();
parameter.setUrl("http://hl7.org/fhir/query#_query").setValue(new StringDt("example"));
String val = new FhirContext().newJsonParser().encodeResourceToString(q);
ourLog.info(val);
//@formatter:off
String expected =
"{" +
"\"resourceType\":\"Query\"," +
"\"parameter\":[" +
"{" +
"\"url\":\"http://hl7.org/fhir/query#_query\"," +
"\"valueString\":\"example\"" +
"}" +
"]" +
"}";
//@formatter:on
ourLog.info("Expect: {}", expected);
ourLog.info("Got : {}", val);
assertEquals(expected, val);
}
@Test
public void testParseBinaryResource() {
Binary val = ourCtx.newJsonParser().parseResource(Binary.class, "{\"resourceType\":\"Binary\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}");
assertEquals("foo", val.getContentType());
assertArrayEquals(new byte[] {1,2,3,4}, val.getContent());
}
@Test
public void testTagList() {
@ -146,8 +231,7 @@ public class JsonParserTest {
assertEquals("term", b.getEntries().get(0).getCategories().get(0).getTerm());
assertEquals("label", b.getEntries().get(0).getCategories().get(0).getLabel());
assertEquals("scheme", b.getEntries().get(0).getCategories().get(0).getScheme());
assertNotNull(b.getEntries().get(0).getResource());
assertEquals(Patient.class, b.getEntries().get(0).getResource().getClass());
assertNull(b.getEntries().get(0).getResource());
}
@ -156,11 +240,10 @@ public class JsonParserTest {
public void testEncodeContainedResources() throws IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/contained-diagnosticreport.xml"));
FhirContext ctx = new FhirContext(DiagnosticReport.class);
IParser p = ctx.newXmlParser();
IParser p = ourCtx.newXmlParser();
DiagnosticReport res = p.parseResource(DiagnosticReport.class, msg);
String encoded = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(res);
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(res);
ourLog.info(encoded);
}
@ -216,7 +299,7 @@ public class JsonParserTest {
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
patient.setFoo(new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -225,7 +308,7 @@ public class JsonParserTest {
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
ResourceReferenceDt ref = actual.getFoo();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getResourceId().getValue());
}
@ -252,7 +335,7 @@ public class JsonParserTest {
Patient patient = new Patient();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -263,7 +346,7 @@ public class JsonParserTest {
List<ExtensionDt> ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
assertEquals(1, ext.size());
ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getReference().getValue());
}
@ -292,7 +375,7 @@ public class JsonParserTest {
String str = p.encodeResourceToString(patient);
assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "123"));
patient.setManagingOrganization(new ResourceReferenceDt("Organization/123"));
str = p.encodeResourceToString(patient);
assertThat(str, StringContains.containsString("\"managingOrganization\":{\"resource\":\"Organization/123\"}"));
@ -444,11 +527,10 @@ public class JsonParserTest {
public void testParseBundle() throws DataFormatException, IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/atom-document-large.json"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newJsonParser();
IParser p = ourCtx.newJsonParser();
Bundle bundle = p.parseBundle(msg);
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle);
ourLog.info(encoded);
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/_search?_format=application/json+fhir&search-id=46d5f0e7-9240-4d4f-9f51-f8ac975c65&search-sort=_id", bundle
@ -472,7 +554,7 @@ public class JsonParserTest {
public void testParseFuroreMetadataWithExtraElements() throws IOException {
String msg = IOUtils.toString(JsonParserTest.class.getResourceAsStream("/furore-conformance.json"));
IParser p = new FhirContext(ValueSet.class).newJsonParser();
IParser p = ourCtx.newJsonParser();
Conformance conf = p.parseResource(Conformance.class, msg);
RestResource res = conf.getRestFirstRep().getResourceFirstRep();
assertEquals("_id", res.getSearchParam().get(1).getName().getValue());
@ -482,12 +564,11 @@ public class JsonParserTest {
public void testParseWithContained() throws DataFormatException, IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/diagnostic-report.json"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newJsonParser();
IParser p = ourCtx.newJsonParser();
// ourLog.info("Reading in message: {}", msg);
DiagnosticReport res = p.parseResource(DiagnosticReport.class, msg);
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
ourLog.info(encoded);
ResourceReferenceDt reference = res.getResult().get(1);
@ -496,14 +577,89 @@ public class JsonParserTest {
assertEquals("789-8", obs.getName().getCoding().get(0).getCode().getValue());
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
@Test
public void testParseBundleDeletedEntry() {
//@formatter:off
String bundleString =
"{" +
"\"resourceType\":\"Bundle\"," +
"\"totalResults\":\"1\"," +
"\"entry\":[" +
"{" +
"\"deleted\":\"2012-05-29T23:45:32+00:00\"," +
"\"id\":\"http://fhir.furore.com/fhir/Patient/1\"," +
"\"link\":[" +
"{" +
"\"rel\":\"self\"," +
"\"href\":\"http://fhir.furore.com/fhir/Patient/1/_history/2\"" +
"}" +
"]" +
"}" +
"]" +
"}";
//@formatter:on
Bundle bundle = ourCtx.newJsonParser().parseBundle(bundleString);
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("2012-05-29T23:45:32+00:00", entry.getDeletedAt().getValueAsString());
assertEquals("http://fhir.furore.com/fhir/Patient/1/_history/2", entry.getLinkSelf().getValue());
assertEquals("1", entry.getResource().getId().getUnqualifiedId());
assertEquals("2", entry.getResource().getId().getUnqualifiedVersionId());
assertEquals(new InstantDt("2012-05-29T23:45:32+00:00"), entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT));
// Now encode
ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(bundle));
String encoded = ourCtx.newJsonParser().encodeBundleToString(bundle);
assertEquals(bundleString,encoded);
}
@Test
public void testEncodeBundle() {
Bundle b= new Bundle();
Patient p1 = new Patient();
p1.addName().addFamily("Family1");
BundleEntry entry = b.addEntry();
entry.getId().setValue("1");
entry.setResource(p1);
Patient p2 = new Patient();
p2.addName().addFamily("Family2");
entry = b.addEntry();
entry.getId().setValue("2");
entry.setLinkAlternate(new StringDt("http://foo/bar"));
entry.setResource(p2);
BundleEntry deletedEntry = b.addEntry();
deletedEntry.setId(new IdDt("Patient/3"));
InstantDt nowDt = InstantDt.withCurrentTime();
deletedEntry.setDeleted(nowDt);
String bundleString = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(bundleString);
List<String> strings = new ArrayList<String>();
strings.addAll(Arrays.asList("\"id\":\"1\""));
strings.addAll(Arrays.asList("\"id\":\"2\"", "\"rel\":\"alternate\"", "\"href\":\"http://foo/bar\""));
strings.addAll(Arrays.asList("\"deleted\":\""+nowDt.getValueAsString()+"\"", "\"id\":\"Patient/3\""));
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
}
@Test
public void testSimpleBundleEncode() throws IOException {
FhirContext ctx = new FhirContext(Observation.class, Patient.class);
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/atom-document-large.xml"), Charset.forName("UTF-8"));
Bundle obs = ctx.newXmlParser().parseBundle(xmlString);
Bundle obs = ourCtx.newXmlParser().parseBundle(xmlString);
String encoded = ctx.newJsonParser().encodeBundleToString(obs);
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(obs);
ourLog.info(encoded);
}
@ -512,15 +668,14 @@ public class JsonParserTest {
public void testSimpleParse() throws DataFormatException, IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/example-patient-general.json"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newJsonParser();
IParser p = ourCtx.newJsonParser();
// ourLog.info("Reading in message: {}", msg);
Patient res = p.parseResource(Patient.class, msg);
assertEquals(2, res.getUndeclaredExtensions().size());
assertEquals(1, res.getUndeclaredModifierExtensions().size());
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
ourLog.info(encoded);
}
@ -528,17 +683,16 @@ public class JsonParserTest {
@Test
public void testSimpleResourceEncode() throws IOException {
FhirContext ctx = new FhirContext(Observation.class);
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.xml"), Charset.forName("UTF-8"));
Patient obs = ctx.newXmlParser().parseResource(Patient.class, xmlString);
Patient obs = ourCtx.newXmlParser().parseResource(Patient.class, xmlString);
List<ExtensionDt> undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
IParser jsonParser = ctx.newJsonParser();
IParser jsonParser = ourCtx.newJsonParser();
String encoded = jsonParser.encodeResourceToString(obs);
ourLog.info(encoded);
@ -556,9 +710,9 @@ public class JsonParserTest {
@Test
public void testSimpleResourceEncodeWithCustomType() throws IOException {
FhirContext ctx = new FhirContext(MyObservationWithExtensions.class);
FhirContext fhirCtx = new FhirContext(MyObservationWithExtensions.class);
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.xml"), Charset.forName("UTF-8"));
MyObservationWithExtensions obs = ctx.newXmlParser().parseResource(MyObservationWithExtensions.class, xmlString);
MyObservationWithExtensions obs = fhirCtx.newXmlParser().parseResource(MyObservationWithExtensions.class, xmlString);
assertEquals(0, obs.getAllUndeclaredExtensions().size());
assertEquals("aaaa", obs.getExtAtt().getContentType().getValue());
@ -569,9 +723,9 @@ public class JsonParserTest {
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
fhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
IParser jsonParser = ctx.newJsonParser();
IParser jsonParser = fhirCtx.newJsonParser();
String encoded = jsonParser.encodeResourceToString(obs);
ourLog.info(encoded);

View File

@ -1,10 +1,7 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -13,6 +10,8 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.IOUtils;
@ -20,6 +19,7 @@ import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXException;
@ -39,10 +39,12 @@ import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
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.Query;
import ca.uhn.fhir.model.dstu.resource.Specimen;
import ca.uhn.fhir.model.dstu.resource.ValueSet;
import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum;
@ -52,6 +54,7 @@ import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
@ -61,6 +64,7 @@ import ca.uhn.fhir.parser.JsonParserTest.MyPatientWithOneDeclaredExtension;
public class XmlParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class);
private static FhirContext ourCtx;
@Test
public void testEncodeBoundCode() {
@ -75,6 +79,67 @@ public class XmlParserTest {
}
@Test
public void testParseQuery() {
String msg = "<Query xmlns=\"http://hl7.org/fhir\">\n" +
" <text>\n" +
" <status value=\"generated\"/>\n" +
" <div xmlns=\"http://www.w3.org/1999/xhtml\">[Put rendering here]</div>\n" +
" </text>\n" +
"\n" +
" <!-- this is an extermely simple query - a request to execute the query 'example' on the\n" +
" responder -->\n" +
" <identifier value=\"urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376\"/>\n" +
" <parameter url=\"http://hl7.org/fhir/query#_query\">\n" +
" <valueString value=\"example\"/>\n" +
" </parameter>\n" +
"</Query>";
Query query = ourCtx.newXmlParser().parseResource(Query.class, msg);
assertEquals("urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376", query.getIdentifier().getValueAsString());
assertEquals("http://hl7.org/fhir/query#_query", query.getParameterFirstRep().getUrlAsString());
assertEquals("example", query.getParameterFirstRep().getValueAsPrimitive().getValueAsString());
}
@Test
public void testEncodeQuery() {
Query q = new Query();
ExtensionDt parameter = q.addParameter();
parameter.setUrl("http://foo").setValue(new StringDt("bar"));
String val = new FhirContext().newXmlParser().encodeResourceToString(q);
ourLog.info(val);
assertEquals("<Query xmlns=\"http://hl7.org/fhir\"><parameter url=\"http://foo\"><valueString value=\"bar\"/></parameter></Query>", val);
}
@Test
public void testEncodeBinaryResource() {
Binary patient = new Binary();
patient.setContentType("foo");
patient.setContent(new byte[] {1,2,3,4});
String val = ourCtx.newXmlParser().encodeResourceToString(patient);
assertEquals("<Binary xmlns=\"http://hl7.org/fhir\" contentType=\"foo\">AQIDBA==</Binary>", val);
}
@Test
public void testParseBinaryResource() {
Binary val = ourCtx.newXmlParser().parseResource(Binary.class, "<Binary xmlns=\"http://hl7.org/fhir\" contentType=\"foo\">AQIDBA==</Binary>");
assertEquals("foo", val.getContentType());
assertArrayEquals(new byte[] {1,2,3,4}, val.getContent());
}
@Test
public void testTagList() {
@ -86,7 +151,7 @@ public class XmlParserTest {
"</taglist>";
//@formatter:on
TagList tagList = new FhirContext().newXmlParser().parseTagList(tagListStr);
TagList tagList = ourCtx.newXmlParser().parseTagList(tagListStr);
assertEquals(3, tagList.size());
assertEquals("term0", tagList.get(0).getTerm());
assertEquals("label0", tagList.get(0).getLabel());
@ -141,7 +206,7 @@ public class XmlParserTest {
Patient patient = new Patient();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -152,7 +217,7 @@ public class XmlParserTest {
List<ExtensionDt> ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
assertEquals(1, ext.size());
ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getReference().getValue());
}
@ -162,7 +227,7 @@ public class XmlParserTest {
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
patient.addAddress().setUse(AddressUseEnum.HOME);
patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
patient.setFoo(new ResourceReferenceDt("Organization/123"));
String val = parser.encodeResourceToString(patient);
ourLog.info(val);
@ -171,7 +236,7 @@ public class XmlParserTest {
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
ResourceReferenceDt ref = actual.getFoo();
assertEquals("Organization/123", ref.getResourceUrl());
assertEquals("Organization/123", ref.getReference().getValue());
}
@ -245,11 +310,42 @@ public class XmlParserTest {
assertEquals("term", b.getEntries().get(0).getCategories().get(0).getTerm());
assertEquals("label", b.getEntries().get(0).getCategories().get(0).getLabel());
assertEquals("scheme", b.getEntries().get(0).getCategories().get(0).getScheme());
assertNotNull(b.getEntries().get(0).getResource());
assertEquals(Patient.class, b.getEntries().get(0).getResource().getClass());
assertNull(b.getEntries().get(0).getResource());
}
@Test
public void testEncodeBundle() {
Bundle b= new Bundle();
Patient p1 = new Patient();
p1.addName().addFamily("Family1");
BundleEntry entry = b.addEntry();
entry.getId().setValue("1");
entry.setResource(p1);
Patient p2 = new Patient();
p2.addName().addFamily("Family2");
entry = b.addEntry();
entry.getId().setValue("2");
entry.setLinkAlternate(new StringDt("http://foo/bar"));
entry.setResource(p2);
BundleEntry deletedEntry = b.addEntry();
deletedEntry.setId(new IdDt("Patient/3"));
deletedEntry.setDeleted(InstantDt.withCurrentTime());
String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(bundleString);
List<String> strings = new ArrayList<String>();
strings.addAll(Arrays.asList("<entry>", "<id>1</id>", "</entry>"));
strings.addAll(Arrays.asList("<entry>", "<id>2</id>", "<link rel=\"alternate\" href=\"http://foo/bar\"/>", "</entry>"));
strings.addAll(Arrays.asList("<at:deleted-entry", "ref=\"Patient/3", "/>"));
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
}
@Test
public void testEncodeContainedResources() {
@ -287,6 +383,49 @@ public class XmlParserTest {
}
}
@Test
public void testEncodePrettyPrint() throws DataFormatException {
Patient patient = new Patient();
patient.getText().getDiv().setValueAsString("<div>\n <i> hello <pre>\n LINE1\n LINE2</pre></i>\n\n\n\n</div>");
patient.addName().addFamily("Family").addGiven("Given");
//@formatter:off
String encoded = new FhirContext().newXmlParser().setPrettyPrint(false).encodeResourceToString(patient);
ourLog.info(encoded);
/*
* Note at least one space is placed where any whitespace was, as
* it is hard to tell what whitespace had no purpose
*/
String expected = "<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">"
+ " <i> hello "
+ "<pre>\n LINE1\n LINE2</pre>"
+ "</i> </div></text><name><family value=\"Family\"/><given value=\"Given\"/></name></Patient>";
assertEquals(expected, encoded);
encoded = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
ourLog.info(encoded);
expected = "<Patient xmlns=\"http://hl7.org/fhir\">\n"
+ " <text>\n"
+ " <div xmlns=\"http://www.w3.org/1999/xhtml\"> \n"
+ " <i> hello \n"
+ " <pre>\n LINE1\n LINE2</pre>\n"
+ " </i> \n"
+ " </div>\n"
+ " </text>\n"
+ " <name>\n"
+ " <family value=\"Family\"/>\n"
+ " <given value=\"Given\"/>\n"
+ " </name>\n"
+ "</Patient>";
//@formatter:on
// Whitespace should be preserved and not reformatted in narrative blocks
assertEquals(expected, encoded);
}
@Test
public void testEncodeResourceRef() throws DataFormatException {
@ -297,7 +436,7 @@ public class XmlParserTest {
String str = p.encodeResourceToString(patient);
assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "123"));
patient.setManagingOrganization(new ResourceReferenceDt("Organization/123"));
str = p.encodeResourceToString(patient);
assertThat(str, StringContains.containsString("<managingOrganization><reference value=\"Organization/123\"/></managingOrganization>"));
@ -346,14 +485,13 @@ public class XmlParserTest {
+ "</Patient>";
//@formatter:on
FhirContext ctx = new FhirContext(Patient.class);
Patient patient = ctx.newXmlParser().parseResource(Patient.class, msg);
Patient patient = ourCtx.newXmlParser().parseResource(Patient.class, msg);
assertEquals(NarrativeStatusEnum.GENERATED, patient.getText().getStatus().getValueAsEnum());
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div>", patient.getText().getDiv().getValueAsString());
assertEquals("PRP1660", patient.getIdentifier().get(0).getValue().getValueAsString());
String encoded = ctx.newXmlParser().encodeResourceToString(patient);
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
Diff d = new Diff(new StringReader(msg), new StringReader(encoded));
assertTrue(d.toString(), d.identical());
@ -362,8 +500,7 @@ public class XmlParserTest {
@Test
public void testLoadAndEncodeDeclaredExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException {
FhirContext ctx = new FhirContext(ResourceWithExtensionsA.class);
IParser p = new XmlParser(ctx);
IParser p = new FhirContext(ResourceWithExtensionsA.class).newXmlParser();
//@formatter:off
String msg = "<ResourceWithExtensionsA xmlns=\"http://hl7.org/fhir\">\n" +
@ -417,8 +554,7 @@ public class XmlParserTest {
@Test
public void testLoadAndEncodeUndeclaredExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException {
FhirContext ctx = new FhirContext(Patient.class);
IParser p = new XmlParser(ctx);
IParser p = ourCtx.newXmlParser();
//@formatter:off
String msg = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
@ -469,11 +605,29 @@ public class XmlParserTest {
assertTrue(d.toString(), d.identical());
}
@Test
public void testParseWithXmlHeader() throws ConfigurationException, DataFormatException {
IParser p = ourCtx.newXmlParser();
//@formatter:off
String msg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<Patient xmlns=\"http://hl7.org/fhir\">\n" +
" <identifier>\n" +
" <label value=\"IdentifierLabel\"/>\n" +
" </identifier>\n" +
"</Patient>";
//@formatter:on
Patient resource = (Patient) p.parseResource(msg);
assertEquals("IdentifierLabel", resource.getIdentifier().get(0).getLabel().getValue());
}
@Test
public void testLoadObservation() throws ConfigurationException, DataFormatException, IOException {
FhirContext ctx = new FhirContext(Observation.class);
IParser p = new XmlParser(ctx);
IParser p = ourCtx.newXmlParser();
String string = IOUtils.toString(XmlParserTest.class.getResourceAsStream("/observation-example-eeg.xml"));
IResource resource = p.parseResource(string);
@ -485,8 +639,7 @@ public class XmlParserTest {
@Test
public void testLoadPatient() throws ConfigurationException, DataFormatException, IOException {
FhirContext ctx = new FhirContext();
IParser p = new XmlParser(ctx);
IParser p = ourCtx.newXmlParser();
String string = IOUtils.toString(XmlParserTest.class.getResourceAsStream("/patient-example-dicom.xml"));
IResource resource = p.parseResource(string);
@ -507,8 +660,7 @@ public class XmlParserTest {
@Test
public void testLoadQuestionnaire() throws ConfigurationException, DataFormatException, IOException {
FhirContext ctx = new FhirContext();
IParser p = new XmlParser(ctx);
IParser p = ourCtx.newXmlParser();
String string = IOUtils.toString(XmlParserTest.class.getResourceAsStream("/questionnaire-example.xml"));
IResource resource = p.parseResource(string);
@ -526,12 +678,11 @@ public class XmlParserTest {
+ "</Patient>";
//@formatter:on
FhirContext ctx = new FhirContext(Patient.class, ca.uhn.fhir.testmodel.Patient.class);
Patient patient1 = ctx.newXmlParser().parseResource(Patient.class, msg);
String encoded1 = ctx.newXmlParser().encodeResourceToString(patient1);
Patient patient1 = ourCtx.newXmlParser().parseResource(Patient.class, msg);
String encoded1 = ourCtx.newXmlParser().encodeResourceToString(patient1);
ca.uhn.fhir.testmodel.Patient patient2 = ctx.newXmlParser().parseResource(ca.uhn.fhir.testmodel.Patient.class, msg);
String encoded2 = ctx.newXmlParser().encodeResourceToString(patient2);
ca.uhn.fhir.testmodel.Patient patient2 = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.testmodel.Patient.class, msg);
String encoded2 = ourCtx.newXmlParser().encodeResourceToString(patient2);
Diff d = new Diff(new StringReader(encoded1), new StringReader(encoded2));
assertTrue(d.toString(), d.identical());
@ -561,6 +712,7 @@ public class XmlParserTest {
assertThat(str, StringContains.containsString("<Patient xmlns=\"http://hl7.org/fhir\">"));
}
@SuppressWarnings("deprecation")
@Test
public void testParseBundle() {
@ -580,6 +732,7 @@ public class XmlParserTest {
" <title>Valueset &quot;256a5231-a2bb-49bd-9fea-f349d428b70d&quot; to support automated processing</title>\n" +
" <id>http://hl7.org/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d</id>\n" +
" <link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>\n" +
" <link href=\"http://hl7.org/foo\" rel=\"alternate\"/>\n" +
" <updated>2014-02-10T04:10:46.987-00:00</updated>\n" +
" <author>\n" +
" <name>HL7, Inc (FHIR Project)</name>\n" +
@ -634,6 +787,7 @@ public class XmlParserTest {
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("HL7, Inc (FHIR Project)", entry.getAuthorName().getValue());
assertEquals("http://hl7.org/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d", entry.getId().getValue());
assertEquals("http://hl7.org/foo", entry.getLinkAlternate().getValue());
assertEquals(1, entry.getCategories().size());
assertEquals("term", entry.getCategories().get(0).getTerm());
assertEquals("label", entry.getCategories().get(0).getLabel());
@ -649,28 +803,64 @@ public class XmlParserTest {
assertEquals("label", tl.get(0).getLabel());
assertEquals("http://foo", tl.get(0).getScheme());
assertEquals(new IdDt("256a5231-a2bb-49bd-9fea-f349d428b70d"), resource.getId());
assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getUnqualifiedId());
msg = msg.replace("<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>", "<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d/_history/12345\" rel=\"self\"/>");
entry = p.parseBundle(msg).getEntries().get(0);
resource = (ValueSet) entry.getResource();
assertEquals(new IdDt("256a5231-a2bb-49bd-9fea-f349d428b70d"), resource.getId());
assertEquals(new IdDt("12345"), resource.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID));
assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getUnqualifiedId());
assertEquals("12345", resource.getId().getUnqualifiedVersionId());
assertEquals("12345", ((IdDt)resource.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID)).getUnqualifiedVersionId());
}
@SuppressWarnings("deprecation")
@Test
public void testParseBundleDeletedEntry() {
//@formatter:off
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">" +
"<title>FHIR Core Valuesets</title>" +
"<id>http://hl7.org/fhir/profile/valuesets</id>" +
"<link rel=\"self\" href=\"http://hl7.org/implement/standards/fhir/valuesets.xml\"/>" +
"<updated>2014-02-10T04:11:24.435+00:00</updated>" +
"<at:deleted-entry xmlns:at=\"http://purl.org/atompub/tombstones/1.0\" ref=\"http://foo/Patient/1\" when=\"2013-02-10T04:11:24.435+00:00\">" +
"<link rel=\"self\" href=\"http://foo/Patient/1/_history/2\"/>" +
"</at:deleted-entry>" +
"</feed>";
//@formatter:on
IParser p = ourCtx.newXmlParser();
Bundle bundle = p.parseBundle(msg);
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("http://foo/Patient/1", entry.getId().getValue());
assertEquals("2013-02-10T04:11:24.435+00:00", entry.getDeletedAt().getValueAsString());
assertEquals("http://foo/Patient/1/_history/2", entry.getLinkSelf().getValue());
assertEquals("1", entry.getResource().getId().getUnqualifiedId());
assertEquals("2", entry.getResource().getId().getUnqualifiedVersionId());
assertEquals("2", ((IdDt)entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID)).getUnqualifiedVersionId());
assertEquals(new InstantDt("2013-02-10T04:11:24.435+00:00"), entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT));
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle));
String encoded = ourCtx.newXmlParser().encodeBundleToString(bundle);
assertEquals(msg,encoded);
}
@Test
public void testParseBundleLarge() throws IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/atom-document-large.xml"));
FhirContext ctx = new FhirContext(Patient.class);
IParser p = ctx.newXmlParser();
IParser p = ourCtx.newXmlParser();
Bundle bundle = p.parseBundle(msg);
assertEquals("http://spark.furore.com/fhir/_snapshot?id=327d6bb9-83b0-4929-aa91-6dd9c41e587b&start=0&_count=20", bundle.getLinkSelf().getValue());
assertEquals("Patient resource with id 3216379", bundle.getEntries().get(0).getTitle().getValue());
assertEquals("http://spark.furore.com/fhir/Patient/3216379", bundle.getEntries().get(0).getId().getValue());
assertEquals("3216379", bundle.getEntries().get(0).getResource().getId().getValue());
assertEquals("3216379", bundle.getEntries().get(0).getResource().getId().getUnqualifiedId());
}
@ -678,7 +868,7 @@ public class XmlParserTest {
public void testParseContainedResources() throws IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/contained-diagnosticreport.xml"));
IParser p = new FhirContext(DiagnosticReport.class).newXmlParser();
IParser p = ourCtx.newXmlParser();
DiagnosticReport bundle = p.parseResource(DiagnosticReport.class, msg);
ResourceReferenceDt result0 = bundle.getResult().get(0);
@ -708,6 +898,7 @@ public class XmlParserTest {
XMLUnit.setIgnoreAttributeOrder(true);
XMLUnit.setIgnoreComments(true);
XMLUnit.setIgnoreWhitespace(true);
ourCtx = new FhirContext();
}
@Test
@ -804,17 +995,16 @@ public class XmlParserTest {
@Test
public void testSimpleResourceEncode() throws IOException, SAXException {
FhirContext ctx = new FhirContext(Observation.class);
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"), Charset.forName("UTF-8"));
Patient obs = ctx.newJsonParser().parseResource(Patient.class, xmlString);
Patient obs = ourCtx.newJsonParser().parseResource(Patient.class, xmlString);
List<ExtensionDt> undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
IParser jsonParser = ctx.newXmlParser();
IParser jsonParser = ourCtx.newXmlParser();
String encoded = jsonParser.encodeResourceToString(obs);
ourLog.info(encoded);
@ -831,9 +1021,9 @@ public class XmlParserTest {
@Test
public void testSimpleResourceEncodeWithCustomType() throws IOException, SAXException {
FhirContext ctx = new FhirContext(MyObservationWithExtensions.class);
FhirContext fhirCtx = new FhirContext(MyObservationWithExtensions.class);
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"), Charset.forName("UTF-8"));
MyObservationWithExtensions obs = ctx.newJsonParser().parseResource(MyObservationWithExtensions.class, xmlString);
MyObservationWithExtensions obs = fhirCtx.newJsonParser().parseResource(MyObservationWithExtensions.class, xmlString);
assertEquals(0, obs.getAllUndeclaredExtensions().size());
assertEquals("aaaa", obs.getExtAtt().getContentType().getValue());
@ -844,9 +1034,9 @@ public class XmlParserTest {
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
fhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
IParser jsonParser = ctx.newXmlParser();
IParser jsonParser = fhirCtx.newXmlParser();
String encoded = jsonParser.encodeResourceToString(obs);
ourLog.info(encoded);

View File

@ -0,0 +1,115 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.server.Constants;
public class BinaryClientTest {
private FhirContext ctx;
private HttpClient httpClient;
private HttpResponse httpResponse;
// atom-document-large.xml
@Before
public void before() {
ctx = new FhirContext(Patient.class, Conformance.class);
httpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ctx.getRestfulClientFactory().setHttpClient(httpClient);
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testRead() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", "foo/bar"));
when(httpResponse.getEntity().getContent()).thenReturn(new ByteArrayInputStream(new byte[] {1,2,3,4}));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
Binary resp = client.read(new IdDt("http://foo/Patient/123"));
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Binary/123", get.getURI().toString());
assertEquals("foo/bar", resp.getContentType());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, resp.getContent());
}
@Test
public void testCreate() throws Exception {
Binary res = new Binary();
res.setContent(new byte[] { 1, 2, 3, 4 });
res.setContentType("text/plain");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML));
when(httpResponse.getEntity().getContent()).thenReturn(new ByteArrayInputStream(new byte[] {}));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
MethodOutcome resp = client.create(res);
assertEquals(HttpPost.class, capt.getValue().getClass());
HttpPost post = (HttpPost) capt.getValue();
assertEquals("http://foo/Binary", post.getURI().toString());
assertEquals("text/plain", post.getEntity().getContentType().getValue());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, IOUtils.toByteArray(post.getEntity().getContent()));
}
private String createBundle() {
return ctx.newXmlParser().encodeBundleToString(new Bundle());
}
private interface IClient extends IBasicClient {
@Read(type=Binary.class)
public Binary read(@IdParam IdDt theBinary);
@Create(type=Binary.class)
public MethodOutcome create(@ResourceParam Binary theBinary);
}
}

View File

@ -100,6 +100,7 @@ public class ClientIntegrationTest {
myAuthorizationHeader = theRequest.getHeader("authorization");
Patient retVal = new Patient();
retVal.setId("1");
retVal.addName().addFamily(theFooParam.getValue());
return Collections.singletonList(retVal);
}

View File

@ -1,8 +1,11 @@
package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.io.StringReader;
@ -104,7 +107,7 @@ public class ClientTest {
assertEquals(HttpPost.class, capt.getValue().getClass());
HttpPost post = (HttpPost) capt.getValue();
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
assertEquals("100", response.getId().getValue());
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue());
}
@ -151,7 +154,7 @@ public class ClientTest {
assertEquals(HttpPost.class, capt.getValue().getClass());
HttpPost post = (HttpPost) capt.getValue();
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
assertEquals("100", response.getId().getValue());
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue());
}
@ -179,7 +182,7 @@ public class ClientTest {
assertEquals(HttpPost.class, capt.getValue().getClass());
HttpPost post = (HttpPost) capt.getValue();
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
assertEquals("100", response.getId().getValue());
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue());
Header[] headers = post.getHeaders("Category");
@ -941,7 +944,7 @@ public class ClientTest {
HttpPut post = (HttpPut) capt.getValue();
assertThat(post.getURI().toASCIIString(), StringEndsWith.endsWith("/Patient/100"));
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
assertEquals("100", response.getId().getValue());
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue());
}
@ -1003,7 +1006,7 @@ public class ClientTest {
assertThat(post.getURI().toASCIIString(), StringEndsWith.endsWith("/Patient/100"));
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
assertThat(post.getFirstHeader("Content-Location").getValue(), StringEndsWith.endsWith("/Patient/100/_history/200"));
assertEquals("100", response.getId().getValue());
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue());
}
@ -1027,7 +1030,7 @@ public class ClientTest {
HttpPost post = (HttpPost) capt.getValue();
assertThat(post.getURI().toASCIIString(), StringEndsWith.endsWith("/Patient/_validate"));
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
assertEquals("100", response.getId().getValue());
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
assertEquals("200", response.getVersionId().getValue());
}

View File

@ -1,8 +1,7 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.StringReader;
import java.nio.charset.Charset;
@ -29,7 +28,6 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants;
public class ReferenceClientTest {

View File

@ -230,6 +230,28 @@ public class TagsClientTest {
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
TagList response = client.getAllTagsPatientId(new IdDt("Patient", "111", "222"));
assertEquals(tagList, response);
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient/111/_history/222/_tags", get.getURI().toString());
}
@Test
public void testGetAllTagsPatientIdVersionOld() throws Exception {
TagList tagList = new TagList();
tagList.add(new Tag("AAA", "BBB", "CCC"));
String ser = ctx.newXmlParser().encodeTagListToString(tagList);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
TagList response = client.getAllTagsPatientIdVersion(new IdDt("111"), new IdDt("222"));
assertEquals(tagList, response);

View File

@ -1,32 +0,0 @@
package ca.uhn.fhir.rest.client;
import java.io.IOException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
public class Tester {
public static final void main(String[] args) throws DataFormatException, IOException {
try {
FhirContext ctx = new FhirContext(Patient.class);
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://spark.furore.com/fhir/");
Patient patient = client.getPatientById(new IdDt("1"));
System.out.println(ctx.newXmlParser().encodeResourceToString(patient));
// Patient patient2 = client.findPatientByMrn(new IdentifierDt("http://orionhealth.com/mrn", "PRP1660"));
// System.out.println(ctx.newXmlParser().encodeResourceToString(patient2));
} catch (NonFhirResponseException e) {
e.printStackTrace();
System.out.println(e.getResponseBody());
}
}
}

View File

@ -0,0 +1,109 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.server.Constants;
public class TransactionClientTest {
private FhirContext ctx;
private HttpClient httpClient;
private HttpResponse httpResponse;
// atom-document-large.xml
@Before
public void before() {
ctx = new FhirContext(Patient.class, Conformance.class);
httpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ctx.getRestfulClientFactory().setHttpClient(httpClient);
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testSimpleTransaction() throws Exception {
Patient patient = new Patient();
patient.setId(new IdDt("Patient/testPersistWithSimpleLinkP01"));
patient.addIdentifier("urn:system", "testPersistWithSimpleLinkP01");
patient.addName().addFamily("Tester").addGiven("Joe");
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/testPersistWithSimpleLinkP01"));
List<IResource> resources = Arrays.asList((IResource)patient, obs);
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
client.searchWithParam(resources);
assertEquals(HttpPost.class, capt.getValue().getClass());
HttpPost post = (HttpPost) capt.getValue();
assertEquals("http://foo/", post.getURI().toString());
Bundle bundle = ctx.newXmlParser().parseBundle(new InputStreamReader(post.getEntity().getContent()));
ourLog.info(ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle));
assertEquals(2, bundle.size());
assertEquals("Patient/testPersistWithSimpleLinkP01", bundle.getEntries().get(0).getId().getValue());
assertEquals("Patient/testPersistWithSimpleLinkP01", bundle.getEntries().get(0).getLinkSelf().getValue());
assertEquals(null, bundle.getEntries().get(0).getLinkAlternate().getValue());
assertTrue(bundle.getEntries().get(1).getId().isEmpty());
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionClientTest.class);
private String createBundle() {
return ctx.newXmlParser().encodeBundleToString(new Bundle());
}
private interface IClient extends IBasicClient {
@Transaction
public List<IResource> searchWithParam(@TransactionParam List<IResource> theResources);
}
}

View File

@ -0,0 +1,185 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.qos.logback.core.util.ContentTypeUtil;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class BinaryTest {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = new FhirContext();
private static Binary ourLast;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BinaryTest.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLast=null;
}
@Test
public void testRead() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
HttpResponse status = ourClient.execute(httpGet);
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("foo", status.getFirstHeader("content-type").getValue());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, responseContent);
}
@Test
public void testCreate() throws Exception {
HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary");
http.setEntity(new ByteArrayEntity(new byte[] {1,2,3,4}, ContentType.create("foo/bar", "UTF-8")));
HttpResponse status = ourClient.execute(http);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals("foo/bar; charset=UTF-8", ourLast.getContentType());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, ourLast.getContent());
}
public void testCreateWrongType() throws Exception {
Binary res = new Binary();
res.setContent(new byte[] { 1, 2, 3, 4 });
res.setContentType("text/plain");
String stringContent = ourCtx.newJsonParser().encodeResourceToString(res);
HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary");
http.setEntity(new StringEntity(stringContent, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(http);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals("text/plain", ourLast.getContentType());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, ourLast.getContent());
}
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_ATOM_XML + "; charset=UTF-8", status.getFirstHeader("content-type").getValue());
ourLog.info(responseContent);
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
Binary bin = (Binary) bundle.getEntries().get(0).getResource();
assertEquals("text/plain", bin.getContentType());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
ResourceProvider patientProvider = new ResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class ResourceProvider implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Binary theBinary) {
ourLast = theBinary;
return new MethodOutcome(new IdDt("1"));
}
@Override
public Class<? extends IResource> getResourceType() {
return Binary.class;
}
@Read
public Binary read(@IdParam IdDt theId) {
Binary retVal = new Binary();
retVal.setId("1");
retVal.setContent(new byte[] { 1, 2, 3, 4 });
retVal.setContentType(theId.getUnqualifiedId());
return retVal;
}
@Search
public List<Binary> search() {
Binary retVal = new Binary();
retVal.setId("1");
retVal.setContent(new byte[] { 1, 2, 3, 4 });
retVal.setContentType("text/plain");
return Collections.singletonList(retVal);
}
}
}

Some files were not shown because too many files have changed in this diff Show More