add server using jaxrs

This commit is contained in:
petervanhoute 2015-10-02 11:27:21 +02:00
parent 9664174b6a
commit b4df6f9612
25 changed files with 2228 additions and 45 deletions

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.method;
public interface IRestfulHeader {
}

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.rest.server;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IRestfulServer {
/**
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
* creating their own.
*/
public FhirContext getFhirContext();
public List<IServerInterceptor> getInterceptors();
}

View File

@ -0,0 +1,16 @@
package ca.uhn.fhir.rest.server;
public interface IRestfulServerDefaults {
/**
* Should the server "pretty print" responses by default (requesting clients can always override this default by supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>
* parameter in the request URL.
* <p>
* The default is <code>false</code>
* </p>
*
* @return Returns the default pretty print setting
*/
public boolean isDefaultPrettyPrint();
}

View File

@ -41,7 +41,6 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -76,7 +75,7 @@ import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.VersionUtil; import ca.uhn.fhir.util.VersionUtil;
public class RestfulServer extends HttpServlet { public class RestfulServer extends HttpServlet implements IRestfulServerDefaults, IRestfulServer {
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor(); private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -1086,6 +1085,7 @@ public class RestfulServer extends HttpServlet {
* *
* @return Returns the default pretty print setting * @return Returns the default pretty print setting
*/ */
@Override
public boolean isDefaultPrettyPrint() { public boolean isDefaultPrettyPrint() {
return myDefaultPrettyPrint; return myDefaultPrettyPrint;
} }

View File

@ -510,7 +510,7 @@ public class RestfulServerUtils {
return null; return null;
} }
public static boolean prettyPrintResponse(RestfulServer theServer, RequestDetails theRequest) { public static boolean prettyPrintResponse(IRestfulServerDefaults theServer, RequestDetails theRequest) {
Map<String, String[]> requestParams = theRequest.getParameters(); Map<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.get(Constants.PARAM_PRETTY); String[] pretty = requestParams.get(Constants.PARAM_PRETTY);
boolean prettyPrint; boolean prettyPrint;

View File

@ -0,0 +1,215 @@
package ca.uhn.fhir.rest.server;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.Manifest;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
public class RestulfulServerConfiguration {
private List<ResourceBinding> resourceBindings;
private List<BaseMethodBinding<?>> serverBindings;
private String implementationDescription;
private String serverVersion;
private String serverName;
private FhirContext fhirContext;
private ServletContext servletContext;
private IServerAddressStrategy serverAddressStrategy;
private String conformanceDate;
public RestulfulServerConfiguration() {
}
public RestulfulServerConfiguration(RestfulServer theRestfulServer) {
this.resourceBindings = new LinkedList<ResourceBinding>(theRestfulServer.getResourceBindings());
this.serverBindings = theRestfulServer.getServerBindings();
this.implementationDescription = theRestfulServer.getImplementationDescription();
this.serverVersion = theRestfulServer.getServerVersion();
this.serverName = theRestfulServer.getServerName();
this.fhirContext = theRestfulServer.getFhirContext();
this.serverAddressStrategy= theRestfulServer.getServerAddressStrategy();
this.servletContext = theRestfulServer.getServletContext();
if (servletContext != null) {
InputStream inputStream = null;
try {
inputStream = getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF");
if (inputStream != null) {
Manifest manifest = new Manifest(inputStream);
this.conformanceDate = manifest.getMainAttributes().getValue("Build-Time");
}
} catch (IOException e) {
// fall through
}
finally {
if (inputStream != null) {
IOUtils.closeQuietly(inputStream);
}
}
}
}
/**
* Get the resourceBindings
* @return the resourceBindings
*/
public List<ResourceBinding> getResourceBindings() {
return resourceBindings;
}
/**
* Set the resourceBindings
* @param resourceBindings the resourceBindings to set
*/
public RestulfulServerConfiguration setResourceBindings(List<ResourceBinding> resourceBindings) {
this.resourceBindings = resourceBindings;
return this;
}
/**
* Get the serverBindings
* @return the serverBindings
*/
public List<BaseMethodBinding<?>> getServerBindings() {
return serverBindings;
}
/**
* Set the serverBindings
* @param serverBindings the serverBindings to set
*/
public RestulfulServerConfiguration setServerBindings(List<BaseMethodBinding<?>> serverBindings) {
this.serverBindings = serverBindings;
return this;
}
/**
* Get the implementationDescription
* @return the implementationDescription
*/
public String getImplementationDescription() {
return implementationDescription;
}
/**
* Set the implementationDescription
* @param implementationDescription the implementationDescription to set
*/
public RestulfulServerConfiguration setImplementationDescription(String implementationDescription) {
this.implementationDescription = implementationDescription;
return this;
}
/**
* Get the serverVersion
* @return the serverVersion
*/
public String getServerVersion() {
return serverVersion;
}
/**
* Set the serverVersion
* @param serverVersion the serverVersion to set
*/
public RestulfulServerConfiguration setServerVersion(String serverVersion) {
this.serverVersion = serverVersion;
return this;
}
/**
* Get the serverName
* @return the serverName
*/
public String getServerName() {
return serverName;
}
/**
* Set the serverName
* @param serverName the serverName to set
*/
public RestulfulServerConfiguration setServerName(String serverName) {
this.serverName = serverName;
return this;
}
/**
* Get the servletContext
* @return the servletContext
*/
public ServletContext getServletContext() {
return servletContext;
}
/**
* Set the servletContext
* @param servletContext the servletContext to set
*/
public RestulfulServerConfiguration setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
return this;
}
/**
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
* creating their own.
*/
public FhirContext getFhirContext() {
return this.fhirContext;
}
/**
* Set the fhirContext
* @param fhirContext the fhirContext to set
*/
public RestulfulServerConfiguration setFhirContext(FhirContext fhirContext) {
this.fhirContext = fhirContext;
return this;
}
/**
* Get the serverAddressStrategy
* @return the serverAddressStrategy
*/
public IServerAddressStrategy getServerAddressStrategy() {
return serverAddressStrategy;
}
/**
* Set the serverAddressStrategy
* @param serverAddressStrategy the serverAddressStrategy to set
*/
public void setServerAddressStrategy(IServerAddressStrategy serverAddressStrategy) {
this.serverAddressStrategy = serverAddressStrategy;
}
/**
* Get the conformanceDate
* @return the conformanceDate
*/
public String getConformanceDate() {
return conformanceDate;
}
/**
* Set the conformanceDate
* @param conformanceDate the conformanceDate to set
*/
public void setConformanceDate(String conformanceDate) {
this.conformanceDate = conformanceDate;
}
public String getServerBaseForRequest(HttpServletRequest theRequest) {
return getServerAddressStrategy().determineServerBase(getServletContext(), theRequest);
}
}

View File

@ -0,0 +1,274 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<repositories>
<repository>
<id>maven.java.net</id>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
<url>https://maven.java.net/service/local/repositories/snapshots/content/</url>
</repository>
</repositories>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR JAX-RS Server</name>
<dependencies>
<!-- HAPI DEPENDENCIES -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>1.3-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<!-- UNKNOWN -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-schematron</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-commons</artifactId>
<scope>test</scope>
</dependency>
<!-- PREVIOUS TESTING
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
-->
<!-- own api -->
<!--dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.2.1.GA</version>
<scope>test</scope>
</dependency-->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>ejb-api</artifactId>
<version>3.0</version>
</dependency>
<!-- Jetty -->
<!-- http://stackoverflow.com/questions/10048004/integrating-jetty-with-jax-rs-jersey -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>test</scope>
</dependency>
<!-- Jersey (JAX-RS) -->
<!--
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-test-framework</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.grizzly</groupId>
<artifactId>grizzly-servlet-webserver</artifactId>
</dependency>
-->
</dependencies>
<!--
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<configuration>
<skipDeploy>true</skipDeploy>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>1.3-SNAPSHOT</version>
<executions>
<execution>
<id>build_dstu1</id>
<goals>
<goal>generate-jparest-server</goal>
</goals>
<configuration>
<version>dstu</version>
<packageBase>ca.uhn.fhir.jpa.rp.dstu</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu1.xml</targetResourceSpringBeansFile>
<baseResourceNames/>
</configuration>
</execution>
<execution>
<id>build_dstu2</id>
<goals>
<goal>generate-jparest-server</goal>
</goals>
<configuration>
<version>dstu2</version>
<packageBase>ca.uhn.fhir.jpa.rp.dstu2</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu2.xml</targetResourceSpringBeansFile>
<baseResourceNames/>
<excludeResourceNames>
<excludeResourceName>OperationDefinition</excludeResourceName>
<excludeResourceName>OperationOutcome</excludeResourceName>
</excludeResourceNames>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
</resource>
<resource>
<directory>${basedir}/target/generated-sources/tinder</directory>
</resource>
<resource>
<directory>${basedir}/target/generated-resources/tinder</directory>
</resource>
</resources>
</build>
-->
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
</plugin>
</plugins>
</reporting>
</project>

View File

@ -0,0 +1,152 @@
package ca.uhn.fhir.jaxrs.server;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
import ca.uhn.fhir.util.ReflectionUtil;
/**
* Conformance Rest Service
* @author Peter Van Houte
*/
@Produces(MediaType.APPLICATION_JSON)
public abstract class AbstractConformanceRestServer extends AbstractJaxRsRestServer implements IConformanceRestServer {
public static final String PATH = "/";
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(AbstractConformanceRestServer.class);
private ResourceBinding myServerBinding = new ResourceBinding();
private ConcurrentHashMap<String, ResourceBinding> myResourceNameToBinding = new ConcurrentHashMap<String, ResourceBinding>();
private RestulfulServerConfiguration serverConfiguration = new RestulfulServerConfiguration();
private Conformance myConformance;
protected AbstractConformanceRestServer(String implementationDescription, String serverName, String serverVersion) {
serverConfiguration.setFhirContext(getFhirContext());
serverConfiguration.setImplementationDescription(implementationDescription);
serverConfiguration.setServerName(serverName);
serverConfiguration.setServerVersion(serverVersion);
}
protected void setUpPostConstruct()
throws Exception {
List<BaseMethodBinding<?>> serverBindings = new ArrayList<BaseMethodBinding<?>>();
for (ResourceBinding baseMethodBinding : myResourceNameToBinding.values()) {
serverBindings.addAll(baseMethodBinding.getMethodBindings());
}
serverConfiguration.setServerBindings(serverBindings);
serverConfiguration.setResourceBindings(new LinkedList<ResourceBinding>(myResourceNameToBinding.values()));
HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy();
hardcodedServerAddressStrategy.setValue(getBaseUri());
serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy);
ServerConformanceProvider serverConformanceProvider = new ServerConformanceProvider(serverConfiguration);
serverConformanceProvider.initializeOperations();
myConformance = serverConformanceProvider.getServerConformance(null);
}
@GET
@OPTIONS
@Path("/metadata")
@Produces(MediaType.APPLICATION_JSON)
public Response conformance(String string) {
String conformanceString = getParser().encodeResourceToString(myConformance);
ResponseBuilder entity = Response.status(Constants.STATUS_HTTP_200_OK).entity(conformanceString);
entity.header("Access-Control-Allow-Origin", "*");
return entity.build();
}
protected int findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
int count = 0;
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) {
continue;
}
count++;
// if (foundMethodBinding instanceof ConformanceMethodBinding) {
// myServerConformanceMethod = foundMethodBinding;
// continue;
// }
if (!Modifier.isPublic(m.getModifiers())) {
throw new ConfigurationException("Method '" + m.getName() + "' is not public, FHIR RESTful methods must be public");
} else {
if (Modifier.isStatic(m.getModifiers())) {
throw new ConfigurationException("Method '" + m.getName() + "' is static, FHIR RESTful methods must not be static");
} else {
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
String resourceName = foundMethodBinding.getResourceName();
ResourceBinding resourceBinding;
if (resourceName == null) {
resourceBinding = myServerBinding;
} else {
RuntimeResourceDefinition definition = getFhirContext().getResourceDefinition(resourceName);
if (myResourceNameToBinding.containsKey(definition.getName())) {
resourceBinding = myResourceNameToBinding.get(definition.getName());
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToBinding.put(resourceName, resourceBinding);
}
}
List<Class<?>> allowableParams = foundMethodBinding.getAllowableParamAnnotations();
if (allowableParams != null) {
for (Annotation[] nextParamAnnotations : m.getParameterAnnotations()) {
for (Annotation annotation : nextParamAnnotations) {
Package pack = annotation.annotationType().getPackage();
if (pack.equals(IdParam.class.getPackage())) {
if (!allowableParams.contains(annotation.annotationType())) {
throw new ConfigurationException("Method[" + m.toString() + "] is not allowed to have a parameter annotated with " + annotation);
}
}
}
}
}
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
}
}
}
return count;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Conformance.class;
}
}

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.jaxrs.server;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* Abstract Jax Rs Rest Server
* @author axmpm
*
*/
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public abstract class AbstractJaxRsRestServer {
private static Logger ourLog = LoggerFactory.getLogger(AbstractJaxRsRestServer.class);
public static FhirContext CTX = FhirContext.forDstu2();
@Context
protected UriInfo info;
@Context
HttpHeaders headers;
private IParser jsonParser = getFhirContext().newJsonParser();
private IParser xmlParser = getFhirContext().newXmlParser();
private String baseUri;
public static FhirContext getFhirContext() {
return CTX;
}
/**
* param and query methods
*/
protected HashMap<String, String[]> getQueryMap() {
MultivaluedMap<String, String> queryParameters = info.getQueryParameters();
HashMap<String, String[]> params = new HashMap<String, String[]>();
for (String key : queryParameters.keySet()) {
params.put(key, queryParameters.get(key).toArray(new String[] {}));
}
return params;
}
private String getParam(String string) {
for (Entry<String, List<String>> entry : info.getQueryParameters().entrySet()) {
if (string.equalsIgnoreCase(entry.getKey())) {
return entry.getValue().iterator().next();
}
}
return null;
}
protected Integer getIntParam(String string) {
String param = getParam(string);
return param == null ? 0 : Integer.valueOf(param);
}
protected String getBaseUri() {
if(this.baseUri == null) {
this.baseUri = info.getBaseUri().toASCIIString();
}
ourLog.debug("BaseUri is equal to %s", baseUri);
return this.baseUri;
}
/**
* PARSING METHODS
*/
public IParser getParser() {
IParser parser = MediaType.APPLICATION_XML.equals(getParserType()) ? xmlParser : jsonParser;
return parser.setPrettyPrint(getPrettyPrint());
}
private boolean getPrettyPrint() {
String printPretty = getParam("_pretty");
return printPretty == null || printPretty.trim().length() == 0 ? true : Boolean.valueOf(printPretty);
}
protected String getParserType() {
if ((headers != null && headers.getMediaType() != null && headers.getMediaType().getSubtype() != null
&& headers.getMediaType().getSubtype().contains("xml")) || getDefaultResponseEncoding() == EncodingEnum.XML
|| "xml".equals(getParam("_format"))) {
return MediaType.APPLICATION_XML;
} else {
return MediaType.APPLICATION_JSON;
}
}
Response createResponse(IBaseResource resource) {
Bundle resultingBundle = new Bundle();
resultingBundle.addEntry().setResource((IResource) resource);
return ok(encodeResponse(resultingBundle));
}
protected Response ok(String entity) {
return Response.status(Constants.STATUS_HTTP_200_OK).header("Content-Type", getParserType()).entity(entity).build();
}
private String encodeResponse(Bundle resource) {
return resource == null ? "null" : getParser().encodeBundleToString(resource);
}
/**
* DEFAULT VALUES
*/
public EncodingEnum getDefaultResponseEncoding() {
return EncodingEnum.JSON;
}
}

