001/* ***** BEGIN LICENSE BLOCK *****
002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003 *
004 * The contents of this file are subject to the Mozilla Public License Version
005 * 1.1 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 * http://www.mozilla.org/MPL/
008 *
009 * Software distributed under the License is distributed on an "AS IS" basis,
010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011 * for the specific language governing rights and limitations under the
012 * License.
013 *
014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
015 * Java(TM), hosted at https://github.com/gunterze/dcm4che.
016 *
017 * The Initial Developer of the Original Code is
018 * Agfa Healthcare.
019 * Portions created by the Initial Developer are Copyright (C) 2012
020 * the Initial Developer. All Rights Reserved.
021 *
022 * Contributor(s):
023 * See @authors listed below
024 *
025 * Alternatively, the contents of this file may be used under the terms of
026 * either the GNU General Public License Version 2 or later (the "GPL"), or
027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
028 * in which case the provisions of the GPL or the LGPL are applicable instead
029 * of those above. If you wish to allow use of your version of this file only
030 * under the terms of either the GPL or the LGPL, and not to allow others to
031 * use your version of this file under the terms of the MPL, indicate your
032 * decision by deleting the provisions above and replace them with the notice
033 * and other provisions required by the GPL or the LGPL. If you do not delete
034 * the provisions above, a recipient may use your version of this file under
035 * the terms of any one of the MPL, the GPL or the LGPL.
036 *
037 * ***** END LICENSE BLOCK ***** */
038
039package org.dcm4che3.tool.hl7snd;
040
041import java.io.ByteArrayOutputStream;
042import java.io.File;
043import java.io.FileDescriptor;
044import java.io.FileInputStream;
045import java.io.IOException;
046import java.net.Socket;
047import java.security.GeneralSecurityException;
048import java.util.List;
049import java.util.ResourceBundle;
050
051import org.apache.commons.cli.CommandLine;
052import org.apache.commons.cli.MissingOptionException;
053import org.apache.commons.cli.OptionBuilder;
054import org.apache.commons.cli.Options;
055import org.apache.commons.cli.ParseException;
056import org.dcm4che3.hl7.MLLPConnection;
057import org.dcm4che3.net.Connection;
058import org.dcm4che3.net.Device;
059import org.dcm4che3.net.IncompatibleConnectionException;
060import org.dcm4che3.tool.common.CLIUtils;
061import org.dcm4che3.util.SafeClose;
062import org.dcm4che3.util.StreamUtils;
063import org.dcm4che3.util.StringUtils;
064
065/**
066 * @author Gunter Zeilinger <gunterze@gmail.com>
067 *
068 */
069public class HL7Snd extends Device {
070
071    private static ResourceBundle rb =
072            ResourceBundle.getBundle("org.dcm4che3.tool.hl7snd.messages");
073
074    private final Connection conn = new Connection();
075    private final Connection remote = new Connection();
076
077    private Socket sock;
078    private MLLPConnection mllp;
079
080    public HL7Snd() throws IOException {
081        super("hl7snd");
082        addConnection(conn);
083    }
084
085    private static CommandLine parseComandLine(String[] args)
086            throws ParseException{
087        Options opts = new Options();
088        addConnectOption(opts);
089        addBindOption(opts);
090        CLIUtils.addResponseTimeoutOption(opts);
091        CLIUtils.addSocketOptions(opts);
092        CLIUtils.addTLSOptions(opts);
093        CLIUtils.addCommonOptions(opts);
094        return CLIUtils.parseComandLine(args, opts, rb, HL7Snd.class);
095    }
096
097    @SuppressWarnings("static-access")
098    private static void addConnectOption(Options opts) {
099        opts.addOption(OptionBuilder
100                .hasArg()
101                .withArgName("host:port")
102                .withDescription(rb.getString("connect"))
103                .withLongOpt("connect")
104                .create("c"));
105        opts.addOption(OptionBuilder
106                .hasArg()
107                .withArgName("[user:password@]host:port")
108                .withDescription(rb.getString("proxy"))
109                .withLongOpt("proxy")
110                .create(null));
111        CLIUtils.addConnectTimeoutOption(opts);
112    }
113
114    @SuppressWarnings("static-access")
115    private static void addBindOption(Options opts) {
116        opts.addOption(OptionBuilder
117                .hasArg()
118                .withArgName("ip")
119                .withDescription(rb.getString("bind"))
120                .withLongOpt("bind")
121                .create("b"));
122    }
123
124    private static void configureConnect(Connection conn, CommandLine cl)
125            throws MissingOptionException, ParseException {
126        if (!cl.hasOption("c"))
127            throw new MissingOptionException(
128                    CLIUtils.rb.getString("missing-connect-opt"));
129
130        String[] hostPort = StringUtils.split(cl.getOptionValue("c"), ':');
131        if (hostPort.length != 2)
132            throw new ParseException(CLIUtils.rb.getString("invalid-connect-opt"));
133        
134        conn.setHostname(hostPort[0]);
135        conn.setPort(Integer.parseInt(hostPort[1]));
136        conn.setHttpProxy(cl.getOptionValue("proxy"));
137    }
138
139    private static void configureBind(Connection conn, CommandLine cl) {
140        if (cl.hasOption("b"))
141            conn.setHostname(cl.getOptionValue("b"));
142    }
143
144    @SuppressWarnings("unchecked")
145    public static void main(String[] args) {
146        try {
147            CommandLine cl = parseComandLine(args);
148            HL7Snd main = new HL7Snd();
149            configureConnect(main.remote, cl);
150            configureBind(main.conn, cl);
151            CLIUtils.configure(main.conn, cl);
152            main.remote.setTlsProtocols(main.conn.tlsProtocols());
153            main.remote.setTlsCipherSuites(main.conn.getTlsCipherSuites());
154            try {
155                main.open();
156                main.sendFiles(cl.getArgList());
157            } finally {
158                main.close();
159            }
160        } catch (ParseException e) {
161            System.err.println("hl7snd: " + e.getMessage());
162            System.err.println(rb.getString("try"));
163            System.exit(2);
164        } catch (Exception e) {
165            System.err.println("hl7snd: " + e.getMessage());
166            e.printStackTrace();
167            System.exit(2);
168        }
169    }
170
171    public void open() throws IOException, IncompatibleConnectionException, GeneralSecurityException {
172        sock = conn.connect(remote);
173        sock.setSoTimeout(conn.getResponseTimeout());
174        mllp = new MLLPConnection(sock);
175    }
176
177    public void close() {
178        conn.close(sock);
179    }
180
181    public void sendFiles(List<String> pathnames) throws IOException {
182        for (String pathname : pathnames) {
183            mllp.writeMessage(readFile(pathname));
184            if (mllp.readMessage() == null)
185                throw new IOException("Connection closed by receiver");
186        }
187    }
188
189    private byte[] readFile(String pathname) throws IOException {
190        FileInputStream in = null;
191        try {
192            if (pathname.equals("-")) {
193                in = new FileInputStream(FileDescriptor.in);
194                ByteArrayOutputStream buf = new ByteArrayOutputStream();
195                StreamUtils.copy(in, buf);
196                return buf.toByteArray();
197            } else {
198                File f = new File(pathname);
199                in = new FileInputStream(f);
200                byte[] b = new byte[(int) f.length()];
201                StreamUtils.readFully(in, b, 0, b.length);
202                return b;
203            }
204        } finally {
205            SafeClose.close(in);
206        }
207    }
208}