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.io.BufferedInputStream;
23 import java.io.BufferedOutputStream;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Properties;
34
35 import org.apache.log4j.Logger;
36 import org.xnap.util.FileHelper;
37 import org.xnap.util.Preferences;
38
39 /***
40 * Provides a repository that handles the resume information for
41 * downloads in the incomplete directory.
42 *
43 * <p>This class is mostly thread safe (except for {@link #iterator()}).
44 */
45 public class OpenNapResumeRepository {
46
47
48
49 /***
50 * The prefix used for resume files.
51 */
52 public static final String FILENAME_PREFIX = ".xnap-openap-resume";
53
54
55
56 private static Logger logger
57 = Logger.getLogger(OpenNapResumeRepository.class);
58
59 private List list = new LinkedList();
60
61
62
63 public OpenNapResumeRepository()
64 {
65 }
66
67
68
69 /***
70 * Adds a download to the repository. Creates a resume data file in path.
71 *
72 * @param path the incomplete directory
73 */
74 public synchronized void add
75 (File path, OpenNapDownloadContainerData download)
76 {
77 list.add(download);
78 if (download.resumeFile == null) {
79 try {
80 download.resumeFile
81 = FileHelper.createUnique
82 (path,
83 FILENAME_PREFIX + "." + download.filename + ".xnap3");
84 write(download);
85 }
86 catch(IOException e) {
87 logger.error("Could not create resume file.", e);
88 }
89 }
90 }
91
92 /***
93 * Returns an iterator over all items in the repository.
94 */
95 public Iterator iterator()
96 {
97 return list.iterator();
98 }
99
100 /***
101 * Removes a download from the repository, i.e. when the download
102 * has succeeded.
103 */
104 public synchronized void remove(OpenNapDownloadContainerData download)
105 {
106 if (download.resumeFile != null) {
107 download.resumeFile.delete();
108 }
109 list.remove(download);
110 }
111
112 /***
113 * Restores a download from file.
114 */
115 private synchronized OpenNapDownloadContainerData read(File file)
116 throws IOException
117 {
118 InputStream in = new BufferedInputStream(new FileInputStream(file));
119 try {
120 Properties p = new Properties();
121 p.load(in);
122
123 OpenNapDownloadContainerData data
124 = new OpenNapDownloadContainerData();
125 data.setAutoSearchingEnabled
126 (p.getProperty
127 ("autoSearchingEnabled",
128 Preferences.getInstance().getAlwaysAutoDownload() + ""));
129 data.setFilename(p.getProperty("filename"));
130 data.setFilesize(p.getProperty("filesize"));
131 data.setRealm(p.getProperty("searchRealm"));
132 data.setSearchText(p.getProperty("searchText"));
133
134
135 List segments = new LinkedList();
136 int i = 0;
137 while (p.getProperty("segment." + i + ".filename") != null) {
138 try {
139 OpenNapSegmentData segment = new OpenNapSegmentData();
140 segment.setFile
141 (file.getParentFile(),
142 p.getProperty("segment." + i + ".filename"));
143 segment.setStart
144 (p.getProperty("segment." + i + ".start"));
145 segment.setMergeFailCount
146 (p.getProperty("segment." + i + ".mergeFailCount"));
147
148 segments.add(segment);
149 }
150 catch (IllegalArgumentException e) {
151
152 logger.warn("Invalid segment: " + i + ", "
153 + file.getAbsolutePath());
154 }
155
156 i++;
157 }
158 data.setSegments
159 ((OpenNapSegmentData[])segments.toArray(new OpenNapSegmentData[0]));
160
161 data.resumeFile = file;
162 return data;
163 }
164 catch (IllegalArgumentException e) {
165 throw new IOException("Invalid data");
166 }
167 finally {
168 try {
169 in.close();
170 }
171 catch (IOException e) {
172
173 }
174 }
175 }
176
177 /***
178 * Scans a directory for resume files and adds the found downloads.
179 *
180 * @param path the directory to scan
181 * @see #add(OpenNapDownloadContainerData)
182 */
183 public synchronized void restore(File path)
184 {
185 logger.debug("Restoring resumes from " + path.getAbsolutePath());
186
187 File[] files = path.listFiles();
188 if (files != null) {
189 for (int i = 0; i < files.length; i++) {
190 if (!files[i].isDirectory()
191 && files[i].getName().startsWith(FILENAME_PREFIX)) {
192
193 try {
194 OpenNapDownloadContainerData data = read(files[i]);
195 add(path, data);
196 }
197 catch (IOException e) {
198 logger.warn("Could not restore incomplete file", e);
199 }
200 }
201 }
202 }
203 }
204
205
206 /***
207 * Writes repository content to <code>filename</code>.
208 */
209 public synchronized void save(File path)
210 {
211 for (Iterator i = list.iterator(); i.hasNext();) {
212 try {
213 OpenNapDownloadContainerData data
214 = (OpenNapDownloadContainerData)i.next();
215 write(data);
216 }
217 catch(IOException e) {
218 logger.warn("Could not write resume data", e);
219 }
220 }
221 }
222
223 /***
224 * Saves the resume information for data.
225 */
226 private synchronized void write(OpenNapDownloadContainerData data)
227 throws IOException
228 {
229 OutputStream out
230 = new BufferedOutputStream(new FileOutputStream(data.resumeFile));
231 try {
232 Properties p = new Properties();
233
234 p.setProperty("autoSearchingEnabled",
235 data.autoSearchingEnabled + "");
236 p.setProperty("filename", data.filename);
237 p.setProperty("filesize", data.filesize + "");
238 if (data.realm != null) {
239 p.setProperty("searchRealm", data.realm);
240 }
241 p.setProperty("searchText", data.searchText);
242
243
244 if (data.segments != null) {
245 int c = 0;
246 for (int i = 0; i < data.segments.length; i++) {
247 if (data.segments[i].file == null) {
248
249 continue;
250 }
251
252 p.setProperty
253 ("segment." + c + ".filename",
254 data.segments[i].file.getAbsolutePath());
255 p.setProperty
256 ("segment." + c + ".start",
257 data.segments[i].start + "");
258 p.setProperty
259 ("segment." + c + ".mergeFailCount",
260 data.segments[i].mergeFailCount + "");
261 c++;
262 }
263 }
264
265 p.store(out, "Automatically generated XNap OpenNap resume file - do not modify");
266 }
267 finally {
268 try {
269 out.close();
270 }
271 catch (IOException e) {
272
273 }
274 }
275 }
276
277 }
278