File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\store\fs\FilePathSplit.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.store.fs;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 import java.io.SequenceInputStream;
13 import java.nio.ByteBuffer;
14 import java.nio.channels.FileChannel;
15 import java.nio.channels.FileLock;
16 import java.util.ArrayList;
17 import java.util.List;
18 import org.h2.constant.SysProperties;
19 import org.h2.message.DbException;
20 import org.h2.util.New;
21
22 /**
23 * A file system that may split files into multiple smaller files.
24 * (required for a FAT32 because it only support files up to 2 GB).
25 */
26 public class FilePathSplit extends FilePathWrapper {
27
28 private static final String PART_SUFFIX = ".part";
29
30 protected String getPrefix() {
31 return getScheme() + ":" + parse(name)[0] + ":";
32 }
33
34 public FilePath unwrap(String fileName) {
35 return FilePath.get(parse(fileName)[1]);
36 }
37
38 public boolean setReadOnly() {
39 boolean result = false;
40 for (int i = 0;; i++) {
41 FilePath f = getBase(i);
42 if (f.exists()) {
43 result = f.setReadOnly();
44 } else {
45 break;
46 }
47 }
48 return result;
49 }
50
51 public void delete() {
52 for (int i = 0;; i++) {
53 FilePath f = getBase(i);
54 if (f.exists()) {
55 f.delete();
56 } else {
57 break;
58 }
59 }
60 }
61
62 public long lastModified() {
63 long lastModified = 0;
64 for (int i = 0;; i++) {
65 FilePath f = getBase(i);
66 if (f.exists()) {
67 long l = f.lastModified();
68 lastModified = Math.max(lastModified, l);
69 } else {
70 break;
71 }
72 }
73 return lastModified;
74 }
75
76 public long size() {
77 long length = 0;
78 for (int i = 0;; i++) {
79 FilePath f = getBase(i);
80 if (f.exists()) {
81 length += f.size();
82 } else {
83 break;
84 }
85 }
86 return length;
87 }
88
89 public ArrayList<FilePath> newDirectoryStream() {
90 List<FilePath> list = getBase().newDirectoryStream();
91 ArrayList<FilePath> newList = New.arrayList();
92 for (int i = 0, size = list.size(); i < size; i++) {
93 FilePath f = list.get(i);
94 if (!f.getName().endsWith(PART_SUFFIX)) {
95 newList.add(wrap(f));
96 }
97 }
98 return newList;
99 }
100
101 public InputStream newInputStream() throws IOException {
102 InputStream input = getBase().newInputStream();
103 for (int i = 1;; i++) {
104 FilePath f = getBase(i);
105 if (f.exists()) {
106 InputStream i2 = f.newInputStream();
107 input = new SequenceInputStream(input, i2);
108 } else {
109 break;
110 }
111 }
112 return input;
113 }
114
115 public FileChannel open(String mode) throws IOException {
116 ArrayList<FileChannel> list = New.arrayList();
117 list.add(getBase().open(mode));
118 for (int i = 1;; i++) {
119 FilePath f = getBase(i);
120 if (f.exists()) {
121 list.add(f.open(mode));
122 } else {
123 break;
124 }
125 }
126 FileChannel[] array = new FileChannel[list.size()];
127 list.toArray(array);
128 long maxLength = array[0].size();
129 long length = maxLength;
130 if (array.length == 1) {
131 long defaultMaxLength = getDefaultMaxLength();
132 if (maxLength < defaultMaxLength) {
133 maxLength = defaultMaxLength;
134 }
135 } else {
136 if (maxLength == 0) {
137 closeAndThrow(0, array, array[0], maxLength);
138 }
139 for (int i = 1; i < array.length - 1; i++) {
140 FileChannel c = array[i];
141 long l = c.size();
142 length += l;
143 if (l != maxLength) {
144 closeAndThrow(i, array, c, maxLength);
145 }
146 }
147 FileChannel c = array[array.length - 1];
148 long l = c.size();
149 length += l;
150 if (l > maxLength) {
151 closeAndThrow(array.length - 1, array, c, maxLength);
152 }
153 }
154 return new FileSplit(this, mode, array, length, maxLength);
155 }
156
157 private long getDefaultMaxLength() {
158 return 1L << Integer.decode(parse(name)[0]).intValue();
159 }
160
161 private void closeAndThrow(int id, FileChannel[] array, FileChannel o, long maxLength) throws IOException {
162 String message = "Expected file length: " + maxLength + " got: " + o.size() + " for " + getName(id);
163 for (FileChannel f : array) {
164 f.close();
165 }
166 throw new IOException(message);
167 }
168
169 public OutputStream newOutputStream(boolean append) {
170 try {
171 return new FileChannelOutputStream(open("rw"), append);
172 } catch (IOException e) {
173 throw DbException.convertIOException(e, name);
174 }
175 }
176
177 public void moveTo(FilePath path) {
178 FilePathSplit newName = (FilePathSplit) path;
179 for (int i = 0;; i++) {
180 FilePath o = getBase(i);
181 if (o.exists()) {
182 o.moveTo(newName.getBase(i));
183 } else {
184 break;
185 }
186 }
187 }
188
189 /**
190 * Split the file name into size and base file name.
191 *
192 * @param fileName the file name
193 * @return an array with size and file name
194 */
195 private String[] parse(String fileName) {
196 if (!fileName.startsWith(getScheme())) {
197 DbException.throwInternalError(fileName + " doesn't start with " + getScheme());
198 }
199 fileName = fileName.substring(getScheme().length() + 1);
200 String size;
201 if (fileName.length() > 0 && Character.isDigit(fileName.charAt(0))) {
202 int idx = fileName.indexOf(':');
203 size = fileName.substring(0, idx);
204 try {
205 fileName = fileName.substring(idx + 1);
206 } catch (NumberFormatException e) {
207 // ignore
208 }
209 } else {
210 size = Long.toString(SysProperties.SPLIT_FILE_SIZE_SHIFT);
211 }
212 return new String[] { size, fileName };
213 }
214
215 /**
216 * Get the file name of a part file.
217 *
218 * @param id the part id
219 * @return the file name including the part id
220 */
221 FilePath getBase(int id) {
222 return FilePath.get(getName(id));
223 }
224
225 private String getName(int id) {
226 return id > 0 ? getBase().name + "." + id + PART_SUFFIX : getBase().name;
227 }
228
229 public String getScheme() {
230 return "split";
231 }
232
233 }
234
235 /**
236 * A file that may be split into multiple smaller files.
237 */
238 class FileSplit extends FileBase {
239
240 private final FilePathSplit file;
241 private final String mode;
242 private final long maxLength;
243 private FileChannel[] list;
244 private long filePointer;
245 private long length;
246
247 FileSplit(FilePathSplit file, String mode, FileChannel[] list, long length, long maxLength) {
248 this.file = file;
249 this.mode = mode;
250 this.list = list;
251 this.length = length;
252 this.maxLength = maxLength;
253 }
254
255 public void implCloseChannel() throws IOException {
256 for (FileChannel c : list) {
257 c.close();
258 }
259 }
260
261 public long position() {
262 return filePointer;
263 }
264
265 public long size() {
266 return length;
267 }
268
269 public int read(ByteBuffer dst) throws IOException {
270 int len = dst.remaining();
271 if (len == 0) {
272 return 0;
273 }
274 len = (int) Math.min(len, length - filePointer);
275 if (len <= 0) {
276 return -1;
277 }
278 long offset = filePointer % maxLength;
279 len = (int) Math.min(len, maxLength - offset);
280 FileChannel channel = getFileChannel();
281 channel.position(offset);
282 len = channel.read(dst);
283 filePointer += len;
284 return len;
285 }
286
287 public FileChannel position(long pos) {
288 filePointer = pos;
289 return this;
290 }
291
292 private FileChannel getFileChannel() throws IOException {
293 int id = (int) (filePointer / maxLength);
294 while (id >= list.length) {
295 int i = list.length;
296 FileChannel[] newList = new FileChannel[i + 1];
297 System.arraycopy(list, 0, newList, 0, i);
298 FilePath f = file.getBase(i);
299 newList[i] = f.open(mode);
300 list = newList;
301 }
302 return list[id];
303 }
304
305 public FileChannel truncate(long newLength) throws IOException {
306 if (newLength >= length) {
307 return this;
308 }
309 filePointer = Math.min(filePointer, newLength);
310 int newFileCount = 1 + (int) (newLength / maxLength);
311 if (newFileCount < list.length) {
312 // delete some of the files
313 FileChannel[] newList = new FileChannel[newFileCount];
314 // delete backwards, so that truncating is somewhat transactional
315 for (int i = list.length - 1; i >= newFileCount; i--) {
316 // verify the file is writable
317 list[i].truncate(0);
318 list[i].close();
319 try {
320 file.getBase(i).delete();
321 } catch (DbException e) {
322 throw DbException.convertToIOException(e);
323 }
324 }
325 System.arraycopy(list, 0, newList, 0, newList.length);
326 list = newList;
327 }
328 long size = newLength - maxLength * (newFileCount - 1);
329 list[list.length - 1].truncate(size);
330 this.length = newLength;
331 return this;
332 }
333
334 public void force(boolean metaData) throws IOException {
335 for (FileChannel c : list) {
336 c.force(metaData);
337 }
338 }
339
340 public int write(ByteBuffer src) throws IOException {
341 if (filePointer >= length && filePointer > maxLength) {
342 // may need to extend and create files
343 long oldFilePointer = filePointer;
344 long x = length - (length % maxLength) + maxLength;
345 for (; x < filePointer; x += maxLength) {
346 if (x > length) {
347 // expand the file size
348 position(x - 1);
349 write(ByteBuffer.wrap(new byte[1]));
350 }
351 filePointer = oldFilePointer;
352 }
353 }
354 long offset = filePointer % maxLength;
355 int len = src.remaining();
356 FileChannel channel = getFileChannel();
357 channel.position(offset);
358 int l = (int) Math.min(len, maxLength - offset);
359 if (l == len) {
360 l = channel.write(src);
361 } else {
362 int oldLimit = src.limit();
363 src.limit(src.position() + l);
364 l = channel.write(src);
365 src.limit(oldLimit);
366 }
367 filePointer += l;
368 length = Math.max(length, filePointer);
369 return l;
370 }
371
372 public synchronized FileLock tryLock(long position, long size, boolean shared) throws IOException {
373 return list[0].tryLock(position, size, shared);
374 }
375
376 public String toString() {
377 return file.toString();
378 }
379
380 }
381