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  
21  package org.xnap.plugin.opennap.net;
22  
23  import java.io.IOException;
24  
25  import org.xnap.plugin.opennap.util.OpenNapFileHelper;
26  import org.xnap.transfer.Segment;
27  
28  /***
29   * 
30   */
31  public class OpenNapSegmentManager {
32  
33  	//--- Constant(s) ---
34  	
35  	/***
36  	 * The number of bytes two segments need to overlap.
37  	 */
38  	public static final int OVERLAP = 100;
39  	
40  	//--- Data Field(s) ---
41  
42  	private OpenNapSegment root;
43  	private Segment[] segments = new Segment[0];
44  	private OpenNapDownloadContainer parent;
45  	private boolean multiSourceDownloading;
46  
47      //--- Constructor(s) ---
48  
49      public OpenNapSegmentManager
50  		(OpenNapDownloadContainer parent,
51  		 boolean multiSourceDownloading,
52  		 OpenNapSegmentData[] data) 
53      {
54  		this.parent = parent;
55  		this.multiSourceDownloading = multiSourceDownloading;
56  
57  		if (data != null && data.length > 0) {
58  			if (data[0].start != 0 
59  				|| data[0].file.length() > parent.getFilesize()) {
60  				// invalid segment
61  				return;
62  			}
63  
64  			// use the first segment as root segment
65  			root = new OpenNapSegment(data[0], parent.getFilesize());
66  			add(root);
67  
68  			// restore segments
69  			OpenNapSegment previous = root;
70  			for (int i = 1; i < data.length; i++) {
71  				long minStart = previous.getStart() 
72  					+ previous.getTransferred() - OVERLAP;
73  
74  				if (data[i].start < minStart
75  					|| (data[i].start + data[i].file.length() 
76  						> parent.getFilesize())) {
77  					// invalid segment
78  					return;
79  				}
80  
81  				// append new segment
82  				OpenNapSegment next 
83  					= new OpenNapSegment(data[i], parent.getFilesize());
84  				previous.append(next);
85  				add(next);
86  
87  				previous = next;
88  			}
89  	    }
90  
91      }
92  
93      public OpenNapSegmentManager
94  		(OpenNapDownloadContainer parent, 
95  		 boolean multiSourceDownloading) 
96      {
97  		this(parent, multiSourceDownloading, null);
98      }
99  
100     //--- Methods ---
101 
102 	public long getBytesTransferred()
103 	{
104 		long bytesTransferred = 0;
105 		long previousEnd = 0;
106 		for (int i = 0; i < segments.length; i++) {
107 			bytesTransferred += segments[i].getTransferred();
108 			// subtract overlap
109 			bytesTransferred -= previousEnd - segments[i].getStart();
110 			previousEnd += segments[i].getEnd();
111 		}
112 		return bytesTransferred;
113 	}
114 	
115 	public OpenNapSegment getRoot()
116 	{
117 		return root;
118 	}
119 
120 	/***
121      * 
122      */
123     public Segment[] getSegments() 
124 	{
125 		return segments;
126     }
127 
128 	synchronized OpenNapSegment requestSegment(OpenNapDownload d)
129 	{
130 		if (root == null) {
131 			root = new OpenNapSegment
132 				(0, parent.getFilesize(), parent.getFilesize());
133 			root.setDownload(d);
134 			add(root);
135 			return root;
136 		}
137 
138 		if (!multiSourceDownloading) {
139 			// do not create any segments besides root if root is busy
140 			if (root.getDownload() == null) {
141 				root.setDownload(d);
142 				return root;
143 			}
144 			else {
145 				return null;
146 			}
147 		}
148 
149 		OpenNapSegment next = root;
150 		OpenNapSegment slowest = null;
151 		while (next != null) {
152 			if (!next.isFinished()) {
153 				if (next.getDownload() == null) {
154 					next.setDownload(d);
155 					return next;
156 				}
157 				else {
158 					if (slowest == null
159 						|| (next.getDownload().getAverageRate() 
160 							< slowest.getDownload().getAverageRate())) {
161 						slowest = next;
162 					}
163 				}
164 			}
165 			next = next.getNext();
166 		}
167 
168 		if (slowest != null) {
169 			// no free segment found, split the slowest
170 			OpenNapSegment s = slowest.split(OVERLAP);
171 
172 			if (s == null) {
173 				// too bad, we couldn't split, try the next best
174 				next = root;
175 				while (s == null && next != null) {
176 					if (!next.isFinished() && next.getDownload() == null) {
177 						s = next.split(OVERLAP);
178 					}
179 					next = next.getNext();
180 				}
181 			}
182 
183 			if (s != null) {
184 				s.setDownload(d);
185 				add(s);
186 				return s;
187 			}
188 		}
189 
190 		return null;
191 	}
192 
193 	synchronized void returnSegment(OpenNapSegment s) throws IOException
194 	{
195 		s.setDownload(null);
196 		
197 		merge(s, s.getNext());
198 		merge(s.getPrevious(), s);
199 	}
200 
201 	private synchronized boolean merge(OpenNapSegment p, OpenNapSegment s) 
202 		throws IOException
203 	{
204 		if (p == null 
205 			|| p.getFile() == null 
206 			|| p.getDownload() != null
207 			|| !p.isFinished() 
208 			|| s == null 
209 			|| s.getFile() == null 
210 			|| s.getDownload() != null) {
211 			// one of the segments is not ready to be merged
212 			return false;
213 		}
214 			
215 		int overlap = (int)(p.getEnd() - s.getStart());
216 		OpenNapFileHelper.match(p.getFile(), s.getFile(), overlap);
217 		OpenNapFileHelper.append(p.getFile(), s.getFile(), overlap);
218 
219 		s.getFile().delete();
220 
221 		// merge s into p and dump s
222 		p.merge(s, overlap);
223 		remove(s);
224 
225 		return true;
226 	}
227 
228 	private synchronized void add(OpenNapSegment segment)
229 	{
230 	    OpenNapSegment[] tmp = new OpenNapSegment[segments.length + 1];
231 		System.arraycopy(segments, 0, tmp, 0, segments.length);
232 		tmp[tmp.length - 1] = segment;
233 		segments = tmp;
234 		parent.setSegments(tmp);
235 	}
236 
237 	private synchronized void remove(OpenNapSegment segment)
238 	{
239 		for (int i = 0; i < segments.length; i++) {
240 			if (segments[i] == segment) {
241 				OpenNapSegment[] tmp = new OpenNapSegment[segments.length - 1];
242 				System.arraycopy(segments, 0, tmp, 0, i);
243 				System.arraycopy(segments, i + 1, tmp, i, 
244 								 segments.length - i - 1);
245 				segments = tmp;
246 				parent.setSegments(tmp);
247 				return;
248 			}
249 		}
250 	}
251 
252 }