From 4cbecb53b7f2229914150c0b1291c14effc8cfbe Mon Sep 17 00:00:00 2001 From: Gunter Zeilinger Date: Thu, 11 Apr 2024 13:59:37 +0200 Subject: [PATCH] Share Transfer Capabilities from other Network AE of the same Device fix #1419 --- .../src/main/resources/dcmNetworkAE.schema.json | 8 +++++++- .../dcm4che3/conf/json/JsonConfiguration.java | 5 +++++ .../main/resources/ldap/apacheds/dcm4che.ldif | 15 ++++++++++++++- .../main/resources/ldap/opendj/11-dcm4che.ldif | 6 ++++++ .../main/resources/ldap/schema/dcm4che.schema | 8 ++++++++ .../resources/ldap/slapd/dcm4che-modify.ldif | 6 ++++++ .../src/main/resources/ldap/slapd/dcm4che.ldif | 6 ++++++ .../conf/ldap/LdapDicomConfiguration.java | 7 +++++++ .../org/dcm4che3/net/ApplicationEntity.java | 17 ++++++++++++++++- .../org/dcm4che3/net/AssociationHandler.java | 9 ++++++--- 10 files changed, 81 insertions(+), 6 deletions(-) diff --git a/dcm4che-conf/dcm4che-conf-json-schema/src/main/resources/dcmNetworkAE.schema.json b/dcm4che-conf/dcm4che-conf-json-schema/src/main/resources/dcmNetworkAE.schema.json index dc77366b11..ada544b9b2 100644 --- a/dcm4che-conf/dcm4che-conf-json-schema/src/main/resources/dcmNetworkAE.schema.json +++ b/dcm4che-conf/dcm4che-conf-json-schema/src/main/resources/dcmNetworkAE.schema.json @@ -42,7 +42,7 @@ } }, "dcmPreferredTransferSyntax": { - "title": "PreferredTransferSyntax", + "title": "Preferred Transfer Syntax", "description": "Preferred Transfer Syntax for selection of Transfer Syntax within a Presentation Context, ordered by priority. If absent, the first acceptable Transfer Syntax in the offered Presentation Context will be selected. May be overwritten by configured values for particular Transfer Capabilities of this AE.", "type": "array", "format": "dcmTransferSyntax", @@ -50,6 +50,12 @@ "type": "string" } }, + "dcmShareTransferCapabilitiesFromAETitle": { + "title": "Share Transfer Capabilities from AE Title", + "description": "Indicates that this Network AE supports the Transfer Capabilities specified for another Network AE of the same Device.", + "type": "string", + "format": "dcmArchiveAETitle" + }, "hl7ApplicationName": { "title": "HL7 Application name", "description": "HL7 Application and Facility name (Application|Facility) associated with this AE", diff --git a/dcm4che-conf/dcm4che-conf-json/src/main/java/org/dcm4che3/conf/json/JsonConfiguration.java b/dcm4che-conf/dcm4che-conf-json/src/main/java/org/dcm4che3/conf/json/JsonConfiguration.java index 1d4a4b77fe..25ef0ed58b 100644 --- a/dcm4che-conf/dcm4che-conf-json/src/main/java/org/dcm4che3/conf/json/JsonConfiguration.java +++ b/dcm4che-conf/dcm4che-conf-json/src/main/java/org/dcm4che3/conf/json/JsonConfiguration.java @@ -625,6 +625,8 @@ private void writeTo(ApplicationEntity ae, List conns, JsonWriter wr writer.writeNotEmpty("dcmOtherAETitle", ae.getOtherAETitles()); writer.writeNotEmpty("dcmNoAsyncModeCalledAETitle", ae.getNoAsyncModeCalledAETitles()); writer.writeNotEmpty("dcmMasqueradeCallingAETitle", ae.getMasqueradeCallingAETitles()); + writer.writeNotNullOrDef("dcmShareTransferCapabilitiesFromAETitle", + ae.getShareTransferCapabilitiesFromAETitle(), null); writer.writeNotNullOrDef("hl7ApplicationName", ae.getHl7ApplicationName(), null); for (JsonConfigurationExtension ext : extensions) ext.storeTo(ae, writer); @@ -704,6 +706,9 @@ private void loadFrom(ApplicationEntity ae, JsonReader reader, Device device, Co case "dcmMasqueradeCallingAETitle": ae.setMasqueradeCallingAETitles(reader.stringArray()); break; + case "dcmShareTransferCapabilitiesFromAETitle": + ae.setShareTransferCapabilitiesFromAETitle(reader.stringValue()); + break; case "hl7ApplicationName": ae.setHl7ApplicationName(reader.stringValue()); break; diff --git a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/apacheds/dcm4che.ldif b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/apacheds/dcm4che.ldif index dcb917f4ae..8216a61568 100644 --- a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/apacheds/dcm4che.ldif +++ b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/apacheds/dcm4che.ldif @@ -1,4 +1,4 @@ -# Generated by Apache Directory Studio on April 26, 2023, 4:28:51 PM +# Generated by Apache Directory Studio on April 11, 2024, 1:51:06 PM # SCHEMA "DCM4CHE" dn: cn=dcm4che, ou=schema @@ -906,6 +906,18 @@ m-equality: caseExactIA5Match m-syntax: 1.3.6.1.4.1.1466.115.121.1.26 m-singleValue: TRUE +dn: m-oid=1.2.40.0.13.1.15.0.3.87, ou=attributetypes, cn=dcm4che, ou=schema +objectclass: metaAttributeType +objectclass: metaTop +objectclass: top +m-oid: 1.2.40.0.13.1.15.0.3.87 +m-name: dcmShareTransferCapabilitiesFromAETitle +m-description: Indicates that this Network AE supports the Transfer Capabilities + specified for another Network AE of the same Device. +m-equality: caseExactIA5Match +m-syntax: 1.3.6.1.4.1.1466.115.121.1.26 +m-singleValue: TRUE + dn: m-oid=1.2.40.0.13.1.15.1.3.1, ou=attributetypes, cn=dcm4che, ou=schema objectclass: metaAttributeType objectclass: metaTop @@ -1548,6 +1560,7 @@ m-may: dcmOtherAETitle m-may: dcmNoAsyncModeCalledAETitle m-may: dcmMasqueradeCallingAETitle m-may: dcmPreferredTransferSyntax +m-may: dcmShareTransferCapabilitiesFromAETitle m-may: hl7ApplicationName dn: m-oid=1.2.40.0.13.1.15.0.4.6, ou=objectclasses, cn=dcm4che, ou=schema diff --git a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/opendj/11-dcm4che.ldif b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/opendj/11-dcm4che.ldif index 3d5fa5931b..57eb3bece6 100644 --- a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/opendj/11-dcm4che.ldif +++ b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/opendj/11-dcm4che.ldif @@ -382,6 +382,11 @@ attributeTypes: ( 1.2.40.0.13.1.15.0.3.86 NAME 'dcmTLSEndpointIdentificationAlgo EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +attributeTypes: ( 1.2.40.0.13.1.15.0.3.87 NAME 'dcmShareTransferCapabilitiesFromAETitle' + DESC 'Indicates that this Network AE supports the Transfer Capabilities specified for another Network AE of the same Device.' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) attributeTypes: ( 1.2.40.0.13.1.15.1.3.1 NAME 'dcmAuditSourceID' DESC 'DICOM PS 3.15 A.5 Audit Source ID; device name if absent' EQUALITY caseIgnoreMatch @@ -614,6 +619,7 @@ objectClasses: ( 1.2.40.0.13.1.15.0.4.5 NAME 'dcmNetworkAE' dcmNoAsyncModeCalledAETitle $ dcmMasqueradeCallingAETitle $ dcmPreferredTransferSyntax $ + dcmShareTransferCapabilitiesFromAETitle $ hl7ApplicationName ) ) objectClasses: ( 1.2.40.0.13.1.15.0.4.6 NAME 'dcmNetworkConnection' DESC 'Extended DICOM Network Connection information' diff --git a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/schema/dcm4che.schema b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/schema/dcm4che.schema index 472b803398..57172702cb 100644 --- a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/schema/dcm4che.schema +++ b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/schema/dcm4che.schema @@ -456,6 +456,13 @@ attributetype ( 1.2.40.0.13.1.15.0.3.86 NAME 'dcmTLSEndpointIdentificationAlgori SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +attributetype ( 1.2.40.0.13.1.15.0.3.87 NAME 'dcmShareTransferCapabilitiesFromAETitle' + DESC 'Indicates that this Network AE supports the Transfer Capabilities specified for another Network AE of the same Device.' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) + + attributetype ( 1.2.40.0.13.1.15.1.3.1 NAME 'dcmAuditSourceID' DESC 'DICOM PS 3.15 A.5 Audit Source ID; device name if absent' EQUALITY caseIgnoreMatch @@ -732,6 +739,7 @@ objectclass ( 1.2.40.0.13.1.15.0.4.5 NAME 'dcmNetworkAE' dcmNoAsyncModeCalledAETitle $ dcmMasqueradeCallingAETitle $ dcmPreferredTransferSyntax $ + dcmShareTransferCapabilitiesFromAETitle $ hl7ApplicationName ) ) objectclass ( 1.2.40.0.13.1.15.0.4.6 NAME 'dcmNetworkConnection' diff --git a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che-modify.ldif b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che-modify.ldif index a8a9b8c6d0..3ed8a13bee 100644 --- a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che-modify.ldif +++ b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che-modify.ldif @@ -382,6 +382,11 @@ olcAttributeTypes: ( 1.2.40.0.13.1.15.0.3.86 NAME 'dcmTLSEndpointIdentificationA EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.0.3.87 NAME 'dcmShareTransferCapabilitiesFromAETitle' + DESC 'Indicates that this Network AE supports the Transfer Capabilities specified for another Network AE of the same Device.' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) olcAttributeTypes: ( 1.2.40.0.13.1.15.1.3.1 NAME 'dcmAuditSourceID' DESC 'DICOM PS 3.15 A.5 Audit Source ID; device name if absent' EQUALITY caseIgnoreMatch @@ -616,6 +621,7 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.0.4.5 NAME 'dcmNetworkAE' dcmNoAsyncModeCalledAETitle $ dcmMasqueradeCallingAETitle $ dcmPreferredTransferSyntax $ + dcmShareTransferCapabilitiesFromAETitle $ hl7ApplicationName ) ) olcObjectClasses: ( 1.2.40.0.13.1.15.0.4.6 NAME 'dcmNetworkConnection' DESC 'Extended DICOM Network Connection information' diff --git a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che.ldif b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che.ldif index 1c836ffbae..d605c3b1d0 100644 --- a/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che.ldif +++ b/dcm4che-conf/dcm4che-conf-ldap-schema/src/main/resources/ldap/slapd/dcm4che.ldif @@ -381,6 +381,11 @@ olcAttributeTypes: ( 1.2.40.0.13.1.15.0.3.86 NAME 'dcmTLSEndpointIdentificationA EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.0.3.87 NAME 'dcmShareTransferCapabilitiesFromAETitle' + DESC 'Indicates that this Network AE supports the Transfer Capabilities specified for another Network AE of the same Device.' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) olcAttributeTypes: ( 1.2.40.0.13.1.15.1.3.1 NAME 'dcmAuditSourceID' DESC 'DICOM PS 3.15 A.5 Audit Source ID; device name if absent' EQUALITY caseIgnoreMatch @@ -613,6 +618,7 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.0.4.5 NAME 'dcmNetworkAE' dcmNoAsyncModeCalledAETitle $ dcmMasqueradeCallingAETitle $ dcmPreferredTransferSyntax $ + dcmShareTransferCapabilitiesFromAETitle $ hl7ApplicationName ) ) olcObjectClasses: ( 1.2.40.0.13.1.15.0.4.6 NAME 'dcmNetworkConnection' DESC 'Extended DICOM Network Connection information' diff --git a/dcm4che-conf/dcm4che-conf-ldap/src/main/java/org/dcm4che3/conf/ldap/LdapDicomConfiguration.java b/dcm4che-conf/dcm4che-conf-ldap/src/main/java/org/dcm4che3/conf/ldap/LdapDicomConfiguration.java index ad613d265a..ccde9f5be8 100644 --- a/dcm4che-conf/dcm4che-conf-ldap/src/main/java/org/dcm4che3/conf/ldap/LdapDicomConfiguration.java +++ b/dcm4che-conf/dcm4che-conf-ldap/src/main/java/org/dcm4che3/conf/ldap/LdapDicomConfiguration.java @@ -1125,6 +1125,8 @@ private Attributes storeTo(ConfigurationChanges.ModifiedObject ldapObj, Applicat ae.getRoleSelectionNegotiationLenient(), null); LdapUtils.storeNotEmpty(ldapObj, attrs, "dcmPreferredTransferSyntax", LdapUtils.addOrdinalPrefix(ae.getPreferredTransferSyntaxes())); + LdapUtils.storeNotNullOrDef(ldapObj, attrs, "dcmShareTransferCapabilitiesFromAETitle", + ae.getShareTransferCapabilitiesFromAETitle(), null); LdapUtils.storeNotNullOrDef(ldapObj, attrs, "hl7ApplicationName", ae.getHl7ApplicationName(), null); LdapUtils.storeNotEmpty(ldapObj, attrs, "dcmAcceptedCallingAETitle", ae.getAcceptedCallingAETitles()); LdapUtils.storeNotEmpty(ldapObj, attrs, "dcmOtherAETitle", ae.getOtherAETitles()); @@ -1630,6 +1632,8 @@ private void loadFrom(ApplicationEntity ae, Attributes attrs) throws NamingExcep ae.setOtherAETitles(LdapUtils.stringArray(attrs.get("dcmOtherAETitle"))); ae.setNoAsyncModeCalledAETitles(LdapUtils.stringArray(attrs.get("dcmNoAsyncModeCalledAETitle"))); ae.setMasqueradeCallingAETitles(LdapUtils.stringArray(attrs.get("dcmMasqueradeCallingAETitle"))); + ae.setShareTransferCapabilitiesFromAETitle(LdapUtils.stringValue( + attrs.get("dcmShareTransferCapabilitiesFromAETitle"), null)); ae.setHl7ApplicationName(LdapUtils.stringValue(attrs.get("hl7ApplicationName"), null)); for (LdapDicomConfigurationExtension ext : extensions) ext.loadFrom(ae, attrs); @@ -2039,6 +2043,9 @@ private List storeDiffs(ConfigurationChanges.ModifiedObject ld LdapUtils.storeDiff(ldapObj, mods, "dcmMasqueradeCallingAETitle", a.getMasqueradeCallingAETitles(), b.getMasqueradeCallingAETitles()); + LdapUtils.storeDiffObject(ldapObj, mods, "dcmShareTransferCapabilitiesFromAETitle", + a.getShareTransferCapabilitiesFromAETitle(), + b.getShareTransferCapabilitiesFromAETitle(), null); LdapUtils.storeDiffObject(ldapObj, mods, "hl7ApplicationName", a.getHl7ApplicationName(), b.getHl7ApplicationName(), null); diff --git a/dcm4che-net/src/main/java/org/dcm4che3/net/ApplicationEntity.java b/dcm4che-net/src/main/java/org/dcm4che3/net/ApplicationEntity.java index 4ea0c675ef..5502a8e61e 100644 --- a/dcm4che-net/src/main/java/org/dcm4che3/net/ApplicationEntity.java +++ b/dcm4che-net/src/main/java/org/dcm4che3/net/ApplicationEntity.java @@ -45,7 +45,6 @@ import java.util.*; import org.dcm4che3.data.Attributes; -import org.dcm4che3.data.Implementation; import org.dcm4che3.net.pdu.AAbort; import org.dcm4che3.net.pdu.AAssociateAC; import org.dcm4che3.net.pdu.AAssociateRQ; @@ -91,6 +90,7 @@ public class ApplicationEntity implements Serializable { private boolean initiator = true; private Boolean installed; private Boolean roleSelectionNegotiationLenient; + private String shareTransferCapabilitiesFromAETitle; private String hl7ApplicationName; private final LinkedHashSet acceptedCallingAETs = new LinkedHashSet<>(); private final LinkedHashSet otherAETs = new LinkedHashSet<>(); @@ -448,6 +448,20 @@ public void setRoleSelectionNegotiationLenient(Boolean roleSelectionNegotiationL this.roleSelectionNegotiationLenient = roleSelectionNegotiationLenient; } + public String getShareTransferCapabilitiesFromAETitle() { + return shareTransferCapabilitiesFromAETitle; + } + + public void setShareTransferCapabilitiesFromAETitle(String shareTransferCapabilitiesFromAETitle) { + this.shareTransferCapabilitiesFromAETitle = shareTransferCapabilitiesFromAETitle; + } + + ApplicationEntity transferCapabilitiesAE() { + return shareTransferCapabilitiesFromAETitle != null + ? device.getApplicationEntity(shareTransferCapabilitiesFromAETitle) + : this; + } + public String getHl7ApplicationName() { return hl7ApplicationName; } @@ -765,6 +779,7 @@ protected void setApplicationEntityAttributes(ApplicationEntity from) { masqueradeCallingAETs.putAll(from.masqueradeCallingAETs); supportedCharacterSets = from.supportedCharacterSets; prefTransferSyntaxes = from.prefTransferSyntaxes; + shareTransferCapabilitiesFromAETitle = from.shareTransferCapabilitiesFromAETitle; hl7ApplicationName = from.hl7ApplicationName; acceptor = from.acceptor; initiator = from.initiator; diff --git a/dcm4che-net/src/main/java/org/dcm4che3/net/AssociationHandler.java b/dcm4che-net/src/main/java/org/dcm4che3/net/AssociationHandler.java index b71ee08cc3..f291e58776 100644 --- a/dcm4che-net/src/main/java/org/dcm4che3/net/AssociationHandler.java +++ b/dcm4che-net/src/main/java/org/dcm4che3/net/AssociationHandler.java @@ -48,7 +48,6 @@ import org.dcm4che3.net.pdu.AAssociateRQ; import org.dcm4che3.net.pdu.PresentationContext; import org.dcm4che3.net.pdu.UserIdentityAC; -import org.dcm4che3.util.ByteUtils; /** * @author Gunter Zeilinger @@ -108,9 +107,13 @@ protected AAssociateAC makeAAssociateAC(Association as, AAssociateRQ rq, ac.setMaxOpsPerformed(Association.minZeroAsMax(rq.getMaxOpsPerformed(), conn.getMaxOpsInvoked())); ac.setUserIdentityAC(userIdentity); - ApplicationEntity ae = as.getApplicationEntity(); + ApplicationEntity ae = as.getApplicationEntity().transferCapabilitiesAE(); for (PresentationContext rqpc : rq.getPresentationContexts()) - ac.addPresentationContext(ae.negotiate(rq, ac, rqpc)); + ac.addPresentationContext(ae != null + ? ae.negotiate(rq, ac, rqpc) + : new PresentationContext(rqpc.getPCID(), + PresentationContext.ABSTRACT_SYNTAX_NOT_SUPPORTED, + rqpc.getTransferSyntax())); return ac; }