View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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  	//--- Constant(s) ---
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  	//--- Data Field(s) ---
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      //--- Constructor(s) ---
100 
101 	public OpenNapDirectBrowseUpload(OpenNapUser user) 
102     {
103 		this.user = user;
104     }
105 
106     //--- Methods ---
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 			// there is no way to verify if this socket is really ours
200 			// so just take it
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     //--- Inner Class(es) ---
226 
227     private class StateMachine extends FiniteStateMachine {
228 
229 		//--- Data Field(s) ---
230 
231 		private OpenNapDirectBrowseUploadRunner runner;
232 		private Thread t;
233 
234 		//--- Constructor(s) ---
235 
236 		public StateMachine()
237 		{
238 			super(State.NOT_STARTED, TRANSITION_TABLE);
239 		}
240 
241 		//--- Method(s) ---
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 						// FIX: the progress is incorrect
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 			//write("\n");
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