View Javadoc
1   package ca.uhn.hl7v2.hoh.encoder;
2   
3   import static ca.uhn.hl7v2.hoh.util.StringUtils.*;
4   
5   import java.io.ByteArrayOutputStream;
6   import java.io.IOException;
7   import java.io.OutputStream;
8   import java.io.OutputStreamWriter;
9   import java.text.DateFormat;
10  import java.text.SimpleDateFormat;
11  import java.util.Date;
12  import java.util.LinkedHashMap;
13  import java.util.Map;
14  import java.util.zip.GZIPOutputStream;
15  
16  import ca.uhn.hl7v2.hoh.api.EncodeException;
17  import ca.uhn.hl7v2.hoh.api.ISendable;
18  import ca.uhn.hl7v2.hoh.sign.SignatureFailureException;
19  import ca.uhn.hl7v2.hoh.util.GZipUtils;
20  import ca.uhn.hl7v2.hoh.util.HTTPUtils;
21  
22  /**
23   * Base class that creates HL7 over HTTP requests. This class is intended to be
24   * single use, so please create a new instance for each message.
25   */
26  public abstract class AbstractHl7OverHttpEncoder extends AbstractHl7OverHttp {
27  
28  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractHl7OverHttpEncoder.class);
29  	private static final DateFormat ourRfc1123DateFormat;
30  
31  	static {
32  		ourRfc1123DateFormat = new SimpleDateFormat("EEE, dd MMM yy HH:mm:ss z");
33  	}
34  
35  	private String myActionLine;
36  	private boolean myGzipData;
37  	private ISendable<?> mySendable;
38  
39  	/**
40  	 * Constructor
41  	 */
42  	public AbstractHl7OverHttpEncoder() {
43  		super();
44  	}
45  
46  	/**
47  	 * @throws EncodeException
48  	 * 
49  	 */
50  	public void encode() throws EncodeException {
51  		ourLog.trace("Entering encode()");
52  		verifyNotUsed();
53  
54  		if (isBlank(getMessage()) && mySendable == null) {
55  			throw new IllegalStateException("Either Message or Sendable must be set");
56  		}
57  		if (getMessage() != null) {
58  			byte[] bytes = getMessage().getBytes(getCharset());
59  			if (myGzipData) {
60  				try {
61  					bytes = GZipUtils.compress(bytes);
62  				} catch (IOException e) {
63  					throw new EncodeException("Failed to apply GZip coding", e);
64  				}
65  			}
66  			setData(bytes);
67  		} else {
68  			ByteArrayOutputStream bos = new ByteArrayOutputStream();
69  			OutputStream os;
70  			if (myGzipData) {
71  				try {
72  					os = new GZIPOutputStream(bos);
73  				} catch (IOException e) {
74  					throw new EncodeException("Failed to create GZip encoder", e);
75  				}
76  			} else {
77  				os = bos;
78  			}
79  
80  			OutputStreamWriter w = new OutputStreamWriter(os, getCharset());
81  			try {
82  				mySendable.writeMessage(w);
83  			} catch (IOException e) {
84  				throw new EncodeException("Failed to convert message to sendable bytes");
85  			}
86  			setData(bos.toByteArray());
87  		}
88  
89  		setActionLineAppropriately();
90  
91  		setHeaders(new LinkedHashMap<>());
92  
93  		StringBuilder ctBuilder = new StringBuilder();
94  		if (mySendable != null) {
95  			ctBuilder.append(mySendable.getEncodingStyle().getContentType());
96  		} else {
97  			ctBuilder.append(EncodingStyle.detect(getMessage()).getContentType());
98  		}
99  		ctBuilder.append("; charset=");
100 		ctBuilder.append(getCharset().name());
101 		getHeaders().put("Content-Type", ctBuilder.toString());
102 
103 		getHeaders().put("Content-Length", Integer.toString(getData().length));
104 
105 		addSpecificHeaders();
106 
107 		synchronized (ourRfc1123DateFormat) {
108 			getHeaders().put("Date", ourRfc1123DateFormat.format(new Date()));
109 		}
110 
111 		if (getSigner() != null) {
112 			try {
113 				getHeaders().put(HTTP_HEADER_HL7_SIGNATURE, getSigner().sign(getData()));
114 			} catch (SignatureFailureException e) {
115 				throw new EncodeException(e);
116 			}
117 		}
118 
119 		ourLog.trace("Exiting encode()");
120 	}
121 
122 	public void encodeToOutputStream(OutputStream theOutputStream) throws IOException, EncodeException {
123 		encode();
124 
125 		ourLog.debug("Writing HTTP action: {}", getActionLine());
126 
127 		OutputStreamWriter w = new OutputStreamWriter(theOutputStream, HTTPUtils.DEFAULT_CHARSET);
128 		w.write(getActionLine());
129 		w.write("\r\n");
130 
131 		for (Map.Entry<String, String> next : getHeaders().entrySet()) {
132 			ourLog.debug("Writing HTTP header- {}: {}", next.getKey(), next.getValue());
133 
134 			w.write(next.getKey());
135 			w.write(": ");
136 			w.write(next.getValue());
137 			w.write("\r\n");
138 		}
139 
140 		w.write("\r\n");
141 		w.flush();
142 
143 		ourLog.debug("Writing {} bytes of actual data", getData().length);
144 		theOutputStream.write(getData());
145 
146 	}
147 
148 	/**
149 	 * @return the actionLine
150 	 */
151 	public String getActionLine() {
152 		return myActionLine;
153 	}
154 
155 	/**
156 	 * @param theActionLine
157 	 *            the actionLine to set
158 	 */
159 	public void setActionLine(String theActionLine) {
160 		myActionLine = theActionLine;
161 	}
162 
163 	/**
164 	 * Provide the message to send with a {@link ISendable} instance. Either
165 	 * this method OR {@link #setMessage(String)} must be called, but not both.
166 	 */
167 	public void setDataProvider(ISendable<?> theSendable) {
168 		if (getMessage() != null) {
169 			throw new IllegalStateException("Message already provided");
170 		}
171 		mySendable = theSendable;
172 	}
173 
174 	/**
175 	 * Provide the message to send with a String. Either this method OR
176 	 * {@link #setDataProvider(ISendable)} must be called, but not both.
177 	 */
178 	@Override
179 	public void setMessage(String theData) {
180 		if (mySendable != null) {
181 			throw new IllegalStateException("Data provider already provided");
182 		}
183 		super.setMessage(theData);
184 	}
185 
186 	protected abstract void addSpecificHeaders();
187 
188 	protected abstract void setActionLineAppropriately();
189 
190 	boolean isGzipData() {
191 		return myGzipData;
192 	}
193 
194 	/**
195 	 * Should the encoded data be GZipped? Note that this doesn't set any
196 	 * headers indicating this fact, so it's up to callers of this method to
197 	 * take care of that.
198 	 */
199 	void setGzipData(boolean theGzipData) {
200 		myGzipData = theGzipData;
201 	}
202 
203 }