View File

@ -0,0 +1,230 @@
package ca.uhn.fhir.jaxrs.server;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import javax.interceptor.Interceptors;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
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 org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.jaxrs.server.interceptor.ExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.util.MethodBindings;
import ca.uhn.fhir.jaxrs.server.util.RestfulServerDefaults;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.method.CreateMethodBinding;
import ca.uhn.fhir.rest.method.OperationMethodBinding;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.ResourceBinding;
/**
* Fhir Physician Rest Service
* @author axmpm
*
*/
@SuppressWarnings({ "unused"})
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public abstract class AbstractResourceRestServer<R extends IResource> extends AbstractJaxRsRestServer implements IResourceProvider {
private static final Logger ourLog = LoggerFactory.getLogger(AbstractResourceRestServer.class);
private ResourceBinding myServerBinding = new ResourceBinding();
private IRestfulServerDefaults serverDefaults = new RestfulServerDefaults();
private MethodBindings bindings = new MethodBindings();
public AbstractResourceRestServer(Class<?> subclass) {
bindings.findMethods(this, subclass, getFhirContext());
}
@GET
@Interceptors(ExceptionInterceptor.class)
Response search() throws Exception {
return execute();
}
protected Response customOperation(final IBaseResource resource, RequestTypeEnum requestType)
throws Exception, IllegalAccessException, InvocationTargetException {
OperationMethodBinding method = bindings.getBinding(OperationMethodBinding.class);
final RequestDetails theRequest = createRequestDetails(resource, requestType);
final Object[] paramsServer = bindings.createParams(resource, method, theRequest);
Parameters result = (Parameters) method.getMethod().invoke(this, paramsServer);
return ok(getParser().encodeResourceToString(result));
}
Response create(final Map<String, String[]> params, R resource) throws Exception {
CreateMethodBinding method = bindings.getBinding(CreateMethodBinding.class);
final RequestDetails theRequest = createRequestDetails(resource, null);
final Object[] paramsServer = bindings.createParams(resource, method, theRequest);
MethodOutcome result = (MethodOutcome) method.getMethod().invoke(this, paramsServer);
return createResponse(result.getResource());
}
@POST
@Interceptors(ExceptionInterceptor.class)
public Response create(final String resourceString)
throws Exception {
return create(getQueryMap(), parseResource(resourceString));
}
@POST
@Interceptors(ExceptionInterceptor.class)
@Path("/_search")
public Response searchWithPost() throws Exception {
return search();
}
@GET
@Path("/{id}")
@Interceptors(ExceptionInterceptor.class)
public Response find(@PathParam("id") final String id) {
final R resource = find(new IdDt(id));
return createSingleResponse(resource);
}
@PUT
@Path("/{id}")
@Interceptors(ExceptionInterceptor.class)
public Response update(@PathParam("id") final String id, final String resourceString)
throws Exception {
final R resource = parseResource(resourceString);
// final MethodOutcome update = update(new IdDt(resource.getId()), practitioner);
// return createResponse(update.getResource());
return createResponse(resource);
}
@DELETE
@Path("/{id}")
@Interceptors(ExceptionInterceptor.class)
public Response delete(@PathParam("id") final String id)
throws Exception {
// final MethodOutcome delete = delete(new IdDt(id));
// return createResponse(delete.getResource());
return null;
}
@GET
@Path("/{id}/_history/{version}")
@Interceptors(ExceptionInterceptor.class)
public Response findHistory(@PathParam("id") final String id, @PathParam("version") final String version) {
final IdDt dt = new IdDt(getBaseUri(), getResourceType().getSimpleName(), id, version);
final R resource = findHistory(dt);
return createSingleResponse(resource);
}
@GET
@Path("/{id}/{compartment}")
@Interceptors(ExceptionInterceptor.class)
public Response findCompartment(@PathParam("id") final String id, @PathParam("compartment") final String compartment) {
final IdDt dt = new IdDt(getBaseUri(), getResourceType().getSimpleName(), id);
final R resource = find(new IdDt(id));
return createResponse(resource);
}
/**
* PARSING METHODS
*/
private Response createSingleResponse(final R resource) {
return ok(getParser().encodeResourceToString(resource));
}
Response createResponse(final IBaseResource resource) {
final Bundle resultingBundle = new Bundle();
resultingBundle.addEntry().setResource((IResource) resource);
return ok(encodeToString(resultingBundle));
}
Response createResponse(final List<R> resources) {
final Bundle resultingBundle = new Bundle();
for (final R resource : resources) {
addBundleEntry(resultingBundle, resource);
}
return ok(encodeToString(resultingBundle));
}
protected Response ok(String entity) {
return Response.status(Constants.STATUS_HTTP_200_OK).header("Content-Type", getParserType()).entity(entity).build();
}
protected String encodeToString(final Bundle resource) {
return resource != null ? getParser().encodeBundleToString(resource) : "null";
}
private R parseResource(final String resource) {
return getParser().parseResource(getResourceType(), resource);
}
@Deprecated
private void addBundleEntry(final Bundle resultingBundle, final R resource) {
final BundleEntry entry = resultingBundle.addEntry();
entry.setResource(resource);
if (resource != null && resource.getId() != null) {
entry.setId(resource.getId());
}
}
private RequestDetails createRequestDetails(final IBaseResource resource, RequestTypeEnum requestType) {
final RequestDetails theRequest = new RequestDetails() {
// @Override
// public String getHeader(String headerIfNoneExist) {
// List<String> requestHeader = headers.getRequestHeader(headerIfNoneExist);
// return (requestHeader == null || requestHeader.size() == 0) ? null : requestHeader.get(0);
// }
};
theRequest.setFhirServerBase(getBaseUri());
// theRequest.setServer(this);
theRequest.setParameters(getQueryMap());
// theRequest.setRequestContent(resource);
theRequest.setRequestType(requestType);
return theRequest;
}
public Response execute() {
SearchMethodBinding method = bindings.getBinding(SearchMethodBinding.class);
final RequestDetails theRequest = createRequestDetails(null, null);
final Object[] paramsServer = bindings.createParams(null, method, theRequest);
Object result = null; //method.invokeServer(null, paramsServer);
final IBundleProvider bundle = (IBundleProvider) result;
IVersionSpecificBundleFactory bundleFactory = getFhirContext().newBundleFactory();
// bundleFactory.initializeBundleFromBundleProvider(this, bundle, EncodingEnum.JSON, info.getAbsolutePath().toASCIIString(),
// info.getAbsolutePath().toASCIIString(), getPrettyPrint(), getIntParam("_getpagesoffset"), getIntParam("_count"), null,
// BundleTypeEnum.SEARCHSET, Collections.emptySet());
IBaseResource resource = bundleFactory.getResourceBundle();
return ok(getParser().encodeResourceToString(resource));
}
public R find(final IdDt theId) {
throw new UnsupportedOperationException();
}
public R findHistory(final IdDt theId) {
throw new UnsupportedOperationException();
}
@Override
public abstract Class<R> getResourceType();
}

