/** * 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.initiator; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.jscsi.exception.NoSuchSessionException; import org.jscsi.exception.TaskExecutionException; import org.jscsi.initiator.connection.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <h1>Initiator</h1> * <p> * This class represents an initiator, which request messages to a target defined by the iSCSI Protocol * (RFC3720). * * @author Volker Wildi, University of Konstanz * @author Sebastian Graf, University of Konstanz */ public final class Initiator { // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** The Logger interface. */ private static final Logger LOGGER = LoggerFactory.getLogger(Initiator.class); // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** Stores all configuration parameters. */ private final Configuration configuration; /** Stores all opened sessions. */ private final Map<String, Session> sessions; /** Stores all configuration parameters. */ private final LinkFactory factory; /** * Constructor to create an empty <code>Initiator</code> object with the * given configuration. * * @param initConfiguration * The user-defined configuration file. */ public Initiator(final Configuration initConfiguration) { configuration = initConfiguration; sessions = new Hashtable<String, Session>(1); factory = new LinkFactory(this); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Creates a new session with the given target name, which is read from the * configuration file. * * @param targetName * The name of the iSCSI Target to connect. * @throws NoSuchSessionException * if no session was found * */ public final void createSession(final String targetName) throws NoSuchSessionException { createSession(configuration.getTargetAddress(targetName), targetName); } /** * Creates a new session to a target with the given Internet address and * port. The target has the name <code>targetName</code>. * * @param targetAddress * The Internet address and Port of the target. * @param targetName * Name of the target, to which a connection should be created. * @throws Exception * if any error occurs. */ public final void createSession(final InetSocketAddress targetAddress, final String targetName) { final Session session = factory.getSession(configuration, targetName, targetAddress); sessions.put(session.getTargetName(), session); LOGGER.info("Created the session with iSCSI Target '" + targetName + "' at " + targetAddress.getHostName() + " on port " + targetAddress.getPort() + "."); } /** * Closes all opened connections within this session to the given target. * * @param targetName * The name of the target, which connection should be closed. * @throws NoSuchSessionException * if no session is accessible * @throws TaskExecutionException * if logout fails. */ public final void closeSession(final String targetName) throws NoSuchSessionException, TaskExecutionException { getSession(targetName).logout(); // TODO Test the removal from the map. sessions.remove(targetName); LOGGER.info("Closed the session to the iSCSI Target '" + targetName + "'."); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Invokes a read operation for the session <code>targetName</code> and * store the read bytes in the buffer <code>dst</code>. Start reading at the * logical block address and request <code>transferLength</code> blocks. * * * @param targetName * The name of the session to invoke this read operation. * @param dst * The buffer to store the read data. * @param logicalBlockAddress * The logical block address of the beginning. * @param transferLength * Number of bytes to read. * @throws Exception * if any error occurs. * * @return FutureObject for MultiThreadedReads * @throws TaskExecutionException * if execution fails * @throws NoSuchSessionException * if session is not found */ public final Future<Void> multiThreadedRead(final String targetName, final ByteBuffer dst, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException, TaskExecutionException { final Future<Void> returnVal = getSession(targetName).read(dst, logicalBlockAddress, transferLength); return returnVal; } /** * Invokes a read operation for the session <code>targetName</code> and * store the read bytes in the buffer <code>dst</code>. Start reading at the * logical block address and request <code>transferLength</code> blocks. * * * @param targetName * The name of the session to invoke this read operation. * @param dst * The buffer to store the read data. * @param logicalBlockAddress * The logical block address of the beginning. * @param transferLength * Number of bytes to read. * @throws TaskExecutionException * if execution fails * @throws NoSuchSessionException * if session is not found */ public final void read(final String targetName, final ByteBuffer dst, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException, TaskExecutionException { try { multiThreadedRead(targetName, dst, logicalBlockAddress, transferLength).get(); } catch (final InterruptedException exc) { throw new TaskExecutionException(exc); } catch (final ExecutionException exc) { throw new TaskExecutionException(exc); } } /** * Invokes a write operation for the session <code>targetName</code> and * transmits the bytes in the buffer <code>dst</code>. Start writing at the * logical block address and transmit <code>transferLength</code> blocks. * * * @param targetName * The name of the session to invoke this write operation. * @param src * The buffer to transmit. * @param logicalBlockAddress * The logical block address of the beginning. * @param transferLength * Number of bytes to write. * @throws Exception * if any error occurs. * @return FutureObject for the multi-threaded write operation * @throws TaskExecutionException * if execution fails * @throws NoSuchSessionException * if session is not found */ public final Future<Void> multiThreadedWrite(final String targetName, final ByteBuffer src, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException, TaskExecutionException { return getSession(targetName).write(src, logicalBlockAddress, transferLength); } /** * Invokes a write operation for the session <code>targetName</code> and * transmits the bytes in the buffer <code>dst</code>. Start writing at the * logical block address and transmit <code>transferLength</code> blocks. * * * @param targetName * The name of the session to invoke this write operation. * @param src * The buffer to transmit. * @param logicalBlockAddress * The logical block address of the beginning. * @param transferLength * Number of bytes to write. * @throws TaskExecutionException * if execution fails * @throws NoSuchSessionException * if session is not found */ public final void write(final String targetName, final ByteBuffer src, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException, TaskExecutionException { try { multiThreadedWrite(targetName, src, logicalBlockAddress, transferLength).get(); } catch (final InterruptedException exc) { throw new TaskExecutionException(exc); } catch (final ExecutionException exc) { throw new TaskExecutionException(exc); } } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Returns the used block size (in bytes) of the iSCSI Target. * * @param targetName * The name of the session to invoke this capacity operation. * @return the used block size (in bytes) of the connected iSCSI Target. * @throws NoSuchSessionException * if the session connected to the target is yet not open. */ public final long getBlockSize(final String targetName) throws NoSuchSessionException { return getSession(targetName).getBlockSize(); } /** * Returns the capacity (in blocks) of the iSCSI Target. * * @param targetName * The name of the session to invoke this capacity operation. * @return the capacity in blocks of the connected iSCSI Target. * @throws NoSuchSessionException * if the session connected to the target is yet not open. */ public final long getCapacity(final String targetName) throws NoSuchSessionException { return getSession(targetName).getCapacity(); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- /** * Returns the <code>Session</code> instance of the iSCSI Target with the * given name. * * @param targetName * The name of the session, which instance you want. * @return The requested <code>Session</code> instance. */ private final Session getSession(final String targetName) throws NoSuchSessionException { final Session session = sessions.get(targetName); if (session != null) { return session; } else { throw new NoSuchSessionException("Session " + targetName + " not found!"); } } /** * Removes the <code>Session</code> instances form the sessions queue. * * @param sessionReq * The Session to remove * @throws NoSuchSessionException * if the Session does not exist in the Map */ public final void removeSession(final Session sessionReq) throws NoSuchSessionException { final Session session = sessions.get(sessionReq.getTargetName()); if (session != null) { sessions.remove(sessionReq.getTargetName()); } else { throw new NoSuchSessionException("Session " + sessionReq.getTargetName() + " not found!"); } } /** * TODO Search a better solution for this. How can we notify the * Application, that all Sessions are closed (and all Tasks are finished) */ /** * is the Sessions Map empty?. * * @return true if it is empty */ public final Boolean noSessions() { return (sessions.size() == 0); } // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- }