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.tool.storescp.test; 040 041import java.io.File; 042import java.io.IOException; 043import java.nio.file.Path; 044import java.security.GeneralSecurityException; 045import java.util.ArrayList; 046import java.util.Iterator; 047import java.util.List; 048import java.util.concurrent.ExecutorService; 049import java.util.concurrent.Executors; 050import java.util.concurrent.ScheduledExecutorService; 051 052import org.dcm4che3.data.Attributes; 053import org.dcm4che3.data.Tag; 054import org.dcm4che3.data.VR; 055import org.dcm4che3.io.DicomOutputStream; 056import org.dcm4che3.net.ApplicationEntity; 057import org.dcm4che3.net.Association; 058import org.dcm4che3.net.Connection; 059import org.dcm4che3.net.Device; 060import org.dcm4che3.net.PDVInputStream; 061import org.dcm4che3.net.Status; 062import org.dcm4che3.net.TransferCapability; 063import org.dcm4che3.net.pdu.PresentationContext; 064import org.dcm4che3.net.service.BasicCEchoSCP; 065import org.dcm4che3.net.service.BasicCStoreSCP; 066import org.dcm4che3.net.service.DicomServiceException; 067import org.dcm4che3.net.service.DicomServiceRegistry; 068import org.dcm4che3.tool.common.test.TestResult; 069import org.dcm4che3.tool.common.test.TestTool; 070import org.dcm4che3.tool.storescp.StoreSCP; 071import org.dcm4che3.util.SafeClose; 072import org.slf4j.Logger; 073import org.slf4j.LoggerFactory; 074 075/** 076 * @author Hesham Elbadawi <bsdreko@gmail.com> 077 */ 078public class StoreSCPTool implements TestTool { 079 080 private final File storageDirectory; 081 082 String aeTitle; 083 084 Device device; 085 086 StoreSCP storeSCP ; 087 088 private final String sourceAETitle; 089 090 private TestResult result; 091 092 Connection bound; 093 094 long totalTime; 095 096 int status; 097 098 long t1=-1,t2=-1; 099 100 private boolean first=true; 101 102 private final boolean noStore; 103 104 private int fileReceived=0; 105 106 private final List<Attributes> rqCMDs = new ArrayList<Attributes>(); 107 108 private final List<String> instanceLocations = new ArrayList<String>(); 109 110 private final List<File> instanceFiles = new ArrayList<File>(); 111 112 private static final Logger LOG = LoggerFactory.getLogger(StoreSCPTool.class); 113 114 private String testDescription; 115 116 private List<String> sopIUIDs; 117 118 private boolean started = false; 119 120 private final BasicCStoreSCP cstoreSCP = new BasicCStoreSCP("*") { 121 122 @Override 123 protected void store(Association as, PresentationContext pc, 124 Attributes rq, PDVInputStream data, Attributes rsp) 125 throws IOException { 126 if(first) 127 t1=System.currentTimeMillis(); 128 first = false; 129 rsp.setInt(Tag.Status, VR.US, status); 130 if (noStore) { 131 fileReceived++; 132 return; 133 } 134 String cuid = rq.getString(Tag.AffectedSOPClassUID); 135 String iuid = rq.getString(Tag.AffectedSOPInstanceUID); 136 addSopUID(iuid); 137 String tsuid = pc.getTransferSyntax(); 138 File file = new File(storageDirectory, iuid); 139 try { 140 LOG.info("{}: M-WRITE {}", as, file); 141 file.getParentFile().mkdirs(); 142 DicomOutputStream out = new DicomOutputStream(file); 143 try { 144 out.writeFileMetaInformation(as.createFileMetaInformation(iuid, cuid, tsuid)); 145 data.copyTo(out); 146 } finally { 147 SafeClose.close(out); 148 fileReceived++; 149 rqCMDs.add(rq); 150 instanceFiles.add(file); 151 instanceLocations.add(file.getAbsolutePath()); 152 } 153 154 } catch (Exception e) { 155 throw new DicomServiceException(Status.ProcessingFailure, e); 156 } 157 } 158 159 }; 160 161 public StoreSCPTool( File storageDirectory, Device device, 162 String sourceAETitle, Connection conn, boolean noStore) { 163 this.device = device; 164 this.sourceAETitle = sourceAETitle; 165 this.storageDirectory = storageDirectory; 166 this.bound = conn; 167 this.noStore = noStore; 168 } 169 170 public void start(String testDescriptionIn) throws InterruptedException { 171 this.start(testDescriptionIn, "*"); 172 } 173 174 175 public void start(String testDescriptionIn, String... transferSyntaxes) throws InterruptedException { 176 177 started = true; 178 179 this.testDescription = testDescriptionIn; 180 ApplicationEntity ae = new ApplicationEntity(sourceAETitle); 181 device.setDimseRQHandler(createServiceRegistry()); 182 device.addApplicationEntity(ae); 183 ae.addConnection(bound); 184 185 for(Iterator<Connection> iterator=device.getConnections().iterator(); iterator.hasNext();) { 186 Connection next = iterator.next(); 187 if(!next.getCommonName().equalsIgnoreCase(bound.getCommonName())) 188 iterator.remove(); 189 } 190 ae.setAssociationAcceptor(true); 191 //accept all 192 ae.addTransferCapability( 193 new TransferCapability(null, 194 "*", 195 TransferCapability.Role.SCP, 196 transferSyntaxes)); 197 ExecutorService executorService = Executors.newCachedThreadPool(); 198 ScheduledExecutorService scheduledExecutorService = 199 Executors.newSingleThreadScheduledExecutor(); 200 device.setScheduledExecutor(scheduledExecutorService); 201 device.setExecutor(executorService); 202 try { 203 device.bindConnections(); 204 } catch (IOException e) { 205 LOG.error("Error binding connection for storescp , {}", e); 206 } catch (GeneralSecurityException e) { 207 LOG.error("Error binding connection for storescp , {}", e); 208 } 209 } 210 211 public void stop() { 212 213 if (!started) 214 return; 215 started = false; 216 217 t2 = System.currentTimeMillis(); 218 device.unbindConnections(); 219 ((ExecutorService) device.getExecutor()).shutdown(); 220 device.getScheduledExecutor().shutdown(); 221 222 //very quick fix to block for listening connection 223 while (device.getConnections().get(0).isListening()) 224 { 225 try { 226 Thread.sleep(10); 227 } catch (InterruptedException e) { 228 // ignore 229 } 230 } 231 232 init(new StoreSCPResult(this.testDescription, t2 - t1, getfilesReceived(), getCmdRQList(), this.sopIUIDs, this.instanceLocations)); 233 } 234 private List<Attributes> getCmdRQList() { 235 return rqCMDs; 236 } 237 238 private int getfilesReceived() { 239 return fileReceived; 240 } 241 242 private DicomServiceRegistry createServiceRegistry() { 243 DicomServiceRegistry serviceRegistry = new DicomServiceRegistry(); 244 serviceRegistry.addDicomService(new BasicCEchoSCP()); 245 serviceRegistry.addDicomService(cstoreSCP); 246 return serviceRegistry; 247 } 248 249 @Override 250 public void init(TestResult resultIn) { 251 this.result = resultIn; 252 } 253 254 @Override 255 public TestResult getResult() { 256 return this.result; 257 } 258 259 private void addSopUID(String uid) { 260 if(this.sopIUIDs == null) 261 this.sopIUIDs = new ArrayList<String>(); 262 this.sopIUIDs.add(uid); 263 } 264 265 public List<String> getInstanceLocations() { 266 return instanceLocations; 267 } 268 269 public List<File> getInstanceFiles() { 270 return instanceFiles; 271 } 272 public Path getStorageDirectory() { 273 return storageDirectory.toPath(); 274 } 275}