View File

@ -0,0 +1,7 @@
package ca.uhn.fhir.jaxrs.server;
import ca.uhn.fhir.rest.server.IResourceProvider;
public interface IConformanceRestServer extends IResourceProvider {
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.jaxrs.server;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.jaxrs.server.interceptor.ExceptionInterceptor;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
/**
* Conformance Rest Service
* @author Peter Van Houte
*/
@Local
@Path(StaticJaxRsServer.PATH)
@Stateless
@Produces(MediaType.APPLICATION_JSON)
public class StaticJaxRsServer {
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(StaticJaxRsServer.class);
static final String PATH = "/";
@POST
@Path("/")
@Interceptors(ExceptionInterceptor.class)
public Response transaction(final String resource) {
ourLog.debug("calling transaction method");
return null;
}
@Transaction
public Bundle transaction(@TransactionParam Bundle theResources) {
ourLog.debug("transaction implemented");
return theResources;
}
}

View File

@ -0,0 +1,169 @@
package ca.uhn.fhir.jaxrs.server.interceptor;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsRestServer;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.OperationOutcomeUtil;
public class ExceptionInterceptor {
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(ExceptionInterceptor.class);
private Class<?>[] myReturnStackTracesForExceptionTypes;
@Context
private UriInfo info;
@Context
private HttpHeaders headers;
FhirContext fhirContext = AbstractJaxRsRestServer.getFhirContext();
@AroundInvoke
public Object intercept(final InvocationContext ctx) throws Exception {
try {
if(!ourLog.isDebugEnabled() || ctx.getMethod().getName().contains("getResourceType")) {
return ctx.proceed();
} else {
ourLog.debug("METHOD_CALL : " + ctx.getMethod().getName() + " [ " + Arrays.asList(ctx.getParameters()) + "] ");
Object proceed = ctx.proceed();
ourLog.debug("RESULT : " + proceed.toString());
return proceed;
}
} catch(final Exception theException) {
return handleException(theException);
}
}
public Response handleException(final Throwable theException)
{
IBaseOperationOutcome oo = null;
int statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
if (theException instanceof BaseServerResponseException) {
oo = ((BaseServerResponseException) theException).getOperationOutcome();
statusCode = ((BaseServerResponseException) theException).getStatusCode();
}
/*
* Generate an OperationOutcome to return, unless the exception throw by the resource provider had one
*/
if (oo == null) {
try {
final RuntimeResourceDefinition ooDef = fhirContext.getResourceDefinition("OperationOutcome");
oo = (IBaseOperationOutcome) ooDef.getImplementingClass().newInstance();
if (theException instanceof InternalErrorException) {
ourLog.error("Failure during REST processing", theException);
populateDetails(fhirContext, theException, oo);
} else if (theException instanceof BaseServerResponseException) {
ourLog.warn("Failure during REST processing: {}", theException);
final BaseServerResponseException baseServerResponseException = (BaseServerResponseException) theException;
statusCode = baseServerResponseException.getStatusCode();
populateDetails(fhirContext, theException, oo);
if (baseServerResponseException.getAdditionalMessages() != null) {
for (final String next : baseServerResponseException.getAdditionalMessages()) {
OperationOutcomeUtil.addIssue(fhirContext, oo, "error", next);
}
}
} else {
ourLog.error("Failure during REST processing: " + theException.toString(), theException);
populateDetails(fhirContext, theException, oo);
statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR;
}
} catch (final Exception e1) {
ourLog.error("Failed to instantiate OperationOutcome resource instance", e1);
final ResponseBuilder result = Response.status(Constants.STATUS_HTTP_500_INTERNAL_ERROR);
result.header(Constants.HEADER_CONTENT_TYPE, Constants.CT_TEXT_WITH_UTF8);
result.header(Constants.HEADER_CONTENT_ENCODING, Constants.CHARSET_NAME_UTF8);
result.entity(theException.getMessage());
return result.build();
}
} else {
ourLog.error("Unknown error during processing", theException);
}
// Add headers associated with the specific error code
if (theException instanceof BaseServerResponseException) {
final Map<String, String[]> additional = ((BaseServerResponseException) theException).getAssociatedHeaders();
if (additional != null) {
for (final Entry<String, String[]> next : additional.entrySet()) {
if (isNotBlank(next.getKey()) && next.getValue() != null) {
final String nextKey = next.getKey();
for (final String nextValue : next.getValue()) {
addHeader(nextKey, nextValue);
}
}
}
}
}
final boolean requestIsBrowser = false; // RestfulServer.requestIsBrowser(theRequest);
final String fhirServerBase = ""; // theRequestDetails.getFhirServerBase();
// theResponse.setStatus(statusCode);
// theRequestDetails.getServer().addHeadersToResponse(theResponse);
// theResponse.setContentType("text/plain");
// theResponse.setCharacterEncoding("UTF-8");
// theResponse.getWriter().append(theException.getMessage());
// theResponse.getWriter().close();
final ResponseBuilder result = Response.status(statusCode);
//final String resName = ctx.getResourceDefinition(oo).getName();
result.header(Constants.HEADER_CONTENT_TYPE, Constants.CT_TEXT_WITH_UTF8);
result.entity(theException.getMessage());
return result.build();
}
private void addHeader(final String nextKey, final String nextValue) {
throw new UnsupportedOperationException();
}
private void populateDetails(final FhirContext theCtx, final Throwable theException, final IBaseOperationOutcome theOo) {
if (myReturnStackTracesForExceptionTypes != null) {
for (final Class<?> next : myReturnStackTracesForExceptionTypes) {
if (next.isAssignableFrom(theException.getClass())) {
final String detailsValue = theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException);
OperationOutcomeUtil.addIssue(theCtx, theOo, "error", detailsValue);
return;
}
}
}
OperationOutcomeUtil.addIssue(theCtx, theOo, "error", theException.getMessage());
}
/**
* If any server methods throw an exception which extends any of the given exception types, the exception stack trace
* will be returned to the user. This can be useful for helping to diagnose issues, but may not be desirable for
* production situations.
*
* @param theExceptionTypes
* The exception types for which to return the stack trace to the user.
* @return Returns an instance of this interceptor, to allow for easy method chaining.
*/
public ExceptionInterceptor setReturnStackTracesForExceptionTypes(final Class<?>... theExceptionTypes) {
myReturnStackTracesForExceptionTypes = theExceptionTypes;
return this;
}
}

