Coverage Report - org.jscsi.target.settings.ConnectionSettingsNegotiator
 
Classes in this File Line Coverage Branch Coverage Complexity
ConnectionSettingsNegotiator
0%
0/70
0%
0/26
2.4
 
 1  
 package org.jscsi.target.settings;
 2  
 
 3  
 
 4  
 import java.util.List;
 5  
 import java.util.Vector;
 6  
 
 7  
 import org.jscsi.parser.ProtocolDataUnit;
 8  
 import org.jscsi.parser.login.LoginStage;
 9  
 import org.jscsi.target.TargetServer;
 10  
 import org.jscsi.target.connection.TargetSession;
 11  
 import org.jscsi.target.settings.entry.BooleanEntry;
 12  
 import org.jscsi.target.settings.entry.Entry;
 13  
 import org.jscsi.target.settings.entry.NumericalEntry;
 14  
 import org.jscsi.target.settings.entry.NumericalRangeEntry;
 15  
 import org.jscsi.target.settings.entry.StringEntry;
 16  
 import org.jscsi.target.settings.entry.Use;
 17  
 
 18  
 
 19  
 /**
 20  
  * In addition to offering all methods of their {@link SettingsNegotiator} parent class,
 21  
  * {@link ConnectionSettingsNegotiator} instances provide all methods necessary for text parameter negotiation and for
 22  
  * creating {@link Settings} objects, which allow access to the current parameters.
 23  
  * <p>
 24  
  * Each instance of this class belongs to one <code>Connection</code>, for which it manages all connection-specific
 25  
  * parameters. A similar association exists between the connection's enclosing {@link TargetSession} and the
 26  
  * {@link #sessionSettingsNegotiator} member variable.
 27  
  * <p>
 28  
  * A negotiation sequence consists of the following steps, performed in the given order:
 29  
  * <p>
 30  
  * <ol>
 31  
  * <li>Call {@link #beginNegotiation()} until it returns <code>true</code>.</li>
 32  
  * <li>One or, if the initiator is using multiple PDU sequences, multiple calls of
 33  
  * {@link #negotiate(TargetServer, LoginStage, boolean, boolean, List, List)}. The method will return <code>false</code>
 34  
  * if there was a problem.</li>
 35  
  * <li>Call {@link #checkConstraints()} to check more complex requirements. The method will return <code>false</code> if
 36  
  * there was a problem.</li>
 37  
  * <li>Call {@link #finishNegotiation(boolean)} with the appropriate parameter. This step is mandatory.</li>
 38  
  * </ol>
 39  
  * 
 40  
  * @author Andreas Ergenzinger, University of Konstanz
 41  
  */
 42  
 public final class ConnectionSettingsNegotiator extends SettingsNegotiator {
 43  
 
 44  
     /**
 45  
      * The {@link SessionSettingsNegotiator} maintaining all {@link Entry} objects for session-wide parameters.
 46  
      */
 47  
     private final SessionSettingsNegotiator sessionSettingsNegotiator;
 48  
 
 49  
     /**
 50  
      * A current snapshot of all connection-specific parameters.
 51  
      */
 52  
     private ConnectionSettingsBuilderComponent connectionSettingBuilderComponent;
 53  
 
 54  
     /**
 55  
      * A current snapshot of all parameters, both connection-specific and session-wide.
 56  
      */
 57  
     private Settings settings;
 58  
 
 59  
     /**
 60  
      * The {@link ConnectionSettingsNegotiator} constructor.
 61  
      * 
 62  
      * @param sessionSettingsNegotiator the {@link SessionSettingsNegotiator} maintaining all {@link Entry} objects for
 63  
      *            session-wide parameters
 64  
      */
 65  
     public ConnectionSettingsNegotiator (final SessionSettingsNegotiator sessionSettingsNegotiator) {
 66  0
         super();// initializes entries
 67  0
         this.sessionSettingsNegotiator = sessionSettingsNegotiator;
 68  
         // initialize settings
 69  0
         updateSettingsBuilderComponent();
 70  0
         updateSettings();
 71  0
     }
 72  
 
 73  
     /**
 74  
      * This method must be called at the beginning of a parameter negotiation sequence.
 75  
      * <p>
 76  
      * This method will block until no other {@link ConnectionSettingsNegotiator} is negotiating and will return
 77  
      * <code>true</code> if negotiations may proceed, or <code>false</code>, if the request for the permission to
 78  
      * negotiate was denied.
 79  
      * 
 80  
      * @return <code>true</code> if and only if the caller is allowed to negotiate parameters
 81  
      * @see #finishNegotiation(boolean)
 82  
      */
 83  
     public boolean beginNegotiation () {
 84  0
         final boolean hasLock = sessionSettingsNegotiator.lock();
 85  0
         if (hasLock) {
 86  
             // back up entries with connection and with session scope
 87  0
             backUpEntries();
 88  0
             sessionSettingsNegotiator.backUpEntries();
 89  
         }
 90  0
         return hasLock;
 91  
     }
 92  
 
 93  
     /**
 94  
      * This method must be called at the end of a negotiation sequence. If there was a problem during negotiations, all
 95  
      * changes to the managed {@link Entry} objects must be discarded by setting the <i>commitChanges</i> parameter to
 96  
      * <code>false</code>. If it is <code>true</code>, then the parameter changes will be incorporated into the updated
 97  
      * {@link #settings}.
 98  
      * <p>
 99  
      * Calling this method should be ensured by a <code>try ... catch ...
 100  
      * finally</code> block.
 101  
      * 
 102  
      * @param commitChanges <code>true</code> if and only if the negotiated parameter changes are to be committed
 103  
      */
 104  
     public void finishNegotiation (final boolean commitChanges) {
 105  
         // commit or roll back connection-specific entries
 106  0
         commitOrRollBackChanges(commitChanges);
 107  
         // commit or roll back session-wide entries
 108  0
         sessionSettingsNegotiator.commitOrRollBackChanges(commitChanges);
 109  
         // update settings
 110  0
         updateSettingsBuilderComponent();
 111  0
         sessionSettingsNegotiator.updateSettingsBuilderComponent();
 112  0
         updateSettings();
 113  
         // allow other Threads to negotiate
 114  0
         sessionSettingsNegotiator.unlock();
 115  0
     }
 116  
 
 117  
     /**
 118  
      * Processes one or more <i>key-value</i> pairs sent by the initiator, formulates response <i>key-value</i> pairs
 119  
      * and changes the involved {@link Entry} instances accordingly.
 120  
      * 
 121  
      * @param loginStage specifies the current stage or phase
 122  
      * @param leadingConnection <code>true</code> if and only if the connection is the first connection of the enclosing
 123  
      *            session
 124  
      * @param initialPdu <code>true</code> if and only if the <i>key-value</i> pairs were sent in the first
 125  
      *            {@link ProtocolDataUnit} received on this connection
 126  
      * @param requestKeyValuePairs contains the <i>key-value</i> pairs from the initiator; processed elements will be
 127  
      *            removed
 128  
      * @param responseKeyValuePairs will contain the <i>key-value</i> pairs from the jSCSI target
 129  
      * @return <code>true</code> if everything went fine and <code>false</code> if there was an irreconcilable problem
 130  
      */
 131  
     public boolean negotiate (TargetServer target, final LoginStage loginStage, final boolean leadingConnection, final boolean initialPdu, final List<String> requestKeyValuePairs, final List<String> responseKeyValuePairs) {
 132  
 
 133  
         // split up key=value pairs from requester
 134  0
         final List<String> keys = new Vector<String>();
 135  0
         final List<String> values = new Vector<String>();
 136  0
         for (String keyValuePair : requestKeyValuePairs) {
 137  0
             final String[] split = TextParameter.splitKeyValuePair(keyValuePair);
 138  0
             if (split == null) {
 139  0
                 System.err.println("malformatted key=value pair: " + keyValuePair);
 140  0
                 return false;
 141  
             }
 142  0
             keys.add(split[0]);
 143  0
             values.add(split[1]);
 144  0
         }
 145  
 
 146  
         // get respective entry (declared by initiator, or negotiated) and
 147  
         // let it process the key=value pair
 148  
         Entry entry;
 149  0
         boolean everythingOkay = true;
 150  0
         while (keys.size() > 0) {
 151  0
             entry = getEntry(keys.get(0));
 152  
             // respond to unknown keys
 153  0
             if (entry == null) {
 154  0
                 responseKeyValuePairs.add(TextParameter.toKeyValuePair(keys.get(0), TextKeyword.NOT_UNDERSTOOD));
 155  
             } else {// appropriate entry was found
 156  
                 // process key=value pair and remember if there is any trouble
 157  0
                 everythingOkay &= entry.negotiate(target, loginStage, leadingConnection, initialPdu, keys.get(0), values.get(0), responseKeyValuePairs);
 158  
             }
 159  
             // remove processed key and values
 160  0
             keys.remove(0);
 161  0
             values.remove(0);
 162  
         }
 163  
 
 164  
         // check if initiator has sent all mandatory parameters
 165  
         // and append target declarations
 166  0
         if (initialPdu) {
 167  
             // initiator must provide the InitiatorName in the first Login PDU
 168  
             // (InitiatorAlias is optional)
 169  0
             everythingOkay &= getEntry(TextKeyword.INITIATOR_NAME).checkAccepted();
 170  
 
 171  
             // in a normal session the initiator must declare TargetName
 172  
             // the target must reply with TargetPortalGroupTag and should
 173  
             // append TargetAlias
 174  0
             boolean normalSession = TextKeyword.NORMAL.equals(((StringEntry) getEntry(TextKeyword.SESSION_TYPE)).getStringValue());
 175  0
             if (normalSession) {
 176  
                 // check if proposed TargetName is correct
 177  0
                 final StringEntry targetNameEntry = (StringEntry) getEntry(TextKeyword.TARGET_NAME);
 178  0
                 final String targetName = targetNameEntry.getStringValue();
 179  0
                 if (targetName == null || // not declared
 180  
                 !target.isValidTargetName(targetName)) // wrong name
 181  0
                 everythingOkay = false;
 182  
 
 183  
                 // send TargetAlias
 184  0
                 String targetAlias = null;
 185  0
                 if (everythingOkay) targetAlias = target.getTarget(targetName).getTargetAlias();// might
 186  
                 // be
 187  
                 // undefined
 188  0
                 if (targetAlias != null) {
 189  0
                     responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.TARGET_ALIAS, targetAlias));
 190  
                 }
 191  
 
 192  
                 // send TargetPortalGroupTag
 193  0
                 responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.TARGET_PORTAL_GROUP_TAG, Integer.valueOf(target.getConfig().getTargetPortalGroupTag()).toString()));
 194  
             }
 195  
         }
 196  
 
 197  0
         return everythingOkay;
 198  
     }
 199  
 
 200  
     @Override
 201  
     public boolean checkConstraints () {
 202  0
         return sessionSettingsNegotiator.checkConstraints();
 203  
         /*
 204  
          * in multi-connection session, the InitiatorName of follow-up connections should be checked against the
 205  
          * InitiatorName of the leading connection
 206  
          */
 207  
     }
 208  
 
 209  
     /**
 210  
      * Returns the {@link Entry} responsible for negotiating the specified <i>key</i> identifying either a session-wide
 211  
      * or connection-specific parameter, or <code>null</code> if no such {@link Entry} can be found.
 212  
      * 
 213  
      * @param key identifies an {@link Entry}
 214  
      * @return the requested {@link Entry} or <code>null</code>
 215  
      */
 216  
     private Entry getEntry (final String key) {
 217  0
         Entry entry = null;
 218  
         // check connection-only entries
 219  0
         entry = getEntry(key, entries);
 220  0
         if (entry == null) // keep looking in session-wide entries
 221  0
         entry = sessionSettingsNegotiator.getEntry(key);
 222  0
         return entry;
 223  
     }
 224  
 
 225  
     /**
 226  
      * This method checks if any parameter changes have been committed since the last call and, if so, creates a new
 227  
      * {@link Settings} object reflecting the current parameters.
 228  
      * 
 229  
      * @return The current parameters
 230  
      */
 231  
     public Settings getSettings () {
 232  
         // check if settings are up to date
 233  0
         if (sessionSettingsNegotiator.getCurrentSettingsId() > settings.getSettingsId()) updateSettings();
 234  0
         return settings;
 235  
     }
 236  
 
 237  
     /**
 238  
      * Updates {@link #settings} by replacing it with a more current copy of the managed parameters.
 239  
      */
 240  
     private synchronized void updateSettings () {
 241  0
         settings = new Settings(connectionSettingBuilderComponent, sessionSettingsNegotiator.getSessionSettingsBuilderComponent());
 242  0
     }
 243  
 
 244  
     @Override
 245  
     protected void initializeEntries () {
 246  
 
 247  
         /*
 248  
          * Determines type and use of the data digest.
 249  
          */
 250  0
         entries.add(new StringEntry(new KeySet(TextKeyword.DATA_DIGEST),// keySet
 251  
         NegotiationType.NEGOTIATED,// negotiationType
 252  
         Use.LOPNS,// use
 253  
         NegotiationStatus.DEFAULT,// negotiationStatus
 254  
         new String[] { TextKeyword.NONE },// supportedValues,
 255  
         TextKeyword.NONE));// defaultValue
 256  
 
 257  
         /*
 258  
          * Determines type and use of the header digest.
 259  
          */
 260  0
         entries.add(new StringEntry(new KeySet(TextKeyword.HEADER_DIGEST),// keySet
 261  
         NegotiationType.NEGOTIATED,// negotiationType
 262  
         Use.LOPNS,// use
 263  
         NegotiationStatus.DEFAULT,// negotiationStatus
 264  
         new String[] { TextKeyword.NONE },// supportedValues,
 265  
         TextKeyword.NONE));// defaultValue
 266  
 
 267  
         /*
 268  
          * Turns the target-to-initiator markers on or off.
 269  
          */
 270  0
         entries.add(new BooleanEntry(new KeySet(TextKeyword.IF_MARKER),// keySet
 271  
         Use.LOPNS,// use
 272  
         NegotiationStatus.DEFAULT,// negotiationStatus
 273  
         false,// negotiationValue
 274  
         BooleanResultFunction.AND,// resultFunction
 275  
         false));// defaultValue
 276  
 
 277  
         /*
 278  
          * Interval value (in 4-byte words) for target-to-initiator markers. The interval is measured from the end of
 279  
          * one marker to the beginning of the next one. The offer can have only a range; the response can have only a
 280  
          * single value (picked from the offered range) or Reject. Will always be Irrelevant.
 281  
          */
 282  0
         entries.add(new NumericalRangeEntry(new KeySet(TextKeyword.IF_MARK_INT),// keySet
 283  
         Use.LOPNS,// use
 284  
         NegotiationStatus.IRRELEVANT,// negotiationStatus
 285  
         2048,// negotiationValue
 286  
         NumericalValueRange.create(1, 65535),// protocolValueRange
 287  
         2048));// defaultValue
 288  
 
 289  
         /*
 290  
          * The maximum amount of data that either the initiator or the target can receive in any iSCSI PDU. Zero (don't
 291  
          * care) can be used. I&T can specify (declare) the Max they can receive. This is a connection- and direction-
 292  
          * specific parameter. The actual value used by the target will be min(This value, MaxBurstLength) for data-in
 293  
          * and solicited data-out data. Min(This value, FirstBurstLength) for unsolicited data.
 294  
          */
 295  0
         entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_RECV_DATA_SEGMENT_LENGTH),// keySet
 296  
         NegotiationType.DECLARED,// negotiationType
 297  
         Use.LOPNS_AND_FFP,// use
 298  
         NegotiationStatus.DEFAULT,// negotiationStatus
 299  
         8192,// negotiationValue (default value, will be used if I sends
 300  
              // 0)
 301  
         NumericalValueRange.create(512, 16777215),// protocolValueRange
 302  
         // 512 to 2^24 - 1
 303  
         NumericalResultFunction.MIN,// resultFunction
 304  
         8192,// defaultValue, 8K
 305  
         true));// zeroMeansDontCare
 306  
 
 307  
         /*
 308  
          * Turns the initiator-to-target markers on or off.
 309  
          */
 310  0
         entries.add(new BooleanEntry(new KeySet(TextKeyword.OF_MARKER),// keySet
 311  
         Use.LOPNS,// use
 312  
         NegotiationStatus.DEFAULT,// negotiationStatus
 313  
         false,// negotiationValue
 314  
         BooleanResultFunction.AND,// resultFunction
 315  
         false));// defaultValue
 316  
 
 317  
         /*
 318  
          * Interval value (in 4-byte words) for initiator-to-target markers. The interval is measured from the end of
 319  
          * one marker to the beginning of the next one. The offer can have only a range; the response can have only a
 320  
          * single value (picked from the offered range) or Reject. Will always be Irrelevant.
 321  
          */
 322  0
         entries.add(new NumericalRangeEntry(new KeySet(TextKeyword.OF_MARK_INT),// keySet
 323  
         Use.LOPNS,// use
 324  
         NegotiationStatus.IRRELEVANT,// negotiationStatus
 325  
         2048,// negotiationValue
 326  
         NumericalValueRange.create(1, 65535),// protocolValueRange
 327  
         2048));// defaultValue
 328  
 
 329  
         /*
 330  
          * This entry is not used for Settings initialization, TargetName has a session-wide scope. This entry
 331  
          * intercepts the TargetName parameter the initiator has to declare at the beginning of normal sessions.
 332  
          */
 333  0
         entries.add(new StringEntry(new KeySet(TextKeyword.TARGET_NAME),// keySet
 334  
         NegotiationType.DECLARED,// negotiationType
 335  
         Use.INITIAL,// use
 336  
         NegotiationStatus.NOT_NEGOTIATED,// negotiationStatus
 337  
         null,// supportedValues, anything goes
 338  
         null));// defaultValue
 339  0
     }
 340  
 
 341  
     /**
 342  
      * Updates {@link ConnectionSettingsBuilderComponent} with the currently valid parameters retrieved from the
 343  
      * elements of {@link SettingsNegotiator#entries}.
 344  
      */
 345  
     protected void updateSettingsBuilderComponent () {
 346  0
         connectionSettingBuilderComponent = new ConnectionSettingsBuilderComponent(entries);
 347  0
     }
 348  
 }