Package org.jscsi.target.settings

Source Code of org.jscsi.target.settings.ConnectionSettingsNegotiator

package org.jscsi.target.settings;


import java.util.List;
import java.util.Vector;

import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.login.LoginStage;
import org.jscsi.target.TargetServer;
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.NumericalRangeEntry;
import org.jscsi.target.settings.entry.StringEntry;
import org.jscsi.target.settings.entry.Use;


/**
* In addition to offering all methods of their {@link SettingsNegotiator} parent class,
* {@link ConnectionSettingsNegotiator} instances provide all methods necessary for text parameter negotiation and for
* creating {@link Settings} objects, which allow access to the current parameters.
* <p>
* Each instance of this class belongs to one <code>Connection</code>, for which it manages all connection-specific
* parameters. A similar association exists between the connection's enclosing {@link TargetSession} and the
* {@link #sessionSettingsNegotiator} member variable.
* <p>
* A negotiation sequence consists of the following steps, performed in the given order:
* <p>
* <ol>
* <li>Call {@link #beginNegotiation()} until it returns <code>true</code>.</li>
* <li>One or, if the initiator is using multiple PDU sequences, multiple calls of
* {@link #negotiate(TargetServer, LoginStage, boolean, boolean, List, List)}. The method will return <code>false</code>
* if there was a problem.</li>
* <li>Call {@link #checkConstraints()} to check more complex requirements. The method will return <code>false</code> if
* there was a problem.</li>
* <li>Call {@link #finishNegotiation(boolean)} with the appropriate parameter. This step is mandatory.</li>
* </ol>
*
* @author Andreas Ergenzinger, University of Konstanz
*/
public final class ConnectionSettingsNegotiator extends SettingsNegotiator {

    /**
     * The {@link SessionSettingsNegotiator} maintaining all {@link Entry} objects for session-wide parameters.
     */
    private final SessionSettingsNegotiator sessionSettingsNegotiator;

    /**
     * A current snapshot of all connection-specific parameters.
     */
    private ConnectionSettingsBuilderComponent connectionSettingBuilderComponent;

    /**
     * A current snapshot of all parameters, both connection-specific and session-wide.
     */
    private Settings settings;

    /**
     * The {@link ConnectionSettingsNegotiator} constructor.
     *
     * @param sessionSettingsNegotiator the {@link SessionSettingsNegotiator} maintaining all {@link Entry} objects for
     *            session-wide parameters
     */
    public ConnectionSettingsNegotiator (final SessionSettingsNegotiator sessionSettingsNegotiator) {
        super();// initializes entries
        this.sessionSettingsNegotiator = sessionSettingsNegotiator;
        // initialize settings
        updateSettingsBuilderComponent();
        updateSettings();
    }

    /**
     * This method must be called at the beginning of a parameter negotiation sequence.
     * <p>
     * This method will block until no other {@link ConnectionSettingsNegotiator} is negotiating and will return
     * <code>true</code> if negotiations may proceed, or <code>false</code>, if the request for the permission to
     * negotiate was denied.
     *
     * @return <code>true</code> if and only if the caller is allowed to negotiate parameters
     * @see #finishNegotiation(boolean)
     */
    public boolean beginNegotiation () {
        final boolean hasLock = sessionSettingsNegotiator.lock();
        if (hasLock) {
            // back up entries with connection and with session scope
            backUpEntries();
            sessionSettingsNegotiator.backUpEntries();
        }
        return hasLock;
    }

    /**
     * This method must be called at the end of a negotiation sequence. If there was a problem during negotiations, all
     * changes to the managed {@link Entry} objects must be discarded by setting the <i>commitChanges</i> parameter to
     * <code>false</code>. If it is <code>true</code>, then the parameter changes will be incorporated into the updated
     * {@link #settings}.
     * <p>
     * Calling this method should be ensured by a <code>try ... catch ...
     * finally</code> block.
     *
     * @param commitChanges <code>true</code> if and only if the negotiated parameter changes are to be committed
     */
    public void finishNegotiation (final boolean commitChanges) {
        // commit or roll back connection-specific entries
        commitOrRollBackChanges(commitChanges);
        // commit or roll back session-wide entries
        sessionSettingsNegotiator.commitOrRollBackChanges(commitChanges);
        // update settings
        updateSettingsBuilderComponent();
        sessionSettingsNegotiator.updateSettingsBuilderComponent();
        updateSettings();
        // allow other Threads to negotiate
        sessionSettingsNegotiator.unlock();
    }