View File

@ -0,0 +1,39 @@
package ca.uhn.fhir.jaxrs.server.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.io.IOUtils;
import ca.uhn.fhir.parser.DataFormatException;
public class GZipUtil {
public static String decompress(byte[] theResource) {
GZIPInputStream is;
try {
is = new GZIPInputStream(new ByteArrayInputStream(theResource));
return IOUtils.toString(is, "UTF-8");
} catch (IOException e) {
throw new DataFormatException("Failed to decompress contents", e);
}
}
public static byte[] compress(String theEncoded) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
GZIPOutputStream gos = new GZIPOutputStream(os);
IOUtils.write(theEncoded, gos, "UTF-8");
gos.close();
os.close();
byte[] retVal = os.toByteArray();
return retVal;
} catch (IOException e) {
throw new DataFormatException("Compress contents", e);
}
}
}

View File

@ -0,0 +1,79 @@
package ca.uhn.fhir.jaxrs.server.util;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractResourceRestServer;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.CreateMethodBinding;
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.IParameter;
import ca.uhn.fhir.rest.method.OperationMethodBinding;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.UpdateMethodBinding;
import ca.uhn.fhir.rest.param.ResourceParameter;
import ca.uhn.fhir.util.ReflectionUtil;
@SuppressWarnings({"unchecked", "rawtypes"})
public class MethodBindings {
/** BaseOutcomeReturningMethodBinding */
private ConcurrentHashMap<String, CreateMethodBinding> createMethods = new ConcurrentHashMap<String, CreateMethodBinding>();
private ConcurrentHashMap<String, UpdateMethodBinding> updateMethods = new ConcurrentHashMap<String, UpdateMethodBinding>();
private ConcurrentHashMap<String, DeleteMethodBinding> delete = new ConcurrentHashMap<String, DeleteMethodBinding>();
/** BaseResourceReturingMethodBinding */
private ConcurrentHashMap<String, SearchMethodBinding> searchMethods = new ConcurrentHashMap<String, SearchMethodBinding>();
private ConcurrentHashMap<String, OperationMethodBinding> operationMethods = new ConcurrentHashMap<String, OperationMethodBinding>();
public <T extends AbstractResourceRestServer<?>> void findMethods(T theProvider, Class<?> subclass, FhirContext fhirContext) {
for (final Method m : ReflectionUtil.getDeclaredMethods(subclass)) {
final BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, fhirContext, theProvider);
if(foundMethodBinding == null) {
continue;
}
ConcurrentHashMap map = getMap(foundMethodBinding.getClass());
if (map.contains(theProvider.getResourceType().getName())) {
throw new IllegalArgumentException("Multiple Search Method Bindings Found : " + foundMethodBinding.getMethod() + " -- "
+ foundMethodBinding.getMethod());
} else {
map.put(theProvider.getResourceType().getName(), foundMethodBinding);
}
}
}
private <T> ConcurrentHashMap<String, T> getMap(Class<T> class1) {
if(class1.isAssignableFrom(CreateMethodBinding.class)) return (ConcurrentHashMap<String, T>) createMethods;
if(class1.isAssignableFrom(UpdateMethodBinding.class)) return (ConcurrentHashMap<String, T>) updateMethods;
if(class1.isAssignableFrom(DeleteMethodBinding.class)) return (ConcurrentHashMap<String, T>) delete;
if(class1.isAssignableFrom(SearchMethodBinding.class)) return (ConcurrentHashMap<String, T>) searchMethods;
if(class1.isAssignableFrom(OperationMethodBinding.class)) return (ConcurrentHashMap<String, T>) operationMethods;
return new ConcurrentHashMap();
}
public Object[] createParams(IBaseResource resource, final BaseMethodBinding<?> method, final RequestDetails theRequest) {
final Object[] paramsServer = new Object[method.getParameters().size()];
for (int i = 0; i < method.getParameters().size(); i++) {
final IParameter param = method.getParameters().get(i);
if(param instanceof ResourceParameter) {
paramsServer[i] = resource;
} else {
paramsServer[i] = param.translateQueryParametersIntoServerArgument(theRequest, null, method);
}
}
return paramsServer;
}
public <T> T getBinding(Class<T> clazz) {
ConcurrentHashMap map = getMap((Class<? extends BaseMethodBinding>) clazz);
if(map.values().size() == 0) {
throw new UnsupportedOperationException();
}
return (T) map.values().iterator().next();
}
}

