/** * 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.datasegment; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.util.Map; import org.jscsi.exception.InternetSCSIException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <h1>OperationTextKeys</h1> * <p> * This class encaspulates all methods needed for the operation text keys, which can emerge in the data * segment of an iSCSI message (RFC3720). * * @author Volker Wildi */ final class TextParameterDataSegment extends AbstractDataSegment { // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** The Logger interface. */ private static final Logger LOGGER = LoggerFactory.getLogger(TextParameterDataSegment.class); /** All strings should be interpreted as this encoding. */ private static final String DEFAULT_TEXT_ENCODING = "UTF-8"; /** Delimiter between the key and the value of a key-value-pair. */ private static final String KEY_VALUE_DELIMITER = "="; /** Delimiter between two key-value-pairs. */ private static final String PAIR_DELIMITER = "\0"; /** * Each line consists of this number of tokens and has the following * structure: <key> = <value>. */ private static final int NUMBER_OF_TOKENS = 2; /** The index of the key in the array. */ private static final int KEY_INDEX = 0; /** The index of the value in the array. */ private static final int VALUE_INDEX = 1; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** This data structure contains all the key-value-pairs of this PDU. */ private final SettingsMap settings; /** Flag to indicate that the buffer and/or the setting map are out of sync. */ private boolean isDirty; // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Default constructor, to create a new, empty <code>TextParameterDataSegment</code> object with a maximum * length of <code>initMaximumLength</code> bytes. * * @param initChunkSize * The size (in bytes) of one chunk, which represents the <code>MaxRecvDataSegmentLength</code> * . */ public TextParameterDataSegment(final int initChunkSize) { super(initChunkSize); settings = new SettingsMap(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Add a given operation text keys with the given value to the key value * pairs. * * @param textKey * One of the valid operation text keys listed above. * @param value * The value of this operation text key. * @see de.unikn.inf.disy.blockdebix.iscsi.parser.datasegment.OperationalTextKey */ public final void add(final OperationalTextKey textKey, final String value) { final String s = textKey.value() + KEY_VALUE_DELIMITER + value + PAIR_DELIMITER; resizeBuffer(s.length(), true); dataBuffer.put(s.getBytes()); isDirty = true; } /** * Add all text parameters of the given <code>textKeys</code> map to this <code>ProtocolDataUnit</code> * object. * * @param textKeys * Map, which contains all the text parameters to insert. */ public final void addAll(final SettingsMap textKeys) { for (Map.Entry<OperationalTextKey, String> e : textKeys.entrySet()) { add(e.getKey(), e.getValue()); } isDirty = true; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** {@inheritDoc} */ public final int append(final ByteBuffer src, final int len) { if (len == 0) { return 0; } resizeBuffer(len, true); dataBuffer.put(src); isDirty = true; return dataBuffer.limit(); } /** {@inheritDoc} */ public final int deserialize(final ByteBuffer src, final int len) { if (len == 0) { return 0; } clear(); resizeBuffer(len, false); return length; } private final void updateSettings() throws InternetSCSIException { if (length == 0) { return; } dataBuffer.rewind(); try { // split into key-value pairs final String[] data; if (dataBuffer.hasArray()) { data = new String(dataBuffer.array(), DEFAULT_TEXT_ENCODING).split(PAIR_DELIMITER); } else { final byte[] dataArray = new byte[dataBuffer.capacity()]; dataBuffer.get(dataArray); dataBuffer.rewind(); data = new String(dataArray, DEFAULT_TEXT_ENCODING).split(PAIR_DELIMITER); } // split the key and value of a key-value pair String[] keyValue; for (int i = 0; i < data.length; i++) { keyValue = data[i].split(KEY_VALUE_DELIMITER); if (keyValue.length != NUMBER_OF_TOKENS) { throw new InternetSCSIException("This PDU does not contain a valid key-value-pair."); } settings.add(OperationalTextKey.valueOfEx(keyValue[KEY_INDEX]), keyValue[VALUE_INDEX]); } } catch (UnsupportedEncodingException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Unsupported Encoding: " + e.getLocalizedMessage()); } // exception rethrow throw new InternetSCSIException(e.getMessage()); } } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Returns the <code>SettingsMap</code> of this <code>TextParameterDataSegment</code> object. * * @return The stored settings of this <code>TextParameterDataSegment</code> object. * @throws InternetSCSIException * if any violation of the iSCSI Standard occurs. */ public final SettingsMap getSettings() throws InternetSCSIException { if (isDirty) { updateSettings(); } return settings; } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Clears all stored content of this OperationTextKeys object. */ public final void clear() { super.clear(); settings.clear(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * {@inheritDoc} * * @throws Exception */ public final boolean equals(final Object anObject) { if (anObject instanceof TextParameterDataSegment) { try { final TextParameterDataSegment anotherTPDS = (TextParameterDataSegment)anObject; return getSettings().equals(anotherTPDS.getSettings()); } catch (Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error(e.getLocalizedMessage()); } } } return super.equals(anObject); } /** {@inheritDoc} */ @Override public final int hashCode() { return super.hashCode(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }