Coverage Report - org.jscsi.target.settings.entry.Entry
 
Classes in this File Line Coverage Branch Coverage Complexity
Entry
0%
0/55
0%
0/20
2.333
 
 1  
 package org.jscsi.target.settings.entry;
 2  
 
 3  
 
 4  
 import java.util.Collection;
 5  
 
 6  
 import javax.naming.OperationNotSupportedException;
 7  
 
 8  
 import org.jscsi.parser.ProtocolDataUnit;
 9  
 import org.jscsi.parser.login.LoginStage;
 10  
 import org.jscsi.target.TargetServer;
 11  
 import org.jscsi.target.settings.KeySet;
 12  
 import org.jscsi.target.settings.NegotiationStatus;
 13  
 import org.jscsi.target.settings.NegotiationType;
 14  
 import org.jscsi.target.settings.SettingsNegotiator;
 15  
 import org.jscsi.target.settings.TextKeyword;
 16  
 import org.jscsi.target.settings.TextParameter;
 17  
 import org.slf4j.Logger;
 18  
 import org.slf4j.LoggerFactory;
 19  
 
 20  
 
 21  
 /**
 22  
  * {@link Entry} objects are used by instances {@link SettingsNegotiator} during text negotiation of connection and
 23  
  * session parameter. For all parameters that are either declared by the iSCSI initiator or negotiated between the
 24  
  * initiator and the target a separate {@link Entry} takes care of processing the respective <i>key=value</i> pair and
 25  
  * returning the negotiated value, if appropriate.
 26  
  * <p>
 27  
  * For brevity, the term "negotiated" will be used in the following in a way that can either mean
 28  
  * "declared or negotiated", unless the distinction is evident by context.
 29  
  * 
 30  
  * @author Andreas Ergenzinger, University of Konstanz
 31  
  */
 32  
 public abstract class Entry {
 33  
 
 34  0
     private static final Logger LOGGER = LoggerFactory.getLogger(Entry.class);
 35  
 
 36  
     /**
 37  
      * A {@link KeySet} containing all keys that can be used for negotiating this {@link Entry}'s value.
 38  
      */
 39  
     protected final KeySet keySet;
 40  
 
 41  
     /**
 42  
      * Specifies if the {@link Entry}'s parameter is declared or negotiated.
 43  
      */
 44  
     protected final NegotiationType negotiationType;
 45  
 
 46  
     /**
 47  
      * Determines during which stages this {@link Entry}'s parameters may be negotiated.
 48  
      */
 49  
     protected final Use use;
 50  
 
 51  
     /**
 52  
      * This variable specifies the progress and necessity of negotiating the parameter managed by this {@link Entry}.
 53  
      */
 54  
     protected NegotiationStatus negotiationStatus;
 55  
 
 56  
     /**
 57  
      * The currently valid value or <code>null</code>.
 58  
      */
 59  
     protected Object value;
 60  
 
 61  
     /**
 62  
      * This variable is used to detect illegal attempts to renegotiate a previously negotiated or declared text
 63  
      * parameter.
 64  
      * <p>
 65  
      * This variable will be set back to <code>false</code> after each negotiation task (login phase, or text parameter
 66  
      * negotiation stage). Renegotiation accross stages/tasks can be prevented by initializing the {@link #use} variable
 67  
      * accordingly.
 68  
      * 
 69  
      * @see #resetAlreadyNegotiated()
 70  
      */
 71  0
     protected boolean alreadyNegotiated = false;
 72  
 
 73  
     /**
 74  
      * Abstract constructor.
 75  
      * 
 76  
      * @param keySet contains all relevant keys
 77  
      * @param negotiationType declared or negotiated
 78  
      * @param use determines under which circumstances the parameter may be negotiated
 79  
      * @param negotiationStatus indicates whether there is a default value or if the parameter must be negotiated
 80  
      * @param defaultValue the default value or <code>null</code>
 81  
      */
 82  0
     public Entry (final KeySet keySet, final NegotiationType negotiationType, final Use use, final NegotiationStatus negotiationStatus, Object defaultValue) {
 83  0
         this.keySet = keySet;
 84  0
         this.negotiationType = negotiationType;
 85  0
         this.use = use;
 86  0
         this.negotiationStatus = negotiationStatus;
 87  0
         this.value = defaultValue;
 88  0
     }
 89  
 
 90  
     /**
 91  
      * Logs an error message containing all {@link #keySet} keys as well as the passed {@link String} parameter and
 92  
      * indicates an unsuccessful negotiation by setting {@link #negotiationStatus} to {@link NegotiationStatus#REJECTED}
 93  
      * .
 94  
      * 
 95  
      * @param logMessage
 96  
      */
 97  
     private void fail (final String logMessage) {
 98  0
         LOGGER.error("negotiation error " + keySet + ": " + logMessage);
 99  0
         negotiationStatus = NegotiationStatus.REJECTED;
 100  0
     }
 101  
 
 102  
     /**
 103  
      * Parses the passed {@link String} parameter and returns a sub-class-specific {@link Object} which represents the
 104  
      * the specified <i>value</i> part a <i>key=value</i> pair.
 105  
      * 
 106  
      * @param values the <i>value</i> part of a <i>key=value</i> pair
 107  
      * @return sub-class-specific {@link Object} or <code>null</code> if the parameter violated the expected format
 108  
      */
 109  
     protected abstract Object parseOffer (TargetServer target, String values);
 110  
 
 111  
     /**
 112  
      * This method is used for negotiating or declaring the {@link Entry}'s parameter.
 113  
      * 
 114  
      * @param loginStage specifying the current stage or phase of the connection whose parameters are to be negotiated
 115  
      * @param leadingConnection <code>true</code> if the connection is the first connection in its session,
 116  
      *            <code>false</code> if not
 117  
      * @param initialPdu <code>true</code> if the <i>key=value</i> pair parameters have been sent in the first login
 118  
      *            {@link ProtocolDataUnit} from the initiator, <code>false</code> if thy have not
 119  
      * @param key the <i>key</i> part from the received <i>key=value</i> pair
 120  
      * @param values the <i>value</i> part from the received <i>key=value</i> pair
 121  
      * @param responseKeyValuePairs where the reply <i>key=value</i> pair will be added to if necessary
 122  
      * @return <code>true</code> if everything went fine, <code>false</code> if errors occured
 123  
      */
 124  
     public final boolean negotiate (TargetServer target, final LoginStage loginStage, final boolean leadingConnection, final boolean initialPdu, final String key, final String values, final Collection<String> responseKeyValuePairs) {
 125  
 
 126  
         // (re)check key (just in case), this should have been checked before
 127  
         // calling this method
 128  0
         if (!matchKey(key)) {
 129  0
             fail("\"" + key + "\" does not match key in" + keySet);
 130  0
             return false;
 131  
         }
 132  
 
 133  
         // prevent renegotiation and remember this negotiation
 134  0
         if (alreadyNegotiated) {
 135  0
             fail("illegal renegotiation");
 136  0
             return false;
 137  
         }
 138  0
         alreadyNegotiated = true;
 139  
 
 140  
         // check use code
 141  0
         if (!use.checkUse(loginStage, leadingConnection, initialPdu)) {
 142  0
             fail("wrong use: " + use + ", " + loginStage + ", " + leadingConnection + ", " + initialPdu);
 143  0
             return false;
 144  
         }
 145  
 
 146  
         // transform values to appropriate type
 147  0
         final Object offer = parseOffer(target, values);
 148  
 
 149  0
         if (offer == null) {
 150  0
             fail("value format error: " + values);
 151  0
             return false;
 152  
         }
 153  
 
 154  
         // check if values are in the protocol-conform range/set of values
 155  0
         if (!inProtocolValueRange(offer)) {
 156  0
             fail("illegal values offered: " + values);
 157  0
             return false;
 158  
         }
 159  
 
 160  
         // *** declare ***
 161  0
         if (negotiationType == NegotiationType.DECLARED) {
 162  
             // save received value ...
 163  0
             processDeclaration(offer);
 164  
             // ... and accept silently
 165  0
             negotiationStatus = NegotiationStatus.ACCEPTED;
 166  0
             return true;
 167  
         }
 168  
 
 169  
         // *** negotiate ***
 170  0
         if (negotiationType == NegotiationType.NEGOTIATED) {
 171  
 
 172  
             String negotiatedValue;// will be returned as value part
 173  
 
 174  0
             if (negotiationStatus == NegotiationStatus.IRRELEVANT)
 175  0
                 negotiatedValue = TextKeyword.IRRELEVANT;
 176  
             else
 177  0
                 negotiatedValue = processNegotiation(offer);
 178  
 
 179  
             String reply;
 180  
             // reply, remember outcome, log, and return
 181  0
             if (negotiatedValue == null) {// no commonly supported values
 182  0
                 reply = TextParameter.toKeyValuePair(key, TextKeyword.REJECT);
 183  0
                 responseKeyValuePairs.add(reply);
 184  0
                 fail("rejected value(s): " + values);
 185  0
                 return false;
 186  
             }// else
 187  0
             reply = TextParameter.toKeyValuePair(key, negotiatedValue);
 188  0
             responseKeyValuePairs.add(reply);
 189  0
             return true;
 190  
         }
 191  
 
 192  
         // we should not be here
 193  0
         fail("initialization error: negotiationType == null");
 194  0
         return false;
 195  
     }
 196  
 
 197  
     /**
 198  
      * Sets {@link #alreadyNegotiated} back to <code>false</code>.
 199  
      * <p>
 200  
      * This method must be used at the end of each negotiation task, i.e. at the end of the login phase and the FFP text
 201  
      * negotiation stage.
 202  
      */
 203  
     public void resetAlreadyNegotiated () {
 204  0
         alreadyNegotiated = false;
 205  0
     }
 206  
 
 207  
     /**
 208  
      * Returns the negotiated (or default) value as a {@link Boolean}.
 209  
      * 
 210  
      * @return the negotiated (or default) value as a {@link Boolean}
 211  
      * @throws OperationNotSupportedException if {@link #value} is not of the boolean type
 212  
      */
 213  
     public Boolean getBooleanValue () throws OperationNotSupportedException {
 214  0
         throw new OperationNotSupportedException();
 215  
     }
 216  
 
 217  
     /**
 218  
      * Returns the negotiated (or default) value as an {@link Integer}.
 219  
      * 
 220  
      * @return the negotiated (or default) value as an {@link Integer}
 221  
      * @throws OperationNotSupportedException if {@link #value} is not of the integer type
 222  
      */
 223  
     public Integer getIntegerValue () throws OperationNotSupportedException {
 224  0
         throw new OperationNotSupportedException();
 225  
     }
 226  
 
 227  
     /**
 228  
      * Returns the negotiated (or default) value as a {@link String}.
 229  
      * 
 230  
      * @return the negotiated (or default) value as a {@link String}
 231  
      * @throws OperationNotSupportedException if {@link #value} is not a {@link String}
 232  
      */
 233  
     public String getStringValue () throws OperationNotSupportedException {
 234  0
         throw new OperationNotSupportedException();
 235  
     }
 236  
 
 237  
     /**
 238  
      * Returns <code>true</code> if one of the keys of {@link #keySet} equals the parameter and <code>false</code> if
 239  
      * there is not match.
 240  
      * 
 241  
      * @param key the key to compare to the {@link #keySet} keys
 242  
      * @return <code>true</code> if one of the keys of {@link #keySet} equals the parameter and <code>false</code> if
 243  
      *         not
 244  
      */
 245  
     public final boolean matchKey (final String key) {
 246  0
         return keySet.matchKey(key);
 247  
     }
 248  
 
 249  
     /**
 250  
      * This method is used for checking if a sub-class-specific {@link Object}, representing a single, a range, or a
 251  
      * list of values sent by the initiator, is illegal, according to the iSCSI standard.
 252  
      * 
 253  
      * @param values a sub-class-specific {@link Object}, representing a single, a range, or a list of values sent by
 254  
      *            the initiator
 255  
      * @return <code>false</code> if the iSCSI standard has been violated, <code>true</code> if not
 256  
      */
 257  
     protected abstract boolean inProtocolValueRange (Object values);
 258  
 
 259  
     /**
 260  
      * Receives a sub-class-specific {@link Object}, representing a legal parameter value declared by the initiator and
 261  
      * accepts it as the new {@link #value}.
 262  
      * 
 263  
      * @param values sub-class-specific representation of a single <i>value</i> declared by the initiator
 264  
      */
 265  
     protected abstract void processDeclaration (Object values);
 266  
 
 267  
     // returns null if reply is to be key=Reject
 268  
     /**
 269  
      * Receives a sub-class-specific {@link Object}, representing a list, a range, or a single legal parameter value
 270  
      * offered by the initiator and tries to select a value from that offer. If none of the offered values is supported
 271  
      * by the jSCSI Target, <code>null</code> is returned, otherwise the selection is accepted as the new {@link #value}
 272  
      * and returned as a {@link String}. {@link #value}.
 273  
      * 
 274  
      * @param values a sub-class-specific {@link Object}, representing a list, a range, or a single legal parameter
 275  
      *            value offered by the initiator
 276  
      * @return the final, negotiated value or <code>null</code>, if the initiator's offer does not overlap with the
 277  
      *         values supported by the jSCSI Target
 278  
      */
 279  
     protected abstract String processNegotiation (Object values);
 280  
 
 281  
     /**
 282  
      * Returns {@link #negotiationStatus}.
 283  
      * 
 284  
      * @return {@link #negotiationStatus}
 285  
      */
 286  
     public final NegotiationStatus getNegotiationStatus () {
 287  0
         return negotiationStatus;
 288  
     }
 289  
 
 290  
     /**
 291  
      * Returns an exact copy of this {@link Entry}.
 292  
      * 
 293  
      * @return a copy of this {@link Entry}.
 294  
      */
 295  
     public abstract Entry copy ();
 296  
 
 297  
     /**
 298  
      * Returns <code>true</code> if {@link #negotiationStatus} is {@link NegotiationStatus#ACCEPTED} and
 299  
      * <code>false</code> if it is not.
 300  
      * 
 301  
      * @return <code>true</code> if {@link #negotiationStatus} is {@link NegotiationStatus#ACCEPTED}, <code>false</code>
 302  
      *         if not
 303  
      */
 304  
     public boolean checkAccepted () {
 305  0
         return negotiationStatus == NegotiationStatus.ACCEPTED;
 306  
     }
 307  
 }