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.mllp.core;
17  
18  import lombok.AccessLevel;
19  import lombok.Getter;
20  import org.apache.camel.*;
21  import org.apache.camel.api.management.ManagedAttribute;
22  import org.apache.camel.api.management.ManagedResource;
23  import org.apache.camel.component.mina2.Mina2Configuration;
24  import org.apache.camel.component.mina2.Mina2Consumer;
25  import org.apache.camel.component.mina2.Mina2Endpoint;
26  import org.apache.camel.component.mina2.Mina2Producer;
27  import org.apache.camel.impl.DefaultEndpoint;
28  import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
29  import org.apache.mina.core.filterchain.IoFilter;
30  import org.apache.mina.core.session.IoSession;
31  import org.openehealth.ipf.commons.audit.model.AuditMessage;
32  import org.openehealth.ipf.commons.ihe.core.ClientAuthType;
33  import org.openehealth.ipf.commons.ihe.hl7v2.Hl7v2InteractionId;
34  import org.openehealth.ipf.commons.ihe.hl7v2.Hl7v2TransactionConfiguration;
35  import org.openehealth.ipf.commons.ihe.hl7v2.NakFactory;
36  import org.openehealth.ipf.commons.ihe.hl7v2.audit.MllpAuditDataset;
37  import org.openehealth.ipf.commons.ihe.hl7v2.audit.MllpAuditUtils;
38  import org.openehealth.ipf.platform.camel.ihe.core.InterceptableEndpoint;
39  import org.openehealth.ipf.platform.camel.ihe.core.InterceptorFactory;
40  import org.openehealth.ipf.platform.camel.ihe.hl7v2.HL7v2Endpoint;
41  import org.openehealth.ipf.platform.camel.ihe.mllp.core.intercept.consumer.ConsumerDispatchingInterceptor;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  import javax.net.ssl.SSLContext;
46  import java.util.List;
47  import java.util.Map;
48  
49  import static java.util.Objects.requireNonNull;
50  
51  /**
52   * A wrapper for standard camel-mina endpoint
53   * which provides support for IHE PIX/PDQ-related extensions.
54   *
55   * @author Dmytro Rud
56   */
57  @ManagedResource(description = "Managed IPF MLLP ITI Endpoint")
58  public abstract class MllpEndpoint<
59          ConfigType extends MllpEndpointConfiguration,
60          AuditDatasetType extends MllpAuditDataset,
61          ComponentType extends MllpComponent<ConfigType, AuditDatasetType>>
62          extends DefaultEndpoint
63          implements InterceptableEndpoint<ConfigType, ComponentType>, HL7v2Endpoint<AuditDatasetType> {
64  
65      private static final Logger LOG = LoggerFactory.getLogger(MllpEndpoint.class);
66  
67      @Getter(AccessLevel.PROTECTED)
68      private final ConfigType config;
69      @Getter(AccessLevel.PROTECTED)
70      private final ComponentType mllpComponent;
71      @Getter(AccessLevel.PROTECTED)
72      private final Mina2Endpoint wrappedEndpoint;
73  
74      /**
75       * Constructor.
76       *
77       * @param mllpComponent   MLLP Component instance which is creating this endpoint.
78       * @param wrappedEndpoint The original camel-mina endpoint instance.
79       * @param config          Configuration parameters.
80       */
81      public MllpEndpoint(
82              ComponentType mllpComponent,
83              Mina2Endpoint wrappedEndpoint,
84              ConfigType config) {
85          super(wrappedEndpoint.getEndpointUri(), mllpComponent);
86          this.mllpComponent = requireNonNull(mllpComponent);
87          this.wrappedEndpoint = requireNonNull(wrappedEndpoint);
88          this.config = requireNonNull(config);
89      }
90  
91      @Override
92      public ComponentType getInterceptableComponent() {
93          return mllpComponent;
94      }
95  
96      @Override
97      public ConfigType getInterceptableConfiguration() {
98          return config;
99      }
100 
101     /**
102      * Returns the original camel-mina2 producer which will be wrapped
103      * into a set of PIX/PDQ-specific interceptors in {@link #createProducer()}.
104      */
105     @Override
106     public Producer doCreateProducer() throws Exception {
107         Mina2Producer producer = (Mina2Producer) wrappedEndpoint.createProducer();
108         if (config.getSslContext() != null) {
109             DefaultIoFilterChainBuilder filterChain = producer.getFilterChain();
110             if (!filterChain.contains("ssl")) {
111                 HandshakeCallbackSSLFilter filter = new HandshakeCallbackSSLFilter(config.getSslContext());
112                 filter.setUseClientMode(true);
113                 filter.setHandshakeExceptionCallback(new HandshakeFailureCallback());
114                 filter.setEnabledProtocols(config.getSslProtocols());
115                 filter.setEnabledCipherSuites(config.getSslCiphers());
116                 filterChain.addFirst("ssl", filter);
117             }
118         }
119         return new MllpProducer(producer);
120     }
121 
122     /**
123      * Returns the original starting point of the camel-mina2 route which will be wrapped
124      * into a set of PIX/PDQ-specific interceptors in {@link #createConsumer(Processor)}.
125      *
126      * @param processor The original consumer processor.
127      */
128     @Override
129     public Consumer doCreateConsumer(Processor processor) throws Exception {
130         Mina2Consumer consumer = (Mina2Consumer) wrappedEndpoint.createConsumer(processor);
131         if (config.getSslContext() != null) {
132             DefaultIoFilterChainBuilder filterChain = consumer.getAcceptor().getFilterChain();
133             if (!filterChain.contains("ssl")) {
134                 HandshakeCallbackSSLFilter filter = new HandshakeCallbackSSLFilter(config.getSslContext());
135                 filter.setNeedClientAuth(config.getClientAuthType() == ClientAuthType.MUST);
136                 filter.setWantClientAuth(config.getClientAuthType() == ClientAuthType.WANT);
137                 filter.setHandshakeExceptionCallback(new HandshakeFailureCallback());
138                 filter.setEnabledProtocols(config.getSslProtocols());
139                 filter.setEnabledCipherSuites(config.getSslCiphers());
140                 filterChain.addFirst("ssl", filter);
141             }
142         }
143         consumer.getAcceptor().getFilterChain().addLast("mllp.exception.filter", new MllpExceptionIoFilter(consumer));
144         return new MllpConsumer(consumer);
145     }
146 
147 
148     private class HandshakeFailureCallback implements HandshakeCallbackSSLFilter.Callback {
149 
150         @Override
151         public void run(IoSession session, String message) {
152             if (config.isAudit()) {
153                 String hostAddress = session.getRemoteAddress().toString();
154                 AuditMessage auditMessage = MllpAuditUtils.auditAuthenticationNodeFailure(
155                         config.getAuditContext(), message, hostAddress);
156                 config.getAuditContext().audit(auditMessage);
157             }
158         }
159     }
160 
161 // ----- getters -----
162 
163     /**
164      * Returns transaction configuration.
165      */
166     @Override
167     public Hl7v2TransactionConfiguration<AuditDatasetType> getHl7v2TransactionConfiguration() {
168         return mllpComponent.getHl7v2TransactionConfiguration();
169     }
170 
171     /**
172      * Returns transaction-specific ACK and NAK factory.
173      */
174     @Override
175     public NakFactory<AuditDatasetType> getNakFactory() {
176         return mllpComponent.getNakFactory();
177     }
178 
179     @Override
180     public Hl7v2InteractionId<AuditDatasetType> getInteractionId() {
181         return mllpComponent.getInteractionId();
182     }
183 
184     /**
185      * Returns <code>true</code> if this endpoint supports segment fragmentation.
186      */
187     @ManagedAttribute(description = "Support Segment Fragmentation Enabled")
188     public boolean isSupportSegmentFragmentation() {
189         return config.isSupportSegmentFragmentation();
190     }
191 
192     /**
193      * Returns threshold for segment fragmentation.
194      */
195     @ManagedAttribute(description = "Segment Fragmentation Threshold")
196     public int getSegmentFragmentationThreshold() {
197         return config.getSegmentFragmentationThreshold();
198     }
199 
200     /**
201      * @return the sslContext
202      */
203     public SSLContext getSslContext() {
204         return config.getSslContext();
205     }
206 
207     /**
208      * @return the sslProtocols
209      */
210     @ManagedAttribute(description = "Defined SSL Protocols")
211     public String[] getSslProtocols() {
212         return config.getSslProtocols();
213     }
214 
215     /**
216      * @return the sslCiphers
217      */
218     @ManagedAttribute(description = "Defined SSL Ciphers")
219     public String[] getSslCiphers() {
220         return config.getSslCiphers();
221     }
222 
223     @ManagedAttribute(description = "Component Type Name")
224     public String getComponentType() {
225         return getComponent().getClass().getName();
226     }
227 
228     @ManagedAttribute(description = "Mina Host")
229     public String getHost() {
230         return getConfiguration().getHost();
231     }
232 
233     @ManagedAttribute(description = "Mina Port")
234     public int getPort() {
235         return getConfiguration().getPort();
236     }
237 
238     @ManagedAttribute(description = "Mina Character Set")
239     public String getCharsetName() {
240         return getConfiguration().getCharsetName();
241     }
242 
243     @ManagedAttribute(description = "Mina Timeout")
244     public long getTimeout() {
245         return getConfiguration().getTimeout();
246     }
247 
248     @ManagedAttribute(description = "Mina Filters")
249     public String[] getIoFilters() {
250         List<IoFilter> filters = getConfiguration().getFilters();
251         return toStringArray(filters);
252     }
253 
254     @ManagedAttribute(description = "SSL Secure Enabled")
255     public boolean isSslSecure() {
256         return getSslContext() != null;
257     }
258 
259     /**
260      * @return the client authentication type.
261      */
262     public ClientAuthType getClientAuthType() {
263         return config.getClientAuthType();
264     }
265 
266     @ManagedAttribute(description = "Client Authentication Type")
267     public String getClientAuthTypeClass() {
268         return getClientAuthType().toString();
269     }
270 
271     /**
272      * @return the customInterceptorFactories
273      */
274     public List<InterceptorFactory> getCustomInterceptorFactories() {
275         return config.getCustomInterceptorFactories();
276     }
277 
278     public ConsumerDispatchingInterceptor getDispatcher() {
279         return config.getDispatcher();
280     }
281 
282 
283     /**
284      * @return the customInterceptors as array of string names
285      */
286     @ManagedAttribute(description = "Custom Interceptor Factories")
287     public String[] getCustomInterceptorFactoryList() {
288         return toStringArray(getCustomInterceptorFactories());
289     }
290 
291     private String[] toStringArray(List<?> list) {
292         final String[] result = new String[list.size()];
293         for (int i = 0; i < list.size(); i++) {
294             result[i] = list.get(i).getClass().getCanonicalName();
295         }
296         return result;
297     }
298 
299 
300     /* ----- dumb delegation, nothing interesting below ----- */
301     @SuppressWarnings({"unchecked", "rawtypes"})
302     @Override
303     public void configureProperties(Map options) {
304         wrappedEndpoint.configureProperties(options);
305     }
306 
307     @Override
308     public Exchange createExchange() {
309         return wrappedEndpoint.createExchange();
310     }
311 
312     @Override
313     public Exchange createExchange(Exchange exchange) {
314         return wrappedEndpoint.createExchange(exchange);
315     }
316 
317     @Override
318     public Exchange createExchange(ExchangePattern pattern) {
319         return wrappedEndpoint.createExchange(pattern);
320     }
321 
322     @Override
323     public PollingConsumer createPollingConsumer() throws Exception {
324         return wrappedEndpoint.createPollingConsumer();
325     }
326 
327     @Override
328     public boolean equals(Object object) {
329         if (object instanceof MllpEndpoint) {
330             MllpEndpoint<?, ?, ?> that = (MllpEndpoint<?, ?, ?>) object;
331             return wrappedEndpoint.equals(that.getWrappedEndpoint());
332         }
333         return false;
334     }
335 
336     @Override
337     public CamelContext getCamelContext() {
338         return wrappedEndpoint.getCamelContext();
339     }
340 
341     @Override
342     public Component getComponent() {
343         return wrappedEndpoint.getComponent();
344     }
345 
346     public Mina2Configuration getConfiguration() {
347         return wrappedEndpoint.getConfiguration();
348     }
349 
350     @Override
351     public String getEndpointKey() {
352         return wrappedEndpoint.getEndpointKey();
353     }
354 
355     @Override
356     public String getEndpointUri() {
357         return wrappedEndpoint.getEndpointUri();
358     }
359 
360     @Override
361     public ExchangePattern getExchangePattern() {
362         return wrappedEndpoint.getExchangePattern();
363     }
364 
365     @Override
366     public int hashCode() {
367         return wrappedEndpoint.hashCode();
368     }
369 
370     @Override
371     public boolean isLenientProperties() {
372         return wrappedEndpoint.isLenientProperties();
373     }
374 
375     @Override
376     public boolean isSingleton() {
377         return wrappedEndpoint.isSingleton();
378     }
379 
380     @Override
381     public void setCamelContext(CamelContext camelContext) {
382         wrappedEndpoint.setCamelContext(camelContext);
383     }
384 
385     @Override
386     public void setEndpointUriIfNotSpecified(String value) {
387         wrappedEndpoint.setEndpointUriIfNotSpecified(value);
388     }
389 
390     @Override
391     public void setExchangePattern(ExchangePattern exchangePattern) {
392         wrappedEndpoint.setExchangePattern(exchangePattern);
393     }
394 
395     @Override
396     public String toString() {
397         return wrappedEndpoint.toString();
398     }
399 
400 }