From ba9d95105306933844ff4d6face73ffd684bd533 Mon Sep 17 00:00:00 2001 From: jkiddo Date: Thu, 15 Oct 2015 15:08:58 +0200 Subject: [PATCH 1/6] Added example using embedded Jetty and Guice - meaning its way more easy to debug and 'deploy' --- hapi-fhir-base-example-embedded-ws/pom.xml | 47 +++++++++++++++ .../main/java/embedded/ContextListener.java | 37 ++++++++++++ .../java/embedded/FhirRestfulServlet.java | 59 +++++++++++++++++++ .../src/main/java/embedded/ServerStartup.java | 25 ++++++++ .../java/embedded/SomeResourceProvider.java | 28 +++++++++ .../java/filters/CharsetResponseFilter.java | 24 ++++++++ .../main/java/filters/CorsResponseFilter.java | 33 +++++++++++ hapi-fhir-base/.project | 5 ++ pom.xml | 3 + 9 files changed, 261 insertions(+) create mode 100644 hapi-fhir-base-example-embedded-ws/pom.xml create mode 100644 hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java create mode 100644 hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java create mode 100644 hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java create mode 100644 hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java create mode 100644 hapi-fhir-base-example-embedded-ws/src/main/java/filters/CharsetResponseFilter.java create mode 100644 hapi-fhir-base-example-embedded-ws/src/main/java/filters/CorsResponseFilter.java diff --git a/hapi-fhir-base-example-embedded-ws/pom.xml b/hapi-fhir-base-example-embedded-ws/pom.xml new file mode 100644 index 00000000000..98dac37f470 --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + ca.uhn.hapi.fhir + hapi-fhir + 1.2 + + + jar + + hapi-fhir-base-example-embedded-ws + + + org.eclipse.jetty + jetty-servlet + + + org.eclipse.jetty + jetty-webapp + + + com.google.guava + guava + + + com.google.inject + guice + 3.0 + + + com.google.inject.extensions + guice-servlet + 3.0 + + + com.sun.jersey.contribs + jersey-guice + 1.18.1 + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu2 + 1.2 + + + \ No newline at end of file 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 new file mode 100644 index 00000000000..02019b886cb --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ContextListener.java @@ -0,0 +1,37 @@ +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/embedded/FhirRestfulServlet.java b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java new file mode 100644 index 00000000000..a7e082015b9 --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/FhirRestfulServlet.java @@ -0,0 +1,59 @@ +package embedded; + +import java.util.ArrayList; +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; + +@Singleton +public class FhirRestfulServlet extends RestfulServer { + + /** + * + */ + private static final long serialVersionUID = -3931111342737918913L; + + public FhirRestfulServlet() { + super(FhirContext.forDstu2()); // Support DSTU2 + } + + /** + * This method is called automatically when the servlet is initializing. + */ + @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/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java new file mode 100644 index 00000000000..fb8dde91365 --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/ServerStartup.java @@ -0,0 +1,25 @@ +package embedded; +import java.util.EnumSet; + +import javax.servlet.DispatcherType; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +import com.google.inject.servlet.GuiceFilter; + +public class ServerStartup { + + public static void main(final String[] args) throws Exception { + + 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.addServlet(DefaultServlet.class, "/"); + server.start(); + } + +} diff --git a/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java new file mode 100644 index 00000000000..aac9da8adb9 --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/src/main/java/embedded/SomeResourceProvider.java @@ -0,0 +1,28 @@ +package embedded; + +import java.util.List; + +import org.hl7.fhir.instance.model.api.IBaseResource; + +import ca.uhn.fhir.model.dstu2.resource.Practitioner; +import ca.uhn.fhir.model.primitive.StringDt; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; + +public class SomeResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Practitioner.class; + } + + @Search() + public List findPractitionersByName( + @RequiredParam(name = Practitioner.SP_NAME) final StringDt theName) { + throw new UnprocessableEntityException( + "Please provide more than 4 characters for the name"); + } + +} 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 new file mode 100644 index 00000000000..656f7535578 --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CharsetResponseFilter.java @@ -0,0 +1,24 @@ +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 new file mode 100644 index 00000000000..cab06c28419 --- /dev/null +++ b/hapi-fhir-base-example-embedded-ws/src/main/java/filters/CorsResponseFilter.java @@ -0,0 +1,33 @@ +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/.project b/hapi-fhir-base/.project index 790dcdc0123..7afe06363c3 100644 --- a/hapi-fhir-base/.project +++ b/hapi-fhir-base/.project @@ -15,6 +15,11 @@ + + org.eclipse.m2e.core.maven2Builder + + + org.eclipse.jem.workbench.JavaEMFNature diff --git a/pom.xml b/pom.xml index f0217b971de..72fa528f260 100644 --- a/pom.xml +++ b/pom.xml @@ -1365,4 +1365,7 @@ + + hapi-fhir-base-example-embedded-ws + From c3b9804b8a0413cf4394645b784285a679a50f51 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 21 Oct 2015 09:54:32 -0400 Subject: [PATCH 2/6] Try to fix OSGi --- hapi-fhir-osgi-core/pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-osgi-core/pom.xml b/hapi-fhir-osgi-core/pom.xml index 09fa0b1c576..183ba966edb 100644 --- a/hapi-fhir-osgi-core/pom.xml +++ b/hapi-fhir-osgi-core/pom.xml @@ -167,23 +167,23 @@ ../hapi-fhir-structures-dstu/src/main/resources - true + false ../hapi-fhir-structures-dstu/target/generated-resources/tinder - true + false ../hapi-fhir-structures-dstu2/src/main/resources - true + false ../hapi-fhir-structures-dstu2/target/generated-resources/tinder - true + false ../hapi-fhir-structures-hl7org-dstu2/src/resources/java - true + false From dba8a35349ea8e01ff1aace636e5bad64d771fa6 Mon Sep 17 00:00:00 2001 From: Sam Lanfranchi Date: Wed, 21 Oct 2015 16:11:57 +0200 Subject: [PATCH 3/6] fixed first server ignored in test overlay --- .../src/main/java/ca/uhn/fhir/to/TesterConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java index df6d7146786..159f5fb8f60 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java @@ -27,6 +27,7 @@ public class TesterConfig { public IServerBuilderStep1 addServer() { ServerBuilder retVal = new ServerBuilder(); + myServerBuilders.add(retVal); return retVal; } From d838a2bd4bc3d0dfac4f89167c0de827b6f6bcdf Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 21 Oct 2015 10:37:56 -0400 Subject: [PATCH 4/6] Reduce JPA uri param length from 256 to 255 to accomodate MySql --- .../fhir/jpa/entity/ResourceIndexedSearchParamString.java | 3 +++ .../uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java | 5 ++++- src/changes/changes.xml | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java index 11bf98d28dd..2dfdafc63a7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamString.java @@ -43,6 +43,9 @@ import org.hibernate.search.annotations.Field; }) public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam { + /* + * Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here + */ public static final int MAX_LENGTH = 200; private static final long serialVersionUID = 1L; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java index 5e3adeca952..6fa9351f7ef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamUri.java @@ -43,7 +43,10 @@ import org.hibernate.search.annotations.Indexed; //@formatter:on public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchParam { - public static final int MAX_LENGTH = 256; + /* + * Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here + */ + public static final int MAX_LENGTH = 255; private static final long serialVersionUID = 1L; diff --git a/src/changes/changes.xml b/src/changes/changes.xml index eb45d185f1d..e1c585f60d6 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -188,6 +188,10 @@ by calling the actual implementing method in the server (previously the call was simulated, which meant that many features did not work) + + JPA server maximumn length for a URI search parameter has been reduced from + 256 to 255 in order to accomodate MySQL's indexing requirements + From 8d515feb6d59105450ecc120fc568ad3617d1ded Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 21 Oct 2015 11:58:19 -0400 Subject: [PATCH 5/6] Fix #242 - Allow compartment and read method to coexist for server --- .../fhir/rest/method/ReadMethodBinding.java | 8 + .../search/IndexNonDeletedInterceptor.java | 2 +- .../util/BigDecimalNumericFieldBridge.java | 2 +- .../rest/server/CompartmentDstu2Test.java | 187 ++++++++++++++++++ src/changes/changes.xml | 5 + vagrant/Vagrantfile | 2 +- 6 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java index 300fcd3f4ce..bb0ae9a495b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/ReadMethodBinding.java @@ -1,5 +1,7 @@ package ca.uhn.fhir.rest.method; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + /* * #%L * HAPI FHIR - Core Library @@ -138,6 +140,9 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem return false; } } + if (isNotBlank(theRequest.getCompartmentName())) { + return false; + } if (theRequest.getRequestType() != RequestTypeEnum.GET) { ourLog.trace("Method {} doesn't match because request type is not GET: {}", theRequest.getId(), theRequest.getRequestType()); return false; @@ -198,6 +203,9 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem return resource; case BUNDLE_PROVIDER: return new SimpleBundleProvider(resource); + case BUNDLE_RESOURCE: + case METHOD_OUTCOME: + break; } throw new IllegalStateException("" + getMethodReturnType()); // should not happen diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java index e440cf65917..3209b63c377 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/IndexNonDeletedInterceptor.java @@ -55,4 +55,4 @@ public class IndexNonDeletedInterceptor implements EntityIndexingInterceptor getResourceType() { + return Patient.class; + } + + @Read() + public Patient method1Read(final @IdParam IdDt theId) { + ourLastMethod = "read"; + ourLastId = theId; + Patient patient = new Patient(); + patient.setId(theId); + return patient; + } + + @Search(compartmentName = "Encounter") + public List method2SearchCompartment(final @IdParam IdDt theId) { + ourLastId = theId; + ourLastMethod = "searchEncounterCompartment"; + System.out.println("Encounter compartment search"); + List encounters = new ArrayList(); + Encounter encounter = new Encounter(); + encounter.setId("1"); + encounter.setPatient(new ResourceReferenceDt(theId)); + encounters.add(encounter); + return encounters; + } + + @Search(compartmentName = "Observation") + public List method2SearchCompartment2(final @IdParam IdDt theId) { + ourLastId = theId; + ourLastMethod = "searchObservationCompartment"; + System.out.println("Encounter compartment search"); + List encounters = new ArrayList(); + Observation obs = new Observation(); + obs.setId("1"); + obs.setSubject(new ResourceReferenceDt(theId)); + encounters.add(obs); + return encounters; + } + + } + +} diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e1c585f60d6..42052504dfc 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -192,6 +192,11 @@ JPA server maximumn length for a URI search parameter has been reduced from 256 to 255 in order to accomodate MySQL's indexing requirements + + Server failed to respond correctly to compartment search operations + if the same provider also contained a read operation. Thanks to GitHub user + @am202 for reporting! + diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile index 7624d3e7640..f635f95e279 100644 --- a/vagrant/Vagrantfile +++ b/vagrant/Vagrantfile @@ -86,7 +86,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| proxy_port: 80, # ssl_port: 443, authbind: 'yes', - java_options: '-Dfhir.logdir=/var/log/fhir -Dfhir.db.location=/var/fhirdb' + java_options: '-Dfhir.logdir=/var/log/fhir -Dfhir.db.location=/var/fhirdb -Dfhir.db.location.dstu2=/var/fhirdb2 -Dfhir.lucene.location.dstu2=/var/fhirlucene2' }, mysql: { version: '5.6', From fad59b75b922823613d323efc951edac84e3620a Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 21 Oct 2015 12:03:36 -0400 Subject: [PATCH 6/6] Credit for #245 --- pom.xml | 4 ++++ src/changes/changes.xml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index c207d763b2f..ab805cbb44a 100644 --- a/pom.xml +++ b/pom.xml @@ -190,6 +190,10 @@ botunge Thomas Andersen + + samlanfranchi + Sam Lanfranchi + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 42052504dfc..d16685d7d70 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -197,6 +197,10 @@ if the same provider also contained a read operation. Thanks to GitHub user @am202 for reporting! + + FIx issue in testpage-overlay's new Java configuration where only the first + configured server actually gets used. +