- add site-documentation for jaxrs base

- set example for operation on instance level
- remove not thrown exceptions
This commit is contained in:
petervanhoute 2015-11-25 15:34:41 +01:00
parent 26a801edcb
commit 94f309114b
9 changed files with 242 additions and 32 deletions

View File

@ -40,6 +40,24 @@
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- jax rs server -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>ejb-api</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<reporting> <reporting>

View File

@ -0,0 +1,41 @@
package example;
import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
/**
* Conformance Rest Service
*
* @author Peter Van Houte
*/
// START SNIPPET: jax-rs-conformance
@Path("")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
@EJB
private JaxRsPatientRestProvider provider;
public JaxRsConformanceProvider() {
super("My Server Version", "My Server Description", "My Server Name");
}
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
map.put(JaxRsConformanceProvider.class, this);
map.put(JaxRsPatientRestProvider.class, provider);
return map;
}
}
// END SNIPPET: jax-rs-conformance

View File

@ -0,0 +1,82 @@
package example;
import java.lang.reflect.InvocationTargetException;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
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.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.Constants;
/**
* A demo JaxRs Patient Rest Provider
*/
@Local
@Stateless
// START SNIPPET: jax-rs-provider-construction
@Path("/Patient")
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Patient> {
public JaxRsPatientRestProvider() {
super(JaxRsPatientRestProvider.class);
}
// END SNIPPET: jax-rs-provider-construction
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional) {
// create the patient ...
return new MethodOutcome(new IdDt(1L)).setCreated(true);
}
// START SNIPPET: jax-rs-provider-operation
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringDt.class) })
public Parameters someCustomOperation(@IdParam IdDt myId, @OperationParam(name = "dummy") StringDt dummyInput) {
Parameters parameters = new Parameters();
parameters.addParameter().setName("return").setValue(new StringDt("My Dummy Result"));
return parameters;
}
// END SNIPPET: jax-rs-provider-operation
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
}

View File

