1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package ca.uhn.hl7v2.testpanel.model.conn;
27
28 import static org.apache.commons.lang.StringUtils.*;
29
30 import java.awt.EventQueue;
31 import java.beans.PropertyVetoException;
32 import java.io.ByteArrayOutputStream;
33 import java.io.File;
34 import java.io.IOException;
35 import java.net.ServerSocket;
36 import java.net.Socket;
37 import java.net.SocketException;
38 import java.nio.charset.Charset;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.UnrecoverableKeyException;
43 import java.security.cert.Certificate;
44 import java.security.cert.CertificateException;
45 import java.security.cert.X509Certificate;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.Date;
49 import java.util.List;
50 import java.util.UUID;
51
52 import javax.net.ServerSocketFactory;
53 import javax.net.ssl.SSLServerSocketFactory;
54 import javax.net.ssl.SSLSocketFactory;
55 import javax.swing.SwingUtilities;
56 import jakarta.xml.bind.annotation.XmlAccessType;
57 import jakarta.xml.bind.annotation.XmlAccessorType;
58 import jakarta.xml.bind.annotation.XmlAttribute;
59 import jakarta.xml.bind.annotation.XmlType;
60
61 import org.apache.commons.lang.StringUtils;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 import ca.uhn.hl7v2.DefaultHapiContext;
66 import ca.uhn.hl7v2.HapiContext;
67 import ca.uhn.hl7v2.hoh.auth.SingleCredentialClientCallback;
68 import ca.uhn.hl7v2.hoh.auth.SingleCredentialServerCallback;
69 import ca.uhn.hl7v2.hoh.llp.Hl7OverHttpLowerLayerProtocol;
70 import ca.uhn.hl7v2.hoh.sign.BouncyCastleCmsMessageSigner;
71 import ca.uhn.hl7v2.hoh.sockets.CustomCertificateTlsSocketFactory;
72 import ca.uhn.hl7v2.hoh.sockets.TlsSocketFactory;
73 import ca.uhn.hl7v2.hoh.util.HapiSocketTlsFactoryWrapper;
74 import ca.uhn.hl7v2.hoh.util.KeystoreUtils;
75 import ca.uhn.hl7v2.hoh.util.ServerRoleEnum;
76 import ca.uhn.hl7v2.llp.ExtendedMinLowerLayerProtocol;
77 import ca.uhn.hl7v2.llp.LLPException;
78 import ca.uhn.hl7v2.llp.LowerLayerProtocol;
79 import ca.uhn.hl7v2.llp.MinLowerLayerProtocol;
80 import ca.uhn.hl7v2.parser.DefaultXMLParser;
81 import ca.uhn.hl7v2.parser.Parser;
82 import ca.uhn.hl7v2.parser.PipeParser;
83 import ca.uhn.hl7v2.testpanel.api.WorkingStatusBean;
84 import ca.uhn.hl7v2.testpanel.controller.Controller;
85 import ca.uhn.hl7v2.testpanel.model.AbstractModelClass;
86 import ca.uhn.hl7v2.testpanel.model.ActivityBase;
87 import ca.uhn.hl7v2.testpanel.model.ActivityIncomingBytes;
88 import ca.uhn.hl7v2.testpanel.model.ActivityInfo;
89 import ca.uhn.hl7v2.testpanel.model.ActivityOutgoingBytes;
90 import ca.uhn.hl7v2.testpanel.ui.IDestroyable;
91 import ca.uhn.hl7v2.testpanel.util.CollectionUtils;
92 import ca.uhn.hl7v2.testpanel.util.llp.ByteCapturingMinLowerLayerProtocolWrapper;
93 import ca.uhn.hl7v2.testpanel.xsd.Hl7V2EncodingTypeEnum;
94 import ca.uhn.hl7v2.util.SocketFactory;
95 import ca.uhn.hl7v2.validation.builder.support.NoValidationBuilder;
96 import ca.uhn.hl7v2.validation.impl.ValidationContextImpl;
97
98 @XmlAccessorType(XmlAccessType.FIELD)
99 @XmlType(name = "AbstractConnection")
100 public abstract class AbstractConnection extends AbstractModelClass implements IDestroyable {
101 public static final String HOH_SIGNATURE_KEYSTORE_STATUS = AbstractConnection.class.getName() + "_HOH_SIGNATURE_KEYSTORE_STATUS";
102 public static final String HOH_SIGNER_AVAILABLE_ALIASES_PROPERTY = AbstractConnection.class.getName() + "_HOH_SIGNER_AVAILABLE_ALIASES";
103 public static final String NAME_PROPERTY = AbstractConnection.class.getName() + "_NAME";
104 public static final String NEW_MESSAGES_PROPERTY = InboundConnection.class.getName() + "_NEW_MESSAGES_PROP";
105 private static final Logger ourLog = LoggerFactory.getLogger(AbstractConnection.class);
106 public static final String PERSISTENT_PROPERTY = AbstractConnection.class.getName() + "_PERSISTENT";
107 public static final String RECENT_ACTIVITY_PROPERTY = AbstractConnection.class.getName() + "_RECENT_ACTIVITY";
108 public static final String STATUS_LINE_PROPERTY = AbstractConnection.class.getName() + "_STATUS_LINE";
109 public static final String STATUS_PROPERTY = AbstractConnection.class.getName() + "_STATUS";
110 public static final String TLS_KEYSTORE_STATUS = AbstractConnection.class.getName() + "_TLS_KEYSTORE_STATUS";
111 public static final String TRANSPORT_PROPERTY = AbstractConnection.class.getName() + "_TRANSPORT";
112
113 @XmlAttribute(required = true)
114 private boolean myCaptureBytes;
115
116 @XmlAttribute(required = true)
117 private String myCharSet;
118
119 private transient Controller myController;
120
121 @XmlAttribute(required = true)
122 private boolean myDetectCharSetInMessage;
123
124 @XmlAttribute(required = true)
125 private boolean myDualPort;
126
127 @XmlAttribute(required = true)
128 private Hl7V2EncodingTypeEnum myEncoding;
129
130 @XmlAttribute(name = "hoh_authentication")
131 private boolean myHohAuthenticationEnabled;
132
133 @XmlAttribute(name = "hoh_auth_pass")
134 private String myHohAuthenticationPassword;
135
136 @XmlAttribute(name = "hoh_auth_user")
137 private String myHohAuthenticationUsername;
138
139 private boolean myHohSecurityKeystoreCheckIsScheduled;
140
141 private transient List<String> myHohSignatureAvailableAliases;
142
143 @XmlAttribute(name = "hoh_signature_enabled")
144 private boolean myHohSignatureEnabled;
145 @XmlAttribute(name = "hoh_signature_key")
146 private String myHohSignatureKey;
147
148 @XmlAttribute(name = "hoh_signature_key_password")
149 private String myHohSignatureKeyPassword;
150
151 @XmlAttribute(name = "hoh_signature_keystore")
152 private String myHohSignatureKeystore;
153 private transient KeyStore myHohSignatureKeystore_;
154 private boolean myHohSignatureKeystoreCheckIsScheduled;
155 @XmlAttribute(name = "hoh_signature_keystore_password")
156 private String myHohSignatureKeystorePassword;
157 private WorkingStatusBean myHohSignatureKeystoreStatus;
158 @XmlAttribute(required = true)
159 private String myHost;
160 @XmlAttribute(name = "httpUriPath", required = false)
161 private String myHttpUriPath;
162 @XmlAttribute(required = true)
163 private String myId;
164 @XmlAttribute(required = true)
165 private int myIncomingOrSinglePort;
166 @XmlAttribute(required = true)
167 private String myName;
168
169 @XmlAttribute(required = true)
170 private boolean myNameIsExplicitlySet;
171
172 private transient int myNewMessages;
173
174 @XmlAttribute(required = true)
175 private int myOutgoingPort;
176
177 @XmlAttribute(required = true)
178 private boolean myPersistent;
179
180 private transient ByteArrayOutputStream myReaderCapture = new ByteArrayOutputStream();
181
182 private transient List<ActivityBase> myRecentActivity = new ArrayList<ActivityBase>();
183
184 private transient StatusEnum myStatus = StatusEnum.STOPPED;
185
186 private transient String myStatusLine;
187
188 private transient StreamWatcherThread myStreamWatcherThread;
189
190 @XmlAttribute(required = true)
191 private boolean myTls;
192
193 private transient KeyStore myTlsKeystore;
194
195 @XmlAttribute(required = false)
196 private String myTlsKeystoreLocation;
197
198 @XmlAttribute(required = false)
199 private String myTlsKeystorePassword;
200 private transient WorkingStatusBean myTlsKeystoreStatus;
201 @XmlAttribute(name = "transport", required = true)
202 private TransportStyleEnum myTransport;
203 private transient ByteArrayOutputStream myWriterCapture = new ByteArrayOutputStream();
204
205 public AbstractConnection() {
206 myId = UUID.randomUUID().toString();
207 }
208
209 protected void addActivity(ActivityBase theActivity) {
210 myRecentActivity.add(theActivity);
211 if (myRecentActivity.size() > 100) {
212 myRecentActivity.remove(0);
213 }
214 firePropertyChange(RECENT_ACTIVITY_PROPERTY, null, null);
215 }
216
217 SocketFactory getSocketFactory() {
218 return new SocketFactory() {
219
220 public Socket createTlsSocket() throws IOException {
221 try {
222 if (getTransport() == TransportStyleEnum.HL7_OVER_HTTP && getTlsKeystore() != null) {
223 return createHohSocketFactory().createClientSocket();
224 }
225 } catch (KeyStoreException e) {
226 throw new IOException(e.getMessage(), e);
227 }
228 return SSLSocketFactory.getDefault().createSocket();
229 }
230
231 public ServerSocket createTlsServerSocket() throws IOException {
232 try {
233 if (getTransport() == TransportStyleEnum.HL7_OVER_HTTP && getHohSignatureKeystore_() != null) {
234 return createHohSocketFactory().createServerSocket();
235 }
236 } catch (KeyStoreException e) {
237 throw new IOException(e.getMessage(), e);
238 }
239 return SSLServerSocketFactory.getDefault().createServerSocket();
240 }
241
242 private CustomCertificateTlsSocketFactory createHohSocketFactory() throws KeyStoreException {
243 KeyStore keystore = getTlsKeystore();
244 String keystorePassword = getTlsKeystorePassword();
245 CustomCertificateTlsSocketFactory sf = new CustomCertificateTlsSocketFactory(keystore, keystorePassword);
246 return sf;
247 }
248
249 public Socket createSocket() throws IOException {
250 return javax.net.SocketFactory.getDefault().createSocket();
251 }
252
253 public ServerSocket createServerSocket() throws IOException {
254 return ServerSocketFactory.getDefault().createServerSocket();
255 }
256
257 public void configureNewAcceptedSocket(Socket theSocket) throws SocketException {
258
259 }
260 };
261 }
262
263 public void addNewMessage() {
264 int oldValue = myNewMessages;
265 int newValue = myNewMessages + 1;
266
267 try {
268 fireVetoableChange(NEW_MESSAGES_PROPERTY, oldValue, newValue);
269 } catch (PropertyVetoException e) {
270 ourLog.debug("Property {} vetoed", NEW_MESSAGES_PROPERTY);
271 return;
272 }
273
274 myNewMessages = newValue;
275 firePropertyChange(NEW_MESSAGES_PROPERTY, oldValue, myNewMessages);
276 }
277
278 protected void beforeProcessingNewMessageIn() {
279 if (isCaptureBytes()) {
280 checkInboundCapture();
281 }
282 }
283
284 protected void beforeProcessingNewMessageOut() {
285 if (isCaptureBytes()) {
286 checkOutboundCapture();
287 }
288 }
289
290 private void checkInboundCapture() {
291 synchronized (myReaderCapture) {
292 byte[] inboundBytes = myReaderCapture.toByteArray();
293 if (inboundBytes.length > 0) {
294 addActivity(new ActivityIncomingBytes(new Date(), inboundBytes));
295 myReaderCapture.reset();
296 }
297 }
298 }
299
300 protected void addActivityInfoInSwingThread(final String msg) {
301 final ActivityBase activity = new ActivityInfo(new Date(), msg);
302 addActivityInSwingThread(activity);
303 }
304
305 protected void addActivityInSwingThread(final ActivityBase theActivity) {
306 SwingUtilities.invokeLater(new Runnable() {
307 public void run() {
308 addActivity(theActivity);
309 }
310 });
311 }
312
313 private void checkOutboundCapture() {
314 synchronized (myWriterCapture) {
315 byte[] outboundBytes = myWriterCapture.toByteArray();
316 if (outboundBytes.length > 0) {
317 addActivity(new ActivityOutgoingBytes(new Date(), outboundBytes));
318 myWriterCapture.reset();
319 }
320 }
321 }
322
323 public void clearNewMessages() {
324 int oldValue = myNewMessages;
325
326 try {
327 fireVetoableChange(NEW_MESSAGES_PROPERTY, oldValue, 0);
328 } catch (PropertyVetoException e) {
329 ourLog.debug("Property {} vetoed", NEW_MESSAGES_PROPERTY);
330 return;
331 }
332
333 myNewMessages = 0;
334 firePropertyChange(NEW_MESSAGES_PROPERTY, oldValue, myNewMessages);
335 }
336
337
338
339
340 public void clearRecentActivity() {
341 myRecentActivity.clear();
342 firePropertyChange(RECENT_ACTIVITY_PROPERTY, null, null);
343 }
344
345 protected String createDescription() {
346 StringBuilder retVal = new StringBuilder();
347 if (StringUtils.isNotBlank(myHost)) {
348 retVal.append(myHost);
349 } else {
350 retVal.append("Unknown");
351 }
352
353 retVal.append(":");
354 if (myIncomingOrSinglePort > 0) {
355 retVal.append(myIncomingOrSinglePort);
356 } else {
357 retVal.append("Unknown");
358 }
359
360 if (myOutgoingPort > 0) {
361 retVal.append(":");
362 retVal.append(myOutgoingPort);
363 }
364 String name = retVal.toString();
365 return name;
366 }
367
368 protected LowerLayerProtocol createLlp() throws LLPException {
369 LowerLayerProtocol llpClass;
370 if (getTransport() == TransportStyleEnum.HL7_OVER_HTTP) {
371
372 ServerRoleEnum role = isInbound() ? ServerRoleEnum.SERVER : ServerRoleEnum.CLIENT;
373 Hl7OverHttpLowerLayerProtocol hohLlp = new Hl7OverHttpLowerLayerProtocol(role);
374 if (isHohAuthenticationEnabled()) {
375 if (isInbound()) {
376 hohLlp.setAuthorizationCallback(new SingleCredentialServerCallback(getHohAuthenticationUsername(), getHohAuthenticationPassword()));
377 } else {
378 hohLlp.setAuthorizationCallback(new SingleCredentialClientCallback(getHohAuthenticationUsername(), getHohAuthenticationPassword()));
379 }
380 }
381 if (isHohSignatureEnabled()) {
382 BouncyCastleCmsMessageSigner signer = new BouncyCastleCmsMessageSigner();
383 try {
384 signer.setKeyStore(getHohSignatureKeystore_());
385 } catch (KeyStoreException e) {
386 throw new LLPException(e.getMessage(), e);
387 }
388 signer.setKeyAlias(getHohSignatureKey());
389 signer.setAliasPassword(getHohSignatureKeyPassword());
390 hohLlp.setSigner(signer);
391 }
392 hohLlp.setUriPath(getHttpUriPath());
393
394 llpClass = hohLlp;
395 } else if (isDetectCharSetInMessage()) {
396 llpClass = new ExtendedMinLowerLayerProtocol();
397 } else {
398 MinLowerLayerProtocol llp = new MinLowerLayerProtocol();
399 llp.setCharset(Charset.forName(getCharSet()));
400 llpClass = llp;
401 }
402
403 if (isCaptureBytes()) {
404 llpClass = new ByteCapturingMinLowerLayerProtocolWrapper(llpClass, myReaderCapture, myWriterCapture);
405 }
406
407 return llpClass;
408 }
409
410 protected HapiContext createHapiContext() throws IOException {
411 try {
412
413 SocketFactory serverSocket;
414 if (!isTls()) {
415 serverSocket = new ca.uhn.hl7v2.util.StandardSocketFactory();
416 } else if (getTlsKeystore() == null) {
417 serverSocket = new HapiSocketTlsFactoryWrapper(new TlsSocketFactory());
418 } else {
419 serverSocket = new HapiSocketTlsFactoryWrapper(new CustomCertificateTlsSocketFactory(getTlsKeystore(), getTlsKeystorePassword()));
420 }
421
422 HapiContext ctx = new DefaultHapiContext(new NoValidationBuilder());
423 ctx.setLowerLayerProtocol(createLlp());
424 ctx.setSocketFactory(serverSocket);
425
426 if (getEncoding() == Hl7V2EncodingTypeEnum.ER_7) {
427 ctx.getGenericParser().setPipeParserAsPrimary();
428 } else {
429 ctx.getGenericParser().setXMLParserAsPrimary();
430 }
431
432 return ctx;
433
434 } catch (Exception e) {
435 throw new IOException(e.getMessage(), e);
436 }
437 }
438
439 protected Parser createParser() {
440 Parser parser;
441 if (getEncoding() == Hl7V2EncodingTypeEnum.ER_7) {
442 parser = new PipeParser();
443 } else {
444 parser = new DefaultXMLParser();
445 }
446 parser.setValidationContext(new ValidationContextImpl());
447 return parser;
448 }
449
450 public void destroy() {
451 stop();
452 }
453
454
455
456
457
458
459 @Override
460 public boolean equals(Object theObj) {
461 return (theObj instanceof AbstractConnection./ca/uhn/hl7v2/testpanel/model/conn/AbstractConnection.html#AbstractConnection">AbstractConnection) && ((AbstractConnection) theObj).myId.equals(myId);
462 }
463
464
465
466
467 public String getCharSet() {
468 return myCharSet;
469 }
470
471
472
473
474 public Controller getController() {
475 return myController;
476 }
477
478
479
480
481 public Hl7V2EncodingTypeEnum getEncoding() {
482 return myEncoding;
483 }
484
485
486
487
488 public String getHohAuthenticationPassword() {
489 return myHohAuthenticationPassword;
490 }
491
492
493
494
495 public String getHohAuthenticationUsername() {
496 return myHohAuthenticationUsername;
497 }
498
499
500
501
502 public List<String> getHohSignatureAvailableAliases() {
503 List<String> retVal;
504 if (myHohSignatureAvailableAliases != null) {
505 retVal = myHohSignatureAvailableAliases;
506 } else {
507 retVal = Collections.emptyList();
508 }
509 return retVal;
510 }
511
512
513
514
515 public String getHohSignatureKey() {
516 return myHohSignatureKey;
517 }
518
519
520
521
522 public String getHohSignatureKeyPassword() {
523 return myHohSignatureKeyPassword;
524 }
525
526
527
528
529 public String getHohSignatureKeystore() {
530 return myHohSignatureKeystore;
531 }
532
533
534
535
536 public KeyStore getHohSignatureKeystore_() throws KeyStoreException {
537 if (isBlank(getHohSignatureKeystore())) {
538 return null;
539 }
540 if (myHohSignatureKeystore_ != null) {
541 return myHohSignatureKeystore_;
542 }
543
544 File jksFile = new File(getHohSignatureKeystore());
545 if (!jksFile.exists() || !jksFile.canRead()) {
546 throw new KeyStoreException("File does not exist or can not be read: " + jksFile.getAbsolutePath());
547 }
548
549 char[] password = null;
550 if (isNotBlank(myHohSignatureKeystorePassword)) {
551 password = myHohSignatureKeystorePassword.toCharArray();
552 }
553
554 KeyStore keystore;
555 try {
556 keystore = KeystoreUtils.loadKeystore(jksFile, password);
557 } catch (NoSuchAlgorithmException e) {
558 ourLog.error("Failed to load keystore!", e);
559 throw new KeyStoreException("Failed to load keystore: " + e.getMessage());
560 } catch (CertificateException e) {
561 ourLog.error("Failed to load keystore!", e);
562 throw new KeyStoreException("Failed to load keystore: " + e.getMessage());
563 } catch (IOException e) {
564 ourLog.error("Failed to load keystore!", e);
565 if (e.getCause() instanceof UnrecoverableKeyException) {
566 throw new KeyStoreException("Keystore password appears to be incorrect");
567 }
568 throw new KeyStoreException("Failed to load keystore: " + e.getMessage());
569 }
570
571 if (this instanceof InboundConnection) {
572 if (!KeystoreUtils.validateKeystoreForSignatureVerifying(keystore)) {
573 throw new KeyStoreException("Keystore contains no keys appropriate for receiving data");
574 }
575 } else if (this instanceof OutboundConnection) {
576 if (!KeystoreUtils.validateKeystoreForSignatureSigning(keystore)) {
577 throw new KeyStoreException("Keystore contains no keys appropriate for receiving data");
578 }
579 }
580
581 myHohSignatureKeystore_ = keystore;
582 return myHohSignatureKeystore_;
583 }
584
585
586
587
588 public String getHohSignatureKeystorePassword() {
589 return myHohSignatureKeystorePassword;
590 }
591
592
593
594
595 public String getHost() {
596 return myHost;
597 }
598
599
600
601
602 public String getHttpUriPath() {
603 return myHttpUriPath;
604 }
605
606 public String getId() {
607 return myId;
608 }
609
610
611
612
613 public int getIncomingOrSinglePort() {
614 return myIncomingOrSinglePort;
615 }
616
617
618
619
620 public String getName() {
621 updateName();
622 return myName;
623 }
624
625
626
627
628 public int getNewMessages() {
629 return myNewMessages;
630 }
631
632
633
634
635 public int getOutgoingPort() {
636 return myOutgoingPort;
637 }
638
639
640
641
642 public List<ActivityBase> getRecentActivity() {
643 return myRecentActivity;
644 }
645
646 @SuppressWarnings("unchecked")
647 public <T extends ActivityBase> List<T> getRecentActivityEntriesOfType(Class<T> theClass) {
648 ArrayList<T> retVal = new ArrayList<T>();
649 for (Object next : getRecentActivity()) {
650 if (theClass.isAssignableFrom(next.getClass())) {
651 retVal.add((T) next);
652 }
653 }
654 return retVal;
655 }
656
657
658
659
660 public StatusEnum getStatus() {
661 return myStatus;
662 }
663
664
665
666
667 public String getStatusLine() {
668 return myStatusLine;
669 }
670
671 public KeyStore getTlsKeystore() throws KeyStoreException {
672 if (isBlank(myTlsKeystoreLocation) || isTls() == false) {
673 return null;
674 }
675 if (myTlsKeystore != null) {
676 return myTlsKeystore;
677 }
678
679 File jksFile = new File(myTlsKeystoreLocation);
680 if (!jksFile.exists() || !jksFile.canRead()) {
681 throw new KeyStoreException("File does not exist or can not be read: " + jksFile.getAbsolutePath());
682 }
683
684 char[] password = null;
685 if (isNotBlank(myTlsKeystorePassword)) {
686 password = myTlsKeystorePassword.toCharArray();
687 }
688
689 KeyStore keystore;
690 try {
691 keystore = KeystoreUtils.loadKeystore(jksFile, password);
692 } catch (NoSuchAlgorithmException e) {
693 ourLog.error("Failed to load keystore!", e);
694 throw new KeyStoreException("Failed to load keystore: " + e.getMessage());
695 } catch (CertificateException e) {
696 ourLog.error("Failed to load keystore!", e);
697 throw new KeyStoreException("Failed to load keystore: " + e.getMessage());
698 } catch (IOException e) {
699 ourLog.error("Failed to load keystore!", e);
700 if (e.getCause() instanceof UnrecoverableKeyException) {
701 throw new KeyStoreException("Keystore password appears to be incorrect");
702 }
703 throw new KeyStoreException("Failed to load keystore: " + e.getMessage());
704 }
705
706 if (this instanceof InboundConnection) {
707 if (!KeystoreUtils.validateKeystoreForTlsReceiving(keystore)) {
708 throw new KeyStoreException("Keystore contains no keys appropriate for receiving data");
709 }
710 } else if (this instanceof OutboundConnection) {
711 if (!KeystoreUtils.validateKeystoreForTlsSending(keystore)) {
712 throw new KeyStoreException("Keystore contains no keys appropriate for receiving data");
713 }
714 }
715
716 myTlsKeystore = keystore;
717 return myTlsKeystore;
718 }
719
720
721
722
723 public String getTlsKeystoreLocation() {
724 return myTlsKeystoreLocation;
725 }
726
727
728
729
730 public String getTlsKeystorePassword() {
731 return myTlsKeystorePassword;
732 }
733
734
735
736
737 public TransportStyleEnum getTransport() {
738 if (myTransport == null) {
739 if (myDualPort) {
740 myTransport = TransportStyleEnum.DUAL_PORT_MLLP;
741 } else {
742 myTransport = TransportStyleEnum.SINGLE_PORT_MLLP;
743 }
744 }
745 return myTransport;
746 }
747
748
749
750
751
752
753 @Override
754 public int hashCode() {
755 return myId.hashCode();
756 }
757
758
759
760
761 public boolean isCaptureBytes() {
762 return myCaptureBytes;
763 }
764
765
766
767
768 public boolean isDetectCharSetInMessage() {
769 return myDetectCharSetInMessage;
770 }
771
772
773
774
775 public boolean isDualPort() {
776 return myDualPort;
777 }
778
779
780
781
782 public boolean isHohAuthenticationEnabled() {
783 return myHohAuthenticationEnabled;
784 }
785
786
787
788
789 public boolean isHohSignatureEnabled() {
790 return myHohSignatureEnabled;
791 }
792
793 private boolean isInbound() {
794 return this instanceof InboundConnection;
795 }
796
797
798
799
800 public boolean isNameIsExplicitlySet() {
801 return myNameIsExplicitlySet;
802 }
803
804
805
806
807 public boolean isPersistent() {
808 return myPersistent;
809 }
810
811
812
813
814 public boolean isTls() {
815 return myTls;
816 }
817
818 private void scheduleHohSecurityKeystoreCheck() {
819 synchronized (this) {
820 if (myHohSecurityKeystoreCheckIsScheduled == false) {
821 setTlsKeystoreStatus(new WorkingStatusBean("Working...", WorkingStatusBean.StatusEnum.WORKING));
822 }
823 myHohSecurityKeystoreCheckIsScheduled = true;
824 if (myController != null) {
825 myController.invokeInBackground(new CheckHohSecurityKeystoreRunnable());
826 }
827 }
828 }
829
830 private void scheduleHohSignatureKeystoreCheck() {
831 synchronized (this) {
832 if (myHohSignatureKeystoreCheckIsScheduled == false) {
833 setHohSignatureKeystoreStatus(new WorkingStatusBean("Working...", WorkingStatusBean.StatusEnum.WORKING));
834 }
835 myHohSignatureKeystoreCheckIsScheduled = true;
836 if (myController != null) {
837 myController.invokeInBackground(new CheckHohSignatureKeystoreRunnable());
838 }
839 }
840 }
841
842
843
844
845
846 public void setCaptureBytes(boolean theCaptureBytes) {
847 myCaptureBytes = theCaptureBytes;
848 }
849
850
851
852
853
854 public void setCharSet(String theCharSet) {
855 myCharSet = theCharSet;
856 }
857
858
859
860
861
862 public void setController(Controller theController) {
863 myController = theController;
864 scheduleHohSecurityKeystoreCheck();
865 scheduleHohSignatureKeystoreCheck();
866 }
867
868
869
870
871
872 public void setDetectCharSetInMessage(boolean theDetectCharSetInMessage) {
873 myDetectCharSetInMessage = theDetectCharSetInMessage;
874 }
875
876
877
878
879
880 public void setDualPort(boolean theDualPort) {
881 myDualPort = theDualPort;
882 updateName();
883 }
884
885
886
887
888
889 public void setEncoding(Hl7V2EncodingTypeEnum theEncoding) {
890 myEncoding = theEncoding;
891 }
892
893
894
895
896
897 public void setHohAuthenticationEnabled(boolean theHohAuthenticationEnabled) {
898 myHohAuthenticationEnabled = theHohAuthenticationEnabled;
899 }
900
901
902
903
904
905 public void setHohAuthenticationPassword(String theHohAuthenticationPassword) {
906 myHohAuthenticationPassword = theHohAuthenticationPassword;
907 }
908
909
910
911
912
913 public void setHohAuthenticationUsername(String theHohAuthenticationUsername) {
914 myHohAuthenticationUsername = theHohAuthenticationUsername;
915 }
916
917
918
919
920
921 public void setHohSignatureAvailableAliases(List<String> theList) {
922 List<String> oldValue = myHohSignatureAvailableAliases;
923 myHohSignatureAvailableAliases = theList;
924 firePropertyChange(HOH_SIGNER_AVAILABLE_ALIASES_PROPERTY, oldValue, theList);
925 }
926
927
928
929
930
931 public void setHohSignatureEnabled(boolean theHohSignatureEnabled) {
932 myHohSignatureEnabled = theHohSignatureEnabled;
933 scheduleHohSignatureKeystoreCheck();
934 }
935
936
937
938
939
940 public void setHohSignatureKey(String theHohSignatureKey) {
941 myHohSignatureKey = theHohSignatureKey;
942 scheduleHohSignatureKeystoreCheck();
943 }
944
945
946
947
948
949 public void setHohSignatureKeyPassword(String theHohSignatureKeyPassword) {
950 myHohSignatureKeyPassword = theHohSignatureKeyPassword;
951 scheduleHohSignatureKeystoreCheck();
952 }
953
954
955
956
957
958 public void setHohSignatureKeystore(String theHohSignatureKeystore) {
959 myHohSignatureKeystore = theHohSignatureKeystore;
960 scheduleHohSignatureKeystoreCheck();
961 }
962
963
964
965
966
967 public void setHohSignatureKeystorePassword(String theHohSignatureKeystorePassword) {
968 myHohSignatureKeystorePassword = theHohSignatureKeystorePassword;
969 scheduleHohSignatureKeystoreCheck();
970 }
971
972 private void setHohSignatureKeystoreStatus(final WorkingStatusBean theStatusBean) {
973 final WorkingStatusBean oldValue = myHohSignatureKeystoreStatus;
974 myHohSignatureKeystoreStatus = theStatusBean;
975 EventQueue.invokeLater(new Runnable() {
976 public void run() {
977 firePropertyChange(HOH_SIGNATURE_KEYSTORE_STATUS, oldValue, theStatusBean);
978 }
979 });
980 }
981
982
983
984
985
986 public void setHost(String theHost) {
987 myHost = theHost;
988 updateName();
989 }
990
991
992
993
994
995 public void setHttpUriPath(String theHttpUriPath) {
996 myHttpUriPath = theHttpUriPath;
997 }
998
999
1000
1001
1002
1003 public void setIncomingOrSinglePort(int theIncomingOrSinglePort) {
1004 myIncomingOrSinglePort = theIncomingOrSinglePort;
1005 updateName();
1006 }
1007
1008
1009
1010
1011
1012 public void setName(String theName) {
1013 String oldValue = myName;
1014 myName = theName;
1015 firePropertyChange(NAME_PROPERTY, oldValue, myName);
1016 }
1017
1018
1019
1020
1021
1022 public void setNameExplicitly(String theName) {
1023 if (theName == null) {
1024 return;
1025 }
1026 String oldValue = myName;
1027 myName = theName;
1028 if (StringUtils.equals(oldValue, theName) == false) {
1029 myNameIsExplicitlySet = true;
1030 }
1031 firePropertyChange(NAME_PROPERTY, oldValue, myName);
1032 }
1033
1034
1035
1036
1037
1038 public void setNameIsExplicitlySet(boolean theNameIsExplicitlySet) {
1039 myNameIsExplicitlySet = theNameIsExplicitlySet;
1040 }
1041
1042
1043
1044
1045
1046 public void setOutgoingPort(int theOutgoingPort) {
1047 myOutgoingPort = theOutgoingPort;
1048 }
1049
1050
1051
1052
1053
1054 public void setPersistent(boolean thePersistent) {
1055 boolean oldValue = myPersistent;
1056 myPersistent = thePersistent;
1057 firePropertyChange(PERSISTENT_PROPERTY, oldValue, myPersistent);
1058 }
1059
1060 protected void setStatus(StatusEnum theTryingToStart) {
1061 StatusEnum oldValue = myStatus;
1062 myStatus = theTryingToStart;
1063 firePropertyChange(STATUS_PROPERTY, oldValue, myStatus);
1064 }
1065
1066
1067
1068
1069
1070 public void setStatusLine(String theStatusLine) {
1071 String oldValue = myStatusLine;
1072 myStatusLine = theStatusLine;
1073 firePropertyChange(STATUS_LINE_PROPERTY, oldValue, theStatusLine);
1074 }
1075
1076 public void setTls(boolean theSelected) {
1077 boolean oldValue = myTls;
1078 myTls = theSelected;
1079 if (oldValue != myTls) {
1080 myTlsKeystore = null;
1081 }
1082 }
1083
1084 public void setTlsKeystoreLocation(String theTlsKeystoreLocation) {
1085 String oldValue = myTlsKeystoreLocation;
1086 myTlsKeystoreLocation = theTlsKeystoreLocation;
1087 if (StringUtils.equals(oldValue, theTlsKeystoreLocation) == false) {
1088 myTlsKeystore = null;
1089 }
1090 scheduleHohSecurityKeystoreCheck();
1091 }
1092
1093 public void setTlsKeystorePassword(String theTlsKeystorePassword) {
1094 String oldValue = myTlsKeystorePassword;
1095 myTlsKeystorePassword = theTlsKeystorePassword;
1096 if (StringUtils.equals(oldValue, theTlsKeystorePassword) == false) {
1097 myTlsKeystore = null;
1098 }
1099 scheduleHohSecurityKeystoreCheck();
1100 }
1101
1102 private void setTlsKeystoreStatus(final WorkingStatusBean theStatusBean) {
1103 final WorkingStatusBean oldValue = myTlsKeystoreStatus;
1104 myTlsKeystoreStatus = theStatusBean;
1105 EventQueue.invokeLater(new Runnable() {
1106 public void run() {
1107 firePropertyChange(TLS_KEYSTORE_STATUS, oldValue, theStatusBean);
1108 }
1109 });
1110 }
1111
1112
1113
1114
1115
1116 public void setTransport(TransportStyleEnum theTransport) {
1117 TransportStyleEnum oldValue = myTransport;
1118 myTransport = theTransport;
1119 firePropertyChange(TRANSPORT_PROPERTY, oldValue, myTransport);
1120 }
1121
1122 public void start() {
1123 if (isCaptureBytes()) {
1124 myStreamWatcherThread = new StreamWatcherThread();
1125 myStreamWatcherThread.start();
1126 }
1127 }
1128
1129 public void stop() {
1130 if (myStreamWatcherThread != null) {
1131 StreamWatcherThread streamWatcherThread = myStreamWatcherThread;
1132 myStreamWatcherThread = null;
1133 streamWatcherThread.interrupt();
1134 }
1135 }
1136
1137 private void updateName() {
1138 if (myName != null && isNameIsExplicitlySet()) {
1139 return;
1140 }
1141
1142 String name = createDescription();
1143 setName(name);
1144 }
1145
1146 private final class CheckHohSecurityKeystoreRunnable implements Runnable {
1147 public void run() {
1148 try {
1149 Thread.sleep(500);
1150 } catch (InterruptedException e) {
1151
1152 }
1153
1154 synchronized (AbstractConnection.this) {
1155 if (!myHohSecurityKeystoreCheckIsScheduled) {
1156 return;
1157 }
1158 myHohSecurityKeystoreCheckIsScheduled = false;
1159 }
1160
1161 KeyStore keystore;
1162 try {
1163 keystore = getTlsKeystore();
1164 if (keystore == null) {
1165 if (isTls()) {
1166 setTlsKeystoreStatus(new WorkingStatusBean("Using system keystore", WorkingStatusBean.StatusEnum.OK));
1167 } else {
1168 setTlsKeystoreStatus(new WorkingStatusBean("", WorkingStatusBean.StatusEnum.OK));
1169 }
1170 } else {
1171 setTlsKeystoreStatus(new WorkingStatusBean("Keystore appears good", WorkingStatusBean.StatusEnum.OK));
1172 }
1173 } catch (KeyStoreException e) {
1174 ourLog.error("Keystore problem", e);
1175 setTlsKeystoreStatus(new WorkingStatusBean(e.getMessage(), WorkingStatusBean.StatusEnum.ERROR));
1176 }
1177 }
1178
1179 }
1180
1181 private final class CheckHohSignatureKeystoreRunnable implements Runnable {
1182 public void run() {
1183
1184 if (!isHohSignatureEnabled()) {
1185 setHohSignatureKeystoreStatus(new WorkingStatusBean("", WorkingStatusBean.StatusEnum.OK));
1186 return;
1187 }
1188
1189 if (isBlank(getHohSignatureKeystore())) {
1190 setHohSignatureKeystoreStatus(new WorkingStatusBean("Select a KeyStore", WorkingStatusBean.StatusEnum.ERROR));
1191 return;
1192 }
1193
1194 try {
1195 Thread.sleep(500);
1196 } catch (InterruptedException e) {
1197
1198 }
1199
1200 synchronized (AbstractConnection.this) {
1201 if (!myHohSignatureKeystoreCheckIsScheduled) {
1202 return;
1203 }
1204 myHohSignatureKeystoreCheckIsScheduled = false;
1205 }
1206
1207 KeyStore keystore;
1208 try {
1209 keystore = getHohSignatureKeystore_();
1210 if (keystore == null) {
1211 setHohSignatureKeystoreStatus(new WorkingStatusBean("No keystore selected", WorkingStatusBean.StatusEnum.ERROR));
1212 setHohSignatureAvailableAliases(new ArrayList<String>());
1213 } else {
1214
1215 List<String> aliases = CollectionUtils.enumerationToList(keystore.aliases());
1216 if (aliases.size() > 0) {
1217 if (isBlank(myHohSignatureKey)) {
1218 myHohSignatureKey = aliases.get(0);
1219 }
1220 } else {
1221 setHohSignatureKeystoreStatus(new WorkingStatusBean("Keystore contains no suitable keys/aliases", WorkingStatusBean.StatusEnum.ERROR));
1222 return;
1223 }
1224
1225 setHohSignatureAvailableAliases(aliases);
1226
1227 if (!isInbound()) {
1228 if (isBlank(myHohSignatureKeyPassword)) {
1229 setHohSignatureKeystoreStatus(new WorkingStatusBean("Key password not specified", WorkingStatusBean.StatusEnum.ERROR));
1230 return;
1231 }
1232 if (!KeystoreUtils.canRecoverKey(getHohSignatureKeystore_(), getHohSignatureKey(), getHohSignatureKeyPassword())) {
1233 setHohSignatureKeystoreStatus(new WorkingStatusBean("Key password is incorrect", WorkingStatusBean.StatusEnum.ERROR));
1234 return;
1235 }
1236 if (!KeystoreUtils.validateKeyForSignatureSigning(getHohSignatureKeystore_(), getHohSignatureKey(), getHohSignatureKeyPassword())) {
1237 setHohSignatureKeystoreStatus(new WorkingStatusBean("Key is not appropriate for signing", WorkingStatusBean.StatusEnum.ERROR));
1238 return;
1239 }
1240 }
1241
1242 Certificate cert = keystore.getCertificate(myHohSignatureKey);
1243 if (cert instanceof X509Certificate) {
1244 String signer = ((X509Certificate) cert).getSubjectX500Principal().getName();
1245 setHohSignatureKeystoreStatus(new WorkingStatusBean("Key belongs to: " + signer, WorkingStatusBean.StatusEnum.OK));
1246 } else {
1247 setHohSignatureKeystoreStatus(new WorkingStatusBean("Keystore appears good", WorkingStatusBean.StatusEnum.OK));
1248 }
1249
1250
1251
1252
1253 }
1254 } catch (KeyStoreException e) {
1255 ourLog.error("Keystore problem", e);
1256 setHohSignatureKeystoreStatus(new WorkingStatusBean(e.getMessage(), WorkingStatusBean.StatusEnum.ERROR));
1257 setHohSignatureAvailableAliases(new ArrayList<String>());
1258 }
1259 }
1260
1261 }
1262
1263 public enum StatusEnum {
1264 FAILED(false), STARTED(true), STOPPED(false), TRYING_TO_START(true);
1265
1266 private boolean myRunning;
1267
1268 private StatusEnum(boolean theRunning) {
1269 myRunning = theRunning;
1270 }
1271
1272 public boolean isRunning() {
1273 return myRunning;
1274 }
1275 }
1276
1277 private class StreamWatcherThread extends Thread {
1278
1279 @Override
1280 public void run() {
1281 while (myStreamWatcherThread == this) {
1282 checkInboundCapture();
1283 checkOutboundCapture();
1284
1285 try {
1286 Thread.sleep(500);
1287 } catch (InterruptedException e) {
1288
1289 }
1290 }
1291 }
1292
1293 }
1294
1295 }