001/*
002 * *** BEGIN LICENSE BLOCK *****
003 *  Version: MPL 1.1/GPL 2.0/LGPL 2.1
004 *
005 *  The contents of this file are subject to the Mozilla Public License Version
006 *  1.1 (the "License"); you may not use this file except in compliance with
007 *  the License. You may obtain a copy of the License at
008 *  http://www.mozilla.org/MPL/
009 *
010 *  Software distributed under the License is distributed on an "AS IS" basis,
011 *  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
012 *  for the specific language governing rights and limitations under the
013 *  License.
014 *
015 *  The Original Code is part of dcm4che, an implementation of DICOM(TM) in
016 *  Java(TM), hosted at https://github.com/gunterze/dcm4che.
017 *
018 *  The Initial Developer of the Original Code is
019 *  Agfa Healthcare.
020 *  Portions created by the Initial Developer are Copyright (C) 2015
021 *  the Initial Developer. All Rights Reserved.
022 *
023 *  Contributor(s):
024 *  See @authors listed below
025 *
026 *  Alternatively, the contents of this file may be used under the terms of
027 *  either the GNU General Public License Version 2 or later (the "GPL"), or
028 *  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
029 *  in which case the provisions of the GPL or the LGPL are applicable instead
030 *  of those above. If you wish to allow use of your version of this file only
031 *  under the terms of either the GPL or the LGPL, and not to allow others to
032 *  use your version of this file under the terms of the MPL, indicate your
033 *  decision by deleting the provisions above and replace them with the notice
034 *  and other provisions required by the GPL or the LGPL. If you do not delete
035 *  the provisions above, a recipient may use your version of this file under
036 *  the terms of any one of the MPL, the GPL or the LGPL.
037 *
038 *  ***** END LICENSE BLOCK *****
039 */
040
041package org.dcm4che3.tool.ianscp;
042
043import org.dcm4che3.data.Attributes;
044import org.dcm4che3.data.Tag;
045import org.dcm4che3.data.UID;
046import org.dcm4che3.io.DicomOutputStream;
047import org.dcm4che3.net.ApplicationEntity;
048import org.dcm4che3.net.Association;
049import org.dcm4che3.net.Commands;
050import org.dcm4che3.net.Connection;
051import org.dcm4che3.net.Device;
052import org.dcm4che3.net.Dimse;
053import org.dcm4che3.net.Status;
054import org.dcm4che3.net.pdu.PresentationContext;
055import org.dcm4che3.net.service.AbstractDicomService;
056import org.dcm4che3.net.service.BasicCEchoSCP;
057import org.dcm4che3.net.service.BasicIanSCP;
058import org.dcm4che3.net.service.DicomService;
059import org.dcm4che3.net.service.DicomServiceException;
060import org.dcm4che3.net.service.DicomServiceRegistry;
061import org.dcm4che3.util.SafeClose;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065import java.io.File;
066import java.io.IOException;
067
068/**
069 * Service Class Provider (SCP) for the Instance Available Notification (IAN) SOP Class. It listens on a specific TCP/IP
070 * port for incoming association requests from a Service Class User (SCU) of the IAN SOP Class. Instance Available
071 * Notifications received in N-CREATE requests are stored in DICOM files. It also supports the Verification Service
072 * Class as a SCP.
073 *
074 * @author Gunter Zeilinger <gunterze@gmail.com>
075 * @author Hermann Czedik-Eysenberg <hermann-agfa@czedik.net>
076 */
077public class IanSCPTool {
078
079    private static final Logger LOG = LoggerFactory.getLogger(IanSCP.class);
080
081    private final Device device;
082    private final ApplicationEntity ae;
083
084    protected final DicomService ianSCP = new BasicIanSCP() {
085        @Override
086        protected Attributes create(Association as, Attributes cmd, Attributes data) throws DicomServiceException {
087            return IanSCPTool.this.create(as, cmd, data);
088        }
089
090        @Override
091        protected int getDefaultResponseStatus() {
092            return IanSCPTool.this.getResponseStatus();
093        }
094    };
095
096    private File storageDir;
097
098    private int status;
099
100    public IanSCPTool(Device device, ApplicationEntity ae) {
101        this.device = device;
102        this.ae = ae;
103        ae.setAssociationAcceptor(true);
104        DicomServiceRegistry serviceRegistry = new DicomServiceRegistry();
105        serviceRegistry.addDicomService(new BasicCEchoSCP());
106        serviceRegistry.addDicomService(ianSCP);
107        ae.setDimseRQHandler(serviceRegistry);
108    }
109
110    public void setStorageDirectory(File storageDir) {
111        if (storageDir != null)
112            storageDir.mkdirs();
113        this.storageDir = storageDir;
114    }
115
116    public File getStorageDirectory() {
117        return storageDir;
118    }
119
120    public void setResponseStatus(int status) {
121        this.status = status;
122    }
123
124    protected int getResponseStatus() {
125        return status;
126    }
127
128    protected Attributes create(Association as, Attributes rq, Attributes rqAttrs)
129            throws DicomServiceException {
130        if (storageDir == null)
131            return null;
132        String cuid = rq.getString(Tag.AffectedSOPClassUID);
133        String iuid = rq.getString(Tag.AffectedSOPInstanceUID);
134        File file = new File(storageDir, iuid);
135        if (file.exists())
136            throw new DicomServiceException(Status.DuplicateSOPinstance).
137                setUID(Tag.AffectedSOPInstanceUID, iuid);
138        DicomOutputStream out = null;
139        LOG.info("{}: M-WRITE {}", as, file);
140        try {
141            out = new DicomOutputStream(file);
142            out.writeDataset(
143                    Attributes.createFileMetaInformation(iuid, cuid,
144                            UID.ExplicitVRLittleEndian),
145                    rqAttrs);
146            out.close();
147            out = null;
148        } catch (IOException e) {
149            LOG.warn(as + ": Failed to store Instance Available Notification:", e);
150            throw new DicomServiceException(Status.ProcessingFailure, e);
151        } finally {
152            SafeClose.close(out);
153        }
154        return null;
155    }
156
157}