View File

@ -0,0 +1,38 @@
package ca.uhn.fhir.jaxrs.server.util;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
public class RestfulServerDefaults implements IRestfulServerDefaults {
public boolean isUseBrowserFriendlyContentTypes() {
return true;
}
public ETagSupportEnum getETagSupport() {
return ETagSupportEnum.DISABLED;
}
public AddProfileTagEnum getAddProfileTag() {
return AddProfileTagEnum.NEVER;
}
public boolean isDefaultPrettyPrint() {
return true;
}
public IPagingProvider getPagingProvider() {
//Integer count = getIntParam("_count");
Integer count = 0;
return count == 0 ? null : new FifoMemoryPagingProvider(count);
}
public BundleInclusionRule getBundleInclusionRule() {
return BundleInclusionRule.BASED_ON_INCLUDES;
}
}

View File

@ -0,0 +1,287 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@Ignore
public class DemoTest {
private static final String serverBase = "http://localhost:8580/hapi-fhir-jaxrsserver-example/jaxrs-demo/";
private static IGenericClient client;
//START SNIPPET: client
@BeforeClass
public static void setUpOnce() {
final FhirContext ctx = FhirContext.forDstu2();
client = ctx.newRestfulGenericClient(serverBase);
client.setEncoding(EncodingEnum.JSON);
}
//END SNIPPET: client
/** Search/Query - Type */
@Test
public void findUsingGenericClientBySearch() {
// Perform a search
final ca.uhn.fhir.model.api.Bundle results = client.search().forResource(Patient.class)
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).execute();
System.out.println(results.getEntries().get(0));
assertEquals(results.getEntries().size(), 1);
}
/** Search - Multi-valued Parameters (ANY/OR) */
@Test
public void findUsingGenericClientBySearchWithMultiValues() {
final ca.uhn.fhir.model.api.Bundle response = client.search().forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario"))
.and(Patient.ADDRESS.matches().values("Canada"))
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).execute();
System.out.println(response.getEntries().get(0));
}
/** Search - Paging */
@Test
public void findWithPaging() {
// Perform a search
for(int i = 0 ; i < 10 ; i++) {
testCreatePatient();
}
final Bundle results = client.search().forResource(Patient.class).limitTo(8).returnBundle(Bundle.class).execute();
System.out.println(results.getEntry().size());
if (results.getLink(Bundle.LINK_NEXT) != null) {
// load next page
final Bundle nextPage = client.loadPage().next(results).execute();
System.out.println(nextPage.getEntry().size());
}
}
/** Search using other query options */
public void testOther() {
//missing
}
/** */
@Test
public void testSearchPost() {
Bundle response = client.search()
.forResource("Patient")
.usingStyle(SearchStyleEnum.POST)
.returnBundle(Bundle.class)
.execute();
assertTrue(response.getEntry().size() > 0);
}
/** Search - Compartments */
@Test
public void testSearchCompartements() {
Bundle response = client.search()
.forResource(Patient.class)
.withIdAndCompartment("1", "condition")
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
assertTrue(response.getEntry().size() > 0);
}
/** Search - Subsetting (_summary and _elements) */
@Test
@Ignore
public void testSummary() {
Object response = client.search()
.forResource(Patient.class)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
}
@Test
public void testCreatePatient() {
final Patient existing = new Patient();
existing.setId((IdDt) null);
existing.getNameFirstRep().addFamily("Created Patient 54");
client.setEncoding(EncodingEnum.XML);
final MethodOutcome results = client.create().resource(existing).execute();
System.out.println(results.getId());
final Bundle bundle = (Bundle) results.getResource();
final Patient patient = (Patient) bundle.getEntryFirstRep().getResource();
System.out.println(patient);
assertNotNull(client.read(patient.getId()));
client.setEncoding(EncodingEnum.JSON);
}
/** Conditional Creates */
@Test
public void testConditionalCreate() {
final Patient existing = new Patient();
existing.setId((IdDt) null);
existing.getNameFirstRep().addFamily("Created Patient 54");
client.setEncoding(EncodingEnum.XML);
final MethodOutcome results = client.create().resource(existing).execute();
System.out.println(results.getId());
final Bundle bundle = (Bundle) results.getResource();
final Patient patient = (Patient) bundle.getEntryFirstRep().getResource();
client.create()
.resource(patient)
.conditional()
.where(Patient.IDENTIFIER.exactly().identifier(patient.getIdentifierFirstRep()))
.execute();
}
/** Find By Id */
@Test
public void findUsingGenericClientById() {
final Patient results = client.read(Patient.class, "1");
assertTrue(results.getIdentifier().toString().contains("THOR"));
}
@Test
public void testUpdateById() {
final Patient existing = client.read(Patient.class, "1");
final List<HumanNameDt> name = existing.getName();
name.get(0).addSuffix("The Second");
existing.setName(name);
client.setEncoding(EncodingEnum.XML);
final MethodOutcome results = client.update("1", existing);
}
@Test
public void testDeletePatient() {
final Patient existing = new Patient();
existing.getNameFirstRep().addFamily("Created Patient XYZ");
final MethodOutcome results = client.create().resource(existing).execute();
System.out.println(results.getId());
final Bundle bundle = (Bundle) results.getResource();
final Patient patient = (Patient) bundle.getEntryFirstRep().getResource();
client.delete(Patient.class, patient.getId());
try {
assertNotNull(client.read(patient.getId()));
}
catch (final ResourceNotFoundException e) {
assertEquals(e.getStatusCode(), Constants.STATUS_HTTP_404_NOT_FOUND);
}
}
/** Transaction - Server */
@Test
public void testTransaction() {
ca.uhn.fhir.model.api.Bundle bundle = new ca.uhn.fhir.model.api.Bundle();
BundleEntry entry = bundle.addEntry();
final Patient existing = new Patient();
existing.getNameFirstRep().addFamily("Created with bundle");
entry.setResource(existing);
BoundCodeDt<BundleEntryTransactionMethodEnum> theTransactionOperation =
new BoundCodeDt(
BundleEntryTransactionMethodEnum.VALUESET_BINDER,
BundleEntryTransactionMethodEnum.POST);
entry.setTransactionMethod(theTransactionOperation);
ca.uhn.fhir.model.api.Bundle response = client.transaction().withBundle(bundle).execute();
}
/** Conformance - Server */
@Test
public void testConformance() {
final Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
System.out.println(conf.getRest().get(0).getResource().get(0).getType());
System.out.println(conf.getRest().get(0).getResource().get(1).getType());
}
/** Extended Operations */
// Create a client to talk to the HeathIntersections server
@Test
public void testExtendedOperations() {
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdDt("Patient", "1"))
.named("$last")
.withParameters(inParams)
//.useHttpGet() // Use HTTP GET instead of POST
.execute();
String resultValue = outParams.getParameter().get(0).getValue().toString();
System.out.println(resultValue);
assertEquals("expected but found : "+ resultValue, resultValue.contains("myAwesomeDummyValue"), true);
}
@Test
public void testExtendedOperationsUsingGet() {
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdDt("Patient", "1"))
.named("$last")
.withParameters(inParams)
.useHttpGet() // Use HTTP GET instead of POST
.execute();
String resultValue = outParams.getParameter().get(0).getValue().toString();
System.out.println(resultValue);
assertEquals("expected but found : "+ resultValue, resultValue.contains("myAwesomeDummyValue"), true);
}
@Test
public void testFindUnknownPatient() {
try {
final Patient existing = client.read(Patient.class, "999955541264");
}
catch (final ResourceNotFoundException e) {
e.printStackTrace();
assertEquals(e.getStatusCode(), 404);
}
}
@Test
public void testVRead() {
final Patient patient = client.vread(Patient.class, "1", "1");
System.out.println(patient);
}
}

