org.dcm4che3.tool.probetc.ProbeTC.java Source code

Java tutorial

Introduction

Here is the source code for org.dcm4che3.tool.probetc.ProbeTC.java

Source

/* ***** 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), hosted at https://github.com/gunterze/dcm4che.
 *
 * The Initial Developer of the Original Code is
 * Agfa Healthcare.
 * Portions created by the Initial Developer are Copyright (C) 2012
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * See @authors listed 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.dcm4che3.tool.probetc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dcm4che3.conf.api.ConfigurationException;
import org.dcm4che3.conf.api.DicomConfiguration;
import org.dcm4che3.conf.ldap.LdapDicomConfiguration;
import org.dcm4che3.conf.prefs.PreferencesDicomConfiguration;
import org.dcm4che3.net.ApplicationEntity;
import org.dcm4che3.net.Association;
import org.dcm4che3.net.Connection;
import org.dcm4che3.net.Device;
import org.dcm4che3.net.IncompatibleConnectionException;
import org.dcm4che3.net.TransferCapability;
import org.dcm4che3.net.TransferCapability.Role;
import org.dcm4che3.net.pdu.AAssociateRQ;
import org.dcm4che3.net.pdu.PresentationContext;
import org.dcm4che3.tool.common.CLIUtils;
import org.dcm4che3.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Hesham Elbadawi <bsdreko@gmail.com>
 * 
 */

public class ProbeTC {

    private String destinationAET;
    private static String probedAET;
    private String sourceAET;
    private ApplicationEntity probedAE;
    private File ldapConfigurationFile;
    private String configurationType;
    private String configType;
    private String callingAET;
    static final Logger LOG = LoggerFactory.getLogger(ProbeTC.class);
    private static Options opts;
    private static ResourceBundle rb = ResourceBundle.getBundle("org.dcm4che3.tool.probetc.messages");
    private Connection destination;
    private AAssociateRQ rq = new AAssociateRQ();

    ProbeTC() {
    }

    public ProbeTC(String probedae, String destinationaet, String sourceaet, String callingaet,
            String configurationType, File ldapConfigurationFile) throws ParseException {
        if (probedae == null || destinationaet == null || sourceaet == null || callingaet == null
                || configurationType == null) {
            LOG.error("null initialization parameter");
            throw new NullPointerException();
        }
        this.setConfigType(configurationType);
        this.setDestinationAET(destinationaet);

        if (ldapConfigurationFile != null && configurationType.compareToIgnoreCase("ldap") == 0)
            this.setLdapConfigurationFile(ldapConfigurationFile);
        else if (configurationType.compareToIgnoreCase("ldap") == 0) {
            LOG.error("Ldap properties file has to be a valid file");
            throw new NullPointerException();
        }
        this.setSourceAET(sourceaet);
        String tmpOption = probedae;
        String aeTitle = tmpOption.split("@")[0];

        if (tmpOption.split("@")[1] == null)
            throw new ParseException(rb.getString("invalid-probed-ae"));
        String host = tmpOption.split("@")[1].split(":")[0];
        int port = Integer.parseInt(tmpOption.split("@")[1].split(":")[1]);
        probedAET = aeTitle;
        Connection conn = new Connection();
        conn.setHostname(host);
        conn.setPort(port);
        conn.setConnectionInstalled(true);
        setDestination(conn);

        this.setProbedAE(new ApplicationEntity(aeTitle));
        this.getProbedAE().addConnection(conn);
        this.setCallingAET(callingaet);
    }

    @SuppressWarnings("static-access")
    private static CommandLine parseComandLine(String[] args) throws ParseException {
        opts = new Options();
        opts.addOption(OptionBuilder.hasArg().withArgName("aet@host:port")
                .withDescription(rb.getString("connection")).withLongOpt("connection").create("c"));
        opts.addOption("d", "destination-aet", true, rb.getString("destination-aet"));
        opts.addOption("s", "source-aet", true, rb.getString("source-aet"));
        opts.addOption("b", "broadcastTitle", true, rb.getString("broadcastTitle"));
        opts.addOption("ldap", "ldap", true, rb.getString("ldap"));
        opts.addOption("prefs", "prefs", false, rb.getString("prefs"));
        CLIUtils.addCommonOptions(opts);
        return CLIUtils.parseComandLine(args, opts, rb, ProbeTC.class);
    }