    /**
     * Processes one or more <i>key-value</i> pairs sent by the initiator, formulates response <i>key-value</i> pairs
     * and changes the involved {@link Entry} instances accordingly.
     *
     * @param loginStage specifies the current stage or phase
     * @param leadingConnection <code>true</code> if and only if the connection is the first connection of the enclosing
     *            session
     * @param initialPdu <code>true</code> if and only if the <i>key-value</i> pairs were sent in the first
     *            {@link ProtocolDataUnit} received on this connection
     * @param requestKeyValuePairs contains the <i>key-value</i> pairs from the initiator; processed elements will be
     *            removed
     * @param responseKeyValuePairs will contain the <i>key-value</i> pairs from the jSCSI target
     * @return <code>true</code> if everything went fine and <code>false</code> if there was an irreconcilable problem
     */
    public boolean negotiate (TargetServer target, final LoginStage loginStage, final boolean leadingConnection, final boolean initialPdu, final List<String> requestKeyValuePairs, final List<String> responseKeyValuePairs) {

        // split up key=value pairs from requester
        final List<String> keys = new Vector<String>();
        final List<String> values = new Vector<String>();
        for (String keyValuePair : requestKeyValuePairs) {
            final String[] split = TextParameter.splitKeyValuePair(keyValuePair);
            if (split == null) {
                System.err.println("malformatted key=value pair: " + keyValuePair);
                return false;
            }
            keys.add(split[0]);
            values.add(split[1]);
        }

        // get respective entry (declared by initiator, or negotiated) and
        // let it process the key=value pair
        Entry entry;
        boolean everythingOkay = true;
        while (keys.size() > 0) {
            entry = getEntry(keys.get(0));
            // respond to unknown keys
            if (entry == null) {
                responseKeyValuePairs.add(TextParameter.toKeyValuePair(keys.get(0), TextKeyword.NOT_UNDERSTOOD));
            } else {// appropriate entry was found
                // process key=value pair and remember if there is any trouble
                everythingOkay &= entry.negotiate(target, loginStage, leadingConnection, initialPdu, keys.get(0), values.get(0), responseKeyValuePairs);
            }
            // remove processed key and values
            keys.remove(0);
            values.remove(0);
        }

        // check if initiator has sent all mandatory parameters
        // and append target declarations
        if (initialPdu) {
            // initiator must provide the InitiatorName in the first Login PDU
            // (InitiatorAlias is optional)
            everythingOkay &= getEntry(TextKeyword.INITIATOR_NAME).checkAccepted();

            // in a normal session the initiator must declare TargetName
            // the target must reply with TargetPortalGroupTag and should
            // append TargetAlias
            boolean normalSession = TextKeyword.NORMAL.equals(((StringEntry) getEntry(TextKeyword.SESSION_TYPE)).getStringValue());
            if (normalSession) {
                // check if proposed TargetName is correct
                final StringEntry targetNameEntry = (StringEntry) getEntry(TextKeyword.TARGET_NAME);
                final String targetName = targetNameEntry.getStringValue();
                if (targetName == null || // not declared
                !target.isValidTargetName(targetName)) // wrong name
                everythingOkay = false;

                // send TargetAlias
                String targetAlias = null;
                if (everythingOkay) targetAlias = target.getTarget(targetName).getTargetAlias();// might
                // be
                // undefined
                if (targetAlias != null) {
                    responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.TARGET_ALIAS, targetAlias));
                }

                // send TargetPortalGroupTag
                responseKeyValuePairs.add(TextParameter.toKeyValuePair(TextKeyword.TARGET_PORTAL_GROUP_TAG, Integer.valueOf(target.getConfig().getTargetPortalGroupTag()).toString()));
            }
        }

        return everythingOkay;
    }

    @Override
    public boolean checkConstraints () {
        return sessionSettingsNegotiator.checkConstraints();
        /*
         * in multi-connection session, the InitiatorName of follow-up connections should be checked against the
         * InitiatorName of the leading connection
         */
    }

    /**
     * Returns the {@link Entry} responsible for negotiating the specified <i>key</i> identifying either a session-wide
     * or connection-specific parameter, or <code>null</code> if no such {@link Entry} can be found.
     *
     * @param key identifies an {@link Entry}
     * @return the requested {@link Entry} or <code>null</code>
     */
    private Entry getEntry (final String key) {
        Entry entry = null;
        // check connection-only entries
        entry = getEntry(key, entries);
        if (entry == null) // keep looking in session-wide entries
        entry = sessionSettingsNegotiator.getEntry(key);
        return entry;
    }

    /**
     * This method checks if any parameter changes have been committed since the last call and, if so, creates a new
     * {@link Settings} object reflecting the current parameters.
     *
     * @return The current parameters
     */
    public Settings getSettings () {
        // check if settings are up to date
        if (sessionSettingsNegotiator.getCurrentSettingsId() > settings.getSettingsId()) updateSettings();
        return settings;
    }

    /**
     * Updates {@link #settings} by replacing it with a more current copy of the managed parameters.
     */
    private synchronized void updateSettings () {
        settings = new Settings(connectionSettingBuilderComponent, sessionSettingsNegotiator.getSessionSettingsBuilderComponent());
    }

    @Override
    protected void initializeEntries () {

        /*
         * Determines type and use of the data digest.
         */
        entries.add(new StringEntry(new KeySet(TextKeyword.DATA_DIGEST),// keySet
        NegotiationType.NEGOTIATED,// negotiationType
        Use.LOPNS,// use
        NegotiationStatus.DEFAULT,// negotiationStatus
        new String[] { TextKeyword.NONE },// supportedValues,
        TextKeyword.NONE));// defaultValue

        /*
         * Determines type and use of the header digest.
         */
        entries.add(new StringEntry(new KeySet(TextKeyword.HEADER_DIGEST),// keySet
        NegotiationType.NEGOTIATED,// negotiationType
        Use.LOPNS,// use
        NegotiationStatus.DEFAULT,// negotiationStatus
        new String[] { TextKeyword.NONE },// supportedValues,
        TextKeyword.NONE));// defaultValue

        /*
         * Turns the target-to-initiator markers on or off.
         */
        entries.add(new BooleanEntry(new KeySet(TextKeyword.IF_MARKER),// keySet
        Use.LOPNS,// use
        NegotiationStatus.DEFAULT,// negotiationStatus
        false,// negotiationValue
        BooleanResultFunction.AND,// resultFunction
        false));// defaultValue

        /*
         * Interval value (in 4-byte words) for target-to-initiator markers. The interval is measured from the end of
         * one marker to the beginning of the next one. The offer can have only a range; the response can have only a
         * single value (picked from the offered range) or Reject. Will always be Irrelevant.
         */
        entries.add(new NumericalRangeEntry(new KeySet(TextKeyword.IF_MARK_INT),// keySet
        Use.LOPNS,// use
        NegotiationStatus.IRRELEVANT,// negotiationStatus
        2048,// negotiationValue
        NumericalValueRange.create(1, 65535),// protocolValueRange
        2048));// defaultValue

        /*
         * The maximum amount of data that either the initiator or the target can receive in any iSCSI PDU. Zero (don't
         * care) can be used. I&T can specify (declare) the Max they can receive. This is a connection- and direction-
         * specific parameter. The actual value used by the target will be min(This value, MaxBurstLength) for data-in
         * and solicited data-out data. Min(This value, FirstBurstLength) for unsolicited data.
         */
        entries.add(new NumericalEntry(new KeySet(TextKeyword.MAX_RECV_DATA_SEGMENT_LENGTH),// keySet
        NegotiationType.DECLARED,// negotiationType
        Use.LOPNS_AND_FFP,// use
        NegotiationStatus.DEFAULT,// negotiationStatus
        8192,// negotiationValue (default value, will be used if I sends
             // 0)
        NumericalValueRange.create(512, 16777215),// protocolValueRange
        // 512 to 2^24 - 1
        NumericalResultFunction.MIN,// resultFunction
        8192,// defaultValue, 8K
        true));// zeroMeansDontCare

        /*
         * Turns the initiator-to-target markers on or off.
         */
        entries.add(new BooleanEntry(new KeySet(TextKeyword.OF_MARKER),// keySet
        Use.LOPNS,// use
        NegotiationStatus.DEFAULT,// negotiationStatus
        false,// negotiationValue
        BooleanResultFunction.AND,// resultFunction
        false));// defaultValue

        /*
         * Interval value (in 4-byte words) for initiator-to-target markers. The interval is measured from the end of
         * one marker to the beginning of the next one. The offer can have only a range; the response can have only a
         * single value (picked from the offered range) or Reject. Will always be Irrelevant.
         */
        entries.add(new NumericalRangeEntry(new KeySet(TextKeyword.OF_MARK_INT),// keySet
        Use.LOPNS,// use
        NegotiationStatus.IRRELEVANT,// negotiationStatus
        2048,// negotiationValue
        NumericalValueRange.create(1, 65535),// protocolValueRange
        2048));// defaultValue

        /*
         * This entry is not used for Settings initialization, TargetName has a session-wide scope. This entry
         * intercepts the TargetName parameter the initiator has to declare at the beginning of normal sessions.
         */
        entries.add(new StringEntry(new KeySet(TextKeyword.TARGET_NAME),// keySet
        NegotiationType.DECLARED,// negotiationType
        Use.INITIAL,// use
        NegotiationStatus.NOT_NEGOTIATED,// negotiationStatus
        null,// supportedValues, anything goes
        null));// defaultValue
    }

    /**
     * Updates {@link ConnectionSettingsBuilderComponent} with the currently valid parameters retrieved from the
     * elements of {@link SettingsNegotiator#entries}.
     */
    protected void updateSettingsBuilderComponent () {
        connectionSettingBuilderComponent = new ConnectionSettingsBuilderComponent(entries);
    }
}
TOP

Related Classes of org.jscsi.target.settings.ConnectionSettingsNegotiator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.