View File

@ -0,0 +1,93 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management. You do not need to use this in your own projects, so the "parent" tag and it's contents below may be removed
if you are using this file as a basis for your own project. -->
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-jaxrsserver-example</artifactId>
<packaging>war</packaging>
<name>HAPI FHIR JPA Server - Example</name>
<repositories>
<repository>
<id>oss-snapshots</id>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<!-- This dependency includes the core HAPI-FHIR classes -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<!-- Tells Maven to name the generated WAR file as hapi-fhir-jpaserver-example.war -->
<finalName>hapi-fhir-jaxrsserver-example</finalName>
<!-- This is to run the integration tests -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>war</type>
<includes>*.war</includes>
</artifactItem>
</artifactItems>
<outputDirectory>C:\Agfa\ORBIS-AS\server\orbis-as-08.05.07.00.0009200-UK\standalone\deployments</outputDirectory>
<stripVersion>true</stripVersion>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.jaxrs.server.example;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* Conformance Rest Service
* @author Peter Van Houte
*/
@Local
@Path(ConformanceRestServer.PATH)
@Stateless
@Produces(MediaType.APPLICATION_JSON)
public class ConformanceRestServer extends ca.uhn.fhir.jaxrs.server.AbstractConformanceRestServer {
private static final String SERVER_VERSION = "1.0.0";
private static final String SERVER_DESCRIPTION = "Jax-Rs Test Example Description";
private static final String SERVER_NAME = "Jax-Rs Test Example";
@EJB
private ConformanceRestServer conformanceRestServer;
@EJB
private IFhirPatientRestServer patientRestServer;
public ConformanceRestServer() {
super(SERVER_DESCRIPTION, SERVER_NAME, SERVER_VERSION);
}
@PostConstruct
public void createMethod()
throws Exception {
findResourceMethods(conformanceRestServer, ConformanceRestServer.class);
findResourceMethods(patientRestServer, FhirPatientRestServer.class);
super.setUpPostConstruct();
}
}

View File

