add javadoc + create builder for jaxrsrequest

This commit is contained in:
petervanhoute 2015-10-30 19:13:20 +01:00
parent 63db1b646f
commit 5a7d2b40e8
23 changed files with 716 additions and 380 deletions

View File

@ -18,11 +18,12 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
@ -39,28 +40,43 @@ import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
import ca.uhn.fhir.util.ReflectionUtil;
/**
* Conformance Rest Service
* This is the conformance provider for the jax rs servers. It requires all providers to be registered
* during startup because the conformance profile is generated during the postconstruct phase.
* @author Peter Van Houte
*/
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProvider {
public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProvider implements IResourceProvider {
public static final String PATH = "/";
/** the logger */
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(AbstractJaxRsConformanceProvider.class);
/** the server bindings */
private ResourceBinding myServerBinding = new ResourceBinding();
/** the resource bindings */
private ConcurrentHashMap<String, ResourceBinding> myResourceNameToBinding = new ConcurrentHashMap<String, ResourceBinding>();
/** the server configuration */
private RestulfulServerConfiguration serverConfiguration = new RestulfulServerConfiguration();
/** the conformance. It is created once during startup */
private Conformance myConformance;
public AbstractJaxRsConformanceProvider(String implementationDescription, String serverName, String serverVersion) {
/**
* Constructor allowing the description, servername and server to be set
* @param implementationDescription the implementation description. If null, "" is used
* @param serverName the server name. If null, "" is used
* @param serverVersion the server version. If null, "" is used
*/
protected AbstractJaxRsConformanceProvider(String implementationDescription, String serverName, String serverVersion) {
serverConfiguration.setFhirContext(getFhirContext());
serverConfiguration.setImplementationDescription(implementationDescription);
serverConfiguration.setServerName(serverName);
serverConfiguration.setServerVersion(serverVersion);
serverConfiguration.setImplementationDescription(StringUtils.defaultIfEmpty(implementationDescription, ""));
serverConfiguration.setServerName(StringUtils.defaultIfEmpty(serverName, ""));
serverConfiguration.setServerVersion(StringUtils.defaultIfEmpty(serverVersion, ""));
}
/**
* This method will set the conformance during the postconstruct phase. The
* method {@link AbstractJaxRsConformanceProvider#getProviders()} is used to
* get all the resource providers include in the conformance
*/
@PostConstruct
protected void setUpPostConstruct() {
for (Entry<Class<? extends IResourceProvider>, IResourceProvider> provider : getProviders().entrySet()) {
@ -80,24 +96,45 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
myConformance = serverConformanceProvider.getServerConformance(null);
}
/**
* This method must return all the resource providers which need to be included in the conformance
* @return a map of the resource providers and their corresponding classes. This class needs to be given
* explicitly because retrieving the interface using {@link Object#getClass()} may not give the correct
* interface in a jee environment.
*/
protected abstract ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders();
/**
* This method will retrieve the conformance using the http OPTIONS method
* @return the response containing the conformance
*/
@OPTIONS
@Path("/metadata")
public Response conformanceUsingOptions() throws IOException {
return conformance();
}
/**
* This method will retrieve the conformance using the http GET method
* @return the response containing the conformance
*/
@GET
@Path("/metadata")
public Response conformance() throws IOException {
JaxRsRequest request = createRequestDetails(null, RequestTypeEnum.OPTIONS, RestOperationTypeEnum.METADATA);
IRestfulResponse response = request.getResponse();
Builder request = getRequest(RequestTypeEnum.OPTIONS, RestOperationTypeEnum.METADATA);
IRestfulResponse response = request.build().getResponse();
response.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, "*");
return (Response) response.returnResponse(ParseAction.create(myConformance), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
}
public int addProvider(Object theProvider, Class<?> theProviderInterface) throws ConfigurationException {
/**
* This method will add a provider to the conformance. This method is almost an exact copy of {@link ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods }
* @param theProvider an instance of the provider interface
* @param theProviderInterface the class describing the providers interface
* @return the numbers of basemethodbindings added
* @see ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods
*/
public int addProvider(IResourceProvider theProvider, Class<? extends IResourceProvider> theProviderInterface) throws ConfigurationException {
int count = 0;
for (Method m : ReflectionUtil.getDeclaredMethods(theProviderInterface)) {

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jaxrs.server;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.core.Context;
@ -11,27 +12,30 @@ import javax.ws.rs.core.UriInfo;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
/**
* Abstract Jax Rs Rest Server
* This is the abstract superclass for all jaxrs providers. It contains some defaults implementing
* the IRestfulServerDefaults interface and exposes the uri and headers.
* @author Peter Van Houte
*
*/
public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults, IResourceProvider {
public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
public static FhirContext CTX = FhirContext.forDstu2();
/** a static initialization for the fhircontext. Only DSTU2 is supported */
private static final FhirContext CTX = FhirContext.forDstu2();
/** the uri info */
@Context
private UriInfo theUriInfo;
/** the http headers */
@Context
private HttpHeaders theHeaders;
@ -40,10 +44,11 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults, I
return CTX;
}
/**
* param and query methods
/**
* This method returns the query parameters
* @return the query parameters
*/
public HashMap<String, String[]> getQueryMap() {
public Map<String, String[]> getParameters() {
MultivaluedMap<String, String> queryParameters = getUriInfo().getQueryParameters();
HashMap<String, String[]> params = new HashMap<String, String[]>();
for (Entry<String, List<String>> paramEntry : queryParameters.entrySet()) {
@ -52,25 +57,36 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults, I
return params;
}
/**
* This method returns the default server address strategy. The default strategy return the
* base uri for the request {@link AbstractJaxRsProvider#getBaseForRequest() getBaseForRequest()}
* @return
*/
public IServerAddressStrategy getServerAddressStrategy() {
HardcodedServerAddressStrategy addressStrategy = new HardcodedServerAddressStrategy();
addressStrategy.setValue(getBaseForRequest());
return addressStrategy;
}
/**
* This method returns the server base, independent of the request or resource.
* @see javax.ws.rs.core.UriInfo#getBaseUri()
* @return the ascii string for the server base
*/
public String getBaseForServer() {
return getUriInfo().getBaseUri().toASCIIString();
}
/**
* This method returns the server base, including the resource path.
* {@link javax.ws.rs.core.UriInfo#getBaseUri() UriInfo#getBaseUri()}
* @return the ascii string for the base resource provider path
*/
public String getBaseForRequest() {
return getBaseForServer();
}
protected JaxRsRequest createRequestDetails(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation) {
return new JaxRsRequest(this, resourceString, requestType, restOperation);
}
/**
/**
* Get the uriInfo
* @return the uri info
*/
@ -101,30 +117,52 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults, I
public void setHeaders(HttpHeaders headers) {
this.theHeaders = headers;
}
/**
* Return the requestbuilder for the server
* @param requestType the type of the request
* @param restOperation the rest operation type
* @return the requestbuilder
*/
public Builder getRequest(RequestTypeEnum requestType, RestOperationTypeEnum restOperation) {
return new JaxRsRequest.Builder(this, requestType, restOperation);
}
/**
* DEFAULT VALUES
* DEFAULT = EncodingEnum.JSON
*/
@Override
public EncodingEnum getDefaultResponseEncoding() {
return EncodingEnum.JSON;
}
/**
* DEFAULT = true
*/
@Override
public boolean isDefaultPrettyPrint() {
return true;
}
/**
* DEFAULT = ETagSupportEnum.DISABLED
*/
@Override
public ETagSupportEnum getETagSupport() {
return ETagSupportEnum.DISABLED;
}
/**
* DEFAULT = AddProfileTagEnum.NEVER
*/
@Override
public AddProfileTagEnum getAddProfileTag() {
return AddProfileTagEnum.NEVER;
}
/**
* DEFAULT = false
*/
@Override
public boolean isUseBrowserFriendlyContentTypes() {
return true;

View File

@ -17,187 +17,279 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.jaxrs.server.interceptor.BaseServerRuntimeResponseException;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.MethodBindings;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IRestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.UrlUtil;
/**
* Fhir Physician Rest Service
* @author axmpm
*
* This server is the abstract superclass for all resource providers. It exposes
* a large amount of the fhir api functionality using JAXRS
* @author Peter Van Houte
*/
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN})
@Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML})
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
@Consumes({ MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON,
Constants.CT_FHIR_XML })
@Interceptors(JaxRsExceptionInterceptor.class)
public abstract class AbstractJaxRsResourceProvider<R extends IResource> extends AbstractJaxRsProvider implements IRestfulServer<JaxRsRequest> {
private static final ExceptionHandlingInterceptor EXCEPTION_HANDLING_INTERCEPTOR = new ExceptionHandlingInterceptor();
private final MethodBindings bindings;
public abstract class AbstractJaxRsResourceProvider<R extends IResource> extends AbstractJaxRsProvider
implements IRestfulServer<JaxRsRequest>, IResourceProvider {
/** the method bindings for this class */
private final JaxRsMethodBindings theBindings;
/**
* The default constructor. The method bindings are retrieved from the class
* being constructed.
*/
protected AbstractJaxRsResourceProvider() {
bindings = MethodBindings.getMethodBindings(this, getClass());
}
theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
}
protected AbstractJaxRsResourceProvider(Class<?> subclass) {
bindings = MethodBindings.getMethodBindings(this, subclass);
}
/**
* This constructor takes in an explicit interface class. This subclass
* should be identical to the class being constructed but is given
* explicitly in order to avoid issues with proxy classes in a jee
* environment.
*
* @param theProviderClass the interface of the class
*/
protected AbstractJaxRsResourceProvider(Class<? extends AbstractJaxRsProvider> theProviderClass) {
theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
}
/**
* The base for request for a resource provider has the following form:</br>
* {@link AbstractJaxRsResourceProvider#getBaseForServer()
* getBaseForServer()} + "/" +
* {@link AbstractJaxRsResourceProvider#getResourceType() getResourceType()}
* .{@link java.lang.Class#getSimpleName() getSimpleName()}
*/
@Override
public String getBaseForRequest() {
try {
return new URL(getUriInfo().getBaseUri().toURL(), getResourceType().getSimpleName()).toExternalForm();
} catch(Exception e) {
// cannot happen
return null;
}
}
try {
return new URL(getUriInfo().getBaseUri().toURL(), getResourceType().getSimpleName()).toExternalForm();
} catch (Exception e) {
// cannot happen
return null;
}
}
@POST
@Interceptors(JaxRsExceptionInterceptor.class)
public Response create(final String resourceString)
throws Exception {
return executeMethod(resourceString, RequestTypeEnum.POST, RestOperationTypeEnum.CREATE, null);
}
/**
* Create a new resource with a server assigned id
*
* @param resource the body of the post method containing resource being created in a xml/json form
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#create">https://www.hl7. org/fhir/http.html#create</a>
*/
@POST
public Response create(final String resource) throws Exception {
return execute(getRequest(RequestTypeEnum.POST, RestOperationTypeEnum.CREATE).resource(resource));
}
@POST
@Interceptors(JaxRsExceptionInterceptor.class)
@Path("/_search")
public Response searchWithPost() throws Exception {
return executeMethod(null, RequestTypeEnum.POST, RestOperationTypeEnum.SEARCH_TYPE, null);
}
@GET
@Interceptors(JaxRsExceptionInterceptor.class)
public Response search() throws Exception {
return executeMethod(null, RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE, null);
}
@PUT
@Path("/{id}")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response update(@PathParam("id") final String id, final String resourceString)
throws Exception {
return executeMethod(resourceString, RequestTypeEnum.PUT, RestOperationTypeEnum.UPDATE, id);
}
@DELETE
@Path("/{id}")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response delete(@PathParam("id") final String id) throws Exception {
return executeMethod(null, RequestTypeEnum.DELETE, RestOperationTypeEnum.DELETE, id);
}
@GET
@Path("/{id}")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response find(@PathParam("id") final String id) throws Exception {
return executeMethod(null, RequestTypeEnum.GET, RestOperationTypeEnum.READ, id);
}
protected Response customOperation(final String resource, RequestTypeEnum requestType, String id, String operationName, RestOperationTypeEnum operationType)
throws Exception {
Validate.notNull(resource, "resource may not be null");
return executeMethod(resource, requestType, operationType, id, getBindings().getBinding(operationType, operationName));
}
@GET
@Path("/{id}/_history/{version}")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response findHistory(@PathParam("id") final String id, @PathParam("version") final String versionString)
throws BaseServerResponseException, IOException {
BaseMethodBinding<?> method = getBindings().getBinding(RestOperationTypeEnum.VREAD);
final RequestDetails theRequest = createRequestDetails(null, RequestTypeEnum.GET, RestOperationTypeEnum.VREAD);
if (id == null) {
throw new InvalidRequestException("Don't know how to handle request path: " + getUriInfo().getRequestUri().toASCIIString());
}
theRequest.setId(new IdDt(getBaseForRequest(), id, UrlUtil.unescape(versionString)));
return (Response) method.invokeServer(this, theRequest);
}
/**
* Search the resource type based on some filter criteria
*
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
*/
@POST
@Path("/_search")
public Response searchWithPost() throws Exception {
return execute(getRequest(RequestTypeEnum.POST, RestOperationTypeEnum.SEARCH_TYPE));
}
/**
* Search the resource type based on some filter criteria
*
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
*/
@GET
@Path("/{id}/{compartment}")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response findCompartment(@PathParam("id") final String id, @PathParam("compartment") final String compartment) throws BaseServerResponseException, IOException {
BaseMethodBinding<?> method = getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, compartment);
final RequestDetails theRequest = createRequestDetails(null, RequestTypeEnum.GET, RestOperationTypeEnum.VREAD);
if (id == null) {
throw new InvalidRequestException("Don't know how to handle request path: " + getUriInfo().getRequestUri().toASCIIString());
}
theRequest.setCompartmentName(compartment);
theRequest.setId(new IdDt(getBaseForRequest(), id));
return (Response) method.invokeServer(this, theRequest);
}
private <T extends BaseMethodBinding<?>> Response executeMethod(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation, String id)
throws BaseServerResponseException, IOException {
BaseMethodBinding<?> method = getBindings().getBinding(restOperation);
return executeMethod(resourceString, requestType, restOperation, id, method);
}
public Response search() throws Exception {
return execute(getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE));
}
private Response executeMethod(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation, String id,
BaseMethodBinding<?> method)
throws IOException {
final JaxRsRequest theRequest = createRequestDetails(resourceString, requestType, restOperation, id);
try {
return (Response) method.invokeServer(this, theRequest);
} catch(BaseServerRuntimeResponseException theException) {
return new JaxRsExceptionInterceptor().handleException(theRequest, theException);
}
}
protected JaxRsRequest createRequestDetails(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation, String id) {
JaxRsRequest theRequest = super.createRequestDetails(resourceString, requestType, restOperation);
theRequest.setId(StringUtils.isBlank(id) ? null : new IdDt(getResourceType().getName(), UrlUtil.unescape(id)));
if(restOperation == RestOperationTypeEnum.UPDATE) {
String contentLocation = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION);
if (contentLocation != null) {
theRequest.setId(new IdDt(contentLocation));
}
}
return theRequest;
}
@Override
/**
* Update an existing resource by its id (or create it if it is new)
*
* @param id the id of the resource
* @param resource the body contents for the put method
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#update">https://www.hl7.org/fhir/http.html#update</a>
*/
@PUT
@Path("/{id}")
public Response update(@PathParam("id") final String id, final String resource) throws Exception {
return execute(getRequest(RequestTypeEnum.PUT, RestOperationTypeEnum.UPDATE).id(id).resource(resource));
}
/**
* Delete a resource
*
* @param id the id of the resource to delete
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#delete">https://www.hl7.org/fhir/http.html#delete</a>
*/
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") final String id) throws Exception {
return execute(getRequest(RequestTypeEnum.DELETE, RestOperationTypeEnum.DELETE).id(id));
}
/**
* Read the current state of the resource
*
* @param id the id of the resource to read
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#read">https://www.hl7.org/fhir/http.html#read</a>
*/
@GET
@Path("/{id}")
public Response find(@PathParam("id") final String id) throws Exception {
return execute(getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.READ).id(id));
}
/**
* Execute a custom operation
*
* @param resource the resource to create
* @param requestType the type of request
* @param id the id of the resource on which to perform the operation
* @param operationName the name of the operation to execute
* @param operationType the rest operation type
* @return the response
* @see <a href="https://www.hl7.org/fhir/operations.html">https://www.hl7.org/fhir/operations.html</a>
*/
protected Response customOperation(final String resource, RequestTypeEnum requestType, String id,
String operationName, RestOperationTypeEnum operationType) throws Exception {
Builder request = getRequest(requestType, operationType).resource(resource).id(id);
return execute(request, operationName);
}
/**
* Retrieve the update history for a particular resource
*
* @param id the id of the resource
* @param version the version of the resource
* @return the response
* @see <a href="https://www.hl7.org/fhir/http.html#history">https://www.hl7.org/fhir/http.html#history</a>
*/
@GET
@Path("/{id}/_history/{version}")
public Response findHistory(@PathParam("id") final String id, @PathParam("version") final String version)
throws IOException {
Builder theRequest = getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.VREAD).id(id)
.version(version);
return execute(theRequest);
}
/**
* Compartment Based Access
*
* @param id the resource to which the compartment belongs
* @param compartment the compartment
* @return the repsonse
* @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
* @see <a href="https://www.hl7.org/fhir/compartments.html#compartment">https://www.hl7.org/fhir/compartments.html#compartment</a>
*/
@GET
@Path("/{id}/{compartment}")
public Response findCompartment(@PathParam("id") final String id,
@PathParam("compartment") final String compartment) throws IOException {
Builder theRequest = getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE).id(id)
.compartment(compartment);
return execute(theRequest, compartment);
}
/**
* Execute the method described by the requestBuilder and methodKey
*
* @param theRequestBuilder the requestBuilder that contains the information about the request
* @param methodKey the key determining the method to be executed
* @return the response
*/
private Response execute(Builder theRequestBuilder, String methodKey) throws IOException {
JaxRsRequest theRequest = theRequestBuilder.build();
BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey);
try {
return (Response) method.invokeServer(this, theRequest);
} catch (JaxRsResponseException theException) {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, theException);
}
}
/**
* Execute the method described by the requestBuilder
*
* @param theRequestBuilder the requestBuilder that contains the information about the request
* @return the response
*/
private Response execute(Builder theRequestBuilder) throws IOException {
return execute(theRequestBuilder, JaxRsMethodBindings.DEFAULT_METHOD_KEY);
}
/**
* Return the method binding for the given rest operation
*
* @param restOperation the rest operation to retrieve
* @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation)
* @return
*/
protected BaseMethodBinding<?> getBinding(RestOperationTypeEnum restOperation, String theBindingKey) {
return getBindings().getBinding(restOperation, theBindingKey);
}
/**
* Default: an empty list of interceptors
*
* @see ca.uhn.fhir.rest.server.IRestfulServer#getInterceptors()
*/
@Override
public List<IServerInterceptor> getInterceptors() {
return Collections.emptyList();
}
/**
* Default: no paging provider
*/
@Override
public IPagingProvider getPagingProvider() {
return null;
}
/**
* Default: BundleInclusionRule.BASED_ON_INCLUDES
*/
@Override
public BundleInclusionRule getBundleInclusionRule() {
return BundleInclusionRule.BASED_ON_INCLUDES;
}
}
/**
* The resource type should return conform to the generic resource included
* in the topic
*/
@Override
public abstract Class<R> getResourceType();
public abstract Class<R> getResourceType();
public MethodBindings getBindings() {
return bindings;
/**
* Return the bindings defined in this resource provider
*
* @return the jax-rs method bindings
*/
public JaxRsMethodBindings getBindings() {
return theBindings;
}
}

