package org.jscsi.target.settings;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import javax.naming.OperationNotSupportedException;
import org.jscsi.target.connection.TargetSession;
import org.jscsi.target.settings.entry.BooleanEntry;
import org.jscsi.target.settings.entry.Entry;
import org.jscsi.target.settings.entry.NumericalEntry;
import org.jscsi.target.settings.entry.StringEntry;
import org.jscsi.target.settings.entry.Use;
import org.jscsi.target.util.BinaryLock;
/**
* A class for managing {@link Entry} objects responsible for negotiating
* session-wide parameters.
* <p>
* Each instance of this class belongs to one {@link TargetSession} object.
*
* @see SettingsNegotiator
* @author Andreas Ergenzinger
*/
public final class SessionSettingsNegotiator extends SettingsNegotiator {
/**
* Prevents concurrent negotiations over multiple connections of the same
* session.
*
* @see #lock()
* @see #unlock()
*/
private final BinaryLock lock = new BinaryLock();
/**
* A counter that provides a unique, ordered identifying value for {@link Settings} objects.
*/
private final AtomicLong currentSettingsId = new AtomicLong();
/**
* A current snapshot of all session-wide parameters.
*/
private SessionSettingsBuilderComponent sessionSettingBuilderComponent;
/**
* The {@link SessionSettingsNegotiator} constructor.
*/
public SessionSettingsNegotiator() {
super();// initializes entries
updateSettingsBuilderComponent();
}
/**
* Blocks until the {@link #lock()} has been acquired and returns <code>true</code> or returns
* <code>false</code> if the method returned
* prematurely due to an interrupt.
* <p>
* The usual safeguards with using {@link Lock} objects should be applied, namely using
* <code>try ... catch ... finally ...</code> blocks to make sure the lock is always released.
*
* @return <code>true</code>if and only if the lock has been acquired
*/
boolean lock() {
return lock.lock();
}
/**
* Releases the {@link #lock} if called by the locking {@link Thread}.
*
* @see #lock()
*/
void unlock() {
lock.unlock();
}
/**
* Returns the {@link Entry} responsible for negotiating the specified
* <i>key</i> identifying a session-wide parameter, or <code>null</code> if
* no such {@link Entry} can be found.
*
* @param key
* identifies an {@link Entry} responsible for a session-wide
* parameter
* @return the requested {@link Entry} or <code>null</code>
*/
Entry getEntry(final String key) {
return getEntry(key, entries);
}
long getCurrentSettingsId() {
return currentSettingsId.get();
}
@Override
protected void initializeEntries() {
/*
* No indicates that the data PDUs within a sequence can be in any
* order. Yes indicates that the data PDUs within a sequence have to be
* at continuously increasing addresses and that overlays are forbidden.
*/
entries.add(new BooleanEntry(new KeySet(TextKeyword.DATA_PDU_IN_ORDER),// keySet
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
true,// negotiationValue
BooleanResultFunction.OR,// resultFunction
true));// defaultValue
/*
* If set to No, the data PDU sequence may be transferred in any order.
* If set to Yes, the sequence must be transferred using continuously
* increasing offsets except for error recovery.
*/
entries.add(new BooleanEntry(new KeySet(TextKeyword.DATA_SEQUENCE_IN_ORDER),// keySet
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
true,// negotiationValue
BooleanResultFunction.OR,// resultFunction
true));// defaultValue
/*
* Max seconds that connection and task allegiance reinstatement is
* still possible following a connection termination or reset. A value
* of zero means that no reinstatement is possible.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.DEFAULT_TIME_2_RETAIN,
TextKeyword.TIME_2_RETAIN),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
0,// negotiationValue
NumericalValueRange.create(0, 3600),// protocolValueRange
NumericalResultFunction.MIN,// resultFunction
20,// defaultValue
false));// zeroMeansDontCare
/*
* Min seconds to wait before attempting connection and task allegiance
* reinstatement after a connection termination or a connection reset. A
* value of zero means that task reassignment can be done immediately.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.DEFAULT_TIME_2_WAIT, TextKeyword.TIME_2_WAIT),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
2,// negotiationValue
NumericalValueRange.create(0, 3600),// protocolValueRange
NumericalResultFunction.MAX,// resultFunction
2,// defaultValue
false));// zeroMeansDontCare
/*
* Recovery levels represent a combination of recovery capabilities.
* Each level includes all the capabilities of the lower recovery level.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.ERROR_RECOVERY_LEVEL),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
0,// negotiationValue
NumericalValueRange.create(0, 2),// protocolValueRange
NumericalResultFunction.MIN,// resultFunction
0,// defaultValue
false));// zeroMeansDontCare
/*
* Maximum SCSI payload, in bytes, of unsolicited data an initiator may
* send to the target. Includes immediate data and a sequence of
* unsolicited Data-Out PDUs. Zero (don't care) can be used. Must be <=
* MaxBurstLength.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.FIRST_BURST_LENGTH),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
65536,// negotiationValue (default value)
NumericalValueRange.create(512, 16777215),// protocolValueRange
// 512 to 2^24 - 1
NumericalResultFunction.MIN,// resultFunction
65536,// defaultValue, 64K
true));// zeroMeansDontCare
/*
* Either the initiator or target can turn off ImmediateData.
*/
entries.add(new BooleanEntry(new KeySet(TextKeyword.IMMEDIATE_DATA),// keySet
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
true,// negotiationValue
BooleanResultFunction.AND,// resultFunction
true));// defaultValue
/*
* Turns off the default use of R2T; allows an initiator to start
* sending data to a target as if it had received an initial R2T.
*/
entries.add(new BooleanEntry(new KeySet(TextKeyword.INITIAL_R_2_T),// keySet
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
true,// negotiationValue
BooleanResultFunction.OR,// resultFunction
true));// defaultValue
/*
* Sent to the target in the login PDU.
*/
entries.add(new StringEntry(new KeySet(TextKeyword.INITIATOR_ALIAS),// keySet
NegotiationType.DECLARED,// negotiationType
Use.INITIAL_AND_FFP,// use
NegotiationStatus.NOT_NEGOTIATED,// negotiationStatus
null,// supportedValues, anything goes
null));// defaultValue
/*
* Must be sent on first login request per connection.
*/
entries.add(new StringEntry(new KeySet(TextKeyword.INITIATOR_NAME),// keySet
NegotiationType.DECLARED,// negotiationType
Use.INITIAL,// use
NegotiationStatus.NOT_NEGOTIATED,// negotiationStatus
null,// supportedValues, anything goes
null));// defaultValue
/*
* Maximum SCSI data payload in bytes for data-in or for a solicited
* data-out sequence. Zero (don't care) can be used.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_BURST_LENGTH),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
262144,// negotiationValue (default value)
NumericalValueRange.create(512, 16777215),// protocolValueRange
// 512 to 2^24 - 1
NumericalResultFunction.MIN,// resultFunction
262144,// defaultValue, 256K
true));// zeroMeansDontCare
/*
* The initiator and target negotiate the maximum number of connections
* that can be requested or are acceptable.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_CONNECTIONS),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
1,// negotiationValue (default value)
NumericalValueRange.create(1, 65535),// protocolValueRange
NumericalResultFunction.MIN,// resultFunction
1,// defaultValue
false));// zeroMeansDontCare
/*
* The maximum number of outstanding R2Ts.
*/
entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_OUTSTANDING_R_2_T),// keySet
NegotiationType.NEGOTIATED,// negotiationType
Use.LEADING_LOPNS,// use
NegotiationStatus.DEFAULT,// negotiationStatus
1,// negotiationValue (default value)
NumericalValueRange.create(1, 65535),// protocolValueRange
NumericalResultFunction.MIN,// resultFunction
1,// defaultValue
false));// zeroMeansDontCare
/*
* The session type (Normal or Discovery).
*/
entries.add(new StringEntry(new KeySet(TextKeyword.SESSION_TYPE),// keySet
NegotiationType.DECLARED,// negotiationType
Use.INITIAL,// use
NegotiationStatus.DEFAULT,// negotiationStatus
null,// supportedValues, no checking
TextKeyword.NORMAL));// defaultValue
}
/**
* Updates {@link #sessionSettingBuilderComponent} with the currently valid
* parameters retrieved from the elements of {@link SettingsNegotiator#entries}.
*/
protected void updateSettingsBuilderComponent() {
sessionSettingBuilderComponent =
new SessionSettingsBuilderComponent(currentSettingsId.getAndIncrement() + 1,// settingsId
entries);// entries with current/new values
}
SessionSettingsBuilderComponent getSessionSettingsBuilderComponent() {
return sessionSettingBuilderComponent;
}
@Override
public boolean checkConstraints() {
try {
// ensure FirstBurstLength <= MaxBurstLength
final int firstBurstLength = getEntry(TextKeyword.FIRST_BURST_LENGTH).getIntegerValue();
final int maxBurstLength = getEntry(TextKeyword.MAX_BURST_LENGTH).getIntegerValue();
if (maxBurstLength > firstBurstLength)
return false;
} catch (OperationNotSupportedException e) {
// programmer error, requested wrong data type
e.printStackTrace();
return false;
}
return true;
}
}