package ca.uhn.fhirtest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.cors.CorsConfiguration; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.config.WebsocketDstu2Config; import ca.uhn.fhir.jpa.config.WebsocketDstu2DispatcherConfig; import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config; import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3DispatcherConfig; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; 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.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.BanUnsupportedHttpMethodsInterceptor; import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; import ca.uhn.fhirtest.config.TdlDstu2Config; import ca.uhn.fhirtest.config.TdlDstu3Config; import ca.uhn.fhirtest.config.TestDstu2Config; import ca.uhn.fhirtest.config.TestDstu3Config; public class TestRestfulServer extends RestfulServer { public static final String FHIR_BASEURL_DSTU1 = "fhir.baseurl.dstu1"; public static final String FHIR_BASEURL_DSTU2 = "fhir.baseurl.dstu2"; public static final String FHIR_BASEURL_DSTU3 = "fhir.baseurl.dstu3"; public static final String FHIR_BASEURL_TDL2 = "fhir.baseurl.tdl2"; public static final String FHIR_BASEURL_TDL3 = "fhir.baseurl.tdl3"; private static final long serialVersionUID = 1L; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestRestfulServer.class); private AnnotationConfigWebApplicationContext myAppCtx; @SuppressWarnings("unchecked") @Override protected void initialize() throws ServletException { super.initialize(); // Get the spring context from the web container (it's declared in web.xml) WebApplicationContext parentAppCtx = ContextLoaderListener.getCurrentWebApplicationContext(); // These two parmeters are also declared in web.xml String implDesc = getInitParameter("ImplementationDescription"); String fhirVersionParam = getInitParameter("FhirVersion"); if (StringUtils.isBlank(fhirVersionParam)) { fhirVersionParam = "DSTU1"; } // Depending on the version this server is supporing, we will // retrieve all the appropriate resource providers and the // conformance provider List<IResourceProvider> beans; @SuppressWarnings("rawtypes") IFhirSystemDao systemDao; ETagSupportEnum etagSupport; String baseUrlProperty; List<Object> plainProviders = new ArrayList<Object>(); switch (fhirVersionParam.trim().toUpperCase()) { case "DSTU1": { myAppCtx = new AnnotationConfigWebApplicationContext(); myAppCtx.setServletConfig(getServletConfig()); myAppCtx.setParent(parentAppCtx); myAppCtx.register(ca.uhn.fhirtest.config.TestDstu1Config.class); myAppCtx.refresh(); setFhirContext(FhirContext.forDstu1()); beans = myAppCtx.getBean("myResourceProvidersDstu1", List.class); plainProviders.add(myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProviderDstu1.class)); systemDao = myAppCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class); etagSupport = ETagSupportEnum.DISABLED; JpaConformanceProviderDstu1 confProvider = new JpaConformanceProviderDstu1(this, systemDao); confProvider.setImplementationDescription(implDesc); setServerConformanceProvider(confProvider); baseUrlProperty = FHIR_BASEURL_DSTU1; break; } case "TDL2": case "DSTU2": { myAppCtx = new AnnotationConfigWebApplicationContext(); myAppCtx.setServletConfig(getServletConfig()); myAppCtx.setParent(parentAppCtx); if ("TDL2".equals(fhirVersionParam.trim().toUpperCase())) { myAppCtx.register(TdlDstu2Config.class); baseUrlProperty = FHIR_BASEURL_TDL2; } else { myAppCtx.register(TestDstu2Config.class, WebsocketDstu2DispatcherConfig.class); baseUrlProperty = FHIR_BASEURL_DSTU2; } myAppCtx.refresh(); setFhirContext(FhirContext.forDstu2()); beans = myAppCtx.getBean("myResourceProvidersDstu2", List.class); plainProviders.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); etagSupport = ETagSupportEnum.ENABLED; JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription(implDesc); setServerConformanceProvider(confProvider); break; } case "TDL3": case "DSTU3": { myAppCtx = new AnnotationConfigWebApplicationContext(); myAppCtx.setServletConfig(getServletConfig()); myAppCtx.setParent(parentAppCtx); if ("TDL3".equals(fhirVersionParam.trim().toUpperCase())) { myAppCtx.register(TdlDstu3Config.class); baseUrlProperty = FHIR_BASEURL_TDL3; } else { myAppCtx.register(TestDstu3Config.class, WebsocketDstu3DispatcherConfig.class); baseUrlProperty = FHIR_BASEURL_DSTU3; } myAppCtx.refresh(); setFhirContext(FhirContext.forDstu3()); beans = myAppCtx.getBean("myResourceProvidersDstu3", List.class); plainProviders.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); etagSupport = ETagSupportEnum.ENABLED; JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription(implDesc); setServerConformanceProvider(confProvider); plainProviders.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); break; } default: throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam); } /* * On the DSTU2 endpoint, we want to enable ETag support */ setETagSupport(etagSupport); /* * This server tries to dynamically generate narratives */ FhirContext ctx = getFhirContext(); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); /* * The resource and system providers (which actually implement the various FHIR * operations in this server) are all retrieved from the spring context above * and are provided to the server here. */ for (IResourceProvider nextResourceProvider : beans) { ourLog.info(" * Have resource provider for: {}", nextResourceProvider.getResourceType().getSimpleName()); } setResourceProviders(beans); setPlainProviders(plainProviders); /* * Enable CORS */ CorsConfiguration config = new CorsConfiguration(); CorsInterceptor corsInterceptor = new CorsInterceptor(config); config.addAllowedHeader("Origin"); config.addAllowedHeader("Accept"); config.addAllowedHeader("X-Requested-With"); config.addAllowedHeader("Content-Type"); config.addAllowedHeader("Access-Control-Request-Method"); config.addAllowedHeader("Access-Control-Request-Headers"); config.addAllowedOrigin("*"); config.addExposedHeader("Location"); config.addExposedHeader("Content-Location"); config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS")); registerInterceptor(corsInterceptor); /* * We want to format the response using nice HTML if it's a browser, since this * makes things a little easier for testers. */ registerInterceptor(new ResponseHighlighterInterceptor()); registerInterceptor(new BanUnsupportedHttpMethodsInterceptor()); /* * Default to JSON with pretty printing */ setDefaultPrettyPrint(true); setDefaultResponseEncoding(EncodingEnum.JSON); /* * The server's base URL (e.g. http://fhirtest.uhn.ca/baseDstu2) is * pulled from a system property, which is helpful if you want to try * hosting your own copy of this server. */ String baseUrl = System.getProperty(baseUrlProperty); if (StringUtils.isBlank(baseUrl)) { // Try to fall back in case the property isn't set baseUrl = System.getProperty("fhir.baseurl"); if (StringUtils.isBlank(baseUrl)) { throw new ServletException("Missing system property: " + baseUrlProperty); } } setServerAddressStrategy(new MyHardcodedServerAddressStrategy(baseUrl)); /* * Spool results to the database */ setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class)); /* * Load interceptors for the server from Spring */ Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values(); for (IServerInterceptor interceptor : interceptorBeans) { this.registerInterceptor(interceptor); } } @Override public void destroy() { super.destroy(); ourLog.info("Server is shutting down"); myAppCtx.destroy(); } /** * The public server is deployed to http://fhirtest.uhn.ca and the JEE webserver * where this FHIR server is deployed is actually fronted by an Apache HTTPd instance, * so we use an address strategy to let the server know how it should address itself. */ private static class MyHardcodedServerAddressStrategy extends HardcodedServerAddressStrategy { public MyHardcodedServerAddressStrategy(String theBaseUrl) { super(theBaseUrl); } @Override public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) { /* * This is a bit of a hack, but we want to support both HTTP and HTTPS seamlessly * so we have the outer httpd proxy relay requests to the Java container on * port 28080 for http and 28081 for https. */ String retVal = super.determineServerBase(theServletContext, theRequest); if (theRequest.getRequestURL().indexOf("28081") != -1) { retVal = retVal.replace("http://", "https://"); } return retVal; } } }