/**
* Copyright (c) 2012, University of Konstanz, Distributed Systems Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the University of Konstanz nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jscsi.parser.asynchronous;
import org.jscsi.exception.InternetSCSIException;
import org.jscsi.parser.Constants;
import org.jscsi.parser.ProtocolDataUnit;
import org.jscsi.parser.TargetMessageParser;
import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat;
import org.jscsi.utils.Utils;
import com.carrotsearch.hppc.ByteObjectOpenHashMap;
/**
* <h1>AsynchronousMessageParser</h1>
* <p>
* Parser for the Asynchronous Message of the iSCSI Protocol (RFC3720).
*
* @author Volker Wildi
*/
public final class AsynchronousMessageParser extends TargetMessageParser {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Enumeration of all valid asynchronous message event codes.
* <table * border="1">
* <tr>
* <th>Event Code</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>0</td>
* <td>A SCSI Asynchronous Event is reported in the sense data. Sense Data that accompanies the report, in
* the data segment, identifies the condition. The sending of a SCSI Event (Asynchronous Event Reporting
* in SCSI terminology) is dependent on the target support for SCSI asynchronous event reporting (see
* [SAM2]) as indicated in the standard INQUIRY data (see [SPC3]). Its use may be enabled by parameters in
* the SCSI Control mode page (see [SPC3]).</td>
* </tr>
* <tr>
* <td>1</td>
* <td>Target requests Logout. This Async Message MUST be sent on the same connection as the one
* requesting to be logged out. The initiator MUST honor this request by issuing a Logout as early as
* possible, but no later than Parameter3 seconds. Initiator MUST send a Logout with a reason code of
* "Close the connection" OR "Close the session" to close all the connections. Once this message is
* received, the initiator SHOULD NOT issue new iSCSI commands on the connection to be logged out. The
* target MAY reject any new I/O requests that it receives after this Message with the reason code
* "Waiting for Logout". If the initiator does not Logout in Parameter3 seconds, the target should send an
* Async PDU with iSCSI event code "Dropped the connection" if possible, or simply terminate the transport
* connection. Parameter1 and Parameter2 are reserved.</td>
* </tr>
* <tr>
* <td>2</td>
* <td>Target indicates it will drop the connection. The Parameter1 field indicates the CID of the
* connection that is going to be dropped. The Parameter2 field (Time2Wait) indicates, in seconds, the
* minimum time to wait before attempting to reconnect or reassign. The Parameter3 field (Time2Retain)
* indicates the maximum time allowed to reassign commands after the initial wait (in Parameter2). If the
* initiator does not attempt to reconnect and/or reassign the outstanding commands within the time
* specified by Parameter3, or if Parameter3 is 0, the target will terminate all outstanding commands on
* this connection. In this case, no other responses should be expected from the target for the
* outstanding commands on this connection. A value of 0 for Parameter2 indicates that reconnect can be
* attempted immediately.</td>
* </tr>
* <tr>
* <td>3</td>
* <td>Target indicates it will drop all the connections of this session. Parameter1 field is reserved.
* The Parameter2 field (Time2Wait) indicates, in seconds, the minimum time to wait before attempting to
* reconnect. The Parameter3 field (Time2Retain) indicates the maximum time allowed to reassign commands
* after the initial wait (in Parameter2). If the initiator does not attempt to reconnect and/or reassign
* the outstanding commands within the time specified by Parameter3, or if Parameter3 is 0, the session is
* terminated. In this case, the target will terminate all outstanding commands in this session; no other
* responses should be expected from the target for the outstanding commands in this session. A value of 0
* for Parameter2 indicates that reconnect can be attempted immediately.</td>
* </tr>
* <tr>
* <td>4</td>
* <td>Target requests parameter negotiation on this connection. The initiator MUST honor this request by
* issuing a Text Request (that can be empty) on the same connection as early as possible, but no later
* than Parameter3 seconds, unless a Text Request is already pending on the connection, or by issuing a
* Logout Request. If the initiator does not issue a Text Request the target may reissue the Asynchronous
* Message requesting parameter negotiation.</td>
* </tr>
* <tr>
* <td>255</td>
* <td>Vendor specific iSCSI Event. The AsyncVCode details the vendor code, and data MAY accompany the
* report.</td>
* </tr>
* </table>
* <br/>
* All other event codes are reserved.
*/
public static enum AsyncEventCodes {
/** A SCSI Asynchronous Event is reported in the sense data. */
SCSI_ASYNCHRONOUS_EVENT((byte)0),
/** Target requests Logout. */
LOGOUT((byte)1),
/** Target indicates it will drop the connection. */
DROP_CONNECTION((byte)2),
/** Target indicates it will drop all the connections of this session. */
DROP_ALL_CONNECTIONS((byte)3),
/** Target requests parameter negotiation on this connection. */
PARAMETER_NEGOTIATION((byte)4),
/** Vendor specific iSCSI Event. */
VENDOR_SPECIFIC_EVENT((byte)255);
private final byte value;
private static ByteObjectOpenHashMap<AsyncEventCodes> mapping;
static {
AsyncEventCodes.mapping = new ByteObjectOpenHashMap<AsyncEventCodes>(values().length);
for (AsyncEventCodes s : values()) {
AsyncEventCodes.mapping.put(s.value, s);
}
}
private AsyncEventCodes(final byte newValue) {
value = newValue;
}
/**
* Returns the value of this enumeration.
*
* @return The value of this enumeration.
*/
public final byte value() {
return value;
}
/**
* Returns the constant defined for the given <code>value</code>.
*
* @param value
* The value to search for.
* @return The constant defined for the given <code>value</code>. Or <code>null</code>, if this value
* is not defined by this
* enumeration.
*/
public static final AsyncEventCodes valueOf(final byte value) {
return AsyncEventCodes.mapping.get(value);
}
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** The event code of this asynchronous message. */
private AsyncEventCodes asyncEvent;
/** The asynchronous vendor specific detail code. */
private byte asyncVCode;
/** The first parameter. */
private short parameter1;
/** The second parameter. */
private short parameter2;
/** The third parameter. */
private short parameter3;
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Default constructor, creates a new, empty <code>AsynchronousMessageParser</code> object.
*
* @param initProtocolDataUnit
* The reference <code>ProtocolDataUnit</code> instance, which
* contains this <code>AsynchronousMessageParser</code> subclass
* object.
*/
public AsynchronousMessageParser(final ProtocolDataUnit initProtocolDataUnit) {
super(initProtocolDataUnit);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public final String toString() {
final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE);
Utils.printField(sb, "LUN", logicalUnitNumber, 1);
sb.append(super.toString());
Utils.printField(sb, "AsyncEvent", asyncEvent.value(), 1);
Utils.printField(sb, "AsyncVCode", asyncVCode, 1);
Utils.printField(sb, "Parameter 1", parameter1, 1);
Utils.printField(sb, "Parameter 2", parameter2, 1);
Utils.printField(sb, "Parameter 3", parameter3, 1);
sb.append(super.toString());
return sb.toString();
}
/** {@inheritDoc} */
@Override
public final DataSegmentFormat getDataSegmentFormat() {
return DataSegmentFormat.BINARY;
}
/** {@inheritDoc} */
@Override
public final void clear() {
super.clear();
asyncEvent = null;
asyncVCode = 0x00;
parameter1 = 0x0000;
parameter2 = 0x0000;
parameter3 = 0x0000;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
* Returns the code of the iSCSI Asynchronous message.
* <p>
* <code>AsyncVCode</code> is a vendor specific detail code that is only valid if the
* <code>AsyncEvent</code> field indicates a vendor specific event. Otherwise, it is reserved.
*
* @return The Asynchronous vendor code.
* @see AsyncEventCodes
*/
public final byte getAsyncVCode() {
return asyncVCode;
}
/**
* Returns the asynchronous event code of this <code>AsynchronousMessageParser</code> object.
*
* @return The asynchronous event.
*/
public final AsyncEventCodes getAsyncEvent() {
return asyncEvent;
}
/**
* Returns the first parameter of this asynchronous message.
*
* @return The first parameter.
*/
public final short getParameter1() {
return parameter1;
}
/**
* Returns the second parameter of this asynchronous message.
*
* @return The second parameter.
*/
public final short getParameter2() {
return parameter2;
}
/**
* Returns the third parameter of this asynchronous message.
*
* @return The third parameter.
*/
public final short getParameter3() {
return parameter3;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void deserializeBytes36to39(final int line) throws InternetSCSIException {
asyncEvent = AsyncEventCodes.valueOf((byte)(line & Constants.FIRST_BYTE_MASK));
asyncVCode = (byte)(line & Constants.SECOND_BYTE_MASK);
parameter1 = (short)(line & Constants.LAST_TWO_BYTES_MASK);
}
/** {@inheritDoc} */
@Override
protected final void deserializeBytes40to43(final int line) throws InternetSCSIException {
parameter2 = (short)(line & Constants.FIRST_TWO_BYTES_MASK);
parameter3 = (short)(line & Constants.LAST_TWO_BYTES_MASK);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final void checkIntegrity() throws InternetSCSIException {
String exceptionMessage;
do {
if (asyncVCode != 0 && asyncEvent != AsyncEventCodes.VENDOR_SPECIFIC_EVENT) {
exceptionMessage =
"A vendor specific code is only valid, if also a vendor specific event occured.";
break;
}
if (asyncEvent == AsyncEventCodes.SCSI_ASYNCHRONOUS_EVENT && logicalUnitNumber == 0) {
exceptionMessage =
"A valid LogicalUnitNumber must be given, if an asynchronous event occured.";
break;
} else {
Utils.isReserved(logicalUnitNumber);
}
// message is checked correctly
return;
} while (false);
throw new InternetSCSIException(exceptionMessage);
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
protected final int serializeBytes36to39() {
int line = parameter1;
line |= asyncVCode << Constants.TWO_BYTES_SHIFT;
line |= asyncEvent.value() << Constants.THREE_BYTES_SHIFT;
return line;
}
/** {@inheritDoc} */
@Override
protected final int serializeBytes40to43() {
int line = parameter3;
line |= parameter2 << Constants.TWO_BYTES_SHIFT;
return line;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
}