Merge pull request #239 from botunge/master

Make dependencies truly optionel and clean up android pom.
This commit is contained in:
James Agnew 2015-10-12 20:06:24 -04:00
commit a2bf374470
21 changed files with 735 additions and 244 deletions

View File

@ -56,6 +56,12 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-android</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.cobertura</groupId>
<artifactId>cobertura</artifactId>
<version>2.1.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@ -74,7 +80,11 @@
</dependency>
<!-- Android does not come with the Servlet API bundled, and MethodUtil
requires it -->
requires it.
We provide a dummy implementation of servlet api to reduce size
and prevent from rewriting the BaseMethodBinding and friends.
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
@ -120,138 +130,134 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<artifactSet>
<includes combine.children="append">
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<include>org.codehaus.woodstox:stax2-api</include>
<include>org.glassfish:javax.json</include>
<include>net.sourceforge.cobertura:cobertura</include>
<!--
<include>org.apache.commons:*</include>
<include>org.apache.httpcomponents:*</include>
<include>commons-codec:commons-codec</include>
-->
<include>javax.servlet:javax.servlet-api</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.xml.stream</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.xml.stream</shadedPattern>
</relocation>
<relocation>
<pattern>javax.json</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
</relocation>
</relocations>
<filters combine.children="append">
<!-- Make CoverageIgnore annotation available for android. -->
<filter>
<artifact>net.sourceforge.cobertura:cobertura</artifact>
<includes>
<include>net/sourceforge/cobertura/CoverageIgnore*</include>
</includes>
</filter>
<!-- Exclude server side stuff, except exceptions which are used clientside -->
<filter>
<artifact>ca.uhn.hapi.fhir:hapi-fhir-base</artifact>
<excludes>
<!-- Exclude test stuff which depends on httpservlet -->
<exclude>ca/uhn/fhir/util/ITestingUiClientFactory</exclude>
<!-- Exclude server side stuff. This could be made nicer if code was divided into shared, server and client -->
<!--exclude>ca/uhn/fhir/rest/server/Add*</exclude>
<exclude>ca/uhn/fhir/rest/server/Apache*</exclude>
<exclude>ca/uhn/fhir/rest/server/Bundle*</exclude>
<exclude>ca/uhn/fhir/rest/server/ETag*</exclude>
<exclude>ca/uhn/fhir/rest/server/Fifo*</exclude>
<exclude>ca/uhn/fhir/rest/server/Hard*</exclude>
<exclude>ca/uhn/fhir/rest/server/IBundle*</exclude>
<exclude>ca/uhn/fhir/rest/server/IDynamic*</exclude>
<exclude>ca/uhn/fhir/rest/server/Incoming*</exclude>
<exclude>ca/uhn/fhir/rest/server/IPaging*</exclude>
<exclude>ca/uhn/fhir/rest/server/IServerAdd*</exclude>
<exclude>ca/uhn/fhir/rest/server/Resource*</exclude>
<exclude>ca/uhn/fhir/rest/server/Rest*</exclude>
<exclude>ca/uhn/fhir/rest/server/Search*</exclude>
<exclude>ca/uhn/fhir/rest/server/Simple*</exclude>
<exclude>ca/uhn/fhir/rest/server/audit/**</exclude>
<exclude>ca/uhn/fhir/rest/server/interceptor/**</exclude-->
<!-- Exclude Phloc validator to make android libs small -->
<exclude>ca/uhn/fhir/validation/schematron/SchematronBaseValidator*</exclude>
<!-- Exclude Thymeleaf to make android libs small -->
<exclude>ca/uhn/fhir/narrative/*Thymeleaf*</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<!-- shaded jar -->
<execution>
<id>normal</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<artifactSet>
<includes>
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<include>org.codehaus.woodstox:stax2-api</include>
<include>org.glassfish:javax.json</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.xml.stream</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.xml.stream</shadedPattern>
</relocation>
<relocation>
<pattern>javax.json</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>ca.uhn.hapi.fhir:hapi-fhir-base</artifact>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
<id>shaded</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
<!-- dstu jar -->
<execution>
<id>dstu</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>dstu</shadedClassifierName>
<artifactSet>
<includes>
<!--
<include>commons-codec:commons-codec</include>
-->
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<!-- <include>javax.servlet:javax.servlet-api</include>-->
<include>org.codehaus.woodstox:stax2-api</include>
<!-- <include>org.slf4j:slf4j*</include> -->
<!--
<include>org.apache.commons:*</include>
<include>org.apache.httpcomponents:*</include>
-->
<include>org.glassfish:javax.json</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.xml.stream</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.xml.stream</shadedPattern>
</relocation>
<relocation>
<pattern>javax.json</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
</relocation>
</relocations>
<filters>
<!-- Exclude server side stuff, except exceptions which are used clientside -->
<!--filters>
<filter>
<artifact>ca.uhn.hapi.fhir:hapi-fhir-base</artifact>
<artifact>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu</artifact>
<excludes>
<exclude>**/*.java</exclude>
<exclude>ca/uhn/fhir/rest/server/**</exclude>
</excludes>
</filter>
</filters>
</filters-->
</configuration>
</execution>
<!-- dstu2 jar -->
<execution>
<id>dstu2</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<createSourcesJar>true</createSourcesJar>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>dstu2</shadedClassifierName>
<artifactSet>
<includes>
<!--
<include>commons-codec:commons-codec</include>
-->
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
<include>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2</include>
<include>org.glassfish:javax.json</include>
<include>org.codehaus.woodstox:woodstox-core-asl</include>
<include>javax.xml.stream:stax-api</include>
<!-- <include>javax.servlet:javax.servlet-api</include>-->
<include>org.codehaus.woodstox:stax2-api</include>
<!--
<include>org.apache.commons:*</include>
<include>org.apache.httpcomponents:*</include>
-->
<include>org.glassfish:javax.json</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>javax.xml.stream</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.xml.stream</shadedPattern>
</relocation>
<relocation>
<pattern>javax.json</pattern>
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
</relocation>
</relocations>
<!-- Exclude server side stuff, except exceptions which are used clientside -->
<!--filters>
<filter>
<artifact>ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2</artifact>
<excludes>
<exclude>ca/uhn/fhir/rest/server/**</exclude>
</excludes>
</filter>
</filters-->
</configuration>
</execution>
</executions>

View File

@ -132,6 +132,20 @@
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>

View File

@ -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);
}
}

View File

@ -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> 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);
}
}
}

View File

@ -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 {

View File

@ -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<IDependencyLog> clas = (Class<IDependencyLog>) Class.forName("ca.uhn.fhir.util.jar.DependencyLogImpl");
return clas.newInstance();
} catch (ReflectiveOperationException e) {
ourLog.info("Could not log dependency.");
return null;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<T> implements IValidationContext<T> {
@ -38,7 +37,7 @@ abstract class BaseValidationContext<T> implements IValidationContext<T> {
@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);
}

View File

@ -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<IValidatorModule> myValidators = new ArrayList<IValidatorModule>();
@ -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<? extends IValidatorModule> 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<? extends IValidatorModule> cls = SchematronProvider.getSchematronValidatorClass();
IValidatorModule instance = SchematronProvider.getSchematronValidatorInstance(myContext);
addOrRemoveValidator(theValidateAgainstStandardSchematron, cls, instance);
return this;
}

View File

@ -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<String> 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"));

View File

@ -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<T> extends BaseValidationContext<T> implements IValidationContext<T> {
public class ValidationContext<T> extends BaseValidationContext<T> implements IValidationContext<T> {
private final IEncoder myEncoder;
private final T myResource;
@ -136,8 +136,8 @@ class ValidationContext<T> extends BaseValidationContext<T> implements IValidati
}
public static IValidationContext<IBaseResource> 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<IBaseResource>(theContext) {
private EncodingEnum myEncoding;
@ -169,5 +169,4 @@ class ValidationContext<T> extends BaseValidationContext<T> implements IValidati
};
}
}

View File

@ -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<Class<? extends IBaseResource>, ISchematronResource> myClassToSchematron = new HashMap<Class<? extends IBaseResource>, 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);

View File

@ -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<? extends IValidatorModule> getSchematronValidatorClass() {
try {
return (Class<? extends IValidatorModule>) 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<? extends IValidatorModule> cls = getSchematronValidatorClass();
Constructor<? extends IValidatorModule> constructor = cls.getConstructor(FhirContext.class);
return constructor.newInstance(myContext);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Cannot construct schematron validator ", e);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}
}