001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2011 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038 039package org.dcm4che3.net; 040 041import org.dcm4che3.conf.core.api.ConfigurableClass; 042import org.dcm4che3.conf.core.api.ConfigurableProperty; 043import org.dcm4che3.conf.core.api.ConfigurableProperty.ConfigurablePropertyType; 044import org.dcm4che3.conf.core.api.ConfigurableProperty.Tag; 045import org.dcm4che3.conf.core.api.LDAP; 046import org.dcm4che3.data.Code; 047import org.dcm4che3.data.Issuer; 048import org.dcm4che3.util.StringUtils; 049 050import javax.net.ssl.KeyManager; 051import javax.net.ssl.SSLContext; 052import javax.net.ssl.TrustManager; 053import java.io.IOException; 054import java.io.Serializable; 055import java.security.GeneralSecurityException; 056import java.security.KeyManagementException; 057import java.security.cert.X509Certificate; 058import java.util.*; 059import java.util.Map.Entry; 060import java.util.concurrent.Executor; 061import java.util.concurrent.ScheduledExecutorService; 062import java.util.concurrent.ScheduledFuture; 063import java.util.concurrent.TimeUnit; 064 065/** 066 * DICOM Part 15, Annex H compliant description of a DICOM enabled system or 067 * device. This is used to describe a DICOM-enabled network endpoint in terms of 068 * its physical attributes (serial number, manufacturer, etc.), its context 069 * (issuer of patient ids used by the device, etc.), as well as its capabilities 070 * (TLS-enabled, AE titles used, etc.). 071 * 072 * @author Gunter Zeilinger <gunterze@gmail.com> 073 */ 074@LDAP( 075 objectClasses = {"dcmDevice", "dicomDevice"}, 076 distinguishingField = "dicomDeviceName") 077@ConfigurableClass(referable = true) 078public class Device implements Serializable { 079 080 private static final long serialVersionUID = -5816872456184522866L; 081 082 @ConfigurableProperty(name = "dicomDeviceName", label = "Device name", tags = Tag.PRIMARY) 083 private String deviceName; 084 085 /** 086 * Temporarily gets assigned the value of device name with a prefix 087 * @see Device#setDeviceName(String) 088 */ 089 @ConfigurableProperty(type = ConfigurablePropertyType.UUID) 090 private String uuid; 091 092 @ConfigurableProperty(name = "dicomDescription") 093 private String description; 094 095 @ConfigurableProperty(type = ConfigurablePropertyType.OptimisticLockingHash) 096 private String olockHash; 097 098 @ConfigurableProperty(name = "dicomManufacturer") 099 private String manufacturer; 100 101 @ConfigurableProperty(name = "dicomManufacturerModelName") 102 private String manufacturerModelName; 103 104 @ConfigurableProperty(name = "dicomStationName") 105 private String stationName; 106 107 @ConfigurableProperty(name = "dicomDeviceSerialNumber") 108 private String deviceSerialNumber; 109 110 @ConfigurableProperty(name = "dcmTrustStoreURL") 111 private String trustStoreURL; 112 113 @ConfigurableProperty(name = "dcmTrustStoreType") 114 private String trustStoreType; 115 116 @ConfigurableProperty(name = "dcmTrustStorePin") 117 private String trustStorePin; 118 119 @ConfigurableProperty(name = "dcmTrustStorePinProperty") 120 private String trustStorePinProperty; 121 122 @ConfigurableProperty(name = "dcmKeyStoreURL") 123 private String keyStoreURL; 124 125 @ConfigurableProperty(name = "dcmKeyStoreType") 126 private String keyStoreType; 127 128 @ConfigurableProperty(name = "dcmKeyStorePin") 129 private String keyStorePin; 130 131 @ConfigurableProperty(name = "dcmKeyStorePinProperty") 132 private String keyStorePinProperty; 133 134 @ConfigurableProperty(name = "dcmKeyStoreKeyPin") 135 private String keyStoreKeyPin; 136 137 @ConfigurableProperty(name = "dcmKeyStoreKeyPinProperty") 138 private String keyStoreKeyPinProperty; 139 140 @ConfigurableProperty(name = "dicomIssuerOfPatientID") 141 private Issuer issuerOfPatientID; 142 143 @ConfigurableProperty(name = "dicomIssuerOfAccessionNumber") 144 private Issuer issuerOfAccessionNumber; 145 146 @ConfigurableProperty(name = "dicomOrderPlacerIdentifier") 147 private Issuer orderPlacerIdentifier; 148 149 @ConfigurableProperty(name = "dicomOrderFillerIdentifier") 150 private Issuer orderFillerIdentifier; 151 152 @ConfigurableProperty(name = "dicomIssuerOfAdmissionID") 153 private Issuer issuerOfAdmissionID; 154 155 @ConfigurableProperty(name = "dicomIssuerOfServiceEpisodeID") 156 private Issuer issuerOfServiceEpisodeID; 157 158 @ConfigurableProperty(name = "dicomIssuerOfContainerIdentifier") 159 private Issuer issuerOfContainerIdentifier; 160 161 @ConfigurableProperty(name = "dicomIssuerOfSpecimenIdentifier") 162 private Issuer issuerOfSpecimenIdentifier; 163 164 @ConfigurableProperty(name = "dicomSoftwareVersion") 165 private String[] softwareVersions = {}; 166 167 @ConfigurableProperty(name = "dicomPrimaryDeviceType") 168 private String[] primaryDeviceTypes = {}; 169 170 @ConfigurableProperty(name = "dicomInstitutionName") 171 private String[] institutionNames = {}; 172 173 @ConfigurableProperty(name = "dicomInstitutionCode") 174 private Code[] institutionCodes = {}; 175 176 @ConfigurableProperty(name = "dicomInstitutionAddress") 177 private String[] institutionAddresses = {}; 178 179 @ConfigurableProperty(name = "dicomInstitutionDepartmentName") 180 private String[] institutionalDepartmentNames = {}; 181 182 @ConfigurableProperty(name = "dicomRelatedDeviceReference") 183 private String[] relatedDeviceRefs = {}; 184 185 @ConfigurableProperty(name = "dicomVendorData") 186 private byte[][] vendorData = {}; 187 188 @ConfigurableProperty(name = "dcmLimitOpenAssociations") 189 private int limitOpenAssociations; 190 191 @ConfigurableProperty(name = "dicomInstalled") 192 private boolean installed = true; 193 194 @ConfigurableProperty(name = "dcmTimeZoneOfDevice") 195 private TimeZone timeZoneOfDevice; 196 197 198 //TODO: finalize and store x509 cretificates !! 199 private final LinkedHashMap<String, X509Certificate[]> authorizedNodeCertificates = 200 new LinkedHashMap<String, X509Certificate[]>(); 201 private final LinkedHashMap<String, X509Certificate[]> thisNodeCertificates = 202 new LinkedHashMap<String, X509Certificate[]>(); 203 204 205 @LDAP(noContainerNode = true) 206 @ConfigurableProperty( 207 name = "dicomConnection", 208 label = "Connections" 209 ) 210 private final List<Connection> connections = new ArrayList<Connection>(); 211 212 /** 213 * Note: This only maps the main AE titles to application entities. 214 * {@link #aliasApplicationEntitiesMap} will contain also alias AE titles. 215 */ 216 @LDAP(noContainerNode = true) 217 @ConfigurableProperty( 218 name = "dicomNetworkAE", 219 label = "Application Entities" 220 ) 221 private final Map<String, ApplicationEntity> applicationEntitiesMap = 222 new TreeMap<String, ApplicationEntity>(); 223 224 225 @ConfigurableProperty(isReference = true, 226 name = "dcmDefaultAE", 227 tags = Tag.PRIMARY, 228 description = "Default AE to be used by both services running locally on this device as well as external services" 229 ) 230 private ApplicationEntity defaultAE; 231 232 /** 233 * Maps alias AE titles ({@link ApplicationEntity#getAETitleAliases()}), 234 * including also the main AE title ({@link ApplicationEntity#getAETitle()}), to application entities. 235 */ 236 private final transient Map<String, ApplicationEntity> aliasApplicationEntitiesMap = new TreeMap<String, ApplicationEntity>(); 237 238 @ConfigurableProperty(name = "deviceExtensions", isExtensionsProperty = true) 239 private Map<Class<? extends DeviceExtension>, DeviceExtension> extensions = 240 new HashMap<Class<? extends DeviceExtension>, DeviceExtension>(); 241 242 private transient AssociationHandler associationHandler = new AssociationHandler(); 243 private transient DimseRQHandler dimseRQHandler; 244 private transient ConnectionMonitor connectionMonitor; 245 246 private transient int assocCount = 0; 247 private transient final Object assocCountLock = new Object(); 248 249 private transient Executor executor; 250 private transient ScheduledExecutorService scheduledExecutor; 251 private transient volatile SSLContext sslContext; 252 private transient volatile KeyManager km; 253 private transient volatile TrustManager tm; 254 255 public Device() { 256 } 257 258 public Device(String name) { 259 setDeviceName(name); 260 } 261 262 private void checkNotEmpty(String name, String val) { 263 if (val != null && val.isEmpty()) 264 throw new IllegalArgumentException(name + " cannot be empty"); 265 } 266 267 public ApplicationEntity getDefaultAE() { 268 return defaultAE; 269 } 270 271 public void setDefaultAE(ApplicationEntity defaultAE) { 272 this.defaultAE = defaultAE; 273 } 274 275 /** 276 * Get the name of this device. 277 * 278 * @return A String containing the device name. 279 */ 280 public final String getDeviceName() { 281 return deviceName; 282 } 283 284 /** 285 * Set the name of this device. 286 * 287 * @param name A String containing the device name. 288 */ 289 public final void setDeviceName(String name) { 290 checkNotEmpty("Device Name", name); 291 this.deviceName = name; 292 // temporarily 293 this.uuid = "Device-" + name; 294 } 295 296 /** 297 * Get the description of this device. 298 * 299 * @return A String containing the device description. 300 */ 301 public final String getDescription() { 302 return description; 303 } 304 305 /** 306 * Set the description of this device. 307 * 308 * @param description A String containing the device description. 309 */ 310 public final void setDescription(String description) { 311 this.description = description; 312 } 313 314 /** 315 * Get the manufacturer of this device. 316 * 317 * @return A String containing the device manufacturer. 318 */ 319 public final String getManufacturer() { 320 return manufacturer; 321 } 322 323 /** 324 * Set the manufacturer of this device. 325 * <p/> 326 * This should be the same as the value of Manufacturer (0008,0070) in SOP 327 * instances created by this device. 328 * 329 * @param manufacturer A String containing the device manufacturer. 330 */ 331 public final void setManufacturer(String manufacturer) { 332 this.manufacturer = manufacturer; 333 } 334 335 /** 336 * Get the manufacturer model name of this device. 337 * 338 * @return A String containing the device manufacturer model name. 339 */ 340 public final String getManufacturerModelName() { 341 return manufacturerModelName; 342 } 343 344 /** 345 * Set the manufacturer model name of this device. 346 * <p/> 347 * This should be the same as the value of Manufacturer Model Name 348 * (0008,1090) in SOP instances created by this device. 349 * 350 * @param manufacturerModelName A String containing the device manufacturer model name. 351 */ 352 public final void setManufacturerModelName(String manufacturerModelName) { 353 this.manufacturerModelName = manufacturerModelName; 354 } 355 356 /** 357 * Get the software versions running on (or implemented by) this device. 358 * 359 * @return A String array containing the software versions. 360 */ 361 public final String[] getSoftwareVersions() { 362 return softwareVersions; 363 } 364 365 /** 366 * Set the software versions running on (or implemented by) this device. 367 * <p/> 368 * This should be the same as the values of Software Versions (0018,1020) in 369 * SOP instances created by this device. 370 * 371 * @param softwareVersions A String array containing the software versions. 372 */ 373 public final void setSoftwareVersions(String... softwareVersions) { 374 this.softwareVersions = softwareVersions; 375 } 376 377 /** 378 * Get the station name belonging to this device. 379 * 380 * @return A String containing the station name. 381 */ 382 public final String getStationName() { 383 return stationName; 384 } 385 386 /** 387 * Set the station name belonging to this device. 388 * <p/> 389 * This should be the same as the value of Station Name (0008,1010) in SOP 390 * instances created by this device. 391 * 392 * @param stationName A String containing the station name. 393 */ 394 public final void setStationName(String stationName) { 395 this.stationName = stationName; 396 } 397 398 /** 399 * Get the serial number belonging to this device. 400 * 401 * @return A String containing the serial number. 402 */ 403 public final String getDeviceSerialNumber() { 404 return deviceSerialNumber; 405 } 406 407 /** 408 * Set the serial number of this device. 409 * <p/> 410 * This should be the same as the value of Device Serial Number (0018,1000) 411 * in SOP instances created by this device. 412 * 413 * @param deviceSerialNumber A String containing the serial number. 414 */ 415 public final void setDeviceSerialNumber(String deviceSerialNumber) { 416 this.deviceSerialNumber = deviceSerialNumber; 417 } 418 419 /** 420 * Get the type codes associated with this device. 421 * 422 * @return A String array containing the type codes of this device. 423 */ 424 public final String[] getPrimaryDeviceTypes() { 425 return primaryDeviceTypes; 426 } 427 428 /** 429 * Set the type codes associated with this device. 430 * <p/> 431 * Represents the kind of device and is most applicable for acquisition 432 * modalities. Types should be selected from the list of code values 433 * (0008,0100) for Context ID 30 in PS3.16 when applicable. 434 * 435 * @param primaryDeviceTypes 436 */ 437 public void setPrimaryDeviceTypes(String... primaryDeviceTypes) { 438 this.primaryDeviceTypes = primaryDeviceTypes; 439 } 440 441 /** 442 * Get the institution name associated with this device; may be the site 443 * where it resides or is operating on behalf of. 444 * 445 * @return A String array containing the institution name values. 446 */ 447 public final String[] getInstitutionNames() { 448 return institutionNames; 449 } 450 451 /** 452 * Set the institution name associated with this device; may be the site 453 * where it resides or is operating on behalf of. 454 * <p/> 455 * Should be the same as the value of Institution Name (0008,0080) in SOP 456 * Instances created by this device. 457 * 458 * @param names A String array containing the institution name values. 459 */ 460 public void setInstitutionNames(String... names) { 461 institutionNames = names; 462 } 463 464 public final Code[] getInstitutionCodes() { 465 return institutionCodes; 466 } 467 468 public void setInstitutionCodes(Code... codes) { 469 institutionCodes = codes; 470 } 471 472 /** 473 * Set the address of the institution which operates this device. 474 * 475 * @return A String array containing the institution address values. 476 */ 477 public final String[] getInstitutionAddresses() { 478 return institutionAddresses; 479 } 480 481 /** 482 * Get the address of the institution which operates this device. 483 * <p/> 484 * Should be the same as the value of Institution Address (0008,0081) 485 * attribute in SOP Instances created by this device. 486 * 487 * @param addresses A String array containing the institution address values. 488 */ 489 public void setInstitutionAddresses(String... addresses) { 490 institutionAddresses = addresses; 491 } 492 493 /** 494 * Get the department name associated with this device. 495 * 496 * @return A String array containing the dept. name values. 497 */ 498 public final String[] getInstitutionalDepartmentNames() { 499 return institutionalDepartmentNames; 500 } 501 502 /** 503 * Set the department name associated with this device. 504 * <p/> 505 * Should be the same as the value of Institutional Department Name 506 * (0008,1040) in SOP Instances created by this device. 507 * 508 * @param names A String array containing the dept. name values. 509 */ 510 public void setInstitutionalDepartmentNames(String... names) { 511 institutionalDepartmentNames = names; 512 } 513 514 public final Issuer getIssuerOfPatientID() { 515 return issuerOfPatientID; 516 } 517 518 public final void setIssuerOfPatientID(Issuer issuerOfPatientID) { 519 this.issuerOfPatientID = issuerOfPatientID; 520 } 521 522 public final Issuer getIssuerOfAccessionNumber() { 523 return issuerOfAccessionNumber; 524 } 525 526 public final void setIssuerOfAccessionNumber(Issuer issuerOfAccessionNumber) { 527 this.issuerOfAccessionNumber = issuerOfAccessionNumber; 528 } 529 530 public final Issuer getOrderPlacerIdentifier() { 531 return orderPlacerIdentifier; 532 } 533 534 public final void setOrderPlacerIdentifier(Issuer orderPlacerIdentifier) { 535 this.orderPlacerIdentifier = orderPlacerIdentifier; 536 } 537 538 public final Issuer getOrderFillerIdentifier() { 539 return orderFillerIdentifier; 540 } 541 542 public final void setOrderFillerIdentifier(Issuer orderFillerIdentifier) { 543 this.orderFillerIdentifier = orderFillerIdentifier; 544 } 545 546 public final Issuer getIssuerOfAdmissionID() { 547 return issuerOfAdmissionID; 548 } 549 550 public final void setIssuerOfAdmissionID(Issuer issuerOfAdmissionID) { 551 this.issuerOfAdmissionID = issuerOfAdmissionID; 552 } 553 554 public final Issuer getIssuerOfServiceEpisodeID() { 555 return issuerOfServiceEpisodeID; 556 } 557 558 public final void setIssuerOfServiceEpisodeID(Issuer issuerOfServiceEpisodeID) { 559 this.issuerOfServiceEpisodeID = issuerOfServiceEpisodeID; 560 } 561 562 public final Issuer getIssuerOfContainerIdentifier() { 563 return issuerOfContainerIdentifier; 564 } 565 566 public final void setIssuerOfContainerIdentifier(Issuer issuerOfContainerIdentifier) { 567 this.issuerOfContainerIdentifier = issuerOfContainerIdentifier; 568 } 569 570 public final Issuer getIssuerOfSpecimenIdentifier() { 571 return issuerOfSpecimenIdentifier; 572 } 573 574 public final void setIssuerOfSpecimenIdentifier(Issuer issuerOfSpecimenIdentifier) { 575 this.issuerOfSpecimenIdentifier = issuerOfSpecimenIdentifier; 576 } 577 578 public X509Certificate[] getAuthorizedNodeCertificates(String ref) { 579 return authorizedNodeCertificates.get(ref); 580 } 581 582 public void setAuthorizedNodeCertificates(String ref, X509Certificate... certs) { 583 authorizedNodeCertificates.put(ref, certs); 584 setTrustManager(null); 585 } 586 587 public X509Certificate[] removeAuthorizedNodeCertificates(String ref) { 588 X509Certificate[] certs = authorizedNodeCertificates.remove(ref); 589 setTrustManager(null); 590 return certs; 591 } 592 593 public void removeAllAuthorizedNodeCertificates() { 594 authorizedNodeCertificates.clear(); 595 setTrustManager(null); 596 } 597 598 public X509Certificate[] getAllAuthorizedNodeCertificates() { 599 return toArray(authorizedNodeCertificates.values()); 600 } 601 602 public String[] getAuthorizedNodeCertificateRefs() { 603 return authorizedNodeCertificates.keySet().toArray(StringUtils.EMPTY_STRING); 604 } 605 606 public final String getTrustStoreURL() { 607 return trustStoreURL; 608 } 609 610 public final void setTrustStoreURL(String trustStoreURL) { 611 checkNotEmpty("trustStoreURL", trustStoreURL); 612 if (trustStoreURL == null 613 ? this.trustStoreURL == null 614 : trustStoreURL.equals(this.trustStoreURL)) 615 return; 616 617 this.trustStoreURL = trustStoreURL; 618 setTrustManager(null); 619 } 620 621 public final String getTrustStoreType() { 622 return trustStoreType; 623 } 624 625 public final void setTrustStoreType(String trustStoreType) { 626 checkNotEmpty("trustStoreType", trustStoreType); 627 this.trustStoreType = trustStoreType; 628 } 629 630 public final String getTrustStorePin() { 631 return trustStorePin; 632 } 633 634 public final void setTrustStorePin(String trustStorePin) { 635 checkNotEmpty("trustStorePin", trustStorePin); 636 this.trustStorePin = trustStorePin; 637 } 638 639 public final String getTrustStorePinProperty() { 640 return trustStorePinProperty; 641 } 642 643 public final void setTrustStorePinProperty(String trustStorePinProperty) { 644 checkNotEmpty("keyPin", keyStoreKeyPin); 645 this.trustStorePinProperty = trustStorePinProperty; 646 } 647 648 public String getOlockHash() { 649 return olockHash; 650 } 651 652 public void setOlockHash(String olockHash) { 653 this.olockHash = olockHash; 654 } 655 656 public X509Certificate[] getThisNodeCertificates(String ref) { 657 return thisNodeCertificates.get(ref); 658 } 659 660 public void setThisNodeCertificates(String ref, X509Certificate... certs) { 661 thisNodeCertificates.put(ref, certs); 662 } 663 664 public X509Certificate[] removeThisNodeCertificates(String ref) { 665 return thisNodeCertificates.remove(ref); 666 } 667 668 public final String getKeyStoreURL() { 669 return keyStoreURL; 670 } 671 672 public final void setKeyStoreURL(String keyStoreURL) { 673 checkNotEmpty("keyStoreURL", keyStoreURL); 674 if (keyStoreURL == null 675 ? this.keyStoreURL == null 676 : keyStoreURL.equals(this.keyStoreURL)) 677 return; 678 679 this.keyStoreURL = keyStoreURL; 680 setKeyManager(null); 681 } 682 683 public final String getKeyStoreType() { 684 return keyStoreType; 685 } 686 687 public final void setKeyStoreType(String keyStoreType) { 688 checkNotEmpty("keyStoreType", keyStoreURL); 689 this.keyStoreType = keyStoreType; 690 } 691 692 public final String getKeyStorePin() { 693 return keyStorePin; 694 } 695 696 public final void setKeyStorePin(String keyStorePin) { 697 checkNotEmpty("keyStorePin", keyStorePin); 698 this.keyStorePin = keyStorePin; 699 } 700 701 public final String getKeyStorePinProperty() { 702 return keyStorePinProperty; 703 } 704 705 public final void setKeyStorePinProperty(String keyStorePinProperty) { 706 checkNotEmpty("keyStorePinProperty", keyStorePinProperty); 707 this.keyStorePinProperty = keyStorePinProperty; 708 } 709 710 public final String getKeyStoreKeyPin() { 711 return keyStoreKeyPin; 712 } 713 714 public final void setKeyStoreKeyPin(String keyStorePin) { 715 checkNotEmpty("keyStoreKeyPin", keyStorePin); 716 this.keyStoreKeyPin = keyStorePin; 717 } 718 719 public final String getKeyStoreKeyPinProperty() { 720 return keyStoreKeyPinProperty; 721 } 722 723 public final void setKeyStoreKeyPinProperty(String keyStoreKeyPinProperty) { 724 checkNotEmpty("keyStoreKeyPinProperty", keyStoreKeyPinProperty); 725 this.keyStoreKeyPinProperty = keyStoreKeyPinProperty; 726 } 727 728 public void removeAllThisNodeCertificates() { 729 thisNodeCertificates.clear(); 730 } 731 732 public X509Certificate[] getAllThisNodeCertificates() { 733 return toArray(thisNodeCertificates.values()); 734 } 735 736 public String[] getThisNodeCertificateRefs() { 737 return thisNodeCertificates.keySet().toArray(StringUtils.EMPTY_STRING); 738 } 739 740 private static X509Certificate[] toArray(Collection<X509Certificate[]> c) { 741 int size = 0; 742 for (X509Certificate[] certs : c) 743 size += certs.length; 744 745 X509Certificate[] dest = new X509Certificate[size]; 746 int destPos = 0; 747 for (X509Certificate[] certs : c) { 748 System.arraycopy(certs, 0, dest, destPos, certs.length); 749 destPos += certs.length; 750 } 751 return dest; 752 } 753 754 755 public final String[] getRelatedDeviceRefs() { 756 return relatedDeviceRefs; 757 } 758 759 public void setRelatedDeviceRefs(String... refs) { 760 relatedDeviceRefs = refs; 761 } 762 763 /** 764 * Get device specific vendor configuration information 765 * 766 * @return An Object of the device data. 767 */ 768 public final byte[][] getVendorData() { 769 return vendorData; 770 } 771 772 /** 773 * Set device specific vendor configuration information 774 * 775 * @param vendorData An Object of the device data. 776 */ 777 public void setVendorData(byte[]... vendorData) { 778 this.vendorData = vendorData; 779 } 780 781 /** 782 * Get a boolean to indicate whether this device is presently installed on 783 * the network. (This is useful for pre-configuration, mobile vans, and 784 * similar situations.) 785 * 786 * @return A boolean which will be true if this device is installed. 787 */ 788 public final boolean isInstalled() { 789 return installed; 790 } 791 792 /** 793 * Get a boolean to indicate whether this device is presently installed on 794 * the network. (This is useful for pre-configuration, mobile vans, and 795 * similar situations.) 796 * 797 * @param installed A boolean which will be true if this device is installed. 798 * @throws IOException 799 * @throws GeneralSecurityException 800 * @throws KeyManagementException 801 */ 802 public final void setInstalled(boolean installed) { 803 if (this.installed == installed) 804 return; 805 806 this.installed = installed; 807 needRebindConnections(); 808 } 809 810 public void setTimeZoneOfDevice(TimeZone timeZoneOfDevice) { 811 this.timeZoneOfDevice = timeZoneOfDevice; 812 } 813 814 public TimeZone getTimeZoneOfDevice() { 815 return timeZoneOfDevice; 816 } 817 818 public final void setDimseRQHandler(DimseRQHandler dimseRQHandler) { 819 this.dimseRQHandler = dimseRQHandler; 820 } 821 822 public final DimseRQHandler getDimseRQHandler() { 823 return dimseRQHandler; 824 } 825 826 public final AssociationHandler getAssociationHandler() { 827 return associationHandler; 828 } 829 830 public void setAssociationHandler(AssociationHandler associationHandler) { 831 if (associationHandler == null) 832 throw new NullPointerException(); 833 this.associationHandler = associationHandler; 834 } 835 836 public ConnectionMonitor getConnectionMonitor() { 837 return connectionMonitor; 838 } 839 840 public void setConnectionMonitor(ConnectionMonitor connectionMonitor) { 841 this.connectionMonitor = connectionMonitor; 842 } 843 844 public void bindConnections() throws IOException, GeneralSecurityException { 845 for (Connection con : connections) 846 con.bind(); 847 } 848 849 public void rebindConnections() throws IOException, GeneralSecurityException { 850 for (Connection con : connections) 851 if (con.isRebindNeeded()) 852 con.rebind(); 853 } 854 855 private void needRebindConnections() { 856 for (Connection con : connections) 857 con.needRebind(); 858 } 859 860 861 private void needReconfigureTLS() { 862 for (Connection con : connections) 863 if (con.isTls()) 864 con.needRebind(); 865 sslContext = null; 866 } 867 868 public void unbindConnections() { 869 // the needReconfigureTLS method is cool 870 for (Connection con : connections) 871 con.unbind(); 872 } 873 874 public final Executor getExecutor() { 875 return executor; 876 } 877 878 public final void setExecutor(Executor executor) { 879 this.executor = executor; 880 } 881 882 public final ScheduledExecutorService getScheduledExecutor() { 883 return scheduledExecutor; 884 } 885 886 public final void setScheduledExecutor(ScheduledExecutorService executor) { 887 this.scheduledExecutor = executor; 888 } 889 890 public void addConnection(Connection conn) { 891 conn.setDevice(this); 892 connections.add(conn); 893 conn.needRebind(); 894 } 895 896 public boolean removeConnection(Connection conn) { 897 for (ApplicationEntity ae : getApplicationEntities()) 898 if (ae.getConnections().contains(conn)) 899 throw new IllegalStateException(conn + " used by AE: " + 900 ae.getAETitle()); 901 902 for (DeviceExtension ext : extensions.values()) 903 ext.verifyNotUsed(conn); 904 905 if (!connections.remove(conn)) 906 return false; 907 908 conn.setDevice(null); 909 conn.unbind(); 910 return true; 911 } 912 913 public List<Connection> listConnections() { 914 return Collections.unmodifiableList(connections); 915 } 916 917 public Connection connectionWithEqualsRDN(Connection other) { 918 for (Connection conn : connections) 919 if (conn.equalsRDN(other)) 920 return conn; 921 922 return null; 923 } 924 925 public List<Connection> getConnections() { 926 return connections; 927 } 928 929 public void setConnections(List<Connection> connections) { 930 this.connections.clear(); 931 for (Connection connection : connections) addConnection(connection); 932 } 933 934 public void setApplicationEntitiesMap(Map<String, ApplicationEntity> applicationEntitiesMap) { 935 this.applicationEntitiesMap.clear(); 936 this.aliasApplicationEntitiesMap.clear(); 937 for (Entry<String, ApplicationEntity> entry : applicationEntitiesMap.entrySet()) { 938 addApplicationEntity(entry.getValue()); 939 } 940 } 941 942 /** 943 * This is a low-level access method. Do not use this method to lookup AEs, 944 * use {@link Device#getApplicationEntity(String)} instead - it will also handle aliases and special cases. 945 * 946 * @return 947 */ 948 @Deprecated 949 public Map<String, ApplicationEntity> getApplicationEntitiesMap() { 950 return new HashMap<String, ApplicationEntity>(applicationEntitiesMap); 951 } 952 953 public void addApplicationEntity(ApplicationEntity ae) { 954 ae.setDevice(this); 955 956 applicationEntitiesMap.put(ae.getAETitle(), ae); 957 958 addAllAliasesForApplicationEntity(ae); 959 } 960 961 public ApplicationEntity removeApplicationEntity(ApplicationEntity ae) { 962 return removeApplicationEntity(ae.getAETitle()); 963 } 964 965 public ApplicationEntity removeApplicationEntity(String aet) { 966 ApplicationEntity ae = applicationEntitiesMap.remove(aet); 967 if (ae != null) { 968 ae.setDevice(null); 969 970 removeAllAliasesForApplicationEntity(ae); 971 } 972 973 return ae; 974 } 975 976 private void addAllAliasesForApplicationEntity(ApplicationEntity ae) { 977 aliasApplicationEntitiesMap.put(ae.getAETitle(), ae); 978 for (String aliasAET : ae.getAETitleAliases()) { 979 aliasApplicationEntitiesMap.put(aliasAET, ae); 980 } 981 } 982 983 private void removeAllAliasesForApplicationEntity(ApplicationEntity ae) { 984 aliasApplicationEntitiesMap.remove(ae.getAETitle()); 985 for (String aliasAET : ae.getAETitleAliases()) { 986 aliasApplicationEntitiesMap.remove(aliasAET); 987 } 988 } 989 990 public void setExtensions(Map<Class<? extends DeviceExtension>, DeviceExtension> extensions) { 991 this.extensions = extensions; 992 } 993 994 public Map<Class<? extends DeviceExtension>, DeviceExtension> getExtensions() { 995 return extensions; 996 } 997 998 999 public void addDeviceExtension(DeviceExtension ext) { 1000 Class<? extends DeviceExtension> clazz = ext.getClass(); 1001 if (extensions.containsKey(clazz)) 1002 throw new IllegalStateException( 1003 "already contains Device Extension:" + clazz); 1004 1005 ext.setDevice(this); 1006 extensions.put(clazz, ext); 1007 } 1008 1009 public boolean removeDeviceExtension(DeviceExtension ext) { 1010 if (extensions.remove(ext.getClass()) == null) 1011 return false; 1012 1013 ext.setDevice(null); 1014 return true; 1015 } 1016 1017 public final int getLimitOpenAssociations() { 1018 return limitOpenAssociations; 1019 } 1020 1021 public final void setLimitOpenAssociations(int limit) { 1022 if (limit < 0) 1023 throw new IllegalArgumentException("limit: " + limit); 1024 1025 this.limitOpenAssociations = limit; 1026 } 1027 1028 public int getNumberOfOpenAssociations() { 1029 return assocCount; 1030 } 1031 1032 void incrementNumberOfOpenAssociations() { 1033 synchronized (assocCountLock) { 1034 assocCount++; 1035 } 1036 } 1037 1038 void decrementNumberOfOpenAssociations() { 1039 synchronized (assocCountLock) { 1040 if (--assocCount <= 0) 1041 assocCountLock.notifyAll(); 1042 } 1043 } 1044 1045 public void waitForNoOpenConnections() throws InterruptedException { 1046 synchronized (assocCountLock) { 1047 while (assocCount > 0) 1048 assocCountLock.wait(); 1049 } 1050 } 1051 1052 public boolean isLimitOfOpenAssociationsExceeded() { 1053 return limitOpenAssociations > 0 && getNumberOfOpenAssociations() > limitOpenAssociations; 1054 } 1055 1056 public ApplicationEntity getApplicationEntity(String aet) { 1057 1058 if(aet == null){ 1059 throw new IllegalArgumentException("Application Entity Title (aet) is null"); 1060 } 1061 1062 ApplicationEntity ae = aliasApplicationEntitiesMap.get(aet); 1063 1064 // special fallback: if one ApplicationEntity defines "*" as an alias AET (or even the main AET), it will get used as a fallback for unknown AETs 1065 if (ae == null) 1066 ae = aliasApplicationEntitiesMap.get("*"); 1067 1068 return ae; 1069 } 1070 1071 /** 1072 * @return AE titles of this device, including alias AE titles 1073 */ 1074 public Collection<String> getApplicationAETitles() { 1075 return aliasApplicationEntitiesMap.keySet(); 1076 } 1077 1078 /** 1079 * This is a low-level access method. Do not use this method to lookup AEs, 1080 * use {@link Device#getApplicationEntity(String)} instead - it will also handle aliases and special cases. 1081 * 1082 * @return 1083 */ 1084 public Collection<ApplicationEntity> getApplicationEntities() { 1085 return applicationEntitiesMap.values(); 1086 } 1087 1088 public final void setKeyManager(KeyManager km) { 1089 this.km = km; 1090 needReconfigureTLS(); 1091 } 1092 1093 public final KeyManager getKeyManager() { 1094 return km; 1095 } 1096 1097 private KeyManager km() throws GeneralSecurityException, IOException { 1098 KeyManager ret = km; 1099 if (ret != null || keyStoreURL == null) 1100 return ret; 1101 String keyStorePin = keyStorePin(); 1102 km = ret = SSLManagerFactory.createKeyManager(keyStoreType(), 1103 StringUtils.replaceSystemProperties(keyStoreURL), 1104 keyStorePin(), keyPin(keyStorePin)); 1105 return ret; 1106 } 1107 1108 private String keyStoreType() { 1109 if (keyStoreType == null) 1110 throw new IllegalStateException("keyStoreURL requires keyStoreType"); 1111 1112 return keyStoreType; 1113 } 1114 1115 private String keyStorePin() { 1116 if (keyStorePin != null) 1117 return keyStorePin; 1118 1119 if (keyStorePinProperty == null) 1120 throw new IllegalStateException( 1121 "keyStoreURL requires keyStorePin or keyStorePinProperty"); 1122 1123 String pin = System.getProperty(keyStorePinProperty); 1124 if (pin == null) 1125 throw new IllegalStateException( 1126 "No such keyStorePinProperty: " + keyStorePinProperty); 1127 1128 return pin; 1129 } 1130 1131 private String keyPin(String keyStorePin) { 1132 if (keyStoreKeyPin != null) 1133 return keyStoreKeyPin; 1134 1135 if (keyStoreKeyPinProperty == null) 1136 return keyStorePin; 1137 1138 String pin = System.getProperty(keyStoreKeyPinProperty); 1139 if (pin == null) 1140 throw new IllegalStateException( 1141 "No such keyPinProperty: " + keyStoreKeyPinProperty); 1142 1143 return pin; 1144 } 1145 1146 public final void setTrustManager(TrustManager tm) { 1147 this.tm = tm; 1148 needReconfigureTLS(); 1149 } 1150 1151 public final TrustManager getTrustManager() { 1152 return tm; 1153 } 1154 1155 private TrustManager tm() throws GeneralSecurityException, IOException { 1156 TrustManager ret = tm; 1157 if (ret != null 1158 || trustStoreURL == null && authorizedNodeCertificates.isEmpty()) 1159 return ret; 1160 1161 tm = ret = trustStoreURL != null 1162 ? SSLManagerFactory.createTrustManager(trustStoreType(), 1163 StringUtils.replaceSystemProperties(trustStoreURL), 1164 trustStorePin()) 1165 : SSLManagerFactory.createTrustManager( 1166 getAllAuthorizedNodeCertificates()); 1167 return ret; 1168 } 1169 1170 private String trustStoreType() { 1171 if (trustStoreType == null) 1172 throw new IllegalStateException("trustStoreURL requires trustStoreType"); 1173 1174 return trustStoreType; 1175 } 1176 1177 private String trustStorePin() { 1178 if (trustStorePin != null) 1179 return trustStorePin; 1180 1181 if (trustStorePinProperty == null) 1182 throw new IllegalStateException( 1183 "trustStoreURL requires trustStorePin or trustStorePinProperty"); 1184 1185 String pin = System.getProperty(trustStorePinProperty); 1186 if (pin == null) 1187 throw new IllegalStateException( 1188 "No such trustStorePinProperty: " + trustStorePinProperty); 1189 1190 return pin; 1191 } 1192 1193 SSLContext sslContext() throws GeneralSecurityException, IOException { 1194 SSLContext ctx = sslContext; 1195 if (ctx != null) 1196 return ctx; 1197 1198 sslContext = ctx = createSSLContext(km(), tm()); 1199 return ctx; 1200 } 1201 1202 private static SSLContext createSSLContext(KeyManager km, TrustManager tm) 1203 throws GeneralSecurityException { 1204 SSLContext ctx = SSLContext.getInstance("TLS"); 1205 ctx.init(km != null ? new KeyManager[]{km} : null, 1206 tm != null ? new TrustManager[]{tm} : null, null); 1207 return ctx; 1208 } 1209 1210 public void execute(Runnable command) { 1211 if (executor == null) 1212 throw new IllegalStateException("executer not initalized"); 1213 1214 executor.execute(command); 1215 } 1216 1217 public ScheduledFuture<?> schedule(Runnable command, long delay, 1218 TimeUnit unit) { 1219 if (scheduledExecutor == null) 1220 throw new IllegalStateException( 1221 "scheduled executor service not initalized"); 1222 1223 return scheduledExecutor.schedule(command, delay, unit); 1224 } 1225 1226 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, 1227 long initialDelay, long period, TimeUnit unit) { 1228 if (scheduledExecutor == null) 1229 throw new IllegalStateException( 1230 "scheduled executor service not initalized"); 1231 1232 return scheduledExecutor.scheduleAtFixedRate(command, 1233 initialDelay, period, unit); 1234 } 1235 1236 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, 1237 long initialDelay, long delay, TimeUnit unit) { 1238 if (scheduledExecutor == null) 1239 throw new IllegalStateException( 1240 "scheduled executor service not initalized"); 1241 1242 return scheduledExecutor.scheduleWithFixedDelay(command, 1243 initialDelay, delay, unit); 1244 } 1245 1246 @Override 1247 public String toString() { 1248 return promptTo(new StringBuilder(512), "").toString(); 1249 } 1250 1251 public StringBuilder promptTo(StringBuilder sb, String indent) { 1252 String indent2 = indent + " "; 1253 StringUtils.appendLine(sb, indent, "Device[name: ", deviceName); 1254 StringUtils.appendLine(sb, indent2, "desc: ", description); 1255 StringUtils.appendLine(sb, indent2, "installed: ", installed); 1256 for (Connection conn : connections) 1257 conn.promptTo(sb, indent2).append(StringUtils.LINE_SEPARATOR); 1258 for (ApplicationEntity ae : applicationEntitiesMap.values()) 1259 ae.promptTo(sb, indent2).append(StringUtils.LINE_SEPARATOR); 1260 return sb.append(indent).append(']'); 1261 } 1262 1263 public void reconfigure(Device from) throws IOException, GeneralSecurityException { 1264 setDeviceAttributes(from); 1265 reconfigureConnections(from); 1266 reconfigureApplicationEntities(from); 1267 reconfigureDeviceExtensions(from); 1268 } 1269 1270 protected void setDeviceAttributes(Device from) { 1271 setOlockHash(from.olockHash); 1272 setDescription(from.description); 1273 setManufacturer(from.manufacturer); 1274 setManufacturerModelName(from.manufacturerModelName); 1275 setSoftwareVersions(from.softwareVersions); 1276 setStationName(from.stationName); 1277 setUuid(from.getUuid()); 1278 setDeviceSerialNumber(from.deviceSerialNumber); 1279 setTrustStoreURL(from.trustStoreURL); 1280 setTrustStoreType(from.trustStoreType); 1281 setTrustStorePin(from.trustStorePin); 1282 setKeyStoreURL(from.keyStoreURL); 1283 setKeyStoreType(from.keyStoreType); 1284 setKeyStorePin(from.keyStorePin); 1285 setKeyStoreKeyPin(from.keyStoreKeyPin); 1286 setTimeZoneOfDevice(from.timeZoneOfDevice); 1287 setIssuerOfPatientID(from.issuerOfPatientID); 1288 setIssuerOfAccessionNumber(from.issuerOfAccessionNumber); 1289 setOrderPlacerIdentifier(from.orderPlacerIdentifier); 1290 setOrderFillerIdentifier(from.orderFillerIdentifier); 1291 setIssuerOfAdmissionID(from.issuerOfAdmissionID); 1292 setIssuerOfServiceEpisodeID(from.issuerOfServiceEpisodeID); 1293 setIssuerOfContainerIdentifier(from.issuerOfContainerIdentifier); 1294 setIssuerOfSpecimenIdentifier(from.issuerOfSpecimenIdentifier); 1295 setInstitutionNames(from.institutionNames); 1296 setInstitutionCodes(from.institutionCodes); 1297 setInstitutionAddresses(from.institutionAddresses); 1298 setInstitutionalDepartmentNames(from.institutionalDepartmentNames); 1299 setPrimaryDeviceTypes(from.primaryDeviceTypes); 1300 setRelatedDeviceRefs(from.relatedDeviceRefs); 1301 setAuthorizedNodeCertificates(from.authorizedNodeCertificates); 1302 setThisNodeCertificates(from.thisNodeCertificates); 1303 setVendorData(from.vendorData); 1304 setLimitOpenAssociations(from.limitOpenAssociations); 1305 setInstalled(from.installed); 1306 setDefaultAE(from.getDefaultAE()); 1307 } 1308 1309 private void setAuthorizedNodeCertificates(Map<String, X509Certificate[]> from) { 1310 if (update(authorizedNodeCertificates, from)) 1311 setTrustManager(null); 1312 } 1313 1314 private void setThisNodeCertificates(Map<String, X509Certificate[]> from) { 1315 update(thisNodeCertificates, from); 1316 } 1317 1318 private boolean update(Map<String, X509Certificate[]> target, 1319 Map<String, X509Certificate[]> from) { 1320 boolean updated = target.keySet().retainAll(from.keySet()); 1321 for (Entry<String, X509Certificate[]> e : from.entrySet()) { 1322 String key = e.getKey(); 1323 X509Certificate[] value = e.getValue(); 1324 X509Certificate[] certs = target.get(key); 1325 if (certs == null || !Arrays.equals(value, certs)) { 1326 target.put(key, value); 1327 updated = true; 1328 } 1329 } 1330 return updated; 1331 } 1332 1333 private void reconfigureConnections(Device from) { 1334 Iterator<Connection> connIter = connections.iterator(); 1335 while (connIter.hasNext()) { 1336 Connection conn = connIter.next(); 1337 if (from.connectionWithEqualsRDN(conn) == null) { 1338 connIter.remove(); 1339 conn.setDevice(null); 1340 conn.unbind(); 1341 } 1342 } 1343 for (Connection src : from.connections) { 1344 Connection conn = connectionWithEqualsRDN(src); 1345 if (conn == null) 1346 this.addConnection(conn = new Connection()); 1347 conn.reconfigure(src); 1348 } 1349 } 1350 1351 private void reconfigureApplicationEntities(Device from) { 1352 applicationEntitiesMap.keySet().retainAll(from.applicationEntitiesMap.keySet()); 1353 for (ApplicationEntity src : from.applicationEntitiesMap.values()) { 1354 ApplicationEntity ae = applicationEntitiesMap.get(src.getAETitle()); 1355 if (ae == null) 1356 addApplicationEntity(ae = new ApplicationEntity(src.getAETitle())); 1357 ae.reconfigure(src); 1358 } 1359 1360 aliasApplicationEntitiesMap.clear(); 1361 for (ApplicationEntity ae : applicationEntitiesMap.values()) { 1362 addAllAliasesForApplicationEntity(ae); 1363 } 1364 } 1365 1366 public void reconfigureConnections(List<Connection> conns, 1367 List<Connection> src) { 1368 conns.clear(); 1369 for (Connection conn : src) 1370 conns.add(connectionWithEqualsRDN(conn)); 1371 } 1372 1373 private void reconfigureDeviceExtensions(Device from) { 1374 for (Iterator<Class<? extends DeviceExtension>> it = 1375 extensions.keySet().iterator(); it.hasNext(); ) { 1376 if (!from.extensions.containsKey(it.next())) 1377 it.remove(); 1378 } 1379 for (DeviceExtension src : from.extensions.values()) { 1380 Class<? extends DeviceExtension> clazz = src.getClass(); 1381 DeviceExtension ext = extensions.get(clazz); 1382 if (ext == null) 1383 try { 1384 addDeviceExtension(ext = clazz.newInstance()); 1385 } catch (Exception e) { 1386 throw new RuntimeException( 1387 "Failed to instantiate " + clazz.getName(), e); 1388 } 1389 ext.reconfigure(src); 1390 } 1391 } 1392 1393 public Collection<DeviceExtension> listDeviceExtensions() { 1394 return extensions.values(); 1395 } 1396 1397 @SuppressWarnings("unchecked") 1398 public <T extends DeviceExtension> T getDeviceExtension(Class<T> clazz) { 1399 return (T) extensions.get(clazz); 1400 } 1401 1402 public <T extends DeviceExtension> T getDeviceExtensionNotNull(Class<T> clazz) { 1403 T devExt = getDeviceExtension(clazz); 1404 if (devExt == null) 1405 throw new IllegalStateException("No " + clazz.getName() + " configured for Device: " + deviceName); 1406 return devExt; 1407 } 1408 1409 public Collection<ApplicationEntity> getAEsSupportingTransferCapability( 1410 TransferCapability transferCapability, boolean onlyAbstractSyntax) { 1411 ArrayList<ApplicationEntity> aes = new ArrayList<ApplicationEntity>(); 1412 for (ApplicationEntity ae : this.getApplicationEntities()) { 1413 if (ae.supportsTransferCapability(transferCapability, 1414 onlyAbstractSyntax)) 1415 aes.add(ae); 1416 } 1417 return aes; 1418 } 1419 1420 public ApplicationEntity getApplicationEntityNotNull(String aet) { 1421 ApplicationEntity applicationEntity = getApplicationEntity(aet); 1422 if (applicationEntity == null) 1423 throw new IllegalArgumentException("Device " + deviceName + " does not contain AET " + aet); 1424 return applicationEntity; 1425 } 1426 1427 public String getUuid() { 1428 return uuid; 1429 } 1430 1431 public void setUuid(String uuid) { 1432 this.uuid = uuid; 1433 } 1434}