|
From: <gun...@us...> - 2008-05-15 13:06:31
|
Revision: 6312
http://dcm4che.svn.sourceforge.net/dcm4che/?rev=6312&view=rev
Author: gunterze
Date: 2008-05-15 06:06:29 -0700 (Thu, 15 May 2008)
Log Message:
-----------
[#DCMEE-836] QRService: Support retrieve by C-GET
Modified Paths:
--------------
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/etc/conf/xmdesc/dcm4chee-qrscp-xmbean.xml
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/AbstractScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/gpwlscp/GPWLScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/hpscp/HPScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mppsscp/MPPSScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mwlscp/MWLFindScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveScp.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveTask.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QueryRetrieveScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/stgcmt/StgCmtScuScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/storescp/StoreScpService.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/stymgt/StudyMgtScpService.java
Added Paths:
-----------
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetScp.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetTask.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QRLevel.java
dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/StudyInstanceUIDAndDirPath.java
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/etc/conf/xmdesc/dcm4chee-qrscp-xmbean.xml
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/etc/conf/xmdesc/dcm4chee-qrscp-xmbean.xml 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/etc/conf/xmdesc/dcm4chee-qrscp-xmbean.xml 2008-05-15 13:06:29 UTC (rev 6312)
@@ -340,10 +340,13 @@
List of accepted Query/Retrieve SOP Classes. Use either the SOP Class
UID value, or following defined symbolic names:
PatientRootQueryRetrieveInformationModelFIND,
+ PatientRootQueryRetrieveInformationModelGET
PatientRootQueryRetrieveInformationModelMOVE,
StudyRootQueryRetrieveInformationModelFIND,
+ StudyRootQueryRetrieveInformationModelGET
StudyRootQueryRetrieveInformationModelMOVE,
PatientStudyOnlyQueryRetrieveInformationModelFIND,
+ PatientStudyOnlyQueryRetrieveInformationModelGET
PatientStudyOnlyQueryRetrieveInformationModelMOVE,
]]>
</description>
@@ -352,10 +355,13 @@
<descriptors>
<value value="
PatientRootQueryRetrieveInformationModelFIND
+ PatientRootQueryRetrieveInformationModelGET
PatientRootQueryRetrieveInformationModelMOVE
StudyRootQueryRetrieveInformationModelFIND
+ StudyRootQueryRetrieveInformationModelGET
StudyRootQueryRetrieveInformationModelMOVE
PatientStudyOnlyQueryRetrieveInformationModelFIND
+ PatientStudyOnlyQueryRetrieveInformationModelGET
PatientStudyOnlyQueryRetrieveInformationModelMOVE
"/>
</descriptors>
@@ -501,29 +507,29 @@
</descriptors>
</attribute>
<attribute access="read-write"
- getMethod="isSendPendingMoveRSP"
- setMethod="setSendPendingMoveRSP">
- <description><![CDATA[Flag indicating if optional C-Move Response DICOM
+ getMethod="isSendPendingRetrieveRSP"
+ setMethod="setSendPendingRetrieveRSP">
+ <description><![CDATA[Flag indicating if optional C-Move/C-Get Response DICOM
message with pending status are returned in the interval defined by
- attribute <i>PendingMoveRSPInterval</i>.]]>
+ attribute <i>PendingRetrieveResponseInterval</i>.]]>
</description>
- <name>SendPendingMoveResponse</name>
+ <name>SendPendingRetrieveResponse</name>
<type>boolean</type>
<descriptors>
<value value="true"/>
</descriptors>
</attribute>
<attribute access="read-write"
- getMethod="getPendingMoveRSPInterval"
- setMethod="setPendingMoveRSPInterval">
- <description><![CDATA[Interval in ms in which C-Move Response DICOM
+ getMethod="getPendingRetrieveRSPInterval"
+ setMethod="setPendingRetrieveRSPInterval">
+ <description><![CDATA[Interval in ms in which C-Move/C-Get Response DICOM
message with pending status are returned. Even send (identical) response
messages, if no storage sub-operation was performed during such interval,
- to keep the association alive. Only effective, if sending C-Move Response
+ to keep the association alive. Only effective, if sending C-Move/C-Get Response
DICOM message with pending status is activated by attribute
- <i>SendPendingMoveResponse</i>.]]>
+ <i>SendPendingRetrieveResponse</i>.]]>
</description>
- <name>PendingMoveRSPInterval</name>
+ <name>PendingRetrieveResponseInterval</name>
<type>long</type>
<descriptors>
<value value="5000"/>
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/AbstractScpService.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/AbstractScpService.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/AbstractScpService.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -417,7 +417,7 @@
}
policy1.setMaxPDULength(maxPDULength);
policy1.setAsyncOpsWindow(maxOpsInvoked, maxOpsPerformed);
- updatePresContexts(policy1, true);
+ enablePresContexts(policy1);
}
return changed;
}
@@ -473,7 +473,7 @@
AcceptorPolicy policy1 = policy
.getPolicyForCalledAET(calledAETs[i]);
if (policy1 != null) {
- updatePresContexts(policy1, false);
+ disablePresContexts(policy1);
if (policy1.listPresContext().isEmpty()) {
policy.putPolicyForCalledAET(calledAETs[i], null);
policy.removeCalledAET(calledAETs[i]);
@@ -621,9 +621,10 @@
protected abstract void unbindDcmServices(DcmServiceRegistry services);
- protected abstract void updatePresContexts(AcceptorPolicy policy,
- boolean enable);
+ protected abstract void enablePresContexts(AcceptorPolicy policy);
+ protected abstract void disablePresContexts(AcceptorPolicy policy);
+
protected void putPresContexts(AcceptorPolicy policy, String[] cuids,
String[] tsuids) {
for (int i = 0; i < cuids.length; i++) {
@@ -638,6 +639,12 @@
}
}
+ protected void removeRoleSelections(AcceptorPolicy policy, String[] cuids) {
+ for (int i = 0; i < cuids.length; i++) {
+ policy.removeRoleSelection(cuids[i]);
+ }
+ }
+
public File getLogFile(Date now, String callingAET, String suffix) {
File dir = new File(logDir, callingAET);
dir.mkdir();
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/gpwlscp/GPWLScpService.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/gpwlscp/GPWLScpService.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/gpwlscp/GPWLScpService.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -150,8 +150,12 @@
services.unbind(UIDs.GeneralPurposePerformedProcedureStepSOPClass);
}
- protected void updatePresContexts(AcceptorPolicy policy, boolean enable) {
+ protected void enablePresContexts(AcceptorPolicy policy) {
putPresContexts(policy, valuesToStringArray(cuidMap),
- enable ? valuesToStringArray(tsuidMap) : null);
+ valuesToStringArray(tsuidMap));
}
+
+ protected void disablePresContexts(AcceptorPolicy policy) {
+ putPresContexts(policy, valuesToStringArray(cuidMap),null);
+ }
}
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/hpscp/HPScpService.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/hpscp/HPScpService.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/hpscp/HPScpService.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -223,10 +223,16 @@
services.unbind(UIDs.HangingProtocolInformationModelMOVE);
}
- protected void updatePresContexts(AcceptorPolicy policy, boolean enable) {
- String[] tsuids = enable ? valuesToStringArray(tsuidMap) : null;
+ protected void enablePresContexts(AcceptorPolicy policy) {
+ String[] tsuids = valuesToStringArray(tsuidMap);
policy.putPresContext(UIDs.HangingProtocolStorage, tsuids);
policy.putPresContext(UIDs.HangingProtocolInformationModelFIND, tsuids);
policy.putPresContext(UIDs.HangingProtocolInformationModelMOVE, tsuids);
}
+
+ protected void disablePresContexts(AcceptorPolicy policy) {
+ policy.putPresContext(UIDs.HangingProtocolStorage, null);
+ policy.putPresContext(UIDs.HangingProtocolInformationModelFIND, null);
+ policy.putPresContext(UIDs.HangingProtocolInformationModelMOVE, null);
+ }
}
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mppsscp/MPPSScpService.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mppsscp/MPPSScpService.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mppsscp/MPPSScpService.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -104,11 +104,15 @@
services.unbind(UIDs.ModalityPerformedProcedureStep);
}
- protected void updatePresContexts(AcceptorPolicy policy, boolean enable) {
+ protected void enablePresContexts(AcceptorPolicy policy) {
policy.putPresContext(UIDs.ModalityPerformedProcedureStep,
- enable ? valuesToStringArray(tsuidMap) : null);
+ valuesToStringArray(tsuidMap));
}
+ protected void disablePresContexts(AcceptorPolicy policy) {
+ policy.putPresContext(UIDs.ModalityPerformedProcedureStep, null);
+ }
+
void sendMPPSNotification(Dataset ds, String eventType) {
long eventID = super.getNextNotificationSequenceNumber();
Notification notif = new Notification(eventType, this, eventID);
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mwlscp/MWLFindScpService.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mwlscp/MWLFindScpService.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/mwlscp/MWLFindScpService.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -162,11 +162,15 @@
services.unbind(UIDs.ModalityWorklistInformationModelFIND);
}
- protected void updatePresContexts(AcceptorPolicy policy, boolean enable) {
+ protected void enablePresContexts(AcceptorPolicy policy) {
policy.putPresContext(UIDs.ModalityWorklistInformationModelFIND,
- enable ? valuesToStringArray(tsuidMap) : null);
+ valuesToStringArray(tsuidMap));
}
+ protected void disablePresContexts(AcceptorPolicy policy) {
+ policy.putPresContext(UIDs.ModalityWorklistInformationModelFIND, null);
+ }
+
private MWLManagerHome getMWLManagerHome() throws HomeFactoryException {
return (MWLManagerHome) EJBHomeFactory.getFactory().lookup(
MWLManagerHome.class, MWLManagerHome.JNDI_NAME);
Added: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetScp.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetScp.java (rev 0)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetScp.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -0,0 +1,162 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
+ * Java(TM), available at http://sourceforge.net/projects/dcm4che.
+ *
+ * The Initial Developer of the Original Code is
+ * Agfa HealthCare.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * See listed authors below.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.dcm4chex.archive.dcm.qrscp;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.dcm4che.data.Command;
+import org.dcm4che.data.Dataset;
+import org.dcm4che.dict.Status;
+import org.dcm4che.dict.UIDs;
+import org.dcm4che.net.ActiveAssociation;
+import org.dcm4che.net.Association;
+import org.dcm4che.net.DcmServiceBase;
+import org.dcm4che.net.DcmServiceException;
+import org.dcm4che.net.Dimse;
+import org.dcm4chex.archive.ejb.interfaces.StudyPermissionDTO;
+import org.dcm4chex.archive.ejb.interfaces.StudyPermissionManager;
+import org.dcm4chex.archive.ejb.jdbc.FileInfo;
+import org.dcm4chex.archive.ejb.jdbc.RetrieveCmd;
+import org.jboss.logging.Logger;
+
+/**
+ * @author Gunter Zeilinger <gun...@gm...>
+ * @version $Revision$ $Date$
+ * @since May 6, 2008
+ */
+public class GetScp extends DcmServiceBase {
+
+ private static final int MISSING_USER_ID_OF_STORE_SCP_ERR_STATUS = 0xCE12;
+
+ private static final int NO_READ_PERMISSION_ERR_STATUS = 0xCE20;
+
+ private static final String MISSING_USER_ID_OF_STORE_SCP_ERR_MSG =
+ "Missing or invalid user identification of retrieve destination";
+
+ private static final String NO_READ_PERMISSION_ERR_MSG =
+ "Retrieve destination has no permission to read Study";
+
+ protected final QueryRetrieveScpService service;
+
+ private final Logger log;
+
+ public GetScp(QueryRetrieveScpService service) {
+ this.service = service;
+ this.log = service.getLog();
+ }
+
+ @Override
+ public void c_get(ActiveAssociation assoc, Dimse rq) throws IOException {
+ int pcid = rq.pcid();
+ Command rqCmd = rq.getCommand();
+ Association a = assoc.getAssociation();
+ try {
+ Dataset rqData = rq.getDataset();
+ if(log.isDebugEnabled()) {
+ log.debug("Identifier:\n");
+ log.debug(rqData);
+ }
+ QRLevel qrLevel = QRLevel.toQRLevel(rqData);
+ qrLevel.checkSOPClass(rqCmd.getAffectedSOPClassUID(),
+ UIDs.StudyRootQueryRetrieveInformationModelGET,
+ UIDs.PatientStudyOnlyQueryRetrieveInformationModelGET);
+ qrLevel.checkRetrieveRQ(rqData);
+ FileInfo[][] fileInfos = null;
+ try {
+ fileInfos = RetrieveCmd.create(rqData).getFileInfos();
+ checkPermission(a, fileInfos);
+ new Thread(new GetTask(service, assoc, rq, fileInfos))
+ .start();
+ } catch (DcmServiceException e) {
+ throw e;
+ } catch (SQLException e) {
+ service.getLog().error("Query DB failed:", e);
+ throw new DcmServiceException(Status.UnableToCalculateNumberOfMatches, e);
+ } catch (Throwable e) {
+ service.getLog().error("Unexpected exception:", e);
+ throw new DcmServiceException(Status.UnableToProcess, e);
+ }
+ } catch (DcmServiceException e) {
+ Command rspCmd = objFact.newCommand();
+ rspCmd.initCGetRSP(
+ rqCmd.getMessageID(),
+ rqCmd.getAffectedSOPClassUID(),
+ e.getStatus());
+ e.writeTo(rspCmd);
+ Dimse rsp = fact.newDimse(pcid, rspCmd);
+ a.write(rsp);
+ }
+ }
+
+ private void checkPermission(Association a, FileInfo[][] fileInfos)
+ throws Exception {
+ if (fileInfos.length == 0) {
+ return;
+ }
+ String callingAET = a.getCallingAET();
+ if (service.hasUnrestrictedReadPermissions(callingAET)) {
+ return;
+ }
+ Subject subject = (Subject) a.getProperty("user");
+ if (subject == null) {
+ throw new DcmServiceException(
+ MISSING_USER_ID_OF_STORE_SCP_ERR_STATUS,
+ MISSING_USER_ID_OF_STORE_SCP_ERR_MSG);
+ }
+ StudyPermissionManager studyPermissionManager =
+ service.getStudyPermissionManager(a);
+ Set<String> suids = new HashSet<String>();
+ for (int i = 0; i < fileInfos.length; i++) {
+ if (suids.add(fileInfos[i][0].studyIUID)) {
+ if (!studyPermissionManager.hasPermission(
+ fileInfos[i][0].studyIUID,
+ StudyPermissionDTO.READ_ACTION, subject)) {
+ throw new DcmServiceException(
+ NO_READ_PERMISSION_ERR_STATUS,
+ NO_READ_PERMISSION_ERR_MSG);
+ }
+ }
+ }
+ }
+}
+
Added: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetTask.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetTask.java (rev 0)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/GetTask.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -0,0 +1,204 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
+ * Java(TM), available at http://sourceforge.net/projects/dcm4che.
+ *
+ * The Initial Developer of the Original Code is
+ * Agfa HealthCare.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * See listed authors below.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.dcm4chex.archive.dcm.qrscp;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TimerTask;
+
+import org.dcm4che.data.Command;
+import org.dcm4che.data.Dataset;
+import org.dcm4che.data.DcmObjectFactory;
+import org.dcm4che.dict.Status;
+import org.dcm4che.dict.Tags;
+import org.dcm4che.net.ActiveAssociation;
+import org.dcm4che.net.Association;
+import org.dcm4che.net.AssociationFactory;
+import org.dcm4che.net.Dimse;
+import org.dcm4che.net.DimseListener;
+import org.dcm4chex.archive.common.Availability;
+import org.dcm4chex.archive.ejb.jdbc.FileInfo;
+import org.dcm4chex.archive.exceptions.NoPresContextException;
+import org.dcm4chex.archive.perf.PerfCounterEnum;
+import org.dcm4chex.archive.perf.PerfPropertyEnum;
+import org.jboss.logging.Logger;
+
+/**
+ * @author Gunter Zeilinger <gun...@gm...>
+ * @version $Revision$ $Date$
+ * @since May 6, 2008
+ */
+class GetTask implements Runnable {
+
+ private final QueryRetrieveScpService service;
+ private final Logger log;
+ private ActiveAssociation assoc;
+ private final int pcid;
+ private final Command rqCmd;
+ private final FileInfo[][] fileInfos;
+ private final ArrayList<FileInfo> transferred = new ArrayList<FileInfo>();
+ private final ArrayList<String> failedIUIDs = new ArrayList<String>();
+
+ private int warnings = 0;
+
+ private int completed = 0;
+
+ private int remaining = 0;
+
+ private boolean canceled = false;
+
+ public GetTask(QueryRetrieveScpService service, ActiveAssociation assoc,
+ Dimse rq, FileInfo[][] fileInfos) {
+ this.service = service;
+ this.log = service.getLog();
+ this.assoc = assoc;
+ this.pcid = rq.pcid();
+ this.rqCmd = rq.getCommand();
+ this.fileInfos = fileInfos;
+ this.remaining = fileInfos.length;
+ if (remaining > 0) {
+ assoc.addCancelListener(rqCmd.getMessageID(), new DimseListener() {
+ public void dimseReceived(Association assoc, Dimse dimse) {
+ GetTask.this.canceled = true;
+ }});
+ }
+ }
+
+ public void run() {
+ if (fileInfos.length > 0) {
+ Set<StudyInstanceUIDAndDirPath> studyInfos =
+ new HashSet<StudyInstanceUIDAndDirPath>();
+ Association a = assoc.getAssociation();
+ TimerTask sendPendingRsp = new TimerTask() {
+ public void run() {
+ if (remaining > 0) {
+ sendGetRsp(Status.Pending, null);
+ }
+ }};
+ service.scheduleSendPendingRsp(sendPendingRsp);
+ try {
+ for (int i = 0; i < fileInfos.length; i++) {
+ final FileInfo[] fileInfo = fileInfos[i];
+ final FileInfo fileInfo0 = fileInfo[0];
+ final String iuid = fileInfo0.sopIUID;
+ DimseListener storeScpListener = new DimseListener() {
+
+ public void dimseReceived(Association assoc, Dimse dimse) {
+ switch (dimse.getCommand().getStatus()) {
+ case Status.Success:
+ ++completed;
+ transferred.add(fileInfo0);
+ break;
+ case Status.CoercionOfDataElements:
+ case Status.DataSetDoesNotMatchSOPClassWarning:
+ case Status.ElementsDiscarded:
+ ++warnings;
+ transferred.add(fileInfo0);
+ break;
+ default:
+ failedIUIDs.add(iuid);
+ break;
+ }
+ --remaining;
+ }
+ };
+
+ try {
+ Dimse rq = service.makeCStoreRQ(assoc, fileInfo0,
+ rqCmd.getInt(Tags.Priority, Command.MEDIUM),
+ null, 0, service.getByteBuffer(a), null);
+ assoc.invoke(rq, storeScpListener);
+ } catch (NoPresContextException e) {
+ if (!service.isIgnorableSOPClass(fileInfo0.sopCUID,
+ a.getCallingAET())) {
+ failedIUIDs.add(fileInfo0.sopIUID);
+ log.warn(e.getMessage());
+ } else {
+ log.info(e.getMessage());
+ }
+ } catch (Exception e) {
+ log.error("Exception during retrieve of " + iuid, e);
+ }
+ // track access on ONLINE FS
+ if (fileInfo0.availability == Availability.ONLINE)
+ studyInfos.add(new StudyInstanceUIDAndDirPath(fileInfo0));
+ }
+ } finally {
+ sendPendingRsp.cancel();
+ }
+ if (!transferred.isEmpty()) {
+ service.logInstancesSent(a, a, transferred);
+ }
+ service.updateStudyAccessTime(studyInfos);
+ }
+ sendGetRsp(status(), service.makeRetrieveRspIdentifier(failedIUIDs));
+ }
+
+ private int status() {
+ return canceled ? Status.Cancel
+ : failedIUIDs.isEmpty() ? Status.Success
+ : completed == 0 ? Status.UnableToPerformSuboperations
+ : Status.SubOpsOneOrMoreFailures;
+ }
+
+ private void sendGetRsp(int status, Dataset ds) {
+ if (assoc == null)
+ return;
+ Command rspCmd = DcmObjectFactory.getInstance().newCommand();
+ rspCmd.initCGetRSP(rqCmd.getMessageID(),
+ rqCmd.getAffectedSOPClassUID(), status);
+ if (remaining > 0) {
+ rspCmd.putUS(Tags.NumberOfRemainingSubOperations, remaining);
+ } else {
+ rspCmd.remove(Tags.NumberOfRemainingSubOperations);
+ }
+ rspCmd.putUS(Tags.NumberOfCompletedSubOperations, completed);
+ rspCmd.putUS(Tags.NumberOfWarningSubOperations, warnings);
+ rspCmd.putUS(Tags.NumberOfFailedSubOperations, failedIUIDs.size());
+ try {
+ assoc.getAssociation().write(
+ AssociationFactory.getInstance().newDimse(pcid, rspCmd, ds));
+ } catch (Exception e) {
+ log.info("Failed to send C-GET RSP to "
+ + assoc.getAssociation().getCallingAET(), e);
+ assoc = null;
+ }
+ }
+
+}
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveScp.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveScp.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveScp.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -49,7 +49,6 @@
import org.dcm4che.data.Command;
import org.dcm4che.data.Dataset;
-import org.dcm4che.data.DcmObject;
import org.dcm4che.dict.Status;
import org.dcm4che.dict.Tags;
import org.dcm4che.dict.UIDs;
@@ -121,9 +120,16 @@
log.debug("Identifier:\n");
log.debug(rqData);
}
-
- checkMoveRQ(a, rq.pcid(), rqCmd, rqData);
String dest = rqCmd.getString(Tags.MoveDestination);
+ if (dest == null) {
+ throw new DcmServiceException(Status.UnableToProcess,
+ "Missing Move Destination");
+ }
+ QRLevel qrLevel = QRLevel.toQRLevel(rqData);
+ qrLevel.checkSOPClass(rqCmd.getAffectedSOPClassUID(),
+ UIDs.StudyRootQueryRetrieveInformationModelMOVE,
+ UIDs.PatientStudyOnlyQueryRetrieveInformationModelMOVE);
+ qrLevel.checkRetrieveRQ(rqData);
boolean thirdPartyMove = !dest.equals(a.getCallingAET());
AEDTO aeData = null;
FileInfo[][] fileInfos = null;
@@ -240,81 +246,6 @@
moveDest);
}
- private void checkMoveRQ(
- Association assoc,
- int pcid,
- Command rqCmd,
- Dataset rqData)
- throws DcmServiceException {
-
- checkAttribute(
- rqCmd,
- Tags.MoveDestination,
- Status.UnableToProcess,
- "Missing Move Destination");
- checkAttribute(
- rqData,
- Tags.QueryRetrieveLevel,
- Status.IdentifierDoesNotMatchSOPClass,
- "Missing Query Retrieve Level");
-
- final String level = rqData.getString(Tags.QueryRetrieveLevel);
- final String asid =
- assoc.getProposedPresContext(pcid).getAbstractSyntaxUID();
- if ("PATIENT".equals(level)) {
- if (UIDs.StudyRootQueryRetrieveInformationModelMOVE.equals(asid)) {
- throw new DcmServiceException(
- Status.IdentifierDoesNotMatchSOPClass,
- "Cannot use Query Retrieve Level PATIENT with Study Root IM");
- }
- checkAttribute(
- rqData,
- Tags.PatientID,
- Status.IdentifierDoesNotMatchSOPClass,
- "Missing Patient ID");
- } else if ("STUDY".equals(level)) {
- checkAttribute(
- rqData,
- Tags.StudyInstanceUID,
- Status.IdentifierDoesNotMatchSOPClass,
- "Missing Study Instance UID");
-
- } else if ("SERIES".equals(level)) {
- if (UIDs.PatientStudyOnlyQueryRetrieveInformationModelMOVE.equals(asid)) {
- throw new DcmServiceException(
- Status.IdentifierDoesNotMatchSOPClass,
- "Cannot use Query Retrieve Level SERIES with Patient Study Only IM");
- }
- checkAttribute(
- rqData,
- Tags.SeriesInstanceUID,
- Status.IdentifierDoesNotMatchSOPClass,
- "Missing Series Instance UID");
- } else if ("IMAGE".equals(level)) {
- if (UIDs.PatientStudyOnlyQueryRetrieveInformationModelMOVE.equals(asid)) {
- throw new DcmServiceException(
- Status.IdentifierDoesNotMatchSOPClass,
- "Cannot use Query Retrieve Level SERIES with Patient Study Only IM");
- }
- checkAttribute(
- rqData,
- Tags.SOPInstanceUID,
- Status.IdentifierDoesNotMatchSOPClass,
- "Missing SOP Instance UID");
- } else {
- throw new DcmServiceException(
- Status.IdentifierDoesNotMatchSOPClass,
- "Invalid Retrieve Level " + level);
- }
- }
-
- private void checkAttribute(DcmObject dcm, int tag, int status, String msg)
- throws DcmServiceException {
- if (!dcm.containsValue(tag)) {
- throw new DcmServiceException(status, msg);
- }
- }
-
public final ObjectName getPerfMonServiceName() {
return perfMon.getPerfMonServiceName();
}
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveTask.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveTask.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/MoveTask.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -39,7 +39,6 @@
package org.dcm4chex.archive.dcm.qrscp;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -48,7 +47,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import java.util.Timer;
import java.util.TimerTask;
import org.dcm4che.auditlog.AuditLoggerFactory;
@@ -74,17 +72,12 @@
import org.dcm4che.net.DimseListener;
import org.dcm4che.net.ExtNegotiation;
import org.dcm4che.net.PDU;
-import org.dcm4che.net.PresContext;
import org.dcm4chex.archive.common.Availability;
-import org.dcm4chex.archive.common.DatasetUtils;
import org.dcm4chex.archive.ejb.interfaces.AEDTO;
import org.dcm4chex.archive.ejb.jdbc.FileInfo;
-import org.dcm4chex.archive.exceptions.NoPresContextException;
import org.dcm4chex.archive.perf.PerfCounterEnum;
import org.dcm4chex.archive.perf.PerfMonDelegate;
import org.dcm4chex.archive.perf.PerfPropertyEnum;
-import org.dcm4chex.archive.util.FileDataSource;
-import org.dcm4chex.archive.util.FileUtils;
import org.jboss.logging.Logger;
/**
@@ -94,8 +87,6 @@
*/
public class MoveTask implements Runnable {
- private static final String SEND_BUFFER = "SEND_BUFFER";
-
private static final String[] NATIVE_LE_TS = { UIDs.ExplicitVRLittleEndian,
UIDs.ImplicitVRLittleEndian, };
@@ -105,11 +96,6 @@
private static final String IMAGE = "IMAGE";
- private static final UIDDictionary uidDict = DictionaryFactory
- .getInstance().getDefaultUIDDictionary();
-
- private static final Timer pendingRspTimer = new Timer(true);
-
protected final QueryRetrieveScpService service;
protected final Logger log;
@@ -138,7 +124,7 @@
private ActiveAssociation moveAssoc;
- private final ArrayList failedIUIDs = new ArrayList();
+ private final List<String> failedIUIDs = new ArrayList<String>();
private final int size;
@@ -293,7 +279,7 @@
// item to avoid ConcurrentModificationException
remaining -= iuids.size();
final String prompt = "No Presentation Context for "
- + uidDict.toString(cuid) + " accepted by " + moveDest
+ + QueryRetrieveScpService.uidDict.toString(cuid) + " accepted by " + moveDest
+ "\n\tCannot send " + iuids.size()
+ " instances of this class";
if (!service.isIgnorableSOPClass(cuid, moveDest)) {
@@ -313,10 +299,7 @@
}
public void run() {
- if (service.isSendPendingMoveRSP()) {
- pendingRspTimer.schedule(sendPendingRsp , 0,
- service.getPendingMoveRSPInterval());
- }
+ service.scheduleSendPendingRsp(sendPendingRsp);
try {
if (retrieveInfo.isRetrieveFromLocal()) {
retrieveLocal();
@@ -478,7 +461,8 @@
private void retrieveLocal() {
this.stgCmtActionInfo = DcmObjectFactory.getInstance().newDataset();
this.refSOPSeq = stgCmtActionInfo.putSQ(Tags.RefSOPSeq);
- Set studyInfos = new HashSet();
+ Set<StudyInstanceUIDAndDirPath> studyInfos =
+ new HashSet<StudyInstanceUIDAndDirPath>();
Association a = storeAssoc.getAssociation();
Collection localFiles = retrieveInfo.getLocalFiles();
final Set remainingIUIDs = new HashSet(retrieveInfo.removeLocalIUIDs());
@@ -517,7 +501,9 @@
};
try {
- Dimse rq = makeCStoreRQ(fileInfo, getByteBuffer(a));
+ Dimse rq = service.makeCStoreRQ(storeAssoc,
+ fileInfo, priority, moveOriginatorAET, msgID,
+ service.getByteBuffer(a), perfMon);
perfMon.start(storeAssoc, rq, PerfCounterEnum.C_STORE_SCU_OBJ_OUT );
perfMon.setProperty(storeAssoc, rq, PerfPropertyEnum.REQ_DIMSE, rq);
perfMon.setProperty(storeAssoc, rq, PerfPropertyEnum.STUDY_IUID, fileInfo.studyIUID);
@@ -528,10 +514,9 @@
} catch (Exception e) {
log.error("Exception during move of " + iuid, e);
}
- if (fileInfo.availability == Availability.ONLINE) // only track
- // access on
- // ONLINE FS
- studyInfos.add(fileInfo.studyIUID + '@' + fileInfo.basedir);
+ // track access on ONLINE FS
+ if (fileInfo.availability == Availability.ONLINE)
+ studyInfos.add(new StudyInstanceUIDAndDirPath(fileInfo));
}
if (a.getState() == Association.ASSOCIATION_ESTABLISHED) {
try {
@@ -573,15 +558,6 @@
.queueStgCmtOrder(moveCalledAET, stgCmtAET,
stgCmtActionInfo);
}
-
- private byte[] getByteBuffer(Association assoc) {
- byte[] buf = (byte[]) assoc.getProperty(SEND_BUFFER);
- if (buf == null) {
- buf = new byte[service.getBufferSize()];
- assoc.putProperty(SEND_BUFFER, buf);
- }
- return buf;
- }
private void updateInstancesAction(final FileInfo info) {
if (instancesAction == null) {
@@ -604,80 +580,13 @@
item.putUI(Tags.RefSOPInstanceUID, fileInfo.sopIUID);
}
-
- private Dimse makeCStoreRQ(FileInfo info, byte[] buffer) throws Exception {
- Association assoc = storeAssoc.getAssociation();
- PresContext presCtx = assoc.getAcceptedPresContext(info.sopCUID,
- info.tsUID);
- if (presCtx == null) {
- presCtx = assoc.getAcceptedPresContext(info.sopCUID,
- UIDs.ExplicitVRLittleEndian);
- if (presCtx == null) {
- presCtx = assoc.getAcceptedPresContext(info.sopCUID,
- UIDs.ImplicitVRLittleEndian);
- if (presCtx == null)
- throw new NoPresContextException(
- "No Presentation Context for "
- + uidDict.toString(info.sopCUID)
- + " accepted by " + moveDest);
- }
- }
- Command storeRqCmd = DcmObjectFactory.getInstance().newCommand();
- storeRqCmd.initCStoreRQ(assoc.nextMsgID(), info.sopCUID, info.sopIUID,
- priority);
- storeRqCmd.putUS(Tags.MoveOriginatorMessageID, msgID);
- storeRqCmd.putAE(Tags.MoveOriginatorAET, moveOriginatorAET);
- File f = getFile(info);
- Dataset mergeAttrs = DatasetUtils.fromByteArray(info.patAttrs,
- DatasetUtils.fromByteArray(info.studyAttrs, DatasetUtils
- .fromByteArray(info.seriesAttrs, DatasetUtils
- .fromByteArray(info.instAttrs))));
- FileDataSource ds = new FileDataSource(f, mergeAttrs, buffer);
- ds.setWithoutPixeldata(withoutPixeldata);
- Dimse rq = AssociationFactory.getInstance().newDimse(presCtx.pcid(),
- storeRqCmd, ds);
- perfMon.setProperty(storeAssoc, rq, PerfPropertyEnum.DICOM_FILE, f);
- return rq;
- }
-
- /**
- * This method may trigger remote retrieval. Use cautiously
- *
- * @param info
- * @return The File
- * @throws Exception
- */
- protected File getFile(FileInfo info) throws Exception {
- return info.basedir.startsWith("tar:") ? service.retrieveFileFromTAR(
- info.basedir, info.fileID) : FileUtils.toFile(info.basedir,
- info.fileID);
- }
-
private void notifyMoveFinished() {
notifyMoveSCU(canceled ? Status.Cancel : failed == 0 ? Status.Success
: completed == 0 ? Status.UnableToPerformSuboperations
: Status.SubOpsOneOrMoreFailures,
- makeMoveRspIdentifier(), null);
+ service.makeRetrieveRspIdentifier(failedIUIDs), null);
}
- private Dataset makeMoveRspIdentifier() {
- if (failed == 0)
- return null;
- Dataset ds = DcmObjectFactory.getInstance().newDataset();
- if (failed == failedIUIDs.size()) {
- String[] a = (String[]) failedIUIDs.toArray(new String[failedIUIDs
- .size()]);
- ds.putUI(Tags.FailedSOPInstanceUIDList, a);
- // check if 64k limit for UI attribute is reached
- if (ds.get(Tags.FailedSOPInstanceUIDList).length() < 0x10000)
- return ds;
- log
- .warn("Failed SOP InstanceUID List exceeds 64KB limit - send empty attribute instead");
- }
- ds.putUI(Tags.FailedSOPInstanceUIDList);
- return ds;
- }
-
private void notifyMoveSCU(int status, Dataset ds, Command fwdMoveRspCmd) {
if (!moveAssocClosed) {
Command cmd = fwdMoveRspCmd != null ? makeMoveRsp(fwdMoveRspCmd)
Added: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QRLevel.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QRLevel.java (rev 0)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QRLevel.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -0,0 +1,128 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
+ * Java(TM), available at http://sourceforge.net/projects/dcm4che.
+ *
+ * The Initial Developer of the Original Code is
+ * Agfa HealthCare.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * See listed authors below.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.dcm4chex.archive.dcm.qrscp;
+
+import org.dcm4che.data.Dataset;
+import org.dcm4che.dict.Status;
+import org.dcm4che.dict.Tags;
+import org.dcm4che.net.DcmServiceException;
+
+/**
+ * @author Gunter Zeilinger <gun...@gm...>
+ * @version $Revision$ $Date$
+ * @since May 6, 2008
+ */
+enum QRLevel {
+ PATIENT, STUDY, SERIES, IMAGE;
+
+ private static final int[] UID_TAGS = {
+ Tags.PatientID,
+ Tags.StudyInstanceUID,
+ Tags.SeriesInstanceUID,
+ Tags.SOPInstanceUID
+ };
+
+ void checkSOPClass(String cuid, String studyRoot, String patientStudyOnly)
+ throws DcmServiceException {
+ if (this == PATIENT) {
+ if (cuid.equals(studyRoot)) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Cannot use Query Retrieve Level PATIENT with Study Root IM");
+ }
+ } else if (this != STUDY) {
+ if (cuid.equals(patientStudyOnly)) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Cannot use Query Retrieve Level " + this
+ + " with Patient Study Only IM");
+ }
+ }
+ }
+
+ void checkRetrieveRQ(Dataset rqData) throws DcmServiceException {
+ for (int level = 0, levelOffset = -ordinal(); level < UID_TAGS.length;
+ level++, levelOffset++) {
+ int uidTag = UID_TAGS[level];
+ String[] uids = rqData.getStrings(uidTag);
+ if (levelOffset > 0) {
+ if (uids != null) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Illegal Unique Key Attribute "
+ + Tags.toString(uidTag) + " in " + this
+ + " Level Retrieve RQ");
+ }
+ } else {
+ if (levelOffset == 0) {
+ if (uids == null || uids.length == 0) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Missing Unique Key Attribute "
+ + Tags.toString(uidTag) + " in " + this
+ + " Level Retrieve RQ");
+ }
+ }
+ if (levelOffset < 0 || level == 0) {
+ if (uids != null && uids.length > 1) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Illegal List of UIDs in Unique Key Attribute "
+ + Tags.toString(uidTag) + " in " + this
+ + " Level Retrieve RQ");
+ }
+ }
+ }
+ }
+ }
+
+ static QRLevel toQRLevel(Dataset rqData) throws DcmServiceException {
+ String qrLevel = rqData.getString(Tags.QueryRetrieveLevel);
+ try {
+ return QRLevel.valueOf(qrLevel);
+ } catch (NullPointerException e) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Missing Query Retrieve Level");
+ } catch (IllegalArgumentException e) {
+ throw new DcmServiceException(
+ Status.IdentifierDoesNotMatchSOPClass,
+ "Invalid Retrieve Level " + qrLevel);
+ }
+ }
+}
Modified: dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QueryRetrieveScpService.java
===================================================================
--- dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QueryRetrieveScpService.java 2008-05-15 06:47:34 UTC (rev 6311)
+++ dcm4chee/dcm4chee-arc/trunk/dcm4jboss-sar/src/java/org/dcm4chex/archive/dcm/qrscp/QueryRetrieveScpService.java 2008-05-15 13:06:29 UTC (rev 6312)
@@ -50,6 +50,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.Map.Entry;
import javax.management.JMException;
@@ -59,20 +61,29 @@
import org.dcm4che.auditlog.InstancesAction;
import org.dcm4che.auditlog.RemoteNode;
+import org.dcm4che.data.Command;
import org.dcm4che.data.Dataset;
import org.dcm4che.data.DcmObjectFactory;
+import org.dcm4che.dict.DictionaryFactory;
import org.dcm4che.dict.Status;
+import org.dcm4che.dict.Tags;
+import org.dcm4che.dict.UIDDictionary;
import org.dcm4che.dict.UIDs;
import org.dcm4che.net.AcceptorPolicy;
+import org.dcm4che.net.ActiveAssociation;
import org.dcm4che.net.Association;
+import org.dcm4che.net.AssociationFactory;
import org.dcm4che.net.DcmServiceException;
import org.dcm4che.net.DcmServiceRegistry;
+import org.dcm4che.net.Dimse;
import org.dcm4che.net.ExtNegotiator;
+import org.dcm4che.net.PresContext;
import org.dcm4che2.audit.message.AuditMessage;
import org.dcm4che2.audit.message.InstancesTransferredMessage;
import org.dcm4che2.audit.message.ParticipantObjectDescription;
import org.dcm4che2.audit.util.InstanceSorter;
import org.dcm4cheri.util.StringUtils;
+import org.dcm4chex.archive.common.DatasetUtils;
import org.dcm4chex.archive.config.RetryIntervalls;
import org.dcm4chex.archive.dcm.AbstractScpService;
import org.dcm4chex.archive.ejb.interfaces.AEDTO;
@@ -84,10 +95,14 @@
import org.dcm4chex.archive.ejb.jdbc.QueryCmd;
import org.dcm4chex.archive.ejb.jdbc.RetrieveCmd;
import org.dcm4chex.archive.exceptions.ConfigurationException;
+import org.dcm4chex.archive.exceptions.NoPresContextException;
import org.dcm4chex.archive.exceptions.UnknownAETException;
import org.dcm4chex.archive.mbean.DicomSecurityDelegate;
import org.dcm4chex.archive.mbean.TLSConfigDelegate;
+import org.dcm4chex.archive.perf.PerfMonDelegate;
+import org.dcm4chex.archive.perf.PerfPropertyEnum;
import org.dcm4chex.archive.util.EJBHomeFactory;
+import org.dcm4chex.archive.util.FileDataSource;
import org.dcm4chex.archive.util.FileUtils;
import org.dcm4chex.archive.util.HomeFactoryException;
import org.jboss.logging.Logger;
@@ -103,6 +118,13 @@
private static final String NONE = "NONE";
+ private static final String SEND_BUFFER = "SEND_BUFFER";
+
+ private static final Timer pendingRspTimer = new Timer(true);
+
+ static final UIDDictionary uidDict =
+ DictionaryFactory.getInstance().getDefaultUIDDictionary();
+
private String[] sendNoPixelDataToAETs = null;
private String[] sendWithDefaultTransferSyntaxToAETitles = null;
@@ -132,9 +154,9 @@
private DicomSecurityDelegate dicomSecurity =
new DicomSecurityDelegate(this);
- private boolean sendPendingMoveRSP = true;
+ private boolean sendPendingRetrieveRSP = true;
- private long pendingMoveRSPInterval = 5000;
+ private long pendingRetrieveRSPInterval = 5000;
private boolean forwardAsMoveOriginator = true;
@@ -172,6 +194,8 @@
private MoveScp moveScp = null;
+ private GetScp getScp = null;
+
private int maxUIDsPerMoveRQ = 100;
private int maxBlockedFindRSP = 10000;
@@ -206,6 +230,7 @@
public QueryRetrieveScpService() {
moveScp = createMoveScp();
+ getScp = createGetScp();
dicomFindScp = createFindScp();
}
@@ -217,6 +242,10 @@
return new MoveScp(this);
}
+ protected GetScp createGetScp() {
+ return new GetScp(this);
+ }
+
protected FindScp createFindScp() {
return new FindScp(this, true);
}
@@ -622,23 +651,23 @@
this.maxStoreOpsInvoked = maxStoreOpsInvoked;
}
- public final boolean isSendPendingMoveRSP() {
- return sendPendingMoveRSP;
+ public final boolean isSendPendingRetrieveRSP() {
+ return sendPendingRetrieveRSP;
}
- public final void setSendPendingMoveRSP(boolean sendPendingMoveRSP) {
- this.sendPendingMoveRSP = sendPendingMoveRSP;
+ public final void setSendPendingRetrieveRSP(boolean sendPendingRetrieveRSP) {
+ this.sendPendingRetrieveRSP = sendPendingRetrieveRSP;
}
- public final void setPendingMoveRSPInterval(long ms) {
+ public final void setPendingRetrieveRSPInterval(long ms) {
if (ms <= 0) {
- throw new IllegalArgumentException("pendingMoveRSPInterval: " + ms);
+ throw new IllegalArgumentException("pendingRetrieveRSPInterval: " + ms);
}
- pendingMoveRSPInterval = ms ;
+ pendingRetrieveRSPInterval = ms ;
}
- public final long getPendingMoveRSPInterval() {
- return pendingMoveRSPInterval ;
+ public final long getPendingRetrieveRSPInterval() {
+ return pendingRetrieveRSPInterval ;
}
public final boolean isForwardAsMoveOriginator() {
@@ -782,10 +811,18 @@
services.bind(UIDs.PatientRootQueryRetrieveInformationModelMOVE,
moveScp);
- services.bind(UIDs.StudyRootQueryRetrieveInformationModelMOVE, moveScp);
+ services.bind(UIDs.StudyRootQueryRetrieveInformationModelMOVE,
+ moveScp);
services.bind(UIDs.PatientStudyOnlyQueryRetrieveInformationModelMOVE,
moveScp);
-
+
+ services.bind(UIDs.PatientRootQueryRetrieveInformationModelGET,
+ getScp);
+ services.bind(UIDs.StudyRootQueryRetrieveInformationModelGET,
+ getScp);
+ services.bind(UIDs.PatientStudyOnlyQueryRetrieveInformationModelGET,
+ getScp);
+
dcmHandler.addAssociationListener(dicomFindScp);
dcmHandler.addAssociationListener(moveScp);
}
@@ -815,7 +852,11 @@
services.unbind(UIDs.PatientRootQueryRetrieveInformationModelMOVE);
services.unbind(UIDs.StudyRootQueryRetrieveInformationModelMOVE);
services.unbind(UIDs.PatientStudyOnlyQueryRetrieveInformationModelMOVE);
-
+
+ services.unbind(UIDs.PatientRootQueryRetrieveInformationModelGET);
+ services.unbind(UIDs.StudyRootQueryRetrieveInformationModelGET);
+ services.unbind(UIDs.PatientStudyOnlyQueryRetrieveInformationModelGET);
+
dcmHandler.removeAssociationListener(dicomFindScp);
dcmHandler.removeAssociationListener(moveScp);
}
@@ -826,13 +867,18 @@
}
};
- protected void updatePresContexts(AcceptorPolicy policy, boolean enable) {
+ protected void enablePresContexts(AcceptorPolicy policy) {
putPresContexts(policy, valuesToStringArray(privateCuidMap),
- enable ? valuesToStringArray(privateTSuidMap) : null);
+ valuesToStringArray(privateTSuidMap));
putPresContexts(policy, valuesToStringArray(standardCuidMap),
- enable ? valuesToStringArray(tsuidMap) : null);
+ valuesToStringArray(tsuidMap));
}
+ protected void disablePresContexts(AcceptorPolicy policy) {
+ putPresContexts(policy, valuesToStringArray(privateCuidMap), null);
+ putPresContexts(policy, valuesToStringArray(standardCuidMap), null);
+ }
+
protected void putPresContexts(AcceptorPolicy policy, String[] cuids,
String[] tsuids) {
super.putPresContexts(policy, cuids, tsuids);
@@ -936,23 +982,24 @@
}
}
- protected void logInstancesSent(Association moveAs, Association storeAs,
- ArrayList fileInfos) {
+ protected void logInstancesSent(Association moveOrGetAs,
+ Association storeAs, ArrayList fileInfos) {
if (auditLogger.isAuditLogIHEYr4()) {
return;
}
try {
InstanceSorter sorter = new InstanceSorter();
- FileInfo fileInfo = null;
+ FileInfo fileInfo = null;
for (Iterator iter = fileInfos.iterator(); iter.hasNext();) {
fileInfo = (FileInfo) iter.next();
sorter.addInstance(fileInfo.studyIUID, fileInfo.sopCUID,
fileInfo.sopIUID, null);
}
- String destAET = storeAs.getCalledAET();
+ String destAET = storeAs.isRequestor() ? storeAs.getCalledAET()
+ : storeAs.getCallingAET();
String destHost = AuditMessage.hostNameOf(
storeAs.getSocket().getInetAddress());
- String origAET = moveAs.getCallingAET();
+ String origAET = moveOrGetAs.getCallingAET();
boolean dstIsRequestor = origAET.equals(destAET);
boolean srcIsRequestor = !dstIsRequestor
&& Arrays.asList(calledAETs).contains(origAET);
@@ -966,7 +1013,7 @@
destHost, dstIsRequestor);
if (!dstIsRequestor && !srcIsRequestor) {
String origHost = AuditMessage.hostNameOf(
- moveAs.getSocket().getInetAddress());
+ moveOrGetAs.getSocket().getInetAddress());
msg.addOtherParticipantProcess(origHost,
new String[] { origAET }, null, origHost, true);
}
@@ -1025,7 +1072,7 @@
FileSystemMgtHome.class, FileSystemMgtHome.JNDI_NAME);
}
- void updateStudyAccessTime(Set studyInfos) {
+ void updateStudyAccessTime(Set<StudyInstanceUIDAndDirPath> studyInfos) {
if (!recordStudyAccessTime)
return;
@@ -1037,12 +1084,10 @@
return;
}
try {
- for (Iterator it = studyInfos.iterator(); it.hasNext();) {
- String studyInfo = (String) it.next();
- int delim = studyInfo.indexOf('@');
+ for (Iterator<StudyInstanceUIDAndDirPath> it = studyInfos.iterator(); it.hasNext();) {
+ StudyInstanceUIDAndDirPath studyInfo = it.next();
try {
- fsMgt.touchStudyOnFileSystem(studyInfo.substring(0, delim),
- studyInfo.substring(delim + 1));
+ fsMgt.touchStudyOnFileSystem(studyInfo.studyIUID, studyInfo.dirpath);
} catch (Exception e) {
log.warn("Failed to update access time for study "
+ studyInfo, e);
@@ -1091,4 +1136,83 @@
return ds;
}
+ void scheduleSendPendingRsp(TimerTask sendPendingRsp) {
+ if (sendPendingRetrieveRSP) {
+ pendingRspTimer.schedule(sendPendingRsp, 0, pendingRetrieveRSPInterval);
+ }
+ }
+
+ Dataset makeRetrieveRspIdentifier(List<String> failedIUIDs) {
+ if (failedIUIDs.isEmpty() )
+ return null;
+ Dataset ds = DcmObjectFactory.getInstance().newDataset();
+ String[] a = failedIUIDs.toArray(new String[failedIUIDs.size()]);
+ ds.putUI(Tags.FailedSOPInstanceUIDList, a);
+ // check if 64k limit for UI attribute is reached
+ if (ds.get(Tags.FailedSOPInstanceUIDList).length() < 0x10000)
+ return ds;
+ log.warn("Failed SOP InstanceUID List exceeds 64KB limit - send empty attribute instead");
+ ds....
[truncated message content] |