    public void probeAndSet() {
        ProbeTC instance = this;
        Device device = new Device(instance.getCallingAET());
        Connection conn = new Connection();
        device.addConnection(conn);
        ApplicationEntity ae = new ApplicationEntity(instance.getCallingAET());
        device.addApplicationEntity(ae);
        ae.addConnection(conn);

        conn.setReceivePDULength(Connection.DEF_MAX_PDU_LENGTH);
        conn.setSendPDULength(Connection.DEF_MAX_PDU_LENGTH);

        conn.setMaxOpsInvoked(0);
        conn.setMaxOpsPerformed(0);

        conn.setPackPDV(true);
        conn.setConnectTimeout(0);
        conn.setRequestTimeout(0);
        conn.setAcceptTimeout(0);
        conn.setReleaseTimeout(0);
        conn.setResponseTimeout(0);
        conn.setRetrieveTimeout(0);
        conn.setIdleTimeout(0);
        conn.setSocketCloseDelay(Connection.DEF_SOCKETDELAY);
        conn.setSendBufferSize(0);
        conn.setReceiveBufferSize(0);
        conn.setTcpNoDelay(true);

        // no tls in this implementation (for tls use command line tool)
        if (instance.getConfigType().compareToIgnoreCase("ldap") == 0) {
            InputStream is = null;
            try {
                is = new FileInputStream(instance.getLdapConfigurationFile());
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            Properties p = new Properties();
            try {
                p.load(is);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                DicomConfiguration conf = new LdapDicomConfiguration(p);
                LOG.info("Started Loading LDAP configuration");
                ApplicationEntity sourceAE = conf.findApplicationEntity(instance.sourceAET);
                ArrayList<TransferCapability> tcs = (ArrayList<TransferCapability>) sourceAE
                        .getTransferCapabilities();
                ArrayList<PresentationContext> pcs = addChunkedPCsandSend(ae, device, instance, tcs);
                // print accepted ones
                ArrayList<PresentationContext> acceptedPCs = new ArrayList<PresentationContext>();
                for (PresentationContext pc : pcs)
                    if (pc.isAccepted())
                        acceptedPCs.add(pc);

                ApplicationEntity destinationAE = conf.findApplicationEntity(instance.destinationAET);
                Device toStore = destinationAE.getDevice();
                TransferCapability[] TCs = mergeTCs(acceptedPCs);
                for (TransferCapability tc : TCs)
                    toStore.getApplicationEntity(instance.destinationAET).addTransferCapability(tc);

                conf.merge(toStore);
                conf.close();
                return;
            } catch (ConfigurationException e) {
                LOG.error("Configuration backend error - {}", e);
            }
        } else {

            try {
                DicomConfiguration conf = new PreferencesDicomConfiguration();
                LOG.info("Started Loading LDAP configuration");
                ApplicationEntity sourceAE = conf.findApplicationEntity(instance.sourceAET);
                ArrayList<TransferCapability> tcs = (ArrayList<TransferCapability>) sourceAE
                        .getTransferCapabilities();
                ArrayList<PresentationContext> pcs = addChunkedPCsandSend(ae, device, instance, tcs);
                // print accepted ones
                ArrayList<PresentationContext> acceptedPCs = new ArrayList<PresentationContext>();
                for (PresentationContext pc : pcs)
                    if (pc.isAccepted())
                        acceptedPCs.add(pc);

                ApplicationEntity destinationAE = conf.findApplicationEntity(instance.destinationAET);
                Device toStore = destinationAE.getDevice();
                TransferCapability[] TCs = mergeTCs(acceptedPCs);
                for (TransferCapability tc : TCs)
                    toStore.getApplicationEntity(instance.destinationAET).addTransferCapability(tc);

                conf.merge(toStore);
                conf.close();
                return;
            } catch (ConfigurationException e) {
                LOG.error("Configuration backend error - {}", e);
            }
        }

    }

    public static void main(String[] args) throws ParseException, IOException, InterruptedException,
            IncompatibleConnectionException, GeneralSecurityException, IllegalAccessException {
        CommandLine cl = null;

        cl = parseComandLine(args);
        ProbeTC instance = new ProbeTC();
        Device device = null;
        if (cl.hasOption("b")) {
            device = new Device(cl.getOptionValue("b").toLowerCase());
        } else {
            LOG.error("Missing broadcast AETitle");
            throw new IllegalAccessException("missing broadcast AETitle");
        }
        Connection conn = new Connection();
        device.addConnection(conn);
        ApplicationEntity ae = new ApplicationEntity(cl.getOptionValue("b").toUpperCase());
        if (cl.hasOption("d")) {
            instance.setDestinationAET(cl.getOptionValue("d"));
        }
        if (cl.hasOption("s")) {
            instance.setSourceAET(cl.getOptionValue("s"));

        }
        device.addApplicationEntity(ae);
        ae.addConnection(conn);

        instance.destination = new Connection();

        configureConnect(instance.destination, instance.rq, cl, ae);
        CLIUtils.configure(conn, cl);
        // instance.sourceAE = new
        // ApplicationEntity(cl.getOptionValue("s").split(":")[0]);

        // here load the TCs

        if (cl.hasOption("ldap")) {
            try {
                InputStream is = null;
                Properties p = new Properties();
                if (!cl.getOptionValue("ldap").isEmpty()) {
                    is = new FileInputStream(new File(cl.getOptionValue("ldap")));
                } else {
                    LOG.error("Missing ldap properties file");
                    throw new IllegalAccessException("missing ldap properties file");
                }
                p.load(is);
                DicomConfiguration conf = new LdapDicomConfiguration(p);
                LOG.info("Started Loading LDAP configuration");
                ArrayList<TransferCapability> tcs = null;
                if (cl.hasOption("s")) {
                    ApplicationEntity sourceAE = conf.findApplicationEntity(instance.sourceAET);
                    tcs = (ArrayList<TransferCapability>) sourceAE.getTransferCapabilities();
                } else {
                    tcs = loadTCFile();
                }
                ArrayList<PresentationContext> pcs = addChunkedPCsandSend(ae, device, instance, tcs);
                // print accepted ones
                ArrayList<PresentationContext> acceptedPCs = new ArrayList<PresentationContext>();
                for (PresentationContext pc : pcs)
                    if (pc.isAccepted())
                        acceptedPCs.add(pc);

                LOG.info("Probed the source ae and found the following accepted presentation contexts");
                for (PresentationContext pc : acceptedPCs) {
                    LOG.info("PC[" + pc.getPCID() + "]\tAbstractSyntax:" + pc.getAbstractSyntax() + "\n with "
                            + " the following Transfer-Syntax:[" + pc.getTransferSyntax() + "]");

                }
                LOG.info("finished probing TCs");
                if (instance.destinationAET != null) {
                    LOG.info("Adding Accepted TCs to configuration backend");
                    ApplicationEntity destinationAE = conf.findApplicationEntity(instance.destinationAET);

                    Device toStore = destinationAE.getDevice();
                    TransferCapability[] TCs = mergeTCs(acceptedPCs);
                    for (TransferCapability tc : TCs)
                        toStore.getApplicationEntity(instance.destinationAET).addTransferCapability(tc);

                    conf.merge(toStore);
                    logAddedTCs(TCs, destinationAE);
                    conf.close();
                }
                System.exit(1);
            } catch (ConfigurationException e) {
                LOG.error("Configuration backend error - {}", e);
            }
        } else if (cl.hasOption("prefs")) {
            // prefs
            try {
                DicomConfiguration conf = new PreferencesDicomConfiguration();
                LOG.info("Started Loading LDAP configuration");
                ArrayList<TransferCapability> tcs = null;
                if (cl.hasOption("s")) {
                    ApplicationEntity sourceAE = conf.findApplicationEntity(instance.sourceAET);
                    tcs = (ArrayList<TransferCapability>) sourceAE.getTransferCapabilities();
                } else {
                    tcs = loadTCFile();
                }
                ArrayList<PresentationContext> pcs = addChunkedPCsandSend(ae, device, instance, tcs);
                // print accepted ones
                ArrayList<PresentationContext> acceptedPCs = new ArrayList<PresentationContext>();
                for (PresentationContext pc : pcs)
                    if (pc.isAccepted())
                        acceptedPCs.add(pc);

                LOG.info("Probed the source ae and found the following accepted presentation contexts");
                for (PresentationContext pc : acceptedPCs) {
                    LOG.info("PC[" + pc.getPCID() + "]\tAbstractSyntax:" + pc.getAbstractSyntax() + "\n with "
                            + " the following Transfer-Syntax:[" + pc.getTransferSyntax() + "]");

                }
                LOG.info("finished probing TCs");
                if (instance.destinationAET != null) {
                    LOG.info("Adding Accepted TCs to configuration backend");
                    ApplicationEntity destinationAE = conf.findApplicationEntity(instance.destinationAET);

                    Device toStore = destinationAE.getDevice();
                    TransferCapability[] TCs = mergeTCs(acceptedPCs);
                    for (TransferCapability tc : TCs)
                        toStore.getApplicationEntity(instance.destinationAET).addTransferCapability(tc);

                    conf.merge(toStore);
                    logAddedTCs(TCs, destinationAE);
                    conf.close();
                }
                System.exit(1);
            } catch (ConfigurationException e) {
                LOG.error("Configuration backend error - {}", e);
            }
        } else {

            LOG.info("Started Loading TCS from file no configuration set or get");
            ArrayList<TransferCapability> tcs = null;
            tcs = loadTCFile();
            LOG.info("added the following presentation contexts: " + tcs.get(0).getSopClass());
            ArrayList<PresentationContext> pcs = addChunkedPCsandSend(ae, device, instance, tcs);
            // print accepted ones
            ArrayList<PresentationContext> acceptedPCs = new ArrayList<PresentationContext>();
            for (PresentationContext pc : pcs)
                if (pc.isAccepted())
                    acceptedPCs.add(pc);

            LOG.info("Probed the source ae and found the following accepted presentation contexts");
            for (PresentationContext pc : acceptedPCs) {
                LOG.info("PC[" + pc.getPCID() + "]\tAbstractSyntax:" + pc.getAbstractSyntax() + "\n with "
                        + " the following Transfer-Syntax:[" + pc.getTransferSyntax() + "]");

            }
            LOG.info("finished probing TCs");

            System.exit(1);

        }

    }

    private static ArrayList<TransferCapability> loadTCFile() {
        ArrayList<TransferCapability> tcs = new ArrayList<TransferCapability>();
        Properties p = null;
        try {
            p = CLIUtils.loadProperties("resource:sampleTCFile.properties", null);
        } catch (IOException e) {
            LOG.error("unable to load sop-classes properties file");
        }
        for (String cuid : p.stringPropertyNames()) {
            String ts = p.getProperty(cuid);
            LOG.info(ts);
            tcs.add(new TransferCapability(null, ts.split(":")[0], TransferCapability.Role.SCP,
                    StringUtils.split(ts.split(":")[1], ',')));
        }
        return tcs;
    }

    public static void configureConnect(Connection conn, AAssociateRQ rq, CommandLine cl, ApplicationEntity ae)
            throws ParseException, IOException {
        if (!cl.hasOption("c"))
            throw new MissingOptionException(rb.getString("missing-probed-ae"));
        String tmpOption = cl.getOptionValue("c");
        String aeTitle = tmpOption.split("@")[0];

        if (tmpOption.split("@")[1] == null)
            throw new ParseException(rb.getString("invalid-probed-ae"));
        String host = tmpOption.split("@")[1].split(":")[0];
        int port = Integer.parseInt(tmpOption.split("@")[1].split(":")[1]);
        probedAET = aeTitle;
        conn.setHostname(host);
        conn.setPort(port);
        conn.setConnectionInstalled(true);
        ae = new ApplicationEntity(aeTitle);
        ae.addConnection(conn);
    }

    public static Association openAssociation(ApplicationEntity ae, AAssociateRQ rq, Connection remote)
            throws IOException, InterruptedException, IncompatibleConnectionException, GeneralSecurityException {
        Association as = ae.connect(remote, rq);
        return as;
    }

    private static void logAddedTCs(TransferCapability[] tCs, ApplicationEntity destinationAE) {
        File log = new File("probe-tc-log-[ae=" + probedAET + "]");
        FileWriter logWriter = null;
        try {
            logWriter = new FileWriter(log);
        } catch (IOException e) {
            LOG.error("Unable to get output stream for log file - {}", e);
        }
        try {
            for (TransferCapability tc : tCs)
                logWriter.write(tc.toString());
        } catch (IOException e) {
            LOG.error("Error writing log for transfer capabilities set - {}", e);
        } finally {
            try {
                logWriter.close();
            } catch (IOException e) {
                LOG.error("Unable to close log File - {} - {}", log.getName(), e);
            }
        }

    }

    private static TransferCapability[] mergeTCs(ArrayList<PresentationContext> acceptedPCs) {
        ArrayList<TransferCapability> tmpTCs = new ArrayList<TransferCapability>();
        for (PresentationContext pc : acceptedPCs) {
            String abstractSyntax = pc.getAbstractSyntax();
            if (containsAbstractSyntax(tmpTCs, abstractSyntax)) {
                continue;
            }
            TransferCapability tmpTC = new TransferCapability();
            tmpTC.setRole(Role.SCP);
            ArrayList<String> tmpTS = new ArrayList<String>();
            tmpTC.setSopClass(abstractSyntax);
            for (PresentationContext tmp : acceptedPCs) {

                if (tmp.getAbstractSyntax().compareToIgnoreCase(abstractSyntax) == 0) {
                    if (!tmpTS.contains(tmp.getTransferSyntax())) {
                        tmpTS.add(tmp.getTransferSyntax());
                    }
                }
            }
            String[] tmpTSStr = new String[tmpTS.size()];
            tmpTS.toArray(tmpTSStr);
            tmpTC.setTransferSyntaxes(tmpTSStr);
            tmpTCs.add(tmpTC);

        }
        TransferCapability[] TCs = new TransferCapability[tmpTCs.size()];
        tmpTCs.toArray(TCs);
        return TCs;
    }

    private static boolean containsAbstractSyntax(ArrayList<TransferCapability> tmpTCs, String abstractSyntax) {
        for (TransferCapability tc : tmpTCs) {
            if (tc.getSopClass().compareToIgnoreCase(abstractSyntax) == 0) {
                return true;
            }
        }
        return false;
    }

    private static ArrayList<PresentationContext> addChunkedPCsandSend(ApplicationEntity ae, Device device,
            ProbeTC instance, ArrayList<TransferCapability> tcs) {
        initThreads(device);
        int pcID = 1;
        ArrayList<ArrayList<PresentationContext>> lst = new ArrayList<ArrayList<PresentationContext>>();
        ArrayList<PresentationContext> fullListSingleTS = new ArrayList<PresentationContext>();
        ArrayList<PresentationContext> allACPCs = new ArrayList<PresentationContext>();

        for (TransferCapability tc : tcs)
            if (tcs.size() > 127)
                for (String ts : tc.getTransferSyntaxes()) {
                    fullListSingleTS.add(new PresentationContext(pcID, tc.getSopClass(), ts));
                    pcID++;
                    if (fullListSingleTS.size() > 127) {
                        lst.add(fullListSingleTS);
                        pcID = 1;
                        fullListSingleTS = new ArrayList<PresentationContext>();
                    }
                }
            else {
                for (String ts : tc.getTransferSyntaxes()) {
                    fullListSingleTS.add(new PresentationContext(pcID, tc.getSopClass(), ts));
                    pcID++;
                }
                lst.add(fullListSingleTS);
            }

        instance.rq.setCallingAET(ae.getAETitle());
        instance.rq.setCalledAET(probedAET);
        // now start sending 128 each
        for (ArrayList<PresentationContext> subList : lst) {
            instance.rq = new AAssociateRQ();
            instance.rq.setCallingAET(ae.getAETitle());
            instance.rq.setCalledAET(probedAET);
            for (PresentationContext pc : subList)
                instance.rq.addPresentationContext(pc);
            try {
                // send
                Association as = openAssociation(ae, instance.rq, instance.destination);
                // cache the pcs
                for (PresentationContext pcAC : as.getAAssociateAC().getPresentationContexts()) {
                    if (pcAC.isAccepted())
                        allACPCs.add(instance.rq.getPresentationContext(pcAC.getPCID()));
                }

                as.release();
            } catch (Exception e) {
                e.printStackTrace();
                // LOG.info("destination rejected the association for the following reason:\n"
                // + as.getException());
                System.exit(1);
            }
        }

        return allACPCs;
    }

    private static void initThreads(Device device) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        device.setExecutor(executorService);
        device.setScheduledExecutor(scheduledExecutorService);
    }

