Server starting to work

This commit is contained in:
jamesagnew 2014-03-04 16:41:18 -05:00
parent ce17e82f9d
commit 5828f49029
30 changed files with 1081 additions and 521 deletions

View File

@ -1,4 +1,4 @@
* Implement JSON parser and encoder
* Implement strategy for narrative generation
* Fix XML encoder to not encode empty elements

View File

@ -39,6 +39,10 @@ public class FhirContext {
return myClassToElementDefinition;
}
public RuntimeResourceDefinition getResourceDefinition(Class<? extends IResource> theResourceType) {
return (RuntimeResourceDefinition) myClassToElementDefinition.get(theResourceType);
}
public RuntimeResourceDefinition getResourceDefinition(IResource theResource) {
return (RuntimeResourceDefinition) myClassToElementDefinition.get(theResource.getClass());
}

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.context;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
@ -60,9 +60,17 @@ class ModelScanner {
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
ModelScanner(Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
ModelScanner(Class<? extends IResource> theResourceTypes) throws ConfigurationException {
Set<Class<? extends IElement>> singleton = new HashSet<>();
singleton.add(theResourceTypes);
init(singleton);
}
Set<Class<? extends IElement>> toScan = new HashSet<Class<? extends IElement>>(theResourceTypes);
ModelScanner(Collection<Class<? extends IResource>> theResourceTypes) throws ConfigurationException {
init(new HashSet<Class<? extends IElement>>(theResourceTypes));
}
private void init(Set<Class<? extends IElement>> toScan) {
toScan.add(NarrativeDt.class);
toScan.add(DateDt.class);
toScan.add(CodeDt.class);
@ -89,7 +97,6 @@ class ModelScanner {
myRuntimeChildUndeclaredExtensionDefinition.sealAndInitialize(myClassToElementDefinitions);
ourLog.info("Done scanning FHIR library, found {} model entries", myClassToElementDefinitions.size());
}
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {

View File

@ -1,77 +1,57 @@
package ca.uhn.fhir.model.dstu.composite;
import java.util.*;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.model.dstu.valueset.*;
import ca.uhn.fhir.model.dstu.resource.*;
import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.ICompositeDatatype;
import ca.uhn.fhir.model.api.ResourceReference;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ChildResource;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
/**
* HAPI/FHIR <b>Identifier</b> Datatype
* (An identifier intended for computation)
* HAPI/FHIR <b>Identifier</b> Datatype (An identifier intended for computation)
*
* <p>
* <b>Definition:</b>
* A technical identifier - identifies some entity uniquely and unambiguously
* <b>Definition:</b> A technical identifier - identifies some entity uniquely and unambiguously
* </p>
*
* <p>
* <b>Requirements:</b>
* Need to be able to identify things with confidence and be sure that the identification is not subject to misinterpretation
* <b>Requirements:</b> Need to be able to identify things with confidence and be sure that the identification is not subject to misinterpretation
* </p>
*/
@DatatypeDef(name="Identifier")
@DatatypeDef(name = "Identifier")
public class IdentifierDt extends BaseElement implements ICompositeDatatype {
@Child(name="use", type=CodeDt.class, order=0, min=0, max=1)
@Child(name = "use", type = CodeDt.class, order = 0, min = 0, max = 1)
private BoundCodeDt<IdentifierUseEnum> myUse;
@Child(name="label", type=StringDt.class, order=1, min=0, max=1)
@Child(name = "label", type = StringDt.class, order = 1, min = 0, max = 1)
private StringDt myLabel;
@Child(name="system", type=UriDt.class, order=2, min=0, max=1)
@Child(name = "system", type = UriDt.class, order = 2, min = 0, max = 1)
private UriDt mySystem;
@Child(name="value", type=StringDt.class, order=3, min=0, max=1)
@Child(name = "value", type = StringDt.class, order = 3, min = 0, max = 1)
private StringDt myValue;
@Child(name="period", type=PeriodDt.class, order=4, min=0, max=1)
@Child(name = "period", type = PeriodDt.class, order = 4, min = 0, max = 1)
private PeriodDt myPeriod;
@Child(name="assigner", order=5, min=0, max=1)
@ChildResource(types= {
Organization.class,
})
@Child(name = "assigner", order = 5, min = 0, max = 1)
@ChildResource(types = { Organization.class, })
private ResourceReference myAssigner;
/**
* Gets the value(s) for <b>use</b> (usual | official | temp | secondary (If known)
).
* creating it if it does
* not exist. Will not return <code>null</code>.
* Gets the value(s) for <b>use</b> (usual | official | temp | secondary (If known) ). creating it if it does not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* The purpose of this identifier
* </p>
* <p>
* <b>Definition:</b> The purpose of this identifier
* </p>
*/
public BoundCodeDt<IdentifierUseEnum> getUse() {
if (myUse == null) {
@ -81,41 +61,33 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype {
}
/**
* Sets the value(s) for <b>use</b> (usual | official | temp | secondary (If known)
)
* Sets the value(s) for <b>use</b> (usual | official | temp | secondary (If known) )
*
* <p>
* <b>Definition:</b>
* The purpose of this identifier
* </p>
* <p>
* <b>Definition:</b> The purpose of this identifier
* </p>
*/
public void setUse(BoundCodeDt<IdentifierUseEnum> theValue) {
myUse = theValue;
}
/**
* Sets the value(s) for <b>use</b> (usual | official | temp | secondary (If known)
)
* Sets the value(s) for <b>use</b> (usual | official | temp | secondary (If known) )
*
* <p>
* <b>Definition:</b>
* The purpose of this identifier
* </p>
* <p>
* <b>Definition:</b> The purpose of this identifier
* </p>
*/
public void setUse(IdentifierUseEnum theValue) {
getUse().setValueAsEnum(theValue);
}
/**
* Gets the value(s) for <b>label</b> (Description of identifier).
* creating it if it does
* not exist. Will not return <code>null</code>.
* Gets the value(s) for <b>label</b> (Description of identifier). creating it if it does not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* A text string for the identifier that can be displayed to a human so they can recognize the identifier
* </p>
* <p>
* <b>Definition:</b> A text string for the identifier that can be displayed to a human so they can recognize the identifier
* </p>
*/
public StringDt getLabel() {
if (myLabel == null) {
@ -127,36 +99,31 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype {
/**
* Sets the value(s) for <b>label</b> (Description of identifier)
*
* <p>
* <b>Definition:</b>
* A text string for the identifier that can be displayed to a human so they can recognize the identifier
* </p>
* <p>
* <b>Definition:</b> A text string for the identifier that can be displayed to a human so they can recognize the identifier
* </p>
*/
public void setLabel(StringDt theValue) {
myLabel = theValue;
}
/**
/**
* Sets the value(s) for <b>label</b> (Description of identifier)
*
* <p>
* <b>Definition:</b>
* A text string for the identifier that can be displayed to a human so they can recognize the identifier
* </p>
* <p>
* <b>Definition:</b> A text string for the identifier that can be displayed to a human so they can recognize the identifier
* </p>
*/
public void setLabel( String theString) {
public void setLabel(String theString) {
myLabel = new StringDt(theString);
}
/**
* Gets the value(s) for <b>system</b> (The namespace for the identifier).
* creating it if it does
* not exist. Will not return <code>null</code>.
* Gets the value(s) for <b>system</b> (The namespace for the identifier). creating it if it does not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* Establishes the namespace in which set of possible id values is unique.
* </p>
* <p>
* <b>Definition:</b> Establishes the namespace in which set of possible id values is unique.
* </p>
*/
public UriDt getSystem() {
if (mySystem == null) {
@ -168,25 +135,20 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype {
/**
* Sets the value(s) for <b>system</b> (The namespace for the identifier)
*
* <p>
* <b>Definition:</b>
* Establishes the namespace in which set of possible id values is unique.
* </p>
* <p>
* <b>Definition:</b> Establishes the namespace in which set of possible id values is unique.
* </p>
*/
public void setSystem(UriDt theValue) {
mySystem = theValue;
}
/**
* Gets the value(s) for <b>value</b> (The value that is unique).
* creating it if it does
* not exist. Will not return <code>null</code>.
* Gets the value(s) for <b>value</b> (The value that is unique). creating it if it does not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* The portion of the identifier typically displayed to the user and which is unique within the context of the system.
* </p>
* <p>
* <b>Definition:</b> The portion of the identifier typically displayed to the user and which is unique within the context of the system.
* </p>
*/
public StringDt getValue() {
if (myValue == null) {
@ -198,36 +160,31 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype {
/**
* Sets the value(s) for <b>value</b> (The value that is unique)
*
* <p>
* <b>Definition:</b>
* The portion of the identifier typically displayed to the user and which is unique within the context of the system.
* </p>
* <p>
* <b>Definition:</b> The portion of the identifier typically displayed to the user and which is unique within the context of the system.
* </p>
*/
public void setValue(StringDt theValue) {
myValue = theValue;
}
/**
/**
* Sets the value(s) for <b>value</b> (The value that is unique)
*
* <p>
* <b>Definition:</b>
* The portion of the identifier typically displayed to the user and which is unique within the context of the system.
* </p>
* <p>
* <b>Definition:</b> The portion of the identifier typically displayed to the user and which is unique within the context of the system.
* </p>
*/
public void setValue( String theString) {
public void setValue(String theString) {
myValue = new StringDt(theString);
}
/**
* Gets the value(s) for <b>period</b> (Time period when id is/was valid for use).
* creating it if it does
* not exist. Will not return <code>null</code>.
* Gets the value(s) for <b>period</b> (Time period when id is/was valid for use). creating it if it does not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* Time period during which identifier is/was valid for use
* </p>
* <p>
* <b>Definition:</b> Time period during which identifier is/was valid for use
* </p>
*/
public PeriodDt getPeriod() {
if (myPeriod == null) {
@ -239,25 +196,20 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype {
/**
* Sets the value(s) for <b>period</b> (Time period when id is/was valid for use)
*
* <p>
* <b>Definition:</b>
* Time period during which identifier is/was valid for use
* </p>
* <p>
* <b>Definition:</b> Time period during which identifier is/was valid for use
* </p>
*/
public void setPeriod(PeriodDt theValue) {
myPeriod = theValue;
}
/**
* Gets the value(s) for <b>assigner</b> (Organization that issued id (may be just text)).
* creating it if it does
* not exist. Will not return <code>null</code>.
* Gets the value(s) for <b>assigner</b> (Organization that issued id (may be just text)). creating it if it does not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* Organization that issued/manages the identifier
* </p>
* <p>
* <b>Definition:</b> Organization that issued/manages the identifier
* </p>
*/
public ResourceReference getAssigner() {
if (myAssigner == null) {
@ -269,16 +221,41 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype {
/**
* Sets the value(s) for <b>assigner</b> (Organization that issued id (may be just text))
*
* <p>
* <b>Definition:</b>
* Organization that issued/manages the identifier
* </p>
* <p>
* <b>Definition:</b> Organization that issued/manages the identifier
* </p>
*/
public void setAssigner(ResourceReference theValue) {
myAssigner = theValue;
}
/**
* Returns true if <code>this</code> identifier has the same {@link IdentifierDt#getValue() value} and {@link IdentifierDt#getSystem() system} (as compared by simple equals comparison). Does not
* compare other values (e.g. {@link IdentifierDt#getUse() use}) or any extensions.
*/
public boolean matchesSystemAndValue(IdentifierDt theIdentifier) {
if (theIdentifier == null) {
return false;
}
return getValue().equals(theIdentifier.getValue()) && getSystem().equals(theIdentifier.getSystem());
}
/**
* Sets the value of this <code>IdentifierDt</code> using the <b>token</b> format. This
* format is used in HTTP queries as a parameter format.
*
* @see See FHIR specification
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search Parameter Types</a>
* for information on the <b>token</b> format
*/
public void setValueAsQueryToken(String theParameter) {
int barIndex = theParameter.indexOf('|');
if (barIndex != -1) {
setSystem(new UriDt(theParameter.substring(0, barIndex)));
setValue(theParameter.substring(barIndex + 1));
} else {
setValue(theParameter);
}
}
}

View File

@ -1,5 +1,8 @@
package ca.uhn.fhir.model.primitive;
import java.util.Date;
import java.util.TimeZone;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
@ -17,4 +20,14 @@ public class InstantDt extends BaseDateTimeDt {
}
}
/**
* Sets the value of this instant to the current time (from the system clock)
* and the local/default timezone (as retrieved using {@link TimeZone#getDefault()}. This
* TimeZone is generally obtained from the underlying OS.
*/
public void setToCurrentTimeInLocalTimeZone() {
setValue(new Date());
setTimeZone(TimeZone.getDefault());
}
}

View File

@ -60,5 +60,30 @@ public class StringDt extends BaseElement implements IPrimitiveDatatype<String>
return myValue;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StringDt other = (StringDt) obj;
if (myValue == null) {
if (other.myValue != null)
return false;
} else if (!myValue.equals(other.myValue))
return false;
return true;
}
}

View File

@ -6,6 +6,7 @@ import java.net.URISyntaxException;
import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
@DatatypeDef(name = "uri")
@ -13,6 +14,21 @@ public class UriDt extends BaseElement implements IPrimitiveDatatype<URI> {
private URI myValue;
/**
* Create a new String
*/
public UriDt() {
// nothing
}
/**
* Create a new String
*/
@SimpleSetter
public UriDt(@SimpleSetter.Parameter(name="theUri") String theValue) {
setValueAsString(theValue);
}
public URI getValue() {
return myValue;
}
@ -43,4 +59,29 @@ public class UriDt extends BaseElement implements IPrimitiveDatatype<URI> {
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myValue == null) ? 0 : myValue.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UriDt other = (UriDt) obj;
if (myValue == null) {
if (other.myValue != null)
return false;
} else if (!myValue.equals(other.myValue))
return false;
return true;
}
}

View File

@ -292,6 +292,8 @@ class ParserState<T extends IElement> {
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {
push(new AtomEntryState(myInstance));
} else if (theLocalPart.equals("published")) {
push(new AtomPrimitiveState(myInstance.getPublished()));
} else if (theLocalPart.equals("title")) {
push(new AtomPrimitiveState(myInstance.getTitle()));
} else if ("id".equals(theLocalPart)) {

View File

@ -1,9 +1,11 @@
package ca.uhn.fhir.parser;
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
@ -71,11 +73,83 @@ public class XmlParser {
return retVal;
}
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
XMLStreamWriter eventWriter;
StringWriter stringWriter = new StringWriter();
private <T extends IElement> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) {
try {
eventWriter = myXmlOutputFactory.createXMLStreamWriter(stringWriter);
while (streamReader.hasNext()) {
XMLEvent nextEvent = streamReader.nextEvent();
try {
if (nextEvent.isStartElement()) {
StartElement elem = nextEvent.asStartElement();
String namespaceURI = elem.getName().getNamespaceURI();
if ("extension".equals(elem.getName().getLocalPart())) {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
if (urlAttr == null || isBlank(urlAttr.getValue())) {
throw new DataFormatException("Extension element has no 'url' attribute");
}
parserState.enteringNewElementExtension(elem, urlAttr.getValue());
} else {
String elementName = elem.getName().getLocalPart();
parserState.enteringNewElement(namespaceURI, elementName);
}
for (@SuppressWarnings("unchecked")
Iterator<Attribute> iter = elem.getAttributes(); iter.hasNext();) {
Attribute next = iter.next();
// if
// (next.getName().getLocalPart().equals("value")) {
parserState.attributeValue(next.getName().getLocalPart(), next.getValue());
// }
}
} else if (nextEvent.isAttribute()) {
Attribute elem = (Attribute) nextEvent;
String name = (elem.getName().getLocalPart());
parserState.attributeValue(name, elem.getValue());
} else if (nextEvent.isEndElement()) {
EndElement elem = nextEvent.asEndElement();
String name = elem.getName().getLocalPart();
String namespaceURI = elem.getName().getNamespaceURI();
// if (!FHIR_NS.equals(namespaceURI) &&
// !XHTML_NS.equals(namespaceURI)) {
// continue;
// }
parserState.endingElement(elem);
if (parserState.isComplete()) {
return parserState.getObject();
}
} else if (nextEvent.isCharacters()) {
parserState.string(nextEvent.asCharacters().getData());
}
parserState.xmlEvent(nextEvent);
} catch (DataFormatException e) {
throw new DataFormatException("DataFormatException at [" + nextEvent.getLocation().toString() + "]: "+e.getMessage(), e);
}
}
return null;
} catch (XMLStreamException e) {
throw new DataFormatException(e);
}
}
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
StringWriter stringWriter = new StringWriter();
encodeBundleToWriter(theBundle, stringWriter);
return stringWriter.toString();
}
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) {
try {
XMLStreamWriter eventWriter;
eventWriter = myXmlOutputFactory.createXMLStreamWriter(theWriter);
eventWriter = decorateStreamWriter(eventWriter);
eventWriter.writeStartElement("feed");
@ -91,10 +165,10 @@ public class XmlParser {
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
if (theBundle.getTotalResults() != null) {
if (theBundle.getTotalResults().getValue() != null) {
eventWriter.writeStartElement("os", OPENSEARCH_NS, "totalResults");
eventWriter.writeNamespace("os", OPENSEARCH_NS);
eventWriter.writeStartElement(OPENSEARCH_NS, "totalResults");
eventWriter.writeCharacters(theBundle.getTotalResults().toString());
eventWriter.writeCharacters(theBundle.getTotalResults().getValue().toString());
eventWriter.writeEndElement();
}
@ -105,6 +179,7 @@ public class XmlParser {
eventWriter.writeStartElement("author");
writeTagWithTextNode(eventWriter, "name", theBundle.getAuthorName());
writeOptionalTagWithTextNode(eventWriter, "uri", theBundle.getAuthorUri());
eventWriter.writeEndElement();
}
for (BundleEntry nextEntry : theBundle.getEntries()) {
@ -114,7 +189,7 @@ public class XmlParser {
eventWriter.writeAttribute("type", "text/xml");
IResource resource = nextEntry.getResource();
encodeResourceToStreamWriter(resource, eventWriter);
encodeResourceToXmlStreamWriter(resource, eventWriter);
eventWriter.writeEndElement(); // content
eventWriter.writeEndElement(); // entry
@ -125,10 +200,10 @@ public class XmlParser {
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
return stringWriter.toString();
}
private boolean encodeChildElementToStreamWriter(XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl) throws XMLStreamException, DataFormatException {
switch (childDef.getChildType()) {
case PRIMITIVE_DATATYPE: {
@ -261,7 +336,25 @@ public class XmlParser {
}
}
public void encodeResourceToStreamWriter(IResource theResource, XMLStreamWriter eventWriter) throws XMLStreamException, DataFormatException {
public String encodeResourceToString(IResource theResource) throws DataFormatException {
Writer stringWriter = new StringWriter();
encodeResourceToWriter(theResource, stringWriter);
return stringWriter.toString();
}
public void encodeResourceToWriter(IResource theResource, Writer stringWriter) {
XMLStreamWriter eventWriter;
try {
eventWriter = myXmlOutputFactory.createXMLStreamWriter(stringWriter);
eventWriter = decorateStreamWriter(eventWriter);
encodeResourceToXmlStreamWriter(theResource, eventWriter);
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
}
public void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter eventWriter) throws XMLStreamException, DataFormatException {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
eventWriter.writeStartElement(resDef.getName());
eventWriter.writeDefaultNamespace(FHIR_NS);
@ -272,21 +365,6 @@ public class XmlParser {
eventWriter.close();
}
public String encodeResourceToString(IResource theResource) throws DataFormatException {
XMLStreamWriter eventWriter;
StringWriter stringWriter = new StringWriter();
try {
eventWriter = myXmlOutputFactory.createXMLStreamWriter(stringWriter);
eventWriter = decorateStreamWriter(eventWriter);
encodeResourceToStreamWriter(theResource, eventWriter);
} catch (XMLStreamException e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
return stringWriter.toString();
}
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
if (theDt == null || theDt.getValue() == null) {
return;
@ -359,19 +437,6 @@ public class XmlParser {
}
}
public IResource parseResource(String theXml) throws ConfigurationException, DataFormatException {
XMLEventReader streamReader;
try {
streamReader = myXmlInputFactory.createXMLEventReader(new StringReader(theXml));
} catch (XMLStreamException e) {
throw new DataFormatException(e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
return parseResource(streamReader);
}
public Bundle parseBundle(String theXml) throws ConfigurationException, DataFormatException {
XMLEventReader streamReader;
try {
@ -390,77 +455,24 @@ public class XmlParser {
return doXmlLoop(theStreamReader, parserState);
}
public IResource parseResource(String theXml) throws ConfigurationException, DataFormatException {
XMLEventReader streamReader;
try {
streamReader = myXmlInputFactory.createXMLEventReader(new StringReader(theXml));
} catch (XMLStreamException e) {
throw new DataFormatException(e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
}
return parseResource(streamReader);
}
public IResource parseResource(XMLEventReader theStreamReader) {
ParserState<IResource> parserState = ParserState.getPreResourceInstance(myContext);
return doXmlLoop(theStreamReader, parserState);
}
private <T extends IElement> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) {
try {
while (streamReader.hasNext()) {
XMLEvent nextEvent = streamReader.nextEvent();
try {
if (nextEvent.isStartElement()) {
StartElement elem = nextEvent.asStartElement();
String namespaceURI = elem.getName().getNamespaceURI();
if ("extension".equals(elem.getName().getLocalPart())) {
Attribute urlAttr = elem.getAttributeByName(new QName("url"));
if (urlAttr == null || isBlank(urlAttr.getValue())) {
throw new DataFormatException("Extension element has no 'url' attribute");
}
parserState.enteringNewElementExtension(elem, urlAttr.getValue());
} else {
String elementName = elem.getName().getLocalPart();
parserState.enteringNewElement(namespaceURI, elementName);
}
for (@SuppressWarnings("unchecked")
Iterator<Attribute> iter = elem.getAttributes(); iter.hasNext();) {
Attribute next = iter.next();
// if
// (next.getName().getLocalPart().equals("value")) {
parserState.attributeValue(next.getName().getLocalPart(), next.getValue());
// }
}
} else if (nextEvent.isAttribute()) {
Attribute elem = (Attribute) nextEvent;
String name = (elem.getName().getLocalPart());
parserState.attributeValue(name, elem.getValue());
} else if (nextEvent.isEndElement()) {
EndElement elem = nextEvent.asEndElement();
String name = elem.getName().getLocalPart();
String namespaceURI = elem.getName().getNamespaceURI();
// if (!FHIR_NS.equals(namespaceURI) &&
// !XHTML_NS.equals(namespaceURI)) {
// continue;
// }
parserState.endingElement(elem);
if (parserState.isComplete()) {
return parserState.getObject();
}
} else if (nextEvent.isCharacters()) {
parserState.string(nextEvent.asCharacters().getData());
}
parserState.xmlEvent(nextEvent);
} catch (DataFormatException e) {
throw new DataFormatException("DataFormatException at [" + nextEvent.getLocation().toString() + "]: "+e.getMessage(), e);
}
}
return null;
} catch (XMLStreamException e) {
throw new DataFormatException(e);
}
}
private void writeAtomLink(XMLStreamWriter theEventWriter, String theRel, StringDt theStringDt) throws XMLStreamException {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.writeStartElement("link");

View File

@ -0,0 +1,8 @@
package ca.uhn.fhir.ws;
public class Constants {
public static final String CT_FHIR_XML = "application/xml+fhir";
public static final String PARAM_FORMAT = "_format";
}

View File

@ -0,0 +1,7 @@
package ca.uhn.fhir.ws;
public enum EncodingUtil {
XML, JSON
}

View File

@ -1,5 +1,9 @@
package ca.uhn.fhir.ws;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.ws.exceptions.InternalErrorException;
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -7,6 +11,7 @@ public class Parameter {
private String name;
private boolean required;
private Class<?> type;
private IParser parser;
public Parameter(){}
@ -19,8 +24,25 @@ public class Parameter {
return type;
}
public void setType(Class<?> type) {
public void setType(final Class<?> type) {
this.type = type;
if (IdentifierDt.class.isAssignableFrom(type)) {
this.parser = new IParser() {
@Override
public Object parse(String theString) throws InternalErrorException {
IdentifierDt dt;
try {
dt = (IdentifierDt) type.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new InternalErrorException(e);
}
dt.setValueAsQueryToken(theString);
return dt;
}
};
} else {
throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName());
}
}
public String getName() {
@ -38,4 +60,14 @@ public class Parameter {
public void setRequired(boolean required) {
this.required = required;
}
public Object parse(String theString) throws InternalErrorException {
return parser.parse(theString);
}
private interface IParser
{
Object parse(String theString) throws InternalErrorException;
}
}

View File

@ -4,18 +4,20 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import ca.uhn.fhir.model.api.IResource;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class Resource {
private String resourceName;
private Class resourceClass;
private List<ResourceMethod> methods = new ArrayList<ResourceMethod>();
private IResourceProvider<? extends IResource> resourceProvider;
public Resource() {}
public Resource(String resourceName, Class resourceClass, List<ResourceMethod> methods) {
public Resource(String resourceName, List<ResourceMethod> methods) {
this.resourceName = resourceName;
this.methods = methods;
}
@ -29,13 +31,6 @@ public class Resource {
return null;
}
public Class getResourceClass() {
return resourceClass;
}
public void setResourceClass(Class resourceClass) {
this.resourceClass = resourceClass;
}
public String getResourceName() {
return resourceName;
@ -55,6 +50,7 @@ public class Resource {
public void addMethod(ResourceMethod method) {
this.methods.add(method);
method.setResource(this);
}
@Override
@ -68,4 +64,12 @@ public class Resource {
return 0;
}
public void setResourceProvider(IResourceProvider<? extends IResource> theProvider) {
resourceProvider = theProvider;
}
public IResourceProvider<? extends IResource> getResourceProvider() {
return resourceProvider;
}
}

View File

@ -1,13 +1,22 @@
package ca.uhn.fhir.ws;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.ws.exceptions.InternalErrorException;
import ca.uhn.fhir.ws.exceptions.InvalidRequestException;
import ca.uhn.fhir.ws.exceptions.MethodNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -21,9 +30,10 @@ public class ResourceMethod {
DELETE
}
RequestType requestType;
List<Parameter> parameters;
Method method;
private RequestType requestType;
private List<Parameter> parameters;
private Method method;
private Resource resource;
public ResourceMethod() {}
@ -36,7 +46,7 @@ public class ResourceMethod {
this.resourceType = resourceType;
}
public Class getResourceType() throws IllegalAccessException, InstantiationException {
public Class getResourceType() {
return resourceType.getClass();
}
@ -76,19 +86,44 @@ public class ResourceMethod {
return methodParamsTemp.containsAll(parameterNames);
}
public IResource invoke(Map<String,String> parameterValues) {
public List<IResource> invoke(IResourceProvider theResourceProvider, Map<String,String[]> parameterValues) throws InvalidRequestException, InternalErrorException {
Object[] params = new Object[parameters.size()];
for (int i = 0; i < parameters.size(); i++) {
Parameter param = parameters.get(i);
String value = parameterValues.get(param.getName());
if (null != value) {
//TODO
//param.getType().newInstance().getClass();
String[] value = parameterValues.get(param.getName());
if (value == null || value.length == 0 || StringUtils.isBlank(value[0])) {
continue;
}
else {
params[i] = null;
if (value.length > 1) {
throw new InvalidRequestException("Multiple values specified for parameter: " + param.getName());
}
params[i] = param.parse(value[0]);
}
return null;
Object response;
try {
response = this.method.invoke(theResourceProvider, params);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new InternalErrorException(e);
}
if (response == null) {
return Collections.emptyList();
}else if (response instanceof IResource) {
return Collections.singletonList((IResource)response);
} else if (response instanceof Collection) {
List<IResource> retVal = new ArrayList<>();
for (Object next : ((Collection<?>)response)) {
retVal.add((IResource) next);
}
return retVal;
} else {
throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName());
}
}
public void setResource(Resource theResource) {
this.resource = theResource;
}
}

View File

@ -1,10 +0,0 @@
package ca.uhn.fhir.ws;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceName {
String value();
}

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.ws;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
@ -11,19 +12,24 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.parser.XmlParser;
import ca.uhn.fhir.ws.exceptions.AbstractResponseException;
import ca.uhn.fhir.ws.exceptions.InternalErrorException;
import ca.uhn.fhir.ws.exceptions.MethodNotFoundException;
import ca.uhn.fhir.ws.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.ws.operations.DELETE;
import ca.uhn.fhir.ws.operations.GET;
import ca.uhn.fhir.ws.operations.POST;
@ -33,46 +39,18 @@ public abstract class RestfulServer extends HttpServlet {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
// map of request handler resources keyed by resource name
private Map<String, Resource> resources = new HashMap<String, Resource>();
private static final long serialVersionUID = 1L;
private FhirContext myFhirContext;
private Map<Class<? extends IResource>, IResourceProvider<?>> myTypeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider<?>>();
public abstract Collection<IResourceProvider<?>> getResourceProviders();
// map of request handler resources keyed by resource name
private Map<String, Resource> resources = new HashMap<String, Resource>();
@Override
public void init() throws ServletException {
try {
ourLog.info("Initializing HAPI FHIR restful server");
private boolean addResourceMethod(Resource resource, Method method) throws Exception {
Collection<IResourceProvider<?>> resourceProvider = getResourceProviders();
for (IResourceProvider<?> nextProvider : resourceProvider) {
if (myTypeToProvider.containsKey(nextProvider.getResourceType())) {
throw new ServletException("Multiple providers for type: " + nextProvider.getResourceType().getCanonicalName());
}
myTypeToProvider.put(nextProvider.getResourceType(), nextProvider);
}
ourLog.info("Got {} resource providers",myTypeToProvider.size());
myFhirContext = new FhirContext(myTypeToProvider.keySet());
findResourceMethods(nextProvider.getClass());
} catch (Exception ex) {
ourLog.error("An error occurred while loading request handlers!", ex);
throw new ServletException("Failed to initialize FHIR Restful server", ex);
}
}
private void addResourceMethod(Resource resource, Method method) throws Exception {
Class<?>[] params = method.getParameterTypes();
ResourceMethod rm = new ResourceMethod();
rm.setResourceType(method.getReturnType());
// each operation name must have a request type annotation and be unique
if (null != method.getAnnotation(GET.class)) {
@ -83,27 +61,23 @@ public abstract class RestfulServer extends HttpServlet {
rm.setRequestType(ResourceMethod.RequestType.POST);
} else if (null != method.getAnnotation(DELETE.class)) {
rm.setRequestType(ResourceMethod.RequestType.DELETE);
} else {
return false;
}
rm.setMethod(method);
rm.setResourceType(method.getReturnType());
rm.setParameters(Util.getResourceParameters(method));
resource.addMethod(rm);
return true;
}
private void findResourceMethods(Class<? extends IResourceProvider> theClass1) throws Exception {
for (Method m : theClass1.getDeclaredMethods()) {
if (Modifier.isPublic(m.getModifiers())) {
String resourceName = Util.getResourceName(m);
Resource r = resources.get(resourceName);
if (null == r) {
r = new Resource();
r.setResourceName(resourceName);
resources.put(resourceName, r);
}
addResourceMethod(r, m);
ourLog.debug("found handler: " + m.getName());
}
}
@SuppressWarnings("unused")
private EncodingUtil determineResponseEncoding(Map<String, String[]> theParams) {
String[] format = theParams.remove(Constants.PARAM_FORMAT);
// TODO: handle this once we support JSON
return EncodingUtil.XML;
}
@Override
@ -126,27 +100,155 @@ public abstract class RestfulServer extends HttpServlet {
handleRequest(ResourceMethod.RequestType.PUT, request, response);
}
protected void handleRequest(ResourceMethod.RequestType requestType, HttpServletRequest request, HttpServletResponse response) {
try {
private void findResourceMethods(IResourceProvider<? extends IResource> theProvider) throws Exception {
response.setContentType(request.getHeader("Accept"));
String resourceName = request.getRequestURI();
Map<String, String> params = Util.getQueryParams(request.getQueryString());
Class<? extends IResource> resourceType = theProvider.getResourceType();
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceType);
Resource resource = resources.get(resourceName);
if (null == resource)
throw new MethodNotFoundException("No resource available for " + resourceName);
Resource r = new Resource();
r.setResourceProvider(theProvider);
r.setResourceName(definition.getName());
resources.put(definition.getName(), r);
ResourceMethod resourceMethod = resource.getMethod(params.keySet());
if (null == resourceMethod)
throw new MethodNotFoundException("No resource method available for the supplied parameters " + params);
Class<?> clazz = theProvider.getClass();
for (Method m : clazz.getDeclaredMethods()) {
if (Modifier.isPublic(m.getModifiers())) {
FhirContext ctx = new FhirContext(resourceMethod.getResourceType());
XmlParser p = new XmlParser(ctx);
response.getWriter().write(p.encodeResourceToString(resourceMethod.invoke(params)));
} catch (Throwable t) {
// TODO: handle errors
boolean foundMethod = addResourceMethod(r, m);
if (foundMethod) {
ourLog.debug("found handler: " + m.getName());
}
}
}
}
public abstract Collection<IResourceProvider<?>> getResourceProviders();
protected void handleRequest(ResourceMethod.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String resourceName = null;
Long identity = null;
Map<String, String[]> params = new HashMap<String, String[]>(request.getParameterMap());
EncodingUtil responseEncoding = determineResponseEncoding(params);
StringTokenizer tok = new StringTokenizer(request.getRequestURI(), "/");
if (!tok.hasMoreTokens()) {
throw new MethodNotFoundException("No resource name specified");
}
resourceName = tok.nextToken();
Resource resourceBinding = resources.get(resourceName);
if (resourceBinding == null) {
throw new MethodNotFoundException("Unknown resource type: " + resourceBinding);
}
if (tok.hasMoreTokens()) {
String identityString = tok.nextToken();
try {
identity = Long.parseLong(identityString);
} catch (NumberFormatException e) {
throw new NumberFormatException("Invalid identity token: " + identity);
}
}
if (identity != null && !tok.hasMoreTokens()) {
if (params == null || params.isEmpty()) {
IResource resource = resourceBinding.getResourceProvider().getResourceById(identity);
if (resource == null) {
throw new ResourceNotFoundException(identity);
}
streamResponseAsResource(response, resource, resourceBinding, responseEncoding);
return;
}
}
ResourceMethod resourceMethod = resourceBinding.getMethod(params.keySet());
if (null == resourceMethod) {
throw new MethodNotFoundException("No resource method available for the supplied parameters " + params);
}
List<IResource> result = resourceMethod.invoke(resourceBinding.getResourceProvider(), params);
streamResponseAsBundle(response, result, responseEncoding);
// resourceMethod.get
} catch (AbstractResponseException e) {
response.setStatus(e.getStatusCode());
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
response.getWriter().append(e.getMessage());
response.getWriter().close();
} catch (Throwable t) {
// TODO: handle this better
ourLog.error("Failed to process invocation", t);
throw new ServletException(t);
}
}
@Override
public void init() throws ServletException {
try {
ourLog.info("Initializing HAPI FHIR restful server");
Collection<IResourceProvider<?>> resourceProvider = getResourceProviders();
for (IResourceProvider<?> nextProvider : resourceProvider) {
if (myTypeToProvider.containsKey(nextProvider.getResourceType())) {
throw new ServletException("Multiple providers for type: " + nextProvider.getResourceType().getCanonicalName());
}
myTypeToProvider.put(nextProvider.getResourceType(), nextProvider);
}
ourLog.info("Got {} resource providers",myTypeToProvider.size());
myFhirContext = new FhirContext(myTypeToProvider.keySet());
for (IResourceProvider<?> provider : myTypeToProvider.values()) {
findResourceMethods(provider);
}
} catch (Exception ex) {
ourLog.error("An error occurred while loading request handlers!", ex);
throw new ServletException("Failed to initialize FHIR Restful server", ex);
}
}
private void streamResponseAsBundle(HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingUtil theResponseEncoding) throws IOException {
theHttpResponse.setStatus(200);
theHttpResponse.setContentType(Constants.CT_FHIR_XML);
theHttpResponse.setCharacterEncoding("UTF-8");
Bundle bundle = new Bundle();
bundle.getAuthorName().setValue(getClass().getCanonicalName());
bundle.getId().setValue(UUID.randomUUID().toString());
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
for (IResource next : theResult) {
BundleEntry entry = new BundleEntry();
bundle.getEntries().add(entry);
entry.setResource(next);
}
bundle.getTotalResults().setValue(theResult.size());
PrintWriter writer = theHttpResponse.getWriter();
myFhirContext.newXmlParser().encodeBundleToWriter(bundle, writer);
writer.close();
}
private void streamResponseAsResource(HttpServletResponse theHttpResponse, IResource theResource, Resource theResourceBinding, EncodingUtil theResponseEncoding) throws IOException {
theHttpResponse.setStatus(200);
theHttpResponse.setContentType(Constants.CT_FHIR_XML);
theHttpResponse.setCharacterEncoding("UTF-8");
PrintWriter writer = theHttpResponse.getWriter();
myFhirContext.newXmlParser().encodeResourceToWriter(theResource, writer);
writer.close();
}

View File

@ -19,6 +19,7 @@ public class Util {
public static Map<String, String> getQueryParams(String query) throws UnsupportedEncodingException {
try {
Map<String, String> params = new HashMap<String, String>();
for (String param : query.split("&")) {
String[] pair = param.split("=");
@ -33,13 +34,6 @@ public class Util {
}
}
public static String getResourceName(Method method) {
ResourceName resourceNameAnnotation = method.getAnnotation(ResourceName.class);
if (null != resourceNameAnnotation) {
return resourceNameAnnotation.value();
}
return null;
}
public static List<Parameter> getResourceParameters(Method method) {
List<Parameter> parameters = new ArrayList<Parameter>();

View File

@ -0,0 +1,42 @@
package ca.uhn.fhir.ws.exceptions;
public abstract class AbstractResponseException extends Exception {
private static final long serialVersionUID = 1L;
private int myStatusCode;
/**
* Constructor
*
* @param theStatusCode
* The HTTP status code corresponding to this problem
* @param theMessage
* The message
*/
public AbstractResponseException(int theStatusCode, String theMessage) {
super(theMessage);
myStatusCode = theStatusCode;
}
/**
* Constructor
*
* @param theStatusCode
* The HTTP status code corresponding to this problem
* @param theCause
* The underlying cause exception
*/
public AbstractResponseException(int theStatusCode, Throwable theCause) {
super(theCause.toString(), theCause);
myStatusCode = theStatusCode;
}
/**
* Returns the HTTP status code corresponding to this problem
*/
public int getStatusCode() {
return myStatusCode;
}
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.ws.exceptions;
import javax.servlet.ServletException;
public class ConfigurationException extends ServletException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,15 @@
package ca.uhn.fhir.ws.exceptions;
public class InternalErrorException extends AbstractResponseException {
private static final long serialVersionUID = 1L;
public InternalErrorException(String theMessage) {
super(500, theMessage);
}
public InternalErrorException(Throwable theCause) {
super(500, theCause);
}
}

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.ws.exceptions;
public class InvalidRequestException extends AbstractResponseException {
private static final long serialVersionUID = 1L;
public InvalidRequestException(String theMessage) {
super(400, theMessage);
}
}

View File

@ -3,6 +3,10 @@ package ca.uhn.fhir.ws.exceptions;
/**
* Created by dsotnikov on 2/27/2014.
*/
public class MethodNotFoundException extends Exception {
public MethodNotFoundException(String error) { super(error); }
public class MethodNotFoundException extends AbstractResponseException {
private static final long serialVersionUID = 1L;
public MethodNotFoundException(String error) {
super(404, error);
}
}

View File

@ -0,0 +1,11 @@
package ca.uhn.fhir.ws.exceptions;
public class ResourceNotFoundException extends AbstractResponseException {
public ResourceNotFoundException(long theId) {
super(404, "Resource " + theId + " is not known");
}
private static final long serialVersionUID = 1L;
}

View File

@ -2,8 +2,12 @@ package ca.uhn.fhir.context;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.Collections;
import org.junit.Test;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.parser.DataFormatException;
public class ModelScannerTest {
@ -11,7 +15,6 @@ public class ModelScannerTest {
@Test
public void testScanExtensionTypes() throws DataFormatException {
@SuppressWarnings("unchecked")
ModelScanner scanner = new ModelScanner(ResourceWithExtensionsA.class);
RuntimeResourceDefinition def = (RuntimeResourceDefinition) scanner.getClassToElementDefinitions().get(ResourceWithExtensionsA.class);

View File

@ -1,6 +1,14 @@
package ca.uhn.fhir.ws;
import java.util.HashMap;
import java.util.Map;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.ws.operations.GET;
import ca.uhn.fhir.ws.parameters.Required;
@ -9,11 +17,48 @@ import ca.uhn.fhir.ws.parameters.Required;
*/
public class DummyPatientResourceProvider implements IResourceProvider<Patient> {
@GET
@ResourceName(value="Patient")
public Patient getPatient(@Required(name="mrn") String mrn) {
return null;
}
private Map<Long, Patient> myIdToPatient = new HashMap<>();
public DummyPatientResourceProvider() {
{
Patient patient = new Patient();
patient.getIdentifier().add(new IdentifierDt());
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00001");
patient.getName().add(new HumanNameDt());
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(new CodeableConceptDt());
patient.getGender().setText("M");
myIdToPatient.put(1L, patient);
}
{
Patient patient = new Patient();
patient.getIdentifier().add(new IdentifierDt());
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00002");
patient.getName().add(new HumanNameDt());
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientTwo");
patient.setGender(new CodeableConceptDt());
patient.getGender().setText("F");
myIdToPatient.put(2L, patient);
}
}
@GET
public Patient getPatient(@Required(name = "identifier") IdentifierDt theIdentifier) {
for (Patient next : myIdToPatient.values()) {
for (IdentifierDt nextId : next.getIdentifier()) {
if (nextId.matchesSystemAndValue(theIdentifier)) {
return next;
}
}
}
return null;
}
@Override
public Class<Patient> getResourceType() {
@ -22,6 +67,6 @@ public class DummyPatientResourceProvider implements IResourceProvider<Patient>
@Override
public Patient getResourceById(long theId) {
return null;
return myIdToPatient.get(theId);
}
}

View File

@ -1,17 +1,12 @@
package ca.uhn.fhir.ws;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
@ -21,153 +16,115 @@ import org.apache.http.impl.conn.SchemeRegistryFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.Before;
import org.junit.AfterClass;
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.dstu.resource.Patient;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class ResfulServerTest extends TestCase {
public class ResfulServerTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResfulServerTest.class);
private static int ourPort;
private static Server ourServer;
private static DefaultHttpClient ourClient;
private static FhirContext ourCtx;
@Before
public void setUp() throws Exception {
/*
* System.setProperty("java.naming.factory.initial",
* "org.apache.naming.java.javaURLContextFactory");
* System.setProperty("java.naming.factory.url.pkgs",
* "org.apache.naming"); InitialContext context = new InitialContext();
* //context.bind("java:comp", "env");
* context.bind(context.composeName("java:comp", "env"),
* "ca.uhn.rest.handlers");
*
* //Context subcontext = context.createSubcontext("java:comp/env");
* //context.bind("java:comp/env/ca.uhn.rest.handlers", "ca.uhn.test");
*
* Context env = (Context) new InitialContext().lookup("java:comp/env");
*
* //System.out.println((String) env.lookup("ca.uhn.rest.handlers"));
*/
}
@Test
public void testServlet() throws Exception {
int port = RandomServerPortProvider.findFreePort();
Server server = new Server(port);
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
ServletHolder servletHolder = new ServletHolder(new DummyRestfulServer(patientProvider));
proxyHandler.addServletWithMapping(servletHolder, "/");
server.setHandler(proxyHandler);
server.start();
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(SchemeRegistryFactory.createDefault(), 5000, TimeUnit.MILLISECONDS);
HttpClient client = new DefaultHttpClient(connectionManager);
ourClient = new DefaultHttpClient(connectionManager);
HttpPost httpPost = new HttpPost("http://localhost:" + port + "/foo/bar?bar=123&more=params");
httpPost.setEntity(new StringEntity("test", ContentType.create("application/json", "UTF-8")));
HttpResponse status = client.execute(httpPost);
ourCtx = new FhirContext(Patient.class);
ourLog.info("Response was: {}", status);
}
// server.join();
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@Test
public void testRequiredParamsMissing() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
public void testSearchByParamIdentifier() throws Exception {
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=urn:hapitest:mrns%7C00001");
HttpResponse status = ourClient.execute(httpGet);
rm.setParameters(methodParams);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
Patient patient = (Patient)bundle.getEntries().get(0).getResource();
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("lastName");
assertEquals(false, rm.matches(inputParams)); // False
}
@Test
public void testRequiredParamsOnly() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
public void testGetById() throws Exception {
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
// HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/1");
// httpPost.setEntity(new StringEntity("test", ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
rm.setParameters(methodParams);
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.debug("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Patient patient = (Patient) ourCtx.newXmlParser().parseResource(responseContent);
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
/*
* Different ID
*/
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.debug("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
patient = (Patient) ourCtx.newXmlParser().parseResource(responseContent);
assertEquals("PatientTwo", patient.getName().get(0).getGiven().get(0).getValue());
/*
* Bad ID
*/
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/9999999");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.debug("Response was:\n{}", responseContent);
assertEquals(404, status.getStatusLine().getStatusCode());
Set<String> inputParams = new HashSet<String>();
inputParams.add("mrn");
assertEquals(true, rm.matches(inputParams)); // True
}
@Test
public void testMixedParams() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("mrn");
assertEquals(true, rm.matches(inputParams)); // True
}
@Test
public void testAllParams() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("lastName");
inputParams.add("mrn");
assertEquals(true, rm.matches(inputParams)); // True
}
@Test
public void testAllParamsWithExtra() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("lastName");
inputParams.add("mrn");
inputParams.add("foo");
assertEquals(false, rm.matches(inputParams)); // False
}
}

View File

@ -0,0 +1,104 @@
package ca.uhn.fhir.ws;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
public class ResourceMethodTest {
@Test
public void testRequiredParamsMissing() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("lastName");
assertEquals(false, rm.matches(inputParams)); // False
}
@Test
public void testRequiredParamsOnly() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("mrn");
assertEquals(true, rm.matches(inputParams)); // True
}
@Test
public void testMixedParams() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("mrn");
assertEquals(true, rm.matches(inputParams)); // True
}
@Test
public void testAllParams() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("lastName");
inputParams.add("mrn");
assertEquals(true, rm.matches(inputParams)); // True
}
@Test
public void testAllParamsWithExtra() {
ResourceMethod rm = new ResourceMethod();
List<Parameter> methodParams = new ArrayList<Parameter>();
methodParams.add(new Parameter("firstName", false));
methodParams.add(new Parameter("lastName", false));
methodParams.add(new Parameter("mrn", true));
rm.setParameters(methodParams);
Set<String> inputParams = new HashSet<String>();
inputParams.add("firstName");
inputParams.add("lastName");
inputParams.add("mrn");
inputParams.add("foo");
assertEquals(false, rm.matches(inputParams)); // False
}
}

View File

@ -0,0 +1,17 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.eclipse" additivity="false">
</logger>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,54 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-test</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR Structures - DSTU (FHIR 0.80)</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<package>ca.uhn.fhir.model.dstu</package>
<baseResourceNames>
<baseResourceName>patient</baseResourceName>
<baseResourceName>valueset</baseResourceName>
<baseResourceName>organization</baseResourceName>
<baseResourceName>device</baseResourceName>
<baseResourceName>location</baseResourceName>
<baseResourceName>practitioner</baseResourceName>
</baseResourceNames>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -31,6 +31,41 @@ public class ${className}Dt extends BaseElement implements ICompositeDatatype {
#childAccessors( $children )
#childResourceBlocks($resourceBlockChildren)
#########################
### Type-specific methods
#########################
#if ( ${className} == "Identifier" )
/**
* Returns true if <code>this</code> identifier has the same {@link IdentifierDt#getValue() value}
* and {@link IdentifierDt#getSystem() system} (as compared by simple equals comparison).
* Does not compare other values (e.g. {@link IdentifierDt#getUse() use}) or any extensions.
*/
public boolean matchesSystemAndValue(IdentifierDt theIdentifier) {
if (theIdentifier == null) {
return false;
}
return getValue().equals(theIdentifier.getValue()) && getSystem().equals(theIdentifier.getSystem());
}
/**
* Sets the value of this <code>IdentifierDt</code> using the <b>token</b> format. This
* format is used in HTTP queries as a parameter format.
*
* @see See FHIR specification
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search Parameter Types</a>
* for information on the <b>token</b> format
*/
public void setValueAsQueryToken(String theParameter) {
int barIndex = theParameter.indexOf('|');
if (barIndex != -1) {
setSystem(new UriDt(theParameter.substring(0, barIndex)));
setValue(theParameter.substring(barIndex + 1));
} else {
setValue(theParameter);
}
}
#end
#childExtensionTypes( $childExtensionTypes )
}