@ -0,0 +1,13 @@
package ca.uhn.fhir.jaxrs.server.example;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
/**
* Fhir Patient Demo Application
* @author Peter Van Houte
*/
@ApplicationPath(value=FhirPatientDemoApplication.PATH)
public class FhirPatientDemoApplication extends Application {
public final static String PATH = "/jaxrs-demo";
}

View File

@ -0,0 +1,222 @@
package ca.uhn.fhir.jaxrs.server.example;
import java.util.LinkedList;
import java.util.List;
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;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.jaxrs.server.AbstractResourceRestServer;
import ca.uhn.fhir.jaxrs.server.interceptor.ExceptionInterceptor;
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.Delete;
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.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
/**
* Fhir Physician Rest Service
* @author axmpm
*
*/
@Local(IFhirPatientRestServer.class)
@Path(FhirPatientRestServer.PATH)
@Stateless
@Produces(MediaType.APPLICATION_JSON)
public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> implements IFhirPatientRestServer {
static final String PATH = "/Patient";
private static Long counter = 1L;
private static final ConcurrentHashMap<String, List<Patient>> patients = new ConcurrentHashMap<String, List<Patient>>();
protected FhirPatientRestServer() throws Exception {
super(FhirPatientRestServer.class);
}
static {
patients.put(""+counter, createPatient("Agfa"));
patients.put(""+(counter), createPatient("Healthcare"));
for(int i = 0 ; i<20 ; i++) {
patients.put(""+(counter), createPatient("Random Patient " + counter));
}
}
private static List<Patient> createPatient(final String name) {
final Patient patient = new Patient();
patient.getNameFirstRep().addFamily(name);
return createPatient(patient);
}
private static List<Patient> createPatient(final Patient patient) {
patient.setId(createId(counter, 1L));
final LinkedList<Patient> list = new LinkedList<Patient>();
list.add(patient);
counter++;
return list ;
}
private static IdDt createId(final Long id, final Long theVersionId) {
return new IdDt("Patient", "" + id, "" + theVersionId);
}
@Search
@Override
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
final List<Patient> result = new LinkedList<Patient>();
for (final List<Patient> patientIterator : patients.values()) {
Patient single = null;
for (Patient patient : patientIterator) {
if (name == null || patient.getNameFirstRep().getFamilyFirstRep().getValueNotNull().equals(name.getValueNotNull())) {
single = patient;
}
}
if (single != null) {
result.add(single);
}
}
return result;
}
@Update
@Override
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient)
throws Exception {
final String idPart = theId.getIdPart();
if(patients.containsKey(idPart)) {
final List<Patient> patientList = patients.get(idPart);
final Patient lastPatient = getLast(patientList);
patient.setId(createId(theId.getIdPartAsLong(), lastPatient.getId().getVersionIdPartAsLong()+1));
patientList.add(patient);
final MethodOutcome result = new MethodOutcome().setCreated(false);
result.setResource(patient);
result.setId(patient.getId());
return result;
} else {
throw new ResourceNotFoundException(theId);
}
}
@Override
@Read
public Patient find(@IdParam final IdDt theId) {
if(patients.containsKey(theId.getIdPart())) {
return getLast(patients.get(theId.getIdPart()));
} else {
throw new ResourceNotFoundException(theId);
}
}
private Patient getLast(final List<Patient> list) {
return list.get(list.size()-1);
}
@Override
@Read(version = false)
public Patient findHistory(@IdParam final IdDt theId) {
if (patients.containsKey(theId.getIdPart())) {
final List<Patient> list = patients.get(theId.getIdPart());
for (final Patient patient : list) {
if (patient.getId().getVersionIdPartAsLong().equals(theId.getVersionIdPartAsLong())) {
return patient;
}
}
}
throw new ResourceNotFoundException(theId);
}
@Create
@Override
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional)
throws Exception {
patients.put(""+counter, createPatient(patient));
final MethodOutcome result = new MethodOutcome().setCreated(true);
result.setResource(patient);
result.setId(patient.getId());
return result;
}
@Delete
@Override
public MethodOutcome delete(@IdParam final IdDt theId) {
final Patient deletedPatient = find(theId);
patients.remove(deletedPatient.getId().getIdPart());
final MethodOutcome result = new MethodOutcome().setCreated(true);
result.setResource(deletedPatient);
return result;
}
@GET
@Path("/{id}/$last")
@Interceptors(ExceptionInterceptor.class)
@Override
public Response operationLastGet(final String resource)
throws Exception {
return customOperation(null, RequestTypeEnum.GET);
}
@POST
@Path("/{id}/$last")
@Interceptors(ExceptionInterceptor.class)
@Override
public Response operationLast(final String resource)
throws Exception {
return customOperation(getParser().parseResource(resource), RequestTypeEnum.POST);
}
// @ca.uhn.fhir.rest.annotation.Validate
// public MethodOutcome validate(
// @ResourceParam T theResource,
// @ResourceParam String theRawResource,
// @ResourceParam EncodingEnum theEncoding,
// @ca.uhn.fhir.rest.annotation.Validate.Mode ValidationModeEnum theMode,
// @ca.uhn.fhir.rest.annotation.Validate.Profile String theProfile) {
// return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile);
// }
@Operation(name="last", idempotent=true, returnParameters= {
@OperationParam(name="return", type=StringDt.class)
})
@Override
public Parameters last(@OperationParam(name = "dummy") StringDt dummyInput) {
System.out.println("inputparameter");
Parameters parameters = new Parameters();
Patient patient = find(new IdDt(counter.intValue()-1));
parameters
.addParameter()
.setName("return")
.setResource(patient)
.setValue(new StringDt((counter-1)+"" + "inputVariable [ " + dummyInput.getValue()+ "]"));
return parameters;
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
}

View File

@ -0,0 +1,39 @@
package ca.uhn.fhir.jaxrs.server.example;
import java.util.List;
import javax.ws.rs.core.Response;
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.api.MethodOutcome;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
public interface IFhirPatientRestServer extends IResourceProvider {
List<Patient> search(StringParam name);
MethodOutcome update(IdDt theId, Patient patient)
throws Exception;
Patient find(IdDt theId);
Patient findHistory(IdDt theId);
MethodOutcome create(Patient patient, String theConditional)
throws Exception;
MethodOutcome delete(IdDt theId);
Response operationLastGet(String resource)
throws Exception;
Response operationLast(String resource)
throws Exception;
Parameters last(StringDt dummyInput);
}

View File

@ -0,0 +1,52 @@
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ./xsd/web-app_3_0.xsd">
<!-- This filters provide support for Cross Origin Resource Sharing (CORS) -->
<!--
<filter>
<filter-name>CORS Filter</filter-name>
<filter-class>org.ebaysf.web.cors.CORSFilter</filter-class>
<init-param>
<description>A comma separated list of allowed origins. Note: An '*' cannot be used for an allowed origin when using credentials.</description>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<description>A comma separated list of HTTP verbs, using which a CORS request can be made.</description>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,PUT,DELETE,OPTIONS</param-value>
</init-param>
<init-param>
<description>A comma separated list of allowed headers when making a non simple CORS request.</description>
<param-name>cors.allowed.headers</param-name>
<param-value>X-FHIR-Starter,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
</init-param>
<init-param>
<description>A comma separated list non-standard response headers that will be exposed to XHR2 object.</description>
<param-name>cors.exposed.headers</param-name>
<param-value>Location,Content-Location</param-value>
</init-param>
<init-param>
<description>A flag that suggests if CORS is supported with cookies</description>
<param-name>cors.support.credentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<description>A flag to control logging</description>
<param-name>cors.logging.enabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<description>Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache.</description>
<param-name>cors.preflight.maxage</param-name>
<param-value>300</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
</web-app>

