Lots more JPA improvementsa

This commit is contained in:
jamesagnew 2014-05-28 16:07:53 -04:00
parent adde115c53
commit 2720e7d273
68 changed files with 1391 additions and 567 deletions

View File

@ -311,27 +311,6 @@
</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,6 +440,20 @@
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>DIST</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
@ -512,13 +505,30 @@
</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>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</profile>
</profiles>
</project>

View File

@ -24,6 +24,12 @@
<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;
@ -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);

View File

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

@ -20,12 +20,22 @@ 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 */{
@ -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();
@ -188,4 +206,90 @@ 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);
}
}
}
}

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;
@ -50,6 +52,18 @@ public class BundleEntry extends BaseBundle {
return retVal;
}
@Override
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);
}

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

@ -27,5 +27,4 @@ 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);
public IdDt getId();
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);
}

View File

@ -20,11 +20,18 @@ package ca.uhn.fhir.model.api;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
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
@ -34,7 +41,12 @@ public enum ResourceMetadataKeyEnum {
* Values for this key are of type <b>{@link InstantDt}</b>
* </p>
*/
DELETED_AT,
DELETED_AT {
@Override
public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
}
},
/**
* The value for this key represents a previous ID used to identify
@ -44,7 +56,12 @@ public enum ResourceMetadataKeyEnum {
* Values for this key are of type <b>{@link IdDt}</b>
* </p>
*/
PREVIOUS_ID,
PREVIOUS_ID {
@Override
public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
}
},
/**
* The value for this key is the bundle entry <b>Published</b> time. This is
@ -61,7 +78,13 @@ public enum ResourceMetadataKeyEnum {
*
* @see InstantDt
*/
PUBLISHED,
PUBLISHED {
@Override
public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED);
}
},
/**
* The value for this key is the list of tags associated with this resource
@ -71,7 +94,22 @@ 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());
}
},
/**
@ -85,7 +123,12 @@ public enum ResourceMetadataKeyEnum {
*
* @see InstantDt
*/
UPDATED,
UPDATED {
@Override
public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
}
},
/**
* The value for this key is the version ID of the resource object.
@ -96,6 +139,49 @@ public enum ResourceMetadataKeyEnum {
* @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getUnqualifiedVersionId()} method
*/
@Deprecated
VERSION_ID;
VERSION_ID {
@Override
public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
}
};
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;
}
}
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());
}
}

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

@ -38,6 +38,26 @@ public class Binary extends BaseResource implements IResource {
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 Collections.emptyList();

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

@ -263,6 +263,9 @@ 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;

View File

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

View File

@ -477,7 +477,12 @@ public class XmlParser extends BaseParser implements IParser {
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");

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;
@ -45,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;
@ -125,6 +128,74 @@ public abstract class BaseClient {
}
try {
ContentType ct = ContentType.get(response.getEntity());
String mimeType = ct != null ? ct.getMimeType() : null;
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (response.getAllHeaders() != null) {
for (Header next : response.getAllHeaders()) {
String name = next.getName().toLowerCase();
List<String> list = headers.get(name);
if (list == null) {
list = new ArrayList<String>();
headers.put(name, list);
}
list.add(next.getValue());
}
}
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
String body=null;
Reader reader=null;
try {
reader = createReaderFromResponse(response);
body = IOUtils.toString(reader);
} catch (Exception e) {
ourLog.debug("Failed to read input stream", e);
} finally {
IOUtils.closeQuietly(reader);
}
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
if (Constants.CT_TEXT.equals(mimeType)) {
message = message+": " + body;
}
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
if(body!=null) {
exception.setResponseBody(body);
}
throw exception;
}
if (binding instanceof IClientResponseHandlerHandlesBinary) {
IClientResponseHandlerHandlesBinary<T> handlesBinary = (IClientResponseHandlerHandlesBinary<T>) binding;
if (handlesBinary.isBinary()) {
InputStream reader = response.getEntity().getContent();
try {
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
byte[] responseBytes = IOUtils.toByteArray(reader);
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = null;
}
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
if (theLogRequestAndResponse) {
ourLog.info("Client response: {} - {} bytes", message, responseBytes.length);
}else {
ourLog.trace("Client response: {} - {} bytes", message, responseBytes.length);
}
reader = new ByteArrayInputStream(responseBytes);
}
return handlesBinary.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} finally {
IOUtils.closeQuietly(reader);
}
}
}
Reader reader = createReaderFromResponse(response);
@ -147,46 +218,6 @@ public abstract class BaseClient {
reader = new StringReader(responseString);
}
ContentType ct = ContentType.get(response.getEntity());
String mimeType = ct != null ? ct.getMimeType() : null;
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (response.getAllHeaders() != null) {
for (Header next : response.getAllHeaders()) {
String name = next.getName().toLowerCase();
List<String> list = headers.get(name);
if (list == null) {
list = new ArrayList<String>();
headers.put(name, list);
}
list.add(next.getValue());
}
}
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
String body=null;
try {
body = IOUtils.toString(reader);
} catch (Exception e) {
ourLog.debug("Failed to read input stream", e);
} finally {
IOUtils.closeQuietly(reader);
}
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
if (Constants.CT_TEXT.equals(mimeType)) {
message = message+": " + body;
}
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
if(body!=null) {
exception.setResponseBody(body);
}
throw exception;
}
try {
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} finally {

View File

@ -59,6 +59,7 @@ 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;
@ -216,6 +217,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
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) {
BaseHttpClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);

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.
*

