001package ca.uhn.fhir.test.utilities.server; 002 003/*- 004 * #%L 005 * HAPI FHIR Test Utilities 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.context.FhirVersionEnum; 025import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; 026import ca.uhn.fhir.interceptor.api.IInterceptorService; 027import ca.uhn.fhir.interceptor.api.Pointcut; 028import ca.uhn.fhir.rest.api.EncodingEnum; 029import ca.uhn.fhir.rest.client.api.IGenericClient; 030import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; 031import ca.uhn.fhir.rest.server.IPagingProvider; 032import ca.uhn.fhir.rest.server.RestfulServer; 033import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; 034import ca.uhn.fhir.validation.FhirValidator; 035import ca.uhn.fhir.validation.ResultSeverityEnum; 036import org.apache.commons.lang3.Validate; 037import org.apache.commons.lang3.time.DateUtils; 038import org.junit.jupiter.api.extension.ExtensionContext; 039 040import javax.servlet.http.HttpServlet; 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.HashMap; 044import java.util.List; 045import java.util.Map; 046import java.util.function.Consumer; 047 048public class RestfulServerExtension extends BaseJettyServerExtension<RestfulServerExtension> { 049 private FhirContext myFhirContext; 050 private List<Object> myProviders = new ArrayList<>(); 051 private FhirVersionEnum myFhirVersion; 052 private RestfulServer myServlet; 053 private List<Consumer<RestfulServer>> myConsumers = new ArrayList<>(); 054 private Map<String, Object> myRunningServerUserData = new HashMap<>(); 055 private ServerValidationModeEnum myServerValidationMode = ServerValidationModeEnum.NEVER; 056 private IPagingProvider myPagingProvider; 057 058 /** 059 * Constructor 060 */ 061 public RestfulServerExtension(FhirContext theFhirContext, Object... theProviders) { 062 Validate.notNull(theFhirContext); 063 myFhirContext = theFhirContext; 064 if (theProviders != null) { 065 myProviders = new ArrayList<>(Arrays.asList(theProviders)); 066 } 067 } 068 069 /** 070 * Constructor: If this is used, it will create and tear down a FhirContext which is good for memory 071 */ 072 public RestfulServerExtension(FhirVersionEnum theFhirVersionEnum) { 073 Validate.notNull(theFhirVersionEnum); 074 myFhirVersion = theFhirVersionEnum; 075 } 076 077 /** 078 * User data map which is automatically cleared when the server is stopped 079 */ 080 public Map<String, Object> getRunningServerUserData() { 081 return myRunningServerUserData; 082 } 083 084 @Override 085 protected void startServer() throws Exception { 086 super.startServer(); 087 088 myFhirContext.getRestfulClientFactory().setSocketTimeout((int) (500 * DateUtils.MILLIS_PER_SECOND)); 089 myFhirContext.getRestfulClientFactory().setServerValidationMode(myServerValidationMode); 090 } 091 092 @Override 093 protected HttpServlet provideServlet() { 094 if (myServlet == null) { 095 myServlet = new RestfulServer(myFhirContext); 096 myServlet.setDefaultPrettyPrint(true); 097 if (myProviders != null) { 098 myServlet.registerProviders(myProviders); 099 } 100 if (myPagingProvider != null) { 101 myServlet.setPagingProvider(myPagingProvider); 102 } 103 104 myConsumers.forEach(t -> t.accept(myServlet)); 105 } 106 107 return myServlet; 108 } 109 110 @Override 111 public void stopServer() throws Exception { 112 super.stopServer(); 113 if (!isRunning()) { 114 return; 115 } 116 myRunningServerUserData.clear(); 117 myPagingProvider = null; 118 myServlet = null; 119 } 120 121 122 private void createContextIfNeeded() { 123 if (myFhirVersion != null) { 124 myFhirContext = FhirContext.forCached(myFhirVersion); 125 } 126 } 127 128 /** 129 * Creates a new client for each callof this method 130 */ 131 public IGenericClient getFhirClient() { 132 return myFhirContext.newRestfulGenericClient(getBaseUrl()); 133 } 134 135 public FhirContext getFhirContext() { 136 createContextIfNeeded(); 137 return myFhirContext; 138 } 139 140 public RestfulServer getRestfulServer() { 141 return myServlet; 142 } 143 144 @Override 145 public void beforeEach(ExtensionContext theContext) throws Exception { 146 createContextIfNeeded(); 147 super.beforeEach(theContext); 148 } 149 150 public RestfulServerExtension registerProvider(Object theProvider) { 151 Validate.notNull(theProvider); 152 if (isStarted()) { 153 myServlet.registerProvider(theProvider); 154 } else { 155 myProviders.add(theProvider); 156 } 157 return this; 158 } 159 160 public RestfulServerExtension withServer(Consumer<RestfulServer> theConsumer) { 161 if (isStarted()) { 162 theConsumer.accept(myServlet); 163 } else { 164 myConsumers.add(theConsumer); 165 } 166 return this; 167 } 168 169 private boolean isStarted() { 170 return myServlet != null; 171 } 172 173 public RestfulServerExtension registerInterceptor(Object theInterceptor) { 174 return withServer(t -> t.getInterceptorService().registerInterceptor(theInterceptor)); 175 } 176 177 public RestfulServerExtension withValidationMode(ServerValidationModeEnum theValidationMode) { 178 myServerValidationMode = theValidationMode; 179 return this; 180 } 181 182 public void unregisterAllInterceptors() { 183 myServlet.getInterceptorService().unregisterAllInterceptors(); 184 } 185 186 public RestfulServerExtension withPagingProvider(IPagingProvider thePagingProvider) { 187 if (isStarted()) { 188 myServlet.setPagingProvider(thePagingProvider); 189 } else { 190 myPagingProvider = thePagingProvider; 191 } 192 return this; 193 } 194 195 public RestfulServerExtension unregisterInterceptor(Object theInterceptor) { 196 return withServer(t -> t.getInterceptorService().unregisterInterceptor(theInterceptor)); 197 } 198 199 public void unregisterProvider(Object theProvider) { 200 withServer(t -> t.unregisterProvider(theProvider)); 201 } 202 203 public Integer getDefaultPageSize() { 204 return myServlet.getDefaultPageSize(); 205 } 206 207 public void setDefaultPageSize(Integer theInitialDefaultPageSize) { 208 myServlet.setDefaultPageSize(theInitialDefaultPageSize); 209 } 210 211 public IInterceptorService getInterceptorService() { 212 return myServlet.getInterceptorService(); 213 } 214 215 public RestfulServerExtension registerAnonymousInterceptor(Pointcut thePointcut, IAnonymousInterceptor theInterceptor) { 216 return withServer(t -> t.getInterceptorService().registerAnonymousInterceptor(thePointcut, theInterceptor)); 217 } 218 219 public RestfulServerExtension withDefaultResponseEncoding(EncodingEnum theEncodingEnum) { 220 return withServer(t -> t.setDefaultResponseEncoding(theEncodingEnum)); 221 } 222}