View File

@ -22,13 +22,17 @@ package ca.uhn.fhir.rest.server.provider;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.text.DateFormat; import java.util.ArrayList;
import java.text.ParseException; import java.util.Collections;
import java.text.SimpleDateFormat; import java.util.Comparator;
import java.util.*; import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import ca.uhn.fhir.parser.DataFormatException; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
@ -49,6 +53,7 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Metadata; import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding; import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
@ -59,10 +64,9 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IServerConformanceProvider; import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding; import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
import ca.uhn.fhir.util.ExtensionConstants; import ca.uhn.fhir.util.ExtensionConstants;
import javax.servlet.http.HttpServletRequest;
/** /**
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation * Server FHIR Provider which serves the conformance statement for a RESTful server implementation
* *
@ -78,10 +82,10 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
private boolean myCache = true; private boolean myCache = true;
private volatile Conformance myConformance; private volatile Conformance myConformance;
private String myPublisher = "Not provided"; private String myPublisher = "Not provided";
private RestfulServer myRestfulServer; private RestulfulServerConfiguration myRestfulServer;
public ServerConformanceProvider(RestfulServer theRestfulServer) { public ServerConformanceProvider(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer; myRestfulServer = new RestulfulServerConfiguration(theRestfulServer);
} }
/* /*
@ -95,7 +99,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
} }
public void setRestfulServer (RestfulServer theRestfulServer) { public void setRestfulServer (RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer; myRestfulServer = new RestulfulServerConfiguration(theRestfulServer);
} }
/** /**

View File

@ -21,15 +21,21 @@ package ca.uhn.fhir.rest.server.provider.dstu2;
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException; import java.util.ArrayList;
import java.io.InputStream; import java.util.Collections;
import java.util.*; import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.jar.Manifest; import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.parser.DataFormatException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
@ -53,6 +59,7 @@ import ca.uhn.fhir.model.dstu2.valueset.TypeRestfulInteractionEnum;
import ca.uhn.fhir.model.dstu2.valueset.UnknownContentCodeEnum; import ca.uhn.fhir.model.dstu2.valueset.UnknownContentCodeEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Initialize; import ca.uhn.fhir.rest.annotation.Initialize;
import ca.uhn.fhir.rest.annotation.Metadata; import ca.uhn.fhir.rest.annotation.Metadata;
@ -69,6 +76,7 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IServerConformanceProvider; import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding; import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
/** /**
@ -86,10 +94,14 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName; private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings; private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
private String myPublisher = "Not provided"; private String myPublisher = "Not provided";
private RestfulServer myRestfulServer; private RestulfulServerConfiguration myServerConfiguration;
public ServerConformanceProvider(RestfulServer theRestfulServer) { public ServerConformanceProvider(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer; this.myServerConfiguration = new RestulfulServerConfiguration(theRestfulServer);
}
public ServerConformanceProvider(RestulfulServerConfiguration theServerConfiguration) {
this.myServerConfiguration = theServerConfiguration;
} }
/* /*
@ -103,7 +115,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
} }
public void setRestfulServer (RestfulServer theRestfulServer) { public void setRestfulServer (RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer; myServerConfiguration = new RestulfulServerConfiguration(theRestfulServer);
} }
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) { private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) {
@ -124,7 +136,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() { private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>(); Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
for (ResourceBinding next : myRestfulServer.getResourceBindings()) { for (ResourceBinding next : myServerConfiguration.getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
@ -133,7 +145,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
} }
for (BaseMethodBinding<?> nextMethodBinding : myRestfulServer.getServerBindings()) { for (BaseMethodBinding<?> nextMethodBinding : myServerConfiguration.getServerBindings()) {
String resourceName = ""; String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>()); resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
@ -170,10 +182,10 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it // needs to be modified to actually allow it
retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription()); retVal.getImplementation().setDescription(myServerConfiguration.getImplementationDescription());
retVal.setKind(ConformanceStatementKindEnum.INSTANCE); retVal.setKind(ConformanceStatementKindEnum.INSTANCE);
retVal.getSoftware().setName(myRestfulServer.getServerName()); retVal.getSoftware().setName(myServerConfiguration.getServerName());
retVal.getSoftware().setVersion(myRestfulServer.getServerVersion()); retVal.getSoftware().setVersion(myServerConfiguration.getServerVersion());
retVal.addFormat(Constants.CT_FHIR_XML); retVal.addFormat(Constants.CT_FHIR_XML);
retVal.addFormat(Constants.CT_FHIR_JSON); retVal.addFormat(Constants.CT_FHIR_JSON);
@ -190,9 +202,9 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
Set<TypeRestfulInteractionEnum> resourceOps = new HashSet<TypeRestfulInteractionEnum>(); Set<TypeRestfulInteractionEnum> resourceOps = new HashSet<TypeRestfulInteractionEnum>();
RestResource resource = rest.addResource(); RestResource resource = rest.addResource();
String resourceName = nextEntry.getKey(); String resourceName = nextEntry.getKey();
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName); RuntimeResourceDefinition def = myServerConfiguration.getFhirContext().getResourceDefinition(resourceName);
resource.getTypeElement().setValue(def.getName()); resource.getTypeElement().setValue(def.getName());
resource.getProfile().setReference(new IdDt(def.getResourceProfile(myRestfulServer.getServerBaseForRequest(theRequest)))); resource.getProfile().setReference(new IdDt(def.getResourceProfile(myServerConfiguration.getServerBaseForRequest(theRequest))));
TreeSet<String> includes = new TreeSet<String>(); TreeSet<String> includes = new TreeSet<String>();
@ -297,7 +309,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
} }
private DateTimeDt conformanceDate() { private DateTimeDt conformanceDate() {
String buildDate = getBuildDateFromManifest(); String buildDate = myServerConfiguration.getConformanceDate();
if (buildDate != null) { if (buildDate != null) {
try { try {
return new DateTimeDt(buildDate); return new DateTimeDt(buildDate);
@ -308,21 +320,6 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
return DateTimeDt.withCurrentTime(); return DateTimeDt.withCurrentTime();
} }
private String getBuildDateFromManifest() {
if (myRestfulServer != null && myRestfulServer.getServletContext() != null) {
InputStream inputStream = myRestfulServer.getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF");
if (inputStream != null) {
try {
Manifest manifest = new Manifest(inputStream);
return manifest.getMainAttributes().getValue("Build-Time");
} catch (IOException e) {
// fall through
}
}
}
return null;
}
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) { private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
includes.addAll(searchMethodBinding.getIncludes()); includes.addAll(searchMethodBinding.getIncludes());
@ -427,7 +424,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode()); param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode());
} }
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) { for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget); RuntimeResourceDefinition targetDef = myServerConfiguration.getFhirContext().getResourceDefinition(nextTarget);
if (targetDef != null) { if (targetDef != null) {
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName()); ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
if (code != null) { if (code != null) {