Coverage Report - ca.uhn.hl7v2.llp.MllpDecoderState
 
Classes in this File Line Coverage Branch Coverage Complexity
MllpDecoderState
100%
25/25
100%
6/6
2
MllpDecoderState$1
100%
4/4
N/A
2
MllpDecoderState$2
100%
3/3
N/A
2
MllpDecoderState$3
100%
3/3
N/A
2
MllpDecoderState$4
25%
1/4
N/A
2
 
 1  
 /*
 2  
  The contents of this file are subject to the Mozilla Public License Version 1.1
 3  
  (the "License"); you may not use this file except in compliance with the License.
 4  
  You may obtain a copy of the License at http://www.mozilla.org/MPL/
 5  
  Software distributed under the License is distributed on an "AS IS" basis,
 6  
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
 7  
  specific language governing rights and limitations under the License.
 8  
 
 9  
  The Original Code is "MllpDecoderState.java".  Description:
 10  
  "State machine that handles reading MLLP frames"
 11  
 
 12  
  The Initial Developer of the Original Code is University Health Network. Copyright (C)
 13  
  2013.  All Rights Reserved.
 14  
 
 15  
  Contributor(s): ______________________________________.
 16  
 
 17  
  Alternatively, the contents of this file may be used under the terms of the
 18  
  GNU General Public License (the "GPL"), in which case the provisions of the GPL are
 19  
  applicable instead of those above.  If you wish to allow use of your version of this
 20  
  file only under the terms of the GPL and not to allow others to use your version
 21  
  of this file under the MPL, indicate your decision by deleting  the provisions above
 22  
  and replace  them with the notice and other provisions required by the GPL License.
 23  
  If you do not delete the provisions above, a recipient may use your version of
 24  
  this file under either the MPL or the GPL.
 25  
  */
 26  
 
 27  
 package ca.uhn.hl7v2.llp;
 28  
 
 29  
 import java.io.IOException;
 30  
 import java.io.InputStream;
 31  
 import java.io.OutputStream;
 32  
 import java.net.SocketException;
 33  
 import java.net.SocketTimeoutException;
 34  
 
 35  
 import org.slf4j.Logger;
 36  
 import org.slf4j.LoggerFactory;
 37  
 
 38  
 import static ca.uhn.hl7v2.llp.MllpConstants.END_BYTE1;
 39  
 import static ca.uhn.hl7v2.llp.MllpConstants.END_BYTE2;
 40  
 import static ca.uhn.hl7v2.llp.MllpConstants.START_BYTE;
 41  
 
 42  
 /**
 43  
  * MllpDecoderState implements a small state machine that handles reading bytes at the various
 44  
  * positions in the MLLP input stream, thereby promoting the state if the MLLP marker bytes
 45  
  * are encountered.
 46  
  *
 47  
  * @author Christian Ohr
 48  
  */
 49  301
 enum MllpDecoderState {
 50  
 
 51  5
     START(START_BYTE, true) {
 52  
         @Override
 53  
         protected MllpDecoderState proceed() {
 54  1416
             return BODY;
 55  
         }
 56  
 
 57  
         @Override
 58  
         protected void handleEndOfStream() throws IOException {
 59  276
             LOG.info("End of input stream reached.");
 60  276
             throw new SocketException("End of input stream reached before message starts");
 61  
         }
 62  
 
 63  
     },
 64  5
     BODY(END_BYTE1, false) {
 65  
         @Override
 66  
         protected MllpDecoderState proceed() {
 67  1401
             return PREPARE_END;
 68  
         }
 69  
 
 70  
         @Override
 71  
         protected void handleEndOfStream() throws IOException, LLPException {
 72  5
             throw new LLPException("MLLP protocol violation - Stream ends in the message body");
 73  
         }
 74  
 
 75  
     },
 76  5
     PREPARE_END(END_BYTE2, true) {
 77  
         @Override
 78  
         protected MllpDecoderState proceed() {
 79  1396
             return END;
 80  
         }
 81  
 
 82  
         @Override
 83  
         protected void handleEndOfStream() throws IOException, LLPException {
 84  5
             throw new LLPException("MLLP protocol violation - Stream ends before LLP end byte");
 85  
         }
 86  
     },
 87  5
     END(0, false) {
 88  
         @Override
 89  
         protected MllpDecoderState proceed() {
 90  0
             return END;
 91  
         }
 92  
 
 93  
         @Override
 94  
         protected void handleEndOfStream() throws IOException, LLPException {
 95  0
         }
 96  
 
 97  
         @Override
 98  
         MllpDecoderState read(InputStream in, OutputStream out) throws IOException, LLPException {
 99  0
             throw new LLPException("Internal error - reading after end of message");
 100  
         }
 101  
     };
 102  
 
 103  
     private int nextStateByte; // byte required for state transition
 104  
     private boolean mustChangeState; // next byte must be nextStateByte
 105  
 
 106  5
     private static final Logger LOG = LoggerFactory.getLogger(MllpDecoderState.class);
 107  
 
 108  
 
 109  20
     MllpDecoderState(int nextStateByte, boolean mustChangeState) {
 110  20
         this.nextStateByte = nextStateByte;
 111  20
         this.mustChangeState = mustChangeState;
 112  20
     }
 113  
 
 114  
     /**
 115  
      * Reads data from the input stream, records the actual payload and promtes the
 116  
      * state if applicable.
 117  
      *
 118  
      * @param in  input
 119  
      * @param out recorded data
 120  
      * @return next decoder state
 121  
      * @throws SocketTimeoutException If the socket times out
 122  
      * @throws IOException
 123  
      * @throws LLPException
 124  
      */
 125  
     MllpDecoderState read(InputStream in, OutputStream out) throws SocketTimeoutException, IOException, LLPException {
 126  
         int c;
 127  
         try {
 128  302699
             if ((c = in.read()) == -1) {
 129  286
                 handleEndOfStream();
 130  
             } else {
 131  301936
                 LowerLayerProtocol.logCharacterReceived(c);
 132  
             }
 133  301936
             if (c == nextStateByte) {
 134  4213
                     return proceed();
 135  
             }
 136  297723
             if (mustChangeState) {
 137  40
                 throw new LLPException("MLLP protocol violation - Expected byte '" + nextStateByte +
 138  
                         "' in state " + this + " but was '" + c + "'");
 139  
             }
 140  297683
             out.write(c);
 141  214
         } catch (SocketTimeoutException e) {
 142  
                 // Logged in the caller so we don't do it here
 143  214
             throw e;
 144  524
         } catch (SocketException e) {
 145  524
             LOG.info("SocketException on read() attempt.  Socket appears to have been closed: " + e.getMessage());
 146  524
             throw e;
 147  297683
         }
 148  297683
         return this;
 149  
     }
 150  
 
 151  
     /**
 152  
      * Proceed to the next state
 153  
      *
 154  
      * @return next decoder state
 155  
      */
 156  
     protected abstract MllpDecoderState proceed();
 157  
 
 158  
     /**
 159  
      * Handle occurences of unexpected ends of streams
 160  
      *
 161  
      * @throws IOException
 162  
      * @throws LLPException
 163  
      */
 164  
     protected abstract void handleEndOfStream() throws IOException, LLPException;
 165  
 
 166  
 }