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 http://sourceforge.net/projects/dcm4che. * * The Initial Developer of the Original Code is * Gunter Zeilinger, Huetteldorferstr. 24/10, 1150 Vienna/Austria/Europe. * Portions created by the Initial Developer are Copyright (C) 2002-2005 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Gunter Zeilinger <gunterze@gmail.com> * * 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.dcm4che2.tool.dcmgpwl; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Date; import java.util.List; import java.util.concurrent.Executor; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.dcm4che2.data.BasicDicomObject; import org.dcm4che2.data.DicomElement; import org.dcm4che2.data.DicomObject; import org.dcm4che2.data.Tag; import org.dcm4che2.data.UID; import org.dcm4che2.data.UIDDictionary; import org.dcm4che2.data.VR; import org.dcm4che2.io.DicomInputStream; import org.dcm4che2.io.DicomOutputStream; import org.dcm4che2.io.StopTagInputHandler; import org.dcm4che2.net.Association; import org.dcm4che2.net.CommandUtils; import org.dcm4che2.net.ConfigurationException; import org.dcm4che2.net.Device; import org.dcm4che2.net.DimseRSP; import org.dcm4che2.net.NetworkApplicationEntity; import org.dcm4che2.net.NetworkConnection; import org.dcm4che2.net.NewThreadExecutor; import org.dcm4che2.net.NoPresentationContextException; import org.dcm4che2.net.TransferCapability; import org.dcm4che2.net.UserIdentity; import org.dcm4che2.util.CloseUtils; /** * @author gunter zeilinger(gunterze@gmail.com) * @version $Revision: 14420 $ $Date: 2010-11-26 14:22:59 +0100 (Fri, 26 Nov 2010) $ * @since Mar 18, 2006 * */ public class DcmGPWL { private static final int KB = 1024; private static final String USAGE = "dcmgpwl <aet>[@<host>[:<port>]] [Options]"; private static final String DESCRIPTION = "Query specified remote Application Entity (=General Purpose Worklist SCP) " + "If <port> is not specified, DICOM default port 104 is assumed. " + "If also no <host> is specified localhost is assumed.\n" + "Options:"; private static final String EXAMPLE = "\nExample: dcmgpwl GPWLSCP@localhost:11112 -status SCHEDULED\n" + "=> Query Application Entity GPWLSCP listening on local port 11112 for " + "all scheduled GP-SPS"; private static final byte[] EXT_NEG_INFO_FUZZY_MATCHING = { 1, 1, 1 }; private static String[] TLS1 = { "TLSv1" }; private static String[] SSL3 = { "SSLv3" }; private static String[] NO_TLS1 = { "SSLv3", "SSLv2Hello" }; private static String[] NO_SSL2 = { "TLSv1", "SSLv3" }; private static String[] NO_SSL3 = { "TLSv1", "SSLv2Hello" }; private static char[] SECRET = { 's', 'e', 'c', 'r', 'e', 't' }; private static final String[] SOP_CUIDS = { UID.GeneralPurposeWorklistInformationModelFIND, UID.GeneralPurposeScheduledProcedureStepSOPClass, UID.GeneralPurposePerformedProcedureStepSOPClass }; private static final String[] METASOP_CUID = { UID.GeneralPurposeWorklistManagementMetaSOPClass }; private static final int[] RETURN_KEYS = { Tag.SOPClassUID, Tag.SOPInstanceUID, Tag.PatientName, Tag.PatientID, Tag.PatientBirthDate, Tag.PatientSex, Tag.StudyInstanceUID, Tag.ScheduledProcedureStepID, Tag.GeneralPurposeScheduledProcedureStepStatus, Tag.GeneralPurposeScheduledProcedureStepPriority, Tag.ScheduledProcedureStepStartDateTime, Tag.MultipleCopiesFlag, Tag.ScheduledProcedureStepModificationDateTime, Tag.ExpectedCompletionDateTime, Tag.InputAvailabilityFlag, }; private static final int[] REQUEST_RETURN_KEYS = { Tag.AccessionNumber, Tag.StudyInstanceUID, Tag.RequestingPhysician, Tag.RequestedProcedureDescription, Tag.RequestedProcedureID, }; private static final int[] RETURN_SQ_KEYS = { Tag.ReferencedPerformedProcedureStepSequence, Tag.ScheduledProcessingApplicationsCodeSequence, Tag.ResultingGeneralPurposePerformedProcedureStepsSequence, Tag.ScheduledWorkitemCodeSequence, Tag.InputInformationSequence, Tag.RelevantInformationSequence, Tag.ScheduledStationNameCodeSequence, Tag.ScheduledStationClassCodeSequence, Tag.ScheduledStationGeographicLocationCodeSequence, Tag.ScheduledHumanPerformersSequence, Tag.ActualHumanPerformersSequence, Tag.ReferencedRequestSequence, }; private static final int[] PPS_CREATE_TYPE2 = { Tag.PatientName, Tag.PatientID, Tag.PatientBirthDate, Tag.PatientSex, Tag.PerformedProcedureStepDescription, }; private static final int[] PPS_SQ_CREATE_TYPE2 = { Tag.PerformedProcessingApplicationsCodeSequence, Tag.PerformedWorkitemCodeSequence, Tag.PerformedStationNameCodeSequence, Tag.PerformedStationClassCodeSequence, Tag.PerformedStationGeographicLocationCodeSequence, Tag.OutputInformationSequence, Tag.ActualHumanPerformersSequence, Tag.ReferencedRequestSequence, }; private static final int[] SPS_SQ_PPS_SQ_MAP = { Tag.ScheduledProcessingApplicationsCodeSequence, Tag.PerformedProcessingApplicationsCodeSequence, Tag.ScheduledWorkitemCodeSequence, Tag.PerformedWorkitemCodeSequence, Tag.ScheduledStationNameCodeSequence, Tag.PerformedStationNameCodeSequence, Tag.ScheduledStationClassCodeSequence, Tag.PerformedStationClassCodeSequence, Tag.ScheduledStationGeographicLocationCodeSequence, Tag.PerformedStationGeographicLocationCodeSequence, Tag.ScheduledHumanPerformersSequence, Tag.ActualHumanPerformersSequence, }; private static final int[] PPS_CREATE_FROM_SPS = { Tag.SpecificCharacterSet, Tag.PatientName, Tag.PatientID, Tag.PatientBirthDate, Tag.PatientSex, Tag.ActualHumanPerformersSequence, Tag.ReferencedRequestSequence, }; private static final String[] IVRLE_TS = { UID.ImplicitVRLittleEndian }; private static final String[] LE_TS = { UID.ExplicitVRLittleEndian, UID.ImplicitVRLittleEndian }; private final Executor executor; private final NetworkApplicationEntity remoteAE = new NetworkApplicationEntity(); private final NetworkConnection remoteConn = new NetworkConnection(); private final Device device; private final NetworkApplicationEntity ae = new NetworkApplicationEntity(); private final NetworkConnection conn = new NetworkConnection(); private File outDir; private String retrieveAET; private Association assoc; private int priority = 0; private int cancelAfter = Integer.MAX_VALUE; private boolean fuzzySemanticPersonNameMatching; private final DicomObject attrs = new BasicDicomObject(); private String keyStoreURL = "resource:tls/test_sys_1.p12"; private char[] keyStorePassword = SECRET; private char[] keyPassword; private String trustStoreURL = "resource:tls/mesa_certs.jks"; private char[] trustStorePassword = SECRET; public DcmGPWL(String name) { device = new Device(name); executor = new NewThreadExecutor(name); remoteAE.setInstalled(true); remoteAE.setAssociationAcceptor(true); remoteAE.setNetworkConnection(new NetworkConnection[] { remoteConn }); device.setNetworkApplicationEntity(ae); device.setNetworkConnection(conn); ae.setNetworkConnection(conn); ae.setAssociationInitiator(true); ae.setAETitle(name); } public void initQuery() { for (int i = 0; i < RETURN_KEYS.length; i++) { attrs.putNull(RETURN_KEYS[i], null); } for (int i = 0; i < RETURN_SQ_KEYS.length; i++) { attrs.putNestedDicomObject(RETURN_SQ_KEYS[i], new BasicDicomObject()); } DicomObject rqAttrs = new BasicDicomObject(); attrs.putNestedDicomObject(Tag.ReferencedRequestSequence, rqAttrs); for (int i = 0; i < REQUEST_RETURN_KEYS.length; i++) { rqAttrs.putNull(REQUEST_RETURN_KEYS[i], null); } rqAttrs.putNestedDicomObject(Tag.RequestedProcedureCodeSequence, new BasicDicomObject()); } public void initAction() { attrs.putString(Tag.GeneralPurposeScheduledProcedureStepStatus, VR.CS, "IN PROGRESS"); } public void initCreatePPS(String[] refsps) { long ts = System.currentTimeMillis(); Date now = new Date(ts); attrs.putString(Tag.PerformedProcedureStepID, VR.SH, Long.toString(ts)); attrs.putDate(Tag.PerformedProcedureStepStartDate, VR.DA, now); attrs.putDate(Tag.PerformedProcedureStepStartTime, VR.TM, now); attrs.putString(Tag.GeneralPurposePerformedProcedureStepStatus, VR.CS, "IN PROGRESS"); for (int i = 0; i < PPS_CREATE_TYPE2.length; i++) { attrs.putNull(PPS_CREATE_TYPE2[i], null); } for (int i = 0; i < PPS_SQ_CREATE_TYPE2.length; i++) { attrs.putSequence(PPS_SQ_CREATE_TYPE2[i]); } if (refsps != null) { DicomObject item = new BasicDicomObject(); item.putString(Tag.ReferencedSOPClassUID, VR.UI, UID.GeneralPurposeScheduledProcedureStepSOPClass); item.putString(Tag.ReferencedSOPInstanceUID, VR.UI, refsps[0]); item.putString(Tag.ReferencedGeneralPurposeScheduledProcedureStepTransactionUID, VR.UI, refsps[1]); attrs.putNestedDicomObject(Tag.ReferencedGeneralPurposeScheduledProcedureStepSequence, item); if (outDir != null) { File f = new File(outDir, refsps[0]); if (f.isFile()) { DicomInputStream din = null; try { din = new DicomInputStream(f); DicomObject sps = din.readDicomObject(); for (int i = 1; i < SPS_SQ_PPS_SQ_MAP.length; i++, i++) { DicomObject codeItem = sps.getNestedDicomObject(SPS_SQ_PPS_SQ_MAP[i - 1]); if (codeItem != null) { attrs.putNestedDicomObject(SPS_SQ_PPS_SQ_MAP[i], codeItem); } } sps.subSet(PPS_CREATE_FROM_SPS).copyTo(attrs); } catch (IOException e) { System.out.println("WARNING: Failed to read " + f + ": " + e.getMessage()); } finally { CloseUtils.safeClose(din); } } } } } public void addOutput(DicomObject inst) { DicomObject studyRef = findOrCreateItem(attrs.get(Tag.OutputInformationSequence), Tag.StudyInstanceUID, inst.getString(Tag.StudyInstanceUID), Tag.ReferencedSeriesSequence); DicomObject seriesRef = findOrCreateItem(studyRef.get(Tag.ReferencedSeriesSequence), Tag.SeriesInstanceUID, inst.getString(Tag.SeriesInstanceUID), Tag.ReferencedSOPSequence); DicomObject refSOP = new BasicDicomObject(); refSOP.putString(Tag.ReferencedSOPClassUID, VR.UI, inst.getString(Tag.SOPClassUID)); refSOP.putString(Tag.ReferencedSOPInstanceUID, VR.UI, inst.getString(Tag.SOPInstanceUID)); refSOP.putString(Tag.RetrieveAETitle, VR.AE, retrieveAET); seriesRef.get(Tag.ReferencedSOPSequence).addDicomObject(refSOP); } private DicomObject findOrCreateItem(DicomElement sq, int tag, String uid, int sqtag) { DicomObject item; for (int i = 0, n = sq.countItems(); i < n; i++) { item = sq.getDicomObject(i); if (uid.equals(item.getString(tag))) { return item; } } item = new BasicDicomObject(); item.putString(tag, VR.UI, uid); item.putSequence(sqtag); sq.addDicomObject(item); return item; } public void initSetPPS() { attrs.putSequence(Tag.OutputInformationSequence); } public final void setLocalHost(String hostname) { conn.setHostname(hostname); } public final void setRemoteHost(String hostname) { remoteConn.setHostname(hostname); } public final void setRemotePort(int port) { remoteConn.setPort(port); } public final void setTlsProtocol(String[] tlsProtocol) { conn.setTlsProtocol(tlsProtocol); } public final void setTlsWithoutEncyrption() { conn.setTlsWithoutEncyrption(); remoteConn.setTlsWithoutEncyrption(); } public final void setTls3DES_EDE_CBC() { conn.setTls3DES_EDE_CBC(); remoteConn.setTls3DES_EDE_CBC(); } public final void setTlsAES_128_CBC() { conn.setTlsAES_128_CBC(); remoteConn.setTlsAES_128_CBC(); } public final void setTlsNeedClientAuth(boolean needClientAuth) { conn.setTlsNeedClientAuth(needClientAuth); } public final void setKeyStoreURL(String url) { keyStoreURL = url; } public final void setKeyStorePassword(String pw) { keyStorePassword = pw.toCharArray(); } public final void setKeyPassword(String pw) { keyPassword = pw.toCharArray(); } public final void setTrustStorePassword(String pw) { trustStorePassword = pw.toCharArray(); } public final void setTrustStoreURL(String url) { trustStoreURL = url; } public final void setCalledAET(String called) { remoteAE.setAETitle(called); } public final void setCalling(String calling) { ae.setAETitle(calling); } public final void setUserIdentity(UserIdentity userIdentity) { ae.setUserIdentity(userIdentity); } public final void setPriority(int priority) { this.priority = priority; } public final void setOutDir(File out) { if (out.mkdirs()) { System.out.println("M-WRITE " + out); } this.outDir = out; } public final void setConnectTimeout(int connectTimeout) { conn.setConnectTimeout(connectTimeout); } public final void setMaxPDULengthReceive(int maxPDULength) { ae.setMaxPDULengthReceive(maxPDULength); } public final void setPackPDV(boolean packPDV) { ae.setPackPDV(packPDV); } public final void setAssociationReaperPeriod(int period) { device.setAssociationReaperPeriod(period); } public final void setDimseRspTimeout(int timeout) { ae.setDimseRspTimeout(timeout); } public final void setTcpNoDelay(boolean tcpNoDelay) { conn.setTcpNoDelay(tcpNoDelay); } public final void setAcceptTimeout(int timeout) { conn.setAcceptTimeout(timeout); } public final void setReleaseTimeout(int timeout) { conn.setReleaseTimeout(timeout); } public final void setSocketCloseDelay(int timeout) { conn.setSocketCloseDelay(timeout); } public final void setMaxPDULengthSend(int maxPDULength) { ae.setMaxPDULengthSend(maxPDULength); } public final void setReceiveBufferSize(int bufferSize) { conn.setReceiveBufferSize(bufferSize); } public final void setSendBufferSize(int bufferSize) { conn.setSendBufferSize(bufferSize); } public final void setCancelAfter(int limit) { this.cancelAfter = limit; } public final void setRetrieveAET(String aet) { this.retrieveAET = aet; } public void setFuzzySemanticPersonNameMatching(boolean b) { this.fuzzySemanticPersonNameMatching = b; } public void addAttr(int tag, String value) { attrs.putString(tag, null, value); } public void addRefRequestAttr(int tag, String value) { DicomObject rqAttrs = attrs.getNestedDicomObject(Tag.ReferencedRequestSequence); if (rqAttrs == null) { rqAttrs = new BasicDicomObject(); attrs.putNestedDicomObject(Tag.ReferencedRequestSequence, rqAttrs); } rqAttrs.putString(tag, null, value); } public void addCodeValueAndScheme(int tag, String[] code) { DicomObject item = attrs.getNestedDicomObject(tag); if (item == null) { item = new BasicDicomObject(); attrs.putNestedDicomObject(tag, item); } setCodeValueAndScheme(item, code); } private void setCodeValueAndScheme(DicomObject codeItem, String[] code) { codeItem.putString(Tag.CodeValue, VR.SH, code[0]); codeItem.putString(Tag.CodingSchemeDesignator, VR.SH, code[1]); codeItem.putString(Tag.CodeMeaning, VR.LO, code[2]); } public void setScheduledHumanPerformerCodeValueAndScheme(String[] valueAndScheme) { DicomObject performerKeys = attrs.getNestedDicomObject(Tag.ScheduledHumanPerformersSequence); DicomObject codeItem = new BasicDicomObject(); setCodeValueAndScheme(codeItem, valueAndScheme); performerKeys.putNestedDicomObject(Tag.HumanPerformerCodeSequence, codeItem); performerKeys.putNull(Tag.HumanPerformerName, VR.PN); performerKeys.putNull(Tag.HumanPerformerOrganization, VR.LO); } public void setActualHumanPerformer(String[] code, String name, String org) { DicomObject performerKeys = new BasicDicomObject(); attrs.putNestedDicomObject(Tag.ActualHumanPerformersSequence, performerKeys); BasicDicomObject codeItem = new BasicDicomObject(); codeItem.putString(Tag.CodeValue, VR.SH, code[0]); codeItem.putString(Tag.CodingSchemeDesignator, VR.SH, code[1]); codeItem.putString(Tag.CodeMeaning, VR.LO, code[2]); performerKeys.putNestedDicomObject(Tag.HumanPerformerCodeSequence, codeItem); if (name != null) { performerKeys.putString(Tag.HumanPerformerName, VR.PN, name); } if (org != null) { performerKeys.putString(Tag.HumanPerformerOrganization, VR.LO, org); } } public void setSPSStatus(String status) { attrs.putString(Tag.GeneralPurposeScheduledProcedureStepStatus, VR.CS, status); } public void setPPSStatus(String status) { attrs.putString(Tag.GeneralPurposePerformedProcedureStepStatus, VR.CS, status); if ("COMPLETED".equals(status) || "DISCONTINUED".equals(status)) { Date now = new Date(); attrs.putDate(Tag.PerformedProcedureStepEndDate, VR.DA, now); attrs.putDate(Tag.PerformedProcedureStepEndTime, VR.TM, now); } } public void configureTransferCapability(String[] cuids, String[] ts) { TransferCapability[] tc = new TransferCapability[cuids.length]; for (int i = 0; i < tc.length; i++) { tc[i] = new TransferCapability(cuids[i], ts, TransferCapability.SCU); if (fuzzySemanticPersonNameMatching && UID.GeneralPurposeWorklistInformationModelFIND.equals(cuids[i])) tc[i].setExtInfo(EXT_NEG_INFO_FUZZY_MATCHING); } ae.setTransferCapability(tc); } public void setTransactionUID(String uid) { attrs.putString(Tag.TransactionUID, VR.UI, uid); } public void open() throws IOException, ConfigurationException, InterruptedException { assoc = ae.connect(remoteAE, executor); } public void close() throws InterruptedException { assoc.release(true); } private TransferCapability selectTransferCapability(String cuid) throws NoPresentationContextException { TransferCapability tc = assoc.getTransferCapabilityAsSCU(UID.GeneralPurposeWorklistManagementMetaSOPClass); if (tc == null) { tc = assoc.getTransferCapabilityAsSCU(cuid); if (tc == null) { throw new NoPresentationContextException( UIDDictionary.getDictionary().prompt(cuid) + "not supported by " + remoteAE.getAETitle()); } } return tc; } public int query() throws IOException, InterruptedException { TransferCapability tc = selectTransferCapability(UID.GeneralPurposeWorklistInformationModelFIND); System.out.println("Send Query Request:"); System.out.println(attrs.toString()); DimseRSP rsp = assoc.cfind(UID.GeneralPurposeWorklistInformationModelFIND, priority, attrs, tc.getTransferSyntax()[0], cancelAfter); int count = 0; while (rsp.next()) { DicomObject cmd = rsp.getCommand(); if (CommandUtils.isPending(cmd)) { DicomObject data = rsp.getDataset(); count++; System.out.println("\nReceived Query Response #" + count + ":"); System.out.println(data.toString()); if (outDir != null) { String iuid = data.getString(Tag.SOPInstanceUID); File f = new File(outDir, iuid); System.out.println("M-WRITE " + f); FileOutputStream fos = new FileOutputStream(f); BufferedOutputStream bos = new BufferedOutputStream(fos); DicomOutputStream dos = new DicomOutputStream(bos); try { data.initFileMetaInformation(UID.ExplicitVRLittleEndian); dos.writeDicomFile(data); } finally { dos.close(); } } } } return count; } private void action(String iuid) throws IOException, InterruptedException { TransferCapability tc = selectTransferCapability(UID.GeneralPurposeScheduledProcedureStepSOPClass); System.out.println("Send GP-SPS Modify Request:"); System.out.println(attrs.toString()); DimseRSP rsp = assoc.naction(UID.GeneralPurposeScheduledProcedureStepSOPClass, iuid, 1, attrs, tc.getTransferSyntax()[0]); rsp.next(); DicomObject cmd = rsp.getCommand(); System.out.println("\nReceived GP-PS Modify Response:"); System.out.println(cmd.toString()); } public void createpps(String iuid) throws IOException, InterruptedException { TransferCapability tc = selectTransferCapability(UID.GeneralPurposePerformedProcedureStepSOPClass); System.out.println("Send GP-PPS Create Request:"); System.out.println(attrs.toString()); DimseRSP rsp = assoc.ncreate(UID.GeneralPurposePerformedProcedureStepSOPClass, iuid, attrs, tc.getTransferSyntax()[0]); rsp.next(); DicomObject cmd = rsp.getCommand(); System.out.println("\nReceived GP-PPS Create Response:"); System.out.println(cmd.toString()); } public void setpps(String iuid) throws IOException, InterruptedException { TransferCapability tc = selectTransferCapability(UID.GeneralPurposePerformedProcedureStepSOPClass); System.out.println("Send GP-PPS Update Request:"); System.out.println(attrs.toString()); DimseRSP rsp = assoc.nset(UID.GeneralPurposePerformedProcedureStepSOPClass, iuid, attrs, tc.getTransferSyntax()[0]); rsp.next(); DicomObject cmd = rsp.getCommand(); System.out.println("\nReceived GP-PPS Update Response:"); System.out.println(cmd.toString()); } @SuppressWarnings("unchecked") public static void main(String[] args) { CommandLine cl = parse(args); DcmGPWL dcmgpwl = new DcmGPWL(cl.hasOption("device") ? cl.getOptionValue("device") : "DCMGPWL"); final List<String> argList = cl.getArgList(); String remoteAE = argList.get(0); String[] calledAETAddress = split(remoteAE, '@'); dcmgpwl.setCalledAET(calledAETAddress[0]); if (calledAETAddress[1] == null) { dcmgpwl.setRemoteHost("127.0.0.1"); dcmgpwl.setRemotePort(104); } else { String[] hostPort = split(calledAETAddress[1], ':'); dcmgpwl.setRemoteHost(hostPort[0]); dcmgpwl.setRemotePort(toPort(hostPort[1])); } if (cl.hasOption("L")) { String localAE = cl.getOptionValue("L"); String[] callingAETHost = split(localAE, '@'); dcmgpwl.setCalling(callingAETHost[0]); if (callingAETHost[1] != null) { dcmgpwl.setLocalHost(callingAETHost[1]); } } if (cl.hasOption("username")) { String username = cl.getOptionValue("username"); UserIdentity userId; if (cl.hasOption("passcode")) { String passcode = cl.getOptionValue("passcode"); userId = new UserIdentity.UsernamePasscode(username, passcode.toCharArray()); } else { userId = new UserIdentity.Username(username); } userId.setPositiveResponseRequested(cl.hasOption("uidnegrsp")); dcmgpwl.setUserIdentity(userId); } if (cl.hasOption("connectTO")) dcmgpwl.setConnectTimeout(parseInt(cl.getOptionValue("connectTO"), "illegal argument of option -connectTO", 1, Integer.MAX_VALUE)); if (cl.hasOption("reaper")) dcmgpwl.setAssociationReaperPeriod(parseInt(cl.getOptionValue("reaper"), "illegal argument of option -reaper", 1, Integer.MAX_VALUE)); if (cl.hasOption("rspTO")) dcmgpwl.setDimseRspTimeout(parseInt(cl.getOptionValue("rspTO"), "illegal argument of option -rspTO", 1, Integer.MAX_VALUE)); if (cl.hasOption("acceptTO")) dcmgpwl.setAcceptTimeout(parseInt(cl.getOptionValue("acceptTO"), "illegal argument of option -acceptTO", 1, Integer.MAX_VALUE)); if (cl.hasOption("releaseTO")) dcmgpwl.setReleaseTimeout(parseInt(cl.getOptionValue("releaseTO"), "illegal argument of option -releaseTO", 1, Integer.MAX_VALUE)); if (cl.hasOption("soclosedelay")) dcmgpwl.setSocketCloseDelay(parseInt(cl.getOptionValue("soclosedelay"), "illegal argument of option -soclosedelay", 1, 10000)); if (cl.hasOption("rcvpdulen")) dcmgpwl.setMaxPDULengthReceive( parseInt(cl.getOptionValue("rcvpdulen"), "illegal argument of option -rcvpdulen", 1, 10000) * KB); if (cl.hasOption("sndpdulen")) dcmgpwl.setMaxPDULengthSend( parseInt(cl.getOptionValue("sndpdulen"), "illegal argument of option -sndpdulen", 1, 10000) * KB); if (cl.hasOption("sosndbuf")) dcmgpwl.setSendBufferSize( parseInt(cl.getOptionValue("sosndbuf"), "illegal argument of option -sosndbuf", 1, 10000) * KB); if (cl.hasOption("sorcvbuf")) dcmgpwl.setReceiveBufferSize( parseInt(cl.getOptionValue("sorcvbuf"), "illegal argument of option -sorcvbuf", 1, 10000) * KB); dcmgpwl.setPackPDV(!cl.hasOption("pdv1")); dcmgpwl.setTcpNoDelay(!cl.hasOption("tcpdelay")); if (cl.hasOption("o")) { dcmgpwl.setOutDir(new File(cl.getOptionValue("o"))); } if (cl.hasOption("retrieve")) { dcmgpwl.setRetrieveAET(cl.getOptionValue("retrieve")); } if (cl.hasOption("action")) { dcmgpwl.initAction(); dcmgpwl.setTransactionUID(cl.getOptionValues("action")[1]); if (cl.hasOption("status")) { dcmgpwl.setSPSStatus(cl.getOptionValue("status").toUpperCase()); } if (cl.hasOption("perfcode")) { dcmgpwl.setActualHumanPerformer(cl.getOptionValues("perfcode"), cl.getOptionValue("perfname"), cl.getOptionValue("perforg")); } } else if (cl.hasOption("createpps")) { dcmgpwl.initCreatePPS(cl.getOptionValues("refsps")); if (cl.hasOption("A")) { String[] matchingKeys = cl.getOptionValues("A"); for (int i = 1; i < matchingKeys.length; i++, i++) dcmgpwl.addAttr(toTag(matchingKeys[i - 1]), matchingKeys[i]); } if (cl.hasOption("rqA")) { String[] matchingKeys = cl.getOptionValues("rqA"); for (int i = 1; i < matchingKeys.length; i++, i++) dcmgpwl.addRefRequestAttr(toTag(matchingKeys[i - 1]), matchingKeys[i]); } if (cl.hasOption("workitem")) { dcmgpwl.addCodeValueAndScheme(Tag.PerformedWorkitemCodeSequence, cl.getOptionValues("workitem")); } if (cl.hasOption("application")) { dcmgpwl.addCodeValueAndScheme(Tag.PerformedProcessingApplicationsCodeSequence, cl.getOptionValues("application")); } if (cl.hasOption("station")) { dcmgpwl.addCodeValueAndScheme(Tag.PerformedStationNameCodeSequence, cl.getOptionValues("station")); } if (cl.hasOption("class")) { dcmgpwl.addCodeValueAndScheme(Tag.PerformedStationClassCodeSequence, cl.getOptionValues("class")); } if (cl.hasOption("location")) { dcmgpwl.addCodeValueAndScheme(Tag.PerformedStationGeographicLocationCodeSequence, cl.getOptionValues("location")); } if (cl.hasOption("perfcode")) { dcmgpwl.setActualHumanPerformer(cl.getOptionValues("perfcode"), cl.getOptionValue("perfname"), cl.getOptionValue("perforg")); } for (int i = 1, n = argList.size(); i < n; i++) { addOutput(dcmgpwl, new File(argList.get(i))); } } else if (cl.hasOption("setpps")) { dcmgpwl.initSetPPS(); if (cl.hasOption("status")) { dcmgpwl.setPPSStatus(cl.getOptionValue("status").toUpperCase()); } for (int i = 1, n = argList.size(); i < n; i++) { addOutput(dcmgpwl, new File(argList.get(i))); } } else { dcmgpwl.initQuery(); if (cl.hasOption("status")) { dcmgpwl.setSPSStatus(cl.getOptionValue("status").toUpperCase()); } if (cl.hasOption("C")) dcmgpwl.setCancelAfter( parseInt(cl.getOptionValue("C"), "illegal argument of option -C", 1, Integer.MAX_VALUE)); if (cl.hasOption("lowprior")) dcmgpwl.setPriority(CommandUtils.LOW); if (cl.hasOption("highprior")) dcmgpwl.setPriority(CommandUtils.HIGH); if (cl.hasOption("fuzzy")) dcmgpwl.setFuzzySemanticPersonNameMatching(true); if (cl.hasOption("A")) { String[] matchingKeys = cl.getOptionValues("A"); for (int i = 1; i < matchingKeys.length; i++, i++) dcmgpwl.addAttr(toTag(matchingKeys[i - 1]), matchingKeys[i]); } if (cl.hasOption("rqA")) { String[] matchingKeys = cl.getOptionValues("rqA"); for (int i = 1; i < matchingKeys.length; i++, i++) dcmgpwl.addRefRequestAttr(toTag(matchingKeys[i - 1]), matchingKeys[i]); } if (cl.hasOption("D")) { dcmgpwl.addAttr(Tag.ScheduledProcedureStepStartDateTime, cl.getOptionValue("D")); } if (cl.hasOption("workitem")) { dcmgpwl.addCodeValueAndScheme(Tag.ScheduledWorkitemCodeSequence, cl.getOptionValues("workitem")); } if (cl.hasOption("application")) { dcmgpwl.addCodeValueAndScheme(Tag.ScheduledProcessingApplicationsCodeSequence, cl.getOptionValues("application")); } if (cl.hasOption("station")) { dcmgpwl.addCodeValueAndScheme(Tag.ScheduledStationNameCodeSequence, cl.getOptionValues("station")); } if (cl.hasOption("class")) { dcmgpwl.addCodeValueAndScheme(Tag.ScheduledStationClassCodeSequence, cl.getOptionValues("class")); } if (cl.hasOption("location")) { dcmgpwl.addCodeValueAndScheme(Tag.ScheduledStationGeographicLocationCodeSequence, cl.getOptionValues("location")); } if (cl.hasOption("performer")) { dcmgpwl.setScheduledHumanPerformerCodeValueAndScheme(cl.getOptionValues("performer")); } } dcmgpwl.configureTransferCapability(cl.hasOption("metasop") ? METASOP_CUID : SOP_CUIDS, cl.hasOption("ivrle") ? IVRLE_TS : LE_TS); if (cl.hasOption("tls")) { String cipher = cl.getOptionValue("tls"); if ("NULL".equalsIgnoreCase(cipher)) { dcmgpwl.setTlsWithoutEncyrption(); } else if ("3DES".equalsIgnoreCase(cipher)) { dcmgpwl.setTls3DES_EDE_CBC(); } else if ("AES".equalsIgnoreCase(cipher)) { dcmgpwl.setTlsAES_128_CBC(); } else { exit("Invalid parameter for option -tls: " + cipher); } if (cl.hasOption("tls1")) { dcmgpwl.setTlsProtocol(TLS1); } else if (cl.hasOption("ssl3")) { dcmgpwl.setTlsProtocol(SSL3); } else if (cl.hasOption("no_tls1")) { dcmgpwl.setTlsProtocol(NO_TLS1); } else if (cl.hasOption("no_ssl3")) { dcmgpwl.setTlsProtocol(NO_SSL3); } else if (cl.hasOption("no_ssl2")) { dcmgpwl.setTlsProtocol(NO_SSL2); } dcmgpwl.setTlsNeedClientAuth(!cl.hasOption("noclientauth")); if (cl.hasOption("keystore")) { dcmgpwl.setKeyStoreURL(cl.getOptionValue("keystore")); } if (cl.hasOption("keystorepw")) { dcmgpwl.setKeyStorePassword(cl.getOptionValue("keystorepw")); } if (cl.hasOption("keypw")) { dcmgpwl.setKeyPassword(cl.getOptionValue("keypw")); } if (cl.hasOption("truststore")) { dcmgpwl.setTrustStoreURL(cl.getOptionValue("truststore")); } if (cl.hasOption("truststorepw")) { dcmgpwl.setTrustStorePassword(cl.getOptionValue("truststorepw")); } long t1 = System.currentTimeMillis(); try { dcmgpwl.initTLS(); } catch (Exception e) { System.err.println("ERROR: Failed to initialize TLS context:" + e.getMessage()); System.exit(2); } long t2 = System.currentTimeMillis(); System.out.println("Initialize TLS context in " + ((t2 - t1) / 1000F) + "s"); } long t1 = System.currentTimeMillis(); try { dcmgpwl.open(); } catch (Exception e) { System.err.println("ERROR: Failed to establish association:"); e.printStackTrace(System.err); System.exit(2); } long t2 = System.currentTimeMillis(); System.out.println("Connected to " + remoteAE + " in " + ((t2 - t1) / 1000F) + "s"); try { if (cl.hasOption("action")) { dcmgpwl.action(cl.getOptionValues("action")[0]); long t3 = System.currentTimeMillis(); System.out.println("Modified GP-SPS in " + ((t3 - t2) / 1000F) + "s"); } else if (cl.hasOption("createpps")) { dcmgpwl.createpps(cl.getOptionValue("createpps")); long t3 = System.currentTimeMillis(); System.out.println("Create GP-PPS in " + ((t3 - t2) / 1000F) + "s"); } else if (cl.hasOption("setpps")) { dcmgpwl.setpps(cl.getOptionValue("setpps")); long t3 = System.currentTimeMillis(); System.out.println("Update GP-PPS in " + ((t3 - t2) / 1000F) + "s"); } else { int n = dcmgpwl.query(); long t3 = System.currentTimeMillis(); System.out.println("Received " + n + " matching entries in " + ((t3 - t2) / 1000F) + "s"); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { dcmgpwl.close(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("Released connection to " + remoteAE); } private static void addOutput(DcmGPWL dcmgpwl, File file) { if (file.isDirectory()) { String[] ss = file.list(); for (int i = 0; i < ss.length; i++) { addOutput(dcmgpwl, new File(file, ss[i])); } } else { DicomInputStream din = null; try { din = new DicomInputStream(file); din.setHandler(new StopTagInputHandler(Tag.StudyID)); dcmgpwl.addOutput(din.readDicomObject()); } catch (IOException e) { System.out.println("WARNING: Failed to read " + file + ": " + e.getMessage()); } finally { CloseUtils.safeClose(din); } } } private static int toTag(String nameOrHex) { try { return (int) Long.parseLong(nameOrHex, 16); } catch (NumberFormatException e) { return Tag.forName(nameOrHex); } } private static CommandLine parse(String[] args) { Options opts = new Options(); OptionBuilder.withArgName("name"); OptionBuilder.hasArg(); OptionBuilder.withDescription("set device name, use DCMGPWL by default"); opts.addOption(OptionBuilder.create("device")); OptionBuilder.withArgName("aet[@host]"); OptionBuilder.hasArg(); OptionBuilder.withDescription("set AET and local address of local Application Entity, use " + "device name and pick up any valid local address to bind the " + "socket by default"); opts.addOption(OptionBuilder.create("L")); OptionBuilder.withArgName("username"); OptionBuilder.hasArg(); OptionBuilder.withDescription( "enable User Identity Negotiation with specified username and " + " optional passcode"); opts.addOption(OptionBuilder.create("username")); OptionBuilder.withArgName("passcode"); OptionBuilder.hasArg(); OptionBuilder.withDescription( "optional passcode for User Identity Negotiation, " + "only effective with option -username"); opts.addOption(OptionBuilder.create("passcode")); opts.addOption("uidnegrsp", false, "request positive User Identity Negotation response, " + "only effective with option -username"); OptionBuilder.withArgName("NULL|3DES|AES"); OptionBuilder.hasArg(); OptionBuilder.withDescription("enable TLS connection without, 3DES or AES encryption"); opts.addOption(OptionBuilder.create("tls")); OptionGroup tlsProtocol = new OptionGroup(); tlsProtocol.addOption(new Option("tls1", "disable the use of SSLv3 and SSLv2 for TLS connections")); tlsProtocol.addOption(new Option("ssl3", "disable the use of TLSv1 and SSLv2 for TLS connections")); tlsProtocol.addOption(new Option("no_tls1", "disable the use of TLSv1 for TLS connections")); tlsProtocol.addOption(new Option("no_ssl3", "disable the use of SSLv3 for TLS connections")); tlsProtocol.addOption(new Option("no_ssl2", "disable the use of SSLv2 for TLS connections")); opts.addOptionGroup(tlsProtocol); opts.addOption("noclientauth", false, "disable client authentification for TLS"); OptionBuilder.withArgName("file|url"); OptionBuilder.hasArg(); OptionBuilder .withDescription("file path or URL of P12 or JKS keystore, resource:tls/test_sys_1.p12 by default"); opts.addOption(OptionBuilder.create("keystore")); OptionBuilder.withArgName("password"); OptionBuilder.hasArg(); OptionBuilder.withDescription("password for keystore file, 'secret' by default"); opts.addOption(OptionBuilder.create("keystorepw")); OptionBuilder.withArgName("password"); OptionBuilder.hasArg(); OptionBuilder .withDescription("password for accessing the key in the keystore, keystore password by default"); opts.addOption(OptionBuilder.create("keypw")); OptionBuilder.withArgName("file|url"); OptionBuilder.hasArg(); OptionBuilder.withDescription("file path or URL of JKS truststore, resource:tls/mesa_certs.jks by default"); opts.addOption(OptionBuilder.create("truststore")); OptionBuilder.withArgName("password"); OptionBuilder.hasArg(); OptionBuilder.withDescription("password for truststore file, 'secret' by default"); opts.addOption(OptionBuilder.create("truststorepw")); opts.addOption("metasop", false, "offer General Purpose Worklist Management Meta SOP Class."); opts.addOption("ivrle", false, "offer only Implicit VR Little Endian Transfer Syntax."); opts.addOption("fuzzy", false, "negotiate support of fuzzy semantic person name attribute matching."); opts.addOption("pdv1", false, "send only one PDV in one P-Data-TF PDU, pack command and data " + "PDV in one P-DATA-TF PDU by default."); opts.addOption("tcpdelay", false, "set TCP_NODELAY socket option to false, true by default"); OptionBuilder.withArgName("ms"); OptionBuilder.hasArg(); OptionBuilder.withDescription("timeout in ms for TCP connect, no timeout by default"); opts.addOption(OptionBuilder.create("connectTO")); OptionBuilder.withArgName("ms"); OptionBuilder.hasArg(); OptionBuilder.withDescription("delay in ms for Socket close after sending A-ABORT, 50ms by default"); opts.addOption(OptionBuilder.create("soclosedelay")); OptionBuilder.withArgName("ms"); OptionBuilder.hasArg(); OptionBuilder.withDescription("period in ms to check for outstanding DIMSE-RSP, 10s by default"); opts.addOption(OptionBuilder.create("reaper")); OptionBuilder.withArgName("ms"); OptionBuilder.hasArg(); OptionBuilder.withDescription("timeout in ms for receiving DIMSE-RSP, 10s by default"); opts.addOption(OptionBuilder.create("rspTO")); OptionBuilder.withArgName("ms"); OptionBuilder.hasArg(); OptionBuilder.withDescription("timeout in ms for receiving A-ASSOCIATE-AC, 5s by default"); opts.addOption(OptionBuilder.create("acceptTO")); OptionBuilder.withArgName("ms"); OptionBuilder.hasArg(); OptionBuilder.withDescription("timeout in ms for receiving A-RELEASE-RP, 5s by default"); opts.addOption(OptionBuilder.create("releaseTO")); OptionBuilder.withArgName("KB"); OptionBuilder.hasArg(); OptionBuilder.withDescription("maximal length in KB of received P-DATA-TF PDUs, 16KB by default"); opts.addOption(OptionBuilder.create("rcvpdulen")); OptionBuilder.withArgName("KB"); OptionBuilder.hasArg(); OptionBuilder.withDescription("maximal length in KB of sent P-DATA-TF PDUs, 16KB by default"); opts.addOption(OptionBuilder.create("sndpdulen")); OptionBuilder.withArgName("KB"); OptionBuilder.hasArg(); OptionBuilder.withDescription("set SO_RCVBUF socket option to specified value in KB"); opts.addOption(OptionBuilder.create("sorcvbuf")); OptionBuilder.withArgName("KB"); OptionBuilder.hasArg(); OptionBuilder.withDescription("set SO_SNDBUF socket option to specified value in KB"); opts.addOption(OptionBuilder.create("sosndbuf")); OptionBuilder.withArgName("status"); OptionBuilder.hasArg(); OptionBuilder.withDescription("match/set GP-SPS/GP-PPS to specified <status>"); opts.addOption(OptionBuilder.create("status")); OptionBuilder.withArgName("iuid:tuid"); OptionBuilder.hasArgs(2); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription( "modify status of GP-SPS with SOP Instance UID <iuid> " + "using Transaction UID <tuid>."); opts.addOption(OptionBuilder.create("action")); OptionBuilder.withArgName("iuid"); OptionBuilder.hasArg(); OptionBuilder.withDescription("create GP-PPS with SOP Instance UID <iuid>."); opts.addOption(OptionBuilder.create("createpps")); OptionBuilder.withArgName("aet"); OptionBuilder.hasArg(); OptionBuilder.withDescription("retrieve AET used in SOP references in Output Information" + "Sequence in created or updated GP-PPS."); opts.addOption(OptionBuilder.create("retrieve")); OptionBuilder.withArgName("iuid"); OptionBuilder.hasArg(); OptionBuilder.withDescription("update GP-PPS with SOP Instance UID <iuid>."); opts.addOption(OptionBuilder.create("setpps")); OptionBuilder.withArgName("iuid:tuid"); OptionBuilder.hasArgs(2); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription( "reference GP-SPS with SOP Instance UID <iuid> and " + "Transaction UID <tuid> in created GP-PPS."); opts.addOption(OptionBuilder.create("refsps")); OptionBuilder.withArgName("attr=value"); OptionBuilder.hasArgs(); OptionBuilder.withValueSeparator('='); OptionBuilder.withDescription("specify matching key or PPS attribute. attr can be specified " + "by name or tag value (in hex), e.g. PatientName or 00100010."); opts.addOption(OptionBuilder.create("A")); OptionBuilder.withArgName("datetime"); OptionBuilder.hasArg(); OptionBuilder.withDescription("specify matching SPS start datetime (range)"); opts.addOption(OptionBuilder.create("D")); OptionBuilder.withArgName("attr=value"); OptionBuilder.hasArgs(); OptionBuilder.withValueSeparator('='); OptionBuilder.withDescription("specify matching Referenced Request key or PPS attribute. " + "attr can be specified by name or tag value (in hex)"); opts.addOption(OptionBuilder.create("rqA")); OptionBuilder.withArgName("code:scheme:[name]"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify matching Scheduled Workitem Code"); opts.addOption(OptionBuilder.create("workitem")); OptionBuilder.withArgName("code:scheme:[name]"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify matching Scheduled Processing Application Code"); opts.addOption(OptionBuilder.create("application")); OptionBuilder.withArgName("code:scheme:[name]"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify matching Scheduled Station Name Code"); opts.addOption(OptionBuilder.create("station")); OptionBuilder.withArgName("code:scheme:[name]"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify matching Scheduled Station Class Code"); opts.addOption(OptionBuilder.create("class")); OptionBuilder.withArgName("code:scheme:[name]"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify matching Scheduled Station Geographic Location Code"); opts.addOption(OptionBuilder.create("location")); OptionBuilder.withArgName("code:scheme:[name]"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify matching Scheduled Human Performer Code"); opts.addOption(OptionBuilder.create("performer")); OptionBuilder.withArgName("code:scheme:name"); OptionBuilder.hasArgs(3); OptionBuilder.withValueSeparator(':'); OptionBuilder.withDescription("specify Actual Human Performer Code"); opts.addOption(OptionBuilder.create("perfcode")); OptionBuilder.withArgName("name"); OptionBuilder.hasArg(); OptionBuilder.withDescription("specify Actual Human Performer Name"); opts.addOption(OptionBuilder.create("perfname")); OptionBuilder.withArgName("name"); OptionBuilder.hasArg(); OptionBuilder.withDescription("specify Actual Human Performer Organisation"); opts.addOption(OptionBuilder.create("perforg")); OptionBuilder.withArgName("num"); OptionBuilder.hasArg(); OptionBuilder.withDescription( "cancel query after receive of specified number of responses, " + "no cancel by default"); opts.addOption(OptionBuilder.create("C")); OptionBuilder.withArgName("dir"); OptionBuilder.hasArg(); OptionBuilder.withDescription("store query results in DICOM files in directory <dir>."); opts.addOption(OptionBuilder.create("o")); opts.addOption("lowprior", false, "LOW priority of the C-FIND operation, MEDIUM by default"); opts.addOption("highprior", false, "HIGH priority of the C-FIND operation, MEDIUM by default"); opts.addOption("h", "help", false, "print this message"); opts.addOption("V", "version", false, "print the version information and exit"); CommandLine cl = null; try { cl = new GnuParser().parse(opts, args); } catch (ParseException e) { exit("dcmgpwl: " + e.getMessage()); throw new RuntimeException("unreachable"); } if (cl.hasOption('V')) { Package p = DcmGPWL.class.getPackage(); System.out.println("dcmgpwl v" + p.getImplementationVersion()); System.exit(0); } if (cl.hasOption('h') || cl.getArgList().size() < 1) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp(USAGE, DESCRIPTION, opts, EXAMPLE); System.exit(0); } return cl; } private static int toPort(String port) { return port != null ? parseInt(port, "illegal port number", 1, 0xffff) : 104; } private static int parseInt(String s, String errPrompt, int min, int max) { try { int i = Integer.parseInt(s); if (i >= min && i <= max) return i; } catch (NumberFormatException e) { // parameter is not a valid integer; fall through to exit } exit(errPrompt); throw new RuntimeException(); } private static String[] split(String s, char delim) { String[] s2 = { s, null }; int pos = s.indexOf(delim); if (pos != -1) { s2[0] = s.substring(0, pos); s2[1] = s.substring(pos + 1); } return s2; } private static void exit(String msg) { System.err.println(msg); System.err.println("Try 'dcmgpwl -h' for more information."); System.exit(1); } public void initTLS() throws GeneralSecurityException, IOException { KeyStore keyStore = loadKeyStore(keyStoreURL, keyStorePassword); KeyStore trustStore = loadKeyStore(trustStoreURL, trustStorePassword); device.initTLS(keyStore, keyPassword != null ? keyPassword : keyStorePassword, trustStore); } private static KeyStore loadKeyStore(String url, char[] password) throws GeneralSecurityException, IOException { KeyStore key = KeyStore.getInstance(toKeyStoreType(url)); InputStream in = openFileOrURL(url); try { key.load(in, password); } finally { in.close(); } return key; } private static InputStream openFileOrURL(String url) throws IOException { if (url.startsWith("resource:")) { return DcmGPWL.class.getClassLoader().getResourceAsStream(url.substring(9)); } try { return new URL(url).openStream(); } catch (MalformedURLException e) { return new FileInputStream(url); } } private static String toKeyStoreType(String fname) { return fname.endsWith(".p12") || fname.endsWith(".P12") ? "PKCS12" : "JKS"; } }