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.overnet.net;
21  
22  import java.awt.event.ActionEvent;
23  import java.io.File;
24  import java.util.Arrays;
25  import javax.swing.AbstractAction;
26  import javax.swing.Action;
27  import javax.swing.Icon;
28  import javax.swing.JOptionPane;
29  
30  import org.apache.log4j.Logger;
31  import org.xnap.XNap;
32  import org.xnap.gui.Dialogs;
33  import org.xnap.gui.XNapFrame;
34  import org.xnap.gui.action.SubmenuAction;
35  import org.xnap.gui.util.*;
36  import org.xnap.peer.Peer;
37  import org.xnap.plugin.Plugin;
38  import org.xnap.plugin.overnet.OvernetPlugin;
39  import org.xnap.plugin.overnet.net.msg.MessageHandler;
40  import org.xnap.plugin.overnet.net.msg.OvernetMessage;
41  import org.xnap.plugin.overnet.net.msg.client.CancelDownloadMessage;
42  import org.xnap.plugin.overnet.net.msg.client.GetGapsMessage;
43  import org.xnap.plugin.overnet.net.msg.client.PauseDownloadMessage;
44  import org.xnap.plugin.overnet.net.msg.client.ResumeDownloadMessage;
45  import org.xnap.plugin.overnet.net.msg.client.SetDownloadPriorityMessage;
46  import org.xnap.plugin.overnet.net.msg.core.GapsMessage;
47  import org.xnap.plugin.overnet.net.msg.core.MessageListener;
48  import org.xnap.plugin.overnet.net.msg.core.NewDownloadMessage;
49  import org.xnap.plugin.overnet.net.msg.core.RemoveDownloadMessage;
50  import org.xnap.plugin.overnet.net.msg.core.UpdateDownloadMessage;
51  import org.xnap.plugin.overnet.util.OvernetPreferences;
52  import org.xnap.transfer.AbstractDownload;
53  import org.xnap.transfer.Segment;
54  import org.xnap.transfer.action.AbstractDeleteAction;
55  import org.xnap.transfer.action.AbstractResumeAction;
56  import org.xnap.transfer.action.AbstractStopAction;
57  import org.xnap.util.Scheduler;
58  import org.xnap.util.XNapTask;
59  
60  public class OvernetDownload extends AbstractDownload 
61  	implements MessageListener
62  {
63  	//--- Constant(s) ---
64  
65  	private static final int STATUS_LOOKING = 2;
66  	private static final int STATUS_DOWNLOADING = 3;
67  	private static final int STATUS_COMPLETING = 9;
68  	private static final int STATUS_COMPLETE = 10;
69  	private static final int STATUS_COMPLETE_CORRUPTED = 11;
70  
71  	private static final String[] STATUS_STRINGS = new String[] {
72  		XNap.tr("Hashing") + "...",
73  		XNap.tr("Queued"),
74  		XNap.tr("Looking" ),
75  		XNap.tr("Downloading") + "...",
76  		XNap.tr("Paused"),
77  		XNap.tr("Insufficient Disk Space."),
78  		XNap.tr("No Sources"),
79  		XNap.tr("Hashing") + "...",
80  		XNap.tr("Error Loading."),
81  		XNap.tr("Completing") + "...",
82  		XNap.tr("Complete"),
83  		XNap.tr("Complete, but corrupted."),
84  		XNap.tr("Transferring") + "...",
85  		XNap.tr("Resuming") + "...",
86  		XNap.tr("Pausing") + "...",
87  	};
88  	
89  	//--- Data Field(s) ---
90  	
91  	private String filename;
92  	private long filesize;
93  	private int status = -1;
94  	private int bytesTransferred;
95  	private int totalBytesTransferred;
96  	private int startSize = -1;
97  	private long currentRate = -1;
98  	private byte[] hash;
99  	private boolean removed = false;
100 	private XNapTask updateTask;
101 	private OvernetSegment[] segments;
102 	private byte priority;
103 	private static OvernetDownload hordeDl = null;
104 
105 	private boolean available = false;
106 	private int sources = 0;
107 
108 	private static Logger logger = Logger.getLogger(OvernetDownload.class);
109 	
110 	//--- Constructor(s) ---
111 
112 	public OvernetDownload(NewDownloadMessage nm)
113 	{
114 		filename = nm.filename;
115 		filesize = nm.filesize;
116 		hash = nm.hash;
117 		priority = nm.priority;
118 
119 		if (priority == NewDownloadMessage.PRIORITY_HIGHEST) {
120 			hordeDl = this;
121 		}
122 
123 		MessageHandler handler = OvernetPlugin.getMessageHandler();
124 		handler.subscribe(RemoveDownloadMessage.TYPE, this);
125 		if (!OvernetPreferences.getInstance().getNewDownloadID()) {
126 			handler.subscribe(GapsMessage.TYPE, this);
127 		}
128 		updateTask = new UpdateGapsListTask();
129 		Scheduler.run(0, 10 * 1000, updateTask);
130 		transferStarted();
131 	}
132 
133 	//--- Method(s) ---
134 
135     public long getTotalBytesTransferred()
136 	{
137 		return totalBytesTransferred;
138 	}
139 
140 	public long getBytesTransferred()
141 	{
142 		return bytesTransferred;
143 	}
144 
145 	public Action[] getActions()
146 	{
147 		return new Action[] { new ResumeAction(), new PauseAction(),
148 							  new DeleteAction(), new PriorityMenuAction(),
149 		};
150 	}
151 
152 	public long getFilesize()
153 	{
154 		return filesize;
155 	}
156 
157 	public String getFilename()
158 	{
159 		return filename;
160 	}
161 
162 	public File getFile()
163 	{
164 		return null;
165 	}
166 
167 	public String getStatus()
168 	{
169 		if (removed && status != STATUS_COMPLETE 
170 			&& status != STATUS_COMPLETING) {
171 			return XNap.tr("Deleted");
172 		}
173 		if (status >=0 && status < STATUS_STRINGS.length) {
174 			return STATUS_STRINGS[status];
175 		}
176 		return XNap.tr("Status unknown");
177 	}
178 
179 	public Icon getIcon()
180 	{
181 		return OvernetPlugin.ICON_16;
182 	}
183 	
184 	public Peer getPeer()
185 	{
186 		return null;
187 	}
188 
189 	public Plugin getPlugin()
190 	{
191 		return OvernetPlugin.getInstance();
192 	}
193 
194 	public boolean isDone()
195 	{
196 		return removed || status == STATUS_COMPLETE;
197 	}
198 
199 	public boolean isRunning()
200 	{
201 		return status == STATUS_DOWNLOADING;
202 	}
203 
204 	public boolean isFailed()
205 	{
206 		return status == STATUS_COMPLETE_CORRUPTED;
207 	}
208 
209 	public long getCurrentRate()
210 	{
211 		return currentRate;
212 	}
213 
214 	private String getPrio()
215 	{
216 		switch (priority) {
217 		case NewDownloadMessage.PRIORITY_LOW:
218 			return XNap.tr("Low");
219 		case NewDownloadMessage.PRIORITY_HIGH:
220 			return XNap.tr("High");
221 		case NewDownloadMessage.PRIORITY_HIGHEST:
222 			return XNap.tr("Highest");
223 		case NewDownloadMessage.PRIORITY_NORMAL:
224 		default:
225 			return XNap.tr("Normal");
226 		}
227 	}
228 
229 	public String getDescription()
230 	{
231 		return "<html><table>"
232 			+ GUIHelper.tableRow(XNap.tr("Filename"), getFilename())
233 			+ GUIHelper.tableRow(XNap.tr("Priority"), getPrio())
234 			+ GUIHelper.tableRow(XNap.tr("Completely Available"), available ? 
235 								 XNap.tr("Yes") : XNap.tr("No"))
236 			+ GUIHelper.tableRow(XNap.tr("Sources"), sources + "")
237 			+ "</table></table>";
238 	}
239 
240 	public void update(UpdateDownloadMessage um)
241 	{
242 		currentRate = (int)(1024 * um.speed);
243 		available = um.availability == 100;
244 		sources = um.sources;
245 
246 		updateStatus((int)um.status);
247 		
248 		totalBytesTransferred = um.transferred;
249 
250 		if (startSize == -1) {
251 			startSize = um.transferred;
252 		}
253 		else {
254 			bytesTransferred = totalBytesTransferred - startSize;
255 		}
256 	}
257 
258 	private void updateStatus(int newStatus)
259 	{
260 		if (newStatus == STATUS_LOOKING && currentRate > 0) {
261 			newStatus = STATUS_DOWNLOADING;
262 		}
263 		if (newStatus != status) {
264 		  status = newStatus;
265 		  stateChanged();
266 		}
267 		if (status == STATUS_COMPLETE) {
268 			transferStopped();
269 		}
270 	}
271 
272 	public void transferStopped()
273 	{
274 		logger.debug("overnet transfer stopped cleanup");
275 		
276 		// do some cleanup here before calling the super method
277 		MessageHandler handler = OvernetPlugin.getMessageHandler();
278 		handler.unsubscribe(RemoveDownloadMessage.TYPE, this);
279 		if (!OvernetPreferences.getInstance().getNewDownloadID()) {
280 			handler.unsubscribe(GapsMessage.TYPE, this);
281 		}
282 		updateTask.cancel();
283 
284 		super.transferStopped();
285 	}
286 
287 	public void messageReceived(OvernetMessage msg)
288 	{
289 		if (msg instanceof RemoveDownloadMessage) {
290 			RemoveDownloadMessage rm = (RemoveDownloadMessage)msg;
291 			if (Arrays.equals(hash, rm.hash)) {
292 				removed = true;
293 				transferStopped();
294 			}
295 		}
296 		else if (msg instanceof GapsMessage) {
297 			GapsMessage gm = (GapsMessage)msg;
298 			if (Arrays.equals(hash, gm.hash)) {
299 				updateGaps(gm);
300 			}
301 		}
302 	}
303 
304 	public void updateGaps(GapsMessage gm)
305 	{
306 		for (int i = 0; i < gm.segments.length; i++) {
307 			gm.segments[i].setTotal(getFilesize());
308 		}
309 		segments = gm.segments;
310 	}
311 
312 	public Segment[] getSegments()
313 	{
314 		return segments;
315 	}
316 
317 	private class UpdateGapsListTask extends XNapTask
318 	{
319 		public void run()
320 		{
321 			OvernetCore.send(new GetGapsMessage(hash));
322 		}
323 	}
324 
325 	private class ResumeAction extends AbstractResumeAction
326 	{
327 		public void actionPerformed(ActionEvent e)
328 		{
329 			OvernetCore.send(new ResumeDownloadMessage(hash));
330 		}
331 	}
332 	
333 	private class PauseAction extends AbstractStopAction
334 	{
335 		public void actionPerformed(ActionEvent e)
336 		{
337 			OvernetCore.send(new PauseDownloadMessage(hash));
338 		}
339 	}
340 
341 	private class DeleteAction extends AbstractDeleteAction
342 	{
343 		public DeleteAction()
344 		{
345 			putValue(Action.NAME, XNap.tr("Delete Transfer"));
346 			putValue(Action.SHORT_DESCRIPTION,
347 					 XNap.tr("Deletes the selected transfers and deletes their files."));
348 			putValue(IconHelper.XNAP_ICON, "editdelete.png");
349 		}
350 
351 		public void actionPerformed(ActionEvent e)
352 		{
353 			if (Dialogs.showConfirmDialog
354 				(XNapFrame.getInstance().getTransferPanel(),
355 				 "DeleteDownloads",
356 				 XNap.tr("Delete Download"),
357 				 XNap.tr("Do you really want to delete\n{0}?", filename),
358 				 JOptionPane.YES_NO_OPTION,
359 				 OvernetPreferences.getInstance()) == JOptionPane.YES_OPTION) {
360 				OvernetCore.send(new CancelDownloadMessage(hash));
361 			}
362 		}
363 	}
364 
365 	private class SetDownloadPriorityAction extends AbstractAction
366 	{
367 		private byte priority;
368 
369 		public SetDownloadPriorityAction(byte priority, String name)
370 		{
371 			this.priority = priority;
372 			putValue(Action.NAME, name);
373 		}
374 
375 		public void actionPerformed(ActionEvent e)
376 		{
377 			OvernetDownload.this.priority = priority;
378 			if (priority == NewDownloadMessage.PRIORITY_HIGHEST) {
379 				if (hordeDl != null && hordeDl != OvernetDownload.this) {
380 					logger.debug("hordeDl != null");
381 					hordeDl.priority = NewDownloadMessage.PRIORITY_HIGH;
382 					hordeDl = OvernetDownload.this;
383 				}
384 			}
385 			OvernetCore.send(new SetDownloadPriorityMessage(hash, priority));
386 		}
387 	}
388 
389 	private class PriorityMenuAction extends SubmenuAction
390 	{
391 		public PriorityMenuAction()
392 		{
393 			super(new Action[] {
394 				new SetDownloadPriorityAction(NewDownloadMessage.PRIORITY_LOW,
395 											  XNap.tr("Low")),
396 				new SetDownloadPriorityAction(NewDownloadMessage.PRIORITY_NORMAL,
397 											  XNap.tr("Normal")),
398 				new SetDownloadPriorityAction(NewDownloadMessage.PRIORITY_HIGH,
399 											  XNap.tr("High")),
400 				new SetDownloadPriorityAction(NewDownloadMessage.PRIORITY_HIGHEST,
401 											  XNap.tr("Highest")),
402 			});
403 			putValue(Action.NAME, XNap.tr("Set Priority"));
404 		}
405 	}
406 }