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.hl7pix;
040
041import java.io.IOException;
042import java.net.Socket;
043import java.security.GeneralSecurityException;
044import java.util.List;
045import java.util.ResourceBundle;
046
047import org.apache.commons.cli.CommandLine;
048import org.apache.commons.cli.MissingOptionException;
049import org.apache.commons.cli.OptionBuilder;
050import org.apache.commons.cli.Options;
051import org.apache.commons.cli.ParseException;
052import org.dcm4che3.hl7.HL7Message;
053import org.dcm4che3.hl7.HL7Segment;
054import org.dcm4che3.hl7.MLLPConnection;
055import org.dcm4che3.net.Connection;
056import org.dcm4che3.net.Device;
057import org.dcm4che3.net.IncompatibleConnectionException;
058import org.dcm4che3.tool.common.CLIUtils;
059
060/**
061 * @author Gunter Zeilinger <gunterze@gmail.com>
062 *
063 */
064public class HL7Pix extends Device {
065
066    private static ResourceBundle rb =
067            ResourceBundle.getBundle("org.dcm4che3.tool.hl7pix.messages");
068
069    private final Connection conn = new Connection();
070    private final Connection remote = new Connection();
071    private String sendingApplication = "hl7pix^dcm4che";
072    private String receivingApplication = "";
073    private String charset;
074
075    private Socket sock;
076    private MLLPConnection mllp;
077
078    public HL7Pix() throws IOException {
079        super("hl7pix");
080        addConnection(conn);
081    }
082
083    public String getSendingApplication() {
084        return sendingApplication;
085    }
086
087    public void setSendingApplication(String sendingApplication) {
088        this.sendingApplication = sendingApplication;
089    }
090
091    public String getReceivingApplication() {
092        return receivingApplication;
093    }
094
095    public void setReceivingApplication(String receivingApplication) {
096        this.receivingApplication = receivingApplication;
097    }
098
099    public void setCharacterSet(String charset) {
100        this.charset = charset;
101    }
102
103    private static CommandLine parseComandLine(String[] args)
104            throws ParseException{
105        Options opts = new Options();
106        addConnectOption(opts);
107        addBindOption(opts);
108        addCharsetOption(opts);
109        CLIUtils.addResponseTimeoutOption(opts);
110        CLIUtils.addSocketOptions(opts);
111        CLIUtils.addTLSOptions(opts);
112        CLIUtils.addCommonOptions(opts);
113        return CLIUtils.parseComandLine(args, opts, rb, HL7Pix.class);
114    }
115
116    @SuppressWarnings("static-access")
117    private static void addCharsetOption(Options opts) {
118        opts.addOption(OptionBuilder
119                .withLongOpt("charset")
120                .hasArg()
121                .withArgName("name")
122                .withDescription(rb.getString("charset"))
123                .create(null));
124    }
125
126    @SuppressWarnings("static-access")
127    private static void addConnectOption(Options opts) {
128        opts.addOption(OptionBuilder
129                .hasArg()
130                .withArgName("app^fac@host:port")
131                .withDescription(rb.getString("connect"))
132                .withLongOpt("connect")
133                .create("c"));
134        opts.addOption(OptionBuilder
135                .hasArg()
136                .withArgName("[user:password@]host:port")
137                .withDescription(rb.getString("proxy"))
138                .withLongOpt("proxy")
139                .create(null));
140        CLIUtils.addConnectTimeoutOption(opts);
141    }
142
143    @SuppressWarnings("static-access")
144    private static void addBindOption(Options opts) {
145        opts.addOption(OptionBuilder
146                .hasArg()
147                .withArgName("app^fac[@ip]")
148                .withDescription(rb.getString("bind"))
149                .withLongOpt("bind")
150                .create("b"));
151    }
152
153    private static void configureConnect(HL7Pix hl7pix, CommandLine cl)
154            throws MissingOptionException, ParseException {
155        String appAtHostPort = cl.getOptionValue("c");
156        if (appAtHostPort == null)
157            throw new MissingOptionException(
158                    CLIUtils.rb.getString("missing-connect-opt"));
159
160        String[] appHostPort = HL7Segment.split(appAtHostPort , '@');
161        if (appHostPort.length != 2)
162            throw new ParseException(CLIUtils.rb.getString("invalid-connect-opt"));
163
164        String[] hostPort = HL7Segment.split(appHostPort[1], ':');
165        if (hostPort.length != 2)
166            throw new ParseException(CLIUtils.rb.getString("invalid-connect-opt"));
167
168        hl7pix.setReceivingApplication(appHostPort[0]);
169        hl7pix.remote.setHostname(hostPort[0]);
170        hl7pix.remote.setPort(Integer.parseInt(hostPort[1]));
171        hl7pix.remote.setHttpProxy(cl.getOptionValue("proxy"));
172    }
173
174    private static void configureBind(HL7Pix hl7pix, CommandLine cl) {
175        String appAtHost = cl.getOptionValue("b");
176        if (appAtHost != null) {
177            String[] appHost = HL7Segment.split(appAtHost, '@');
178            hl7pix.setSendingApplication(appHost[0]);
179            if (appHost.length > 1)
180                hl7pix.conn.setHostname(appHost[1]);
181        }
182    }
183
184    @SuppressWarnings("unchecked")
185    public static void main(String[] args) {
186        try {
187            CommandLine cl = parseComandLine(args);
188            HL7Pix main = new HL7Pix();
189            configureConnect(main, cl);
190            configureBind(main, cl);
191            CLIUtils.configure(main.conn, cl);
192            main.setCharacterSet(cl.getOptionValue("charset"));
193            main.remote.setTlsProtocols(main.conn.tlsProtocols());
194            main.remote.setTlsCipherSuites(main.conn.getTlsCipherSuites());
195            List<String> argList = cl.getArgList();
196            if (argList.isEmpty())
197                throw new ParseException(rb.getString("missing"));
198            try {
199                main.open();
200                main.query(argList.get(0), 
201                        argList.subList(1, argList.size()).toArray(new String[0]));
202            } finally {
203                main.close();
204            }
205        } catch (ParseException e) {
206            System.err.println("hl7pix: " + e.getMessage());
207            System.err.println(rb.getString("try"));
208            System.exit(2);
209        } catch (Exception e) {
210            System.err.println("hl7pix: " + e.getMessage());
211            e.printStackTrace();
212            System.exit(2);
213        }
214    }
215
216    public void open() throws IOException, IncompatibleConnectionException, GeneralSecurityException {
217        sock = conn.connect(remote);
218        sock.setSoTimeout(conn.getResponseTimeout());
219        mllp = new MLLPConnection(sock);
220    }
221
222    public void close() {
223        conn.close(sock);
224    }
225
226    public void query(String pid, String[] domains) throws IOException {
227        HL7Message qbp = HL7Message.makePixQuery(pid, domains);
228        HL7Segment msh = qbp.get(0);
229        msh.setSendingApplicationWithFacility(sendingApplication);
230        msh.setReceivingApplicationWithFacility(receivingApplication);
231        msh.setField(17, charset);
232        mllp.writeMessage(qbp.getBytes(charset));
233        if (mllp.readMessage() == null)
234            throw new IOException("Connection closed by receiver");
235    }
236}