001/*
002 * Created on 3-May-2004
003 */
004package ca.uhn.hl7v2.protocol.impl;
005
006import java.util.ArrayList;
007import java.util.HashMap;
008import java.util.List;
009import java.util.Map;
010
011import ca.uhn.hl7v2.HL7Exception;
012import ca.uhn.hl7v2.model.Message;
013import ca.uhn.hl7v2.parser.GenericParser;
014import ca.uhn.hl7v2.parser.Parser;
015import ca.uhn.hl7v2.protocol.Initiator;
016import ca.uhn.hl7v2.protocol.Processor;
017import ca.uhn.hl7v2.protocol.Transportable;
018import ca.uhn.hl7v2.util.Terser;
019
020/**
021 * Default implementation of <code>Initiator</code>. 
022 * 
023 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
024 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
025 */
026public class InitiatorImpl implements Initiator {
027
028    private final List<String> myMetadataFields;
029    private final Parser myParser;
030    private final Processor myProcessor;
031    private int myMaxRetries;
032    private long myRetryInterval;
033    private long myReceiveTimeout;
034    
035    /**
036     * Creates an instance that uses a <code>GenericParser</code>
037     * @param theProcessor the <code>Processor</code> used to communicate 
038     *  with the remote system
039     */
040    public InitiatorImpl(Processor theProcessor) {
041        myMetadataFields = new ArrayList<String>(20);
042        myMetadataFields.add("MSH-18"); //add character set by default
043        myParser = new GenericParser();
044        myProcessor = theProcessor;
045        init();
046    }
047    
048    /**
049     * Creates an instance that uses the given <code>Parser</code>
050     * @param theParser parser to use for parsing and encoding messages
051     * @param theProcessor the <code>Processor</code> used to communicate 
052     *  with the remote system
053     */
054    public InitiatorImpl(Parser theParser, Processor theProcessor) {
055        myMetadataFields = new ArrayList<String>(20);
056        myParser = theParser;
057        myProcessor = theProcessor;
058        init();
059    }
060    
061    private void init() {
062        myMaxRetries = 3;
063        myRetryInterval = 3000;
064        myReceiveTimeout = 10000;
065    }
066    
067    
068    /**
069     * @param theMaxRetries max number of retries for initial message delivery 
070     */
071    public void setMaxRetries(int theMaxRetries) {
072        myMaxRetries = theMaxRetries;
073    }
074    
075    public int getMaxRetries() {
076        return myMaxRetries;
077    }
078    
079    /**
080     * @param theRetryIntervalMillis milliseconds between retries of message delivery 
081     */
082    public void setRetryInterval(long theRetryIntervalMillis) {
083        myRetryInterval = theRetryIntervalMillis;
084    }
085    
086    public long getRetryInterval() {
087        return myRetryInterval;
088    }
089    
090    /**
091     * @param theReceiveTimeout the length of time we wait for responses (defaults 
092     *      to 10 seconds)
093     */
094    public void setReceiveTimeout(long theReceiveTimeout) {
095        myReceiveTimeout = theReceiveTimeout;
096    }
097    
098    public long getReceiveTimeout() {
099        return myReceiveTimeout;
100    }
101    
102    /**
103     * @see ca.uhn.hl7v2.protocol.Initiator#sendAndReceive(ca.uhn.hl7v2.model.Message)
104     */
105    public Message sendAndReceive(Message theMessage) throws HL7Exception {
106        Terser t = new Terser(theMessage);
107        String appAckNeeded = t.get("/MSH-16");
108        String msgId = t.get("/MSH-10");
109        
110        String messageText = getParser().encode(theMessage);
111        Map<String, Object> metadata = getMetadata(theMessage);
112        Transportable out = new TransportableImpl(messageText, metadata);
113        
114        if (needAck(appAckNeeded)) {
115            myProcessor.reserve(msgId, getReceiveTimeout());
116        }
117        
118        myProcessor.send(out, getMaxRetries(), getRetryInterval());
119        
120        Message in = null;
121        if (needAck(appAckNeeded)) { 
122            Transportable received = myProcessor.receive(msgId, getReceiveTimeout());
123            if (received != null && received.getMessage() != null) {
124                in = getParser().parse(received.getMessage());
125            }
126        }
127        
128        return in;
129    }
130    
131    /**
132     * @param theAckCode from MSH-16
133     * @return true if the code indicates that we should wait for an ACK and try 
134     *      to return it to the caller 
135     */
136    private boolean needAck(String theAckCode) {
137        boolean need = false;
138        if (theAckCode == null 
139            || theAckCode.equals("") 
140            || theAckCode.equals(Processor.AL) 
141            || theAckCode.equals(Processor.ER)) 
142        {
143            need = true; 
144        }        
145        return need;
146    }
147    
148    private Map<String, Object> getMetadata(Message theMessage) throws HL7Exception {
149        Map<String, Object> md = new HashMap<String, Object>();
150        Terser t = new Terser(theMessage);
151
152        //snapshot so concurrent changes won't break our iteration
153        String[] fields = getMetadataFields().toArray(new String[0]);
154        
155        for (int i = 0; i < fields.length; i++) {
156            String field = fields[i].toString();
157            String val = t.get(field); 
158            md.put(field, val);
159        }
160        
161        return md;
162    }
163
164    /** 
165     * @see ca.uhn.hl7v2.protocol.Initiator#getParser()
166     */
167    public Parser getParser() {
168        return myParser;
169    }
170
171    /** 
172     * @see ca.uhn.hl7v2.protocol.Initiator#getUnderlyingProcessor()
173     */
174    public Processor getUnderlyingProcessor() {
175        return myProcessor;
176    }
177
178    /**
179     * @see ca.uhn.hl7v2.protocol.Initiator#getMetadataFields()
180     */
181    public List<String> getMetadataFields() {
182        return myMetadataFields;
183    }
184
185
186}