View File

@ -5,55 +5,67 @@ import java.io.IOException;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.servlet.ServletException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
/**
* An interceptor that catches the jax-rs exceptions
* @author Peter Van Houte
*/
public class JaxRsExceptionInterceptor {
/** the existing exception handler which is able to convert exception into responses*/
private ExceptionHandlingInterceptor exceptionHandler;
@Context
private UriInfo info;
@Context
private HttpHeaders headers;
AbstractJaxRsProvider theServer;
FhirContext fhirContext = AbstractJaxRsProvider.CTX;
/**
* The default constructor
*/
public JaxRsExceptionInterceptor() {
this.exceptionHandler = new ExceptionHandlingInterceptor();
}
public JaxRsExceptionInterceptor(ExceptionHandlingInterceptor exceptionHandler) {
/**
* A utility constructor for unit testing
* @param exceptionHandler the handler for the exception conversion
*/
JaxRsExceptionInterceptor(ExceptionHandlingInterceptor exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
/**
* This interceptor will catch all exception and convert them using the exceptionhandler
* @param ctx the invocation context
* @return the result
* @throws JaxRsResponseException an exception that can be handled by a jee container
*/
@AroundInvoke
public Object intercept(final InvocationContext ctx) throws Throwable {
public Object intercept(final InvocationContext ctx) throws JaxRsResponseException {
try {
return ctx.proceed();
} catch(final Exception theException) {
theServer = (AbstractJaxRsProvider) ctx.getTarget();
AbstractJaxRsProvider theServer = (AbstractJaxRsProvider) ctx.getTarget();
throw convertException(theServer, theException);
}
}
public BaseServerRuntimeResponseException convertException(final AbstractJaxRsProvider theServer, final Exception theException) {
JaxRsRequest requestDetails = new JaxRsRequest(theServer, null, null, null);
private JaxRsResponseException convertException(final AbstractJaxRsProvider theServer, final Exception theException) {
JaxRsRequest requestDetails = theServer.getRequest(null, null).build();
BaseServerResponseException convertedException = preprocessException(theException, requestDetails);
return new BaseServerRuntimeResponseException(convertedException);
return new JaxRsResponseException(convertedException);
}
public Response handleException(JaxRsRequest theRequest, BaseServerRuntimeResponseException theException)
/**
* This method converts an exception into a response
* @param theRequest the request
* @param theException the thrown exception
* @return the response describing the error
*/
public Response convertExceptionIntoResponse(JaxRsRequest theRequest, JaxRsResponseException theException)
throws IOException {
return handleExceptionWithoutServletError(theRequest, theException);
}

View File

@ -4,12 +4,21 @@ import javax.ejb.ApplicationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
/**
* A JEE wrapper exception that will not force a rollback.
* @author Peter Van Houte
*/
@ApplicationException(rollback=false)
public class BaseServerRuntimeResponseException extends BaseServerResponseException {
public class JaxRsResponseException extends BaseServerResponseException {
private static final long serialVersionUID = 1L;
public BaseServerRuntimeResponseException(BaseServerResponseException base) {
/**
* Utility constructor
*
* @param base the base exception
*/
public JaxRsResponseException(BaseServerResponseException base) {
super(base.getStatusCode(), base.getMessage(), base.getCause(), base.getOperationOutcome());
}

View File

@ -0,0 +1,133 @@
package ca.uhn.fhir.jaxrs.server.util;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.OperationMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import ca.uhn.fhir.util.ReflectionUtil;
/**
* Class that contains the method bindings defined by a ResourceProvider
* @author Peter Van Houte
*/
public class JaxRsMethodBindings {
/** DEFAULT_METHOD_KEY="" */
public static final String DEFAULT_METHOD_KEY = "";
/** Static collection of bindings mapped to a class*/
private static final ConcurrentHashMap<Class<?>, JaxRsMethodBindings> classBindings = new ConcurrentHashMap<Class<?>, JaxRsMethodBindings>();
/** Static collection of operationBindings mapped to a class */
private ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String, BaseMethodBinding<?>>> operationBindings = new ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String,BaseMethodBinding<?>>>();
/**
* The constructor
* @param theProvider the provider which is an implementation of the theProviderClass
* @param theProviderClass the class definition contaning the operations
*/
public JaxRsMethodBindings(AbstractJaxRsProvider theProvider, Class<? extends AbstractJaxRsProvider> theProviderClass) {
for (final Method m : ReflectionUtil.getDeclaredMethods(theProviderClass)) {
final BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, theProvider.getFhirContext(), theProvider);
if (foundMethodBinding == null) {
continue;
}
String bindingKey = getBindingKey(foundMethodBinding);
addMethodBinding(bindingKey, foundMethodBinding);
}
}
/**
* Get the key for the baseMethodBinding. This is:
* <ul>
* <li>the compartName for SearchMethodBindings
* <li>the methodName for OperationMethodBindings
* <li> {@link #DEFAULT_METHOD_KEY} for all other MethodBindings
* </ul>
* @param theBinding the methodbinding
* @return the key for the methodbinding.
*/
private String getBindingKey(final BaseMethodBinding<?> theBinding) {
if (theBinding instanceof OperationMethodBinding) {
return ((OperationMethodBinding) theBinding).getName();
} else if (theBinding instanceof SearchMethodBinding) {
Search search = theBinding.getMethod().getAnnotation(Search.class);
return search.compartmentName();
} else {
return DEFAULT_METHOD_KEY;
}
}
private void addMethodBinding(String key, BaseMethodBinding<?> binding) {
ConcurrentHashMap<String, BaseMethodBinding<?>> mapByOperation = getMapForOperation(binding.getRestOperationType());
if (mapByOperation.containsKey(key)) {
throw new IllegalArgumentException("Multiple Search Method Bindings Found : " + mapByOperation.get(key) + " -- " + binding.getMethod());
}
mapByOperation.put(key, binding);
}
/**
* Get the map for the given operation type. If no map exists for this operation type, create a new hashmap for this
* operation type and add it to the operation bindings.
*
* @param operationType the operation type.
* @return the map defined in the operation bindings
*/
private ConcurrentHashMap<String, BaseMethodBinding<?>> getMapForOperation(RestOperationTypeEnum operationType) {
ConcurrentHashMap<String, BaseMethodBinding<?>> result = operationBindings.get(operationType);
if(result == null) {
operationBindings.putIfAbsent(operationType, new ConcurrentHashMap<String, BaseMethodBinding<?>>());
return getMapForOperation(operationType);
} else {
return result;
}
}
/**
* Get the binding
*
* @param operationType the type of operation
* @param theBindingKey the binding key
* @return the binding defined
* @throws NotImplementedOperationException cannot be found
*/
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType, String theBindingKey) {
String bindingKey = StringUtils.defaultIfBlank(theBindingKey, DEFAULT_METHOD_KEY);
ConcurrentHashMap<String, BaseMethodBinding<?>> map = operationBindings.get(operationType);
if(map == null || !map.containsKey(bindingKey)) {
throw new NotImplementedOperationException("Operation not implemented");
} else {
return map.get(bindingKey);
}
}
/**
* Get the method bindings for the given class. If this class is not yet contained in the classBindings, they will be added for this class
*
* @param theProvider the implementation class
* @param theProviderClass the provider class
* @return the methodBindings for this class
*/
public static JaxRsMethodBindings getMethodBindings(AbstractJaxRsProvider theProvider, Class<? extends AbstractJaxRsProvider> theProviderClass) {
if(!getClassBindings().containsKey(theProviderClass)) {
JaxRsMethodBindings foundBindings = new JaxRsMethodBindings(theProvider, theProviderClass);
getClassBindings().putIfAbsent(theProviderClass, foundBindings);
}
return getClassBindings().get(theProviderClass);
}
/**
* @return the classBindings
*/
static ConcurrentHashMap<Class<?>, JaxRsMethodBindings> getClassBindings() {
return classBindings;
}
}

View File

@ -11,21 +11,118 @@ import javax.ws.rs.core.HttpHeaders;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IRestfulResponse;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.UrlUtil;
/**
* The JaxRsRequest is a jax-rs specific implementation of the RequestDetails.
*
* @author Peter Van Houte
*/
public class JaxRsRequest extends RequestDetails {
/**
* An implementation of the builder pattern for the JaxRsRequest
*/
public static class Builder {
private String theResource;
private AbstractJaxRsProvider theServer;
private RequestTypeEnum theRequestType;
private RestOperationTypeEnum theRestOperation;
private String theId;
private String theVersion;
private String theCompartment;
public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType,
RestOperationTypeEnum theRestOperation) {
this.theServer = theServer;
this.theRequestType = theRequestType;
this.theRestOperation = theRestOperation;
}
/**
* Set the resource
* @param resource the body contents of an http method
* @return the builder
*/
public Builder resource(String resource) {
this.theResource = resource;
return this;
}
/**
* Set the id
* @param id the resource id
* @return the builder
*/
public Builder id(String id) {
this.theId = id;
return this;
}
/**
* Set the id version
* @param version the version of the resource
* @return the builder
*/
public Builder version(String version) {
this.theVersion = version;
return this;
}
/**
* Set the compartment
* @param compartment the compartment
* @return the builder
*/
public Builder compartment(String compartment) {
this.theCompartment = compartment;
return this;
}
/**
* Create the jax-rs request
* @return the jax-rs request
*/
public JaxRsRequest build() {
JaxRsRequest result = new JaxRsRequest(theServer, theResource, theRequestType, theRestOperation);
if ((StringUtils.isNotBlank(theVersion) || StringUtils.isNotBlank(theCompartment))
&& StringUtils.isBlank(theId)) {
throw new InvalidRequestException("Don't know how to handle request path: "
+ theServer.getUriInfo().getRequestUri().toASCIIString());
}
if (StringUtils.isNotBlank(theVersion)) {
result.setId(
new IdDt(theServer.getBaseForRequest(), UrlUtil.unescape(theId), UrlUtil.unescape(theVersion)));
} else if (StringUtils.isNotBlank(theId)) {
result.setId(new IdDt(theServer.getBaseForRequest(), UrlUtil.unescape(theId)));
}
if (theRestOperation == RestOperationTypeEnum.UPDATE) {
String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
if (contentLocation != null) {
result.setId(new IdDt(contentLocation));
}
}
result.setCompartmentName(theCompartment);
return result;
}
}
private String theResourceString;
private HttpHeaders headers;
private AbstractJaxRsProvider myServer;
public JaxRsRequest() {
}
public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
RestOperationTypeEnum restOperation) {
this.headers = server.getHeaders();
@ -33,7 +130,7 @@ public class JaxRsRequest extends RequestDetails {
this.setRestOperationType(restOperation);
setServer(server);
setFhirServerBase(server.getBaseForServer());
setParameters(server.getQueryMap());
setParameters(server.getParameters());
setRequestType(requestType);
}
@ -65,7 +162,8 @@ public class JaxRsRequest extends RequestDetails {
@Override
protected byte[] getByteStreamRequestContents() {
return StringUtils.defaultIfEmpty(theResourceString, "").getBytes(ResourceParameter.determineRequestCharset(this));
return StringUtils.defaultIfEmpty(theResourceString, "")
.getBytes(ResourceParameter.determineRequestCharset(this));
}
@Override

View File

@ -13,6 +13,7 @@ import javax.ws.rs.core.Response.ResponseBuilder;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.method.ParseAction;
@ -21,12 +22,26 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulResponse;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
/**
* The JaxRsResponse is a jax-rs specific implementation of the RestfulResponse.
*
* @author Peter Van Houte
*/
public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
public JaxRsResponse(JaxRsRequest jaxRsRequestDetails) {
super(jaxRsRequestDetails);
/**
* The constructor
*
* @param request the JaxRs Request
*/
public JaxRsResponse(JaxRsRequest request) {
super(request);
}
/**
* The response writer is a simple String Writer. All output is configured
* by the server.
*/
@Override
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip)
throws UnsupportedEncodingException, IOException {
@ -55,8 +70,8 @@ public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
MethodOutcome response, String resourceName) throws IOException {
StringWriter writer = new StringWriter();
if (outcome != null) {
IParser parser = RestfulServerUtils.getNewParser(getRequestDetails().getServer().getFhirContext(),
getRequestDetails());
FhirContext fhirContext = getRequestDetails().getServer().getFhirContext();
IParser parser = RestfulServerUtils.getNewParser(fhirContext, getRequestDetails());
outcome.execute(parser, writer);
}
return sendWriterResponse(operationStatus, getParserType(), null, writer);

View File

@ -1,86 +0,0 @@
package ca.uhn.fhir.jaxrs.server.util;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.OperationMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import ca.uhn.fhir.util.ReflectionUtil;
/**
* @author Peter Van Houte
* Class that contains the method bindings defined by a ResourceProvider
*/
public class MethodBindings {
/** Static collection of bindings mapped to a class*/
private static final ConcurrentHashMap<Class<?>, MethodBindings> classBindings = new ConcurrentHashMap<Class<?>, MethodBindings>();
/** Static collection of operationBindings mapped to a class */
private ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String, BaseMethodBinding<?>>> operationBindings = new ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String,BaseMethodBinding<?>>>();
public <T extends AbstractJaxRsResourceProvider<?>> MethodBindings(T theProvider, Class<?> clazz) {
for (final Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
final BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, theProvider.getFhirContext(), theProvider);
if (foundMethodBinding == null) {
continue;
}
String bindingKey = getBindingKey(foundMethodBinding);
putIfAbsent(bindingKey, foundMethodBinding);
}
}
private String getBindingKey(final BaseMethodBinding<?> foundMethodBinding) {
if (foundMethodBinding instanceof OperationMethodBinding) {
return ((OperationMethodBinding) foundMethodBinding).getName();
} else if (foundMethodBinding instanceof SearchMethodBinding) {
Search search = foundMethodBinding.getMethod().getAnnotation(Search.class);
return search.compartmentName();
} else {
return "";
}
}
private void putIfAbsent(String key, BaseMethodBinding<?> binding) {
operationBindings.putIfAbsent(binding.getRestOperationType(), new ConcurrentHashMap<String, BaseMethodBinding<?>>());
ConcurrentHashMap<String, BaseMethodBinding<?>> map = operationBindings.get(binding.getRestOperationType());
if (map.containsKey(key)) {
throw new IllegalArgumentException("Multiple Search Method Bindings Found : " + map.get(key) + " -- " + binding.getMethod());
}
map.put(key, binding);
}
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType) {
return getBinding(operationType, "");
}
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType, String qualifier) {
String nonEmptyQualifier = StringUtils.defaultIfBlank(qualifier, "");
ConcurrentHashMap<String, BaseMethodBinding<?>> map = operationBindings.get(operationType);
if(map == null || !map.containsKey(nonEmptyQualifier)) {
throw new NotImplementedOperationException("Operation not implemented");
} else {
return map.get(nonEmptyQualifier);
}
}
public static <T extends AbstractJaxRsResourceProvider<?>> MethodBindings getMethodBindings(T theProvider, Class<?> clazz) {
if(!getClassBindings().containsKey(clazz)) {
MethodBindings foundBindings = new MethodBindings(theProvider, clazz);
getClassBindings().putIfAbsent(clazz, foundBindings);
}
return getClassBindings().get(clazz);
}
public static ConcurrentHashMap<Class<?>, MethodBindings> getClassBindings() {
return classBindings;
}
}

View File

@ -19,8 +19,8 @@ import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jaxrs.server.example.TestDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.example.TestJaxRsMockPatientRestProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProvider;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
@ -49,10 +49,18 @@ public class AbstractJaxRsConformanceProviderTest {
@Test
public void testConformance() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestDummyPatientProvider.class, new TestDummyPatientProvider());
providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider());
Response response = createConformanceProvider(providers).conformance();
System.out.println(response);
}
@Test
public void testConformanceUsingOptions() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider());
Response response = createConformanceProvider(providers).conformanceUsingOptions();
System.out.println(response);
}
@Test
public void testConformanceWithMethods() throws Exception {

View File

@ -33,10 +33,10 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Matchers;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.example.RandomServerPortProvider;
import ca.uhn.fhir.jaxrs.server.example.TestJaxRsConformanceRestProvider;
import ca.uhn.fhir.jaxrs.server.example.TestJaxRsMockPatientRestProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.BaseServerRuntimeResponseException;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.test.RandomServerPortProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsConformanceRestProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
@ -353,7 +353,7 @@ public class AbstractJaxRsResourceProviderTest {
@Test
public void testXFindUnknownPatient() {
try {
BaseServerRuntimeResponseException notFoundException = new BaseServerRuntimeResponseException(new ResourceNotFoundException(new IdDt("999955541264")));
JaxRsResponseException notFoundException = new JaxRsResponseException(new ResourceNotFoundException(new IdDt("999955541264")));
when(mock.find(idCaptor.capture())).thenThrow(notFoundException);
client.read(Patient.class, "999955541264");
fail();

View File

@ -24,7 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.jaxrs.server.example.TestDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@ -41,10 +41,10 @@ public class JaxRsExceptionInterceptorTest {
public void setUp() {
interceptor = new JaxRsExceptionInterceptor();
context = mock(InvocationContext.class);
TestDummyPatientProvider provider = spy(TestDummyPatientProvider.class);
TestJaxRsDummyPatientProvider provider = spy(TestJaxRsDummyPatientProvider.class);
when(context.getTarget()).thenReturn(provider);
doReturn("http://baseUri").when(provider).getBaseForServer();
doReturn(new HashMap<String, String[]>()).when(provider).getQueryMap();
doReturn(new HashMap<String, String[]>()).when(provider).getParameters();
doReturn(mock(HttpHeaders.class)).when(provider).getHeaders();
}
@ -85,20 +85,17 @@ public class JaxRsExceptionInterceptorTest {
@Test
public void testHandleExceptionWithServletError() throws Throwable {
JaxRsRequest request = new JaxRsRequest((AbstractJaxRsProvider) context.getTarget(), null, null, null);
JaxRsRequest request = ((AbstractJaxRsProvider) context.getTarget()).getRequest(null, null).build();
ExceptionHandlingInterceptor exceptionHandler = spy(ExceptionHandlingInterceptor.class);
doThrow(new ServletException("someMessage")).when(exceptionHandler).preProcessOutgoingException(any(RequestDetails.class), any(Throwable.class),
isNull(HttpServletRequest.class));
interceptor = new JaxRsExceptionInterceptor(exceptionHandler);
when(context.proceed()).thenThrow(new ServletException());
BaseServerRuntimeResponseException thrownException = new BaseServerRuntimeResponseException(new NotImplementedOperationException("not implemented"));
JaxRsResponseException thrownException = new JaxRsResponseException(new NotImplementedOperationException("not implemented"));
doThrow(new javax.servlet.ServletException("someMessage")).when(exceptionHandler).handleException(request, thrownException);
BaseServerRuntimeResponseException internalException = thrownException;
Response result = interceptor.handleException(request, internalException);
Response result = interceptor.convertExceptionIntoResponse(request, thrownException);
assertEquals(InternalErrorException.STATUS_CODE, result.getStatus());
}

View File

@ -0,0 +1,23 @@
package ca.uhn.fhir.jaxrs.server.interceptor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import javax.ejb.ApplicationException;
import org.junit.Test;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
public class JaxRsResponseExceptionTest {
@Test
public void testException() {
ForbiddenOperationException wrappedException = new ForbiddenOperationException("someMessage");
JaxRsResponseException response = new JaxRsResponseException(wrappedException);
assertEquals(response.getMessage(), wrappedException.getMessage());
assertEquals(response.getStatusCode(), wrappedException.getStatusCode());
assertNotNull(response.getClass().getAnnotation(ApplicationException.class));
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jaxrs.server.example;
package ca.uhn.fhir.jaxrs.server.test;
import java.io.IOException;
import java.net.ServerSocket;

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jaxrs.server.example;
package ca.uhn.fhir.jaxrs.server.test;
import java.util.concurrent.ConcurrentHashMap;
@ -12,18 +12,15 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
/**
* Fhir Physician Rest Service
*
* @author axmpm
*
* A conformance provider exposes the mock patient and this provider
*/
@Path(TestJaxRsConformanceRestProvider.PATH)
@Path("")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class TestJaxRsConformanceRestProvider extends AbstractJaxRsConformanceProvider {
public TestJaxRsConformanceRestProvider() {
super("", "", "");
super("description", "name", "version");
}
@Override

View File

@ -1,9 +1,12 @@
package ca.uhn.fhir.jaxrs.server.example;
package ca.uhn.fhir.jaxrs.server.test;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.model.dstu2.resource.Patient;
public class TestDummyPatientProvider extends AbstractJaxRsResourceProvider<Patient> {
/**
* A dummy patient provider exposing no methods
*/
public class TestJaxRsDummyPatientProvider extends AbstractJaxRsResourceProvider<Patient> {
@Override
public Class<Patient> getResourceType() {

View File

@ -1,9 +1,8 @@
package ca.uhn.fhir.jaxrs.server.example;
package ca.uhn.fhir.jaxrs.server.test;
import java.util.List;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@ -15,7 +14,6 @@ import javax.ws.rs.core.Response;
import org.mockito.Mockito;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
@ -42,10 +40,7 @@ import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
/**
* Fhir Physician Rest Service
*
* @author axmpm
*
* A test server delegating each call to a mock
*/
@Path(TestJaxRsMockPatientRestProvider.PATH)
@Stateless
@ -96,7 +91,6 @@ public class TestJaxRsMockPatientRestProvider extends AbstractJaxRsResourceProvi
@GET
@Path("/{id}/$someCustomOperation")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
@ -109,7 +103,6 @@ public class TestJaxRsMockPatientRestProvider extends AbstractJaxRsResourceProvi
@POST
@Path("/{id}/$someCustomOperation")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);

View File

@ -11,7 +11,7 @@ import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import ca.uhn.fhir.jaxrs.server.example.TestDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
@ -29,51 +29,51 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
@FixMethodOrder(MethodSorters.DEFAULT)
public class MethodBindingsTest {
public class JaxRsMethodBindingsTest {
@Before
public void setUp() {
MethodBindings.getClassBindings().clear();
JaxRsMethodBindings.getClassBindings().clear();
}
@Test(expected = NotImplementedOperationException.class)
public void testFindMethodsForProviderNotDefinedMappingMethods() {
new TestDummyPatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "");
new TestJaxRsDummyPatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "");
}
@Test
public void testFindMethodsForProviderWithMethods() {
class TestFindPatientProvider extends TestDummyPatientProvider {
class TestFindPatientProvider extends TestJaxRsDummyPatientProvider {
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
}
new TestFindPatientProvider();
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE).getMethod().getDeclaringClass());
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getDeclaringClass());
}
@Test
public void testFindMethodsFor2ProvidersWithMethods() {
class TestFindPatientProvider extends TestDummyPatientProvider {
class TestFindPatientProvider extends TestJaxRsDummyPatientProvider {
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
}
}
class TestUpdatePatientProvider extends TestDummyPatientProvider {
class TestUpdatePatientProvider extends TestJaxRsDummyPatientProvider {
@Update
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) {
return null;
}
}
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE).getMethod().getDeclaringClass());
assertEquals(TestUpdatePatientProvider.class, new TestUpdatePatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE).getMethod().getDeclaringClass());
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getDeclaringClass());
assertEquals(TestUpdatePatientProvider.class, new TestUpdatePatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "").getMethod().getDeclaringClass());
}
@Test
public void testFindMethodsWithDoubleMethodsDeclaration() {
class TestDoubleSearchProvider extends TestDummyPatientProvider {
class TestDoubleSearchProvider extends TestJaxRsDummyPatientProvider {
@Search
public List<Patient> search1(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
@ -95,7 +95,7 @@ public class MethodBindingsTest {
@Test
public void testFindMethodsWithMultipleMethods() {
class TestFindPatientProvider extends TestDummyPatientProvider {
class TestFindPatientProvider extends TestJaxRsDummyPatientProvider {
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
return null;
@ -113,9 +113,9 @@ public class MethodBindingsTest {
return null;
}
}
MethodBindings bindings = new TestFindPatientProvider().getBindings();
assertEquals("search", bindings.getBinding(RestOperationTypeEnum.SEARCH_TYPE).getMethod().getName());
assertEquals("update", bindings.getBinding(RestOperationTypeEnum.UPDATE).getMethod().getName());
JaxRsMethodBindings bindings = new TestFindPatientProvider().getBindings();
assertEquals("search", bindings.getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getName());
assertEquals("update", bindings.getBinding(RestOperationTypeEnum.UPDATE, "").getMethod().getName());
assertEquals("firstMethod", bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$firstMethod").getMethod().getName());
assertEquals("secondMethod", bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$secondMethod").getMethod().getName());
try {

View File

@ -18,13 +18,12 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jaxrs.server.example.TestDummyPatientProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
@ -37,7 +36,7 @@ public class JaxRsRequestTest {
private JaxRsRequest details;
private MultivaluedMap<String, String> queryParameters = new MultivaluedHashMap<String, String>();
private ContainerRequest headers;
private TestDummyPatientProvider provider;
private TestJaxRsDummyPatientProvider provider;
@Before
public void setUp() throws URISyntaxException {
@ -96,11 +95,6 @@ public class JaxRsRequestTest {
assertEquals(this.provider, details.getServer());
}
@Test
public void testJaxRsRequestDetails() {
Validate.notNull(new JaxRsRequest());
}
public JaxRsRequest createRequestDetails() throws URISyntaxException {
//headers
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null, new MapPropertiesDelegate());
@ -110,7 +104,7 @@ public class JaxRsRequestTest {
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
//mocks
provider = spy(TestDummyPatientProvider.class);
provider = spy(TestJaxRsDummyPatientProvider.class);
doReturn(uriInfo).when(provider).getUriInfo();
doReturn(BASEURI).when(provider).getBaseForRequest();
doReturn(BASEURI).when(provider).getBaseForServer();

View File

@ -3,12 +3,10 @@ package ca.uhn.fhir.jaxrs.server.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import javax.ws.rs.core.Response;
@ -153,22 +151,6 @@ public class JaxRsResponseTest {
assertEquals(Constants.CT_XML+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
}
private String unzip(Object entity) throws IOException {
byte[] compressed = ((String) entity).getBytes(Constants.CHARSET_NAME_UTF8);
final int BUFFER_SIZE = 32;
ByteArrayInputStream is = new ByteArrayInputStream(compressed);
GZIPInputStream gis = new GZIPInputStream(is, BUFFER_SIZE);
StringBuilder string = new StringBuilder();
byte[] data = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = gis.read(data)) != -1) {
string.append(new String(data, 0, bytesRead));
}
gis.close();
is.close();
return string.toString();
}
private Bundle getSinglePatientResource() {
Patient theResource = createPatient();
Bundle bundle = Bundle.withSingleResource(theResource);

View File

@ -5,6 +5,7 @@ import javax.ws.rs.core.Application;
/**
* Fhir Patient Demo Application
*
* @author Peter Van Houte
*/
@ApplicationPath(value=FhirPatientDemoApplication.PATH)

View File

@ -9,7 +9,6 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@ -19,7 +18,6 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Condition;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
@ -51,10 +49,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
/**
* Fhir Physician Rest Service
*
* @author axmpm
*
* A demo JaxRs Patient Rest Provider
*/
@Local
@Path(JaxRsPatientRestProvider.PATH)
@ -72,10 +67,10 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
}
static {
patients.put("" + counter, createPatient("Van Houte"));
patients.put("" + (counter), createPatient("Agnew"));
patients.put(String.valueOf(counter), createPatient("Van Houte"));
patients.put(String.valueOf(counter), createPatient("Agnew"));
for (int i = 0; i < 20; i++) {
patients.put("" + (counter), createPatient("Random Patient " + counter));
patients.put(String.valueOf(counter), createPatient("Random Patient " + counter));
}
}
@ -133,7 +128,6 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
}
@Read
@Interceptors(JaxRsExceptionInterceptor.class)
public Patient find(@IdParam final IdDt theId) throws InvocationTargetException {
if (patients.containsKey(theId.getIdPart())) {
return getLast(patients.get(theId.getIdPart()));
@ -180,9 +174,8 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
@GET
@Path("/{id}/$last")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response operationLastGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$last",
public Response operationLastGet(@PathParam("id") String id) throws Exception {
return customOperation(null, RequestTypeEnum.GET, id, "$last",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
}
@ -197,7 +190,6 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
@POST
@Path("/{id}/$last")
@Interceptors(JaxRsExceptionInterceptor.class)
public Response operationLast(final String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, null, "$last",
RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
@ -206,7 +198,6 @@ public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Pati
@Operation(name = "last", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringDt.class) })
public Parameters last(@OperationParam(name = "dummy") StringDt dummyInput) throws InvocationTargetException {
System.out.println("inputparameter");
Parameters parameters = new Parameters();
Patient patient = find(new IdDt(counter.intValue() - 1));
parameters.addParameter().setName("return").setResource(patient)

View File

@ -63,8 +63,7 @@ public class JaxRsPatientProviderTest {
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
//client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
client = ourCtx.newRestfulGenericClient("http://localhost:8580/hapi-fhir-jaxrsserver-example/jaxrs-demo/");
client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
client.setEncoding(EncodingEnum.JSON);
client.registerInterceptor(new LoggingInterceptor(true));
}