View Javadoc

1   package org.jscsi.target.connection;
2   
3   
4   import java.io.IOException;
5   import java.nio.channels.SocketChannel;
6   import java.security.DigestException;
7   import java.util.concurrent.Callable;
8   
9   import javax.naming.OperationNotSupportedException;
10  
11  import org.jscsi.exception.InternetSCSIException;
12  import org.jscsi.parser.OperationCode;
13  import org.jscsi.parser.ProtocolDataUnit;
14  import org.jscsi.target.connection.phase.TargetFullFeaturePhase;
15  import org.jscsi.target.connection.phase.TargetLoginPhase;
16  import org.jscsi.target.connection.phase.TargetPhase;
17  import org.jscsi.target.connection.stage.fullfeature.PingStage;
18  import org.jscsi.target.connection.stage.fullfeature.ReadStage;
19  import org.jscsi.target.settings.ConnectionSettingsNegotiator;
20  import org.jscsi.target.settings.SessionSettingsNegotiator;
21  import org.jscsi.target.settings.Settings;
22  import org.jscsi.target.settings.SettingsException;
23  import org.jscsi.target.util.FastByteArrayProvider;
24  import org.jscsi.target.util.SerialArithmeticNumber;
25  import org.slf4j.Logger;
26  import org.slf4j.LoggerFactory;
27  
28  
29  /**
30   * A class for objects representing an iSCSI connection with all necessary variables.
31   * <p>
32   * Each {@link TargetConnection} runs in a separate {@link Thread}. The conceptually most important parts of its
33   * behavior can be likened to a finite state machine (FSM), in which the most basic states (stages) are grouped into
34   * more general states (phases). Commands send by the initiator are carried out in these stages, usually without
35   * transitioning to a different phase. A connection's current phase determines which stages are reachable, limiting the
36   * kind of commands the initiator may issue at any given moment.
37   * 
38   * @author Andreas Ergenzinger
39   */
40  public interface Connection extends Callable<Void> {
41  
42      Settings getSettings ();
43  
44      SerialArithmeticNumber getStatusSequenceNumber ();
45  
46      boolean isLeadingConnection ();
47  
48      ProtocolDataUnit receivePdu () throws DigestException , InternetSCSIException , IOException , SettingsException;
49  
50      void sendPdu (ProtocolDataUnit pDataUnit) throws InterruptedException , IOException , InternetSCSIException;
51  
52      ConnectionSettingsNegotiator getConnectionSettingsNegotiator ();
53  
54      void setSession (TargetSession pSession);
55  
56      TargetSession getTargetSession ();
57  
58      void setStatusSequenceNumber (int pStatusSequenceNumber);
59  
60      void initializeConnectionSettingsNegotiator (SessionSettingsNegotiator pSettingsNegotiator);
61  
62      byte[] getDataInArray (int pLength);
63      
64      public boolean stop();
65  
66      public static class TargetConnection implements Connection {
67  
68          private static final Logger LOGGER = LoggerFactory.getLogger(TargetConnection.class);
69  
70          /**
71           * The {@link TargetSession} this connection belongs to.
72           */
73          private TargetSession targetSession;
74  
75          /**
76           * The {@link TargetSenderWorker} used by this connection for sending and receiving {@link ProtocolDataUnit}s.
77           */
78          TargetSenderWorker senderWorker;
79  
80          /**
81           * The {@link ConnectionSettingsNegotiator} of this connection responsible for negotiating and storing
82           * connection parameters which have been negotiated with or declared by the initiator.
83           */
84          private ConnectionSettingsNegotiator connectionSettingsNegotiator;
85  
86          /**
87           * The current {@link TargetPhase} describing a general state of the connection.
88           */
89          private TargetPhase phase;
90  
91          /**
92           * A counter for the <code>StatSN</code> field of sent {@link ProtocolDataUnit} objects with Status.
93           */
94          private SerialArithmeticNumber statusSequenceNumber;
95  
96          /**
97           * Will manage and serve as a source of byte arrays to be used for sending Data In PDUs in the {@link ReadStage}
98           * .
99           */
100         private FastByteArrayProvider dataInArrayProvider = new FastByteArrayProvider(4);
101 
102         /**
103          * <code>true</code> if and only if this connection is the first connection to be associated with its parent
104          * session.
105          * <p>
106          * This distinction is necessary because some parameters may only be declared over the leading connection.
107          */
108         private final boolean isLeadingConnection;
109 
110         /**
111          * The last {@link ProtocolDataUnit} received on this connection.
112          */
113         private ProtocolDataUnit lastReceivedPDU;
114 
115         /**
116          * The {@link TargetConnection} constructor.
117          * 
118          * @param socketChannel used for sending and receiving PDUs
119          * @param isLeadingConnection <code>true</code> if and only if this connection is the first connection
120          *            associated with its enclosing session
121          */
122         public TargetConnection (SocketChannel socketChannel, final boolean isLeadingConnection) {
123             this.isLeadingConnection = isLeadingConnection;
124             senderWorker = new TargetSenderWorker(this, socketChannel);
125         }
126 
127         /**
128          * Returns a byte array that can be used for holding data segment data of Data In PDUs sent during the
129          * {@link ReadStage}.
130          * 
131          * @param length the length of the array
132          * @return a byte array of the specified length
133          */
134         public byte[] getDataInArray (final int length) {
135             return dataInArrayProvider.getArray(length);
136         }
137 
138         /**
139          * Returns the {@link TargetSession} this connection belongs to.
140          * 
141          * @return the {@link TargetSession} this connection belongs to
142          */
143         TargetSession getSession () {
144             return targetSession;
145         }
146 
147         /**
148          * Sets the {@link TargetSession} this connection belongs to.
149          * 
150          * @param session the {@link TargetSession} this connection belongs to
151          */
152         public void setSession (TargetSession session) {
153             this.targetSession = session;
154             senderWorker.setSession(session);
155         }
156 
157         /**
158          * Returns the next {@link ProtocolDataUnit} to be received on the connection.
159          * <p>
160          * The method will block until a PDU has been completely received.
161          * 
162          * @return the next received PDU
163          * @throws DigestException if a digest error has occured
164          * @throws InternetSCSIException if a general iSCSI protocol error has been detected
165          * @throws IOException if the connection was closed
166          * @throws SettingsException will not happen
167          */
168         public ProtocolDataUnit receivePdu () throws DigestException , InternetSCSIException , IOException , SettingsException {
169             lastReceivedPDU = senderWorker.receiveFromWire();
170 
171             if (lastReceivedPDU.getBasicHeaderSegment().getOpCode().equals(OperationCode.NOP_OUT)) {
172                 try {
173                     // System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Handling ping immediately..");
174                     // System.out.println("******************************\nRecieving\nSystem Time: " + new
175                     // java.sql.Timestamp(System.currentTimeMillis()).toString() + "\n" + lastReceivedPDU +
176                     // "\n******************************");
177                     new PingStage(new TargetFullFeaturePhase(this)).execute(lastReceivedPDU);
178                 } catch (InterruptedException e) {}
179                 lastReceivedPDU = senderWorker.receiveFromWire();
180             }
181 
182             // System.out.println("******************************\nRecieving\nSystem Time: " + new
183             // java.sql.Timestamp(System.currentTimeMillis()).toString() + "\n" + lastReceivedPDU +
184             // "\n******************************");
185             return lastReceivedPDU;
186         }
187 
188         /**
189          * Serializes and sends a {@link ProtocolDataUnit} over the connection.
190          * 
191          * @param pdu the PDU to send
192          * @throws InterruptedException
193          * @throws IOException
194          * @throws InternetSCSIException
195          */
196         public void sendPdu (ProtocolDataUnit pdu) throws InterruptedException , IOException , InternetSCSIException {
197             // System.out.println("******************************\nSending\nSystem Time: " + new
198             // java.sql.Timestamp(System.currentTimeMillis()).toString() + "\n" + pdu +
199             // "\n******************************");
200             senderWorker.sendOverWire(pdu);
201         }
202 
203         /**
204          * Starts the processing of PDUs by this connection.
205          * <p>
206          * For this method to work properly, the leading PDU send by the initiator over this connection must have been
207          * received via {@link #receivePdu()}.
208          * 
209          */
210         public Void call () {
211 
212             try {
213                 // *** login phase ***
214                 phase = new TargetLoginPhase(this);
215                 if (phase.execute(lastReceivedPDU)) {
216                     LOGGER.debug("Login Phase successful");
217 
218                     // if this is the leading connection, set the session type
219                     final Settings settings = getSettings();
220                     if (isLeadingConnection) targetSession.setSessionType(SessionType.getSessionType(settings.getSessionType()));
221                     targetSession.setTargetName(settings.getTargetName());
222                     // *** full feature phase ***
223                     phase = new TargetFullFeaturePhase(this);
224 
225                     phase.execute();
226                 }
227                 senderWorker.close();
228             } catch (OperationNotSupportedException | IOException | InterruptedException | InternetSCSIException | DigestException
229                     | SettingsException e) {
230                 LOGGER.error("Exception throws", e);
231             }
232 
233             targetSession.removeTargetConnection(this);
234 
235             LOGGER.debug("closed connection");
236 
237             return null;
238         }
239 
240         public TargetSession getTargetSession () {
241             return targetSession;
242         }
243 
244         /**
245          * Returns <code>true</code> if this is the leading connection, i.e. the first TargetConnection in the
246          * connection's {@link TargetSession}. Otherwise <code>false</code> is returned.
247          * 
248          * @return <code>true</code> if this is the leading connection
249          */
250         public boolean isLeadingConnection () {
251             return isLeadingConnection;
252         }
253 
254         /**
255          * Initializes {@link #connectionSettingsNegotiator}.
256          * <p>
257          * This method must be be called after the this connection has been added to its session.
258          */
259         public void initializeConnectionSettingsNegotiator (final SessionSettingsNegotiator sessionSettingsNegotiator) {
260             connectionSettingsNegotiator = new ConnectionSettingsNegotiator(sessionSettingsNegotiator);
261         }
262 
263         /**
264          * Returns a {@link Settings} object with a snapshot of the current connection and session parameters.
265          * 
266          * @return the current {@link Settings}
267          */
268         public Settings getSettings () {
269             return connectionSettingsNegotiator.getSettings();
270         }
271 
272         public ConnectionSettingsNegotiator getConnectionSettingsNegotiator () {
273             return connectionSettingsNegotiator;
274         }
275 
276         public SerialArithmeticNumber getStatusSequenceNumber () {
277             return statusSequenceNumber;
278         }
279 
280         public void setStatusSequenceNumber (final int statusSequenceNumber) {
281             this.statusSequenceNumber = new SerialArithmeticNumber(statusSequenceNumber);
282         }
283         
284         public boolean stop(){
285             if(phase instanceof TargetFullFeaturePhase){
286                 ((TargetFullFeaturePhase)phase).stop();
287                 return true;
288             }
289             
290             return false;
291         }
292     }
293 }