View File

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

View File

@ -25,6 +25,8 @@ 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;
@ -32,6 +34,7 @@ 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;
@ -88,6 +91,14 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
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;
@ -111,11 +122,11 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
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

@ -191,11 +191,13 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
// getMethod().in
}
private IResource parseIncomingServerResource(Request theRequest) {
IResource resource;
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
/**
* @throws IOException
*/
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
EncodingEnum encoding = RestfulServer.determineRequestEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
resource = parser.parseResource(theRequest.getInputReader());
IResource resource = parser.parseResource(theRequest.getServletRequest().getReader());
return resource;
}

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,6 +32,9 @@ 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.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.param.IParameter;
@ -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
*/

View File

@ -38,6 +38,7 @@ 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;
@ -255,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()) {
@ -318,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());
}
@ -348,7 +355,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
/**
* Subclasses may override
*/
protected Object parseRequestObject(@SuppressWarnings("unused") Request theRequest) {
protected Object parseRequestObject(@SuppressWarnings("unused") Request theRequest) throws IOException {
return null;
}
@ -361,154 +368,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
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 = theContext.getResourceDefinition(next);
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
entry.getTitle().setValue(def.getName() + " " + next.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 = next.getId().getUnqualifiedId();
b.append(resId);
entry.getId().setValue(b.toString());
if (isNotBlank(next.getId().getUnqualifiedVersionId())) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(next.getId().getUnqualifiedVersionId());
} else {
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
if (versionId != null) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(versionId.getValue());
}
}
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);
}
InstantDt deleted = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.DELETED_AT);
if (deleted != null) {
entry.setDeleted(deleted);
}
IdDt previous = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.PREVIOUS_ID);
if (previous != null) {
entry.getLinkAlternate().setValue(previous.toQualifiedUrl(theServerBase, def.getName()));
}
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.addResource(next, theContext, theServerBase);
}
bundle.getTotalResults().setValue(theResult.size());
return bundle;
}
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());
}
private static 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());
}
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());
}
public enum MethodReturnTypeEnum {
BUNDLE, LIST_OF_RESOURCES, RESOURCE

View File

@ -155,7 +155,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
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);
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get(nextResource);
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))");
}

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.rest.method;
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;
@ -47,7 +47,7 @@ public class HttpPostClientInvocation extends BaseHttpClientInvocationWithConten
@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

@ -22,7 +22,7 @@ package ca.uhn.fhir.rest.method;
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;
@ -34,7 +34,7 @@ public class HttpPutClientInvocation extends BaseHttpClientInvocationWithContent
}
@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

@ -20,15 +20,22 @@ 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;
@ -36,10 +43,11 @@ 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;
@ -156,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

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@ -125,10 +126,10 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
}
@Override
protected Object parseRequestObject(Request theRequest) {
protected Object parseRequestObject(Request theRequest) throws IOException {
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
Bundle bundle = parser.parseBundle(theRequest.getInputReader());
Bundle bundle = parser.parseBundle(theRequest.getServletRequest().getReader());
return bundle;
}
@ -139,10 +140,15 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
@Override
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
@SuppressWarnings("unchecked")
List<IResource> resources = (List<IResource>) theArgs[myTransactionParamIndex];
FhirContext context = getContext();
return new HttpPostClientInvocation(context, resources);
return createTransactionInvocation(resources, context);
}
public static BaseHttpClientInvocation createTransactionInvocation(List<IResource> resources, FhirContext theContext) {
return new HttpPostClientInvocation(theContext, resources);
}
}

