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 extends IBaseResource> 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/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/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/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
../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
diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java
new file mode 100644
index 00000000000..bcd1312565d
--- /dev/null
+++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java
@@ -0,0 +1,187 @@
+package ca.uhn.fhir.rest.server;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+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.junit.After;
+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.model.api.IResource;
+import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
+import ca.uhn.fhir.model.dstu2.resource.Encounter;
+import ca.uhn.fhir.model.dstu2.resource.Observation;
+import ca.uhn.fhir.model.dstu2.resource.Patient;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.model.primitive.StringDt;
+import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
+import ca.uhn.fhir.rest.annotation.Create;
+import ca.uhn.fhir.rest.annotation.IdParam;
+import ca.uhn.fhir.rest.annotation.OptionalParam;
+import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.annotation.ResourceParam;
+import ca.uhn.fhir.rest.annotation.Search;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.util.PortUtil;
+
+/**
+ * Created by dsotnikov on 2/25/2014.
+ */
+public class CompartmentDstu2Test {
+ private static CloseableHttpClient ourClient;
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompartmentDstu2Test.class);
+ private static int ourPort;
+ private static Server ourServer;
+ private static String ourLastMethod;
+ private static FhirContext ourCtx = FhirContext.forDstu2();
+ private static IdDt ourLastId;
+
+ @Before
+ public void before() {
+ ourLastMethod = null;
+ ourLastId = null;
+ }
+
+
+ @Test
+ public void testReadFirst() throws Exception {
+ init(new TempPatientResourceProvider());
+
+ HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123");
+ HttpResponse status = ourClient.execute(httpGet);
+ String responseContent = IOUtils.toString(status.getEntity().getContent());
+ IOUtils.closeQuietly(status.getEntity().getContent());
+ ourLog.info("Response was:\n{}", responseContent);
+ assertEquals("read", ourLastMethod);
+ assertEquals("Patient", ourLastId.getResourceType());
+ assertEquals("123", ourLastId.getIdPart());
+ assertThat(responseContent, startsWith(" 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/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;
}
diff --git a/pom.xml b/pom.xml
index c207d763b2f..5bfdc74a027 100644
--- a/pom.xml
+++ b/pom.xml
@@ -190,6 +190,10 @@
botunge
Thomas Andersen
+
+ samlanfranchi
+ Sam Lanfranchi
+
@@ -1374,4 +1378,7 @@
+
+ hapi-fhir-base-example-embedded-ws
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index db3b07ab36c..4fa882f66e3 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -194,6 +194,19 @@
handle JSON responses or use interceptors. Thanks to
JT for reporting!
+
+ 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!
+
+
+ FIx issue in testpage-overlay's new Java configuration where only the first
+ configured server actually gets used.
+
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',