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.commons.ihe.ws;
17  
18  import org.apache.cxf.endpoint.Client;
19  import org.apache.cxf.feature.AbstractFeature;
20  import org.apache.cxf.frontend.ClientProxy;
21  import org.apache.cxf.interceptor.InterceptorProvider;
22  import org.apache.cxf.transport.http.HTTPConduit;
23  import org.apache.cxf.ws.addressing.MAPAggregator;
24  import org.apache.cxf.ws.addressing.soap.MAPCodec;
25  import org.openehealth.ipf.commons.audit.AuditContext;
26  import org.openehealth.ipf.commons.ihe.core.atna.AuditStrategy;
27  import org.openehealth.ipf.commons.ihe.ws.correlation.AsynchronyCorrelator;
28  import org.openehealth.ipf.commons.ihe.ws.cxf.Cxf3791WorkaroundInterceptor;
29  import org.openehealth.ipf.commons.ihe.ws.cxf.FixContentTypeOutInterceptor;
30  import org.openehealth.ipf.commons.ihe.ws.cxf.MustUnderstandDecoratorInterceptor;
31  import org.openehealth.ipf.commons.ihe.ws.cxf.ProvidedAttachmentOutInterceptor;
32  import org.openehealth.ipf.commons.ihe.ws.cxf.audit.WsAuditDataset;
33  import org.openehealth.ipf.commons.ihe.ws.cxf.payload.OutPayloadExtractorInterceptor;
34  import org.openehealth.ipf.commons.ihe.ws.cxf.payload.OutStreamSubstituteInterceptor;
35  import org.openehealth.ipf.commons.ihe.ws.utils.SoapUtils;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  import javax.xml.namespace.QName;
40  import javax.xml.ws.Binding;
41  import javax.xml.ws.BindingProvider;
42  import javax.xml.ws.Service;
43  import javax.xml.ws.soap.SOAPBinding;
44  import java.net.URL;
45  import java.util.List;
46  import java.util.Map;
47  import java.util.function.Supplier;
48  
49  import static java.util.Objects.requireNonNull;
50  
51  /**
52   * Factory for ITI Web Service stubs.
53   *
54   * @author Jens Riemschneider
55   */
56  public class JaxWsClientFactory<AuditDatasetType extends WsAuditDataset> {
57      private static final Logger LOG = LoggerFactory.getLogger(JaxWsClientFactory.class);
58  
59      protected final ThreadLocal<Object> threadLocalPort = new ThreadLocal<>();
60      protected final WsTransactionConfiguration<AuditDatasetType> wsTransactionConfiguration;
61      protected final String serviceUrl;
62      protected final InterceptorProvider customInterceptors;
63      protected final List<AbstractFeature> features;
64      protected final Map<String, Object> properties;
65      protected final AuditStrategy<AuditDatasetType> auditStrategy;
66      protected final AuditContext auditContext;
67      protected final AsynchronyCorrelator<AuditDatasetType> correlator;
68  
69      /**
70       * Constructs the factory.
71       *
72       * @param wsTransactionConfiguration the info about the Web Service.
73       * @param serviceUrl                 the URL of the Web Service.
74       * @param auditStrategy              client-side ATNA audit strategy.
75       * @param customInterceptors         user-defined custom CXF interceptors.
76       * @param correlator                 optional asynchrony correlator.
77       */
78      public JaxWsClientFactory(
79              WsTransactionConfiguration<AuditDatasetType> wsTransactionConfiguration,
80              String serviceUrl,
81              AuditStrategy<AuditDatasetType> auditStrategy,
82              AuditContext auditContext,
83              InterceptorProvider customInterceptors,
84              List<AbstractFeature> features,
85              Map<String, Object> properties,
86              AsynchronyCorrelator<AuditDatasetType> correlator) {
87          requireNonNull(wsTransactionConfiguration, "wsTransactionConfiguration");
88          this.wsTransactionConfiguration = wsTransactionConfiguration;
89          this.serviceUrl = serviceUrl;
90          this.auditStrategy = auditStrategy;
91          this.auditContext = auditContext;
92          this.customInterceptors = customInterceptors;
93          this.features = features;
94          this.properties = properties;
95          this.correlator = correlator;
96      }
97  
98      /**
99       * Returns a client stub for the web-service.
100      *
101      * @param securityInformationSupplier Conduit-related security information or null if no security shall be set
102      * @return the client stub
103      */
104     public synchronized Object getClient(Supplier<WsSecurityInformation> securityInformationSupplier) {
105         if (threadLocalPort.get() == null) {
106             URL wsdlURL = getClass().getClassLoader().getResource(wsTransactionConfiguration.getWsdlLocation());
107             Service service = Service.create(wsdlURL, wsTransactionConfiguration.getServiceName());
108             Object port = service.getPort(wsTransactionConfiguration.getSei());
109             Client client = ClientProxy.getClient(port);
110             configureBinding(port);
111             configureInterceptors(client);
112             configureProperties(client);
113             WsSecurityInformation securityInformation = securityInformationSupplier.get();
114             if (securityInformation != null) {
115                 securityInformation.configureHttpConduit((HTTPConduit) client.getConduit());
116             }
117             threadLocalPort.set(port);
118             LOG.debug("Created client adapter for: {}", wsTransactionConfiguration.getServiceName());
119         }
120         return threadLocalPort.get();
121     }
122 
123     public synchronized Object getClient() {
124         return getClient(() -> null);
125     }
126 
127     /**
128      * @return the service info of this factory.
129      */
130     public WsTransactionConfiguration<AuditDatasetType> getWsTransactionConfiguration() {
131         return wsTransactionConfiguration;
132     }
133 
134 
135     /**
136      * Configures SOAP interceptors for the given client.
137      */
138     protected void configureInterceptors(Client client) {
139         client.getInInterceptors().add(new Cxf3791WorkaroundInterceptor());
140 
141         // WS-Addressing-related interceptors
142         if (wsTransactionConfiguration.isAddressing()) {
143             MustUnderstandDecoratorInterceptor interceptor = new MustUnderstandDecoratorInterceptor();
144             for (String nsUri : SoapUtils.WS_ADDRESSING_NS_URIS) {
145                 interceptor.addHeader(new QName(nsUri, "Action"));
146             }
147 
148             client.getOutInterceptors().add(interceptor);
149 
150             MAPCodec mapCodec = new MAPCodec();
151             MAPAggregator mapAggregator = new MAPAggregator();
152             client.getInInterceptors().add(mapCodec);
153             client.getInInterceptors().add(mapAggregator);
154             client.getInFaultInterceptors().add(mapCodec);
155             client.getInFaultInterceptors().add(mapAggregator);
156             client.getOutInterceptors().add(mapCodec);
157             client.getOutInterceptors().add(mapAggregator);
158             client.getOutFaultInterceptors().add(mapCodec);
159             client.getOutFaultInterceptors().add(mapAggregator);
160         }
161 
162         if (wsTransactionConfiguration.isSwaOutSupport()) {
163             client.getOutInterceptors().add(new ProvidedAttachmentOutInterceptor());
164             client.getOutInterceptors().add(new FixContentTypeOutInterceptor());
165         }
166 
167         if (features != null) {
168             for (AbstractFeature feature : features) {
169                 client.getEndpoint().getActiveFeatures().add(feature);
170                 feature.initialize(client, client.getBus());
171             }
172         }
173 
174         InterceptorUtils.copyInterceptorsFromProvider(customInterceptors, client);
175 
176     }
177 
178     protected void configureProperties(Client client) {
179         if (properties != null) {
180             client.getEndpoint().putAll(properties);
181         }
182     }
183 
184     /**
185      * Helper method for installing of payload-collecting SOAP interceptors
186      * for the given Client.
187      */
188     static protected void installPayloadInterceptors(Client client) {
189         client.getOutInterceptors().add(new OutStreamSubstituteInterceptor());
190         client.getOutInterceptors().add(new OutPayloadExtractorInterceptor());
191     }
192 
193 
194     /**
195      * Configures SOAP binding of the given SOAP port.
196      */
197     private void configureBinding(Object port) {
198         BindingProvider bindingProvider = (BindingProvider) port;
199 
200         Map<String, Object> reqContext = bindingProvider.getRequestContext();
201         reqContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serviceUrl);
202 
203         Binding binding = bindingProvider.getBinding();
204         SOAPBinding soapBinding = (SOAPBinding) binding;
205         soapBinding.setMTOMEnabled(wsTransactionConfiguration.isMtom());
206     }
207 }