View File

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

View File

@ -77,6 +77,7 @@ public class Constants {
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

@ -606,11 +606,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);
@ -723,6 +718,32 @@ public class RestfulServer extends HttpServlet {
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

@ -176,6 +176,7 @@ public class RestfulServerTesterServlet extends HttpServlet {
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
client.setKeepResponses(true);
boolean returnsResource;
long latency=0;
try {
String method = theReq.getParameter("method");
@ -190,6 +191,7 @@ public class RestfulServerTesterServlet extends HttpServlet {
client.setEncoding(EncodingEnum.JSON);
}
long start = System.currentTimeMillis();
if ("conformance".equals(method)) {
returnsResource = true;
client.conformance();
@ -311,6 +313,7 @@ public class RestfulServerTesterServlet extends HttpServlet {
return;
}
latency = System.currentTimeMillis() - start;
} catch (DataFormatException e) {
ourLog.error("Failed to invoke method", e);
returnsResource = false;
@ -396,6 +399,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) {

View File

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

View File

@ -28,7 +28,7 @@ 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 ${latencyMs}ms
</div>
<table border="0" width="100%" cellpadding="0" cellspacing="0" style="margin-top: 4px;">

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

@ -44,6 +44,7 @@ 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;
@ -82,6 +83,58 @@ public class JsonParserTest {
}
@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() {

View File

@ -44,6 +44,7 @@ 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;
@ -78,6 +79,45 @@ 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() {

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

@ -11,6 +11,7 @@ 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;
@ -72,6 +73,18 @@ public class BinaryTest {
@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");
@ -150,6 +163,7 @@ public class BinaryTest {
@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;
@ -160,6 +174,7 @@ public class BinaryTest {
@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);

View File

@ -239,6 +239,7 @@ public class PlainProviderTest {
private static Patient createPatient() {
Patient patient = new Patient();
patient.setId("1");
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
@ -252,6 +253,7 @@ public class PlainProviderTest {
private static Organization createOrganization() {
Organization retVal = new Organization();
retVal.setId("1");
retVal.addIdentifier();
retVal.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
retVal.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));

View File

@ -144,6 +144,7 @@ public class ReferenceParameterTest {
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient p = new Patient();
p.setId("1");
p.addName().addFamily("0"+theParam.getValueAsQueryToken());
p.addName().addFamily("1"+defaultString(theParam.getResourceType()));
p.addName().addFamily("2"+defaultString(theParam.getChain()));

View File

@ -130,6 +130,27 @@ public class ResfulServerMethodTest {
}
@Test
public void testCreateJson() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("001");
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("Location").getValue());
}
@Test
public void testCreateWithUnprocessableEntity() throws Exception {
@ -1033,10 +1054,10 @@ public class ResfulServerMethodTest {
httpPut.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse results = ourClient.execute(httpPut);
assertEquals(400, results.getStatusLine().getStatusCode());
String responseContent = IOUtils.toString(results.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, results.getStatusLine().getStatusCode());
}
public void testUpdateWrongResourceType() throws Exception {
@ -1192,8 +1213,15 @@ public class ResfulServerMethodTest {
@Search()
public Collection<AdverseReaction> getAllResources() {
ArrayList<AdverseReaction> retVal = new ArrayList<AdverseReaction>();
retVal.add(new AdverseReaction());
retVal.add(new AdverseReaction());
AdverseReaction ar1 = new AdverseReaction();
ar1.setId("1");
retVal.add(ar1);
AdverseReaction ar2 = new AdverseReaction();
ar2.setId("2");
retVal.add(ar2);
return retVal;
}

View File

@ -134,6 +134,7 @@ public class ResfulServerSelfReferenceTest {
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
{
Patient patient = new Patient();
patient.setId("1");
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
@ -146,6 +147,7 @@ public class ResfulServerSelfReferenceTest {
}
{
Patient patient = new Patient();
patient.setId("2");
patient.getIdentifier().add(new IdentifierDt());
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));

View File

@ -80,6 +80,7 @@ public class ServerExtraParametersTest {
myServerBase = theServerBase;
Patient retVal = new Patient();
retVal.setId("1");
retVal.addName().addFamily(theFooParam.getValue());
return Collections.singletonList(retVal);
}

View File

@ -2,7 +2,9 @@ package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@ -30,6 +32,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
@ -150,6 +153,19 @@ public class ServerFeaturesTest {
}
@Test
public void testInternalErrorIfNoId() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/?_query=findPatientsWithNoIdSpecified");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML+ "; pretty=true");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(500, status.getStatusLine().getStatusCode());
assertThat(responseContent, StringContains.containsString("ID"));
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
@ -216,6 +232,13 @@ public class ServerFeaturesTest {
return getIdToPatient().get(theId.getValue());
}
@Search(queryName="findPatientsWithNoIdSpecified")
public List<Patient> findPatientsWithNoIdSpecified() {
Patient p = new Patient();
p.addIdentifier().setSystem("foo");
return Collections.singletonList(p);
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;

View File

@ -155,6 +155,7 @@ public class SortTest {
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient p = new Patient();
p.setId("1");
p.addName().addFamily().setValue(theName.getValue());
SortSpec sort = theSort;
while (sort != null) {

View File

@ -142,22 +142,27 @@ public class StringParameterTest {
ArrayList<Patient> retVal = new ArrayList<Patient>();
if (theParam.isExact() && theParam.getValue().equals("aaa")) {
retVal.add(new Patient());
Patient patient = new Patient();
patient.setId("1");
retVal.add(patient);
}
if (!theParam.isExact() && theParam.getValue().toLowerCase().equals("aaa")) {
retVal.add(new Patient());
Patient patient = new Patient();
patient.setId("2");
retVal.add(patient);
}
return retVal;
}
@Search
public List<Patient> findPatient(@RequiredParam(name = "plain") String theParam) {
ArrayList<Patient> retVal = new ArrayList<Patient>();
if (theParam.toLowerCase().equals("aaa")) {
retVal.add(new Patient());
Patient patient = new Patient();
patient.setId("1");
retVal.add(patient);
}
return retVal;

View File

@ -194,7 +194,7 @@ public abstract class BaseFhirDao {
valueOf = Long.valueOf(id);
} catch (Exception e) {
String resName = getContext().getResourceDefinition(type).getName();
throw new InvalidRequestException("Resource ID " + resName + "/" + id + " is invalid (must be numeric), specified in path: " + nextPath);
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPath + " (this is an invalid ID, must be numeric on this server)");
}
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
if (target == null) {

View File

@ -17,6 +17,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.FhirTerser;
public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
@ -66,7 +67,9 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
myEntityManager.flush();
}
idConversions.put(nextId, new IdDt(resourceName + '/' + entity.getId()));
IdDt newId = new IdDt(resourceName + '/' + entity.getId());
ourLog.info("Incoming ID[{}] has been assigned ID[{}]", nextId, newId);
idConversions.put(nextId, newId);
persistedResources.add(entity);
}

View File

@ -22,6 +22,7 @@ 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.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class FhirSystemDaoTest {
@ -92,6 +93,30 @@ public class FhirSystemDaoTest {
}
@Test
public void testPersistWithUnknownId() {
Observation obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/999998888888"));
try {
ourSystemDao.transaction(Arrays.asList((IResource) obs));
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Resource Patient/999998888888 not found, specified in path: Observation.subject"));
}
obs = new Observation();
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/1.2.3.4"));
try {
ourSystemDao.transaction(Arrays.asList((IResource) obs));
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Resource Patient/1.2.3.4 not found, specified in path: Observation.subject"));
}
}
@AfterClass
public static void afterClass() {
ourCtx.close();

View File

@ -6,6 +6,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-resources/tinder"/>
<classpathentry kind="src" path="target/generated-sources/tinder"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>

View File

@ -70,8 +70,6 @@
<configuration>
<packageBase>ca.uhn.test.jpasrv</packageBase>
<baseResourceNames>
<!-- <baseResourceName>account</baseResourceName> -->
<!-- <baseResourceName>activitydefinition-extensions</baseResourceName> -->
<baseResourceName>adversereaction</baseResourceName>
<baseResourceName>alert</baseResourceName>
<baseResourceName>allergyintolerance</baseResourceName>
@ -92,7 +90,6 @@
<baseResourceName>documentmanifest</baseResourceName>
<baseResourceName>documentreference</baseResourceName>
<baseResourceName>encounter</baseResourceName>
<!-- <baseResourceName>familyhistory-genetics-profile</baseResourceName> -->
<baseResourceName>familyhistory</baseResourceName>
<baseResourceName>geneexpression</baseResourceName>
<baseResourceName>geneticanalysis</baseResourceName>
@ -112,7 +109,6 @@
<baseResourceName>medicationstatement</baseResourceName>
<baseResourceName>messageheader</baseResourceName>
<baseResourceName>microarray</baseResourceName>
<!-- <baseResourceName>namespace</baseResourceName> -->
<baseResourceName>observation</baseResourceName>
<baseResourceName>operationoutcome</baseResourceName>
<baseResourceName>orderresponse</baseResourceName>
@ -120,34 +116,24 @@
<baseResourceName>organization</baseResourceName>
<baseResourceName>other</baseResourceName>
<baseResourceName>patient</baseResourceName>
<!--<baseResourceName>person</baseResourceName>-->
<baseResourceName>practitioner</baseResourceName>
<baseResourceName>procedure</baseResourceName>
<baseResourceName>profile</baseResourceName>
<!-- <baseResourceName>protocol</baseResourceName> -->
<!-- <baseResourceName>provenance-extensions</baseResourceName> -->
<baseResourceName>provenance</baseResourceName>
<baseResourceName>query</baseResourceName>
<!-- <baseResourceName>questionnaire-extensions</baseResourceName> -->
<baseResourceName>questionnaire</baseResourceName>
<baseResourceName>relatedperson</baseResourceName>
<baseResourceName>remittance</baseResourceName>
<!-- <baseResourceName>resource</baseResourceName> -->
<baseResourceName>securityevent</baseResourceName>
<!--<baseResourceName>sequence</baseResourceName>-->
<baseResourceName>sequencinganalysis</baseResourceName>
<baseResourceName>sequencinglab</baseResourceName>
<baseResourceName>slot</baseResourceName>
<baseResourceName>specimen</baseResourceName>
<baseResourceName>substance</baseResourceName>
<baseResourceName>supply</baseResourceName>
<!--<baseResourceName>template</baseResourceName>-->
<baseResourceName>test</baseResourceName>
<!-- <baseResourceName>test</baseResourceName> -->
<baseResourceName>user</baseResourceName>
<!-- <baseResourceName>valueset-extensions</baseResourceName> -->
<baseResourceName>valueset</baseResourceName>
<!--<baseResourceName>vcfmeta</baseResourceName>-->
<!--<baseResourceName>vcfvariant</baseResourceName>-->
</baseResourceNames>
<buildDatatypes>true</buildDatatypes>
</configuration>
@ -162,6 +148,7 @@
</configuration>
</plugin>
</plugins>
<finalName>hapi-fhir-jpaserver</finalName>
</build>
<packaging>war</packaging>

View File

@ -28,7 +28,7 @@ public class TestRestfulServer extends RestfulServer {
ourLog.info("Failed to create database: {}",e.getMessage());
}
myAppCtx = new ClassPathXmlApplicationContext("fhir-spring-uhnfhirtest-config.xml");
myAppCtx = new ClassPathXmlApplicationContext("fhir-spring-uhnfhirtest-config.xml", "hapi-jpaserver-springbeans.xml");
Collection<IResourceProvider> beans = myAppCtx.getBeansOfType(IResourceProvider.class).values();
for (IResourceProvider nextResourceProvider : beans) {

View File

@ -0,0 +1,14 @@
package ca.uhn.fhirtest;
import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
public class TesterServlet extends RestfulServerTesterServlet {
private static final long serialVersionUID = 1L;
public TesterServlet() {
String baseUrl = System.getProperty("fhir.baseurl");
setServerBase(baseUrl);
}
}

View File

@ -13,47 +13,7 @@
<context:annotation-config />
<context:mbean-server />
<bean id="myDiagnosticReportDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.DiagnosticReport"/>
</bean>
<bean id="myDiagnosticReportRp" class="ca.uhn.test.jpasrv.DiagnosticReportResourceProvider">
<property name="dao" ref="myDiagnosticReportDao"/>
</bean>
<bean id="myPatientDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Patient"/>
</bean>
<bean id="myPatientRp" class="ca.uhn.test.jpasrv.PatientResourceProvider">
<property name="dao" ref="myPatientDao"/>
</bean>
<bean id="myObservationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Observation"/>
</bean>
<bean id="myObservationRp" class="ca.uhn.test.jpasrv.ObservationResourceProvider">
<property name="dao" ref="myObservationDao"/>
</bean>
<bean id="myOrganizationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Organization"/>
</bean>
<bean id="myOrganizationRp" class="ca.uhn.test.jpasrv.OrganizationResourceProvider">
<property name="dao" ref="myOrganizationDao"/>
</bean>
<bean id="myLocationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Location"/>
</bean>
<bean id="myLocationRp" class="ca.uhn.test.jpasrv.LocationResourceProvider">
<property name="dao" ref="myLocationDao"/>
</bean>
<bean id="myQuestionnaireDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Questionnaire"/>
</bean>
<bean id="myQuestionnaireRp" class="ca.uhn.test.jpasrv.QuestionnaireResourceProvider">
<property name="dao" ref="myQuestionnaireDao"/>
</bean>
<!-- <import resource="classpath:hapi-jpaserver-springbeans.xml" /> -->
<bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao">
</bean>

View File

@ -9,9 +9,20 @@
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>testerServlet</servlet-name>
<servlet-class>ca.uhn.fhirtest.TesterServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fhirServlet</servlet-name>
<url-pattern>/base/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>testerServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.tinder;
import static org.apache.commons.lang.StringUtils.defaultString;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@ -8,8 +10,10 @@ import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.ParseException;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@ -21,19 +25,9 @@ import org.apache.maven.project.MavenProject;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.valueset.RestfulConformanceModeEnum;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.tinder.model.BaseRootType;
import ca.uhn.fhir.tinder.model.RestResourceTm;
import ca.uhn.fhir.tinder.model.SearchParameter;
import ca.uhn.fhir.tinder.parser.ProfileParser;
import ca.uhn.fhir.tinder.model.Extension;
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
import edu.emory.mathcs.backport.java.util.Collections;
@Mojo(name = "generate-jparest-server", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class TinderJpaRestServerMojo extends AbstractMojo {
@ -43,6 +37,12 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
@Parameter(required = true, defaultValue = "${project.build.directory}/generated-sources/tinder")
private File targetDirectory;
@Parameter(required = true, defaultValue = "${project.build.directory}/generated-resources/tinder")
private File targetResourceDirectory;
@Parameter(required = true, defaultValue = "hapi-jpaserver-springbeans.xml")
private String targetResourceSpringBeansFile;
@Parameter(required = true)
private String packageBase;
@ -61,7 +61,6 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
ResourceGeneratorUsingSpreadsheet gen = new ResourceGeneratorUsingSpreadsheet();
gen.setBaseResourceNames(baseResourceNames);
try {
gen.parse();
@ -79,8 +78,32 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
myProject.addCompileSourceRoot(directoryBase.getAbsolutePath());
}
try {
VelocityContext ctx = new VelocityContext();
ctx.put("resources", gen.getResources());
VelocityEngine v = new VelocityEngine();
v.setProperty("resource.loader", "cp");
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
v.setProperty("runtime.references.strict", Boolean.TRUE);
InputStream templateIs = ResourceGeneratorUsingSpreadsheet.class.getResourceAsStream("/vm/jpa_spring_beans.vm");
InputStreamReader templateReader = new InputStreamReader(templateIs);
targetResourceDirectory.mkdirs();
FileWriter w = new FileWriter(new File(targetResourceDirectory, targetResourceSpringBeansFile));
v.evaluate(ctx, w, "", templateReader);
w.close();
Resource resource = new Resource();
resource.setDirectory(targetResourceDirectory.getAbsolutePath());
resource.addInclude(targetResourceSpringBeansFile);
myProject.addResource(resource);
} catch (Exception e) {
throw new MojoFailureException("Failed to generate server", e);
}
}
private void write() throws IOException {
// File directoryBase;

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xmlns:util="http://www.springframework.org/schema/util"
default-autowire="no" default-lazy-init="false"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
"
>
#foreach ( $res in $resources )
<bean id="my${res.name}Dao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.${res.declaringClassNameComplete}"/>
</bean>
<bean id="my${res.name}Rp" class="ca.uhn.test.jpasrv.${res.declaringClassNameComplete}ResourceProvider">
<property name="dao" ref="my${res.name}Dao"/>
</bean>
#end
</beans>