Use corrrect types from client

This commit is contained in:
James Agnew 2016-07-27 18:15:09 -04:00
parent 41b58108f2
commit dd8b1cd979
14 changed files with 316 additions and 283 deletions

View File

@ -26,26 +26,19 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>2.0-SNAPSHOT</version>
<exclusions>
<!--
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
-->
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@ -90,19 +83,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Android does not come with the Servlet API bundled, and MethodUtil
requires it.
We provide a dummy implementation of servlet api to reduce size
and prevent from rewriting the BaseMethodBinding and friends.
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@ -118,6 +98,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<!--
<classpathDependencyScopeExclude>compile+runtime+test+provided</classpathDependencyScopeExclude>
<additionalClasspathElements>
<additionalClasspathElement>${project.build.directory}/hapi-fhir-android-${project.version}-dstu2.jar</additionalClasspathElement>
@ -128,6 +109,7 @@
<classpathDependencyExclude>javax.json:*</classpathDependencyExclude>
<classpathDependencyExclude>org.glassfish:javax.json</classpathDependencyExclude>
</classpathDependencyExcludes>
-->
</configuration>
<executions>
<execution>
@ -148,18 +130,6 @@
<artifactSet>
<includes combine.children="append">
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<include>org.codehaus.woodstox:stax2-api</include>
<include>org.glassfish:javax.json</include>
<!--
<include>net.sourceforge.cobertura:cobertura</include>
<include>org.apache.commons:*</include>
<include>org.apache.httpcomponents:*</include>
<include>commons-codec:commons-codec</include>
-->
<include>javax.servlet:javax.servlet-api</include>
</includes>
</artifactSet>
<relocations>

View File