    public String getDestinationAET() {
        return destinationAET;
    }

    public void setDestinationAET(String destinationAET) {
        this.destinationAET = destinationAET;
    }

    public String getSourceAET() {
        return sourceAET;
    }

    public void setSourceAET(String sourceAET) {
        this.sourceAET = sourceAET;
    }

    public ApplicationEntity getProbedAE() {
        return probedAE;
    }

    public void setProbedAE(ApplicationEntity probedAE) {
        this.probedAE = probedAE;
    }

    public File getLdapConfigurationFile() {
        return ldapConfigurationFile;
    }

    public void setLdapConfigurationFile(File ldapConfigurationFile) {
        this.ldapConfigurationFile = ldapConfigurationFile;
    }

    public String getConfigurationType() {
        return configurationType;
    }

    public void setConfigurationType(String configurationType) {
        this.configurationType = configurationType;
    }

    public String getConfigType() {
        return configType;
    }

    public void setConfigType(String configType) {
        this.configType = configType;
    }

    public Connection getDestination() {
        return destination;
    }

    public void setDestination(Connection destination) {
        this.destination = destination;
    }

    public AAssociateRQ getRq() {
        return rq;
    }

    public void setRq(AAssociateRQ rq) {
        this.rq = rq;
    }

    public String getCallingAET() {
        return callingAET;
    }

    public void setCallingAET(String callingAET) {
        this.callingAET = callingAET;
    }
}