| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| SessionSettingsNegotiator |
|
| 1.4444444444444444;1.444 |
| 1 | package org.jscsi.target.settings; | |
| 2 | ||
| 3 | ||
| 4 | import java.util.concurrent.atomic.AtomicLong; | |
| 5 | import java.util.concurrent.locks.Lock; | |
| 6 | ||
| 7 | import javax.naming.OperationNotSupportedException; | |
| 8 | ||
| 9 | import org.jscsi.target.connection.TargetSession; | |
| 10 | import org.jscsi.target.settings.entry.BooleanEntry; | |
| 11 | import org.jscsi.target.settings.entry.Entry; | |
| 12 | import org.jscsi.target.settings.entry.NumericalEntry; | |
| 13 | import org.jscsi.target.settings.entry.StringEntry; | |
| 14 | import org.jscsi.target.settings.entry.Use; | |
| 15 | import org.jscsi.target.util.BinaryLock; | |
| 16 | ||
| 17 | ||
| 18 | /** | |
| 19 | * A class for managing {@link Entry} objects responsible for negotiating session-wide parameters. | |
| 20 | * <p> | |
| 21 | * Each instance of this class belongs to one {@link TargetSession} object. | |
| 22 | * | |
| 23 | * @see SettingsNegotiator | |
| 24 | * @author Andreas Ergenzinger | |
| 25 | */ | |
| 26 | public final class SessionSettingsNegotiator extends SettingsNegotiator { | |
| 27 | ||
| 28 | /** | |
| 29 | * Prevents concurrent negotiations over multiple connections of the same session. | |
| 30 | * | |
| 31 | * @see #lock() | |
| 32 | * @see #unlock() | |
| 33 | */ | |
| 34 | 0 | private final BinaryLock lock = new BinaryLock(); |
| 35 | ||
| 36 | /** | |
| 37 | * A counter that provides a unique, ordered identifying value for {@link Settings} objects. | |
| 38 | */ | |
| 39 | 0 | private final AtomicLong currentSettingsId = new AtomicLong(); |
| 40 | ||
| 41 | /** | |
| 42 | * A current snapshot of all session-wide parameters. | |
| 43 | */ | |
| 44 | private SessionSettingsBuilderComponent sessionSettingBuilderComponent; | |
| 45 | ||
| 46 | /** | |
| 47 | * The {@link SessionSettingsNegotiator} constructor. | |
| 48 | */ | |
| 49 | public SessionSettingsNegotiator () { | |
| 50 | 0 | super();// initializes entries |
| 51 | 0 | updateSettingsBuilderComponent(); |
| 52 | 0 | } |
| 53 | ||
| 54 | /** | |
| 55 | * Blocks until the {@link #lock()} has been acquired and returns <code>true</code> or returns <code>false</code> if | |
| 56 | * the method returned prematurely due to an interrupt. | |
| 57 | * <p> | |
| 58 | * The usual safeguards with using {@link Lock} objects should be applied, namely using | |
| 59 | * <code>try ... catch ... finally ...</code> blocks to make sure the lock is always released. | |
| 60 | * | |
| 61 | * @return <code>true</code>if and only if the lock has been acquired | |
| 62 | */ | |
| 63 | boolean lock () { | |
| 64 | 0 | return lock.lock(); |
| 65 | } | |
| 66 | ||
| 67 | /** | |
| 68 | * Releases the {@link #lock} if called by the locking {@link Thread}. | |
| 69 | * | |
| 70 | * @see #lock() | |
| 71 | */ | |
| 72 | void unlock () { | |
| 73 | 0 | lock.unlock(); |
| 74 | 0 | } |
| 75 | ||
| 76 | /** | |
| 77 | * Returns the {@link Entry} responsible for negotiating the specified <i>key</i> identifying a session-wide | |
| 78 | * parameter, or <code>null</code> if no such {@link Entry} can be found. | |
| 79 | * | |
| 80 | * @param key identifies an {@link Entry} responsible for a session-wide parameter | |
| 81 | * @return the requested {@link Entry} or <code>null</code> | |
| 82 | */ | |
| 83 | Entry getEntry (final String key) { | |
| 84 | 0 | return getEntry(key, entries); |
| 85 | } | |
| 86 | ||
| 87 | long getCurrentSettingsId () { | |
| 88 | 0 | return currentSettingsId.get(); |
| 89 | } | |
| 90 | ||
| 91 | @Override | |
| 92 | protected void initializeEntries () { | |
| 93 | ||
| 94 | /* | |
| 95 | * No indicates that the data PDUs within a sequence can be in any order. Yes indicates that the data PDUs | |
| 96 | * within a sequence have to be at continuously increasing addresses and that overlays are forbidden. | |
| 97 | */ | |
| 98 | 0 | entries.add(new BooleanEntry(new KeySet(TextKeyword.DATA_PDU_IN_ORDER),// keySet |
| 99 | Use.LEADING_LOPNS,// use | |
| 100 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 101 | true,// negotiationValue | |
| 102 | BooleanResultFunction.OR,// resultFunction | |
| 103 | true));// defaultValue | |
| 104 | ||
| 105 | /* | |
| 106 | * If set to No, the data PDU sequence may be transferred in any order. If set to Yes, the sequence must be | |
| 107 | * transferred using continuously increasing offsets except for error recovery. | |
| 108 | */ | |
| 109 | 0 | entries.add(new BooleanEntry(new KeySet(TextKeyword.DATA_SEQUENCE_IN_ORDER),// keySet |
| 110 | Use.LEADING_LOPNS,// use | |
| 111 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 112 | true,// negotiationValue | |
| 113 | BooleanResultFunction.OR,// resultFunction | |
| 114 | true));// defaultValue | |
| 115 | ||
| 116 | /* | |
| 117 | * Max seconds that connection and task allegiance reinstatement is still possible following a connection | |
| 118 | * termination or reset. A value of zero means that no reinstatement is possible. | |
| 119 | */ | |
| 120 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.DEFAULT_TIME_2_RETAIN, TextKeyword.TIME_2_RETAIN),// keySet |
| 121 | NegotiationType.NEGOTIATED,// negotiationType | |
| 122 | Use.LEADING_LOPNS,// use | |
| 123 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 124 | 0,// negotiationValue | |
| 125 | NumericalValueRange.create(0, 3600),// protocolValueRange | |
| 126 | NumericalResultFunction.MIN,// resultFunction | |
| 127 | 20,// defaultValue | |
| 128 | false));// zeroMeansDontCare | |
| 129 | ||
| 130 | /* | |
| 131 | * Min seconds to wait before attempting connection and task allegiance reinstatement after a connection | |
| 132 | * termination or a connection reset. A value of zero means that task reassignment can be done immediately. | |
| 133 | */ | |
| 134 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.DEFAULT_TIME_2_WAIT, TextKeyword.TIME_2_WAIT),// keySet |
| 135 | NegotiationType.NEGOTIATED,// negotiationType | |
| 136 | Use.LEADING_LOPNS,// use | |
| 137 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 138 | 2,// negotiationValue | |
| 139 | NumericalValueRange.create(0, 3600),// protocolValueRange | |
| 140 | NumericalResultFunction.MAX,// resultFunction | |
| 141 | 2,// defaultValue | |
| 142 | false));// zeroMeansDontCare | |
| 143 | ||
| 144 | /* | |
| 145 | * Recovery levels represent a combination of recovery capabilities. Each level includes all the capabilities of | |
| 146 | * the lower recovery level. | |
| 147 | */ | |
| 148 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.ERROR_RECOVERY_LEVEL),// keySet |
| 149 | NegotiationType.NEGOTIATED,// negotiationType | |
| 150 | Use.LEADING_LOPNS,// use | |
| 151 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 152 | 0,// negotiationValue | |
| 153 | NumericalValueRange.create(0, 2),// protocolValueRange | |
| 154 | NumericalResultFunction.MIN,// resultFunction | |
| 155 | 0,// defaultValue | |
| 156 | false));// zeroMeansDontCare | |
| 157 | ||
| 158 | /* | |
| 159 | * Maximum SCSI payload, in bytes, of unsolicited data an initiator may send to the target. Includes immediate | |
| 160 | * data and a sequence of unsolicited Data-Out PDUs. Zero (don't care) can be used. Must be <= MaxBurstLength. | |
| 161 | */ | |
| 162 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.FIRST_BURST_LENGTH),// keySet |
| 163 | NegotiationType.NEGOTIATED,// negotiationType | |
| 164 | Use.LEADING_LOPNS,// use | |
| 165 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 166 | 65536,// negotiationValue (default value) | |
| 167 | NumericalValueRange.create(512, 16777215),// protocolValueRange | |
| 168 | // 512 to 2^24 - 1 | |
| 169 | NumericalResultFunction.MIN,// resultFunction | |
| 170 | 65536,// defaultValue, 64K | |
| 171 | true));// zeroMeansDontCare | |
| 172 | ||
| 173 | /* | |
| 174 | * Either the initiator or target can turn off ImmediateData. | |
| 175 | */ | |
| 176 | 0 | entries.add(new BooleanEntry(new KeySet(TextKeyword.IMMEDIATE_DATA),// keySet |
| 177 | Use.LEADING_LOPNS,// use | |
| 178 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 179 | true,// negotiationValue | |
| 180 | BooleanResultFunction.AND,// resultFunction | |
| 181 | true));// defaultValue | |
| 182 | ||
| 183 | /* | |
| 184 | * Turns off the default use of R2T; allows an initiator to start sending data to a target as if it had received | |
| 185 | * an initial R2T. | |
| 186 | */ | |
| 187 | 0 | entries.add(new BooleanEntry(new KeySet(TextKeyword.INITIAL_R_2_T),// keySet |
| 188 | Use.LEADING_LOPNS,// use | |
| 189 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 190 | true,// negotiationValue | |
| 191 | BooleanResultFunction.OR,// resultFunction | |
| 192 | true));// defaultValue | |
| 193 | ||
| 194 | /* | |
| 195 | * Sent to the target in the login PDU. | |
| 196 | */ | |
| 197 | 0 | entries.add(new StringEntry(new KeySet(TextKeyword.INITIATOR_ALIAS),// keySet |
| 198 | NegotiationType.DECLARED,// negotiationType | |
| 199 | Use.INITIAL_AND_FFP,// use | |
| 200 | NegotiationStatus.NOT_NEGOTIATED,// negotiationStatus | |
| 201 | null,// supportedValues, anything goes | |
| 202 | null));// defaultValue | |
| 203 | ||
| 204 | /* | |
| 205 | * Must be sent on first login request per connection. | |
| 206 | */ | |
| 207 | 0 | entries.add(new StringEntry(new KeySet(TextKeyword.INITIATOR_NAME),// keySet |
| 208 | NegotiationType.DECLARED,// negotiationType | |
| 209 | Use.INITIAL,// use | |
| 210 | NegotiationStatus.NOT_NEGOTIATED,// negotiationStatus | |
| 211 | null,// supportedValues, anything goes | |
| 212 | null));// defaultValue | |
| 213 | ||
| 214 | /* | |
| 215 | * Maximum SCSI data payload in bytes for data-in or for a solicited data-out sequence. Zero (don't care) can be | |
| 216 | * used. | |
| 217 | */ | |
| 218 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_BURST_LENGTH),// keySet |
| 219 | NegotiationType.NEGOTIATED,// negotiationType | |
| 220 | Use.LEADING_LOPNS,// use | |
| 221 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 222 | 262144,// negotiationValue (default value) | |
| 223 | NumericalValueRange.create(512, 16777215),// protocolValueRange | |
| 224 | // 512 to 2^24 - 1 | |
| 225 | NumericalResultFunction.MIN,// resultFunction | |
| 226 | 262144,// defaultValue, 256K | |
| 227 | true));// zeroMeansDontCare | |
| 228 | ||
| 229 | /* | |
| 230 | * The initiator and target negotiate the maximum number of connections that can be requested or are acceptable. | |
| 231 | */ | |
| 232 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_CONNECTIONS),// keySet |
| 233 | NegotiationType.NEGOTIATED,// negotiationType | |
| 234 | Use.LEADING_LOPNS,// use | |
| 235 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 236 | 1,// negotiationValue (default value) | |
| 237 | NumericalValueRange.create(1, 65535),// protocolValueRange | |
| 238 | NumericalResultFunction.MIN,// resultFunction | |
| 239 | 1,// defaultValue | |
| 240 | false));// zeroMeansDontCare | |
| 241 | ||
| 242 | /* | |
| 243 | * The maximum number of outstanding R2Ts. | |
| 244 | */ | |
| 245 | 0 | entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_OUTSTANDING_R_2_T),// keySet |
| 246 | NegotiationType.NEGOTIATED,// negotiationType | |
| 247 | Use.LEADING_LOPNS,// use | |
| 248 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 249 | 1,// negotiationValue (default value) | |
| 250 | NumericalValueRange.create(1, 65535),// protocolValueRange | |
| 251 | NumericalResultFunction.MIN,// resultFunction | |
| 252 | 1,// defaultValue | |
| 253 | false));// zeroMeansDontCare | |
| 254 | ||
| 255 | /* | |
| 256 | * The session type (Normal or Discovery). | |
| 257 | */ | |
| 258 | 0 | entries.add(new StringEntry(new KeySet(TextKeyword.SESSION_TYPE),// keySet |
| 259 | NegotiationType.DECLARED,// negotiationType | |
| 260 | Use.INITIAL,// use | |
| 261 | NegotiationStatus.DEFAULT,// negotiationStatus | |
| 262 | null,// supportedValues, no checking | |
| 263 | TextKeyword.NORMAL));// defaultValue | |
| 264 | 0 | } |
| 265 | ||
| 266 | /** | |
| 267 | * Updates {@link #sessionSettingBuilderComponent} with the currently valid parameters retrieved from the elements | |
| 268 | * of {@link SettingsNegotiator#entries}. | |
| 269 | */ | |
| 270 | protected void updateSettingsBuilderComponent () { | |
| 271 | ||
| 272 | 0 | sessionSettingBuilderComponent = new SessionSettingsBuilderComponent(currentSettingsId.getAndIncrement() + 1,// settingsId |
| 273 | entries);// entries with current/new values | |
| 274 | 0 | } |
| 275 | ||
| 276 | SessionSettingsBuilderComponent getSessionSettingsBuilderComponent () { | |
| 277 | 0 | return sessionSettingBuilderComponent; |
| 278 | } | |
| 279 | ||
| 280 | @Override | |
| 281 | public boolean checkConstraints () { | |
| 282 | try { | |
| 283 | // ensure FirstBurstLength <= MaxBurstLength | |
| 284 | 0 | final int firstBurstLength = getEntry(TextKeyword.FIRST_BURST_LENGTH).getIntegerValue(); |
| 285 | 0 | final int maxBurstLength = getEntry(TextKeyword.MAX_BURST_LENGTH).getIntegerValue(); |
| 286 | 0 | if (maxBurstLength > firstBurstLength) return false; |
| 287 | 0 | } catch (OperationNotSupportedException e) { |
| 288 | // programmer error, requested wrong data type | |
| 289 | 0 | e.printStackTrace(); |
| 290 | 0 | return false; |
| 291 | 0 | } |
| 292 | 0 | return true; |
| 293 | } | |
| 294 | } |