View Javadoc
1   /*
2    * Copyright 2009 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  package org.openehealth.ipf.platform.camel.ihe.ws;
17  
18  import org.apache.camel.Consumer;
19  import org.apache.camel.Processor;
20  import org.apache.camel.Producer;
21  import org.apache.camel.api.management.ManagedAttribute;
22  import org.apache.camel.api.management.ManagedResource;
23  import org.apache.camel.impl.DefaultEndpoint;
24  import org.apache.camel.util.jsse.SSLContextParameters;
25  import org.apache.cxf.common.i18n.Exception;
26  import org.apache.cxf.endpoint.Server;
27  import org.apache.cxf.feature.AbstractFeature;
28  import org.apache.cxf.frontend.ServerFactoryBean;
29  import org.apache.cxf.headers.Header;
30  import org.apache.cxf.interceptor.InterceptorProvider;
31  import org.openehealth.ipf.commons.audit.AuditContext;
32  import org.openehealth.ipf.commons.core.URN;
33  import org.openehealth.ipf.commons.ihe.core.atna.AuditStrategy;
34  import org.openehealth.ipf.commons.ihe.ws.JaxWsClientFactory;
35  import org.openehealth.ipf.commons.ihe.ws.JaxWsServiceFactory;
36  import org.openehealth.ipf.commons.ihe.ws.WsInteractionId;
37  import org.openehealth.ipf.commons.ihe.ws.WsTransactionConfiguration;
38  import org.openehealth.ipf.commons.ihe.ws.correlation.AsynchronyCorrelator;
39  import org.openehealth.ipf.commons.ihe.ws.cxf.WsRejectionHandlingStrategy;
40  import org.openehealth.ipf.commons.ihe.ws.cxf.audit.WsAuditDataset;
41  import org.openehealth.ipf.platform.camel.ihe.atna.AuditableEndpoint;
42  
43  import javax.net.ssl.HostnameVerifier;
44  import javax.xml.namespace.QName;
45  import java.util.List;
46  import java.util.Map;
47  
48  /**
49   * Camel endpoint used to create producers and consumers based on webservice calls.
50   *
51   * @author Jens Riemschneider
52   * @author Dmytro Rud
53   */
54  @ManagedResource(description = "Managed IPF eHealth Web Service Endpoint")
55  public abstract class AbstractWsEndpoint<
56          AuditDatasetType extends WsAuditDataset,
57          ConfigType extends WsTransactionConfiguration<AuditDatasetType>>
58          extends DefaultEndpoint implements AuditableEndpoint<AuditDatasetType> {
59  
60      private static final String ENDPOINT_PROTOCOL = "http://";
61      private static final String ENDPOINT_PROTOCOL_SECURE = "https://";
62  
63      /**
64       * Name of incoming Camel header where the user should store the URL
65       * of asynchronous response endpoint (WS-Addressing header "ReplyTo").
66       */
67      public static final String WSA_REPLYTO_HEADER_NAME =
68              AbstractWsEndpoint.class.getName() + ".REPLY_TO";
69  
70      /**
71       * Name of Camel message header where the user should store
72       * the optional correlation key.
73       */
74      public static final String CORRELATION_KEY_HEADER_NAME =
75              AbstractWsEndpoint.class.getName() + ".CORRELATION_KEY";
76  
77      /**
78       * Name of Camel message header where incoming HTTP headers
79       * will be stored as a <code>Map&lt;String, String&gt;</code>.
80       */
81      public static final String INCOMING_HTTP_HEADERS =
82              AbstractWsEndpoint.class.getName() + ".INCOMING_HTTP_HEADERS";
83  
84      /**
85       * Name of Camel message header from where additional user-defined HTTP
86       * headers will be taken as a <code>Map&lt;String, String&gt;</code>.
87       */
88      public static final String OUTGOING_HTTP_HEADERS =
89              AbstractWsEndpoint.class.getName() + ".OUTGOING_HTTP_HEADERS";
90  
91      /**
92       * Name of Camel message header where incoming SOAP headers
93       * will be stored as a <code>Map&lt;{@link QName}, {@link Header}&gt;</code>.
94       */
95      public static final String INCOMING_SOAP_HEADERS =
96              AbstractWsEndpoint.class.getName() + ".INCOMING_SOAP_HEADERS";
97  
98      /**
99       * Name of Camel message header from where additional user-defined HTTP
100      * headers will be taken as a <code>List&lt;{@link Header}&gt;</code>
101      * or <code>Map&lt;{@link QName}, {@link Header}&gt;</code>.
102      */
103     public static final String OUTGOING_SOAP_HEADERS =
104             AbstractWsEndpoint.class.getName() + ".OUTGOING_SOAP_HEADERS";
105 
106     private final String address;
107 
108     private String serviceAddress;
109     private String serviceUrl;
110 
111     private AuditContext auditContext;
112     private AsynchronyCorrelator<AuditDatasetType> correlator = null;
113     private InterceptorProvider customInterceptors;
114     private String homeCommunityId = null;
115     private WsRejectionHandlingStrategy rejectionHandlingStrategy = null;
116     private List<AbstractFeature> features;
117     private List<String> schemaLocations;
118     private Class<? extends AbstractWebService> serviceClass;
119     private Map<String, Object> properties;
120 
121     private boolean secure;
122     private SSLContextParameters sslContextParameters;
123     private HostnameVerifier hostnameVerifier;
124     private String username;
125     private String password;
126 
127 
128     protected AbstractWsEndpoint(
129             String endpointUri,
130             String address,
131             AbstractWsComponent<AuditDatasetType, ConfigType, ? extends WsInteractionId<ConfigType>> component,
132             Map<String, Object> parameters,
133             Class<? extends AbstractWebService> serviceClass) {
134         this(endpointUri, address, component,
135                 component.getAuditContext(parameters),
136                 component.getCustomInterceptors(parameters),
137                 component.getFeatures(parameters),
138                 component.getSchemaLocations(parameters),
139                 component.getProperties(parameters),
140                 serviceClass);
141     }
142 
143     /**
144      * Constructs the endpoint.
145      *
146      * @param endpointUri        the URI of the endpoint.
147      * @param address            the endpoint address from the URI.
148      * @param component          the component creating this endpoint.
149      * @param auditContext       the audit context
150      * @param customInterceptors user-defined set of additional CXF interceptors.
151      * @param features           user-defined list of CXF features.
152      */
153     protected AbstractWsEndpoint(
154             String endpointUri,
155             String address,
156             AbstractWsComponent<AuditDatasetType, ConfigType, ? extends WsInteractionId<ConfigType>> component,
157             AuditContext auditContext,
158             InterceptorProvider customInterceptors,
159             List<AbstractFeature> features,
160             List<String> schemaLocations,
161             Map<String, Object> properties,
162             Class<? extends AbstractWebService> serviceClass) {
163         super(endpointUri, component);
164         this.auditContext = auditContext;
165         this.address = address;
166         this.customInterceptors = customInterceptors;
167         this.features = features;
168         this.schemaLocations = schemaLocations;
169         this.properties = properties;
170         this.serviceClass = serviceClass;
171         configure();
172     }
173 
174     private void configure() {
175         serviceUrl = (isSecure() ? ENDPOINT_PROTOCOL_SECURE : ENDPOINT_PROTOCOL) + address;
176         serviceAddress = "/" + address;
177     }
178 
179     /**
180      * Constructs and returns a transaction-specific service class instance
181      * for the given endpoint.
182      *
183      * @return service class instance for the given endpoint.
184      */
185     public AbstractWebService getServiceInstance() {
186         AbstractWebService service = getCustomServiceInstance(this);
187         if (service == null) {
188             if (serviceClass != null) {
189                 try {
190                     return serviceClass.newInstance();
191                 } catch (InstantiationException | IllegalAccessException e) {
192                     throw new RuntimeException("Could not instantiate service of type " + serviceClass, e);
193                 }
194             } else {
195                 throw new RuntimeException("Could not instantiate service of for endpoint of class " + getClass());
196             }
197         }
198         return service;
199     }
200 
201     /**
202      * Returns a new instance of a service class.
203      * Overwrite this method if a simple call to {@link Class#newInstance()} is not sufficient.
204      *
205      * @param endpoint this endpoint as paramater
206      * @return service class instance
207      */
208     protected AbstractWebService getCustomServiceInstance(AbstractWsEndpoint<AuditDatasetType, ConfigType> endpoint) {
209         return null;
210     }
211 
212     @Override
213     public AuditStrategy<AuditDatasetType> getClientAuditStrategy() {
214         return getComponent().getClientAuditStrategy();
215     }
216 
217     @Override
218     public AuditStrategy<AuditDatasetType> getServerAuditStrategy() {
219         return getComponent().getServerAuditStrategy();
220     }
221 
222     @Override
223     @ManagedAttribute
224     public boolean isSingleton() {
225         return true;
226     }
227 
228     /**
229      * Returns the URL of the service.
230      * <p/>
231      * The URL is derived from the endpoint URI defined in the constructor. If the
232      * URI does not represent a producer, this method throws an exception.
233      *
234      * @return the service URL.
235      */
236     public String getServiceUrl() {
237         return serviceUrl;
238     }
239 
240     /**
241      * Returns the address of the service.
242      * <p/>
243      * The address is derived from the endpoint URI defined in the constructor. If the
244      * URI does not represent a consumer, this method throws an exception.
245      *
246      * @return the service address.
247      */
248     @ManagedAttribute(description = "Service Address")
249     public String getServiceAddress() {
250         return serviceAddress;
251     }
252 
253     /**
254      * @return <code>true</code> if auditing is turned on. <code>true</code> by default.
255      */
256     @Override
257     @ManagedAttribute(description = "Audit Enabled")
258     public boolean isAudit() {
259         return getAuditContext().isAuditEnabled();
260     }
261 
262     /**
263      * @param audit <code>true</code> if auditing shall be turned on.
264      */
265     public void setAudit(boolean audit) {
266         getAuditContext().setAuditEnabled(audit);
267     }
268 
269     @Override
270     public AuditContext getAuditContext() {
271         return auditContext;
272     }
273 
274     public void setAuditContext(AuditContext auditContext) {
275         this.auditContext = auditContext;
276     }
277 
278     /**
279      * @return <code>true</code> if https should be used instead of http. Defaults
280      * to <code>false</code>.
281      */
282     @ManagedAttribute(description = "Security Enabled")
283     public boolean isSecure() {
284         return secure;
285     }
286 
287     /**
288      * @param secure <code>true</code> if https should be used instead of http.
289      */
290     public void setSecure(boolean secure) {
291         this.secure = secure;
292         configure();
293     }
294 
295     public String getUsername() {
296         return username;
297     }
298 
299     @ManagedAttribute(description = "Basic Authentication Username")
300     public void setUsername(String username) {
301         this.username = username;
302     }
303 
304     @ManagedAttribute(description = "Basic Authentication Password")
305     public String getPassword() {
306         return password;
307     }
308 
309     public void setPassword(String password) {
310         this.password = password;
311     }
312 
313     public SSLContextParameters getSslContextParameters() {
314         return sslContextParameters;
315     }
316 
317     public void setSslContextParameters(SSLContextParameters sslContextParameters) {
318         this.sslContextParameters = sslContextParameters;
319         setSecure(secure || (sslContextParameters != null));
320     }
321 
322     public HostnameVerifier getHostnameVerifier() {
323         return hostnameVerifier;
324     }
325 
326     public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
327         this.hostnameVerifier = hostnameVerifier;
328     }
329 
330     /**
331      * Configures the asynchrony correlator for this endpoint.
332      */
333     public void setCorrelator(AsynchronyCorrelator<AuditDatasetType> correlator) {
334         this.correlator = correlator;
335     }
336 
337     /**
338      * Returns the correlator.
339      */
340     public AsynchronyCorrelator<AuditDatasetType> getCorrelator() {
341         return correlator;
342     }
343 
344     /**
345      * Returns custom interceptors configured for this endpoint.
346      */
347     public InterceptorProvider getCustomInterceptors() {
348         return customInterceptors;
349     }
350 
351     /**
352      * @return homeCommunityId of this endpoint.
353      */
354     @ManagedAttribute(description = "HomeCommunityId")
355     public String getHomeCommunityId() {
356         return homeCommunityId;
357     }
358 
359     /**
360      * Configures homeCommunityId for this endpoint.
361      *
362      * @param homeCommunityId homeCommunityId in format "urn:oid:1.2.3.4.5".
363      */
364     public void setHomeCommunityId(String homeCommunityId) {
365         this.homeCommunityId = homeCommunityId;
366     }
367 
368     /**
369      * Configures homeCommunityId for this endpoint.
370      *
371      * @param urn homeCommunityId in format "urn:oid:1.2.3.4.5".
372      */
373     public void setHomeCommunityId(URN urn) {
374         this.homeCommunityId = urn.toString();
375     }
376 
377     /**
378      * @return rejection handling strategy, if any configured.
379      */
380     public WsRejectionHandlingStrategy getRejectionHandlingStrategy() {
381         return rejectionHandlingStrategy;
382     }
383 
384     /**
385      * @param rejectionHandlingStrategy a rejection handling strategy instance.
386      */
387     public void setRejectionHandlingStrategy(WsRejectionHandlingStrategy rejectionHandlingStrategy) {
388         this.rejectionHandlingStrategy = rejectionHandlingStrategy;
389     }
390 
391     /**
392      * @return CXF features configured for this endpoint.
393      */
394     public List<AbstractFeature> getFeatures() {
395         return features;
396     }
397 
398     /**
399      * @return CXF schema locations configured for this endpoint.
400      */
401     public List<String> getSchemaLocations() {
402         return schemaLocations;
403     }
404 
405     /**
406      * @return CXF schema locations configured for this endpoint.
407      */
408     public Map<String, Object> getProperties() {
409         return properties;
410     }
411 
412     @SuppressWarnings("unchecked")
413     @Override
414     public AbstractWsComponent<AuditDatasetType, ConfigType, ? extends WsInteractionId> getComponent() {
415         return (AbstractWsComponent<AuditDatasetType, ConfigType, WsInteractionId<ConfigType>>) super.getComponent();
416     }
417 
418     /**
419      * @return JAX-WS client object factory.
420      */
421     public abstract JaxWsClientFactory<AuditDatasetType> getJaxWsClientFactory();
422 
423     /**
424      * @return JAX-WS service object factory.
425      */
426     public abstract JaxWsServiceFactory<AuditDatasetType> getJaxWsServiceFactory();
427 
428 
429     @Override
430     public Consumer createConsumer(Processor processor) throws Exception {
431         AbstractWebService serviceInstance = getServiceInstance();
432         ServerFactoryBean serverFactory = getJaxWsServiceFactory().createServerFactory(serviceInstance);
433         if (features != null) {
434             serverFactory.getFeatures().addAll(features);
435         }
436         if (schemaLocations != null) {
437             if (serverFactory.getSchemaLocations() == null) {
438                 serverFactory.setSchemaLocations(schemaLocations);
439             } else {
440                 serverFactory.getSchemaLocations().addAll(schemaLocations);
441             }
442         }
443         if (properties != null) {
444             if (serverFactory.getProperties() == null) {
445                 serverFactory.setProperties(properties);
446             } else {
447                 serverFactory.getProperties().putAll(properties);
448             }
449         }
450 
451         Server server = serverFactory.create();
452         AbstractWebService service = (AbstractWebService) serverFactory.getServiceBean();
453         return new DefaultWsConsumer<>(this, processor, service, server);
454     }
455 
456     @Override
457     public Producer createProducer() throws java.lang.Exception {
458         return getProducer(this, getJaxWsClientFactory());
459     }
460 
461     /**
462      * Constructs and returns a transaction-specific Camel producer instance
463      *
464      * @param clientFactory JAX-WS client factory instance.
465      * @return Camel producer instance.
466      * @since 3.1
467      */
468     public abstract AbstractWsProducer<AuditDatasetType, ConfigType, ?, ?> getProducer(AbstractWsEndpoint<AuditDatasetType, ConfigType> endpoint, JaxWsClientFactory<AuditDatasetType> clientFactory);
469 
470 
471     //special managed attributes
472 
473     /**
474      * @return <code>true</code> if WS-Addressing enabled.
475      */
476     @ManagedAttribute(description = "Addressing Enabled")
477     public boolean isAddressing() {
478         return getComponent().getWsTransactionConfiguration().isAddressing();
479     }
480 
481     /**
482      * @return <code>true</code> if MTOM enabled.
483      */
484     @ManagedAttribute(description = "Mtom Enabled")
485     public boolean isMtom() {
486         return getComponent().getWsTransactionConfiguration().isMtom();
487     }
488 
489     /**
490      * @return <code>true</code> if SOAP With Attachments Output enabled.
491      */
492     @ManagedAttribute(description = "SOAP With Attachments Output Enabled")
493     public boolean isSwaOutSupport() {
494         return getComponent().getWsTransactionConfiguration().isSwaOutSupport();
495     }
496 
497 
498 }