View Javadoc

1   /**
2    * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
5    * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of
6    * conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice,
7    * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the
8    * distribution. * Neither the name of the University of Konstanz nor the names of its contributors may be used to
9    * endorse or promote products derived from this software without specific prior written permission.
10   * 
11   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
12   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
13   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
14   * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
16   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
17   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18   */
19  package org.jscsi.initiator.connection.state;
20  
21  
22  import java.nio.ByteBuffer;
23  
24  import org.jscsi.exception.InternetSCSIException;
25  import org.jscsi.exception.OperationalTextKeyException;
26  import org.jscsi.initiator.connection.Connection;
27  import org.jscsi.parser.AbstractMessageParser;
28  import org.jscsi.parser.ProtocolDataUnit;
29  import org.jscsi.parser.data.DataInParser;
30  import org.jscsi.parser.datasegment.OperationalTextKey;
31  import org.jscsi.parser.scsi.SCSIResponseParser;
32  import org.jscsi.parser.scsi.SCSIStatus;
33  
34  
35  /**
36   * <h1>ReadResponseState</h1>
37   * <p/>
38   * This state handles a Read Response.
39   * 
40   * @author Volker Wildi
41   */
42  public final class ReadResponseState extends AbstractState {
43  
44      // --------------------------------------------------------------------------
45      // --------------------------------------------------------------------------
46  
47      /**
48       * This is the wrap around divisor (2**32) of the modulo operation used by incrementing the sequence numbers. See
49       * [RFC1982] for details.
50       */
51      private static final int WRAP_AROUND_DIVISOR = (int) Math.pow(2, 32);
52  
53      // --------------------------------------------------------------------------
54      // --------------------------------------------------------------------------
55  
56      /** The buffer to used for the message transfer. */
57      private final ByteBuffer buffer;
58  
59      /** The start offset of the data to send. */
60      private int bufferOffset;
61  
62      /** The expected data sequence number of the next response. */
63      private int expectedDataSequenceNumber;
64  
65      // --------------------------------------------------------------------------
66      // --------------------------------------------------------------------------
67  
68      /**
69       * Constructor to create a new, empty <code>ReadResponseState</code>.
70       * 
71       * @param initConnection This is the connection, which is used for the network transmission.
72       * @param initBuffer The buffer, where the readed bytes are stored in.
73       * @param initBufferOffset The start offset of the data to send.
74       * @param initExpectedDataSequenceNumber The Expected Data Sequence Number of the next response message.
75       */
76      public ReadResponseState (final Connection initConnection, final ByteBuffer initBuffer, final int initBufferOffset, final int initExpectedDataSequenceNumber) {
77  
78          super(initConnection);
79          buffer = initBuffer;
80          bufferOffset = initBufferOffset;
81          expectedDataSequenceNumber = initExpectedDataSequenceNumber;
82      }
83  
84      // --------------------------------------------------------------------------
85      // --------------------------------------------------------------------------
86  
87      /** {@inheritDoc} */
88      public final void execute () throws InternetSCSIException {
89  
90          ProtocolDataUnit protocolDataUnit;
91  
92          do {
93              protocolDataUnit = connection.receive();
94              boolean dataWasRead = false;
95              if (protocolDataUnit.getBasicHeaderSegment().getParser() instanceof DataInParser) {
96                  final DataInParser parser = (DataInParser) protocolDataUnit.getBasicHeaderSegment().getParser();
97  
98                  if (LOGGER.isDebugEnabled()) {
99                      LOGGER.debug("Remaining, DataSegmentLength: " + buffer.remaining() + ", " + protocolDataUnit.getBasicHeaderSegment().getDataSegmentLength());
100                 }
101 
102                 final ByteBuffer dataSegment = protocolDataUnit.getDataSegment();
103                 while (buffer.hasRemaining() && dataSegment.hasRemaining()) {
104                     buffer.put(dataSegment.get());
105                 }
106                 dataWasRead = true;
107                 // last message with the status flag set
108                 if (parser.isStatusFlag() && parser.getStatus() == SCSIStatus.GOOD) {
109                     // return false;
110                     return;
111                 } else if (connection.getSettingAsInt(OperationalTextKey.ERROR_RECOVERY_LEVEL) > 0 && parser.isAcknowledgeFlag()) {
112                     // TODO: Test this case
113                     // send a DataAck
114                     connection.nextState(new SNACKRequestState(connection, this, parser.getTargetTaskTag()));
115                     // return true;
116                     return;
117                 } else if (protocolDataUnit.getBasicHeaderSegment().getParser() instanceof SCSIResponseParser && !dataWasRead) {
118                     readHandleImmediateData(protocolDataUnit);
119                 }
120             }
121         } while (!protocolDataUnit.getBasicHeaderSegment().isFinalFlag());
122 
123         if (connection.getSettingAsBoolean(OperationalTextKey.IMMEDIATE_DATA)) {
124             return;
125         } else {
126             protocolDataUnit = connection.receive();
127             if (protocolDataUnit.getBasicHeaderSegment().getParser() instanceof SCSIResponseParser) {
128                 readHandleImmediateData(protocolDataUnit);
129             }
130         }
131 
132     }
133 
134     private void readHandleImmediateData (final ProtocolDataUnit protocolDataUnit) throws InternetSCSIException {
135         final SCSIResponseParser parser = (SCSIResponseParser) protocolDataUnit.getBasicHeaderSegment().getParser();
136 
137         final ByteBuffer dataSegment = protocolDataUnit.getDataSegment();
138         while (buffer.hasRemaining() && dataSegment.hasRemaining()) {
139             buffer.put(dataSegment.get());
140         }
141 
142         if (parser.getStatus() == SCSIStatus.GOOD) {
143             // return false;
144             super.stateFollowing = false;
145             return;
146         } else {
147             throw new InternetSCSIException();
148         }
149     }
150 
151     // --------------------------------------------------------------------------
152     // --------------------------------------------------------------------------
153 
154     /** {@inheritDoc} */
155     @Override
156     public Exception isCorrect (final ProtocolDataUnit protocolDataUnit) {
157 
158         final AbstractMessageParser parser = protocolDataUnit.getBasicHeaderSegment().getParser();
159 
160         if (parser instanceof DataInParser) {
161 
162             final DataInParser dataParser = (DataInParser) parser;
163             try {
164                 if (connection.getSettingAsBoolean(OperationalTextKey.DATA_PDU_IN_ORDER) && connection.getSettingAsBoolean(OperationalTextKey.DATA_SEQUENCE_IN_ORDER)) {
165                     if (dataParser.getBufferOffset() < bufferOffset) { return new IllegalStateException(new StringBuilder("This buffer offsets must be in increasing order and overlays are forbidden.").append(" The parserOffset here is ").append(dataParser.getBufferOffset()).append(" and the bufferOffset is ").append(bufferOffset).toString()); }
166                     bufferOffset = dataParser.getBufferOffset();
167                 }
168             } catch (OperationalTextKeyException e) {
169                 return e;
170             }
171 
172             if (dataParser.getDataSequenceNumber() != expectedDataSequenceNumber) { return new IllegalStateException(new StringBuilder("Data Sequence Number Mismatch (received, expected): " + dataParser.getDataSequenceNumber() + ", " + expectedDataSequenceNumber).toString());
173 
174             }
175 
176             incrementExpectedDataSequenceNumber();
177 
178             if (dataParser.isStatusFlag()) {
179                 incrementExpectedDataSequenceNumber();
180                 return super.isCorrect(protocolDataUnit);
181             } else if (dataParser.getStatusSequenceNumber() != 0) { return new IllegalStateException(new StringBuilder("Status Sequence Number must be zero.").toString()); }
182             return null;
183         } else if (parser instanceof SCSIResponseParser) {
184             try {
185                 if (connection.getSettingAsBoolean(OperationalTextKey.IMMEDIATE_DATA)) { return new IllegalStateException(new StringBuilder("Parser ").append("should not be instance of SCSIResponseParser because of ImmendiateData-Flag \"no\" in config!").toString()); }
186             } catch (OperationalTextKeyException e) {
187                 return e;
188             }
189 
190             return null;
191         } else {
192             return new IllegalStateException(new StringBuilder("Parser ").append(protocolDataUnit.getBasicHeaderSegment().getParser().toString()).append(" is instance of ").append(protocolDataUnit.getBasicHeaderSegment().getParser().getClass().toString()).append(" and not instance of either DataInParser or SCSIResponseParser!").toString());
193         }
194 
195     }
196 
197     // --------------------------------------------------------------------------
198     // --------------------------------------------------------------------------
199 
200     /**
201      * Increments the Expected Data Sequence Number counter.
202      */
203     private void incrementExpectedDataSequenceNumber () {
204 
205         expectedDataSequenceNumber = (expectedDataSequenceNumber + 1) % WRAP_AROUND_DIVISOR;
206     }
207 
208     // --------------------------------------------------------------------------
209     // --------------------------------------------------------------------------
210     // --------------------------------------------------------------------------
211     // --------------------------------------------------------------------------
212 
213 }