1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.plugin.opennap.net;
21
22 import java.awt.event.ActionEvent;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.net.Socket;
28 import java.util.Hashtable;
29
30 import javax.swing.Action;
31 import javax.swing.Icon;
32
33 import org.xnap.XNap;
34 import org.xnap.io.Library;
35 import org.xnap.io.MetaInfoFile;
36 import org.xnap.io.MetaInfoManager;
37 import org.xnap.net.NetHelper;
38 import org.xnap.peer.Peer;
39 import org.xnap.plugin.Plugin;
40 import org.xnap.plugin.opennap.OpenNapPlugin;
41 import org.xnap.plugin.opennap.net.msg.ExceptionListener;
42 import org.xnap.plugin.opennap.net.msg.MessageHandler;
43 import org.xnap.plugin.opennap.net.msg.client.DirectBrowseAckMessage;
44 import org.xnap.plugin.opennap.net.msg.client.DirectShareFileMessage;
45 import org.xnap.plugin.opennap.user.OpenNapUser;
46 import org.xnap.transfer.AbstractTransfer;
47 import org.xnap.transfer.DefaultSegment;
48 import org.xnap.transfer.Segment;
49 import org.xnap.transfer.Upload;
50 import org.xnap.transfer.UploadManager;
51 import org.xnap.transfer.action.AbstractStopAction;
52 import org.xnap.util.FiniteStateMachine;
53 import org.xnap.util.Range;
54 import org.xnap.util.State;
55
56 /***
57 *
58 */
59 public class OpenNapDirectBrowseUpload extends AbstractTransfer
60 implements ExceptionListener, Upload, SocketListener {
61
62
63
64 public static final int SOCKET_TIMEOUT = 1 * 60 * 1000;
65
66 /***
67 * The state transition table.
68 */
69 private static final Hashtable TRANSITION_TABLE;
70 static {
71 State[][] table = new State[][] {
72 { State.NOT_STARTED,
73 State.WAITING, },
74 { State.WAITING,
75 State.CONNECTING, State.FINISHED, State.STOPPING, },
76 { State.CONNECTING,
77 State.FINISHED, State.STOPPING, State.UPLOADING, },
78 { State.UPLOADING,
79 State.FINISHED, State.STOPPING, },
80 { State.STOPPING,
81 State.FINISHED }
82 };
83
84 TRANSITION_TABLE = FiniteStateMachine.createStateTable(table);
85 }
86
87
88
89 private StateMachine sm = new StateMachine();
90
91 private OpenNapUser user;
92
93 private BrowseUploadSocket inSocket;
94
95 private long bytesTransferred;
96 private DefaultSegment segment;
97 private long size;
98
99
100
101 public OpenNapDirectBrowseUpload(OpenNapUser user)
102 {
103 this.user = user;
104 }
105
106
107
108 public void exceptionThrown(Exception e)
109 {
110 setState(State.FAILED, e.getLocalizedMessage());
111 }
112
113 public File getFile()
114 {
115 return null;
116 }
117
118 public String getFilename()
119 {
120 return XNap.tr("OpenNap Direct Browse");
121 }
122
123 /***
124 *
125 */
126 public long getFilesize()
127 {
128 return size;
129 }
130
131 public Plugin getPlugin()
132 {
133 return OpenNapPlugin.getInstance();
134 }
135
136 /***
137 * @see xnap.transfer.Transfer#getActions()
138 */
139 public Action[] getActions()
140 {
141 return new Action[] { new StopAction() };
142 }
143
144 /***
145 *
146 */
147 public long getBytesTransferred()
148 {
149 return bytesTransferred;
150 }
151
152 public Icon getIcon()
153 {
154 return OpenNapPlugin.ICON_16;
155 }
156
157 /***
158 * @see xnap.transfer.Transfer#getPeer()
159 */
160 public Peer getPeer()
161 {
162 return user;
163 }
164
165 public Segment[] getSegments()
166 {
167 return (segment != null) ? new Segment[] { segment } : null;
168 }
169
170 /***
171 * @see xnap.transfer.Transfer#getStatus()
172 */
173 public String getStatus()
174 {
175 return sm.getDescription();
176 }
177
178 /***
179 * @see xnap.transfer.Transfer#getTotalBytesTransferred()
180 */
181 public long getTotalBytesTransferred()
182 {
183 return (isDone()) ? bytesTransferred : 0;
184 }
185
186 public boolean isDone()
187 {
188 return sm.getState() == State.FINISHED;
189 }
190
191 public boolean isRunning()
192 {
193 return sm.getState() == State.UPLOADING;
194 }
195
196 public boolean socketReceived(IncomingSocket s)
197 {
198 if (s instanceof BrowseUploadSocket) {
199
200
201 inSocket = (BrowseUploadSocket)s;
202 setState(State.CONNECTING);
203 return true;
204 }
205 return false;
206 }
207
208 void setState(State newState, String description)
209 {
210 sm.setState(newState, description);
211 stateChanged();
212 }
213
214 void setState(State newState)
215 {
216 sm.setState(newState);
217 stateChanged();
218 }
219
220 void start()
221 {
222 setState(State.WAITING);
223 }
224
225
226
227 private class StateMachine extends FiniteStateMachine {
228
229
230
231 private OpenNapDirectBrowseUploadRunner runner;
232 private Thread t;
233
234
235
236 public StateMachine()
237 {
238 super(State.NOT_STARTED, TRANSITION_TABLE);
239 }
240
241
242
243 protected synchronized void stateChanged(State oldState,
244 State newState)
245 {
246 if (newState == State.WAITING) {
247 UploadManager.getInstance().add(OpenNapDirectBrowseUpload.this);
248
249 user.getServer().getListener().addSocketListener
250 (OpenNapDirectBrowseUpload.this);
251
252 DirectBrowseAckMessage msg
253 = new DirectBrowseAckMessage(user.getName());
254 msg.setExceptionListener(OpenNapDirectBrowseUpload.this);
255 MessageHandler.send(user.getServer(), msg);
256
257 if (user.getHost() != null && user.getPort() > 0) {
258 this.setState(State.CONNECTING);
259 }
260 }
261 else if (newState == State.CONNECTING) {
262 runner = new OpenNapDirectBrowseUploadRunner(inSocket);
263 t = new Thread(runner, "OpenNapDirectBrowseUpload:"
264 + user.getName());
265 t.start();
266 }
267 else if (newState == State.UPLOADING) {
268 transferStarted();
269 }
270 else if (newState == State.STOPPING) {
271 if (oldState == State.CONNECTING
272 || oldState == State.UPLOADING) {
273 runner.stop();
274 t.interrupt();
275 }
276 else {
277 this.setState(State.FINISHED);
278 }
279 }
280 else if (newState == State.FINISHED) {
281 UploadManager.getInstance().remove(OpenNapDirectBrowseUpload.this);
282 }
283
284 if (oldState == State.WAITING) {
285 user.getServer().getListener().removeSocketListener(OpenNapDirectBrowseUpload.this);
286 }
287 else if (oldState == State.UPLOADING) {
288 transferStopped();
289 }
290 }
291 }
292
293 private class StopAction extends AbstractStopAction {
294
295 public void actionPerformed(ActionEvent e)
296 {
297 setState(State.STOPPING);
298 }
299
300 }
301
302 private class OpenNapDirectBrowseUploadRunner implements Runnable
303 {
304
305 private boolean die;
306
307 private Socket socket;
308 private InputStream in;
309 private OutputStream out;
310
311 public OpenNapDirectBrowseUploadRunner(BrowseUploadSocket b)
312 {
313 if (b != null) {
314 socket = b.socket;
315 in = b.in;
316 }
317 }
318
319 public void run()
320 {
321 try {
322 if (socket != null) {
323 connect();
324 }
325 else {
326 connect(user.getHost(), user.getPort());
327 }
328
329 setState(State.UPLOADING);
330
331 if (OpenNapPlugin.getPreferences().getSendWholeRepository()) {
332 sendList(null);
333 }
334 else {
335 Range[] ranges = user.getServer().getShared();
336 if (ranges != null) {
337
338 for (int i = 0; i < ranges.length; i++) {
339 sendList(ranges[i]);
340 }
341 }
342 }
343
344 setState(State.FINISHED, XNap.tr("Succeeded"));
345 }
346 catch (IOException e) {
347 setState(State.FINISHED, NetHelper.getErrorMessage(e));
348 }
349 catch (InterruptedException e) {
350 setState(State.FINISHED, XNap.tr("Stopped"));
351 }
352 finally {
353 close();
354 }
355 }
356
357 public void stop()
358 {
359 die = true;
360 }
361
362 private void close()
363 {
364 try {
365 if (socket != null)
366 socket.close();
367 if (in != null)
368 in.close();
369 if (out != null)
370 out.close();
371 }
372 catch (IOException e) {
373 }
374 }
375
376 private void connect(String host, int port) throws IOException
377 {
378 logger.debug("connecting to " + host + ":" + port
379 + " for direct browse push");
380 socket = new Socket(host, port);
381 socket.setSoTimeout(SOCKET_TIMEOUT);
382 out = socket.getOutputStream();
383
384 write("SENDLIST " + user.getServer().getLocalPeer().getName() + "\n");
385 }
386
387 private void connect() throws IOException
388 {
389 socket.setSoTimeout(SOCKET_TIMEOUT);
390 out = socket.getOutputStream();
391
392 write(user.getServer().getLocalPeer().getName() + "\n");
393
394 }
395
396 private void sendList(Range r)
397 throws IOException, InterruptedException
398 {
399 if (r == null) {
400 r = new Range(0, Library.getInstance().size() - 1);
401 }
402
403 segment = new DefaultSegment(r.end - r.start + 1, 0,
404 r.end - r.start + 1, 0, 255);
405 for (int i = (int)r.start; i <= (int)r.end; i++) {
406 if (die) {
407 throw new InterruptedException();
408 }
409
410 MetaInfoFile f = Library.getInstance().get(i);
411 if (f != null && f.isShared()) {
412 MetaInfoManager.handle(f);
413
414 DirectShareFileMessage msg
415 = new DirectShareFileMessage(i, f);
416 write(msg.data + "\n");
417 }
418 segment.setTransferred(i - r.start + 1);
419 }
420 write("\n");
421 }
422
423 private void write(String message) throws IOException
424 {
425 logger.debug("> " + message);
426 out.write(message.getBytes());
427 bytesTransferred += message.getBytes().length;
428 out.flush();
429 }
430
431 }
432
433 }
434