Some work on jamesagnew/hapi-fhir/#1297

This commit is contained in:
jelmer.terwal 2019-10-04 16:21:49 +02:00 committed by James Agnew
parent d0704269d7
commit 5002a087bb
8 changed files with 395 additions and 37 deletions
hapi-fhir-base/src/main/java/ca/uhn/fhir/util
hapi-fhir-jaxrsserver-example
pom.xml
src
main/kotlin/cn/uhn/fhir/jaxrs/server/example
test/kotlin/ca/uhn/fhir/jaxrs/server/example
hapi-kotlin-test
pom.xml
src/main/kotlin/ca/uhn/fhir/rest/server

View File

@ -51,16 +51,8 @@ public class ReflectionUtil {
} }
public static Class<?> getGenericCollectionTypeOfField(Field next) { public static Class<?> getGenericCollectionTypeOfField(Field next) {
Class<?> type;
ParameterizedType collectionType = (ParameterizedType) next.getGenericType(); ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
Type firstArg = collectionType.getActualTypeArguments()[0]; return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
ParameterizedType pt = ((ParameterizedType) firstArg);
type = (Class<?>) pt.getRawType();
} else {
type = (Class<?>) firstArg;
}
return type;
} }
/** /**
@ -84,42 +76,37 @@ public class ReflectionUtil {
} }
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) { public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
Class<?> type;
Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex]; Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) { if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) {
return null; return null;
} }
ParameterizedType collectionType = (ParameterizedType) genericParameterType; ParameterizedType collectionType = (ParameterizedType) genericParameterType;
Type firstArg = collectionType.getActualTypeArguments()[0]; return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
ParameterizedType pt = ((ParameterizedType) firstArg);
type = (Class<?>) pt.getRawType();
} else {
type = (Class<?>) firstArg;
}
return type;
} }
@SuppressWarnings({ "rawtypes" })
public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) { public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
Class<?> type;
Type genericReturnType = theMethod.getGenericReturnType(); Type genericReturnType = theMethod.getGenericReturnType();
if (!(genericReturnType instanceof ParameterizedType)) { if (!(genericReturnType instanceof ParameterizedType)) {
return null; return null;
} }
ParameterizedType collectionType = (ParameterizedType) genericReturnType; ParameterizedType collectionType = (ParameterizedType) genericReturnType;
Type firstArg = collectionType.getActualTypeArguments()[0]; return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) { }
ParameterizedType pt = ((ParameterizedType) firstArg);
@SuppressWarnings({ "rawtypes" })
private static Class<?> getGenericCollectionTypeOf(Type theType) {
Class<?> type;
if (ParameterizedType.class.isAssignableFrom(theType.getClass())) {
ParameterizedType pt = ((ParameterizedType) theType);
type = (Class<?>) pt.getRawType(); type = (Class<?>) pt.getRawType();
} else if (firstArg instanceof TypeVariable<?>) { } else if (theType instanceof TypeVariable<?>) {
Type decl = ((TypeVariable) firstArg).getBounds()[0]; Type decl = ((TypeVariable) theType).getBounds()[0];
return (Class<?>) decl; return (Class<?>) decl;
} else if (firstArg instanceof WildcardType) { } else if (theType instanceof WildcardType) {
Type decl = ((WildcardType) firstArg).getUpperBounds()[0]; Type decl = ((WildcardType) theType).getUpperBounds()[0];
return (Class<?>) decl; return (Class<?>) decl;
} else { } else {
type = (Class<?>) firstArg; type = (Class<?>) theType;
} }
return type; return type;
} }
@ -154,8 +141,7 @@ public class ReflectionUtil {
public static Object newInstanceOfFhirServerType(String theType) { public static Object newInstanceOfFhirServerType(String theType) {
String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!"; String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer"; String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType); return newInstanceOfType(theType, errorMessage, wantedType);
return fhirServerVersion;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -1,7 +1,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<!-- Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management. You do not need to use this in your own projects, so the "parent" tag and it's contents below may be removed <!-- Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management. You do not need to use this in your own projects, so the "parent" tag and it's contents below may be removed
if you are using this file as a basis for your own project. --> if you are using this file as a basis for your own project. -->
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
@ -83,12 +83,22 @@
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-test-utilities</artifactId> <artifactId>hapi-fhir-test-utilities</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies> </dependencies>
@ -118,7 +128,38 @@
<skip>true</skip> <skip>true</skip>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals> <goal>compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals> <goal>test-compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<kotlin.version>1.3.50</kotlin.version>
</properties>
</project> </project>

View File

@ -0,0 +1,14 @@
package cn.uhn.fhir.jaxrs.server.example
import ca.uhn.fhir.model.api.annotation.ResourceDef
import org.hl7.fhir.dstu3.model.BaseResource
import org.hl7.fhir.dstu3.model.Organization
import org.hl7.fhir.instance.model.api.IIdType
@ResourceDef(name = "Organization")
open class ExtendedOrganization : Organization() {
override fun setId(value: IIdType?): BaseResource? {
return this
}
}

View File

@ -0,0 +1,35 @@
package cn.uhn.fhir.jaxrs.server.example
import ca.uhn.fhir.context.FhirContext
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider
import ca.uhn.fhir.model.api.Include
import ca.uhn.fhir.rest.annotation.IncludeParam
import ca.uhn.fhir.rest.annotation.OptionalParam
import ca.uhn.fhir.rest.annotation.Search
import ca.uhn.fhir.rest.api.Constants
import ca.uhn.fhir.rest.param.StringParam
import org.hl7.fhir.instance.model.api.IAnyResource
import javax.ejb.Stateless
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType
@Path("Organization")
@Stateless
@Produces(MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML)
class ExtendedOrganizationResource : BaseResource<ExtendedOrganization>() {
override fun getResourceType(): Class<ExtendedOrganization>? = ExtendedOrganization::class.java
@Search
fun find(
@OptionalParam(name = "_id") theId: StringParam?,
@IncludeParam(allow = ["Patient:general-practitioner"]) includes: Collection<Include>?
): List<ExtendedOrganization> {
val organization = ExtendedOrganization().also {
it.id = "id"
}
return listOf(organization)
}
}
abstract class BaseResource<T: IAnyResource>: AbstractJaxRsResourceProvider<T>(FhirContext.forDstu3())

View File

@ -0,0 +1,69 @@
package ca.uhn.fhir.jaxrs.server.example
import ca.uhn.fhir.test.utilities.JettyUtil
import ca.uhn.fhir.util.TestUtil
import cn.uhn.fhir.jaxrs.server.example.ExtendedOrganizationResource
import org.apache.commons.lang3.StringUtils
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.ServletContextHandler
import org.hamcrest.CoreMatchers.`is`
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
import org.junit.AfterClass
import org.junit.Assert.assertThat
import org.junit.BeforeClass
import org.junit.Test
import javax.ws.rs.core.Response
class ExtendedOrganizationResourceTest {
@Test
fun makeSureSearchDoesNotThrowOnIncludeParam() {
val response = ResteasyClientBuilder()
.build()
.target("http://localhost:$ourPort/Organization?_id=1")
.request()
.method("GET")
assertThat(
"This should not explode!",
response.status,
`is`(Response.Status.OK.statusCode)
)
}
companion object {
private var ourPort: Int = 0
private lateinit var jettyServer: Server
@JvmStatic
@AfterClass
@Throws(Exception::class)
fun afterClassClearContext() {
JettyUtil.closeServer(jettyServer)
TestUtil.clearAllStaticFieldsForUnitTest()
}
@JvmStatic
@BeforeClass
@Throws(Exception::class)
fun setUpClass() {
val context = ServletContextHandler(ServletContextHandler.SESSIONS).also {
it.contextPath = "/"
}
jettyServer = Server(0).also {
it.handler = context
}
val jerseyServlet = context.addServlet(HttpServletDispatcher::class.java, "/*").also {
it.initOrder = 0
//@formatter:off
it.setInitParameter(
"resteasy.resources",
StringUtils.join(listOf(ExtendedOrganizationResource::class.java.canonicalName), ",")
)
//@formatter:on
}
JettyUtil.startServer(jettyServer)
ourPort = JettyUtil.getPortForStartedServer(jettyServer)
}
}
}

176
hapi-kotlin-test/pom.xml Normal file
View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hapi-fhir</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>4.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hapi-kotlin-test</artifactId>
<name>HAPI FHIR - Kotlin tests</name>
<dependencies>
<!-- This dependency includes the core HAPI-FHIR classes -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>ca.uhn.hapi.fhir</groupId>-->
<!-- <artifactId>hapi-fhir-validation-resources-dstu3</artifactId>-->
<!-- <version>${project.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>ca.uhn.hapi.fhir</groupId>-->
<!-- <artifactId>hapi-fhir-structures-dstu3</artifactId>-->
<!-- <version>${project.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.dstu3</artifactId>
<version>${fhir_core_version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.ejb</groupId>-->
<!-- <artifactId>ejb-api</artifactId>-->
<!-- <version>3.0</version>-->
<!-- <scope>provided</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty_version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty_version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-test-utilities</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- This dependency includes the core HAPI-FHIR classes -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Include the HAPI server framework -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<!-- This dependency is used for the "FHIR Tester" web app overlay -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>${project.version}</version>
<classifier>classes</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals> <goal>compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/main/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals> <goal>test-compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
<sourceDir>${project.basedir}/src/test/java</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>no-arg</plugin>
<plugin>all-open</plugin>
</compilerPlugins>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Fragment-Host>
ca.uhn.hapi.fhir.hapi-fhir-base
</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<kotlin.version>1.3.50</kotlin.version>
</properties>
</project>

View File

@ -0,0 +1,15 @@
package ca.uhn.fhir.rest.server
import ca.uhn.fhir.rest.server.resources.ExtendedPatient
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider
import org.hl7.fhir.instance.model.api.IAnyResource
class Server : BaseResource<ExtendedPatient>() {
override fun getResourceType(): Class<ExtendedPatient>? = ExtendedPatient::class.java
}
abstract class BaseResource<T: IAnyResource>: AbstractJaxRsResourceProvider<T>()
fun main() {
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.rest.server.resources
import ca.uhn.fhir.model.api.annotation.Child
import ca.uhn.fhir.model.api.annotation.Extension
import ca.uhn.fhir.model.api.annotation.ResourceDef
import org.hl7.fhir.dstu3.model.CodeableConcept
import org.hl7.fhir.dstu3.model.Patient
import org.hl7.fhir.instance.model.api.IBaseResource
import java.util.Collections.emptyList
@ResourceDef(name = "Patient")
class ExtendedPatient : Patient() {
@Child(name = "status")
@Extension(
url = "http://some.url",
definedLocally = false,
isModifier = false
)
var status: List<CodeableConcept> = emptyList()
}