/** * Allocates the requested space in the file. * * @param len requested space * @return allocated file position and length as FileEntry object */ private FileEntry allocate(int len) { synchronized (freeList) { // lookup a free entry of sufficient size SortedSet<FileEntry> candidates = freeList.tailSet(new FileEntry(0, len)); for (Iterator<FileEntry> it = candidates.iterator(); it.hasNext(); ) { FileEntry free = it.next(); // ignore entries that are still in use by concurrent readers if (free.isLocked()) continue; // There's no race condition risk between locking the entry on // loading and checking whether it's locked (or store allocation), // because for the entry to be lockable, it needs to be in the // entries collection, in which case it's not in the free list. // The only way an entry can be found in the free list is if it's // been removed, and to remove it, lock on "entries" needs to be // acquired, which is also a pre-requisite for loading data. // found one, remove from freeList it.remove(); return allocateExistingEntry(free, len); } // no appropriate free section available, append at end of file FileEntry fe = new FileEntry(filePos, len); filePos += len; if (trace) log.tracef( "New entry allocated at %d:%d, %d free entries, file size is %d", fe.offset, fe.size, freeList.size(), filePos); return fe; } }
/** * 创建Zipper对象 * * @param out 输出流 * @param filter 文件过滤,不过滤可以为null。 * @param srcFilename 源文件名。可以有多个源文件,如果源文件是目录,那么所有子目录都将被包含。 */ protected Zipper(OutputStream out, List<FileEntry> fileEntrys, String encoding) { Assert.notEmpty(fileEntrys); long begin = System.currentTimeMillis(); log.debug("开始制作压缩包"); try { try { zipOut = new ZipOutputStream(out); if (!StringUtils.isBlank(encoding)) { log.debug("using encoding: {}", encoding); zipOut.setEncoding(encoding); } else { log.debug("using default encoding"); } for (FileEntry fe : fileEntrys) { zip(fe.getFile(), fe.getFilter(), fe.getZipEntry(), fe.getPrefix()); } } finally { zipOut.close(); } } catch (IOException e) { throw new RuntimeException("制作压缩包时,出现IO异常!", e); } long end = System.currentTimeMillis(); log.info("制作压缩包成功。耗时:{}ms。", end - begin); }
@Override public View getView(final int position, final View convertView, final ViewGroup parent) { // We need to get the best view (re-used if possible) and then // retrieve its corresponding ViewHolder, which optimizes lookup efficiency final View view = getWorkingView(convertView); final ViewHolder viewHolder = getViewHolder(view); final FileEntry entry = getItem(position); // Setting the title view is straightforward viewHolder.titleView.setText(entry.getFilename()); // Setting the subTitle view requires a tiny bit of formatting final String formattedSubTitle = String.format( "By %s on %s", entry.getAuthor(), DateFormat.getDateInstance(DateFormat.SHORT).format(entry.getPostDate())); viewHolder.subTitleView.setText(formattedSubTitle); // Setting image view is also simple viewHolder.imageView.setImageResource(entry.getIcon()); return view; }
@Override public void clear() { resizeLock.writeLock().lock(); try { synchronized (entries) { synchronized (freeList) { // wait until all readers are done reading file entries for (FileEntry fe : entries.values()) fe.waitUnlocked(); for (FileEntry fe : freeList) fe.waitUnlocked(); // clear in-memory state entries.clear(); freeList.clear(); // reset file if (trace) log.tracef("Truncating file, current size is %d", filePos); channel.truncate(0); channel.write(ByteBuffer.wrap(MAGIC), 0); filePos = MAGIC.length; } } } catch (Exception e) { throw new PersistenceException(e); } finally { resizeLock.writeLock().unlock(); } }
/** * Coalesces adjacent free entries to create larger free entries (so that the probability of * finding a free entry during allocation increases) */ private void mergeFreeEntries(List<FileEntry> entries) { long startTime = 0; if (trace) startTime = timeService.wallClockTime(); FileEntry lastEntry = null; FileEntry newEntry = null; int mergeCounter = 0; for (Iterator<FileEntry> it = entries.iterator(); it.hasNext(); ) { FileEntry fe = it.next(); if (fe.isLocked()) { continue; } // Merge any holes created (consecutive free entries) in the file if ((lastEntry != null) && (lastEntry.offset == (fe.offset + fe.size))) { if (newEntry == null) { newEntry = new FileEntry(fe.offset, fe.size + lastEntry.size); freeList.remove(lastEntry); mergeCounter++; } else { newEntry = new FileEntry(fe.offset, fe.size + newEntry.size); } freeList.remove(fe); mergeCounter++; } else { if (newEntry != null) { try { addNewFreeEntry(newEntry); if (trace) log.tracef( "Merged %d entries at %d:%d, %d free entries", mergeCounter, newEntry.offset, newEntry.size, freeList.size()); } catch (IOException e) { throw new PersistenceException("Could not add new merged entry", e); } newEntry = null; mergeCounter = 0; } } lastEntry = fe; } if (newEntry != null) { try { addNewFreeEntry(newEntry); if (trace) log.tracef( "Merged %d entries at %d:%d, %d free entries", mergeCounter, newEntry.offset, newEntry.size, freeList.size()); } catch (IOException e) { throw new PersistenceException("Could not add new merged entry", e); } } if (trace) log.tracef( "Total time taken for mergeFreeEntries: " + (timeService.wallClockTime() - startTime) + " (ms)"); }
/** * Initialize the observer. * * @throws Exception if an error occurs */ public void initialize() throws Exception { rootEntry.refresh(rootEntry.getFile()); File[] files = listFiles(rootEntry.getFile()); FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_ENTRIES; for (int i = 0; i < files.length; i++) { children[i] = createFileEntry(rootEntry, files[i]); } rootEntry.setChildren(children); }
/** * Fire directory/file delete events to the registered listeners. * * @param entry The file entry */ private void doDelete(FileEntry entry) { for (FileAlterationListener listener : listeners) { if (entry.isDirectory()) { listener.onDirectoryDelete(entry.getFile()); } else { listener.onFileDelete(entry.getFile()); } } }
/** * Create a new file entry for the specified file. * * @param parent The parent file entry * @param file The file to create an entry for * @return A new file entry */ private FileEntry createFileEntry(FileEntry parent, File file) { FileEntry entry = parent.newChildInstance(file); entry.refresh(file); File[] files = listFiles(file); FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_ENTRIES; for (int i = 0; i < files.length; i++) { children[i] = createFileEntry(entry, files[i]); } entry.setChildren(children); return entry; }
/** * Fire directory/file change events to the registered listeners. * * @param entry The previous file system entry * @param file The current file */ private void doMatch(FileEntry entry, File file) { if (entry.refresh(file)) { for (FileAlterationListener listener : listeners) { if (entry.isDirectory()) { listener.onDirectoryChange(file); } else { listener.onFileChange(file); } } } }
/** * Fire directory/file created events to the registered listeners. * * @param entry The file entry */ private void doCreate(FileEntry entry) { for (FileAlterationListener listener : listeners) { if (entry.isDirectory()) { listener.onDirectoryCreate(entry.getFile()); } else { listener.onFileCreate(entry.getFile()); } } FileEntry[] children = entry.getChildren(); for (FileEntry aChildren : children) { doCreate(aChildren); } }
void assertFileEntryEquals(FileEntry actual, FileEntry expected) { assertEquals( actual.getType(), expected.getType(), "The first column, the entry type, is wrong"); assertEquals( actual.getNewSHA(), expected.getNewSHA(), "The second column, the new SHA-256, is wrong"); assertEquals( actual.getOldSHA(), expected.getOldSHA(), "The third column, the old SHA-256, is wrong"); assertEquals( actual.getFile(), expected.getFile(), "The fourth column, the file name, is wrong"); }
@Override public void purge(Executor threadPool, final PurgeListener task) { long now = timeService.wallClockTime(); List<KeyValuePair<Object, FileEntry>> entriesToPurge = new ArrayList<KeyValuePair<Object, FileEntry>>(); synchronized (entries) { for (Iterator<Map.Entry<K, FileEntry>> it = entries.entrySet().iterator(); it.hasNext(); ) { Map.Entry<K, FileEntry> next = it.next(); FileEntry fe = next.getValue(); if (fe.isExpired(now)) { it.remove(); entriesToPurge.add(new KeyValuePair<Object, FileEntry>(next.getKey(), fe)); } } } resizeLock.readLock().lock(); try { for (Iterator<KeyValuePair<Object, FileEntry>> it = entriesToPurge.iterator(); it.hasNext(); ) { KeyValuePair<Object, FileEntry> next = it.next(); FileEntry fe = next.getValue(); if (fe.isExpired(now)) { it.remove(); try { free(fe); } catch (Exception e) { throw new PersistenceException(e); } if (task != null) task.entryPurged(next.getKey()); } } // Disk space optimizations synchronized (freeList) { processFreeEntries(); } } finally { resizeLock.readLock().unlock(); } }
/** * Compare two file lists for files which have been created, modified or deleted. * * @param parent The parent entry * @param previous The original list of files * @param files The current list of files */ private void checkAndNotify(FileEntry parent, FileEntry[] previous, File[] files) { int c = 0; FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_ENTRIES; for (FileEntry entry : previous) { while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) { current[c] = createFileEntry(parent, files[c]); doCreate(current[c]); c++; } if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) { doMatch(entry, files[c]); checkAndNotify(entry, entry.getChildren(), listFiles(files[c])); current[c] = entry; c++; } else { checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY); doDelete(entry); } } for (; c < files.length; c++) { current[c] = createFileEntry(parent, files[c]); doCreate(current[c]); } parent.setChildren(current); }
/** Check whether the file and its chlidren have been created, modified or deleted. */ public void checkAndNotify() { /* fire onStart() */ for (FileAlterationListener listener : listeners) { listener.onStart(this); } /* fire directory/file events */ File rootFile = rootEntry.getFile(); if (rootFile.exists()) { checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile)); } else if (rootEntry.isExists()) { checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY); } else { // Didn't exist and still doesn't } /* fire onStop() */ for (FileAlterationListener listener : listeners) { listener.onStop(this); } }
/** Removes free entries towards the end of the file and truncates the file. */ private void truncateFile(List<FileEntry> entries) { long startTime = 0; if (trace) startTime = timeService.wallClockTime(); int reclaimedSpace = 0; int removedEntries = 0; long truncateOffset = -1; for (Iterator<FileEntry> it = entries.iterator(); it.hasNext(); ) { FileEntry fe = it.next(); // Till we have free entries at the end of the file, // we can remove them and contract the file to release disk // space. if (!fe.isLocked() && ((fe.offset + fe.size) == filePos)) { truncateOffset = fe.offset; filePos = fe.offset; freeList.remove(fe); it.remove(); reclaimedSpace += fe.size; removedEntries++; } else { break; } } if (truncateOffset > 0) { try { channel.truncate(truncateOffset); } catch (IOException e) { throw new PersistenceException("Error while truncating file", e); } } if (trace) { log.tracef("Removed entries: " + removedEntries + ", Reclaimed Space: " + reclaimedSpace); log.tracef( "Time taken for truncateFile: " + (timeService.wallClockTime() - startTime) + " (ms)"); } }
private MarshalledEntry<K, V> _load(Object key, boolean loadValue, boolean loadMetadata) { final FileEntry fe; resizeLock.readLock().lock(); try { synchronized (entries) { // lookup FileEntry of the key fe = entries.get(key); if (fe == null) return null; // Entries are removed due to expiration from {@link SingleFileStore#purge} if (fe.isExpired(timeService.wallClockTime())) { return null; } else { // lock entry for reading before releasing entries monitor fe.lock(); } } } finally { resizeLock.readLock().unlock(); } org.infinispan.commons.io.ByteBuffer valueBb = null; org.infinispan.commons.io.ByteBuffer metadataBb = null; // If we only require the key, then no need to read disk if (!loadValue && !loadMetadata) { try { return ctx.getMarshalledEntryFactory().newMarshalledEntry(key, valueBb, metadataBb); } finally { fe.unlock(); } } final byte[] data; try { // load serialized data from disk data = new byte[fe.keyLen + fe.dataLen + (loadMetadata ? fe.metadataLen : 0)]; // The entry lock will prevent clear() from truncating the file at this point channel.read(ByteBuffer.wrap(data), fe.offset + KEY_POS); } catch (Exception e) { throw new PersistenceException(e); } finally { // No need to keep the lock for deserialization. // FileEntry is immutable, so its members can't be changed by another thread. fe.unlock(); } if (trace) log.tracef("Read entry %s at %d:%d", key, fe.offset, fe.actualSize()); ByteBufferFactory factory = ctx.getByteBufferFactory(); org.infinispan.commons.io.ByteBuffer keyBb = factory.newByteBuffer(data, 0, fe.keyLen); if (loadValue) { valueBb = factory.newByteBuffer(data, fe.keyLen, fe.dataLen); } if (loadMetadata && fe.metadataLen > 0) { metadataBb = factory.newByteBuffer(data, fe.keyLen + fe.dataLen, fe.metadataLen); } return ctx.getMarshalledEntryFactory().newMarshalledEntry(keyBb, valueBb, metadataBb); }
/** * Construct an observer for the specified directory, file filter and file comparator. * * @param rootEntry the root directory to observe * @param fileFilter The file filter or null if none * @param caseSensitivity what case sensitivity to use comparing file names, null means system * sensitive */ protected FileAlterationObserver( FileEntry rootEntry, FileFilter fileFilter, IOCase caseSensitivity) { if (rootEntry == null) { throw new IllegalArgumentException("Root entry is missing"); } if (rootEntry.getFile() == null) { throw new IllegalArgumentException("Root directory is missing"); } this.rootEntry = rootEntry; this.fileFilter = fileFilter; if (caseSensitivity == null || caseSensitivity.equals(IOCase.SYSTEM)) { this.comparator = NameFileComparator.NAME_SYSTEM_COMPARATOR; } else if (caseSensitivity.equals(IOCase.INSENSITIVE)) { this.comparator = NameFileComparator.NAME_INSENSITIVE_COMPARATOR; } else { this.comparator = NameFileComparator.NAME_COMPARATOR; } }
/** * The base class implementation calls {@link #load(Object)} for this, we can do better because we * keep all keys in memory. */ @Override public boolean contains(Object key) { FileEntry entry = entries.get(key); return entry != null && !entry.isExpired(timeService.wallClockTime()); }
/** * Return the directory being observed. * * @return the directory being observed */ public File getDirectory() { return rootEntry.getFile(); }