@ -1,15 +1,9 @@
package ca.uhn.fhir.android;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Observable;
import java.util.Set;
import java.util.TreeSet;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@ -18,10 +12,7 @@ import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.junit.BeforeClass;
import org.junit.Test;
import com.ctc.wstx.stax.WstxInputFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
@ -39,7 +30,11 @@ public class BuiltJarIT {
@Test
public void testParserXml() throws Exception {
// fail("*******: " + WstxInputFactory.class.getProtectionDomain().getCodeSource().getLocation().toString());
try {
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
} catch (ClassNotFoundException e) {
return;
}
FhirContext ctx = FhirContext.forDstu2();
@ -54,8 +49,6 @@ public class BuiltJarIT {
@Test
public void testParserJson() {
ourLog.info("AAAAA");
ourLog.info("AAAAA");
FhirContext ctx = FhirContext.forDstu2();
@ -72,7 +65,6 @@ public class BuiltJarIT {
QuantityDt dt = (QuantityDt) p2.getValue();
dt.getComparatorElement().getValueAsEnum();
QuantityCompararatorEnum.GREATERTHAN.name();
}
/**

View File

@ -168,15 +168,6 @@ public abstract class BaseClient implements IRestfulClient {
return myLastResponseBody;
}
/**
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
* servers which might implement it).
*/
public Boolean getPrettyPrint() {
return myPrettyPrint;
}
/**
* {@inheritDoc}
*/
@ -319,22 +310,6 @@ public abstract class BaseClient implements IRestfulClient {
if (handlesBinary.isBinary()) {
InputStream reader = response.readEntity();
try {
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
byte[] responseBytes = IOUtils.toByteArray(reader);
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = null;
}
String message = "HTTP " + response.getStatus() + " " + response.getStatusInfo();
if (theLogRequestAndResponse) {
ourLog.info("Client response: {} - {} bytes", message, responseBytes.length);
} else {
ourLog.trace("Client response: {} - {} bytes", message, responseBytes.length);
}
reader = new ByteArrayInputStream(responseBytes);
}
return handlesBinary.invokeClient(mimeType, reader, response.getStatus(), headers);
} finally {
IOUtils.closeQuietly(reader);
@ -441,20 +416,6 @@ public abstract class BaseClient implements IRestfulClient {
myKeepResponses = theKeepResponses;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastResponse(IHttpResponse theLastResponse) {
myLastResponse = theLastResponse;
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastResponseBody(String theLastResponseBody) {
myLastResponseBody = theLastResponseBody;
}
/**
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other

View File

@ -116,7 +116,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) {
protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, List<Class<? extends IBaseResource>> thePreferTypes) {
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
if (encoding == null) {
NonFhirResponseException ex = NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -125,6 +125,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
IParser parser = encoding.newParser(getContext());
parser.setPreferTypes(thePreferTypes);
return parser;
}
@ -325,7 +328,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
ex = new PreconditionFailedException("Server responded with HTTP 412");
break;
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode, null);
// TODO: handle if something other than OO comes back
BaseOperationOutcome operationOutcome = (BaseOperationOutcome) parser.parseResource(theResponseReader);
ex = new UnprocessableEntityException(myContext, operationOutcome);

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.method;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -25,15 +25,7 @@ import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.*;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
@ -91,7 +83,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
private MethodReturnTypeEnum myMethodReturnType;
private Class<?> myResourceListCollectionType;
private String myResourceName;
private Class<? extends IResource> myResourceType;
private Class<? extends IBaseResource> myResourceType;
private List<Class<? extends IBaseResource>> myPreferTypesList;
@SuppressWarnings("unchecked")
public BaseResourceReturningMethodBinding(Class<?> theReturnResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
@ -137,6 +130,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
}
}
}
myPreferTypesList = createPreferTypesList();
}
public MethodReturnTypeEnum getMethodReturnType() {
@ -157,11 +152,11 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
@Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) {
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode, myPreferTypesList);
switch (getReturnType()) {
case BUNDLE: {
Bundle dstu1bundle = null;
IBaseBundle dstu2bundle = null;
List<? extends IBaseResource> listOfResources = null;
@ -177,7 +172,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
dstu2bundle = (IBaseBundle) parser.parseResource(type, theResponseReader);
listOfResources = BundleUtil.toListOfResources(getContext(), dstu2bundle);
}
switch (getMethodReturnType()) {
case BUNDLE:
return dstu1bundle;
@ -185,7 +180,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
return dstu2bundle;
case LIST_OF_RESOURCES:
if (myResourceListCollectionType != null) {
for (Iterator<? extends IBaseResource> iter = listOfResources.iterator(); iter.hasNext(); ) {
for (Iterator<? extends IBaseResource> iter = listOfResources.iterator(); iter.hasNext();) {
IBaseResource next = iter.next();
if (!myResourceListCollectionType.isAssignableFrom(next.getClass())) {
ourLog.debug("Not returning resource of type {} because it is not a subclass or instance of {}", next.getClass(), myResourceListCollectionType);
@ -243,6 +238,19 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
throw new IllegalStateException("Should not get here!");
}
@SuppressWarnings("unchecked")
private List<Class<? extends IBaseResource>> createPreferTypesList() {
List<Class<? extends IBaseResource>> preferTypes = null;
if (myResourceListCollectionType != null && IBaseResource.class.isAssignableFrom(myResourceListCollectionType)) {
preferTypes = new ArrayList<Class<? extends IBaseResource>>(1);
preferTypes.add((Class<? extends IBaseResource>) myResourceListCollectionType);
// } else if (myResourceType != null) {
// preferTypes = new ArrayList<Class<? extends IBaseResource>>(1);
// preferTypes.add((Class<? extends IBaseResource>) myResourceListCollectionType);
}
return preferTypes;
}
@Override
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
@ -250,7 +258,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
if (responseObject.getResource() != null) {
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getResource());
@ -258,12 +266,12 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
return null;
}
}
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, null,
theRequest.isRespondGzip(), isAddContentLocationHeader());
return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, null, theRequest.isRespondGzip(),
isAddContentLocationHeader());
} else {
// Is this request coming from a browser
String uaHeader = theRequest.getHeader("user-agent");
@ -271,7 +279,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
if (uaHeader != null && uaHeader.contains("Mozilla")) {
requestIsBrowser = true;
}
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getDstu1Bundle());
@ -356,7 +364,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
responseObject = new ResourceOrDstu1Bundle(resource);
break;
} else {
Set<Include> includes = getRequestIncludesFromParams(params);
@ -364,20 +372,21 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
if (count == null) {
count = result.preferredPageSize();
}
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
if (offsetI == null || offsetI < 0) {
offsetI = 0;
}
int start = Math.max(0, Math.min(offsetI, result.size() - 1));
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) && responseEncoding != null ? responseEncoding.getEncoding() : null;
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
bundleFactory.initializeBundleFromBundleProvider(theServer, result, linkEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, null, getResponseBundleType(), includes);
bundleFactory.initializeBundleFromBundleProvider(theServer, result, linkEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, null, getResponseBundleType(),
includes);
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
responseObject = new ResourceOrDstu1Bundle(bundle);

View File

@ -153,7 +153,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
@Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
if (theResponseStatusCode == Constants.STATUS_HTTP_200_OK) {
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode, null);
TagList retVal = parser.parseTagList(theResponseReader);
return retVal;
} else {

View File

@ -8,80 +8,28 @@ import java.io.PushbackReader;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.At;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Elements;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.ServerBase;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.annotation.TagListParam;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.OperationParameter.IOperationParamConverter;
import ca.uhn.fhir.rest.param.CollectionBinder;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.NumberAndListParam;
import ca.uhn.fhir.rest.param.QuantityAndListParam;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TransactionParameter;
import ca.uhn.fhir.rest.param.UriAndListParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
@ -121,8 +69,17 @@ public class MethodUtil {
private static final String LABEL = "label=\"";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MethodUtil.class);
private static final String SCHEME = "scheme=\"";
private static final Set<String> ourServletRequestTypes = new HashSet<String>();
private static final Set<String> ourServletResponseTypes = new HashSet<String>();
static {
ourServletRequestTypes.add("javax.servlet.ServletRequest");
ourServletResponseTypes.add("javax.servlet.ServletResponse");
ourServletRequestTypes.add("javax.servlet.http.HttpServletRequest");
ourServletResponseTypes.add("javax.servlet.http.HttpServletResponse");
}
static void addTagsToPostOrPut(FhirContext theContext, IBaseResource resource, BaseHttpClientInvocation retVal) {
if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
@ -391,16 +348,22 @@ public class MethodUtil {
throw new ConfigurationException("Argument #" + paramIndex + " of Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is of an invalid generic type (can not be a collection of a collection of a collection)");
}
}
if (parameterType.equals(HttpServletRequest.class) || parameterType.equals(ServletRequest.class)) {
/*
* Note: for the frst two here, we're using strings instead of static binding
* so that we don't need the java.servlet JAR on the classpath in order to use
* this class
*/
if (ourServletRequestTypes.contains(parameterType.getName())) {
param = new ServletRequestParameter();
} else if (ourServletResponseTypes.contains(parameterType.getName())) {
param = new ServletResponseParameter();
} else if (parameterType.equals(RequestDetails.class)) {
param = new RequestDetailsParameter();
} else if (parameterType.equals(IRequestOperationCallback.class)) {
param = new RequestOperationCallbackParameter();
} else if (parameterType.equals(SummaryEnum.class)) {
param = new SummaryEnumParameter();
} else if (parameterType.equals(HttpServletResponse.class) || parameterType.equals(ServletResponse.class)) {
param = new ServletResponseParameter();
} else {
for (int i = 0; i < annotations.length && param == null; i++) {
Annotation nextAnnotation = annotations[i];

View File

@ -163,7 +163,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
@Override
public HttpGetClientInvocation invokeClient(Object[] theArgs) {
HttpGetClientInvocation retVal;
IdDt id = ((IdDt) theArgs[myIdIndex]);
IIdType id = ((IIdType) theArgs[myIdIndex]);
if (myVersionIdIndex == null) {
String resourceName = getResourceName();
if (id.hasVersionIdPart()) {

View File

@ -56,12 +56,12 @@ import ca.uhn.fhir.util.jar.IDependencyLog;
* This class contains code adapted from the Apache Axiom project.
*/
public class XmlUtil {
private static XMLOutputFactory ourFragmentOutputFactory;
private static volatile boolean ourHaveLoggedStaxImplementation;
private static volatile XMLInputFactory ourInputFactory;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
private static volatile XMLOutputFactory ourOutputFactory;
private static XMLOutputFactory ourFragmentOutputFactory;
private static Throwable ourNextException;
private static volatile XMLOutputFactory ourOutputFactory;
private static final Map<String, Integer> VALID_ENTITY_NAMES;
private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver();
@ -1519,6 +1519,42 @@ public class XmlUtil {
VALID_ENTITY_NAMES = Collections.unmodifiableMap(validEntityNames);
}
private static XMLOutputFactory createOutputFactory() throws FactoryConfigurationError {
try {
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLOutputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
} catch (ClassNotFoundException e) {
// ok
}
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
if (!ourHaveLoggedStaxImplementation) {
logStaxImplementation(outputFactory.getClass());
}
/*
* Note that these properties are Woodstox specific and they cause a crash in environments where SJSXP is
* being used (e.g. glassfish) so we don't set them there.
*/
try {
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
if (outputFactory instanceof WstxOutputFactory) {
outputFactory.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
}
} catch (ClassNotFoundException e) {
ourLog.debug("WstxOutputFactory (Woodstox) not found on classpath");
}
return outputFactory;
}
public static XMLEventWriter createXmlFragmentWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory outputFactory = getOrCreateFragmentOutputFactory();
XMLEventWriter retVal = outputFactory.createXMLEventWriter(theWriter);
return retVal;
}
public static XMLEventReader createXmlReader(Reader reader) throws FactoryConfigurationError, XMLStreamException {
throwUnitTestExceptionIfConfiguredToDoSo();
@ -1529,16 +1565,6 @@ public class XmlUtil {
return er;
}
private static void throwUnitTestExceptionIfConfiguredToDoSo() throws FactoryConfigurationError, XMLStreamException {
if (ourNextException != null) {
if (ourNextException instanceof FactoryConfigurationError) {
throw ((FactoryConfigurationError)ourNextException);
} else {
throw (XMLStreamException)ourNextException;
}
}
}
public static XMLStreamWriter createXmlStreamWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
throwUnitTestExceptionIfConfiguredToDoSo();
@ -1547,15 +1573,20 @@ public class XmlUtil {
return retVal;
}
public static XMLEventWriter createXmlFragmentWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory outputFactory = getOrCreateFragmentOutputFactory();
public static XMLEventWriter createXmlWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory outputFactory = getOrCreateOutputFactory();
XMLEventWriter retVal = outputFactory.createXMLEventWriter(theWriter);
return retVal;
}
public static XMLEventWriter createXmlWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory outputFactory = getOrCreateOutputFactory();
XMLEventWriter retVal = outputFactory.createXMLEventWriter(theWriter);
private static XMLOutputFactory getOrCreateFragmentOutputFactory() throws FactoryConfigurationError {
XMLOutputFactory retVal = ourFragmentOutputFactory;
if (retVal == null) {
retVal = createOutputFactory();
retVal.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
ourFragmentOutputFactory = retVal;
return retVal;
}
return retVal;
}
@ -1614,17 +1645,6 @@ public class XmlUtil {
return ourInputFactory;
}
private static XMLOutputFactory getOrCreateFragmentOutputFactory() throws FactoryConfigurationError {
XMLOutputFactory retVal = ourFragmentOutputFactory;
if (retVal == null) {
retVal = createOutputFactory();
retVal.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, Boolean.TRUE);
ourFragmentOutputFactory = retVal;
return retVal;
}
return retVal;
}
private static XMLOutputFactory getOrCreateOutputFactory() throws FactoryConfigurationError {
if (ourOutputFactory == null) {
@ -1633,36 +1653,6 @@ public class XmlUtil {
return ourOutputFactory;
}
private static XMLOutputFactory createOutputFactory() throws FactoryConfigurationError {
try {
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
Class.forName("ca.uhn.fhir.repackage.javax.xml.stream.XMLOutputFactory");
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
} catch (ClassNotFoundException e) {
// ok
}
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
if (!ourHaveLoggedStaxImplementation) {
logStaxImplementation(outputFactory.getClass());
}
/*
* Note that these properties are Woodstox specific and they cause a crash in environments where SJSXP is
* being used (e.g. glassfish) so we don't set them there.
*/
try {
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
if (outputFactory instanceof WstxOutputFactory) {
outputFactory.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
}
} catch (ClassNotFoundException e) {
ourLog.debug("WstxOutputFactory (Woodstox) not found on classpath");
}
return outputFactory;
}
private static void logStaxImplementation(Class<?> theClass) {
IDependencyLog logger = DependencyLogFactory.createJarLogger();
if (logger != null) {
@ -1671,6 +1661,23 @@ public class XmlUtil {
ourHaveLoggedStaxImplementation = true;
}
/**
* FOR UNIT TESTS ONLY - Throw this exception for the next operation
*/
static void setThrowExceptionForUnitTest(Throwable theException) {
ourNextException = theException;
}
private static void throwUnitTestExceptionIfConfiguredToDoSo() throws FactoryConfigurationError, XMLStreamException {
if (ourNextException != null) {
if (ourNextException instanceof FactoryConfigurationError) {
throw ((FactoryConfigurationError)ourNextException);
} else {
throw (XMLStreamException)ourNextException;
}
}
}
private static final class ExtendedEntityReplacingXmlResolver implements XMLResolver {
@Override
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) throws XMLStreamException {
@ -1735,11 +1742,4 @@ public class XmlUtil {
}
/**
* FOR UNIT TESTS ONLY - Throw this exception for the next operation
*/
static void setThrowExceptionForUnitTest(Throwable theException) {
ourNextException = theException;
}
}

View File

@ -38,19 +38,19 @@ import ca.uhn.fhir.util.TestUtil;
public class BinaryClientTest {
private FhirContext ctx;
private HttpClient httpClient;
private FhirContext mtCtx;
private HttpClient myHttpClient;
private HttpResponse httpResponse;
// atom-document-large.xml
@Before
public void before() {
ctx = FhirContext.forDstu1();
mtCtx = FhirContext.forDstu1();
httpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ctx.getRestfulClientFactory().setHttpClient(httpClient);
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
mtCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
mtCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@ -58,12 +58,12 @@ public class BinaryClientTest {
@Test
public void testRead() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(myHttpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", "foo/bar"));
when(httpResponse.getEntity().getContent()).thenReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 }));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
IClient client = mtCtx.newRestfulClient(IClient.class, "http://foo");
Binary resp = client.read(new IdDt("http://foo/Patient/123"));
assertEquals(HttpGet.class, capt.getValue().getClass());
@ -81,12 +81,12 @@ public class BinaryClientTest {
res.setContentType("text/plain");
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(myHttpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML));
when(httpResponse.getEntity().getContent()).thenReturn(new ByteArrayInputStream(new byte[] {}));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
IClient client = mtCtx.newRestfulClient(IClient.class, "http://foo");
client.create(res);
assertEquals(HttpPost.class, capt.getValue().getClass());

View File

@ -74,11 +74,14 @@ public class LoggingInterceptorTest {
public void testLogger() throws Exception {
System.out.println("Starting testLogger");
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
client.registerInterceptor(new LoggingInterceptor(true));
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
LoggingInterceptor interceptor = new LoggingInterceptor(true);
client.registerInterceptor(interceptor);
Patient patient = client.read(Patient.class, "1");
assertFalse(patient.getIdentifierFirstRep().isEmpty());
verify(myMockAppender, atLeastOnce()).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
verify(myMockAppender, times(1)).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
@Override
public boolean matches(final Object argument) {
String formattedMessage = ((LoggingEvent) argument).getFormattedMessage();
@ -86,6 +89,22 @@ public class LoggingInterceptorTest {
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/xml+fhir;charset=utf-8".toLowerCase());
}
}));
// Unregister the interceptor
client.unregisterInterceptor(interceptor);
patient = client.read(Patient.class, "1");
assertFalse(patient.getIdentifierFirstRep().isEmpty());
verify(myMockAppender, times(1)).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
@Override
public boolean matches(final Object argument) {
String formattedMessage = ((LoggingEvent) argument).getFormattedMessage();
System.out.println("Verifying: " + formattedMessage);
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/xml+fhir;charset=utf-8".toLowerCase());
}
}));
}
@AfterClass

View File

@ -15,11 +15,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
@ -32,16 +28,8 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.Device;
import org.hl7.fhir.dstu3.model.Encounter;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
@ -102,6 +90,62 @@ public class GenericClientDstu3Test {
return "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client; FHIR " + FhirVersionEnum.DSTU3.getFhirVersionString() + "/DSTU3; apache)";
}
@Test
public void testValidateCustomTypeFromClientSearch() throws Exception {
IParser p = ourCtx.newXmlParser();
Bundle b = new Bundle();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
b.addEntry().setResource(patient);
final String respString = p.encodeResourceToString(b);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle bundle = client.search().forResource(MyPatientWithExtensions.class).returnBundle(Bundle.class).execute();
assertEquals(1, bundle.getEntry().size());
assertEquals(MyPatientWithExtensions.class, bundle.getEntry().get(0).getResource().getClass());
}
@Test
public void testValidateCustomTypeFromClientRead() throws Exception {
IParser p = ourCtx.newXmlParser();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(patient);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
MyPatientWithExtensions read = client.read().resource(MyPatientWithExtensions.class).withId(new IdType("1")).execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", read.getText().getDivAsString());
}
private byte[] extractBodyAsByteArray(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
byte[] body = IOUtils.toByteArray(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent());
return body;

View File

@ -7,6 +7,7 @@ import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
@ -17,6 +18,8 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -31,8 +34,7 @@ import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
@ -60,6 +62,62 @@ public class NonGenericClientDstu3Test {
return body;
}
@Test
public void testValidateCustomTypeFromClientSearch() throws Exception {
IParser p = ourCtx.newXmlParser();
Bundle b = new Bundle();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
b.addEntry().setResource(patient);
final String respString = p.encodeResourceToString(b);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IClientWithCustomType client = ourCtx.newRestfulClient(IClientWithCustomType.class, "http://example.com/fhir");
List<MyPatientWithExtensions> list = client.search();
assertEquals(1, list.size());
assertEquals(MyPatientWithExtensions.class, list.get(0).getClass());
}
@Test
public void testValidateCustomTypeFromClientRead() throws Exception {
IParser p = ourCtx.newXmlParser();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(patient);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IClientWithCustomType client = ourCtx.newRestfulClient(IClientWithCustomType.class, "http://example.com/fhir");
MyPatientWithExtensions read = client.read(new IdType("1"));
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", read.getText().getDivAsString());
}
@Test
public void testValidateResourceOnly() throws Exception {
IParser p = ourCtx.newXmlParser();
@ -102,21 +160,31 @@ public class NonGenericClientDstu3Test {
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
private interface IClient extends IRestfulClient {
@Validate
MethodOutcome validate(@ResourceParam IBaseResource theResource, @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
private interface IClientWithCustomType extends IRestfulClient {
@Search
List<MyPatientWithExtensions> search();
@Read
MyPatientWithExtensions read(@IdParam IdType theId);
}
}

View File

@ -123,6 +123,10 @@
conditional deletes which did not find any matches,
per FHIR-I decision.
</action>
<action type="fix">
Client that declares explicitly that it is searching/reading/etc for
a custom type did not automatically parse into that type.
</action>
</release>
<release version="1.6" date="2016-07-07">
<action type="fix">