1 /** 2 * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 5 * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of 6 * conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, 7 * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the 8 * distribution. * Neither the name of the University of Konstanz nor the names of its contributors may be used to 9 * endorse or promote products derived from this software without specific prior written permission. 10 * 11 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 12 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 13 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 14 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 15 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 16 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 17 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 */ 19 package org.jscsi.initiator; 20 21 22 import java.net.InetSocketAddress; 23 import java.nio.ByteBuffer; 24 import java.util.Hashtable; 25 import java.util.Map; 26 import java.util.concurrent.ExecutionException; 27 import java.util.concurrent.Future; 28 29 import org.jscsi.exception.NoSuchSessionException; 30 import org.jscsi.exception.TaskExecutionException; 31 import org.jscsi.initiator.connection.Session; 32 import org.slf4j.Logger; 33 import org.slf4j.LoggerFactory; 34 35 36 /** 37 * <h1>Initiator</h1> 38 * <p> 39 * This class represents an initiator, which request messages to a target defined by the iSCSI Protocol (RFC3720). 40 * 41 * @author Volker Wildi, University of Konstanz 42 * @author Sebastian Graf, University of Konstanz 43 */ 44 public final class Initiator { 45 46 // -------------------------------------------------------------------------- 47 // -------------------------------------------------------------------------- 48 49 /** The Logger interface. */ 50 private static final Logger LOGGER = LoggerFactory.getLogger(Initiator.class); 51 52 // -------------------------------------------------------------------------- 53 // -------------------------------------------------------------------------- 54 55 /** Stores all configuration parameters. */ 56 private final Configuration configuration; 57 58 /** Stores all opened sessions. */ 59 private final Map<String , Session> sessions; 60 61 /** Stores all configuration parameters. */ 62 private final LinkFactory factory; 63 64 /** 65 * Constructor to create an empty <code>Initiator</code> object with the given configuration. 66 * 67 * @param initConfiguration The user-defined configuration file. 68 */ 69 public Initiator (final Configuration initConfiguration) { 70 71 configuration = initConfiguration; 72 sessions = new Hashtable<String , Session>(1); 73 factory = new LinkFactory(this); 74 } 75 76 // -------------------------------------------------------------------------- 77 // -------------------------------------------------------------------------- 78 79 /** 80 * Creates a new session with the given target name, which is read from the configuration file. 81 * 82 * @param targetName The name of the iSCSI Target to connect. 83 * @throws NoSuchSessionException if no session was found 84 * 85 */ 86 public final void createSession (final String targetName) throws NoSuchSessionException { 87 88 createSession(configuration.getTargetAddress(targetName), targetName); 89 } 90 91 /** 92 * Creates a new session to a target with the given Internet address and port. The target has the name 93 * <code>targetName</code>. 94 * 95 * @param targetAddress The Internet address and Port of the target. 96 * @param targetName Name of the target, to which a connection should be created. 97 * @throws Exception if any error occurs. 98 */ 99 public final void createSession (final InetSocketAddress targetAddress, final String targetName) { 100 101 final Session session = factory.getSession(configuration, targetName, targetAddress); 102 sessions.put(session.getTargetName(), session); 103 LOGGER.info("Created the session with iSCSI Target '" + targetName + "' at " + targetAddress.getHostName() + " on port " + targetAddress.getPort() + "."); 104 } 105 106 /** 107 * Closes all opened connections within this session to the given target. 108 * 109 * @param targetName The name of the target, which connection should be closed. 110 * @throws NoSuchSessionException if no session is accessible 111 * @throws TaskExecutionException if logout fails. 112 */ 113 public final void closeSession (final String targetName) throws NoSuchSessionException , TaskExecutionException { 114 115 getSession(targetName).logout(); 116 // TODO Test the removal from the map. 117 sessions.remove(targetName); 118 119 LOGGER.info("Closed the session to the iSCSI Target '" + targetName + "'."); 120 } 121 122 // -------------------------------------------------------------------------- 123 // -------------------------------------------------------------------------- 124 125 /** 126 * Invokes a read operation for the session <code>targetName</code> and store the read bytes in the buffer 127 * <code>dst</code>. Start reading at the logical block address and request <code>transferLength</code> blocks. 128 * 129 * 130 * @param targetName The name of the session to invoke this read operation. 131 * @param dst The buffer to store the read data. 132 * @param logicalBlockAddress The logical block address of the beginning. 133 * @param transferLength Number of bytes to read. 134 * @throws Exception if any error occurs. 135 * 136 * @return FutureObject for MultiThreadedReads 137 * @throws TaskExecutionException if execution fails 138 * @throws NoSuchSessionException if session is not found 139 */ 140 public final Future<Void> multiThreadedRead (final String targetName, final ByteBuffer dst, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException , TaskExecutionException { 141 142 final Future<Void> returnVal = getSession(targetName).read(dst, logicalBlockAddress, transferLength); 143 return returnVal; 144 } 145 146 /** 147 * Invokes a read operation for the session <code>targetName</code> and store the read bytes in the buffer 148 * <code>dst</code>. Start reading at the logical block address and request <code>transferLength</code> blocks. 149 * 150 * 151 * @param targetName The name of the session to invoke this read operation. 152 * @param dst The buffer to store the read data. 153 * @param logicalBlockAddress The logical block address of the beginning. 154 * @param transferLength Number of bytes to read. 155 * @throws TaskExecutionException if execution fails 156 * @throws NoSuchSessionException if session is not found 157 */ 158 public final void read (final String targetName, final ByteBuffer dst, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException , TaskExecutionException { 159 try { 160 multiThreadedRead(targetName, dst, logicalBlockAddress, transferLength).get(); 161 } catch (final InterruptedException exc) { 162 throw new TaskExecutionException(exc); 163 } catch (final ExecutionException exc) { 164 throw new TaskExecutionException(exc); 165 } 166 } 167 168 /** 169 * Invokes a write operation for the session <code>targetName</code> and transmits the bytes in the buffer 170 * <code>dst</code>. Start writing at the logical block address and transmit <code>transferLength</code> blocks. 171 * 172 * 173 * @param targetName The name of the session to invoke this write operation. 174 * @param src The buffer to transmit. 175 * @param logicalBlockAddress The logical block address of the beginning. 176 * @param transferLength Number of bytes to write. 177 * @throws Exception if any error occurs. 178 * @return FutureObject for the multi-threaded write operation 179 * @throws TaskExecutionException if execution fails 180 * @throws NoSuchSessionException if session is not found 181 */ 182 public final Future<Void> multiThreadedWrite (final String targetName, final ByteBuffer src, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException , TaskExecutionException { 183 184 return getSession(targetName).write(src, logicalBlockAddress, transferLength); 185 } 186 187 /** 188 * Invokes a write operation for the session <code>targetName</code> and transmits the bytes in the buffer 189 * <code>dst</code>. Start writing at the logical block address and transmit <code>transferLength</code> blocks. 190 * 191 * 192 * @param targetName The name of the session to invoke this write operation. 193 * @param src The buffer to transmit. 194 * @param logicalBlockAddress The logical block address of the beginning. 195 * @param transferLength Number of bytes to write. 196 * @throws TaskExecutionException if execution fails 197 * @throws NoSuchSessionException if session is not found 198 */ 199 public final void write (final String targetName, final ByteBuffer src, final int logicalBlockAddress, final long transferLength) throws NoSuchSessionException , TaskExecutionException { 200 201 try { 202 multiThreadedWrite(targetName, src, logicalBlockAddress, transferLength).get(); 203 } catch (final InterruptedException exc) { 204 throw new TaskExecutionException(exc); 205 } catch (final ExecutionException exc) { 206 throw new TaskExecutionException(exc); 207 } 208 } 209 210 // -------------------------------------------------------------------------- 211 // -------------------------------------------------------------------------- 212 213 /** 214 * Returns the used block size (in bytes) of the iSCSI Target. 215 * 216 * @param targetName The name of the session to invoke this capacity operation. 217 * @return the used block size (in bytes) of the connected iSCSI Target. 218 * @throws NoSuchSessionException if the session connected to the target is yet not open. 219 */ 220 public final long getBlockSize (final String targetName) throws NoSuchSessionException { 221 222 return getSession(targetName).getBlockSize(); 223 } 224 225 /** 226 * Returns the capacity (in blocks) of the iSCSI Target. 227 * 228 * @param targetName The name of the session to invoke this capacity operation. 229 * @return the capacity in blocks of the connected iSCSI Target. 230 * @throws NoSuchSessionException if the session connected to the target is yet not open. 231 */ 232 public final long getCapacity (final String targetName) throws NoSuchSessionException { 233 234 return getSession(targetName).getCapacity(); 235 } 236 237 // -------------------------------------------------------------------------- 238 // -------------------------------------------------------------------------- 239 240 /** 241 * Returns the <code>Session</code> instance of the iSCSI Target with the given name. 242 * 243 * @param targetName The name of the session, which instance you want. 244 * @return The requested <code>Session</code> instance. 245 */ 246 private final Session getSession (final String targetName) throws NoSuchSessionException { 247 248 final Session session = sessions.get(targetName); 249 250 if (session != null) { 251 return session; 252 } else { 253 throw new NoSuchSessionException("Session " + targetName + " not found!"); 254 } 255 } 256 257 /** 258 * Removes the <code>Session</code> instances form the sessions queue. 259 * 260 * @param sessionReq The Session to remove 261 * @throws NoSuchSessionException if the Session does not exist in the Map 262 */ 263 public final void removeSession (final Session sessionReq) throws NoSuchSessionException { 264 265 final Session session = sessions.get(sessionReq.getTargetName()); 266 267 if (session != null) { 268 sessions.remove(sessionReq.getTargetName()); 269 } else { 270 throw new NoSuchSessionException("Session " + sessionReq.getTargetName() + " not found!"); 271 } 272 } 273 274 /** 275 * TODO Search a better solution for this. How can we notify the Application, that all Sessions are closed (and all 276 * Tasks are finished) 277 */ 278 /** 279 * is the Sessions Map empty?. 280 * 281 * @return true if it is empty 282 */ 283 public final Boolean noSessions () { 284 285 return (sessions.size() == 0); 286 } 287 288 // -------------------------------------------------------------------------- 289 // -------------------------------------------------------------------------- 290 // -------------------------------------------------------------------------- 291 // -------------------------------------------------------------------------- 292 293 }