diff --git a/.travis.yml b/.travis.yml index 048e6b014cb..026f024b581 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,4 +20,4 @@ before_script: script: # - mvn -e -B clean install && cd hapi-fhir-ra && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID clean test jacoco:report coveralls:report - - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report + - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report diff --git a/HELPWANTED.md b/HELPWANTED.md new file mode 100644 index 00000000000..5ef933eda90 --- /dev/null +++ b/HELPWANTED.md @@ -0,0 +1,8 @@ +# Help Wanted + +This page is a work in progress! + +It serves as a place to list potential help a new volunteer could offer. + +* Investigate adding support for FHIR's RDF (Turtle) encoding to HAPI + diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.core.resources.prefs b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..e9441bb123e --- /dev/null +++ b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding/=UTF-8 diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.jdt.core.prefs b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..60105c1b951 --- /dev/null +++ b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.m2e.core.prefs b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000000..f897a7f1cb2 --- /dev/null +++ b/example-projects/hapi-fhir-base-example-embedded-ws/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/hapi-fhir-base-example-embedded-ws/pom.xml b/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml similarity index 71% rename from hapi-fhir-base-example-embedded-ws/pom.xml rename to example-projects/hapi-fhir-base-example-embedded-ws/pom.xml index a057289c490..ce746e8ae97 100644 --- a/hapi-fhir-base-example-embedded-ws/pom.xml +++ b/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml @@ -5,6 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir 2.3-SNAPSHOT + ../hapi-deployable-pom/pom.xml jar @@ -26,25 +27,40 @@ com.google.inject guice - 3.0 + 4.1.0 com.google.inject.extensions guice-servlet - 3.0 + 4.1.0 com.sun.jersey.contribs jersey-guice - 1.18.1 + 1.19.1 + + + org.ebaysf.web + cors-filter + 1.0.1 ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 - 1.2 + 2.1-SNAPSHOT + + + org.slf4j + slf4j-simple + 1.7.21 + + + org.slf4j + jul-to-slf4j + 1.7.21 - + @@ -56,5 +72,5 @@ - - \ No newline at end of file + + diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java new file mode 100644 index 00000000000..20cb9c43c7a --- /dev/null +++ b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java @@ -0,0 +1,27 @@ +package embedded; + +import javax.inject.Singleton; + +import org.ebaysf.web.cors.CORSFilter; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.servlet.GuiceServletContextListener; +import com.sun.jersey.guice.JerseyServletModule; + +public class ContextListener extends GuiceServletContextListener { + + @Override + protected Injector getInjector() { + + return Guice.createInjector(new JerseyServletModule() { + + @Override + protected void configureServlets() { + bind(CORSFilter.class).in(Singleton.class); + filter("/*").through(CORSFilter.class); + serve("/model/*").with(FhirRestfulServlet.class); + } + }); + } +} diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java similarity index 50% rename from hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java rename to example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java index a7e082015b9..e197c62edda 100644 --- a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java +++ b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java @@ -6,8 +6,6 @@ import java.util.List; import javax.inject.Singleton; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; -import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; @@ -29,29 +27,10 @@ public class FhirRestfulServlet extends RestfulServer { */ @Override public void initialize() { - /* - * Two resource providers are defined. Each one handles a specific type - * of resource. - */ final List providers = new ArrayList(); providers.add(new SomeResourceProvider()); setResourceProviders(providers); - /* - * Use a narrative generator. This is a completely optional step, but - * can be useful as it causes HAPI to generate narratives for resources - * which don't otherwise have one. - */ - final INarrativeGenerator narrativeGen = new DefaultThymeleafNarrativeGenerator(); - getFhirContext().setNarrativeGenerator(narrativeGen); - - /* - * Tells HAPI to use content types which are not technically FHIR - * compliant when a browser is detected as the requesting client. This - * prevents browsers from trying to download resource responses instead - * of displaying them inline which can be handy for troubleshooting. - */ - setUseBrowserFriendlyContentTypes(true); registerInterceptor(new ResponseHighlighterInterceptor()); diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java similarity index 65% rename from hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java rename to example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java index fb8dde91365..984829826a2 100644 --- a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java +++ b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java @@ -1,4 +1,5 @@ package embedded; + import java.util.EnumSet; import javax.servlet.DispatcherType; @@ -6,6 +7,7 @@ import javax.servlet.DispatcherType; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.slf4j.bridge.SLF4JBridgeHandler; import com.google.inject.servlet.GuiceFilter; @@ -13,13 +15,18 @@ public class ServerStartup { public static void main(final String[] args) throws Exception { + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + final Server server = new Server(9090); final ServletContextHandler sch = new ServletContextHandler(server, "/"); sch.addEventListener(new ContextListener()); - sch.addFilter(GuiceFilter.class, "/*", - EnumSet.of(DispatcherType.REQUEST)); + sch.addFilter(GuiceFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); sch.addServlet(DefaultServlet.class, "/"); - server.start(); + server.start(); + + // Service is now accessible through + // http://localhost:9090/model/Practitioner } } diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java similarity index 80% rename from hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java rename to example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java index aac9da8adb9..b1d9d80cbe0 100644 --- a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java +++ b/example-projects/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java @@ -4,6 +4,8 @@ import java.util.List; import org.hl7.fhir.instance.model.api.IBaseResource; +import com.google.common.collect.Lists; + import ca.uhn.fhir.model.dstu2.resource.Practitioner; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.RequiredParam; @@ -21,8 +23,9 @@ public class SomeResourceProvider implements IResourceProvider { @Search() public List findPractitionersByName( @RequiredParam(name = Practitioner.SP_NAME) final StringDt theName) { - throw new UnprocessableEntityException( - "Please provide more than 4 characters for the name"); +// throw new UnprocessableEntityException( +// "Please provide more than 4 characters for the name"); + return Lists.newArrayList(); } } diff --git a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.core.resources.prefs b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..f9fe34593fc --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=UTF-8 diff --git a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.jdt.core.prefs b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..714351aec19 --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.m2e.core.prefs b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000000..f897a7f1cb2 --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/example-projects/hapi-fhir-standalone-overlay-example/pom.xml b/example-projects/hapi-fhir-standalone-overlay-example/pom.xml new file mode 100644 index 00000000000..fef33f1705c --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/pom.xml @@ -0,0 +1,81 @@ + + 4.0.0 + + ca.uhn.hapi.fhir + hapi-fhir + 2.3-SNAPSHOT + ../hapi-deployable-pom/pom.xml + + hapi-fhir-standalone-overlay-example + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-war-plugin + + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + + + fhirtester + true + + + + + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + 2.3-SNAPSHOT + war + provided + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + 2.3-SNAPSHOT + classes + provided + + + org.eclipse.jetty + jetty-servlet + + + org.eclipse.jetty + jetty-webapp + + + com.google.inject + guice + 4.1.0 + + + com.google.inject.extensions + guice-servlet + 4.1.0 + + + com.sun.jersey.contribs + jersey-guice + 1.19.1 + + + org.ebaysf.web + cors-filter + 1.0.1 + + + + diff --git a/example-projects/hapi-fhir-standalone-overlay-example/src/main/java/embedded/example/ContextListener.java b/example-projects/hapi-fhir-standalone-overlay-example/src/main/java/embedded/example/ContextListener.java new file mode 100644 index 00000000000..ce89689f297 --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/src/main/java/embedded/example/ContextListener.java @@ -0,0 +1,51 @@ +package embedded.example; + +import javax.inject.Singleton; +import javax.servlet.ServletContextEvent; + +import org.ebaysf.web.cors.CORSFilter; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.servlet.GuiceServletContextListener; +import com.sun.jersey.guice.JerseyServletModule; + +public class ContextListener extends GuiceServletContextListener { + + static String username; + static String password; + static String serverAddress; + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) { + super.contextInitialized(servletContextEvent); + + username = servletContextEvent.getServletContext().getInitParameter("username") != null + ? servletContextEvent.getServletContext().getInitParameter("username") + : null; + password = servletContextEvent.getServletContext().getInitParameter("password") != null + ? servletContextEvent.getServletContext().getInitParameter("password") + : null; + serverAddress = servletContextEvent.getServletContext().getInitParameter("serverAddress") != null + ? servletContextEvent.getServletContext().getInitParameter("serverAddress") + : null; + } + + @Override + protected Injector getInjector() { + return Guice.createInjector(new JerseyServletModule() { + + @Override + protected void configureServlets() { + + AnnotationConfigWebApplicationContext webApp = new AnnotationConfigWebApplicationContext(); + webApp.setConfigLocation(FhirTesterConfig.class.getName()); + serve("/*").with(new DispatcherServlet(webApp)); + bind(CORSFilter.class).in(Singleton.class); + filter("/*").through(CORSFilter.class); + } + }); + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-standalone-overlay-example/src/main/java/embedded/example/FhirTesterConfig.java b/example-projects/hapi-fhir-standalone-overlay-example/src/main/java/embedded/example/FhirTesterConfig.java new file mode 100644 index 00000000000..91a3e9f6b25 --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/src/main/java/embedded/example/FhirTesterConfig.java @@ -0,0 +1,73 @@ +package embedded.example; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import com.google.common.base.Strings; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.client.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.to.FhirTesterMvcConfig; +import ca.uhn.fhir.to.TesterConfig; +import ca.uhn.fhir.util.ITestingUiClientFactory; + +//@formatter:off +/** + * This spring config file configures the web testing module. It serves two + * purposes: 1. It imports FhirTesterMvcConfig, which is the spring config for + * the tester itself 2. It tells the tester which server(s) to talk to, via the + * testerConfig() method below + */ +@Configuration +@Import(FhirTesterMvcConfig.class) +public class FhirTesterConfig { + + /** + * This bean tells the testing webpage which servers it should configure + * itself to communicate with. In this example we configure it to talk to + * the local server, as well as one public server. If you are creating a + * project to deploy somewhere else, you might choose to only put your own + * server's address here. + * + * Note the use of the ${serverBase} variable below. This will be replaced + * with the base URL as reported by the server itself. Often for a simple + * Tomcat (or other container) installation, this will end up being + * something like "http://localhost:8080/hapi-fhir-jpaserver-example". If + * you are deploying your server to a place with a fully qualified domain + * name, you might want to use that instead of using the variable. + */ + @Bean + public TesterConfig testerConfig() { + final TesterConfig retVal = new TesterConfig(); + retVal.addServer().withId("Test-Server").withFhirVersion(FhirVersionEnum.DSTU2) + .withBaseUrl(ContextListener.serverAddress).withName("FHIR Server Test Front End"); + + if (!Strings.isNullOrEmpty(ContextListener.username)) { + ITestingUiClientFactory clientFactory = new ITestingUiClientFactory() { + + @Override + public IGenericClient newClient(FhirContext theFhirContext, HttpServletRequest theRequest, + String theServerBaseUrl) { + // Create a client + IGenericClient client = theFhirContext.newRestfulGenericClient(theServerBaseUrl); + + // Register an interceptor which adds credentials + client.registerInterceptor( + new BasicAuthInterceptor(ContextListener.username, ContextListener.password)); + + return client; + } + + }; + retVal.setClientFactory(clientFactory); + } + return retVal; + } + +} +// @formatter:on \ No newline at end of file diff --git a/example-projects/hapi-fhir-standalone-overlay-example/src/main/webapp/WEB-INF/web.xml b/example-projects/hapi-fhir-standalone-overlay-example/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..8c985e61d0c --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,22 @@ + + + + Guice Filter + com.google.inject.servlet.GuiceFilter + + + Guice Filter + /* + + + embedded.example.ContextListener + + + + + serverAddress + http://fhirtest.uhn.ca/baseDstu2 + + \ No newline at end of file diff --git a/example-projects/hapi-fhir-standalone-overlay-example/src/test/java/test/WarTester.java b/example-projects/hapi-fhir-standalone-overlay-example/src/test/java/test/WarTester.java new file mode 100644 index 00000000000..fffbc626c75 --- /dev/null +++ b/example-projects/hapi-fhir-standalone-overlay-example/src/test/java/test/WarTester.java @@ -0,0 +1,14 @@ +package test; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.webapp.WebAppContext; + +public class WarTester { + + public static void main(String[] args) throws Exception { + final Server server = new Server(8080); + server.setHandler(new WebAppContext("target/fhirtester.war", "/")); + server.start(); + } + +} diff --git a/hapi-fhir-android-realm/.project b/hapi-fhir-android-realm/.project new file mode 100644 index 00000000000..13005c6b849 --- /dev/null +++ b/hapi-fhir-android-realm/.project @@ -0,0 +1,23 @@ + + + hapi-fhir-android-realm + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java deleted file mode 100644 index 02019b886cb..00000000000 --- a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java +++ /dev/null @@ -1,37 +0,0 @@ -package embedded; -import java.util.Map; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.servlet.GuiceServletContextListener; -import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter; -import com.sun.jersey.api.core.ResourceConfig; -import com.sun.jersey.guice.JerseyServletModule; - -import filters.CharsetResponseFilter; -import filters.CorsResponseFilter; - -public class ContextListener extends GuiceServletContextListener { - - @Override - protected Injector getInjector() { - - return Guice.createInjector(new JerseyServletModule() { - - @Override - protected void configureServlets() { - final Map params = ImmutableMap - . builder() - .put(ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS, - Joiner.on(";").join( - CharsetResponseFilter.class.getName(), - CorsResponseFilter.class.getName(), - GZIPContentEncodingFilter.class - .getName())).build(); - serve("/model/*").with(FhirRestfulServlet.class, params); - } - }); - } -} diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CharsetResponseFilter.java b/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CharsetResponseFilter.java deleted file mode 100644 index 656f7535578..00000000000 --- a/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CharsetResponseFilter.java +++ /dev/null @@ -1,24 +0,0 @@ -package filters; - - -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; - -import com.sun.jersey.spi.container.ContainerRequest; -import com.sun.jersey.spi.container.ContainerResponse; -import com.sun.jersey.spi.container.ContainerResponseFilter; - -public class CharsetResponseFilter implements ContainerResponseFilter { - - @Override - public ContainerResponse filter(final ContainerRequest request, - final ContainerResponse response) { - - final MediaType contentType = response.getMediaType(); - if (contentType != null) { - response.getHttpHeaders().putSingle(HttpHeaders.CONTENT_TYPE, - contentType.toString() + ";charset=UTF-8"); - } - return response; - } -} \ No newline at end of file diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CorsResponseFilter.java b/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CorsResponseFilter.java deleted file mode 100644 index cab06c28419..00000000000 --- a/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CorsResponseFilter.java +++ /dev/null @@ -1,33 +0,0 @@ -package filters; - - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; - -import com.sun.jersey.spi.container.ContainerRequest; -import com.sun.jersey.spi.container.ContainerResponse; -import com.sun.jersey.spi.container.ContainerResponseFilter; - -public class CorsResponseFilter implements ContainerResponseFilter { - - @Override - public ContainerResponse filter(final ContainerRequest req, - final ContainerResponse contResp) { - - final ResponseBuilder resp = Response.fromResponse(contResp - .getResponse()); - resp.header("Access-Control-Allow-Origin", "*").header( - "Access-Control-Allow-Methods", "GET, POST, OPTIONS"); - - final String reqHead = req - .getHeaderValue("Access-Control-Request-Headers"); - - if (null != reqHead && !reqHead.equals("")) { - resp.header("Access-Control-Allow-Headers", reqHead); - } - - contResp.setResponse(resp.build()); - return contResp; - } - -} \ No newline at end of file diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index edcbecb4ea6..8e60f760bb5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -82,87 +82,22 @@ public class FhirContext { private Map> myDefaultTypeForProfile = new HashMap>(); private volatile Map myIdToResourceDefinition = Collections.emptyMap(); private boolean myInitialized; + private boolean myInitializing; private HapiLocalizer myLocalizer = new HapiLocalizer(); private volatile Map> myNameToElementDefinition = Collections.emptyMap(); private volatile Map myNameToResourceDefinition = Collections.emptyMap(); private volatile Map> myNameToResourceType; private volatile INarrativeGenerator myNarrativeGenerator; private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler(); + private ParserOptions myParserOptions = new ParserOptions(); private Set myPerformanceOptions = new HashSet(); private Collection> myResourceTypesToScan; private volatile IRestfulClientFactory myRestfulClientFactory; private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition; - private final IFhirVersion myVersion; - private Map>> myVersionToNameToResourceType = Collections.emptyMap(); - private boolean myInitializing; private IContextValidationSupport myValidationSupport; + private final IFhirVersion myVersion; - /** - * Returns the validation support module configured for this context, creating a default - * implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)} - * method - * @see #setValidationSupport(IContextValidationSupport) - */ - public IContextValidationSupport getValidationSupport() { - if (myValidationSupport == null) { - myValidationSupport = myVersion.createValidationSupport(); - } - return myValidationSupport; - } - - /** - * Creates a new FluentPath engine which can be used to exvaluate - * path expressions over FHIR resources. Note that this engine will use the - * {@link IContextValidationSupport context validation support} module which is - * configured on the context at the time this method is called. - *

