| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| Entry |
|
| 2.3333333333333335;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 | } |