1 24 25 package com.mckoi.store; 26 27 import java.util.ArrayList ; 28 import java.io.*; 29 30 38 39 public class ScatteringStoreDataAccessor implements StoreDataAccessor { 40 41 46 49 private final File path; 50 51 54 private final String file_name; 55 56 59 private final String first_ext; 60 61 64 private final long max_slice_size; 65 66 70 private ArrayList slice_list; 71 72 75 private long true_file_length; 76 77 80 private final Object lock = new Object ();; 81 82 85 private boolean open = false; 86 87 88 89 92 public ScatteringStoreDataAccessor(File path, String file_name, 93 String first_ext, long max_slice_size) { 94 slice_list = new ArrayList (); 95 this.path = path; 96 this.file_name = file_name; 97 this.first_ext = first_ext; 98 this.max_slice_size = max_slice_size; 99 } 100 101 105 public void convertToScatteringStore(File f) throws IOException { 106 107 int BUFFER_SIZE = 65536; 108 109 RandomAccessFile src = new RandomAccessFile(f, "rw"); 110 long file_size = f.length(); 111 long current_p = max_slice_size; 112 long to_write = Math.min(file_size - current_p, max_slice_size); 113 int write_to_part = 1; 114 115 byte[] copy_buffer = new byte[BUFFER_SIZE]; 116 117 while (to_write > 0) { 118 119 src.seek(current_p); 120 121 File to_f = slicePartFile(write_to_part); 122 if (to_f.exists()) { 123 throw new IOException("Copy error, slice already exists."); 124 } 125 FileOutputStream to_raf = new FileOutputStream(to_f); 126 127 while (to_write > 0) { 128 int size_to_copy = (int) Math.min(BUFFER_SIZE, to_write); 129 130 src.readFully(copy_buffer, 0, size_to_copy); 131 to_raf.write(copy_buffer, 0, size_to_copy); 132 133 current_p += size_to_copy; 134 to_write -= size_to_copy; 135 } 136 137 to_raf.flush(); 138 to_raf.close(); 139 140 to_write = Math.min(file_size - current_p, max_slice_size); 141 ++write_to_part; 142 } 143 144 if (file_size > max_slice_size) { 146 src.seek(0); 147 src.setLength(max_slice_size); 148 } 149 src.close(); 150 151 } 152 153 158 private File slicePartFile(int i) { 159 if (i == 0) { 160 return new File(path, file_name + "." + first_ext); 161 } 162 StringBuffer fn = new StringBuffer (); 163 fn.append(file_name); 164 fn.append("."); 165 if (i < 10) { 166 fn.append("00"); 167 } 168 else if (i < 100) { 169 fn.append("0"); 170 } 171 fn.append(i); 172 return new File(path, fn.toString()); 173 } 174 175 178 private int countStoreFiles() { 179 int i = 0; 180 File f = slicePartFile(i); 181 while (f.exists()) { 182 ++i; 183 f = slicePartFile(i); 184 } 185 return i; 186 } 187 188 191 private StoreDataAccessor createSliceDataAccessor(File file) { 192 return new IOStoreDataAccessor(file); 194 } 195 196 200 private long discoverSize() throws IOException { 201 long running_total = 0; 202 203 synchronized (lock) { 204 int i = 0; 206 File f = slicePartFile(i); 207 while (f.exists()) { 208 running_total += createSliceDataAccessor(f).getSize(); 209 210 ++i; 211 f = slicePartFile(i); 212 } 213 } 214 215 return running_total; 216 } 217 218 220 public void open(boolean read_only) throws IOException { 221 long running_length; 222 223 synchronized (lock) { 224 slice_list = new ArrayList (); 225 226 File f = slicePartFile(0); 228 boolean open_existing = f.exists(); 229 230 if (open_existing && f.length() > max_slice_size) { 233 File f2 = slicePartFile(1); 234 if (f2.exists()) { 235 throw new IOException( 236 "File length exceeds maximum slice size setting."); 237 } 238 if (!read_only) { 240 convertToScatteringStore(f); 241 } 242 else { 243 throw new IOException( 244 "Unable to convert to a scattered store because read-only."); 245 } 246 } 247 248 FileSlice slice = new FileSlice(); 250 slice.data = createSliceDataAccessor(f); 251 slice.data.open(read_only); 252 253 slice_list.add(slice); 254 running_length = slice.data.getSize(); 255 256 if (open_existing) { 259 int i = 1; 260 File slice_part = slicePartFile(i); 261 while (slice_part.exists()) { 262 slice = new FileSlice(); 264 slice.data = createSliceDataAccessor(slice_part); 265 slice.data.open(read_only); 266 267 slice_list.add(slice); 268 running_length += slice.data.getSize(); 269 270 ++i; 271 slice_part = slicePartFile(i); 272 } 273 } 274 275 true_file_length = running_length; 276 277 open = true; 278 } 279 } 280 281 public void close() throws IOException { 282 synchronized (lock) { 283 int sz = slice_list.size(); 284 for (int i = 0; i < sz; ++i) { 285 FileSlice slice = (FileSlice) slice_list.get(i); 286 slice.data.close(); 287 } 288 slice_list = null; 289 open = false; 290 } 291 } 292 293 public boolean delete() { 294 int count_files = countStoreFiles(); 296 for (int i = count_files - 1; i >= 0; --i) { 298 File f = slicePartFile(i); 299 boolean delete_success = createSliceDataAccessor(f).delete(); 300 if (!delete_success) { 301 return false; 302 } 303 } 304 return true; 305 } 306 307 public boolean exists() { 308 return slicePartFile(0).exists(); 309 } 310 311 312 public void read(long position, byte[] buf, int off, int len) 313 throws IOException { 314 while (len > 0) { 316 int file_i = (int) (position / max_slice_size); 317 long file_p = (position % max_slice_size); 318 int file_len = (int) Math.min((long) len, max_slice_size - file_p); 319 320 FileSlice slice; 321 synchronized (lock) { 322 if (file_i < 0 || file_i >= slice_list.size()) { 324 return; 325 } 326 slice = (FileSlice) slice_list.get(file_i); 327 } 328 slice.data.read(file_p, buf, off, file_len); 329 330 position += file_len; 331 off += file_len; 332 len -= file_len; 333 } 334 } 335 336 public void write(long position, byte[] buf, int off, int len) 337 throws IOException { 338 while (len > 0) { 340 int file_i = (int) (position / max_slice_size); 341 long file_p = (position % max_slice_size); 342 int file_len = (int) Math.min((long) len, max_slice_size - file_p); 343 344 FileSlice slice; 345 synchronized (lock) { 346 if (file_i < 0 || file_i >= slice_list.size()) { 348 return; 349 } 350 slice = (FileSlice) slice_list.get(file_i); 351 } 352 slice.data.write(file_p, buf, off, file_len); 353 354 position += file_len; 355 off += file_len; 356 len -= file_len; 357 } 358 } 359 360 public void setSize(long length) throws IOException { 361 synchronized (lock) { 362 long total_size_to_grow = length - true_file_length; 364 if (total_size_to_grow < 0) { 366 throw new IOException("Unable to make the data area size " + 367 "smaller for this type of store."); 368 } 369 370 while (total_size_to_grow > 0) { 371 int last = slice_list.size() - 1; 373 FileSlice slice = (FileSlice) slice_list.get(last); 374 final long old_slice_length = slice.data.getSize(); 375 long to_grow = Math.min(total_size_to_grow, 376 (max_slice_size - old_slice_length)); 377 378 slice.data.setSize(old_slice_length + to_grow); 380 slice.data.synch(); 383 384 total_size_to_grow -= to_grow; 385 if (total_size_to_grow > 0) { 387 File slice_file = slicePartFile(last + 1); 388 389 slice = new FileSlice(); 390 slice.data = createSliceDataAccessor(slice_file); 391 slice.data.open(false); 392 393 slice_list.add(slice); 394 } 395 } 396 true_file_length = length; 397 } 398 399 } 400 401 public long getSize() throws IOException { 402 synchronized (lock) { 403 if (open) { 404 return true_file_length; 405 } 406 else { 407 return discoverSize(); 408 } 409 } 410 } 411 412 public void synch() throws IOException { 413 synchronized (lock) { 414 int sz = slice_list.size(); 415 for (int i = 0; i < sz; ++i) { 416 FileSlice slice = (FileSlice) slice_list.get(i); 417 slice.data.synch(); 418 } 419 } 420 } 421 422 424 429 private static class FileSlice { 430 431 StoreDataAccessor data; 432 433 } 434 435 } 436 437 | Popular Tags |