KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > coldcore > coloradoftp > connection > impl > GenericDataConnection


1 package com.coldcore.coloradoftp.connection.impl;
2
3 import com.coldcore.coloradoftp.command.Reply;
4 import com.coldcore.coloradoftp.connection.*;
5 import com.coldcore.coloradoftp.factory.ObjectFactory;
6 import com.coldcore.coloradoftp.factory.ObjectName;
7 import com.coldcore.coloradoftp.session.Session;
8 import com.coldcore.coloradoftp.session.SessionAttributeName;
9 import org.apache.log4j.Logger;
10
11 import java.nio.ByteBuffer JavaDoc;
12 import java.nio.channels.ReadableByteChannel JavaDoc;
13 import java.nio.channels.WritableByteChannel JavaDoc;
14 import java.nio.channels.Channel JavaDoc;
15
16 /**
17  * @see com.coldcore.coloradoftp.connection.DataConnection
18  */

19 public class GenericDataConnection extends GenericConnection implements DataConnection {
20
21   private static Logger log = Logger.getLogger(GenericDataConnection.class);
22   protected ControlConnection controlConnection;
23   protected ReadableByteChannel JavaDoc rbc;
24   protected WritableByteChannel JavaDoc wbc;
25   protected DataConnectionMode mode;
26   protected String JavaDoc filename;
27   protected boolean userAborted;
28   protected boolean successful;
29   protected boolean skipReply;
30   protected DataConnectionCallback callback;
31
32
33   public GenericDataConnection(int bufferSize) {
34     super();
35
36     //rbuffer = ByteBuffer.allocateDirect(bufferSize);
37
rbuffer = ByteBuffer.allocate(bufferSize);
38     rbuffer.flip();
39   }
40
41
42   /** Read data from user */
43   protected void read() throws Exception JavaDoc {
44     /* We will read data from the user and write it into the channel until the user
45      * disconnects. There is no way to check if a complete file has been uploaded,
46      * so we assume that every transfer is a success.
47      */

48
49     //Read data from user into the buffer if the buffer is empty
50
if (!rbuffer.hasRemaining()) {
51       rbuffer.clear();
52       int i = sc.read(rbuffer); //Thread blocks here...
53
rbuffer.flip();
54
55       //Client disconnected?
56
if (i == -1) {
57         successful = true;
58         throw new TransferCompleteException();
59       }
60
61       bytesRead += i;
62       log.debug("Read from socket "+i+" bytes (total "+bytesRead+")");
63     }
64
65     //Forward the data into the channel
66
wbc.write(rbuffer);
67   }
68
69
70   /** Write data to user */
71   protected void write() throws Exception JavaDoc {
72     /* We wiil read data from the channel and write it to the user until the
73      * channel is empty (successful transfer). If user disconnects earlier than
74      * all data is transferred then the transfer has failed.
75      */

76
77     //Read the data from the channel into the buffer if the buffer is empty
78
if (!rbuffer.hasRemaining()) {
79       rbuffer.clear();
80       int i = rbc.read(rbuffer);
81       rbuffer.flip();
82
83       //File done?
84
if (i == -1) {
85         successful = true;
86         throw new TransferCompleteException();
87       }
88     }
89
90     //Forward the data to the user
91
int i = sc.write(rbuffer); //Thread blocks here...
92

93     //Client disconnected?
94
if (i == -1) throw new TransferAbortedException();
95
96     bytesWrote += i;
97     log.debug("Wrote into socket "+i+" bytes (total "+bytesWrote+")");
98   }
99
100
101   /** Activate the connection if not active yet */
102   protected void activate() {
103     /* The connection will start to function as soon as it gets MODE and CHANNEL from
104      * user session (we must get CHANNEL last as it starts read/write routines).
105      * Those attributes then have to be removed or the next data connection will use them as well.
106      * There is also a FILENAME attribute for file operations.
107      */

108
109     if (rbc != null || wbc != null) return;
110
111     if (mode == null) {
112       Session session = controlConnection.getSession();
113       mode = (DataConnectionMode) session.getAttribute(SessionAttributeName.DATA_CONNECTION_MODE);
114       if (mode != null) {
115         log.debug("Mode extracted from user session");
116       }
117     }
118
119     //Mode first
120
if (mode == null) return;
121
122     if (filename == null) {
123       Session session = controlConnection.getSession();
124       filename = (String JavaDoc) session.getAttribute(SessionAttributeName.DATA_CONNECTION_FILENAME);
125       if (filename != null) {
126         log.debug("Filename extracted from user session");
127       }
128     }
129
130     //Filename second
131
if (mode != DataConnectionMode.LIST && filename == null) return;
132
133     //Channel third (also start an appropriate thread)
134
if (rbc == null && wbc == null) {
135       Session session = controlConnection.getSession();
136       if (mode == DataConnectionMode.LIST || mode == DataConnectionMode.RETR) {
137         rbc = (ReadableByteChannel JavaDoc) session.getAttribute(SessionAttributeName.DATA_CONNECTION_CHANNEL);
138         startWriterThread(); //To write data to user
139
} else {
140         wbc = (WritableByteChannel JavaDoc) session.getAttribute(SessionAttributeName.DATA_CONNECTION_CHANNEL);
141         startReaderThread(); //To read data from user
142
}
143       if (rbc != null || wbc != null) {
144         log.debug("Channel extracted from user session (data transfer begins)");
145       }
146     }
147   }
148
149
150   public void service() throws Exception JavaDoc {
151     //User aborted the transfer
152
if (userAborted) throw new TransferAbortedException();
153
154     //Try to activate the data transfer
155
activate();
156   }
157
158
159   /** Close data channel */
160   protected void closeDataChannel() {
161     Session session = controlConnection.getSession();
162     Channel JavaDoc odc = (Channel JavaDoc) session.getAttribute(SessionAttributeName.DATA_CONNECTION_CHANNEL);
163     session.removeAttribute(SessionAttributeName.DATA_CONNECTION_FILENAME);
164     try {
165       if (odc != null) odc.close();
166     } catch (Throwable JavaDoc e) {
167       log.error("Error closing data channel (ignoring)", e);
168     }
169   }
170
171
172   /** Send reply to user upon connection termination */
173   protected void reply() {
174     try {
175       //Transfer aborted by user - send "426" and then "226"
176
if (userAborted) {
177         Reply reply = (Reply) ObjectFactory.getObject(ObjectName.REPLY);
178         reply.setCode("426");
179         reply.setText("Connection closed, transfer aborted.");
180         controlConnection.reply(reply);
181
182         reply = (Reply) ObjectFactory.getObject(ObjectName.REPLY);
183         reply.setCode("226");
184         reply.setText("Abort command successful.");
185         controlConnection.reply(reply);
186
187         log.debug("User aborted data transfer");
188         return;
189       }
190
191       //Transfer failed
192
if (!successful) {
193         Reply reply = (Reply) ObjectFactory.getObject(ObjectName.REPLY);
194         reply.setCode("426");
195         reply.setText("Connection closed, transfer aborted.");
196         controlConnection.reply(reply);
197
198         log.debug("Data transfer failed");
199         return;
200       }
201
202       //Transfer OK (note that STOU has a different code)
203
Reply reply = (Reply) ObjectFactory.getObject(ObjectName.REPLY);
204       if (mode == DataConnectionMode.STOU) reply.setCode("250");
205       else reply.setCode("226");
206
207       if (mode == DataConnectionMode.LIST) {
208         reply.setText("Transfer completed.");
209       } else {
210         //Encode double-quated in the filename
211
String JavaDoc encf = filename.replaceAll("\"", "\"\"");
212         reply.setText("Transfer completed for \""+encf+"\".");
213       }
214       controlConnection.reply(reply);
215
216       log.debug("Data transfer successful");
217
218     } catch (Throwable JavaDoc e) {
219       log.error("Error sending completion reply (ignoring)", e);
220     }
221   }
222
223
224
225   public synchronized void destroy() {
226     closeDataChannel();
227
228     //Hook for post-upload/download logic via callback
229
if (!skipReply && callback != null)
230       try {
231         if (successful) callback.onTransferComplete(this);
232         else callback.onTransferAbort(this);
233       } catch (Throwable JavaDoc e) {
234         log.error("Callback error (ignoring)", e);
235       }
236
237     //When data transfer finishes, a reply must be send to user
238
if (!skipReply) reply();
239
240     //Clear the attributes to prevent misuse by future instances
241
Session session = controlConnection.getSession();
242     session.removeAttribute(SessionAttributeName.DATA_CONNECTION_MODE);
243     session.removeAttribute(SessionAttributeName.DATA_CONNECTION_CHANNEL);
244
245     //Clear control connection reference
246
if (controlConnection != null) controlConnection.setDataConnection(null);
247
248     super.destroy();
249   }
250
251
252   public void destroyNoReply() {
253     skipReply = true;
254     destroy();
255   }
256
257
258   public void abort() {
259     userAborted = true;
260   }
261
262
263   public ControlConnection getControlConnection() {
264     return controlConnection;
265   }
266
267
268   public void setControlConnection(ControlConnection controlConnection) {
269     this.controlConnection = controlConnection;
270   }
271
272
273   public DataConnectionCallback getDataConnectionCallback() {
274     return callback;
275   }
276
277
278   public void setDataConnectionCallback(DataConnectionCallback callback) {
279     this.callback = callback;
280   }
281 }
282
Popular Tags