1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.plugin.jtella;
21
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.net.HttpURLConnection;
29 import java.net.URL;
30 import java.util.Hashtable;
31
32 import javax.swing.Action;
33
34 import org.apache.log4j.Logger;
35 import org.xnap.io.ThrottledInputStream;
36 import org.xnap.peer.Peer;
37 import org.xnap.plugin.Plugin;
38 import org.xnap.transfer.AbstractTransfer;
39 import org.xnap.transfer.Download;
40 import org.xnap.util.FileHelper;
41 import org.xnap.util.FiniteStateMachine;
42 import org.xnap.util.IllegalOperationException;
43 import org.xnap.util.State;
44
45 import com.kenmccrary.jtella.SearchReplyMessage;
46
47 /***
48 * Downloads a file.
49 */
50 public class JTellaDownload extends AbstractTransfer implements Download {
51
52
53
54 private static final Hashtable TRANSITION_TABLE;
55 static {
56 State[][] table = new State[][] {
57 { State.NOT_STARTED,
58 State.CONNECTING, },
59 { State.CONNECTING,
60 State.DOWNLOADING, State.ABORTING, State.FAILED, },
61 { State.DOWNLOADING,
62 State.SUCCEEDED, State.ABORTING, State.FAILED, },
63 { State.ABORTING,
64 State.ABORTED }
65 };
66
67 TRANSITION_TABLE = FiniteStateMachine.createStateTable(table);
68 }
69
70
71
72 private static Logger logger = Logger.getLogger(JTellaDownload.class);
73
74 private File file;
75 private JTellaSearchResult result;
76 private long bytesTransferred = 0;
77 private StateMachine sm = new StateMachine();
78 private DownloadRunner runner;
79
80
81
82 public JTellaDownload(JTellaSearchResult result)
83 {
84 this.result = result;
85 }
86
87
88
89 public long getBytesTransferred()
90 {
91 return bytesTransferred;
92 }
93
94 public Action[] getActions()
95 {
96 return null;
97 }
98
99 public File getFile()
100 {
101 return file;
102 }
103
104 public String getFilename()
105 {
106 return result.getFilename();
107 }
108
109 public long getFilesize()
110 {
111 return result.getFilesize();
112 }
113
114 public Peer getPeer()
115 {
116 return result.getPeer();
117 }
118
119 public Plugin getPlugin()
120 {
121 return JTellaPlugin.getInstance();
122 }
123
124 public String getStatus()
125 {
126 return sm.getDescription();
127 }
128
129 public long getTotalBytesTransferred()
130 {
131 return bytesTransferred;
132 }
133
134 public boolean isDone()
135 {
136 State s = sm.getState();
137 return s == State.SUCCEEDED || s == State.ABORTED || s == State.FAILED;
138 }
139
140 public boolean isFailed()
141 {
142 State s = sm.getState();
143 return s == State.FAILED;
144 }
145
146 public boolean isRunning()
147 {
148 State s = sm.getState();
149 return s == State.CONNECTING || s == State.DOWNLOADING
150 || s == State.ABORTING;
151 }
152
153 public void start()
154 {
155 setState(State.CONNECTING);
156 }
157
158 private void setState(State newState, String description)
159 {
160 sm.setState(newState, description);
161 stateChanged();
162 }
163
164 private void setState(State newState)
165 {
166 sm.setState(newState);
167 stateChanged();
168 }
169
170
171
172 private class StateMachine extends FiniteStateMachine
173 {
174
175
176
177 public StateMachine()
178 {
179 super(State.NOT_STARTED, TRANSITION_TABLE);
180 }
181
182
183
184 protected synchronized void stateChanged(State oldState,
185 State newState)
186 {
187 if (newState == State.CONNECTING) {
188 runner = new DownloadRunner();
189 Thread t
190 = new Thread(runner, "JTellaDownload:"
191 + getFilename());
192 t.start();
193 return;
194 }
195 else if (oldState == State.CONNECTING) {
196 if (newState == State.ABORTING) {
197 runner.die = true;
198 return;
199 }
200 }
201 }
202
203 }
204
205 private class DownloadRunner implements Runnable
206 {
207
208
209
210 private boolean die = false;
211
212
213
214 public String getFilenameRequest()
215 {
216 StringBuffer sb = new StringBuffer();
217 sb.append("/get/");
218 sb.append(result.getFileRecord().getIndex());
219 sb.append("/");
220 sb.append(result.getFilename());
221 sb.append("/");
222 return sb.toString();
223 }
224
225 public void run()
226 {
227 HttpURLConnection connection = null;
228 InputStream in = null;
229 try {
230 SearchReplyMessage reply = result.getReply();
231 URL url = new URL("http", reply.getIPAddress(),
232 reply.getPort(), getFilenameRequest());
233
234 connection = (HttpURLConnection)url.openConnection();
235
236 if (die) {
237 return;
238 }
239
240 if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
241 in = new ThrottledInputStream(connection.getInputStream());
242 download(in);
243 }
244 else {
245 throw new IOException(connection.getResponseMessage());
246 }
247
248 setState(State.SUCCEEDED);
249 }
250 catch (IOException e) {
251 try {
252 logger.warn("connection failed", e);
253 setState(State.FAILED, e.getLocalizedMessage());
254 }
255 catch (IllegalOperationException e2) {
256 }
257 }
258 finally {
259 if (in != null) {
260 try {
261 in.close();
262 }
263 catch (IOException e2) {
264 }
265 }
266 if (connection != null) {
267 connection.disconnect();
268 }
269 }
270 }
271
272 public void download(InputStream in) throws IOException
273 {
274 OutputStream out = null;
275 try {
276 file = FileHelper.createIncompleteFile(result.getFilename());
277 out = new BufferedOutputStream(new FileOutputStream(file));
278
279 setState(State.DOWNLOADING);
280
281 byte[] data = new byte[512];
282 while (!die && bytesTransferred < getFilesize()) {
283
284 long toRead = getFilesize() - bytesTransferred;
285 int len = (int)Math.min(toRead, data.length);
286
287 len = in.read(data, 0, len);
288
289 if (len == -1) {
290 break;
291 }
292
293 out.write(data, 0, len);
294 bytesTransferred += len;
295 }
296 out.flush();
297 }
298 finally {
299 transferStopped();
300
301 if (out != null) {
302 try {
303 out.close();
304 }
305 catch (IOException e) {
306 }
307 }
308 }
309 }
310
311 }
312
313 }