diff --git a/hapi-fhir-base/.classpath b/hapi-fhir-base/.classpath index 42f0d694287..29fce1ef7fd 100644 --- a/hapi-fhir-base/.classpath +++ b/hapi-fhir-base/.classpath @@ -1,86 +1,69 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-base/.project b/hapi-fhir-base/.project index 22f076c3eef..a75380b92e5 100644 --- a/hapi-fhir-base/.project +++ b/hapi-fhir-base/.project @@ -1,3 +1,4 @@ + hapi-fhir-base NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index caa552954f2..33091f7c6c0 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -61,7 +61,12 @@ org.apache.httpcomponents httpclient - 4.2.3 + 4.3.3 + + + org.apache.httpcomponents + httpcore + 4.3.2 diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 5bb849a77b2..fa8fa7c75a4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -8,7 +8,9 @@ import java.util.Set; import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.XmlParser; +import ca.uhn.fhir.rest.client.RestfulClientFactory; public class FhirContext { @@ -47,8 +49,12 @@ public class FhirContext { return (RuntimeResourceDefinition) myClassToElementDefinition.get(theResource.getClass()); } - public XmlParser newXmlParser() { + public IParser newXmlParser() { return new XmlParser(this); } + + public RestfulClientFactory newClientFactory() { + return new RestfulClientFactory(this); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java index ba294ad7f1e..c1ac1241b45 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java @@ -20,7 +20,16 @@ public class Bundle extends BaseBundle implements IElement { private InstantDt myPublished; private StringDt myTitle; private IntegerDt myTotalResults; - private InstantDt myUpdated; + private InstantDt myUpdated; + + /** + * Adds and returns a new bundle entry + */ + public BundleEntry addEntry() { + BundleEntry retVal = new BundleEntry(); + getEntries().add(retVal); + return retVal; + } public List getEntries() { if (myEntries == null) { @@ -87,25 +96,39 @@ public class Bundle extends BaseBundle implements IElement { public StringDt getTitle() { if (myTitle == null) { - myTitle= new StringDt(); + myTitle = new StringDt(); } return myTitle; } public IntegerDt getTotalResults() { - if (myTotalResults== null) { - myTotalResults= new IntegerDt(); + if (myTotalResults == null) { + myTotalResults = new IntegerDt(); } return myTotalResults; } public InstantDt getUpdated() { if (myUpdated == null) { - myUpdated= new InstantDt(); + myUpdated = new InstantDt(); } return myUpdated; } - + public List toListOfResources() { + ArrayList retVal = new ArrayList(); + for (BundleEntry next : getEntries()) { + if (next.getResource() != null) { + retVal.add(next.getResource()); + } + } + return retVal; + } + + public static Bundle withSingleResource(IResource theResource) { + Bundle retVal = new Bundle(); + retVal.addEntry().setResource(theResource); + return retVal; + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleCategory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleCategory.java new file mode 100644 index 00000000000..1a1b3c03008 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleCategory.java @@ -0,0 +1,33 @@ +package ca.uhn.fhir.model.api; + +public class BundleCategory { + + private String myLabel; + private String myScheme; + private String myTerm; + + public String getLabel() { + return myLabel; + } + + public String getScheme() { + return myScheme; + } + + public String getTerm() { + return myTerm; + } + + public void setLabel(String theLabel) { + myLabel = theLabel; + } + + public void setScheme(String theScheme) { + myScheme = theScheme; + } + + public void setTerm(String theTerm) { + myTerm = theTerm; + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java index 77c8d42967d..31c0c59f2ed 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BundleEntry.java @@ -1,5 +1,8 @@ package ca.uhn.fhir.model.api; +import java.util.ArrayList; +import java.util.List; + import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.XhtmlDt; @@ -12,6 +15,7 @@ public class BundleEntry extends BaseBundle { private StringDt myTitle; private InstantDt myUpdated; private XhtmlDt mySummary; + private List myCategories; public StringDt getId() { if (myId == null) { @@ -70,4 +74,17 @@ public class BundleEntry extends BaseBundle { return mySummary; } + public BundleCategory addCategory() { + BundleCategory retVal = new BundleCategory(); + getCategories().add(retVal); + return retVal; + } + + public List getCategories() { + if (myCategories == null) { + myCategories = new ArrayList(); + } + return myCategories; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeDatatype.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeDatatype.java index a23ffbeb211..85e67f4abf8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeDatatype.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeDatatype.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.model.api; -public interface ICompositeDatatype extends IDatatype, ICompositeElement { +public interface ICompositeDatatype extends IDatatype, ICompositeElement { + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java new file mode 100644 index 00000000000..54271e08021 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java @@ -0,0 +1,26 @@ +package ca.uhn.fhir.model.api; + +public interface IQueryParameterType { + + /** + * Sets the value of this type using the token format. This + * format is used in HTTP queries as a parameter format. + * + * @see See FHIR specification + * 2.2.2 Search Parameter Types + * for information on the token format + */ + public void setValueAsQueryToken(String theParameter); + + /** + * Returns the value of this type using the token format. This + * format is used in HTTP queries as a parameter format. + * + * @see See FHIR specification + * 2.2.2 Search Parameter Types + * for information on the token format + */ + public String getValueAsQueryToken(); + + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/IdentifierDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/IdentifierDt.java index f7dca33053d..e99bfe0315b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/IdentifierDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/dstu/composite/IdentifierDt.java @@ -17,6 +17,7 @@ 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.*; @@ -38,7 +39,7 @@ import ca.uhn.fhir.model.dstu.resource.*; *

*/ @DatatypeDef(name="Identifier") -public class IdentifierDt extends BaseElement implements ICompositeDatatype { +public class IdentifierDt extends BaseElement implements ICompositeDatatype, IQueryParameterType { @Child(name="use", type=CodeDt.class, order=0, min=0, max=1) private BoundCodeDt myUse; @@ -61,6 +62,21 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype { }) private ResourceReference myAssigner; + /** + * Creates a new identifier + */ + public IdentifierDt() { + // nothing + } + + /** + * Creates a new identifier with the given system and value + */ + public IdentifierDt(String theSystem, String theValue) { + setSystem(theSystem); + setValue(theValue); + } + /** * Gets the value(s) for use (usual | official | temp | secondary (If known) ). @@ -303,13 +319,21 @@ public class IdentifierDt extends BaseElement implements ICompositeDatatype { } /** - * Sets the value of this IdentifierDt using the token format. This - * format is used in HTTP queries as a parameter format. - * - * @see See FHIR specification - * 2.2.2 Search Parameter Types - * for information on the token format + * {@inheritDoc} */ + @Override + public String getValueAsQueryToken() { + if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) { + return getSystem().getValueAsString() + '|' + getValue().getValueAsString(); + } else { + return getValue().getValueAsString(); + } + } + + /** + * {@inheritDoc} + */ + @Override public void setValueAsQueryToken(String theParameter) { int barIndex = theParameter.indexOf('|'); if (barIndex != -1) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java index 6c12b64a5e7..3b22b54545b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java @@ -4,12 +4,13 @@ import org.apache.commons.lang3.StringUtils; import ca.uhn.fhir.model.api.BaseElement; import ca.uhn.fhir.model.api.IPrimitiveDatatype; +import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.SimpleSetter; import ca.uhn.fhir.parser.DataFormatException; @DatatypeDef(name = "string") -public class StringDt extends BaseElement implements IPrimitiveDatatype { +public class StringDt extends BaseElement implements IPrimitiveDatatype, IQueryParameterType { private String myValue; @@ -84,6 +85,22 @@ public class StringDt extends BaseElement implements IPrimitiveDatatype return false; return true; } + + /** + * {@inheritDoc} + */ + @Override + public void setValueAsQueryToken(String theParameter) { + setValue(theParameter); + } + + /** + * {@inheritDoc} + */ + @Override + public String getValueAsQueryToken() { + return getValue(); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java new file mode 100644 index 00000000000..34e8043bbd7 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.parser; + +import java.io.Reader; +import java.io.Writer; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.IResource; + +public interface IParser { + + String encodeBundleToString(Bundle theBundle) throws DataFormatException; + + void encodeBundleToWriter(Bundle theBundle, Writer theWriter); + + String encodeResourceToString(IResource theResource) throws DataFormatException; + + void encodeResourceToWriter(IResource theResource, Writer stringWriter); + + void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter eventWriter) throws XMLStreamException, DataFormatException; + + Bundle parseBundle(Reader theReader); + + Bundle parseBundle(String theXml) throws ConfigurationException, DataFormatException; + + IResource parseResource(String theXml) throws ConfigurationException, DataFormatException; + + IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException; + + IResource parseResource(XMLEventReader theStreamReader); + +} \ No newline at end of file diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 77e41277adc..1d9517f9618 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -15,7 +15,6 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition; -import ca.uhn.fhir.context.RuntimeChildPrimitiveBoundCodeDatatypeDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition; import ca.uhn.fhir.context.RuntimeResourceBlockDefinition; @@ -23,6 +22,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition; import ca.uhn.fhir.model.api.BaseBundle; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.BundleCategory; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.ICompositeDatatype; import ca.uhn.fhir.model.api.ICompositeElement; @@ -84,9 +84,7 @@ class ParserState { } /** - * Invoked after any new XML event is individually processed, containing a - * copy of the XML event. This is basically intended for embedded XHTML - * content + * Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically intended for embedded XHTML content */ public void xmlEvent(XMLEvent theNextEvent) { myState.xmlEvent(theNextEvent); @@ -140,6 +138,37 @@ class ParserState { } + public class AtomCategoryState extends BaseState { + + private BundleCategory myInstance; + + public AtomCategoryState(BundleCategory theEntry) { + myInstance = theEntry; + } + + @Override + public void endingElement(EndElement theElem) throws DataFormatException { + pop(); + } + + @Override + public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { + throw new DataFormatException("Unexpected element: " + theLocalPart); + } + + @Override + public void attributeValue(String theName, String theValue) throws DataFormatException { + if ("term".equals(theName)) { + myInstance.setTerm(theValue); + } else if ("label".equals(theName)) { + myInstance.setLabel(theValue); + } else if ("scheme".equals(theName)) { + myInstance.setScheme(theValue); + } + } + + } + public class AtomEntryState extends BaseState { private BundleEntry myEntry; @@ -171,7 +200,9 @@ class ParserState { } else if ("content".equals(theLocalPart)) { push(new PreResourceState(myEntry)); } else if ("summary".equals(theLocalPart)) { - push(new XhtmlState(myEntry.getSummary(),false)); + push(new XhtmlState(myEntry.getSummary(), false)); + } else if ("category".equals(theLocalPart)) { + push(new AtomCategoryState(myEntry.addCategory())); } else { throw new DataFormatException("Unexpected element in entry: " + theLocalPart); } @@ -281,8 +312,6 @@ class ParserState { myInstance = theInstance; } - - @Override public void endingElement(EndElement theElem) throws DataFormatException { pop(); @@ -307,7 +336,7 @@ class ParserState { } else if ("author".equals(theLocalPart)) { push(new AtomAuthorState(myInstance)); } else { - throw new DataFormatException("Unexpected element: "+ theLocalPart); + throw new DataFormatException("Unexpected element: " + theLocalPart); } // TODO: handle category and DSig @@ -387,7 +416,6 @@ class ParserState { myParentInstance = theParentInstance; } - @Override public void endingElement(EndElement theElem) throws DataFormatException { pop(); @@ -522,7 +550,7 @@ class ParserState { RuntimePrimitiveDatatypeNarrativeDefinition xhtmlTarget = (RuntimePrimitiveDatatypeNarrativeDefinition) target; XhtmlDt newDt = xhtmlTarget.newInstance(); child.getMutator().addValue(myInstance, newDt); - XhtmlState state = new XhtmlState(newDt,true); + XhtmlState state = new XhtmlState(newDt, true); push(state); return; } @@ -562,7 +590,6 @@ class ParserState { myExtension = theExtension; } - @Override public void endingElement(EndElement theElem) throws DataFormatException { if (myExtension.getValue() != null && myExtension.getUndeclaredExtensions().size() > 0) { @@ -622,7 +649,6 @@ class ParserState { private Bundle myInstance; - @Override public void endingElement(EndElement theElem) throws DataFormatException { // ignore @@ -666,7 +692,6 @@ class ParserState { myEntry = theEntry; } - @Override public void endingElement(EndElement theElem) throws DataFormatException { pop(); @@ -723,14 +748,14 @@ class ParserState { pop(); } -// @Override -// public void enteringNewElementExtension(StartElement theElement, String theUrlAttr) { -// if (myInstance instanceof ISupportsUndeclaredExtensions) { -// UndeclaredExtension ext = new UndeclaredExtension(theUrlAttr); -// ((ISupportsUndeclaredExtensions) myInstance).getUndeclaredExtensions().add(ext); -// push(new ExtensionState(ext)); -// } -// } + // @Override + // public void enteringNewElementExtension(StartElement theElement, String theUrlAttr) { + // if (myInstance instanceof ISupportsUndeclaredExtensions) { + // UndeclaredExtension ext = new UndeclaredExtension(theUrlAttr); + // ((ISupportsUndeclaredExtensions) myInstance).getUndeclaredExtensions().add(ext); + // push(new ExtensionState(ext)); + // } + // } @Override public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { @@ -761,7 +786,7 @@ class ParserState { if (!"value".equals(theName)) { return; } - + switch (mySubState) { case DISPLAY: myInstance.setDisplay(theValue); @@ -840,7 +865,7 @@ class ParserState { if (theEvent.isStartElement()) { myDepth++; } - + if (theEvent.isEndElement()) { if (myDepth == 0) { myDt.setValue(myEvents); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index a1ecec60309..1d31e5b7e8d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.parser; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; @@ -48,7 +49,7 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.util.PrettyPrintWriterWrapper; -public class XmlParser { +public class XmlParser implements IParser { static final String ATOM_NS = "http://www.w3.org/2005/Atom"; static final String FHIR_NS = "http://hl7.org/fhir"; static final String OPENSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1/"; @@ -139,6 +140,10 @@ public class XmlParser { } } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#encodeBundleToString(ca.uhn.fhir.model.api.Bundle) + */ + @Override public String encodeBundleToString(Bundle theBundle) throws DataFormatException { StringWriter stringWriter = new StringWriter(); encodeBundleToWriter(theBundle, stringWriter); @@ -146,6 +151,10 @@ public class XmlParser { return stringWriter.toString(); } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#encodeBundleToWriter(ca.uhn.fhir.model.api.Bundle, java.io.Writer) + */ + @Override public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) { try { XMLStreamWriter eventWriter; @@ -336,12 +345,20 @@ public class XmlParser { } } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#encodeResourceToString(ca.uhn.fhir.model.api.IResource) + */ + @Override public String encodeResourceToString(IResource theResource) throws DataFormatException { Writer stringWriter = new StringWriter(); encodeResourceToWriter(theResource, stringWriter); return stringWriter.toString(); } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#encodeResourceToWriter(ca.uhn.fhir.model.api.IResource, java.io.Writer) + */ + @Override public void encodeResourceToWriter(IResource theResource, Writer stringWriter) { XMLStreamWriter eventWriter; try { @@ -354,6 +371,10 @@ public class XmlParser { } } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#encodeResourceToXmlStreamWriter(ca.uhn.fhir.model.api.IResource, javax.xml.stream.XMLStreamWriter) + */ + @Override public void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter eventWriter) throws XMLStreamException, DataFormatException { RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); eventWriter.writeStartElement(resDef.getName()); @@ -411,13 +432,22 @@ public class XmlParser { StartElement se = event.asStartElement(); if (firstEvent) { theEventWriter.writeStartElement(se.getName().getLocalPart()); - theEventWriter.writeNamespace(se.getName().getPrefix(), se.getName().getNamespaceURI()); + if (StringUtils.isBlank(se.getName().getPrefix())) { + theEventWriter.writeDefaultNamespace(se.getName().getNamespaceURI()); + }else { + theEventWriter.writeNamespace(se.getName().getPrefix(), se.getName().getNamespaceURI()); + } } else { if (isBlank(se.getName().getPrefix())) { if (isBlank(se.getName().getNamespaceURI())) { theEventWriter.writeStartElement(se.getName().getLocalPart()); } else { + if (StringUtils.isBlank(se.getName().getPrefix())) { + theEventWriter.writeStartElement(se.getName().getLocalPart()); + theEventWriter.writeDefaultNamespace(se.getName().getNamespaceURI()); + }else { theEventWriter.writeStartElement(se.getName().getNamespaceURI(), se.getName().getLocalPart()); + } } } else { theEventWriter.writeStartElement(se.getName().getPrefix(), se.getName().getLocalPart(), se.getName().getNamespaceURI()); @@ -437,17 +467,13 @@ public class XmlParser { } } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#parseBundle(java.lang.String) + */ + @Override public Bundle parseBundle(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 parseBundle(streamReader); + StringReader reader = new StringReader(theXml); + return parseBundle(reader); } private Bundle parseBundle(XMLEventReader theStreamReader) { @@ -455,19 +481,19 @@ public class XmlParser { return doXmlLoop(theStreamReader, parserState); } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#parseResource(java.lang.String) + */ + @Override 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); + StringReader reader = new StringReader(theXml); + return parseResource(reader); } + /* (non-Javadoc) + * @see ca.uhn.fhir.parser.IParser#parseResource(javax.xml.stream.XMLEventReader) + */ + @Override public IResource parseResource(XMLEventReader theStreamReader) { ParserState parserState = ParserState.getPreResourceInstance(myContext); return doXmlLoop(theStreamReader, parserState); @@ -504,4 +530,32 @@ public class XmlParser { } theEventWriter.writeEndElement(); } + + @Override + public Bundle parseBundle(Reader theReader) { + XMLEventReader streamReader; + try { + streamReader = myXmlInputFactory.createXMLEventReader(theReader); + } catch (XMLStreamException e) { + throw new DataFormatException(e); + } catch (FactoryConfigurationError e) { + throw new ConfigurationException("Failed to initialize STaX event factory", e); + } + + return parseBundle(streamReader); + } + + @Override + public IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException { + XMLEventReader streamReader; + try { + streamReader = myXmlInputFactory.createXMLEventReader(theReader); + } catch (XMLStreamException e) { + throw new DataFormatException(e); + } catch (FactoryConfigurationError e) { + throw new ConfigurationException("Failed to initialize STaX event factory", e); + } + + return parseResource(streamReader); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClientInvocation.java new file mode 100644 index 00000000000..6e0bfa8be17 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClientInvocation.java @@ -0,0 +1,14 @@ +package ca.uhn.fhir.rest.client; + +import org.apache.http.client.methods.HttpRequestBase; + +public abstract class BaseClientInvocation { + + /** + * Create an HTTP request out of this client request + * + * @param theUrlBase The FHIR server base url (with a trailing "/") + */ + public abstract HttpRequestBase asHttpRequest(String theUrlBase); + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocation.java deleted file mode 100644 index b3c257c1757..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocation.java +++ /dev/null @@ -1,5 +0,0 @@ -package ca.uhn.fhir.rest.client; - -public class ClientInvocation { - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java index 6793e99d446..74827a5630e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java @@ -1,35 +1,117 @@ package ca.uhn.fhir.rest.client; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.ContentType; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; +import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.common.BaseMethodBinding; +import ca.uhn.fhir.rest.server.Constants; public class ClientInvocationHandler implements InvocationHandler { - private HttpClient myClient; - private Map myBindings = new HashMap(); - private FhirContext myContext; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientInvocationHandler.class); + private final Map myBindings = new HashMap(); + private final HttpClient myClient; + private final FhirContext myContext; + private final String myUrlBase; - public ClientInvocationHandler(HttpClient theClient, FhirContext theContext) { + public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase) { myClient = theClient; myContext = theContext; - } - - @Override - public Object invoke(Object theProxy, Method theMethod, Object[] theArgs) throws Throwable { - BaseMethodBinding binding = myBindings.get(theMethod); - ClientInvocation clientInvocation = binding.invokeClient(theArgs); - return null; + myUrlBase = theUrlBase; } public void addBinding(Method theMethod, BaseMethodBinding theBinding) { myBindings.put(theMethod, theBinding); } + @Override + public Object invoke(Object theProxy, Method theMethod, Object[] theArgs) throws Throwable { + BaseMethodBinding binding = myBindings.get(theMethod); + GetClientInvocation clientInvocation = binding.invokeClient(theArgs); + HttpRequestBase httpRequest = clientInvocation.asHttpRequest(myUrlBase); + HttpResponse response = myClient.execute(httpRequest); + + ContentType ct = ContentType.get(response.getEntity()); + Charset charset = ct.getCharset(); + + if (charset == null) { + ourLog.warn("Response did not specify a charset."); + charset = Charset.forName("UTF-8"); + } + + Reader reader = new InputStreamReader(response.getEntity().getContent(), charset); + + if (ourLog.isTraceEnabled()) { + String responseString = IOUtils.toString(reader); + ourLog.trace("FHIR response:\n{}\n{}", response, responseString); + reader = new StringReader(responseString); + } + + IParser parser; + String mimeType = ct.getMimeType(); + if (Constants.CT_ATOM_XML.equals(mimeType)) { + parser = myContext.newXmlParser(); + } else if (Constants.CT_FHIR_XML.equals(mimeType)) { + parser = myContext.newXmlParser(); + } else { + throw new NonFhirResponseException("Response contains non-FHIR content-type: " + mimeType, mimeType, response.getStatusLine().getStatusCode(), IOUtils.toString(reader)); + } + + switch (binding.getReturnType()) { + case BUNDLE: { + Bundle bundle = parser.parseBundle(reader); + switch (binding.getMethodReturnType()) { + case BUNDLE: + return bundle; + case LIST_OF_RESOURCES: + return bundle.toListOfResources(); + case RESOURCE: + List list = bundle.toListOfResources(); + if (list.size() == 0) { + return null; + } else if (list.size() == 1) { + return list.get(1); + } else { + throw new InvalidResponseException("FHIR server call returned a bundle with multiple resources, but this method is only able to returns one."); + } + } + break; + } + case RESOURCE: { + IResource resource = parser.parseResource(reader); + switch (binding.getMethodReturnType()) { + case BUNDLE: + return Bundle.withSingleResource(resource); + case LIST_OF_RESOURCES: + return Collections.singletonList(resource); + case RESOURCE: + return resource; + } + break; + } + } + + throw new IllegalStateException("Should not get here!"); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GetClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GetClientInvocation.java new file mode 100644 index 00000000000..da4874c6c32 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GetClientInvocation.java @@ -0,0 +1,41 @@ +package ca.uhn.fhir.rest.client; + +import java.util.Collections; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpRequestBase; + +import ca.uhn.fhir.rest.common.BaseMethodBinding; + +public class GetClientInvocation extends BaseClientInvocation { + + private final Map myParameters; + private final String myUrlPath; + + public GetClientInvocation(Map theParameters, String... theUrlFragments) { + myParameters = theParameters; + myUrlPath = StringUtils.join(theUrlFragments, '/'); + } + + public GetClientInvocation(String... theUrlFragments) { + myParameters = Collections.emptyMap(); + myUrlPath = StringUtils.join(theUrlFragments, '/'); + } + + + public Map getParameters() { + return myParameters; + } + + public String getUrlPath() { + return myUrlPath; + } + + @Override + public HttpRequestBase asHttpRequest(String theUrlBase) { + return new HttpGet(theUrlBase + myUrlPath); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index d1590313cdf..d857bbe3c02 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -6,8 +6,11 @@ import java.lang.reflect.Proxy; import java.util.concurrent.TimeUnit; import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.SchemeRegistryFactory; import ca.uhn.fhir.context.ConfigurationException; @@ -46,10 +49,12 @@ public class RestfulClientFactory { throw new ConfigurationException(theClientType.getCanonicalName() + " is not an interface"); } - PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(SchemeRegistryFactory.createDefault(), 5000, TimeUnit.MILLISECONDS); - HttpClient client = new DefaultHttpClient(connectionManager); - - ClientInvocationHandler theInvocationHandler = new ClientInvocationHandler(client, myContext); + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + CloseableHttpClient client = builder.build(); + + ClientInvocationHandler theInvocationHandler = new ClientInvocationHandler(client, myContext, theServerBase); for (Method nextMethod : theClientType.getMethods()) { BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/BaseServerResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/BaseServerResponseException.java new file mode 100644 index 00000000000..e9c7e95ba9a --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/BaseServerResponseException.java @@ -0,0 +1,39 @@ +package ca.uhn.fhir.rest.client.exceptions; + +public abstract class BaseServerResponseException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructor + * + * @param theMessage + * The message + */ + public BaseServerResponseException(String theMessage) { + super(theMessage); + } + + /** + * Constructor + * + * @param theMessage + * The message + * @param theCause The cause + */ + public BaseServerResponseException(String theMessage, Throwable theCause) { + super(theMessage, theCause); + } + + /** + * Constructor + * + * @param theCause + * The underlying cause exception + */ + public BaseServerResponseException(Throwable theCause) { + super(theCause.toString(), theCause); + } + + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/InvalidResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/InvalidResponseException.java new file mode 100644 index 00000000000..b60afe5cdc4 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/InvalidResponseException.java @@ -0,0 +1,38 @@ +package ca.uhn.fhir.rest.client.exceptions; + +public class InvalidResponseException extends BaseServerResponseException { + + private static final long serialVersionUID = 1L; + + /** + * Constructor + * + * @param theMessage + * The message + */ + public InvalidResponseException(String theMessage) { + super(theMessage); + } + + /** + * Constructor + * + * @param theMessage + * The message + * @param theCause The cause + */ + public InvalidResponseException(String theMessage, Throwable theCause) { + super(theMessage, theCause); + } + + /** + * Constructor + * + * @param theCause + * The underlying cause exception + */ + public InvalidResponseException(Throwable theCause) { + super(theCause.toString(), theCause); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java new file mode 100644 index 00000000000..6173fc01f4a --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java @@ -0,0 +1,40 @@ +package ca.uhn.fhir.rest.client.exceptions; + +public class NonFhirResponseException extends BaseServerResponseException { + + private static final long serialVersionUID = 1L; + private final String myContentType; + private final int myStatusCode; + private final String myResponseText; + + /** + * Constructor + * + * @param theMessage + * The message + * @param theResponseText + * @param theStatusCode + * @param theContentType + */ + public NonFhirResponseException(String theMessage, String theContentType, int theStatusCode, String theResponseText) { + super(theMessage); + myContentType=theContentType; + myStatusCode=theStatusCode; + myResponseText=theResponseText; + } + + public String getContentType() { + return myContentType; + } + + public int getStatusCode() { + return myStatusCode; + } + + public String getResponseText() { + return myResponseText; + } + + + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java index 86fb07ee9cd..33e6b1dc5d4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java @@ -9,11 +9,12 @@ import java.util.Map; import java.util.Set; import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.client.ClientInvocation; +import ca.uhn.fhir.rest.client.GetClientInvocation; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.Resource; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; @@ -23,23 +24,23 @@ import ca.uhn.fhir.rest.server.operations.Search; public abstract class BaseMethodBinding { private String myResourceName; + private MethodReturnTypeEnum myMethodReturnType; - public BaseMethodBinding(Class theAnnotatedResourceType) { + public BaseMethodBinding(MethodReturnTypeEnum theMethodReturnType, Class theAnnotatedResourceType) { ResourceDef resourceDefAnnotation = theAnnotatedResourceType.getAnnotation(ResourceDef.class); if (resourceDefAnnotation == null) { - throw new ConfigurationException(theAnnotatedResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName()+ " annotation"); + throw new ConfigurationException(theAnnotatedResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation"); } myResourceName = resourceDefAnnotation.name(); + myMethodReturnType = theMethodReturnType; } public abstract ReturnTypeEnum getReturnType(); - public ClientInvocation invokeClient(Object[] theArgs) { - // TODO Auto-generated method stub - return null; - } + public abstract GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException; - public abstract List invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map theParameterValues) throws InvalidRequestException, InternalErrorException; + public abstract List invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map theParameterValues) throws InvalidRequestException, + InternalErrorException; public abstract boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set theParameterNames); @@ -60,11 +61,21 @@ public abstract class BaseMethodBinding { } Class methodReturnType = theMethod.getReturnType(); - + MethodReturnTypeEnum methodReturnTypeEnum; + if (methodReturnType.equals(List.class)) { + methodReturnTypeEnum = MethodReturnTypeEnum.LIST_OF_RESOURCES; + } else if (methodReturnType.isAssignableFrom(annotatedResourceType)) { + methodReturnTypeEnum = MethodReturnTypeEnum.RESOURCE; + } else if (Bundle.class.isAssignableFrom(methodReturnType)) { + methodReturnTypeEnum = MethodReturnTypeEnum.LIST_OF_RESOURCES; + } else { + throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName()); + } + if (read != null) { - return new ReadMethodBinding(annotatedResourceType, theMethod); + return new ReadMethodBinding(methodReturnTypeEnum, annotatedResourceType, theMethod); } else if (search != null) { - return new SearchMethodBinding(annotatedResourceType, theMethod); + return new SearchMethodBinding(methodReturnTypeEnum, annotatedResourceType, theMethod); } else { throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName()); } @@ -98,8 +109,8 @@ public abstract class BaseMethodBinding { if (obj1 == null) { obj1 = object; } else { - throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() - + ". Can not have both."); + throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName() + ". Can not have both."); } } @@ -125,7 +136,15 @@ public abstract class BaseMethodBinding { } } + public enum MethodReturnTypeEnum { + RESOURCE, BUNDLE, LIST_OF_RESOURCES + } + public enum ReturnTypeEnum { BUNDLE, RESOURCE } + + public MethodReturnTypeEnum getMethodReturnType() { + return myMethodReturnType; + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java index 465c78fc471..af5ffda5bac 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java @@ -10,6 +10,8 @@ import org.apache.commons.lang3.Validate; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.client.GetClientInvocation; +import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.Util; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; @@ -22,8 +24,8 @@ public class ReadMethodBinding extends BaseMethodBinding { private Integer myVersionIdIndex; private int myParameterCount; - public ReadMethodBinding(Class theAnnotatedResourceType, Method theMethod) { - super(theAnnotatedResourceType); + public ReadMethodBinding(MethodReturnTypeEnum theMethodReturnType, Class theAnnotatedResourceType, Method theMethod) { + super(theMethodReturnType, theAnnotatedResourceType); Validate.notNull(theMethod, "Method must not be null"); @@ -86,4 +88,15 @@ public class ReadMethodBinding extends BaseMethodBinding { return toResourceList(response); } + @Override + public GetClientInvocation invokeClient(Object[] theArgs) { + String id = ((IdDt)theArgs[myIdIndex]).getValue(); + if (myVersionIdIndex == null) { + return new GetClientInvocation(getResourceName(), id); + }else { + String vid = ((IdDt)theArgs[myVersionIdIndex]).getValue(); + return new GetClientInvocation(getResourceName(), id, Constants.URL_TOKEN_HISTORY, vid); + } + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java index 9ccb161e7b0..01cb1de63b7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.rest.common; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -9,9 +10,9 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; -import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.client.GetClientInvocation; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.Parameter; import ca.uhn.fhir.rest.server.Util; @@ -26,14 +27,14 @@ public class SearchMethodBinding extends BaseMethodBinding { private Method method; - private List parameters; + private List myParameters; private RequestType requestType; private Class myDeclaredResourceType; - public SearchMethodBinding(Class theAnnotatedResourceType, Method theMethod) { - super(theAnnotatedResourceType); + public SearchMethodBinding(MethodReturnTypeEnum theMethodReturnTypeEnum, Class theAnnotatedResourceType, Method theMethod) { + super(theMethodReturnTypeEnum, theAnnotatedResourceType); this.method = theMethod; - this.parameters = Util.getResourceParameters(theMethod); + this.myParameters = Util.getResourceParameters(theMethod); this.myDeclaredResourceType = theMethod.getReturnType(); } @@ -43,7 +44,7 @@ public class SearchMethodBinding extends BaseMethodBinding { } public List getParameters() { - return parameters; + return myParameters; } public RequestType getRequestType() { @@ -64,9 +65,9 @@ public class SearchMethodBinding extends BaseMethodBinding { assert theId == null; assert theVersionId == null; - Object[] params = new Object[parameters.size()]; - for (int i = 0; i < parameters.size(); i++) { - Parameter param = parameters.get(i); + Object[] params = new Object[myParameters.size()]; + for (int i = 0; i < myParameters.size(); i++) { + Parameter param = myParameters.get(i); String[] value = parameterValues.get(param.getName()); if (value == null || value.length == 0 || StringUtils.isBlank(value[0])) { continue; @@ -104,8 +105,8 @@ public class SearchMethodBinding extends BaseMethodBinding { } Set methodParamsTemp = new HashSet(); - for (int i = 0; i < this.parameters.size(); i++) { - Parameter temp = this.parameters.get(i); + for (int i = 0; i < this.myParameters.size(); i++) { + Parameter temp = this.myParameters.get(i); methodParamsTemp.add(temp.getName()); if (temp.isRequired() && !theParameterNames.contains(temp.getName())) { ourLog.trace("Method {} doesn't match param '{}' is not present", method.getName(), temp.getName()); @@ -124,7 +125,7 @@ public class SearchMethodBinding extends BaseMethodBinding { } public void setParameters(List parameters) { - this.parameters = parameters; + this.myParameters = parameters; } public void setRequestType(RequestType requestType) { @@ -139,4 +140,20 @@ public class SearchMethodBinding extends BaseMethodBinding { DELETE, GET, POST, PUT } + @Override + public GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { + assert theArgs.length == myParameters.size() : "Wrong number of arguments: " + theArgs.length; + + Map args = new HashMap(); + + for (int idx = 0; idx < theArgs.length; idx++) { + Object object = theArgs[idx]; + Parameter nextParam = myParameters.get(idx); + String value = nextParam.encode(object); + args.put(nextParam.getName(), value); + } + + return new GetClientInvocation(args, getResourceName()); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 2e03db8218e..3868f075ca3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -4,5 +4,7 @@ public class Constants { public static final String CT_FHIR_XML = "application/xml+fhir"; public static final String PARAM_FORMAT = "_format"; + public static final String URL_TOKEN_HISTORY = "_history"; + public static final String CT_ATOM_XML = "application/atom+xml"; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Parameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Parameter.java index 7cd3c20c6a3..12d94a28eeb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Parameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Parameter.java @@ -1,88 +1,104 @@ package ca.uhn.fhir.rest.server; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; /** * Created by dsotnikov on 2/25/2014. */ public class Parameter { - private String name; + + private String name; + private IParamBinder parser; private boolean required; private Class type; - private IParser parser; - - public Parameter(){} + public Parameter(){} public Parameter(String name, boolean required) { this.name = name; this.required = required; } - public Class getType() { - return type; - } - - public void setType(final Class type) { - this.type = type; - if (type.getSimpleName().equals("IdentifierDt")) { - this.parser = new IParser() { - @Override - public Object parse(String theString) throws InternalErrorException { - Object dt; - try { - dt = type.newInstance(); - - Method method = dt.getClass().getMethod("setValueAsQueryToken", String.class); - method.invoke(dt, theString); - - } catch (InstantiationException e) { - throw new InternalErrorException(e); - } catch (IllegalAccessException e) { - throw new InternalErrorException(e); - } catch (NoSuchMethodException e) { - throw new InternalErrorException(e); - } catch (SecurityException e) { - throw new InternalErrorException(e); - } catch (IllegalArgumentException e) { - throw new InternalErrorException(e); - } catch (InvocationTargetException e) { - throw new InternalErrorException(e); - } - return dt; - } - }; - } else { - throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName()); - } - } - public String getName() { return name; } - public void setName(String name) { - this.name = name; + public Class getType() { + return type; } public boolean isRequired() { return required; } + public Object parse(String theString) throws InternalErrorException { + return parser.parse(theString); + } + + public void setName(String name) { + this.name = name; + } + public void setRequired(boolean required) { this.required = required; } - public Object parse(String theString) throws InternalErrorException { - return parser.parse(theString); - } + @SuppressWarnings("unchecked") + public void setType(final Class type) { + this.type = type; + if (IQueryParameterType.class.isAssignableFrom(type)) { + this.parser = new IdentifierParamBinder((Class) type); + } else { + throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName()); + } + } + + - private interface IParser + private final class IdentifierParamBinder implements IParamBinder { + private final Class myType; + + private IdentifierParamBinder(Class theType) { + myType = theType; + } + + @Override + public Object parse(String theString) throws InternalErrorException { + IQueryParameterType dt; + try { + dt = myType.newInstance(); + dt.setValueAsQueryToken(theString); + } catch (InstantiationException e) { + throw new InternalErrorException(e); + } catch (IllegalAccessException e) { + throw new InternalErrorException(e); + } catch (SecurityException e) { + throw new InternalErrorException(e); + } + return dt; + } + + @Override + public String encode(Object theString) throws InternalErrorException { + return ((IQueryParameterType)theString).getValueAsQueryToken(); + } + } + + + + private interface IParamBinder { Object parse(String theString) throws InternalErrorException; + + String encode(Object theString) throws InternalErrorException; + + } + + + + public String encode(Object theObject) throws InternalErrorException { + return parser.encode(theObject); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 682a327ad73..df849858046 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -26,7 +26,7 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.common.BaseMethodBinding; import ca.uhn.fhir.rest.common.SearchMethodBinding; -import ca.uhn.fhir.rest.server.exceptions.AbstractResponseException; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.MethodNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -174,7 +174,7 @@ public abstract class RestfulServer extends HttpServlet { } // resourceMethod.get - } catch (AbstractResponseException e) { + } catch (BaseServerResponseException e) { if (e instanceof InternalErrorException) { ourLog.error("Failure during REST processing", e); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java.orig b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java.orig deleted file mode 100644 index 55e899d66f6..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java.orig +++ /dev/null @@ -1,331 +0,0 @@ -package ca.uhn.fhir.server; - -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; -import java.util.ArrayList; -import java.util.Collection; -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 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.server.exceptions.AbstractResponseException; -import ca.uhn.fhir.server.exceptions.InternalErrorException; -import ca.uhn.fhir.server.exceptions.MethodNotFoundException; -import ca.uhn.fhir.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.server.operations.DELETE; -import ca.uhn.fhir.server.operations.GET; -import ca.uhn.fhir.server.operations.POST; -import ca.uhn.fhir.server.operations.PUT; - -public abstract class RestfulServer extends HttpServlet { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class); - - private static final long serialVersionUID = 1L; - -<<<<<<< HEAD:hapi-fhir-base/src/main/java/ca/uhn/fhir/server/RestfulServer.java - private FhirContext myFhirContext; - - private Map, IResourceProvider> myTypeToProvider = new HashMap, IResourceProvider>(); -======= - private Map, IResourceProvider> myTypeToProvider = new HashMap, IResourceProvider>(); - - private FhirContext myFhirContext; - - public abstract Collection> getResourceProviders(); - - @Override - public void init() throws ServletException { - try { - ourLog.info("Initializing HAPI FHIR restful server"); - - Collection> 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()); ->>>>>>> b15504ab6af00727419d4888cd3a1c5215f5b5e3:hapi-fhir-base/src/main/java/ca/uhn/fhir/ws/RestfulServer.java - - // map of request handler resources keyed by resource name - private Map resources = new HashMap(); - - private boolean addResourceMethod(Resource resource, Method method) throws Exception { - - ResourceMethod rm = new ResourceMethod(); - - // each operation name must have a request type annotation and be unique - if (null != method.getAnnotation(GET.class)) { - rm.setRequestType(ResourceMethod.RequestType.GET); - } else if (null != method.getAnnotation(PUT.class)) { - rm.setRequestType(ResourceMethod.RequestType.PUT); - } else if (null != method.getAnnotation(POST.class)) { - 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; - } - - @SuppressWarnings("unused") - private EncodingUtil determineResponseEncoding(Map theParams) { - String[] format = theParams.remove(Constants.PARAM_FORMAT); - // TODO: handle this once we support JSON - return EncodingUtil.XML; - } - - @Override - protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(ResourceMethod.RequestType.DELETE, request, response); - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(ResourceMethod.RequestType.GET, request, response); - } - - @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(ResourceMethod.RequestType.POST, request, response); - } - - @Override - protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - handleRequest(ResourceMethod.RequestType.PUT, request, response); - } - - private void findResourceMethods(IResourceProvider theProvider) throws Exception { - - Class resourceType = theProvider.getResourceType(); - RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceType); - - Resource r = new Resource(); - r.setResourceProvider(theProvider); - r.setResourceName(definition.getName()); - resources.put(definition.getName(), r); - - Class clazz = theProvider.getClass(); - for (Method m : clazz.getDeclaredMethods()) { - if (Modifier.isPublic(m.getModifiers())) { - - boolean foundMethod = addResourceMethod(r, m); - if (foundMethod) { - ourLog.debug("found handler: " + m.getName()); - } - } - } - } - - public abstract Collection> getResourceProviders(); - - protected void handleRequest(ResourceMethod.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - try { - String resourceName = null; - Long identity = null; - - Map params = new HashMap(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 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> 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 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(); - - } - - /** - * Recursive method used to find all classes in a given directory and - * subdirs. - * - * @param directory - * The base directory - * @param packageName - * The package name for classes found inside the base directory - * @return The classes - * @throws ClassNotFoundException - */ - private static List> findClasses(File directory, String packageName) throws ClassNotFoundException { - List> classes = new ArrayList>(); - if (!directory.exists()) { - return classes; - } - File[] files = directory.listFiles(); - for (File file : files) { - if (file.isDirectory()) { - assert !file.getName().contains("."); - classes.addAll(findClasses(file, packageName + "." + file.getName())); - } else if (file.getName().endsWith(".class")) { - classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); - } - } - return classes; - } - - private static List> getClasses(String packageName) throws ClassNotFoundException, IOException { - - if (null == packageName) - throw new ClassNotFoundException("package name must be specified for JSON operations"); - - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - assert classLoader != null; - String path = packageName.replace('.', '/'); - Enumeration resources = classLoader.getResources(path); - List dirs = new ArrayList(); - while (resources.hasMoreElements()) { - URL resource = resources.nextElement(); - dirs.add(new File(resource.getFile())); - } - - ArrayList> classes = new ArrayList>(); - for (File directory : dirs) { - classes.addAll(findClasses(directory, packageName)); - } - return classes; - } -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AbstractResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java similarity index 76% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AbstractResponseException.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java index f5165d78ce2..9bce258a9d5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AbstractResponseException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.rest.server.exceptions; -public abstract class AbstractResponseException extends Exception { +public abstract class BaseServerResponseException extends Exception { private static final long serialVersionUID = 1L; @@ -14,7 +14,7 @@ public abstract class AbstractResponseException extends Exception { * @param theMessage * The message */ - public AbstractResponseException(int theStatusCode, String theMessage) { + public BaseServerResponseException(int theStatusCode, String theMessage) { super(theMessage); myStatusCode = theStatusCode; } @@ -28,7 +28,7 @@ public abstract class AbstractResponseException extends Exception { * The message * @param theCause The cause */ - public AbstractResponseException(int theStatusCode, String theMessage, Throwable theCause) { + public BaseServerResponseException(int theStatusCode, String theMessage, Throwable theCause) { super(theMessage, theCause); myStatusCode = theStatusCode; } @@ -41,7 +41,7 @@ public abstract class AbstractResponseException extends Exception { * @param theCause * The underlying cause exception */ - public AbstractResponseException(int theStatusCode, Throwable theCause) { + public BaseServerResponseException(int theStatusCode, Throwable theCause) { super(theCause.toString(), theCause); myStatusCode = theStatusCode; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InternalErrorException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InternalErrorException.java index 5048a78d110..bcef3f570a0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InternalErrorException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InternalErrorException.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.rest.server.exceptions; -public class InternalErrorException extends AbstractResponseException { +public class InternalErrorException extends BaseServerResponseException { private static final long serialVersionUID = 1L; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java index 8a26a81034d..a9f00026dd8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/InvalidRequestException.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.rest.server.exceptions; -public class InvalidRequestException extends AbstractResponseException { +public class InvalidRequestException extends BaseServerResponseException { private static final long serialVersionUID = 1L; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/MethodNotFoundException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/MethodNotFoundException.java index 00356c9c288..ed536853b39 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/MethodNotFoundException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/MethodNotFoundException.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.rest.server.exceptions; /** * Created by dsotnikov on 2/27/2014. */ -public class MethodNotFoundException extends AbstractResponseException { +public class MethodNotFoundException extends BaseServerResponseException { private static final long serialVersionUID = 1L; public MethodNotFoundException(String error) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java index 5799a604433..c7ea30f8aa2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server.exceptions; import ca.uhn.fhir.model.primitive.IdDt; -public class ResourceNotFoundException extends AbstractResponseException { +public class ResourceNotFoundException extends BaseServerResponseException { public ResourceNotFoundException(IdDt theId) { super(404, "Resource " + (theId != null ? theId.getValue() : "") + " is not known"); diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index 584deac2df6..971349ca58f 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -20,10 +20,20 @@ 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.model.dstu.resource.Observation; +import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.ValueSet; public class XmlParserTest { + @Test + public void testParseBundleLarge() throws IOException { + + String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/atom-document-large.xml")); + IParser p = new FhirContext(Patient.class).newXmlParser(); + Bundle bundle = p.parseBundle(msg); + + } + @Test public void testParseBundle() { @@ -85,7 +95,7 @@ public class XmlParserTest { ""; //@formatter:on - XmlParser p = new FhirContext(ValueSet.class).newXmlParser(); + IParser p = new FhirContext(ValueSet.class).newXmlParser(); Bundle bundle = p.parseBundle(msg); assertEquals("FHIR Core Valuesets", bundle.getTitle().getValue()); @@ -105,7 +115,7 @@ public class XmlParserTest { @Test public void testLoadAndEncodeExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException { FhirContext ctx = new FhirContext(ResourceWithExtensionsA.class); - XmlParser p = new XmlParser(ctx); + IParser p = new XmlParser(ctx); //@formatter:off String msg = "\n" + @@ -169,7 +179,7 @@ public class XmlParserTest { public void testLoadObservation() throws ConfigurationException, DataFormatException, IOException { FhirContext ctx = new FhirContext(Observation.class); - XmlParser p = new XmlParser(ctx); + IParser p = new XmlParser(ctx); IResource resource = p.parseResource(IOUtils.toString(XmlParserTest.class.getResourceAsStream("/observation-example-eeg.xml"))); diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java index 8ce8648f715..141aebf5e0b 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java @@ -19,4 +19,6 @@ public interface ITestClient extends IRestfulClient { @Search(value=Patient.class) Bundle findPatientByLastName(@Required(name = Patient.SP_FAMILY) IdentifierDt theId); + + } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/Tester.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/Tester.java new file mode 100644 index 00000000000..01168b0952f --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/Tester.java @@ -0,0 +1,32 @@ +package ca.uhn.fhir.rest.client; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.dstu.composite.IdentifierDt; +import ca.uhn.fhir.model.dstu.resource.Patient; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; + +public class Tester { + + public static final void main(String[] args) { + try { + + FhirContext ctx = new FhirContext(Patient.class); + RestfulClientFactory factory = ctx.newClientFactory(); + ITestClient client = factory.newClient(ITestClient.class, "http://spark.furore.com/fhir/"); + +// Patient patient = client.getPatientById(new IdDt("1")); +// System.out.println(ctx.newXmlParser().encodeResourceToString(patient)); + + Patient patient2 = client.findPatientByMrn(new IdentifierDt("http://orionhealth.com/mrn", "PRP1660")); + System.out.println(ctx.newXmlParser().encodeResourceToString(patient2)); + + } catch (NonFhirResponseException e) { + e.printStackTrace(); + System.out.println(e.getResponseText()); + } + + } + +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java index 5d95afeb78a..205feb6584e 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java @@ -12,6 +12,7 @@ import org.junit.Test; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.rest.common.SearchMethodBinding; +import ca.uhn.fhir.rest.common.BaseMethodBinding.MethodReturnTypeEnum; import ca.uhn.fhir.rest.server.Parameter; public class ResourceMethodTest { @@ -20,7 +21,7 @@ public class ResourceMethodTest { @Before public void before() throws NoSuchMethodException, SecurityException { - rm = new SearchMethodBinding(Patient.class, ResourceMethodTest.class.getMethod("before")); + rm = new SearchMethodBinding(MethodReturnTypeEnum.RESOURCE, Patient.class, ResourceMethodTest.class.getMethod("before")); } @Test diff --git a/hapi-fhir-base/src/test/resources/atom-document-large.xml b/hapi-fhir-base/src/test/resources/atom-document-large.xml new file mode 100644 index 00000000000..123fd8cee76 --- /dev/null +++ b/hapi-fhir-base/src/test/resources/atom-document-large.xml @@ -0,0 +1,838 @@ + + Search on resources in collection 'Patient' + urn:uuid:8b8428a5-ba56-4fc7-b176-36d13346c1ad + 2014-03-06T22:09:58.9121174Z + + Spark MatchBox Search Engine + + 676 + + + + + + + Patient resource with id 3216379 + http://spark.furore.com/fhir/Patient/3216379 + 2014-03-06T18:07:52.2209263Z + 2014-03-06T18:07:52.2209263Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + + + + + Patient resource with id 3212416 + http://spark.furore.com/fhir/Patient/3212416 + 2014-01-18T19:48:05.7634661Z + 2014-01-18T19:48:05.747866Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + + + Patient resource with id 3212417 + http://spark.furore.com/fhir/Patient/3212417 + 2014-01-18T19:48:40.9572917Z + 2014-01-18T19:48:40.9416916Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + + + Patient resource with id 3212429 + http://spark.furore.com/fhir/Patient/3212429 + 2014-01-18T19:52:14.7410621Z + 2014-01-18T19:52:14.6786617Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + + + http://spark.furore.com/fhir/Patient/3212430 + 2014-01-18T19:52:16.6442743Z + + + + + + + + + + + + + + + + + + + + + http://spark.furore.com/fhir/Patient/3212433 + 2014-01-18T19:52:17.5490801Z + + + + + + + + + + + + + + + + + + + + + Patient resource with id 3212435 + http://spark.furore.com/fhir/Patient/3212435 + 2014-01-18T19:52:18.7658879Z + 2014-01-18T19:52:18.7658879Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + + + Patient Resource + http://spark.furore.com/fhir/Patient/3212446 + 2014-01-18T19:55:50.8346474Z + + + + + + +
Parkerson, Parkie
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+ +
Parkerson, Parkie
+
+
+ + Patient Resource + http://spark.furore.com/fhir/Patient/3212492 + 2014-01-18T22:09:21.1526936Z + + + + + + +
Parkerson, Parkie
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+ +
Parkerson, Parkie
+
+
+ + patient record + http://spark.furore.com/fhir/Patient/3212516 + 2014-01-18T22:30:06.8604936Z + + + + + + + +
Person DOE, John, M, dob: 27/05/1956
+
+ + + + + + + + + + + +
+
+ +
Person DOE, John, M, dob: 27/05/1956
+
+
+ + Patient Resource + http://spark.furore.com/fhir/Patient/3212576 + 2014-01-18T22:47:32.8716936Z + + + + + + +
Parkerson, Parkie
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+ +
Parkerson, Parkie
+
+
+ + Patient Resource + http://spark.furore.com/fhir/Composition/3212638 + 2014-01-18T23:16:43.9026936Z + + + + + + +
Parkerson, Parkie
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+ +
Parkerson, Parkie
+
+
+ + Patient Resource + http://spark.furore.com/fhir/Patient/3212673 + 2014-01-18T23:43:42.9527548Z + + + + + + +
Parkerson, Parkie
+
+ + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+ +
Parkerson, Parkie
+
+
+ + Patient resource with id 3212861 + http://spark.furore.com/fhir/Patient/3212861 + 2014-01-19T15:06:57.8105834Z + 2014-01-19T15:06:57.8105834Z + + (unauthenticated) + + + + + + + +
+ BizTalk Test +
+
+ + + + + + + + + + + + +
+ + +
+ + + + +
+
+ +
+ BizTalk Test +
+
+
+ + Patient resource with id 3213112 + http://spark.furore.com/fhir/Patient/3213112 + 2014-01-19T16:17:08.4218784Z + 2014-01-19T16:17:08.4218784Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+
+
+ + Patient resource with id 3213341 + http://spark.furore.com/fhir/Patient/3213341 + 2014-01-19T16:29:22.0804724Z + 2014-01-19T16:29:22.0648726Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+
+ + Patient resource with id 3213417 + http://spark.furore.com/fhir/Patient/3213417 + 2014-01-19T16:33:17.1694584Z + 2014-01-19T16:33:17.1538586Z + + (unauthenticated) + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+
+ + Patient + http://spark.furore.com/fhir/Patient/3213445 + 2014-01-19T16:39:43.3113078Z + + + + + + + Isabella Isa Jones + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + +
+
+ + Isabella Isa Jones + +
+ + Patient resource with id 3213586 + http://spark.furore.com/fhir/Patient/3213586 + 2014-01-19T16:41:54.9432766Z + 2014-01-19T16:41:54.9432766Z + + (unauthenticated) + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+
+ + Patient + http://spark.furore.com/fhir/Patient/3213739 + 2014-01-19T16:51:12.1772766Z + + + + + + + Isabella Isa Jones + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + +
+
+ + Isabella Isa Jones + +
+
\ No newline at end of file diff --git a/hapi-fhir-base/src/test/resources/logback-test.xml b/hapi-fhir-base/src/test/resources/logback-test.xml index 88219d4e47b..8a5cde21149 100644 --- a/hapi-fhir-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-base/src/test/resources/logback-test.xml @@ -10,6 +10,10 @@ + + + + diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java index 3b96bd31889..840c5691c94 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ValueSetGenerator.java @@ -28,7 +28,7 @@ import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.dstu.resource.ValueSet; import ca.uhn.fhir.model.dstu.resource.ValueSet.DefineConcept; -import ca.uhn.fhir.parser.XmlParser; +import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.tinder.model.ValueSetTm; import ca.uhn.fhir.tinder.model.ValueSetTm.Code; @@ -53,7 +53,7 @@ public class ValueSetGenerator { } public void parse() throws FileNotFoundException, IOException { - XmlParser newXmlParser = new FhirContext(ValueSet.class).newXmlParser(); + IParser newXmlParser = new FhirContext(ValueSet.class).newXmlParser(); ourLog.info("Parsing built-in ValueSets"); String vs = IOUtils.toString(ValueSetGenerator.class.getResourceAsStream("/vs/all-valuesets-bundle.xml")); diff --git a/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm b/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm index 87ecc0aa956..e8c5246f411 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/dt_composite.vm @@ -24,7 +24,31 @@ import ${packageBase}.resource.*; *

*/ @DatatypeDef(name="${className}") -public class ${className}Dt extends BaseElement implements ICompositeDatatype { +public class ${className}Dt extends BaseElement implements ICompositeDatatype +#if ( ${className} == "Identifier" ) +, IQueryParameter +#end +{ + +######################### +### Type-specific constructors +######################### +#if ( ${className} == "Identifier" ) + /** + * Creates a new identifier + */ + public IdentifierDt() { + // nothing + } + + /** + * Creates a new identifier with the given system and value + */ + public IdentifierDt(String theSystem, String theValue) { + setSystem(theSystem); + setValue(theValue); + } +#end #childExtensionFields( $childExtensionTypes ) #childVars( $children ) @@ -48,13 +72,21 @@ public class ${className}Dt extends BaseElement implements ICompositeDatatype { } /** - * Sets the value of this IdentifierDt using the token format. This - * format is used in HTTP queries as a parameter format. - * - * @see See FHIR specification - * 2.2.2 Search Parameter Types - * for information on the token format + * {@inheritDoc} */ + @Override + public String getValueAsQueryToken() { + if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) { + return getSystem().getValueAsString() + '|' + getValue().getValueAsString(); + } else { + return getValue().getValueAsString(); + } + } + + /** + * {@inheritDoc} + */ + @Override public void setValueAsQueryToken(String theParameter) { int barIndex = theParameter.indexOf('|'); if (barIndex != -1) {