package marubinotto.piggydb.impl; import static marubinotto.util.CollectionUtils.set; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Set; import marubinotto.piggydb.model.FileRepository; import marubinotto.piggydb.model.Fragment; import marubinotto.util.Assert; import marubinotto.util.FileSystemUtils; import marubinotto.util.ZipUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.TrueFileFilter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tools.zip.ZipOutputStream; public class FileRepositoryImpl extends FileRepository.Base { private static Log logger = LogFactory.getLog(FileRepositoryImpl.class); private static final String DIR_SUFFIX = "-files"; private File baseDirectory; private FileRepository delegate; public void setDatabasePath(String databasePath) throws MalformedURLException { Assert.Arg.notNull(databasePath, "databasePath"); this.delegate = null; if (databasePath.startsWith("mem:")) { logger.info("Memory mode"); this.delegate = new FileRepository.InMemory(); } else if (databasePath.startsWith("file:")) { this.baseDirectory = FileUtils.toFile(new URL(databasePath + DIR_SUFFIX)); } else if (databasePath.startsWith("~/")) { String path = System.getProperty("user.home") + databasePath.substring(1) + DIR_SUFFIX; this.baseDirectory = new File(path); } else { throw new IllegalArgumentException("Invalid database path: " + databasePath); } if (this.baseDirectory != null) logger.info("baseDirectory: " + this.baseDirectory.getAbsolutePath()); } public void setBaseDirectory(File baseDirectory) { Assert.Arg.notNull(baseDirectory, "baseDirectory"); Assert.require(baseDirectory.isDirectory(), "baseDirectory.isDirectory()"); this.baseDirectory = baseDirectory; this.delegate = null; } public File getBaseDirectory() { return this.baseDirectory; } public void putFile(Fragment fragment) throws Exception { Assert.Arg.notNull(fragment, "fragment"); Assert.Arg.notNull(fragment.getId(), "fragment.getId()"); Assert.Arg.notNull(fragment.getFileInput(), "fragment.getFileInput()"); if (this.delegate != null) { this.delegate.putFile(fragment); return; } ensureBaseDirectoryCreated(); deleteOldFile(fragment.getId()); File filePath = getFragmentFilePath(fragment); logger.info("Putting a file: " + filePath); fragment.getFileInput().write(filePath); } private void deleteOldFile(Long id) { File noExtension = new File(this.baseDirectory, id.toString()); if (noExtension.isFile()) { logger.info("Deleting a file: " + noExtension); noExtension.delete(); } final String prefix = id.toString() + "."; File[] filesWithExtension = this.baseDirectory.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.startsWith(prefix); } }); for (File file : filesWithExtension) { logger.info("Deleting a file: " + file); file.delete(); } } public void getFile(OutputStream output, Fragment fragment) throws Exception { Assert.Arg.notNull(output, "output"); Assert.Arg.notNull(fragment, "fragment"); Assert.Arg.notNull(fragment.getId(), "fragment.getId()"); if (this.delegate != null) { this.delegate.getFile(output, fragment); return; } ensureBaseDirectoryCreated(); File filePath = getFragmentFilePath(fragment); InputStream input = FileUtils.openInputStream(filePath); try { IOUtils.copyLarge(input, output); } finally { IOUtils.closeQuietly(input); } } public int size() throws Exception { if (this.delegate != null) return this.delegate.size(); ensureBaseDirectoryCreated(); return this.baseDirectory.listFiles().length; } public Set<String>getFileNames() throws Exception { if (this.delegate != null) return this.delegate.getFileNames(); ensureBaseDirectoryCreated(); return set(this.baseDirectory.list()); } public void outputAll(String namePrefix, ZipOutputStream zipOut) throws Exception { Assert.Arg.notNull(namePrefix, "namePrefix"); Assert.Arg.notNull(zipOut, "zipOut"); if (this.delegate != null) { logger.debug("Delegating outputAll ..."); this.delegate.outputAll(namePrefix, zipOut); return; } ensureBaseDirectoryCreated(); ZipUtils.appendDirectory( namePrefix, getBaseDirectory(), getBaseDirectory(), TrueFileFilter.INSTANCE, zipOut); } public void clear() throws Exception { if (this.delegate != null) { this.delegate.clear(); return; } ensureBaseDirectoryCreated(); FileUtils.cleanDirectory(this.baseDirectory); } public ZipUtils.EntryReader getEntryReader() throws Exception { if (this.delegate != null) return this.delegate.getEntryReader(); ensureBaseDirectoryCreated(); return new ZipUtils.Directory(this.baseDirectory); } public void deleteFile(Fragment fragment) throws Exception { Assert.Arg.notNull(fragment, "fragment"); Assert.Arg.notNull(fragment.getId(), "fragment.getId()"); if (this.delegate != null) { this.delegate.deleteFile(fragment); return; } ensureBaseDirectoryCreated(); File filePath = getFragmentFilePath(fragment); logger.info("Deleting file: " + filePath); FileSystemUtils.forceDeleteIfExist(filePath); } // Internal private void ensureBaseDirectoryCreated() throws IOException { Assert.Property.requireNotNull(baseDirectory, "baseDirectory"); if (!this.baseDirectory.isDirectory()) { logger.info("Creating baseDirectory: " + this.baseDirectory.getAbsolutePath()); FileUtils.forceMkdir(this.baseDirectory); } } private File getFragmentFilePath(Fragment fragment) { Assert.Property.requireNotNull(baseDirectory, "baseDirectory"); return new File(this.baseDirectory, getFragmentFileKey(fragment)); } }