- * In other words, call {@link #setValidationSupport(IContextValidationSupport)} before - * calling {@link #newFluentPath()} - *

- *

- * Note that this feature was added for FHIR DSTU3 and is not available - * for contexts configured to use an older version of FHIR. Calling this method - * on a context for a previous version of fhir will result in an - * {@link UnsupportedOperationException} - *

- * - * @since 2.2 - */ - public IFluentPath newFluentPath() { - return myVersion.createFluentPathExecutor(this); - } - - /** - * Sets the validation support module to use for this context. The validation support module - * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) - * as well as to provide terminology services to modules such as the validator and FluentPath executor - */ - public void setValidationSupport(IContextValidationSupport theValidationSupport) { - myValidationSupport = theValidationSupport; - } - - private ParserOptions myParserOptions = new ParserOptions(); - - /** - * Returns the parser options object which will be used to supply default - * options to newly created parsers - * - * @return The parser options - Will not return null - */ - public ParserOptions getParserOptions() { - return myParserOptions; - } - - /** - * Sets the parser options object which will be used to supply default - * options to newly created parsers - * - * @param theParserOptions The parser options object - Must not be null - */ - public void setParserOptions(ParserOptions theParserOptions) { - Validate.notNull(theParserOptions, "theParserOptions must not be null"); - myParserOptions = theParserOptions; - } + private Map>> myVersionToNameToResourceType = Collections.emptyMap(); /** * @deprecated It is recommended that you use one of the static initializer methods instead @@ -172,7 +107,7 @@ public class FhirContext { public FhirContext() { this(EMPTY_LIST); } - + /** * @deprecated It is recommended that you use one of the static initializer methods instead * of this method, e.g. {@link #forDstu2()} or {@link #forDstu3()} @@ -282,7 +217,7 @@ public class FhirContext { validateInitialized(); return myNameToResourceDefinition.values(); } - + /** * Returns the default resource type for the given profile * @@ -332,7 +267,7 @@ public class FhirContext { validateInitialized(); return Collections.unmodifiableCollection(myClassToElementDefinition.values()); } - + /** * This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with * caution @@ -348,6 +283,16 @@ public class FhirContext { return myNarrativeGenerator; } + /** + * Returns the parser options object which will be used to supply default + * options to newly created parsers + * + * @return The parser options - Will not return null + */ + public ParserOptions getParserOptions() { + return myParserOptions; + } + /** * Get the configured performance options */ @@ -449,6 +394,20 @@ public class FhirContext { return myIdToResourceDefinition.get(theId); } +// /** +// * Return an unmodifiable collection containing all known resource definitions +// */ +// public Collection getResourceDefinitions() { +// +// Set> datatypes = Collections.emptySet(); +// Map, BaseRuntimeElementDefinition> existing = Collections.emptyMap(); +// HashMap> types = new HashMap>(); +// ModelScanner.scanVersionPropertyFile(datatypes, types, myVersion.getVersion(), existing); +// for (int next : types.) +// +// return Collections.unmodifiableCollection(myIdToResourceDefinition.values()); +// } + /** * Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the * core library. @@ -476,6 +435,19 @@ public class FhirContext { return myRuntimeChildUndeclaredExtensionDefinition; } + /** + * Returns the validation support module configured for this context, creating a default + * implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)} + * method + * @see #setValidationSupport(IContextValidationSupport) + */ + public IContextValidationSupport getValidationSupport() { + if (myValidationSupport == null) { + myValidationSupport = myVersion.createValidationSupport(); + } + return myValidationSupport; + } + public IFhirVersion getVersion() { return myVersion; } @@ -500,6 +472,28 @@ public class FhirContext { return myVersion.newBundleFactory(this); } + /** + * Creates a new FluentPath engine which can be used to exvaluate + * path expressions over FHIR resources. Note that this engine will use the + * {@link IContextValidationSupport context validation support} module which is + * configured on the context at the time this method is called. + *

+ * In other words, call {@link #setValidationSupport(IContextValidationSupport)} before + * calling {@link #newFluentPath()} + *

+ *

+ * Note that this feature was added for FHIR DSTU3 and is not available + * for contexts configured to use an older version of FHIR. Calling this method + * on a context for a previous version of fhir will result in an + * {@link UnsupportedOperationException} + *

+ * + * @since 2.2 + */ + public IFluentPath newFluentPath() { + return myVersion.createFluentPathExecutor(this); + } + /** * Create and return a new JSON parser. * @@ -783,6 +777,17 @@ public class FhirContext { myParserErrorHandler = theParserErrorHandler; } + /** + * Sets the parser options object which will be used to supply default + * options to newly created parsers + * + * @param theParserOptions The parser options object - Must not be null + */ + public void setParserOptions(ParserOptions theParserOptions) { + Validate.notNull(theParserOptions, "theParserOptions must not be null"); + myParserOptions = theParserOptions; + } + /** * Sets the configured performance options * @@ -818,6 +823,15 @@ public class FhirContext { this.myRestfulClientFactory = theRestfulClientFactory; } + /** + * Sets the validation support module to use for this context. The validation support module + * is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc) + * as well as to provide terminology services to modules such as the validator and FluentPath executor + */ + public void setValidationSupport(IContextValidationSupport theValidationSupport) { + myValidationSupport = theValidationSupport; + } + @SuppressWarnings({ "cast" }) private List> toElementList(Collection> theResourceTypes) { if (theResourceTypes == null) { @@ -850,6 +864,13 @@ public class FhirContext { return new FhirContext(FhirVersionEnum.DSTU2); } + /** + * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) + */ + public static FhirContext forDstu2_1() { + return new FhirContext(FhirVersionEnum.DSTU2_1); + } + /** * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference * Implementation Structures) @@ -885,11 +906,4 @@ public class FhirContext { return retVal; } - /** - * Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2} (2016 May DSTU3 Snapshot) - */ - public static FhirContext forDstu2_1() { - return new FhirContext(FhirVersionEnum.DSTU2_1); - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 42776441e9a..e1d96461213 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -34,6 +34,7 @@ import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.hl7.fhir.instance.model.api.*; +import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IElement; @@ -437,7 +438,7 @@ class ModelScanner { } - RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments, toTargetList(searchParam.target())); + RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments, toTargetList(searchParam.target()), RuntimeSearchParamStatusEnum.ACTIVE); theResourceDef.addSearchParam(param); nameToParam.put(param.getName(), param); } @@ -457,7 +458,7 @@ class ModelScanner { compositeOf.add(param); } - RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), RestSearchParameterTypeEnum.COMPOSITE, compositeOf, null, toTargetList(searchParam.target())); + RuntimeSearchParam param = new RuntimeSearchParam(null, null, searchParam.name(), searchParam.description(), searchParam.path(), RestSearchParameterTypeEnum.COMPOSITE, compositeOf, null, toTargetList(searchParam.target()), RuntimeSearchParamStatusEnum.ACTIVE); theResourceDef.addSearchParam(param); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java index 22da3f10e0f..28c7377674a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.Set; import java.util.StringTokenizer; +import org.hl7.fhir.instance.model.api.IIdType; + import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; /* @@ -29,7 +31,7 @@ import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum; */ public class RuntimeSearchParam { - + private final IIdType myId; private final List myCompositeOf; private final String myDescription; private final String myName; @@ -37,15 +39,28 @@ public class RuntimeSearchParam { private final String myPath; private final Set myTargets; private final Set myProvidesMembershipInCompartments; + private final RuntimeSearchParamStatusEnum myStatus; + private final String myUri; - public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List theCompositeOf, - Set theProvidesMembershipInCompartments, Set theTargets) { + public IIdType getId() { + return myId; + } + + public String getUri() { + return myUri; + } + + public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List theCompositeOf, + Set theProvidesMembershipInCompartments, Set theTargets, RuntimeSearchParamStatusEnum theStatus) { super(); + myId = theId; + myUri = theUri; myName = theName; myDescription = theDescription; myPath = thePath; myParamType = theParamType; myCompositeOf = theCompositeOf; + myStatus = theStatus; if (theProvidesMembershipInCompartments != null && !theProvidesMembershipInCompartments.isEmpty()) { myProvidesMembershipInCompartments = Collections.unmodifiableSet(theProvidesMembershipInCompartments); } else { @@ -62,8 +77,12 @@ public class RuntimeSearchParam { return myTargets; } - public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set theProvidesMembershipInCompartments, Set theTargets) { - this(theName, theDescription, thePath, theParamType, null, theProvidesMembershipInCompartments, theTargets); + public RuntimeSearchParamStatusEnum getStatus() { + return myStatus; + } + + public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set theProvidesMembershipInCompartments, Set theTargets, RuntimeSearchParamStatusEnum theStatus) { + this(null, null, theName, theDescription, thePath, theParamType, null, theProvidesMembershipInCompartments, theTargets, theStatus); } public List getCompositeOf() { @@ -108,4 +127,10 @@ public class RuntimeSearchParam { return myProvidesMembershipInCompartments; } + public enum RuntimeSearchParamStatusEnum { + ACTIVE, + DRAFT, + RETIRED + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeElement.java index 48c78514743..d3cdd977866 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ICompositeElement.java @@ -28,7 +28,15 @@ public interface ICompositeElement extends IElement { * Returns a list containing all child elements matching a given type * * @param theType The type to match. If set to null, all child elements will be returned + * + * @deprecated This method is not used by HAPI at this point, so there isn't much + * point to keeping it around. We are not deleting it just so that we don't break + * existing implementer code, but you do not need to supply an implementation + * of this code in your own structures. Deprecated in HAPI FHIR 2.3 (Jan 2017). + * See See for + * a discussion about this. */ + @Deprecated List getAllPopulatedChildElementsOfType(Class theType); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OptionalParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OptionalParam.java index 5a1b573b030..6b7bf8a3df6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OptionalParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/OptionalParam.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.annotation; +import java.lang.annotation.ElementType; + /* * #%L * HAPI FHIR - Core Library @@ -10,7 +12,7 @@ package ca.uhn.fhir.rest.annotation; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -22,19 +24,19 @@ package ca.uhn.fhir.rest.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.ReferenceParam; -//import ca.uhn.fhir.testmodel.Patient; // TODO: qualify this correctly - /** - * Parameter annotation which specifies a search parameter for a {@link Search} method. + * Parameter annotation which specifies a search parameter for a {@link Search} method. */ @Retention(RetentionPolicy.RUNTIME) +@Target(value=ElementType.PARAMETER) public @interface OptionalParam { public static final String ALLOW_CHAIN_ANY = "*"; @@ -42,63 +44,63 @@ public @interface OptionalParam { public static final String ALLOW_CHAIN_NOTCHAINED = ""; /** - * For reference parameters ({@link ReferenceParam}) this value may be - * used to indicate which chain values (if any) are not valid - * for the given parameter. Values here will supercede any values specified - * in {@link #chainWhitelist()} - *

- * If the parameter annotated with this annotation is not a {@link ReferenceParam}, - * this value must not be populated. - *

- */ + * For reference parameters ({@link ReferenceParam}) this value may be + * used to indicate which chain values (if any) are not valid + * for the given parameter. Values here will supercede any values specified + * in {@link #chainWhitelist()} + *

+ * If the parameter annotated with this annotation is not a {@link ReferenceParam}, + * this value must not be populated. + *

+ */ String[] chainBlacklist() default {}; - - /** - * For reference parameters ({@link ReferenceParam}) this value may be - * used to indicate which chain values (if any) are valid for the given - * parameter. If the list contains the value {@link #ALLOW_CHAIN_ANY}, all values are valid. (this is the default) - * If the list contains the value {@link #ALLOW_CHAIN_NOTCHAINED} - * then the reference param only supports the empty chain (i.e. the resource - * ID). - *

- * Valid values for this parameter include: - *

- *
    - *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_NOTCHAINED } - Only allow resource reference (no chaining allowed for this parameter)
  • - *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_ANY } - Allow any chaining at all (including a non chained value, this is the default)
  • - *
  • chainWhitelist={ "foo", "bar" } - Allow property.foo and property.bar
  • - *
- *

- * Any values specified in - * {@link #chainBlacklist()} will supercede (have priority over) values - * here. - *

- *

- * If the parameter annotated with this annotation is not a {@link ReferenceParam}, - * this value must not be populated. - *

- */ - String[] chainWhitelist() default {ALLOW_CHAIN_ANY}; - - /** - * For composite parameters ({@link CompositeParam}) this value may be - * used to indicate the parameter type(s) which may be referenced by this param. - *

- * If the parameter annotated with this annotation is not a {@link CompositeParam}, - * this value must not be populated. - *

- */ - Class[] compositeTypes() default {}; - /** - * This is the name for the parameter. Generally this should be a + /** + * For reference parameters ({@link ReferenceParam}) this value may be + * used to indicate which chain values (if any) are valid for the given + * parameter. If the list contains the value {@link #ALLOW_CHAIN_ANY}, all values are valid. (this is the default) + * If the list contains the value {@link #ALLOW_CHAIN_NOTCHAINED} + * then the reference param only supports the empty chain (i.e. the resource + * ID). + *

+ * Valid values for this parameter include: + *

+ *
    + *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_NOTCHAINED } - Only allow resource reference (no chaining allowed for this parameter)
  • + *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_ANY } - Allow any chaining at all (including a non chained value, this is the default)
  • + *
  • chainWhitelist={ "foo", "bar" } - Allow property.foo and property.bar
  • + *
+ *

+ * Any values specified in + * {@link #chainBlacklist()} will supercede (have priority over) values + * here. + *

+ *

+ * If the parameter annotated with this annotation is not a {@link ReferenceParam}, + * this value must not be populated. + *

+ */ + String[] chainWhitelist() default { ALLOW_CHAIN_ANY }; + + /** + * For composite parameters ({@link CompositeParam}) this value may be + * used to indicate the parameter type(s) which may be referenced by this param. + *

+ * If the parameter annotated with this annotation is not a {@link CompositeParam}, + * this value must not be populated. + *

+ */ + Class[] compositeTypes() default {}; + + /** + * This is the name for the parameter. Generally this should be a * simple string (e.g. "name", or "identifier") which will be the name * of the URL parameter used to populate this method parameter. *

* Most resource model classes have constants which may be used to * supply values for this field, e.g. Patient.SP_NAME or * Observation.SP_DATE - *

+ *

*

* If you wish to specify a parameter for a resource reference which * only accepts a specific chained value, it is also valid to supply @@ -109,13 +111,13 @@ public @interface OptionalParam { */ String name(); - /** - * For resource reference parameters ({@link ReferenceParam}) this value may be - * used to indicate the resource type(s) which may be referenced by this param. - *

- * If the parameter annotated with this annotation is not a {@link ReferenceParam}, - * this value must not be populated. - *

- */ - Class[] targetTypes() default {}; + /** + * For resource reference parameters ({@link ReferenceParam}) this value may be + * used to indicate the resource type(s) which may be referenced by this param. + *

+ * If the parameter annotated with this annotation is not a {@link ReferenceParam}, + * this value must not be populated. + *

+ */ + Class[] targetTypes() default {}; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RawParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RawParam.java new file mode 100644 index 00000000000..24772e1ecd9 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RawParam.java @@ -0,0 +1,40 @@ +package ca.uhn.fhir.rest.annotation; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2017 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.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * On a {@link Search} method, a parameter marked with this annotation + * will receive search parameters not captured by other parameters. + *

+ * Parameters with this annotation must be of type + * {@code Map>} + *

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value=ElementType.PARAMETER) +public @interface RawParam { + // nothing +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RequiredParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RequiredParam.java index af12e9e14ac..9efda9da757 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RequiredParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/RequiredParam.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.annotation; +import java.lang.annotation.ElementType; + /* * #%L * HAPI FHIR - Core Library @@ -10,7 +12,7 @@ package ca.uhn.fhir.rest.annotation; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -22,18 +24,19 @@ package ca.uhn.fhir.rest.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.ReferenceParam; -//import ca.uhn.fhir.testmodel.Patient; // TODO: qualify this correctly -@Retention(RetentionPolicy.RUNTIME) /** - * Parameter annotation which specifies a search parameter for a {@link Search} method. + * Parameter annotation which specifies a search parameter for a {@link Search} method. */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value=ElementType.PARAMETER) public @interface RequiredParam { /** @@ -45,32 +48,32 @@ public @interface RequiredParam { */ String[] chainBlacklist() default {}; - /** - * For reference parameters ({@link ReferenceParam}) this value may be - * used to indicate which chain values (if any) are valid for the given - * parameter. If the list contains the value {@link OptionalParam#ALLOW_CHAIN_ANY}, all values are valid. (this is the default) - * If the list contains the value {@link OptionalParam#ALLOW_CHAIN_NOTCHAINED} - * then the reference param only supports the empty chain (i.e. the resource - * ID). - *

- * Valid values for this parameter include: - *

- *
    - *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_NOTCHAINED } - Only allow resource reference (no chaining allowed for this parameter)
  • - *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_ANY } - Allow any chaining at all (including a non chained value, this is the default)
  • - *
  • chainWhitelist={ "foo", "bar" } - Allow property.foo and property.bar
  • - *
- *

- * Any values specified in - * {@link #chainBlacklist()} will supercede (have priority over) values - * here. - *

- *

- * If the parameter annotated with this annotation is not a {@link ReferenceParam}, - * this value must not be populated. - *

- */ - String[] chainWhitelist() default {OptionalParam.ALLOW_CHAIN_ANY}; + /** + * For reference parameters ({@link ReferenceParam}) this value may be + * used to indicate which chain values (if any) are valid for the given + * parameter. If the list contains the value {@link OptionalParam#ALLOW_CHAIN_ANY}, all values are valid. (this is the default) + * If the list contains the value {@link OptionalParam#ALLOW_CHAIN_NOTCHAINED} + * then the reference param only supports the empty chain (i.e. the resource + * ID). + *

+ * Valid values for this parameter include: + *

+ *
    + *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_NOTCHAINED } - Only allow resource reference (no chaining allowed for this parameter)
  • + *
  • chainWhitelist={ OptionalParam.ALLOW_CHAIN_ANY } - Allow any chaining at all (including a non chained value, this is the default)
  • + *
  • chainWhitelist={ "foo", "bar" } - Allow property.foo and property.bar
  • + *
+ *

+ * Any values specified in + * {@link #chainBlacklist()} will supercede (have priority over) values + * here. + *

+ *

+ * If the parameter annotated with this annotation is not a {@link ReferenceParam}, + * this value must not be populated. + *

+ */ + String[] chainWhitelist() default { OptionalParam.ALLOW_CHAIN_ANY }; /** * For composite parameters ({@link CompositeParam}) this parameter may be used to indicate the parameter type(s) which may be referenced by this param. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java index f7e9e949af8..a3e02c64ea2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/interceptor/CapturingInterceptor.java @@ -23,6 +23,11 @@ package ca.uhn.fhir.rest.client.interceptor; import ca.uhn.fhir.rest.client.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; + +import java.io.IOException; /** * Client interceptor which simply captures request and response objects and stores them so that they can be inspected after a client @@ -55,8 +60,23 @@ public class CapturingInterceptor implements IClientInterceptor { } @Override - public void interceptResponse(IHttpResponse theRequest) { - myLastResponse = theRequest; + public void interceptResponse(IHttpResponse theResponse) { + //Buffer the reponse to avoid errors when content has already been read and the entity is not repeatable + try { + if(theResponse.getResponse() instanceof HttpResponse) { + HttpEntity entity = ((HttpResponse) theResponse.getResponse()).getEntity(); + if( entity != null && !entity.isRepeatable()){ + theResponse.bufferEntity(); + } + } else { + theResponse.bufferEntity(); + } + } catch (IOException e) { + throw new InternalErrorException("Unable to buffer the entity for capturing", e); + } + + + myLastResponse = theResponse; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index acf66dc7a1e..0a1ec69dbfd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -407,6 +407,8 @@ public class MethodUtil { parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType); MethodUtil.extractDescription(parameter, annotations); param = parameter; + } else if (nextAnnotation instanceof RawParam) { + param = new RawParamsParmeter(parameters); } else if (nextAnnotation instanceof IncludeParam) { Class> instantiableCollectionType; Class specType; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RawParamsParmeter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RawParamsParmeter.java new file mode 100644 index 00000000000..fae9a90bc63 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/RawParamsParmeter.java @@ -0,0 +1,94 @@ +package ca.uhn.fhir.rest.method; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2017 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.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.annotation.RawParam; +import ca.uhn.fhir.rest.method.SearchMethodBinding.QualifierDetails; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; + +public class RawParamsParmeter implements IParameter { + + private final List myAllMethodParameters; + + public RawParamsParmeter(List theParameters) { + myAllMethodParameters = theParameters; + } + + @Override + public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map> theTargetQueryArguments, IBaseResource theTargetResource) + throws InternalErrorException { + // not supported on client for now + } + + @Override + public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException { + HashMap> retVal = null; + + for (String nextName : theRequest.getParameters().keySet()) { + if (nextName.startsWith("_")) { + continue; + } + + QualifierDetails qualifiers = SearchMethodBinding.extractQualifiersFromParameterName(nextName); + + boolean alreadyCaptured = false; + for (IParameter nextParameter : myAllMethodParameters) { + if (nextParameter instanceof SearchParameter) { + SearchParameter nextSearchParam = (SearchParameter)nextParameter; + if (nextSearchParam.getName().equals(qualifiers.getParamName())) { + if (qualifiers.passes(nextSearchParam.getQualifierWhitelist(), nextSearchParam.getQualifierBlacklist())) { + alreadyCaptured = true; + break; + } + } + } + } + + if (!alreadyCaptured) { + if (retVal == null) { + retVal = new HashMap>(); + } + retVal.put(nextName, Arrays.asList(theRequest.getParameters().get(nextName))); + } + + } + + return retVal; + } + + @Override + public void initializeTypes(Method theMethod, Class> theOuterCollectionType, Class> theInnerCollectionType, Class theParameterType) { + Validate.isTrue(theParameterType.equals(Map.class), "Parameter with @" + RawParam.class + " must be of type Map>"); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java index 9311d60edb3..3ca67891b3f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java @@ -37,7 +37,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; @@ -131,15 +130,26 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { @Override public ReturnTypeEnum getReturnType() { -// if (getContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) { return ReturnTypeEnum.BUNDLE; -// } else { -// return ReturnTypeEnum.RESOURCE; -// } } @Override public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) { + + String clientPreference = theRequest.getHeader(Constants.HEADER_PREFER); + boolean lenientHandling = false; + if(clientPreference != null) + { + String[] preferences = clientPreference.split(";"); + for( String p : preferences){ + if("handling:lenient".equalsIgnoreCase(p)) + { + lenientHandling = true; + break; + } + } + } + if (theRequest.getId() != null && myIdParamIndex == null) { ourLog.trace("Method {} doesn't match because ID is not null: {}", theRequest.getId()); return false; @@ -235,6 +245,9 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { } } Set keySet = theRequest.getParameters().keySet(); + if(lenientHandling == true) + return true; + if (myAllowUnknownParams == false) { for (String next : keySet) { if (!methodParamsTemp.contains(next)) { @@ -271,7 +284,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { } @Override - public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { + public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException { if (myIdParamIndex != null) { theMethodParams[myIdParamIndex] = theRequest.getId(); } @@ -389,14 +402,27 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { if (dotIdx < colonIdx) { retVal.setDotQualifier(theParamName.substring(dotIdx, colonIdx)); retVal.setColonQualifier(theParamName.substring(colonIdx)); + retVal.setParamName(theParamName.substring(0, dotIdx)); + retVal.setWholeQualifier(theParamName.substring(dotIdx)); } else { retVal.setColonQualifier(theParamName.substring(colonIdx, dotIdx)); retVal.setDotQualifier(theParamName.substring(dotIdx)); + retVal.setParamName(theParamName.substring(0, colonIdx)); + retVal.setWholeQualifier(theParamName.substring(colonIdx)); } } else if (dotIdx != -1) { retVal.setDotQualifier(theParamName.substring(dotIdx)); + retVal.setParamName(theParamName.substring(0, dotIdx)); + retVal.setWholeQualifier(theParamName.substring(dotIdx)); } else if (colonIdx != -1) { retVal.setColonQualifier(theParamName.substring(colonIdx)); + retVal.setParamName(theParamName.substring(0, colonIdx)); + retVal.setWholeQualifier(theParamName.substring(colonIdx)); + } else { + retVal.setParamName(theParamName); + retVal.setColonQualifier(null); + retVal.setDotQualifier(null); + retVal.setWholeQualifier(null); } return retVal; @@ -406,6 +432,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { private String myColonQualifier; private String myDotQualifier; + private String myParamName; + private String myWholeQualifier; public boolean passes(Set theQualifierWhitelist, Set theQualifierBlacklist) { if (theQualifierWhitelist != null) { @@ -451,6 +479,14 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { return true; } + public void setParamName(String theParamName) { + myParamName = theParamName; + } + + public String getParamName() { + return myParamName; + } + public void setColonQualifier(String theColonQualifier) { myColonQualifier = theColonQualifier; } @@ -459,6 +495,14 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { myDotQualifier = theDotQualifier; } + public String getWholeQualifier() { + return myWholeQualifier; + } + + public void setWholeQualifier(String theWholeQualifier) { + myWholeQualifier = theWholeQualifier; + } + } public static BaseHttpClientInvocation createSearchInvocation(FhirContext theContext, String theSearchUrl, Map> theParams) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java index 259d188cd91..1b63cfc9d2e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParameterUtil.java @@ -108,8 +108,10 @@ public class ParameterUtil { } else { if (b.length() > 0) { retVal.add(b.toString()); - b.setLength(0); + } else { + retVal.add(null); } + b.setLength(0); } } } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index 5a64b6b0248..6d3677e6f08 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -61,7 +61,6 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.method.ElementsParameter; import ca.uhn.fhir.rest.method.RequestDetails; @@ -378,7 +377,7 @@ public class RestfulServerUtils { break; } } catch (IllegalArgumentException e) { - ourLog.debug("Invalid {} parameger: {}", Constants.PARAM_NARRATIVE, narrative[0]); + ourLog.debug("Invalid {} parameter: {}", Constants.PARAM_NARRATIVE, narrative[0]); } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index 5cb84b72763..3000ed48133 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -28,10 +28,7 @@ import java.io.Reader; import java.nio.charset.Charset; import java.util.Collections; import java.util.Enumeration; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.zip.GZIPInputStream; import javax.servlet.http.HttpServletRequest; @@ -41,7 +38,6 @@ import org.apache.commons.io.IOUtils; import org.apache.http.entity.ContentType; import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.BaseMethodBinding.IRequestReader; import ca.uhn.fhir.rest.method.RequestDetails; @@ -168,17 +164,6 @@ public class ServletRequestDetails extends RequestDetails { this.myServletResponse = myServletResponse; } - public static RequestDetails withResourceAndParams(String theResourceName, RequestTypeEnum theRequestType, Set theParamNames) { - RequestDetails retVal = new ServletRequestDetails(); - retVal.setResourceName(theResourceName); - retVal.setRequestType(theRequestType); - Map paramNames = new HashMap(); - for (String next : theParamNames) { - paramNames.put(next, new String[0]); - } - retVal.setParameters(paramNames); - return retVal; - } @Override public Charset getCharset() { diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java index 43e8076acfc..1a54dc7db73 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlParser.java @@ -38,7 +38,7 @@ package org.hl7.fhir.utilities.xhtml; * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -48,7 +48,6 @@ package org.hl7.fhir.utilities.xhtml; * #L% */ - import java.io.*; import java.util.*; @@ -378,10 +377,12 @@ public class XhtmlParser { } return result; } + public XhtmlDocument parse(String source, String entryName) throws FHIRFormatError, IOException { rdr = new StringReader(source); return parse(entryName); } + private void parseAttributes(XhtmlNode node) throws FHIRFormatError, IOException { while (Character.isWhitespace(peekChar())) readChar(); @@ -410,6 +411,7 @@ public class XhtmlParser { readChar(); } } + private String parseAttributeValue(char term) throws IOException, FHIRFormatError { StringBuilder b = new StringBuilder(); while (peekChar() != '\0' && peekChar() != '>' && (term != '\0' || peekChar() != '/') && peekChar() != term) { @@ -422,6 +424,7 @@ public class XhtmlParser { readChar(); return b.toString(); } + private void parseElement(XhtmlNode parent, List parents, NSMap nsm) throws IOException, FHIRFormatError { QName name = new QName(readName()); XhtmlNode node = parent.addTag(name.getName()); @@ -438,6 +441,7 @@ public class XhtmlParser { parseElementInner(node, newParents, nsm); } } + private void parseElementInner(XhtmlNode node, List parents, NSMap nsm) throws FHIRFormatError, IOException { StringBuilder s = new StringBuilder(); while (peekChar() != '\0' && !parents.contains(unwindPoint) && !(node == unwindPoint)) { @@ -488,6 +492,7 @@ public class XhtmlParser { } addTextNode(node, s); } + private XhtmlNode parseFragment() throws IOException, FHIRException { skipWhiteSpace(); if (peekChar() != '<') @@ -558,6 +563,8 @@ public class XhtmlParser { s.append(XhtmlNode.NBSP); else if (c.equals("amp")) s.append('&'); + else if (c.equals("rsquo")) + s.append('’'); else if (c.equals("gt")) s.append('>'); else if (c.equals("lt")) @@ -860,12 +867,12 @@ public class XhtmlParser { StartElement firstEvent = (StartElement) xpp.nextEvent(); res.setName(firstEvent.getSchemaType().getLocalPart()); - for (Iterator attrIter = firstEvent.getAttributes(); attrIter.hasNext(); ) { + for (Iterator attrIter = firstEvent.getAttributes(); attrIter.hasNext();) { Attribute nextAttr = (Attribute) attrIter.next(); if (attributeIsOk(firstEvent.getName().getLocalPart(), nextAttr.getName().getLocalPart(), nextAttr.getValue())) res.getAttributes().put(nextAttr.getName().getLocalPart(), nextAttr.getValue()); } - + while (xpp.hasNext()) { XMLEvent nextEvent = xpp.nextEvent(); int eventType = nextEvent.getEventType(); @@ -873,11 +880,11 @@ public class XhtmlParser { break; } if (eventType == XMLEvent.CHARACTERS) { - res.addText(((Characters)xpp).getData()); + res.addText(((Characters) xpp).getData()); } else if (eventType == XMLEvent.COMMENT) { - res.addComment(((Comment)xpp).getText()); + res.addComment(((Comment) xpp).getText()); } else if (eventType == XMLEvent.START_ELEMENT) { - StartElement nextStart = (StartElement)nextEvent; + StartElement nextStart = (StartElement) nextEvent; if (elementIsOk(nextStart.getName().getLocalPart())) { res.getChildNodes().add(parseNode(xpp)); } diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index ff82937d610..636b56fc038 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -43,11 +43,9 @@ ca.uhn.fhir.rest.param.ResourceParameter.noContentTypeInRequest=No Content-Type ca.uhn.fhir.rest.param.ResourceParameter.failedToParseRequest=Failed to parse request body as {0} resource. Error was: {1} ca.uhn.fhir.parser.ParserState.wrongResourceTypeFound=Incorrect resource type found, expected "{0}" but found "{1}" - ca.uhn.fhir.rest.server.RestfulServer.getPagesNonHttpGet=Requests for _getpages must use HTTP GET ca.uhn.fhir.rest.server.RestfulServer.unknownMethod=Invalid request: The FHIR endpoint on this server does not know how to handle {0} operation[{1}] with parameters [{2}] ca.uhn.fhir.rest.server.RestfulServer.rootRequest=This is the base URL of FHIR server. Unable to handle this request, as it does not contain a resource type or operation name. - ca.uhn.fhir.validation.ValidationContext.unableToDetermineEncoding=Unable to determine encoding (e.g. XML / JSON) on validation input. Is this a valid FHIR resource body? ca.uhn.fhir.validation.FhirValidator.noPhlocWarningOnStartup=Phloc-schematron library not found on classpath, will not attempt to perform schematron validation ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not found on classpath, can not enable perform schematron validation @@ -81,6 +79,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.unableToDeleteNotFound=Unable to fin ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulCreate=Successfully created resource "{0}" in {1}ms ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulUpdate=Successfully updated resource "{0}" in {1}ms ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms +ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter=Unknown search parameter "{0}". Value search parameters for this search are: {1} ca.uhn.fhir.jpa.dao.SearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1} ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1} diff --git a/hapi-fhir-client-okhttp/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-client-okhttp/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..60105c1b951 --- /dev/null +++ b/hapi-fhir-client-okhttp/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jacoco/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-jacoco/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..60105c1b951 --- /dev/null +++ b/hapi-fhir-jacoco/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jaxrsserver-base/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-jaxrsserver-base/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..60105c1b951 --- /dev/null +++ b/hapi-fhir-jaxrsserver-base/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jaxrsserver-example/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-jaxrsserver-example/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..60105c1b951 --- /dev/null +++ b/hapi-fhir-jaxrsserver-example/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 2c123d151d8..132b010aa38 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -447,6 +447,7 @@ alphabetical ${argLine} -Dfile.encoding=UTF-8 -Xmx1024m + 0.6C @@ -523,14 +524,29 @@ + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + target/generated-sources/tinder + + + + + ${basedir}/src/main/resources - - ${basedir}/target/generated-sources/tinder - ${basedir}/target/generated-resources/tinder @@ -553,6 +569,20 @@ true + + NOPARALLEL + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + + + + + + javax.servlet servlet-api @@ -89,13 +86,7 @@ com.phloc phloc-schematron - test + test com.phloc @@ -119,11 +110,7 @@ commons-lang commons-lang - + @@ -140,11 +127,7 @@ commons-lang commons-lang - + @@ -252,9 +235,7 @@ deviceusestatement diagnosticorder diagnosticreport - + documentmanifest documentreference eligibilityrequest @@ -332,6 +313,24 @@ --> + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + target/generated-sources/tinder + + + + + diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/XhtmlNodeTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/XhtmlNodeTest.java index ab4796e1465..b87263ed32b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/XhtmlNodeTest.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/model/XhtmlNodeTest.java @@ -3,10 +3,12 @@ package ca.uhn.fhir.model; import static org.junit.Assert.*; import org.hl7.fhir.dstu3.model.ExplanationOfBenefit; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; import org.junit.AfterClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.util.TestUtil; public class XhtmlNodeTest { @@ -20,6 +22,15 @@ public class XhtmlNodeTest { private static FhirContext ourCtx = FhirContext.forDstu3(); + @Test + public void testParseRsquo() { + XhtmlNode dt = new XhtmlNode(); + dt.setValueAsString("It’s January again"); + assertEquals("
It’s January again
", dt.getValueAsString()); + assertEquals("
It’s January again
", new XhtmlNode().setValue(dt.getValue()).getValueAsString()); + } + + /** * See #443 */ diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java new file mode 100644 index 00000000000..7b50cd0a1be --- /dev/null +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java @@ -0,0 +1,261 @@ +package ca.uhn.fhir.rest.server; + +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.hl7.fhir.dstu3.model.HumanName; +import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.RawParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.util.TestUtil; + +public class SearchDefaultMethodDstu3Test { + + private static CloseableHttpClient ourClient; + private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDefaultMethodDstu3Test.class); + private static int ourPort; + private static Server ourServer; + private static String ourLastMethod; + private static StringAndListParam ourLastParam1; + private static StringAndListParam ourLastParam2; + + @Before + public void before() { + ourLastMethod = null; + ourLastParam1 = null; + ourLastParam2 = null; + ourLastAdditionalParams = null; + } + + @Test + public void testSearchNoParams() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + CloseableHttpResponse status = ourClient.execute(httpGet); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertThat(ourLastMethod, either(equalTo("search01")).or(equalTo("search02"))); + assertEquals(null, ourLastParam1); + assertEquals(null, ourLastParam2); + assertEquals(null, ourLastAdditionalParams); + + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @Test + public void testSearchOneOptionalParam() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1"); + CloseableHttpResponse status = ourClient.execute(httpGet); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertThat(ourLastParam1.getValuesAsQueryTokens(), hasSize(1)); + assertThat(ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val1", ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + assertEquals(null, ourLastParam2); + assertEquals(null, ourLastAdditionalParams); + + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @Test + public void testSearchTwoOptionalParams() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2"); + CloseableHttpResponse status = ourClient.execute(httpGet); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertThat(ourLastParam1.getValuesAsQueryTokens(), hasSize(1)); + assertThat(ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val1", ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + + assertThat(ourLastParam2.getValuesAsQueryTokens(), hasSize(1)); + assertThat(ourLastParam2.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val2", ourLastParam2.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + + assertEquals(null, ourLastAdditionalParams); + + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @Test + public void testSearchTwoOptionalParamsAndExtraParam() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2¶m3=val3&_pretty=true"); + CloseableHttpResponse status = ourClient.execute(httpGet); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("search03", ourLastMethod); + + assertThat(ourLastParam1.getValuesAsQueryTokens(), hasSize(1)); + assertThat(ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val1", ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + + assertThat(ourLastParam2.getValuesAsQueryTokens(), hasSize(1)); + assertThat(ourLastParam2.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val2", ourLastParam2.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + + ourLog.info(ourLastAdditionalParams.toString()); + assertEquals(1, ourLastAdditionalParams.size()); + assertEquals("val3", ourLastAdditionalParams.get("param3").get(0)); + + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @Test + public void testSearchTwoOptionalParamsWithQualifierAndExtraParam() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2¶m2:exact=val2e¶m3=val3&_pretty=true"); + CloseableHttpResponse status = ourClient.execute(httpGet); + try { + String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + assertEquals("search03", ourLastMethod); + + assertThat(ourLastParam1.getValuesAsQueryTokens(), hasSize(1)); + assertThat(ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val1", ourLastParam1.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + + assertThat(ourLastParam2.getValuesAsQueryTokens(), hasSize(2)); + assertThat(ourLastParam2.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens(), hasSize(1)); + assertEquals("val2", ourLastParam2.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); + assertEquals("val2e", ourLastParam2.getValuesAsQueryTokens().get(1).getValuesAsQueryTokens().get(0).getValue()); + + ourLog.info(ourLastAdditionalParams.toString()); + assertEquals(1, ourLastAdditionalParams.size()); + assertEquals("val3", ourLastAdditionalParams.get("param3").get(0)); + + } finally { + IOUtils.closeQuietly(status.getEntity().getContent()); + } + + } + + @AfterClass + public static void afterClassClearContext() throws Exception { + ourServer.stop(); + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ourPort = PortUtil.findFreePort(); + ourServer = new Server(ourPort); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + RestfulServer servlet = new RestfulServer(ourCtx); + servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); + + servlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + ourServer.start(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + + private static Map> ourLastAdditionalParams; + + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Search() + public List search01( + @OptionalParam(name = "param1") StringAndListParam theParam1) { + ourLastMethod = "search01"; + ourLastParam1 = theParam1; + ArrayList retVal = new ArrayList(); + retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1")); + return retVal; + } + + @Search() + public List search02( + @OptionalParam(name = "param1") StringAndListParam theParam1, + @OptionalParam(name = "param2") StringAndListParam theParam2) { + ourLastMethod = "search02"; + ourLastParam1 = theParam1; + ourLastParam2 = theParam2; + ArrayList retVal = new ArrayList(); + retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1")); + return retVal; + } + + @Search(allowUnknownParams = true) + public List search03( + @OptionalParam(name = "param1") StringAndListParam theParam1, + @OptionalParam(name = "param2") StringAndListParam theParam2, + @RawParam() Map> theAdditionalParams) { + ourLastMethod = "search03"; + ourLastParam1 = theParam1; + ourLastParam2 = theParam2; + ourLastAdditionalParams = theAdditionalParams; + ArrayList retVal = new ArrayList(); + retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1")); + return retVal; + } + + } + +} diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java index 1becf3dc3d4..32de1098112 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java @@ -82,7 +82,7 @@ public class SearchPostDstu3Test { ourLastSortSpec = null; ourLastName = null; - for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { + for (IServerInterceptor next : new ArrayList(ourServlet.getInterceptors())) { ourServlet.unregisterInterceptor(next); } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java index cccd3a2d90f..05ffa150167 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java @@ -69,7 +69,7 @@ public class ServerMimetypeDstu3Test { private List toStrings(List theFormat) { - ArrayList retVal = new ArrayList<>(); + ArrayList retVal = new ArrayList(); for (CodeType next : theFormat) { retVal.add(next.asStringValue()); } diff --git a/hapi-fhir-validation-resources-dstu2.1/.settings/org.eclipse.jdt.core.prefs b/hapi-fhir-validation-resources-dstu2.1/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..60105c1b951 --- /dev/null +++ b/hapi-fhir-validation-resources-dstu2.1/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index 212dfb62ee2..58abd420a19 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -39,7 +39,7 @@ public class ${className}ResourceProvider extends return ${className}.class; } - @Search() + @Search(allowUnknownParams=true) public ca.uhn.fhir.rest.server.IBundleProvider search( javax.servlet.http.HttpServletRequest theServletRequest, @@ -99,6 +99,9 @@ public class ${className}ResourceProvider extends #end #end + @RawParam + Map> theAdditionalRawParams, + #if ( $version != 'dstu' ) @IncludeParam(reverse=true) Set theRevIncludes, @@ -151,6 +154,8 @@ public class ${className}ResourceProvider extends paramMap.setCount(theCount); paramMap.setRequestDetails(theRequestDetails); + getDao().translateRawParameters(theAdditionalRawParams, paramMap); + ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap); return retVal; } finally { diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm index 7230adc3648..d3c5c30e8c8 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm @@ -21,9 +21,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.dao.*; @Configuration public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jpa.config${package_suffix}.Base${versionCapitalized}Config { @@ -49,12 +47,23 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp #foreach ( $res in $resources ) @Bean(name="my${res.name}Dao${versionCapitalized}", autowire=Autowire.BY_NAME) @Lazy - public IFhirResourceDao<${resourcePackage}.${res.declaringClassNameComplete}> dao${res.declaringClassNameComplete}${versionCapitalized}() { - ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}> retVal; -#if ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem')) - retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); + public +#if ( ${versionCapitalized} == 'Dstu2' && ${res.name} == 'ValueSet' ) + IFhirResourceDaoValueSet +#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ValueSet' ) + IFhirResourceDaoValueSet +#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'CodeSystem' ) + IFhirResourceDaoCodeSystem +#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter')) + IFhirResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}> #else - retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}>(); + IFhirResourceDao<${resourcePackage}.${res.declaringClassNameComplete}> +#end + dao${res.declaringClassNameComplete}${versionCapitalized}() { +#if ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem')) + ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); +#else + ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}> retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}>(); #end retVal.setResourceType(${resourcePackage}.${res.declaringClassNameComplete}.class); retVal.setContext(fhirContext${versionCapitalized}()); diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index c96532291e8..a5d6bab59d1 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 @@ -30,10 +29,8 @@ hapi-fhir-structures-dstu2 ${project.version}
- + ca.uhn.hapi.fhir hapi-fhir-structures-dstu2.1 @@ -76,17 +73,11 @@ - + ca.uhn.hapi.fhir @@ -268,11 +259,8 @@ - + @@ -286,77 +274,65 @@ - + - + - + - - ca.uhn.hapi.fhir - hapi-fhir-base - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-structures-dstu - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-structures-dstu2 - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-structures-dstu2.1 - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-base - - - - - ca.uhn.hapi.fhir - hapi-fhir-structures-dstu3 - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-structures-hl7org-dstu2 - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-validation-resources-dstu2 - ${project.version} - - - ca.uhn.hapi.fhir - hapi-fhir-validation-resources-dstu3 - ${project.version} - + + ca.uhn.hapi.fhir + hapi-fhir-base + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu2 + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu2.1 + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-base + + + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu3 + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-structures-hl7org-dstu2 + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-validation-resources-dstu2 + ${project.version} + + + ca.uhn.hapi.fhir + hapi-fhir-validation-resources-dstu3 + ${project.version} + ca.uhn.hapi.fhir hapi-tinder-plugin @@ -371,11 +347,28 @@ true + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + target/generated-sources/tinder + + + + + - + org.eclipse.m2e lifecycle-mapping @@ -428,6 +421,14 @@ + + + ${basedir}/src/main/resources + + + ${basedir}/target/generated-resources/tinder + +
diff --git a/pom.xml b/pom.xml index 65d9954a550..f47ee66a7b7 100644 --- a/pom.xml +++ b/pom.xml @@ -288,6 +288,19 @@ rqg0717 James Ren + + Robbert1 + Robbert van Waveren + + + daliboz + Jenny Syed + Cerner + + + sekaijin + sekaijin + @@ -311,18 +324,18 @@ 10.13.1.1 2.24 9.3.14.v20161028 - - - 5.1.0.Final - 5.2.4.Final + 5.2.7.Final + 5.3.4.Final + + 5.7.0.CR1 + 5.5.2 2.5.3 1.8 2.4 2.7.1 4.4.6 - 4.3.1.RELEASE - 3.0.1.RELEASE + 4.3.6.RELEASE + 3.0.2.RELEASE 1.6 @@ -516,12 +529,12 @@ org.apache.lucene lucene-highlighter - 5.3.1 + ${lucene_version} org.apache.lucene lucene-analyzers-phonetic - 5.3.1 + ${lucene_version} org.apache.maven.doxia @@ -676,7 +689,7 @@ org.hibernate hibernate-search-orm - 5.5.4.Final + ${hibernate_search_version} org.javassist @@ -821,7 +834,6 @@ --> 1.8 1.8 - javac-with-errorprone true UTF-8 @@ -1055,6 +1067,7 @@ + @@ -1643,7 +1657,8 @@ hapi-fhir-cli hapi-fhir-dist examples - hapi-fhir-base-example-embedded-ws + example-projects/hapi-fhir-base-example-embedded-ws + example-projects/hapi-fhir-standalone-overlay-example hapi-fhir-jacoco @@ -1661,6 +1676,20 @@ + + ERRORPRONE + + + + org.apache.maven.plugins + maven-compiler-plugin + + javac-with-errorprone + + + + + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2ce0f1a6ecf..7b478705ada 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -6,6 +6,124 @@ HAPI FHIR Changelog + + + Bump the version of a few dependencies to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Hibernate (JPA): 5.1.0 -> 5.2.7
  • +
  • Hibernate Search (JPA): 5.5.4 ->p; 5.7.0.CR1
  • +
  • Hibernate Validator (JPA): 5.2.4 ->p; 5.3.4
  • +
  • Spring (JPA): 4.3.1 -> 4.3.6
  • + + ]]> +
    + + The JPA server now supports custom search parameters in DSTU3 + mode. This allows users to create search parameters which contain + custom paths, or even override and disable existing search + parameters. + + CLI example uploader couldn't find STU3 examples after CI server + was moved to build.fhir.org + + + Fix issue in JPA subscription module that prevented purging stale + subscriptions when many were present on Postgres + + + Server interceptor methods were being called twice unnecessarily + by the JPA server, and the DaoConfig interceptor registration + framework was not actually useful. Thanks to GitHub user + @mattiuusitalo for reporting! + + + AuthorizationInterceptor on JPA server did not correctly + apply rules on deleting resources in a specific compartment + because the resource metadata was stripped by the JPA server + before the interceptor could see it. Thanks to + Eeva Turkka for reporting! + + + JPA server exported CapabilityStatement includes + double entries for the _id parameter and uses the + wrong type (string instead of token). Thanks to + Robert Lichtenberger for reporting! + + + Custom resource types which extend Binary must not + have declared extensions since this is invalid in + FHIR (and HAPI would just ignore them anyhow). Thanks + to Thomas S Berg for reporting! + + + Standard HAPI zip/tar distributions did not include the project + sources and JavaDoc JARs. Thanks to Keith Boone for pointing + this out! + + + Server AuthorizationInterceptor always rejects history operation + at the type level even if rules should allow it. + + + JPA server terminology service was not correctly validating or expanding codes + in SNOMED CT or LOINC code systems. Thanks to David Hay for reporting! + + + Attempting to search for an invalid resource type (e.g. GET base/FooResource) should + return an HTTP 404 and not a 400, per the HTTP spec. Thanks to + GitHub user @CarthageKing for the pull request! + + + When parsing a Bundle containing placeholder fullUrls and references + (e.g. "urn:uuid:0000-0000") the resource reference targets did not get + populated with the given resources. Note that as a part of this + change, IdType and IdDt]]> have been modified + so that when parsing a placeholder ID, the complete placeholder including the + "urn:uuid:" or "urn:oid:" prefix will be placed into the ID part. Previously, + the prefix was treated as the base URL, which led to strange behaviour + like the placeholder being treated as a real IDs. Thanks to GitHub + user @jodue for reporting! + + + Declared extensions with multiple type() options listed in the @Child + annotation caused a crash on startup. Now this is supported. + + + STU3 XHTML parser for narrative choked if the narrative contained + an &rsquot;]]> entity string. + + + When parsing a quantity parameter on the server with a + value and units but no system (e.g. + GET [base]/Observation?value=5.4||mg]]>) + the unit was incorrectly treated as the system. Thanks to + @CarthageKing for the pull request! + + + Correct a typo in the JPA ValueSet ResourceProvider which prevented + successful operation under Spring 4.3. Thanks to + Robbert van Waveren for the pull request! + + + Deprecate the method + ICompositeElement#getAllPopulatedChildElementsOfType(Class)]]> + as it is no longer used by HAPI and is just an annoying step + in creating custom structures. Thanks to Allan Bro Hansen + for pointing this out. + + + CapturingInterceptor did not buffer the response meaning + that in many circumstances it did not actually capture + the response. Thanks to Jenny Syed of Cerner for + the pull request and contribution! + + + Clean up dependencies and remove Eclipse project files from git. Thanks to + @sekaijin for the pull request! + +
    Bump the version of a few dependencies to the @@ -237,6 +355,35 @@ Declared extensions with multiple type() options listed in the @Child annotation caused a crash on startup. Now this is supported. + + STU3 XHTML parser for narrative choked if the narrative contained + an &rsquot;]]> entity string. + + + When parsing a quantity parameter on the server with a + value and units but no system (e.g. + GET [base]/Observation?value=5.4||mg]]>) + the unit was incorrectly treated as the system. Thanks to + @CarthageKing for the pull request! + + + Correct a typo in the JPA ValueSet ResourceProvider which prevented + successful operation under Spring 4.3. Thanks to + Robbert van Waveren for the pull request! + + + Deprecate the method + ICompositeElement#getAllPopulatedChildElementsOfType(Class)]]> + as it is no longer used by HAPI and is just an annoying step + in creating custom structures. Thanks to Allan Bro Hansen + for pointing this out. + + + CapturingInterceptor did not buffer the response meaning + that in many circumstances it did not actually capture + the response. Thanks to Jenny Syed of Cerner for + the pull request and contribution! + diff --git a/src/site/xdoc/doc_resource_references.xml b/src/site/xdoc/doc_resource_references.xml index 84071153644..fc29addb9c8 100644 --- a/src/site/xdoc/doc_resource_references.xml +++ b/src/site/xdoc/doc_resource_references.xml @@ -209,7 +209,7 @@ System.out.println(encoded);]]> -
    +

    By default, HAPI will strip resource versions from references between resources.