diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 3bc12274ac0..0f7f5a7c60e 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 @@ -74,7 +80,11 @@ + requires it. + + We provide a dummy implementation of servlet api to reduce size + and prevent from rewriting the BaseMethodBinding and friends. + --> javax.servlet javax.servlet-api @@ -120,138 +130,134 @@ org.apache.maven.plugins maven-shade-plugin 2.3 + + 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 + net.sourceforge.cobertura:cobertura + + javax.servlet:javax.servlet-api + + + + + 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/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 - - **/*.java - - - - - + 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 - - - + + + 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 - - + + 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 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/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/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/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/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); 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); + } + } +} 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..36bf76d64ca --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DependencyLogUtilTest.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.util; + +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() { + IDependencyLog logger = DependencyLogFactory.createJarLogger(); + assertNotNull(logger); + logger.logStaxImplementation(DependencyLogUtilTest.class); + } +} 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()); + } + } + +} 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