@ -300,7 +300,7 @@ public class AbstractJaxRsResourceProviderTest {
// prepare mock // prepare mock
Parameters resultParameters = new Parameters(); Parameters resultParameters = new Parameters();
resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue")); resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue"));
when(mock.someCustomOperation(eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters); when(mock.someCustomOperation(any(IdDt.class), eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters);
// Create the input parameters to pass to the server // Create the input parameters to pass to the server
Parameters inParams = new Parameters(); Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01")); inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
@ -318,7 +318,7 @@ public class AbstractJaxRsResourceProviderTest {
// prepare mock // prepare mock
Parameters resultParameters = new Parameters(); Parameters resultParameters = new Parameters();
resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue")); resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue"));
when(mock.someCustomOperation(eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters); when(mock.someCustomOperation(any(IdDt.class), eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters);
// Create the input parameters to pass to the server // Create the input parameters to pass to the server
Parameters inParams = new Parameters(); Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01")); inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));

View File

@ -89,29 +89,29 @@ public class TestJaxRsMockPatientRestProvider extends AbstractJaxRsResourceProvi
return mock.delete(theId); return mock.delete(theId);
} }
@Search(compartmentName = "Condition")
public List<IResource> searchCompartment(@IdParam IdDt thePatientId) {
return mock.searchCompartment(thePatientId);
}
@GET @GET
@Path("/{id}/$someCustomOperation") @Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception { public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation", return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE); RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Search(compartmentName = "Condition")
public List<IResource> searchCompartment(@IdParam IdDt thePatientId) {
return mock.searchCompartment(thePatientId);
} }
@POST @POST
@Path("/{id}/$someCustomOperation") @Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception { public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation", return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE); RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
} }
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = { @Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringDt.class) }) @OperationParam(name = "return", type = StringDt.class) })
public Parameters someCustomOperation(@OperationParam(name = "dummy") StringDt dummyInput) { public Parameters someCustomOperation(@IdParam IdDt myId, @OperationParam(name = "dummy") StringDt dummyInput) {
return mock.someCustomOperation(dummyInput); return mock.someCustomOperation(myId, dummyInput);
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jaxrs.server.example; package ca.uhn.fhir.jaxrs.server.example;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -113,7 +114,7 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
} }
@Update @Update
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) throws Exception { public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) {
final String idPart = theId.getIdPart(); final String idPart = theId.getIdPart();
if (patients.containsKey(idPart)) { if (patients.containsKey(idPart)) {
final List<Patient> patientList = patients.get(idPart); final List<Patient> patientList = patients.get(idPart);
@ -130,7 +131,7 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
} }
@Read @Read
public Patient find(@IdParam final IdDt theId) throws InvocationTargetException { public Patient find(@IdParam final IdDt theId) {
if (patients.containsKey(theId.getIdPart())) { if (patients.containsKey(theId.getIdPart())) {
return getLast(patients.get(theId.getIdPart())); return getLast(patients.get(theId.getIdPart()));
} else { } else {
@ -166,21 +167,13 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
} }
@Delete @Delete
public MethodOutcome delete(@IdParam final IdDt theId) throws InvocationTargetException { public MethodOutcome delete(@IdParam final IdDt theId) {
final Patient deletedPatient = find(theId); final Patient deletedPatient = find(theId);
patients.remove(deletedPatient.getId().getIdPart()); patients.remove(deletedPatient.getId().getIdPart());
final MethodOutcome result = new MethodOutcome().setCreated(true); final MethodOutcome result = new MethodOutcome().setCreated(true);
result.setResource(deletedPatient); result.setResource(deletedPatient);
return result; return result;
} }
@GET
@Path("/{id}/$last")
public Response operationLastGet(@PathParam("id") String id) throws Exception {
return customOperation(null, RequestTypeEnum.GET, id, "$last",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
}
@Search(compartmentName = "Condition") @Search(compartmentName = "Condition")
public List<IResource> searchCompartment(@IdParam IdDt thePatientId) { public List<IResource> searchCompartment(@IdParam IdDt thePatientId) {
List<IResource> retVal = new ArrayList<IResource>(); List<IResource> retVal = new ArrayList<IResource>();
@ -190,18 +183,24 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
return retVal; return retVal;
} }
@POST @GET
@Path("/{id}/$last") @Path("/{id}/$firstVersion")
public Response operationLast(final String resource) throws Exception { public Response operationFirstVersionUsingGet(@PathParam("id") String id) throws IOException {
return customOperation(resource, RequestTypeEnum.POST, null, "$last", return customOperation(null, RequestTypeEnum.GET, id, "$firstVersion", RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
} }
@Operation(name = "last", idempotent = true, returnParameters = {
@POST
@Path("/{id}/$firstVersion")
public Response operationFirstVersionUsingGet(@PathParam("id") String id, final String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$firstVersion", RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "firstVersion", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringDt.class) }) @OperationParam(name = "return", type = StringDt.class) })
public Parameters last(@OperationParam(name = "dummy") StringDt dummyInput) throws InvocationTargetException { public Parameters firstVersion(@IdParam final IdDt theId, @OperationParam(name = "dummy") StringDt dummyInput) {
Parameters parameters = new Parameters(); Parameters parameters = new Parameters();
Patient patient = find(new IdDt(counter.intValue() - 1)); Patient patient = find(new IdDt(theId.getResourceType(), theId.getIdPart(), "0"));
parameters.addParameter().setName("return").setResource(patient) parameters.addParameter().setName("return").setResource(patient)
.setValue(new StringDt((counter - 1) + "" + "inputVariable [ " + dummyInput.getValue() + "]")); .setValue(new StringDt((counter - 1) + "" + "inputVariable [ " + dummyInput.getValue() + "]"));
return parameters; return parameters;

View File

@ -261,7 +261,7 @@ public class JaxRsPatientProviderTest {
Parameters outParams = client Parameters outParams = client
.operation() .operation()
.onInstance(new IdDt("Patient", "1")) .onInstance(new IdDt("Patient", "1"))
.named("$last") .named("$firstVersion")
.withParameters(inParams) .withParameters(inParams)
//.useHttpGet() // Use HTTP GET instead of POST //.useHttpGet() // Use HTTP GET instead of POST
.execute(); .execute();
@ -282,7 +282,7 @@ public class JaxRsPatientProviderTest {
Parameters outParams = client Parameters outParams = client
.operation() .operation()
.onInstance(new IdDt("Patient", "1")) .onInstance(new IdDt("Patient", "1"))
.named("$last") .named("$firstVersion")
.withParameters(inParams) .withParameters(inParams)
.useHttpGet() // Use HTTP GET instead of POST .useHttpGet() // Use HTTP GET instead of POST
.execute(); .execute();

View File

@ -1293,6 +1293,7 @@
<module>hapi-fhir-structures-dstu2</module> <module>hapi-fhir-structures-dstu2</module>
<module>hapi-fhir-structures-hl7org-dstu2</module> <module>hapi-fhir-structures-hl7org-dstu2</module>
<module>hapi-fhir-jpaserver-base</module> <module>hapi-fhir-jpaserver-base</module>
<module>hapi-fhir-jaxrsserver-base</module>
<!-- <module>hapi-fhir-cobertura</module> --> <!-- <module>hapi-fhir-cobertura</module> -->
<module>examples</module> <module>examples</module>
</modules> </modules>

View File

@ -598,6 +598,75 @@
</section> </section>
<section name="JAX-RS Server">
<p>
The standard server is implemented using Servlet technology. A <a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-jaxrsserver-base">module</a>
exists which implements the server using <a href="https://jax-rs-spec.java.net/nonav/2.0/apidocs/index.html">JAX-RS</a> technology.
This enables the usage of existing Java EE interceptors and annotations. This module does not provide the full set of features.
The server currently supports
<a href="./doc_rest_server.html#ConformanceMetadata_Statement">Conformance Statements</a>,
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Read.html">@Read</a>,
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Search.html">@Search</a>,
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Create.html">@Create</a>,
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Update.html">@Update</a>,
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Delete.html">@Delete</a> and
<a href="./apidocs/ca/uhn/fhir/rest/annotation/Operation.html">@Operation</a>.
</p>
<p>
The primary intention for this project is to ensure that other web technologies (JAX-RS in this case) can be used together with the base-server functionality.
An example server can be found in the Git repo <a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-jaxrsserver-example">here</a>.
</p>
<subsection name="JAX-RS Implementation specifics">
<p>
The set-up of a JAX-RS server goes beyond the scope of this documentation. The implementation of the server follows the same pattern as the standard server. It is required
to put the correct <a href="./doc_rest_operations.html">annotation</a> on the methods in the <a href="#Defining_Resource_Providers">Resource Providers</a> in order to be able to call them.
</p>
<p>
Implementing a JAX-RS Resource Provider requires some JAX-RS annotations. The <code><a href="https://docs.oracle.com/javaee/6/api/javax/ws/rs/Path.html">@Path</a></code>
annotation needs to define the resource path. The <code><a href="https://docs.oracle.com/javaee/6/api/javax/ws/rs/Produces.html">@Produces</a></code> annotation
needs to declare the produced formats. The constructor needs to pass the class of the object explicitely in order to avoid problems with proxy classes in a Java EE environment.
It is necessary to extend the abstract class
<code><a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.html">AbstractJaxRsResourceProvider</a></code>.
<macro name="snippet">
<param name="id" value="jax-rs-provider-construction" />
<param name="file" value="examples/src/main/java/example/JaxRsPatientRestProvider.java" />
</macro>
</p>
<p>
<a href="./doc_rest_operations.html#Extended_Operations">Extended Operations</a> require the correct JAX-RS (
<code><a href="https://docs.oracle.com/javaee/6/api/javax/ws/rs/Path.html">@Path</a></code>,
<code><a href="https://docs.oracle.com/javaee/6/api/javax/ws/rs/GET.html">@GET</a></code> or
<code><a href="https://docs.oracle.com/javaee/6/api/javax/ws/rs/POST.html">@POST</a></code>) annotations. The body of the method needs to call the
method <code><a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.html#customOperation-java.lang.String-ca.uhn.fhir.rest.api.RequestTypeEnum-java.lang.String-java.lang.String-ca.uhn.fhir.rest.api.RestOperationTypeEnum">AbstractJaxRsResourceProvider#customOperation</a></code>
with the correct parameters. The server will then call the method with corresponding name.
<macro name="snippet">
<param name="id" value="jax-rs-provider-operation" />
<param name="file" value="examples/src/main/java/example/JaxRsPatientRestProvider.java" />
</macro>
</p>
<p>
In order to create the conformance profile, a conformance provider class needs to be deployed which exports the provider's conformance statements.
These providers need to be returned as the result of
the method <code><a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.html#getProviders">AbstractJaxRsResourceProvider#getProviders</a></code>.
This method is called once, during <code><a href="https://docs.oracle.com/javaee/6/api/javax/annotation/PostConstruct.html">PostConstruct</a></code>.
<macro name="snippet">
<param name="id" value="jax-rs-conformance" />
<param name="file" value="examples/src/main/java/example/JaxRsConformanceProvider.java" />
</macro>
</p>
</subsection>
</section>
</body> </body>
</document> </document>