View Javadoc
1   /*
2    * Copyright 2016 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.openehealth.ipf.commons.ihe.fhir;
18  
19  import ca.uhn.fhir.context.FhirContext;
20  import ca.uhn.fhir.rest.api.MethodOutcome;
21  import ca.uhn.fhir.rest.api.server.IBundleProvider;
22  import org.hl7.fhir.instance.model.api.IBaseBundle;
23  import org.hl7.fhir.instance.model.api.IBaseResource;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import javax.security.cert.X509Certificate;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  import java.io.Serializable;
31  import java.util.*;
32  
33  /**
34   * Abstract plain provider that allows subclasses to forward the received payload into the
35   * Camel route served by the consumer. Note that this can be subclassed for writing so-called
36   * plain providers, while resource-specific providers should extend from {@link AbstractResourceProvider}.
37   *
38   * @author Christian Ohr
39   * @since 3.1
40   */
41  public abstract class AbstractPlainProvider implements Serializable {
42  
43      private static final Logger LOG = LoggerFactory.getLogger(AbstractPlainProvider.class);
44  
45      private RequestConsumer consumer;
46  
47      protected FhirContext getFhirContext() {
48          return consumer.getFhirContext();
49      }
50  
51      /**
52       * Requests a single resource
53       *
54       * @param payload             FHIR request resource
55       * @param resultType          expected result type
56       * @param httpServletRequest  servlet request
57       * @param httpServletResponse servlet response
58       * @param <R>                 Result type
59       * @return result of processing
60       */
61      protected final <R extends IBaseResource> R requestResource(
62              Object payload, Class<R> resultType,
63              HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
64          return requestResource(payload, null, resultType, httpServletRequest, httpServletResponse);
65      }
66  
67      /**
68       * Requests a single resource with parameters
69       *
70       * @param payload             FHIR request resource (often null)
71       * @param parameters          FHIR parameters
72       * @param resultType          expected result type
73       * @param httpServletRequest  servlet request
74       * @param httpServletResponse servlet response
75       * @param <R>                 Result type
76       * @return result of processing
77       */
78      protected final <R extends IBaseResource> R requestResource(
79              Object payload, FhirSearchParameters parameters, Class<R> resultType,
80              HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
81          if (consumer == null) {
82              throw new IllegalStateException("Consumer is not initialized");
83          }
84          Map<String, Object> headers = enrichParameters(parameters, httpServletRequest);
85          return consumer.handleResourceRequest(payload, headers, resultType);
86      }
87  
88      /**
89       * Requests a list of resources
90       *
91       * @param payload             FHIR request resource
92       * @param httpServletRequest  servlet request
93       * @param httpServletResponse servlet response
94       * @param <R>                 Result type
95       * @return result of processing
96       */
97      protected final <R extends IBaseResource> List<R> requestBundle(Object payload,
98                                                                      HttpServletRequest httpServletRequest,
99                                                                      HttpServletResponse httpServletResponse) {
100         return requestBundle(payload, null, httpServletRequest, httpServletResponse);
101     }
102 
103     /**
104      * Requests a list of resources with parameters
105      *
106      * @param payload             FHIR request resource (often null)
107      * @param parameters          FHIR search parameters
108      * @param httpServletRequest  servlet request
109      * @param httpServletResponse servlet response
110      * @param <R>                 Result type
111      * @return result of processing
112      */
113     protected final <R extends IBaseResource> List<R> requestBundle(
114             Object payload, FhirSearchParameters parameters,
115             HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
116         return requestBundle(payload, parameters, null, httpServletRequest, httpServletResponse);
117     }
118 
119     /**
120      * Requests a list of resources with parameters
121      *
122      * @param payload             FHIR request resource (often null)
123      * @param parameters          FHIR search parameters
124      * @param resourceType        FHIR resource type being searched
125      * @param httpServletRequest  servlet request
126      * @param httpServletResponse servlet response
127      * @param <R>                 Result type
128      * @return result of processing
129      */
130     protected final <R extends IBaseResource> List<R> requestBundle(
131             Object payload, FhirSearchParameters parameters,
132             String resourceType,
133             HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
134         if (consumer == null) {
135             throw new IllegalStateException("Consumer is not initialized");
136         }
137         Map<String, Object> headers = enrichParameters(parameters, httpServletRequest);
138         if (resourceType != null) {
139             headers.put(Constants.FHIR_RESOURCE_TYPE_HEADER, resourceType);
140         }
141         return consumer.handleBundleRequest(payload, headers);
142     }
143 
144     /**
145      * Requests a {@link IBundleProvider} that takes over the responsibility to fetch requested
146      * bundles. The type of the returned {@link IBundleProvider} instance is determined
147      * by the {@link #consumer RequestConsumer} impelmentation.
148      *
149      * @param payload             FHIR request resource (often null)
150      * @param searchParameters    FHIR search parameters
151      * @param httpServletRequest  servlet request
152      * @param httpServletResponse servlet response
153      * @return IBundleProvider
154      */
155     protected final IBundleProvider requestBundleProvider(
156             Object payload, FhirSearchParameters searchParameters,
157             HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
158         return requestBundleProvider(payload, searchParameters, null, httpServletRequest, httpServletResponse);
159     }
160 
161     /**
162      * Requests a {@link IBundleProvider} that takes over the responsibility to fetch requested
163      * bundles. The type of the returned {@link IBundleProvider} instance is determined
164      * by the {@link #consumer RequestConsumer} impelmentation.
165      *
166      * @param payload             FHIR request resource (often null)
167      * @param searchParameters    FHIR search parameters
168      * @param resourceType        FHIR resource type
169      * @param httpServletRequest  servlet request
170      * @param httpServletResponse servlet response
171      * @return IBundleProvider
172      */
173     protected final IBundleProvider requestBundleProvider(
174             Object payload, FhirSearchParameters searchParameters,
175             String resourceType,
176             HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
177         if (consumer == null) {
178             throw new IllegalStateException("Consumer is not initialized");
179         }
180         Map<String, Object> headers = enrichParameters(searchParameters, httpServletRequest);
181         if (resourceType != null) {
182             headers.put(Constants.FHIR_RESOURCE_TYPE_HEADER, resourceType);
183         }
184         return consumer.handleBundleProviderRequest(payload, headers);
185     }
186 
187     /**
188      * Submits a resource to be created or updated
189      *
190      * @param payload             resource payload
191      * @param httpServletRequest  servlet request
192      * @param httpServletResponse servlet response
193      * @return result of processing
194      */
195     protected final MethodOutcome requestAction(
196             Object payload,
197             HttpServletRequest httpServletRequest,
198             HttpServletResponse httpServletResponse) {
199         return requestAction(payload, null, httpServletRequest, httpServletResponse);
200     }
201 
202     /**
203      * Submits a resource to be created or updated
204      *
205      * @param payload             resource payload
206      * @param parameters          parameters
207      * @param httpServletRequest  servlet request
208      * @param httpServletResponse servlet response
209      * @return result of processing
210      */
211     protected final MethodOutcome requestAction(
212             Object payload,
213             FhirSearchParameters parameters,
214             HttpServletRequest httpServletRequest,
215             HttpServletResponse httpServletResponse) {
216         if (consumer == null) {
217             throw new IllegalStateException("Consumer is not initialized");
218         }
219         Map<String, Object> headers = enrichParameters(parameters, httpServletRequest);
220         return consumer.handleAction(payload, headers);
221     }
222 
223     /**
224      * Submits a transaction request bundle, expecting a corresponding response bundle
225      *
226      * @param payload             transaction bundle
227      * @param httpServletRequest  servlet request
228      * @param httpServletResponse servlet response
229      * @return result of processing
230      */
231      protected final <T extends IBaseBundle> T requestTransaction(
232             Object payload,
233             Class<T> bundleClass,
234             HttpServletRequest httpServletRequest,
235             HttpServletResponse httpServletResponse) {
236         if (consumer == null) {
237             throw new IllegalStateException("Consumer is not initialized");
238         }
239         Map<String, Object> headers = enrichParameters(null, httpServletRequest);
240          return consumer.handleTransactionRequest(payload, headers, bundleClass);
241     }
242 
243     protected Map<String, Object> enrichParameters(FhirSearchParameters parameters, HttpServletRequest httpServletRequest) {
244         // Populate some headers.
245         Map<String, Object> enriched = new HashMap<>();
246         enriched.put(Constants.HTTP_URI, httpServletRequest.getRequestURI());
247         enriched.put(Constants.HTTP_URL, httpServletRequest.getRequestURL().toString());
248         enriched.put(Constants.HTTP_METHOD, httpServletRequest.getMethod());
249         enriched.put(Constants.HTTP_QUERY, httpServletRequest.getQueryString());
250         enriched.put(Constants.HTTP_CHARACTER_ENCODING, httpServletRequest.getCharacterEncoding());
251         enriched.put(Constants.HTTP_CONTENT_TYPE, httpServletRequest.getContentType());
252         enriched.put(Constants.HTTP_PROTOCOL_VERSION, httpServletRequest.getProtocol());
253         enriched.put(Constants.HTTP_SCHEME, httpServletRequest.getScheme());
254         enriched.put(Constants.HTTP_CLIENT_IP_ADDRESS, httpServletRequest.getRemoteAddr());
255 
256         Map<String, List<String>> headers = extractHttpHeaders(httpServletRequest);
257         enriched.put(Constants.HTTP_HEADERS, headers);
258 
259         String cipherSuite = (String) httpServletRequest.getAttribute("javax.servlet.request.cipher_suite");
260         if (cipherSuite != null) {
261             enriched.put(Constants.HTTP_X509_CERTIFICATES, httpServletRequest.getAttribute(X509Certificate.class.getName()));
262         }
263 
264         if (parameters != null) {
265             enriched.put(Constants.FHIR_REQUEST_PARAMETERS, parameters);
266         }
267         return enriched;
268     }
269 
270     /**
271      * @param httpServletRequest HTTP servlet request.
272      * @return A map mapping header names to list of header values.
273      */
274     private static Map<String, List<String>> extractHttpHeaders(HttpServletRequest httpServletRequest) {
275         Map<String, List<String>> result = new HashMap<>();
276         Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
277         if (headerNames != null) {
278             while (headerNames.hasMoreElements()) {
279                 String name = headerNames.nextElement();
280                 Enumeration<String> headers = httpServletRequest.getHeaders(name);
281                 if (headers != null) {
282                     List<String> list = new ArrayList<>();
283                     while (headers.hasMoreElements()) {
284                         list.add(headers.nextElement());
285                     }
286                     if (!list.isEmpty()) {
287                         result.put(name, list);
288                     }
289                 }
290             }
291         }
292         return result;
293     }
294 
295     /**
296      * @return the configured consumer
297      */
298     public RequestConsumer getConsumer() {
299         return consumer;
300     }
301 
302     // Ensure this is only used once!
303     public void setConsumer(RequestConsumer consumer) {
304         if (this.consumer != null) {
305             throw new IllegalStateException("This provider is already used by a different consumer: " + consumer);
306         }
307         this.consumer = consumer;
308         LOG.info("Connected consumer {} to provider {}", consumer, this);
309     }
310 
311     public void unsetConsumer(RequestConsumer consumer) {
312         if (this.consumer == consumer) {
313             this.consumer = null;
314             LOG.info("Disconnected consumer {} from provider {}", consumer, this);
315         }
316     }
317 
318 }