Coverage Report - org.jscsi.target.connection.TargetSenderWorker
 
Classes in this File Line Coverage Branch Coverage Complexity
TargetSenderWorker
0%
0/49
0%
0/22
3.8
 
 1  
 package org.jscsi.target.connection;
 2  
 
 3  
 
 4  
 import java.io.IOException;
 5  
 import java.nio.channels.ClosedChannelException;
 6  
 import java.nio.channels.SocketChannel;
 7  
 import java.security.DigestException;
 8  
 
 9  
 import org.jscsi.exception.InternetSCSIException;
 10  
 import org.jscsi.parser.BasicHeaderSegment;
 11  
 import org.jscsi.parser.InitiatorMessageParser;
 12  
 import org.jscsi.parser.OperationCode;
 13  
 import org.jscsi.parser.ProtocolDataUnit;
 14  
 import org.jscsi.parser.ProtocolDataUnitFactory;
 15  
 import org.jscsi.parser.TargetMessageParser;
 16  
 import org.jscsi.parser.scsi.SCSICommandParser;
 17  
 import org.jscsi.target.scsi.cdb.ScsiOperationCode;
 18  
 import org.jscsi.target.settings.Settings;
 19  
 import org.jscsi.target.settings.SettingsException;
 20  
 import org.jscsi.target.settings.TextKeyword;
 21  
 import org.jscsi.target.util.Debug;
 22  
 import org.slf4j.Logger;
 23  
 import org.slf4j.LoggerFactory;
 24  
 
 25  
 
 26  
 /**
 27  
  * Instances of this class are used by {@link Connection} objects for sending and receiving {@link ProtocolDataUnit}
 28  
  * objects.
 29  
  * 
 30  
  * @author Andreas Ergenzinger, University of Konstanz
 31  
  */
 32  
 public class TargetSenderWorker {
 33  
 
 34  0
     private static final Logger LOGGER = LoggerFactory.getLogger(TargetSenderWorker.class);
 35  
 
 36  
     /**
 37  
      * The connection which uses this object for sending and receiving PDUs.
 38  
      */
 39  
     final private Connection connection;
 40  
 
 41  
     /**
 42  
      * The session to which {@link #connection} belongs to.
 43  
      */
 44  
     private TargetSession session;
 45  
 
 46  
     /**
 47  
      * Used for writing serialized PDUs to and reading serialized PDUs from.
 48  
      */
 49  
     final private SocketChannel socketChannel;
 50  
 
 51  
     /**
 52  
      * Will be used to create {@link ProtocolDataUnit} objects from the byte stream read from the {@link #socketChannel}
 53  
      * .
 54  
      */
 55  
     final private ProtocolDataUnitFactory protocolDataUnitFactory;
 56  
 
 57  
     /**
 58  
      * If this is <code>true</code>, then the next PDU read from the {@link #socketChannel} will be the first PDU
 59  
      * received in the {@link #session}.
 60  
      * <p>
 61  
      * Will be initializes to <code>true</code> if and only if the {@link #connection} is the leading connection of its
 62  
      * session.
 63  
      * <p>
 64  
      * PDUs identified by this variable as the first PDU in a session will not have their counters (i.e. CmdSN and
 65  
      * ExpStatSN) checked. Instead the values of these counters will be used to initialize the targets local copies of
 66  
      * these counters that will be used to ensure that no PDUs have been lost in transit.
 67  
      */
 68  
     private boolean initialPdu;
 69  
 
 70  
     /**
 71  
      * Creates a new {@link TargetSenderWorker} object.
 72  
      * 
 73  
      * @param connection the connection that will use this object for sending and receiving PDUs
 74  
      * @param socketChannel used for sending and receiving serialized PDU to and from the target
 75  
      */
 76  0
     public TargetSenderWorker (final Connection connection, final SocketChannel socketChannel) {
 77  0
         this.connection = connection;
 78  0
         this.socketChannel = socketChannel;
 79  0
         protocolDataUnitFactory = new ProtocolDataUnitFactory();
 80  0
         initialPdu = connection.isLeadingConnection();
 81  0
     }
 82  
 
 83  
     /**
 84  
      * Sets the {@link #session} variable.
 85  
      * <p>
 86  
      * During the time this object is initialized, the {@link Connection#getSession()} method will return
 87  
      * <code>null</code>. Therefore {@link #session} must be set manually once the {@link TargetSession} object has been
 88  
      * created.
 89  
      * 
 90  
      * @param session the session of the {@link #connection}
 91  
      */
 92  
     void setSession (final TargetSession session) {
 93  0
         this.session = session;
 94  0
     }
 95  
 
 96  
     /**
 97  
      * This method does all the necessary steps, which are needed when a connection should be closed.
 98  
      * 
 99  
      * @throws IOException if an I/O error occurs.
 100  
      */
 101  
     public final void close () throws IOException {
 102  0
         socketChannel.close();
 103  0
     }
 104  
 
 105  
     /**
 106  
      * Receives a <code>ProtocolDataUnit</code> from the socket and appends it to the end of the receiving queue of this
 107  
      * connection.
 108  
      * 
 109  
      * @return Queue with the resulting units
 110  
      * @throws IOException if an I/O error occurs.
 111  
      * @throws InternetSCSIException if any violation of the iSCSI-Standard emerge.
 112  
      * @throws DigestException if a mismatch of the digest exists.
 113  
      * @throws SettingsException
 114  
      */
 115  
     ProtocolDataUnit receiveFromWire () throws DigestException , InternetSCSIException , IOException , SettingsException {
 116  
 
 117  
         ProtocolDataUnit pdu;
 118  0
         if (initialPdu) {
 119  
             /*
 120  
              * The connection's ConnectionSettingsNegotiator has not been initialized, hence getSettings() would throw a
 121  
              * NullPointerException. Initialize PDU with default values, i.e. no digests.
 122  
              */
 123  0
             pdu = protocolDataUnitFactory.create(TextKeyword.NONE,// header
 124  
                                                                   // digest
 125  
                     TextKeyword.NONE);// data digest
 126  
         } else {
 127  
             // use negotiated or (now available) default settings
 128  0
             final Settings settings = connection.getSettings();
 129  0
             pdu = protocolDataUnitFactory.create(settings.getHeaderDigest(), settings.getDataDigest());
 130  
         }
 131  
 
 132  
         try {
 133  0
             pdu.read(socketChannel);
 134  0
         } catch (ClosedChannelException e) {
 135  0
             throw new InternetSCSIException(e);
 136  0
         }
 137  
 
 138  0
         if (LOGGER.isDebugEnabled()) LOGGER.debug("Receiving this PDU:\n" + pdu);
 139  
 
 140  
         // parse sequence counters
 141  0
         final BasicHeaderSegment bhs = pdu.getBasicHeaderSegment();
 142  0
         final InitiatorMessageParser parser = (InitiatorMessageParser) bhs.getParser();
 143  
         // final int commandSequenceNumber = parser.getCommandSequenceNumber();
 144  
         // final int expectedStatusSequenceNumber = parser.getExpectedStatusSequenceNumber();
 145  
 
 146  0
         if (LOGGER.isDebugEnabled()) {
 147  
             // sharrajesh
 148  
             // Needed to debug, out of order receiving of StatusSN and ExpStatSN
 149  0
             if (bhs.getOpCode() == OperationCode.SCSI_COMMAND) {
 150  0
                 final SCSICommandParser scsiParser = (SCSICommandParser) bhs.getParser();
 151  0
                 ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(scsiParser.getCDB().get(0));
 152  0
                 LOGGER.debug("scsiOpCode = " + scsiOpCode);
 153  0
                 LOGGER.debug("CDB bytes: \n" + Debug.byteBufferToString(scsiParser.getCDB()));
 154  
             }
 155  
             // LOGGER.debug("parser.expectedStatusSequenceNumber: " + expectedStatusSequenceNumber);
 156  0
             if (connection == null)
 157  0
                 LOGGER.debug("connection: null");
 158  0
             else if (connection.getStatusSequenceNumber() == null)
 159  0
                 LOGGER.debug("connection.getStatusSequenceNumber: null");
 160  
             else
 161  0
                 LOGGER.debug("connection.getStatusSequenceNumber: " + connection.getStatusSequenceNumber().getValue());
 162  
         }
 163  
 
 164  
         // if this is the first PDU in the leading connection, then
 165  
         // initialize the session's ExpectedCommandSequenceNumber
 166  0
         if (initialPdu) {
 167  0
             initialPdu = false;
 168  
             // PDU is immediate Login PDU, checked in Target.main(),
 169  
             // ExpCmdSN of this PDU will be used to initialize the
 170  
             // respective session and connection parameters (sequence numbers)
 171  
             // see TargetSession and TargetConnection initialization in
 172  
             // Target.main()
 173  
         } else {
 174  
             // check sequence counters
 175  
             // if (session.getMaximumCommandSequenceNumber().lessThan(commandSequenceNumber))
 176  
             // throw new InternetSCSIException("received CmdSN > local MaxCmdSN");
 177  
 
 178  
             // verified, is working with Windows 8 initiator
 179  
             // if (!connection.getStatusSequenceNumber().equals(expectedStatusSequenceNumber)
 180  
             // && expectedStatusSequenceNumber != 0)// required by MS iSCSI
 181  
             // // initiator DATA-OUT
 182  
             // // PDU sequence
 183  
             // throw new InternetSCSIException("received ExpStatusSN != local StatusSN + 1");
 184  
         }
 185  
 
 186  
         // increment CmdSN if not immediate PDU (or Data-Out PDU)
 187  
         try {
 188  0
             if (parser.incrementSequenceNumber()) session.getExpectedCommandSequenceNumber().increment();
 189  0
         } catch (NullPointerException exc) {
 190  
 
 191  0
         }
 192  
 
 193  0
         return pdu;
 194  
     }
 195  
 
 196  
     /**
 197  
      * Sends the given <code>ProtocolDataUnit</code> instance over the socket to the connected iSCSI Target.
 198  
      * 
 199  
      * @param pdu The <code>ProtocolDataUnit</code> instances to send.
 200  
      * @throws InternetSCSIException if any violation of the iSCSI-Standard emerge.
 201  
      * @throws IOException if an I/O error occurs.
 202  
      * @throws InterruptedException if another caller interrupted the current caller before or while the current caller
 203  
      *             was waiting for a notification. The interrupted status of the current caller is cleared when this
 204  
      *             exception is thrown.
 205  
      */
 206  
 
 207  
     final void sendOverWire (final ProtocolDataUnit pdu) throws InternetSCSIException , IOException , InterruptedException {
 208  
 
 209  
         // set sequence counters
 210  0
         final TargetMessageParser parser = (TargetMessageParser) pdu.getBasicHeaderSegment().getParser();
 211  0
         parser.setExpectedCommandSequenceNumber(session.getExpectedCommandSequenceNumber().getValue());
 212  0
         parser.setMaximumCommandSequenceNumber(session.getMaximumCommandSequenceNumber().getValue());
 213  0
         final boolean incrementSequenceNumber = parser.incrementSequenceNumber();
 214  0
         if (incrementSequenceNumber) // set StatSN only if field is not reserved
 215  0
         parser.setStatusSequenceNumber(connection.getStatusSequenceNumber().getValue());
 216  
 
 217  0
         if (LOGGER.isDebugEnabled()) LOGGER.debug("Sending this PDU:\n" + pdu);
 218  
 
 219  
         // send pdu
 220  0
         pdu.write(socketChannel);
 221  
 
 222  
         // increment StatusSN if this was a Response PDU (with status)
 223  
         // or if special cases apply
 224  0
         if (incrementSequenceNumber) connection.getStatusSequenceNumber().increment();
 225  
 
 226  0
     }
 227  
 }