Java tutorial
/* ***** 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; } }