From 9560ab8dd2843172baf98ec6aafff4024bb69334 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Fri, 2 Oct 2015 00:13:19 +0200 Subject: [PATCH 1/7] Remove server code from android client and make android client smaller --- hapi-fhir-android/pom.xml | 45 +++++++++++++++++-- .../java/javax/servlet/ServletRequest.java | 5 +++ .../java/javax/servlet/ServletResponse.java | 5 +++ .../servlet/http/HttpServletRequest.java | 7 +++ .../servlet/http/HttpServletResponse.java | 7 +++ .../fhir/rest/server/Dstu1BundleFactory.java | 0 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java create mode 100644 hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java create mode 100644 hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java create mode 100644 hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java rename {hapi-fhir-base => hapi-fhir-structures-dstu}/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java (100%) diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 2dcd3a1932b..adadb8915db 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -75,12 +75,16 @@ - + requires it. + + We provide a dummy implementation of servlet api to reduce size. + and prevent from rewriting the BaseMethodBinding and friends. + --> + @@ -156,8 +160,13 @@ ca.uhn.hapi.fhir:hapi-fhir-base + ca/uhn/fhir/util/ITestingUiClientFactory + ca/uhn/fhir/rest/server/** **/*.java + + ca/uhn/fhir/rest/server/exceptions/** + @@ -207,8 +216,19 @@ ca.uhn.hapi.fhir:hapi-fhir-base + ca/uhn/fhir/util/ITestingUiClientFactory + ca/uhn/fhir/rest/server/** **/*.java + + ca/uhn/fhir/rest/server/exceptions/** + + + + ca.uhn.hapi.fhir:hapi-fhir-structures-dstu + + ca/uhn/fhir/rest/server/** + @@ -253,6 +273,25 @@ ca.uhn.fhir.repackage.javax.json + + + ca.uhn.hapi.fhir:hapi-fhir-base + + ca/uhn/fhir/util/ITestingUiClientFactory + ca/uhn/fhir/rest/server/** + **/*.java + + + ca/uhn/fhir/rest/server/exceptions/** + + + + ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2 + + ca/uhn/fhir/rest/server/** + + + diff --git a/hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java b/hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java new file mode 100644 index 00000000000..2292db91fac --- /dev/null +++ b/hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java @@ -0,0 +1,5 @@ +package javax.servlet; + +public class ServletRequest { + +} diff --git a/hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java b/hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java new file mode 100644 index 00000000000..efa8a9baa43 --- /dev/null +++ b/hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java @@ -0,0 +1,5 @@ +package javax.servlet; + +public class ServletResponse { + +} diff --git a/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java b/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java new file mode 100644 index 00000000000..37551bd241d --- /dev/null +++ b/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java @@ -0,0 +1,7 @@ +package javax.servlet.http; + +import javax.servlet.ServletRequest; + +public class HttpServletRequest extends ServletRequest { + +} diff --git a/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java b/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java new file mode 100644 index 00000000000..230ab022b4d --- /dev/null +++ b/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java @@ -0,0 +1,7 @@ +package javax.servlet.http; + +import javax.servlet.ServletResponse; + +public class HttpServletResponse extends ServletResponse{ + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java similarity index 100% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java rename to hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java From f4f57fd480b489637f63559ad82d0b76390bb2e5 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Sun, 4 Oct 2015 21:05:30 +0200 Subject: [PATCH 2/7] Make dependencies truly optionel + clean up / minimize android build both Phloc and Schematron was not truly optionel since they was referred from different files. So the references has been moved to a factory and ObjectUtil extended for some of the missing methods, where Schematron was used. Android pom file is cleaned so that all artifcates are build using the same base configuration and then schematron, phloc and some server components have been removed from artifact to minimize android artifacts. --- hapi-fhir-android/pom.xml | 227 ++++++++---------- .../java/ca/uhn/fhir/util/ObjectUtil.java | 14 ++ .../validation/BaseValidationContext.java | 5 +- .../ca/uhn/fhir/validation/FhirValidator.java | 23 +- .../fhir/validation/SchemaBaseValidator.java | 6 +- .../fhir/validation/ValidationContext.java | 9 +- .../schematron/SchematronProvider.java | 42 ++++ 7 files changed, 174 insertions(+), 152 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronProvider.java diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index adadb8915db..cb0c021d677 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -56,6 +56,12 @@ org.slf4j slf4j-android + + net.sourceforge.cobertura + cobertura + 2.1.1 + true + org.slf4j slf4j-api @@ -77,7 +83,7 @@ + + + + + javax.xml.stream + ca.uhn.fhir.repackage.javax.xml.stream + + + javax.json + ca.uhn.fhir.repackage.javax.json + + + + + + net.sourceforge.cobertura:cobertura + + net/sourceforge/cobertura/CoverageIgnore* + + + + + ca.uhn.hapi.fhir:hapi-fhir-base + + + ca/uhn/fhir/util/ITestingUiClientFactory + + ca/uhn/fhir/rest/server/Add* + ca/uhn/fhir/rest/server/Apache* + ca/uhn/fhir/rest/server/Bundle* + ca/uhn/fhir/rest/server/ETag* + ca/uhn/fhir/rest/server/Fifo* + ca/uhn/fhir/rest/server/Hard* + ca/uhn/fhir/rest/server/IBundle* + ca/uhn/fhir/rest/server/IDynamic* + ca/uhn/fhir/rest/server/Incoming* + ca/uhn/fhir/rest/server/IPaging* + ca/uhn/fhir/rest/server/IServerAdd* + ca/uhn/fhir/rest/server/Resource* + ca/uhn/fhir/rest/server/Rest* + ca/uhn/fhir/rest/server/Search* + ca/uhn/fhir/rest/server/Simple* + ca/uhn/fhir/rest/server/audit/** + ca/uhn/fhir/rest/server/interceptor/** + + ca/uhn/fhir/validation/schematron/SchematronBaseValidator* + + ca/uhn/fhir/narrative/*Thymeleaf* + + + + + - normal - package - - shade - - - true - true - true - - - ca.uhn.hapi.fhir:hapi-fhir-base - org.glassfish:javax.json - org.codehaus.woodstox:woodstox-core-asl - javax.xml.stream:stax-api - org.codehaus.woodstox:stax2-api - org.glassfish:javax.json - - - - - javax.xml.stream - ca.uhn.fhir.repackage.javax.xml.stream - - - javax.json - ca.uhn.fhir.repackage.javax.json - - - - - ca.uhn.hapi.fhir:hapi-fhir-base - - ca/uhn/fhir/util/ITestingUiClientFactory - ca/uhn/fhir/rest/server/** - **/*.java - - - ca/uhn/fhir/rest/server/exceptions/** - - - - - + shaded + package + + shade + + + dstu - package - - shade - + package + + shade + - true - true - true dstu - - ca.uhn.hapi.fhir:hapi-fhir-base ca.uhn.hapi.fhir:hapi-fhir-structures-dstu - org.glassfish:javax.json - org.codehaus.woodstox:woodstox-core-asl - javax.xml.stream:stax-api - - org.codehaus.woodstox:stax2-api - - - org.glassfish:javax.json - - - javax.xml.stream - ca.uhn.fhir.repackage.javax.xml.stream - - - javax.json - ca.uhn.fhir.repackage.javax.json - - - - ca.uhn.hapi.fhir:hapi-fhir-base - - ca/uhn/fhir/util/ITestingUiClientFactory - ca/uhn/fhir/rest/server/** - **/*.java - - - ca/uhn/fhir/rest/server/exceptions/** - - + ca.uhn.hapi.fhir:hapi-fhir-structures-dstu @@ -233,58 +236,22 @@ + dstu2 - package - - shade - + package + + shade + - true - true - true dstu2 - - ca.uhn.hapi.fhir:hapi-fhir-base ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2 - org.glassfish:javax.json - org.codehaus.woodstox:woodstox-core-asl - javax.xml.stream:stax-api - - org.codehaus.woodstox:stax2-api - - org.glassfish:javax.json - - - javax.xml.stream - ca.uhn.fhir.repackage.javax.xml.stream - - - javax.json - ca.uhn.fhir.repackage.javax.json - - - - ca.uhn.hapi.fhir:hapi-fhir-base - - ca/uhn/fhir/util/ITestingUiClientFactory - ca/uhn/fhir/rest/server/** - **/*.java - - - ca/uhn/fhir/rest/server/exceptions/** - - + ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2 diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ObjectUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ObjectUtil.java index 70413d258c3..b1b6bcd678b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ObjectUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ObjectUtil.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.util; +import org.apache.commons.lang3.StringUtils; + /* * #%L * HAPI FHIR - Core Library @@ -32,4 +34,16 @@ public class ObjectUtil { return object1.equals(object2); } + public static T requireNonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } + + public static void requireNotEmpty(String str, String message) { + if (StringUtils.isBlank(str)) { + throw new IllegalArgumentException(message); + } + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/BaseValidationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/BaseValidationContext.java index cb0e7aabaae..9cf24acd05c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/BaseValidationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/BaseValidationContext.java @@ -23,9 +23,8 @@ package ca.uhn.fhir.validation; import java.util.ArrayList; import java.util.List; -import org.thymeleaf.util.Validate; - import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.util.ObjectUtil; abstract class BaseValidationContext implements IValidationContext { @@ -38,7 +37,7 @@ abstract class BaseValidationContext implements IValidationContext { @Override public void addValidationMessage(SingleValidationMessage theMessage) { - Validate.notNull(theMessage, "theMessage must not be null"); + ObjectUtil.requireNonNull(theMessage, "theMessage must not be null"); myMessages.add(theMessage); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java index 1d03cf0d89a..a91e40e51db 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.util.OperationOutcomeUtil; +import ca.uhn.fhir.validation.schematron.SchematronProvider; /** * Resource validator, which checks resources for compliance against various validation schemes (schemas, schematrons, profiles, etc.) @@ -49,9 +50,6 @@ public class FhirValidator { private static final String I18N_KEY_NO_PHLOC_ERROR = FhirValidator.class.getName() + ".noPhlocError"; - private static final String I18N_KEY_NO_PHLOC_WARNING = FhirValidator.class.getName() + ".noPhlocWarningOnStartup"; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirValidator.class); - private static volatile Boolean ourPhlocPresentOnClasspath; private final FhirContext myContext; private List myValidators = new ArrayList(); @@ -63,13 +61,7 @@ public class FhirValidator { myContext = theFhirContext; if (ourPhlocPresentOnClasspath == null) { - try { - Class.forName("com.phloc.schematron.ISchematronResource"); - ourPhlocPresentOnClasspath = true; - } catch (ClassNotFoundException e) { - ourLog.info(theFhirContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_WARNING)); - ourPhlocPresentOnClasspath = false; - } + ourPhlocPresentOnClasspath = SchematronProvider.isScematronAvailable(theFhirContext); } } @@ -110,7 +102,12 @@ public class FhirValidator { * Should the validator validate the resource against the base schema (the schema provided with the FHIR distribution itself) */ public synchronized boolean isValidateAgainstStandardSchematron() { - return haveValidatorOfType(SchematronBaseValidator.class); + if (!ourPhlocPresentOnClasspath) { + return false; // No need to ask since we dont have Phloc. Also Class.forname will complain + // about missing phloc import. + } + Class cls = SchematronProvider.getSchematronValidatorClass(); + return haveValidatorOfType(cls); } /** @@ -147,7 +144,9 @@ public class FhirValidator { if (theValidateAgainstStandardSchematron && !ourPhlocPresentOnClasspath) { throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_ERROR)); } - addOrRemoveValidator(theValidateAgainstStandardSchematron, SchematronBaseValidator.class, new SchematronBaseValidator(myContext)); + Class cls = SchematronProvider.getSchematronValidatorClass(); + IValidatorModule instance = SchematronProvider.getSchematronValidatorInstance(myContext); + addOrRemoveValidator(theValidateAgainstStandardSchematron, cls, instance); return this; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java index 57d6fa7f34c..cebd80a4693 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java @@ -53,7 +53,9 @@ import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -class SchemaBaseValidator implements IValidatorModule { +public class SchemaBaseValidator implements IValidatorModule { + public static final String RESOURCES_JAR_NOTE = "Note that as of HAPI FHIR 1.2, DSTU2 validation files are kept in a separate JAR (hapi-fhir-validation-resources-XXX.jar) which must be added to your classpath. See the HAPI FHIR download page for more information."; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaBaseValidator.class); private static final Set SCHEMA_NAMES; @@ -129,7 +131,7 @@ class SchemaBaseValidator implements IValidatorModule { ourLog.debug("Going to load resource: {}", pathToBase); InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase); if (baseIs == null) { - throw new InternalErrorException("Schema not found. " + SchematronBaseValidator.RESOURCES_JAR_NOTE); + throw new InternalErrorException("Schema not found. " + RESOURCES_JAR_NOTE); } baseIs = new BOMInputStream(baseIs, false); InputStreamReader baseReader = new InputStreamReader(baseIs, Charset.forName("UTF-8")); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java index a7cdb87a951..843f676b553 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java @@ -21,7 +21,6 @@ package ca.uhn.fhir.validation; */ import org.hl7.fhir.instance.model.api.IBaseResource; -import org.thymeleaf.util.Validate; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; @@ -29,8 +28,9 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.method.MethodUtil; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.ObjectUtil; -class ValidationContext extends BaseValidationContext implements IValidationContext { +public class ValidationContext extends BaseValidationContext implements IValidationContext { private final IEncoder myEncoder; private final T myResource; @@ -136,8 +136,8 @@ class ValidationContext extends BaseValidationContext implements IValidati } public static IValidationContext forText(final FhirContext theContext, final String theResourceBody) { - Validate.notNull(theContext, "theContext can not be null"); - Validate.notEmpty(theResourceBody, "theResourceBody can not be null or empty"); + ObjectUtil.requireNonNull(theContext, "theContext can not be null"); + ObjectUtil.requireNotEmpty(theResourceBody, "theResourceBody can not be null or empty"); return new BaseValidationContext(theContext) { private EncodingEnum myEncoding; @@ -169,5 +169,4 @@ class ValidationContext extends BaseValidationContext implements IValidati }; } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronProvider.java new file mode 100644 index 00000000000..a48abde04b1 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronProvider.java @@ -0,0 +1,42 @@ +package ca.uhn.fhir.validation.schematron; + +import java.lang.reflect.Constructor; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.IValidatorModule; + +public class SchematronProvider { + + + private static final String I18N_KEY_NO_PHLOC_WARNING = FhirValidator.class.getName() + ".noPhlocWarningOnStartup"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirValidator.class); + + public static boolean isScematronAvailable(FhirContext theFhirContext) { + try { + Class.forName("com.phloc.schematron.ISchematronResource"); + return true; + } catch (ClassNotFoundException e) { + ourLog.info(theFhirContext.getLocalizer().getMessage(I18N_KEY_NO_PHLOC_WARNING)); + return false; + } + } + + public static Class getSchematronValidatorClass() { + try { + return (Class) Class.forName("ca.uhn.fhir.validation.schematron.SchematronBaseValidator"); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Cannot resolve schematron validator ", e); + } + } + + public static IValidatorModule getSchematronValidatorInstance(FhirContext myContext) { + try { + Class cls = getSchematronValidatorClass(); + Constructor constructor = cls.getConstructor(FhirContext.class); + return constructor.newInstance(myContext); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Cannot construct schematron validator ", e); + } + } +} From 64b70d412a3ab628de8dfe2b63e826eb78f0ba21 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Sun, 4 Oct 2015 21:05:55 +0200 Subject: [PATCH 3/7] move schematron validator to sub package --- .../SchematronBaseValidator.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) rename hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/{ => schematron}/SchematronBaseValidator.java (88%) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java similarity index 88% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java index 2b100b0ab40..98397a8f1a7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchematronBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.validation; +package ca.uhn.fhir.validation.schematron; /* * #%L @@ -37,6 +37,13 @@ import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.IValidationContext; +import ca.uhn.fhir.validation.IValidatorModule; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhir.validation.SchemaBaseValidator; +import ca.uhn.fhir.validation.SingleValidationMessage; +import ca.uhn.fhir.validation.ValidationContext; import com.phloc.commons.error.IResourceError; import com.phloc.commons.error.IResourceErrorGroup; @@ -44,13 +51,16 @@ import com.phloc.schematron.ISchematronResource; import com.phloc.schematron.SchematronHelper; import com.phloc.schematron.xslt.SchematronResourceSCH; +/** + * This class is only used using reflection from {@link SchematronProvider} in order + * to be truly optional. + */ public class SchematronBaseValidator implements IValidatorModule { - static final String RESOURCES_JAR_NOTE = "Note that as of HAPI FHIR 1.2, DSTU2 validation files are kept in a separate JAR (hapi-fhir-validation-resources-XXX.jar) which must be added to your classpath. See the HAPI FHIR download page for more information."; private Map, ISchematronResource> myClassToSchematron = new HashMap, ISchematronResource>(); private FhirContext myCtx; - SchematronBaseValidator(FhirContext theContext) { + public SchematronBaseValidator(FhirContext theContext) { myCtx = theContext; } @@ -126,7 +136,7 @@ public class SchematronBaseValidator implements IValidatorModule { InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase); try { if (baseIs == null) { - throw new InternalErrorException("Failed to load schematron for resource '" + theCtx.getFhirContext().getResourceDefinition(theCtx.getResource()).getBaseDefinition().getName() + "'. " + RESOURCES_JAR_NOTE); + throw new InternalErrorException("Failed to load schematron for resource '" + theCtx.getFhirContext().getResourceDefinition(theCtx.getResource()).getBaseDefinition().getName() + "'. " + SchemaBaseValidator.RESOURCES_JAR_NOTE); } } finally { IOUtils.closeQuietly(baseIs); From cdf18f00b1b7e126d8c7fdda3ae09071373eba14 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Mon, 5 Oct 2015 02:10:05 +0200 Subject: [PATCH 4/7] undo attempt to remove javax.servlet dependency undo attempt to remove javax.servlet dependency --- hapi-fhir-android/pom.xml | 22 +++++++++---------- .../java/javax/servlet/ServletRequest.java | 5 ----- .../java/javax/servlet/ServletResponse.java | 5 ----- .../servlet/http/HttpServletRequest.java | 7 ------ .../servlet/http/HttpServletResponse.java | 7 ------ 5 files changed, 11 insertions(+), 35 deletions(-) delete mode 100644 hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java delete mode 100644 hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java delete mode 100644 hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java delete mode 100644 hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 7c04f199068..0f7f5a7c60e 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -85,11 +85,11 @@ We provide a dummy implementation of servlet api to reduce size and prevent from rewriting the BaseMethodBinding and friends. --> - + @@ -147,8 +147,8 @@ org.apache.commons:* org.apache.httpcomponents:* commons-codec:commons-codec - javax.servlet:javax.servlet-api --> + javax.servlet:javax.servlet-api @@ -176,7 +176,7 @@ ca/uhn/fhir/util/ITestingUiClientFactory - ca/uhn/fhir/rest/server/Add* + ca/uhn/fhir/validation/schematron/SchematronBaseValidator* @@ -224,15 +224,15 @@ ca.uhn.hapi.fhir:hapi-fhir-structures-dstu - - + + @@ -249,15 +249,15 @@ ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2 - - + + diff --git a/hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java b/hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java deleted file mode 100644 index 2292db91fac..00000000000 --- a/hapi-fhir-android/src/main/java/javax/servlet/ServletRequest.java +++ /dev/null @@ -1,5 +0,0 @@ -package javax.servlet; - -public class ServletRequest { - -} diff --git a/hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java b/hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java deleted file mode 100644 index efa8a9baa43..00000000000 --- a/hapi-fhir-android/src/main/java/javax/servlet/ServletResponse.java +++ /dev/null @@ -1,5 +0,0 @@ -package javax.servlet; - -public class ServletResponse { - -} diff --git a/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java b/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java deleted file mode 100644 index 37551bd241d..00000000000 --- a/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package javax.servlet.http; - -import javax.servlet.ServletRequest; - -public class HttpServletRequest extends ServletRequest { - -} diff --git a/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java b/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java deleted file mode 100644 index 230ab022b4d..00000000000 --- a/hapi-fhir-android/src/main/java/javax/servlet/http/HttpServletResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package javax.servlet.http; - -import javax.servlet.ServletResponse; - -public class HttpServletResponse extends ServletResponse{ - -} From 91afb99b7b2c26dc1135a2f7d72ba0828a8c5285 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Mon, 5 Oct 2015 21:18:59 +0200 Subject: [PATCH 5/7] make sure hapi fhir base creates sources jar In order to streamline build of android jar with dependencies all dependencies should publish a sources jar also. --- hapi-fhir-base/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 72051e0877f..949f1bda8ec 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -132,6 +132,20 @@ + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + src/main/resources From 7ad5d6bba8d952fc9b358b8050c6219fb3d0f4f8 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Tue, 6 Oct 2015 21:56:29 +0200 Subject: [PATCH 6/7] Move dependency on java.util.jar and java.beans to optional dependency Move some more dependencies to optionel dependencies and write more test for different base methods. --- .../main/java/ca/uhn/fhir/util/BeanUtils.java | 58 ++++------ .../main/java/ca/uhn/fhir/util/XmlUtil.java | 83 ++------------ .../fhir/util/jar/DependencyLogFactory.java | 36 ++++++ .../uhn/fhir/util/jar/DependencyLogImpl.java | 107 ++++++++++++++++++ .../ca/uhn/fhir/util/jar/IDependencyLog.java | 27 +++++ .../uhn/fhir/util/reflection/IBeanUtils.java | 31 +++++ .../util/reflection/JavaBeansBeanUtil.java | 70 ++++++++++++ .../util/reflection/JavaReflectBeanUtil.java | 63 +++++++++++ .../java/ca/uhn/fhir/util/BeanUtilTest.java | 78 +++++++++++++ .../uhn/fhir/util/DependencyLogUtilTest.java | 15 +++ .../java/ca/uhn/fhir/util/ObjectUtilTest.java | 57 ++++++++++ 11 files changed, 514 insertions(+), 111 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/IDependencyLog.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ObjectUtilTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java index 6e8b9b18495..03b86d86aee 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BeanUtils.java @@ -20,49 +20,37 @@ package ca.uhn.fhir.util; * #L% */ -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; import java.lang.reflect.Method; +import ca.uhn.fhir.util.reflection.IBeanUtils; + public class BeanUtils { - public static Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) throws NoSuchFieldException { - BeanInfo info; - try { - info = Introspector.getBeanInfo(theClassToIntrospect); - } catch (IntrospectionException e) { - throw new NoSuchFieldException(e.getMessage()); - } - for (PropertyDescriptor pd : info.getPropertyDescriptors()) { - if (thePropertyName.equals(pd.getName())) { - if (theTargetReturnType.isAssignableFrom(pd.getPropertyType())) { - return pd.getReadMethod(); - }else { - throw new NoSuchFieldException(theClassToIntrospect + " has an accessor for field " + thePropertyName + " but it does not return type " + theTargetReturnType); + private static IBeanUtils beanUtils; + + private static IBeanUtils getBeanUtils() { + if (beanUtils == null) { + try { + beanUtils = (IBeanUtils) Class.forName("ca.uhn.fhir.util.reflection.JavaBeansBeanUtil").newInstance(); + } catch (ReflectiveOperationException e) { + try { + beanUtils = (IBeanUtils) Class.forName("ca.uhn.fhir.util.reflection.JavaReflectBeanUtil") + .newInstance(); + } catch (ReflectiveOperationException e1) { + throw new RuntimeException("Could not resolve BeanUtil implementation"); } } } - throw new NoSuchFieldException(theClassToIntrospect + " has no accessor for field " + thePropertyName); + return beanUtils; } - public static Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) throws NoSuchFieldException { - BeanInfo info; - try { - info = Introspector.getBeanInfo(theClassToIntrospect); - } catch (IntrospectionException e) { - throw new NoSuchFieldException(e.getMessage()); - } - for (PropertyDescriptor pd : info.getPropertyDescriptors()) { - if (thePropertyName.equals(pd.getName())) { - if (theTargetReturnType.isAssignableFrom(pd.getPropertyType())) { - return pd.getWriteMethod(); - }else { - throw new NoSuchFieldException(theClassToIntrospect + " has an mutator for field " + thePropertyName + " but it does not return type " + theTargetReturnType); - } - } - } - throw new NoSuchFieldException(theClassToIntrospect + " has no mutator for field " + thePropertyName); + public static Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) + throws NoSuchFieldException { + return getBeanUtils().findAccessor(theClassToIntrospect, theTargetReturnType, thePropertyName); } + + public static Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) + throws NoSuchFieldException { + return getBeanUtils().findMutator(theClassToIntrospect, theTargetReturnType, thePropertyName); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java index 5938389b912..fe6b26cd1e8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java @@ -21,20 +21,15 @@ package ca.uhn.fhir.util; */ import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; -import java.net.MalformedURLException; -import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.jar.Attributes; -import java.util.jar.Manifest; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLEventReader; @@ -52,18 +47,15 @@ import org.codehaus.stax2.io.EscapingWriterFactory; import com.ctc.wstx.api.WstxInputProperties; import com.ctc.wstx.stax.WstxOutputFactory; +import ca.uhn.fhir.util.jar.DependencyLogFactory; +import ca.uhn.fhir.util.jar.IDependencyLog; + /** * Utility methods for working with the StAX API. * * This class contains code adapted from the Apache Axiom project. */ public class XmlUtil { - private static final Attributes.Name BUNDLE_SYMBOLIC_NAME = new Attributes.Name("Bundle-SymbolicName"); - private static final Attributes.Name BUNDLE_VENDOR = new Attributes.Name("Bundle-Vendor"); - private static final Attributes.Name BUNDLE_VERSION = new Attributes.Name("Bundle-Version"); - private static final Attributes.Name IMPLEMENTATION_TITLE = new Attributes.Name("Implementation-Title"); - private static final Attributes.Name IMPLEMENTATION_VENDOR = new Attributes.Name("Implementation-Vendor"); - private static final Attributes.Name IMPLEMENTATION_VERSION = new Attributes.Name("Implementation-Version"); private static volatile boolean ourHaveLoggedStaxImplementation; private static volatile XMLInputFactory ourInputFactory; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class); @@ -1645,73 +1637,12 @@ public class XmlUtil { return outputFactory; } - private static URL getRootUrlForClass(Class cls) { - ClassLoader classLoader = cls.getClassLoader(); - String resource = cls.getName().replace('.', '/') + ".class"; - if (classLoader == null) { - // A null class loader means the bootstrap class loader. In this case we use the - // system class loader. This is safe since we can assume that the system class - // loader uses parent first as delegation policy. - classLoader = ClassLoader.getSystemClassLoader(); - } - URL url = classLoader.getResource(resource); - if (url == null) { - return null; - } - String file = url.getFile(); - if (file.endsWith(resource)) { - try { - return new URL(url.getProtocol(), url.getHost(), url.getPort(), file.substring(0, file.length() - resource.length())); - } catch (MalformedURLException ex) { - return null; - } - } else { - return null; - } - } - private static void logStaxImplementation(Class theClass) { - try { - URL rootUrl = getRootUrlForClass(theClass); - if (rootUrl == null) { - ourLog.info("Unable to determine location of StAX implementation containing class"); - } else { - Manifest manifest; - URL metaInfUrl = new URL(rootUrl, "META-INF/MANIFEST.MF"); - InputStream is = metaInfUrl.openStream(); - try { - manifest = new Manifest(is); - } finally { - is.close(); - } - Attributes attrs = manifest.getMainAttributes(); - String title = attrs.getValue(IMPLEMENTATION_TITLE); - String symbolicName = attrs.getValue(BUNDLE_SYMBOLIC_NAME); - if (symbolicName != null) { - int i = symbolicName.indexOf(';'); - if (i != -1) { - symbolicName = symbolicName.substring(0, i); - } - } - String vendor = attrs.getValue(IMPLEMENTATION_VENDOR); - if (vendor == null) { - vendor = attrs.getValue(BUNDLE_VENDOR); - } - String version = attrs.getValue(IMPLEMENTATION_VERSION); - if (version == null) { - version = attrs.getValue(BUNDLE_VERSION); - } - if (ourLog.isDebugEnabled()) { - ourLog.debug("FHIR XML procesing will use StAX implementation at {}\n Title: {}\n Symbolic name: {}\n Vendor: {}\n Version: {}", new Object[] { rootUrl, title, symbolicName, vendor, version } ); - } else { - ourLog.info("FHIR XML procesing will use StAX implementation '{}' version '{}'", title, version); - } - } - } catch (Throwable e) { - ourLog.info("Unable to determine StAX implementation: " + e.getMessage()); - } finally { - ourHaveLoggedStaxImplementation = true; + IDependencyLog logger = DependencyLogFactory.createJarLogger(); + if (logger != null) { + logger.logStaxImplementation(theClass); } + ourHaveLoggedStaxImplementation = true; } public static void main(String[] args) throws FactoryConfigurationError, XMLStreamException { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java new file mode 100644 index 00000000000..ea861a8254d --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogFactory.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.util.jar; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class DependencyLogFactory { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DependencyLogFactory.class); + + @SuppressWarnings("unchecked") + public static IDependencyLog createJarLogger() { + try { + Class clas = (Class) Class.forName("ca.uhn.fhir.util.jar.DependencyLogImpl"); + return clas.newInstance(); + } catch (ReflectiveOperationException e) { + ourLog.info("Could not log dependency."); + return null; + } + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java new file mode 100644 index 00000000000..b3c778073d5 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/DependencyLogImpl.java @@ -0,0 +1,107 @@ +package ca.uhn.fhir.util.jar; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import ca.uhn.fhir.util.XmlUtil; + +public class DependencyLogImpl implements IDependencyLog { + private static final Attributes.Name BUNDLE_SYMBOLIC_NAME = new Attributes.Name("Bundle-SymbolicName"); + private static final Attributes.Name BUNDLE_VENDOR = new Attributes.Name("Bundle-Vendor"); + private static final Attributes.Name BUNDLE_VERSION = new Attributes.Name("Bundle-Version"); + private static final Attributes.Name IMPLEMENTATION_TITLE = new Attributes.Name("Implementation-Title"); + private static final Attributes.Name IMPLEMENTATION_VENDOR = new Attributes.Name("Implementation-Vendor"); + private static final Attributes.Name IMPLEMENTATION_VERSION = new Attributes.Name("Implementation-Version"); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class); + + @Override + public void logStaxImplementation(Class theClass) { + try { + URL rootUrl = getRootUrlForClass(theClass); + if (rootUrl == null) { + ourLog.info("Unable to determine location of StAX implementation containing class"); + } else { + Manifest manifest; + URL metaInfUrl = new URL(rootUrl, "META-INF/MANIFEST.MF"); + InputStream is = metaInfUrl.openStream(); + try { + manifest = new Manifest(is); + } finally { + is.close(); + } + Attributes attrs = manifest.getMainAttributes(); + String title = attrs.getValue(IMPLEMENTATION_TITLE); + String symbolicName = attrs.getValue(BUNDLE_SYMBOLIC_NAME); + if (symbolicName != null) { + int i = symbolicName.indexOf(';'); + if (i != -1) { + symbolicName = symbolicName.substring(0, i); + } + } + String vendor = attrs.getValue(IMPLEMENTATION_VENDOR); + if (vendor == null) { + vendor = attrs.getValue(BUNDLE_VENDOR); + } + String version = attrs.getValue(IMPLEMENTATION_VERSION); + if (version == null) { + version = attrs.getValue(BUNDLE_VERSION); + } + if (ourLog.isDebugEnabled()) { + ourLog.debug("FHIR XML procesing will use StAX implementation at {}\n Title: {}\n Symbolic name: {}\n Vendor: {}\n Version: {}", new Object[] { rootUrl, title, symbolicName, vendor, version } ); + } else { + ourLog.info("FHIR XML procesing will use StAX implementation '{}' version '{}'", title, version); + } + } + } catch (Throwable e) { + ourLog.info("Unable to determine StAX implementation: " + e.getMessage()); + } + } + + private static URL getRootUrlForClass(Class cls) { + ClassLoader classLoader = cls.getClassLoader(); + String resource = cls.getName().replace('.', '/') + ".class"; + if (classLoader == null) { + // A null class loader means the bootstrap class loader. In this case we use the + // system class loader. This is safe since we can assume that the system class + // loader uses parent first as delegation policy. + classLoader = ClassLoader.getSystemClassLoader(); + } + URL url = classLoader.getResource(resource); + if (url == null) { + return null; + } + String file = url.getFile(); + if (file.endsWith(resource)) { + try { + return new URL(url.getProtocol(), url.getHost(), url.getPort(), file.substring(0, file.length() - resource.length())); + } catch (MalformedURLException ex) { + return null; + } + } else { + return null; + } + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/IDependencyLog.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/IDependencyLog.java new file mode 100644 index 00000000000..3ad25a542ed --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/jar/IDependencyLog.java @@ -0,0 +1,27 @@ +package ca.uhn.fhir.util.jar; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public interface IDependencyLog { + + void logStaxImplementation(Class theClass); + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java new file mode 100644 index 00000000000..36f1784c6f3 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/IBeanUtils.java @@ -0,0 +1,31 @@ +package ca.uhn.fhir.util.reflection; + +import java.lang.reflect.Method; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public interface IBeanUtils { + Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) + throws NoSuchFieldException; + + Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) + throws NoSuchFieldException; +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java new file mode 100644 index 00000000000..c7f43cd1106 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaBeansBeanUtil.java @@ -0,0 +1,70 @@ +package ca.uhn.fhir.util.reflection; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; + +public class JavaBeansBeanUtil implements IBeanUtils { + + @Override + public Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) throws NoSuchFieldException { + BeanInfo info; + try { + info = Introspector.getBeanInfo(theClassToIntrospect); + } catch (IntrospectionException e) { + throw new NoSuchFieldException(e.getMessage()); + } + for (PropertyDescriptor pd : info.getPropertyDescriptors()) { + if (thePropertyName.equals(pd.getName())) { + if (theTargetReturnType.isAssignableFrom(pd.getPropertyType())) { + return pd.getReadMethod(); + }else { + throw new NoSuchFieldException(theClassToIntrospect + " has an accessor for field " + thePropertyName + " but it does not return type " + theTargetReturnType); + } + } + } + throw new NoSuchFieldException(theClassToIntrospect + " has no accessor for field " + thePropertyName); + } + + @Override + public Method findMutator(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) throws NoSuchFieldException { + BeanInfo info; + try { + info = Introspector.getBeanInfo(theClassToIntrospect); + } catch (IntrospectionException e) { + throw new NoSuchFieldException(e.getMessage()); + } + for (PropertyDescriptor pd : info.getPropertyDescriptors()) { + if (thePropertyName.equals(pd.getName())) { + if (theTargetReturnType.isAssignableFrom(pd.getPropertyType())) { + return pd.getWriteMethod(); + }else { + throw new NoSuchFieldException(theClassToIntrospect + " has an mutator for field " + thePropertyName + " but it does not return type " + theTargetReturnType); + } + } + } + throw new NoSuchFieldException(theClassToIntrospect + " has no mutator for field " + thePropertyName); + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java new file mode 100644 index 00000000000..a8c8bd98dd3 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/reflection/JavaReflectBeanUtil.java @@ -0,0 +1,63 @@ +package ca.uhn.fhir.util.reflection; + +import java.lang.reflect.Method; + +import org.apache.commons.lang3.text.WordUtils; + +import ca.uhn.fhir.context.ConfigurationException; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public class JavaReflectBeanUtil implements IBeanUtils { + + @Override + public Method findAccessor(Class theClassToIntrospect, Class theTargetReturnType, String thePropertyName) + throws NoSuchFieldException { + String methodName = "get" + WordUtils.capitalize(thePropertyName); + try { + Method method = theClassToIntrospect.getMethod(methodName); + if (theTargetReturnType.isAssignableFrom(method.getReturnType())) { + return method; + } + } catch (NoSuchMethodException e) { + // fall through + } catch (SecurityException e) { + throw new ConfigurationException("Failed to scan class '" + theClassToIntrospect + "' because of a security exception", e); + } + throw new NoSuchFieldException(theClassToIntrospect + " has no accessor for field " + thePropertyName); + } + + @Override + public Method findMutator(Class theClassToIntrospect, Class theTargetArgumentType, String thePropertyName) + throws NoSuchFieldException { + String methodName = "set" + WordUtils.capitalize(thePropertyName); + try { + return theClassToIntrospect.getMethod(methodName, theTargetArgumentType); + } catch (NoSuchMethodException e) { + //fall through + } catch (SecurityException e) { + throw new ConfigurationException("Failed to scan class '" + theClassToIntrospect + "' because of a security exception", e); + } + throw new NoSuchFieldException(theClassToIntrospect + " has an mutator for field " + thePropertyName + " but it does not return type " + theTargetArgumentType); + + } + +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java new file mode 100644 index 00000000000..bf195717f58 --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/BeanUtilTest.java @@ -0,0 +1,78 @@ +package ca.uhn.fhir.util; + +import static org.junit.Assert.*; + +import java.lang.reflect.Method; + +import org.junit.Test; + +import ca.uhn.fhir.util.reflection.IBeanUtils; +import ca.uhn.fhir.util.reflection.JavaBeansBeanUtil; +import ca.uhn.fhir.util.reflection.JavaReflectBeanUtil; + +public class BeanUtilTest { + + @Test + public void testFindAccessor() throws Exception { + JavaBeansBeanUtil javaBeansBeanUtil = new JavaBeansBeanUtil(); + testBeanUtilsAccessor(javaBeansBeanUtil); + JavaReflectBeanUtil javaReflectBeanUtil = new JavaReflectBeanUtil(); + testBeanUtilsAccessor(javaReflectBeanUtil); + assertNotNull(BeanUtils.findAccessor(BeanUtilTestClass.class, String.class, "field")); + Method jbMGet = javaBeansBeanUtil.findAccessor(BeanUtilTestClass.class, String.class, "field"); + Method jrMGet = javaReflectBeanUtil.findAccessor(BeanUtilTestClass.class, String.class, "field"); + assertNotNull(jbMGet); + assertNotNull(jrMGet); + assertEquals(jbMGet, jrMGet); + } + + @Test + public void testFindMutator() throws Exception { + JavaBeansBeanUtil javaBeansBeanUtil = new JavaBeansBeanUtil(); + testBeanUtilsMutator(javaBeansBeanUtil); + JavaReflectBeanUtil javaReflectBeanUtil = new JavaReflectBeanUtil(); + testBeanUtilsMutator(javaReflectBeanUtil); + assertNotNull(BeanUtils.findMutator(BeanUtilTestClass.class, String.class, "field")); + Method jbMSet = javaBeansBeanUtil.findMutator(BeanUtilTestClass.class, String.class, "field"); + Method jrMSet = javaReflectBeanUtil.findMutator(BeanUtilTestClass.class, String.class, "field"); + assertNotNull(jbMSet); + assertNotNull(jrMSet); + assertEquals(jbMSet, jrMSet); + } + + private void testBeanUtilsAccessor(IBeanUtils util) throws Exception { + assertNotNull(util.findAccessor(BeanUtilTestClass.class, String.class, "field")); + try { + assertNull(util.findAccessor(BeanUtilTestClass.class, String.class, "fieldX")); + fail("Field is not in class"); + } catch (NoSuchFieldException e) { } + try { + assertNull(util.findAccessor(BeanUtilTestClass.class, Integer.class, "field")); + fail("Field is in class, but we expect Integer as return type"); + } catch (NoSuchFieldException e) { } + } + + private void testBeanUtilsMutator(IBeanUtils util) throws Exception { + assertNotNull(util.findMutator(BeanUtilTestClass.class, String.class, "field")); + try { + assertNull(util.findMutator(BeanUtilTestClass.class, String.class, "fieldX")); + fail("Field is not in class"); + } catch (NoSuchFieldException e) { } + try { + assertNull(util.findMutator(BeanUtilTestClass.class, Integer.class, "field")); + fail("Field is in class, but we expect Integer as parameter type"); + } catch (NoSuchFieldException e) { } + } + + public static class BeanUtilTestClass { + private String myField; + + public String getField() { + return myField; + } + + public void setField(String value) { + this.myField = value; + } + } +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java new file mode 100644 index 00000000000..faecd8fcce4 --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java @@ -0,0 +1,15 @@ +package ca.uhn.fhir.util; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import ca.uhn.fhir.util.jar.DependencyLogFactory; + +public class DependencyLogUtilTest { + + @Test + public void testDependencyLogFactory() { + assertNotNull(DependencyLogFactory.createJarLogger()); + } +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ObjectUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ObjectUtilTest.java new file mode 100644 index 00000000000..80a8d43db49 --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ObjectUtilTest.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.util; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class ObjectUtilTest { + + @Test + public void testEquals() { + String a = new String("a"); + String b = new String("b"); + assertFalse(ObjectUtil.equals(b, a)); + assertFalse(ObjectUtil.equals(a, b)); + assertFalse(ObjectUtil.equals(a, null)); + assertFalse(ObjectUtil.equals(null, a)); + assertTrue(ObjectUtil.equals(null, null)); + assertTrue(ObjectUtil.equals(a, a)); + } + + @Test + public void testRequireNonNull() { + String message = "Must not be null in test"; + try { + ObjectUtil.requireNonNull(null, message); + fail("should not get here."); + } catch (NullPointerException e) { + assertEquals(message, e.getMessage()); + } + assertNotNull(ObjectUtil.requireNonNull("some string", message)); + } + + @Test + public void testRequireNotEmpty() { + //All these are empty, null or whitespace strings. + testRequireNotEmptyErrorScenario(null); + testRequireNotEmptyErrorScenario(""); + testRequireNotEmptyErrorScenario(" "); + testRequireNotEmptyErrorScenario(" "); + //All these are non empty, some non whitespace char in the string. + ObjectUtil.requireNotEmpty("abc ", ""); + ObjectUtil.requireNotEmpty(" abc ", ""); + ObjectUtil.requireNotEmpty(" abc", ""); + + } + + private void testRequireNotEmptyErrorScenario(String string) { + String message = "must not be empty in test"; + try { + ObjectUtil.requireNotEmpty(string, message); + fail("should not get here."); + } catch (IllegalArgumentException e) { + assertEquals(message, e.getMessage()); + } + } + +} From 828a7966c7cba184ae43e283099a33e443e67470 Mon Sep 17 00:00:00 2001 From: Thomas Andersen Date: Tue, 6 Oct 2015 22:12:36 +0200 Subject: [PATCH 7/7] some more test --- .../test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java index faecd8fcce4..36bf76d64ca 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java @@ -5,11 +5,14 @@ import static org.junit.Assert.*; import org.junit.Test; import ca.uhn.fhir.util.jar.DependencyLogFactory; +import ca.uhn.fhir.util.jar.IDependencyLog; public class DependencyLogUtilTest { @Test public void testDependencyLogFactory() { - assertNotNull(DependencyLogFactory.createJarLogger()); + IDependencyLog logger = DependencyLogFactory.createJarLogger(); + assertNotNull(logger); + logger.logStaxImplementation(DependencyLogUtilTest.class); } }