// ============================================================================ // // Copyright (C) 2006-2016 Talend Inc. - www.talend.com // // This source code is available under agreement available at // %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt // // You should have received a copy of the agreement // along with this program; if not, write to Talend SA // 9 rue Pages 92150 Suresnes, France // // ============================================================================ package org.talend.dataquality.semantic.index; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collection; import org.apache.log4j.Logger; import org.apache.lucene.store.BaseDirectory; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockFactory; /** * Implementation of {@link BaseDirectory} which supports accessing Lucene index inside a jar. The inner index files are extracted * to a temporary folder if not present. */ public class JARDirectory extends Directory { private static final Logger LOGGER = Logger.getLogger(JARDirectory.class); private static final Object indexExtractionLock = new Object(); private final JARDescriptor jarDescriptor; final String extractPath; final String indexDirectory; private FSDirectory fsDir; /** * Describes a opened JAR file. */ public static class JARDescriptor { FileSystem fileSystem; String jarFileName; } /** * <p> * Creates a new {@link Directory directory} that looks up for Lucene indexes inside a JAR file. * </p> * <p> * <b>Important #1</b>: Intentionally left private. Use {@link ClassPathDirectory#open(URI)} to create an instance of this. * </p> * <p> * <b>Important #2</b>: URI's scheme "jar" stores content in a temporary directory, and is only cleaned upon * {@link #close()} call. * </p> * * @param extractPath the location for index content extraction * @param descriptor A {@link JARDescriptor descriptor} to the JAR file to open. * @param directory A path inside the opened JAR file. * @see ClassPathDirectory#open(URI) */ public JARDirectory(String extractPath, JARDescriptor descriptor, String directory) { this.extractPath = extractPath; this.jarDescriptor = descriptor; this.indexDirectory = directory; try { extractIndex("default"); // the default context name } catch (IOException e) { LOGGER.error("Failed to extract index: " + e.getMessage(), e); } } private void extractIndex(String contextName) throws IOException { final String unzipFolderName = extractPath + File.separator + indexDirectory + File.separator + contextName + File.separator; final File destinationFolder = Paths.get(unzipFolderName).toFile(); LOGGER.info("Extrating index to temporary directory: " + destinationFolder.getAbsolutePath()); if (destinationFolder.exists()) { // File was already extracted, reuse it fsDir = FSDirectory.open(destinationFolder); } else { // File was not previously extracted to temporary directory, extract it and open it. // To prevent multiple concurrent extracts, lock on class synchronized (indexExtractionLock) { Path start = jarDescriptor.fileSystem.getPath(indexDirectory); if (!destinationFolder.exists()) { destinationFolder.mkdirs(); } Files.walkFileTree(start, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException { Path destFilePath = Paths.get(destinationFolder.toString(), file.getFileName().toString()); Files.copy(file, destFilePath, StandardCopyOption.REPLACE_EXISTING); return FileVisitResult.CONTINUE; } }); fsDir = FSDirectory.open(destinationFolder); } } } @Override public void close() throws IOException { fsDir.close(); } @Override public IndexOutput createOutput(String name, IOContext context) throws IOException { return fsDir.createOutput(name, context); } @Override public void deleteFile(String name) throws IOException { fsDir.deleteFile(name); } @Override public boolean fileExists(String name) throws IOException { return fsDir.fileExists(name); } @Override public long fileLength(String name) throws IOException { return fsDir.fileLength(name); } @Override public String[] listAll() throws IOException { return fsDir.listAll(); } @Override public IndexInput openInput(String arg0, IOContext arg1) throws IOException { return fsDir.openInput(arg0, arg1); } @Override public void sync(Collection<String> arg0) throws IOException { fsDir.sync(arg0); } @Override public void clearLock(String arg0) throws IOException { fsDir.clearLock(arg0); } @Override public LockFactory getLockFactory() { return fsDir.getLockFactory(); } @Override public Lock makeLock(String arg0) { return fsDir.makeLock(arg0); } @Override public void setLockFactory(LockFactory arg0) throws IOException { fsDir.setLockFactory(arg0); } }