diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index a844220f2fc..803fb89a3ef 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -137,7 +137,7 @@ Other Changes * SOLR-17321: Minimum Java version for Apache Solr is now 21, and for SolrJ, it is 17. (Sanjay Dutt, David Smiley) -* SOLR-16903: Update CLI tools to use java.nio.file.Path instead of java.io.File (Andrey Bozhko) +* SOLR-16903: Update CLI tools and Solr Core to use java.nio.file.Path instead of java.io.File (Andrey Bozhko, Matthew Biscocho) * SOLR-17568: SolrCloud no longer reroutes/proxies a core request to another node if not found locally. (David Smiley) diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java b/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java index 68654851d5b..4a1b6ca2af9 100644 --- a/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java +++ b/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java @@ -17,10 +17,10 @@ package org.apache.solr.cloud; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; +import java.nio.file.FileSystems; import java.nio.file.Path; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.cloud.ZooKeeperException; @@ -121,7 +121,9 @@ public InputStream openResource(String resource) throws IOException { try { // delegate to the class loader (looking into $INSTANCE_DIR/lib jars) - is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/')); + is = + classLoader.getResourceAsStream( + resource.replace(FileSystems.getDefault().getSeparator(), "/")); } catch (Exception e) { throw new IOException("Error opening " + resource, e); } diff --git a/solr/core/src/java/org/apache/solr/core/CachingDirectoryFactory.java b/solr/core/src/java/org/apache/solr/core/CachingDirectoryFactory.java index dfe9487809e..a84663c3b51 100644 --- a/solr/core/src/java/org/apache/solr/core/CachingDirectoryFactory.java +++ b/solr/core/src/java/org/apache/solr/core/CachingDirectoryFactory.java @@ -16,7 +16,6 @@ */ package org.apache.solr.core; -import java.io.File; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.nio.file.DirectoryStream; @@ -367,10 +366,7 @@ private void close(CacheValue val) { } private static boolean isSubPath(CacheValue cacheValue, CacheValue otherCacheValue) { - int one = cacheValue.path.lastIndexOf(File.separatorChar); - int two = otherCacheValue.path.lastIndexOf(File.separatorChar); - - return otherCacheValue.path.startsWith(cacheValue.path + File.separatorChar) && two > one; + return Path.of(otherCacheValue.path).startsWith(Path.of(cacheValue.path)); } @Override diff --git a/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java b/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java index 6109b2f860b..27d91b4d90e 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java +++ b/solr/core/src/java/org/apache/solr/core/CoreDescriptor.java @@ -16,11 +16,11 @@ */ package org.apache.solr.core; -import java.io.File; import java.io.IOException; import java.io.Reader; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; @@ -64,7 +64,7 @@ public class CoreDescriptor { public static final String SOLR_CORE_PROP_PREFIX = "solr.core."; public static final String DEFAULT_EXTERNAL_PROPERTIES_FILE = - "conf" + File.separator + "solrcore.properties"; + "conf" + FileSystems.getDefault().getSeparator() + "solrcore.properties"; /** * Whether this core was configured using a configSet that was trusted. This helps in avoiding the @@ -96,7 +96,7 @@ public Properties getPersistableUserProperties() { CORE_CONFIG, "solrconfig.xml", CORE_SCHEMA, "schema.xml", CORE_CONFIGSET_PROPERTIES, ConfigSetProperties.DEFAULT_FILENAME, - CORE_DATADIR, "data" + File.separator, + CORE_DATADIR, "data" + FileSystems.getDefault().getSeparator(), CORE_TRANSIENT, "false", CORE_LOADONSTARTUP, "true"); diff --git a/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java b/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java index aebbd64b7a1..33274fcd969 100644 --- a/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java +++ b/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java @@ -17,17 +17,15 @@ package org.apache.solr.core; import java.io.Closeable; -import java.io.File; -import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.stream.Stream; import org.apache.commons.io.file.PathUtils; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FilterDirectory; @@ -338,59 +336,68 @@ public String getDataHome(CoreDescriptor cd) throws IOException { } public void cleanupOldIndexDirectories( - final String dataDirPath, final String currentIndexDirPath, boolean afterCoreReload) { + final String dataDirPath, final String currentIndexDirPath, boolean afterCoreReload) + throws IOException { - // TODO SOLR-8282 move to PATH - File dataDir = new File(dataDirPath); - if (!dataDir.isDirectory()) { + Path dataDirFile = Path.of(dataDirPath); + if (!Files.isDirectory(dataDirFile)) { log.debug( "{} does not point to a valid data directory; skipping clean-up of old index directories.", dataDirPath); return; } - final File currentIndexDir = new File(currentIndexDirPath); - File[] oldIndexDirs = - dataDir.listFiles( - new FileFilter() { - @Override - public boolean accept(File file) { - String fileName = file.getName(); - return file.isDirectory() - && !file.equals(currentIndexDir) - && (fileName.equals("index") || fileName.matches(INDEX_W_TIMESTAMP_REGEX)); - } - }); + final Path currentIndexDir = Path.of(currentIndexDirPath); + List dirsList; + try (Stream oldIndexDirs = Files.list(dataDirFile)) { + dirsList = + oldIndexDirs + .filter( + (file) -> { + String fileName = file.getFileName().toString(); + return Files.isDirectory(file) + && !file.equals(currentIndexDir) + && (fileName.equals("index") || fileName.matches(INDEX_W_TIMESTAMP_REGEX)); + }) + .sorted(Comparator.reverseOrder()) + .toList(); + } + ; - if (oldIndexDirs == null || oldIndexDirs.length == 0) + if (dirsList.isEmpty()) { return; // nothing to do (no log message needed) - - List dirsList = Arrays.asList(oldIndexDirs); - dirsList.sort(Collections.reverseOrder()); + } int i = 0; if (afterCoreReload) { - log.info("Will not remove most recent old directory after reload {}", oldIndexDirs[0]); + if (log.isInfoEnabled()) + log.info("Will not remove most recent old directory after reload {}", dirsList.getFirst()); i = 1; } - log.info( - "Found {} old index directories to clean-up under {} afterReload={}", - oldIndexDirs.length - i, - dataDirPath, - afterCoreReload); - for (; i < dirsList.size(); i++) { - File dir = dirsList.get(i); - String dirToRmPath = dir.getAbsolutePath(); - try { - if (deleteOldIndexDirectory(dirToRmPath)) { - log.info("Deleted old index directory: {}", dirToRmPath); - } else { - log.warn("Delete old index directory {} failed.", dirToRmPath); - } - } catch (IOException ioExc) { - log.error("Failed to delete old directory {} due to: ", dir.getAbsolutePath(), ioExc); - } - } + + if (log.isInfoEnabled()) + log.info( + "Found {} old index directories to clean-up under {} afterReload={}", + dirsList.size() - i, + dataDirPath, + afterCoreReload); + + dirsList.stream() + .skip(i) + .forEach( + (entry) -> { + String dirToRmPath = entry.toAbsolutePath().toString(); + try { + if (deleteOldIndexDirectory(dirToRmPath)) { + log.info("Deleted old index directory: {}", dirToRmPath); + } else { + log.warn("Delete old index directory {} failed.", dirToRmPath); + } + } catch (IOException ioExc) { + log.error( + "Failed to delete old directory {} due to: ", entry.toAbsolutePath(), ioExc); + } + }); } // Extension point to allow subclasses to infuse additional code when deleting old index diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index 3611b943feb..720a751f07a 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -22,7 +22,6 @@ import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; import java.io.Closeable; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -33,9 +32,9 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -1354,14 +1353,32 @@ public void initializeMetrics(SolrMetricsContext parentContext, String scope) { Category.CORE.toString()); } - // TODO SOLR-8282 move to PATH // initialize disk total / free metrics - Path dataDirPath = Paths.get(dataDir); - File dataDirFile = dataDirPath.toFile(); + Path dataDirPath = Path.of(dataDir); parentContext.gauge( - () -> dataDirFile.getTotalSpace(), true, "totalSpace", Category.CORE.toString(), "fs"); + () -> { + try { + return Files.getFileStore(dataDirPath).getTotalSpace(); + } catch (IOException e) { + return 0L; + } + }, + true, + "totalSpace", + Category.CORE.toString(), + "fs"); parentContext.gauge( - () -> dataDirFile.getUsableSpace(), true, "usableSpace", Category.CORE.toString(), "fs"); + () -> { + try { + return Files.getFileStore(dataDirPath).getUsableSpace(); + } catch (IOException e) { + return 0L; + } + }, + true, + "usableSpace", + Category.CORE.toString(), + "fs"); parentContext.gauge( () -> dataDirPath.toAbsolutePath().toString(), true, diff --git a/solr/core/src/java/org/apache/solr/core/SolrPaths.java b/solr/core/src/java/org/apache/solr/core/SolrPaths.java index 7906c1e219f..59aa0076437 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrPaths.java +++ b/solr/core/src/java/org/apache/solr/core/SolrPaths.java @@ -17,8 +17,8 @@ package org.apache.solr.core; -import java.io.File; import java.lang.invoke.MethodHandles; +import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; @@ -44,7 +44,7 @@ private SolrPaths() {} // don't create this /** Ensures a directory name always ends with a '/'. */ public static String normalizeDir(String path) { return (path != null && (!(path.endsWith("/") || path.endsWith("\\")))) - ? path + File.separator + ? path + FileSystems.getDefault().getSeparator() : path; } diff --git a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java index 11f61a12ae2..6d1065a0e54 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java +++ b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java @@ -18,7 +18,6 @@ import com.google.common.annotations.VisibleForTesting; import java.io.Closeable; -import java.io.File; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; @@ -31,6 +30,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; @@ -373,12 +373,16 @@ public InputStream openResource(String resource) throws IOException { // Delegate to the class loader (looking into $INSTANCE_DIR/lib jars). // We need a ClassLoader-compatible (forward-slashes) path here! - InputStream is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/')); + InputStream is = + classLoader.getResourceAsStream( + resource.replace(FileSystems.getDefault().getSeparator(), "/")); // This is a hack just for tests (it is not done in ZKResourceLoader)! // TODO can we nuke this? if (is == null && System.getProperty("jetty.testMode") != null) { - is = classLoader.getResourceAsStream(("conf/" + resource).replace(File.separatorChar, '/')); + is = + classLoader.getResourceAsStream( + ("conf/" + resource.replace(FileSystems.getDefault().getSeparator(), "/"))); } if (is == null) { @@ -405,7 +409,8 @@ public String resourceLocation(String resource) { } try (InputStream is = - classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/'))) { + classLoader.getResourceAsStream( + resource.replace(FileSystems.getDefault().getSeparator(), "/"))) { if (is != null) return "classpath:" + resource; } catch (IOException e) { // ignore diff --git a/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java b/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java index df3353a7b31..37a18e08243 100644 --- a/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java +++ b/solr/core/src/java/org/apache/solr/core/backup/BackupManager.java @@ -16,7 +16,6 @@ */ package org.apache.solr.core.backup; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -24,6 +23,7 @@ import java.lang.invoke.MethodHandles; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; import java.time.Instant; import java.util.Collections; import java.util.List; @@ -343,7 +343,8 @@ private void downloadConfigToRepo(ConfigSetService configSetService, String conf // getAllConfigFiles always separates file paths with '/' for (String filePath : filePaths) { // Replace '/' to ensure that propre file is resolved for writing. - URI uri = repository.resolve(dir, filePath.replace('/', File.separatorChar)); + URI uri = + repository.resolve(dir, filePath.replace("/", FileSystems.getDefault().getSeparator())); // checking for '/' is correct for a directory since ConfigSetService#getAllConfigFiles // always separates file paths with '/' if (!filePath.endsWith("/")) { diff --git a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java index 1a462dee26e..1847b05f950 100644 --- a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java +++ b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java @@ -22,7 +22,6 @@ import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -43,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Stream; import net.jcip.annotations.NotThreadSafe; import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.client.HttpClient; @@ -80,19 +80,21 @@ public DistribFileStore(CoreContainer coreContainer) { } @Override - public Path getRealpath(String path) { + public Path getRealPath(String path) { return _getRealPath(path, solrHome); } - private static Path _getRealPath(String path, Path solrHome) { - if (File.separatorChar == '\\') { - path = path.replace('/', File.separatorChar); - } - SolrPaths.assertNotUnc(Path.of(path)); - while (path.startsWith(File.separator)) { // Trim all leading slashes - path = path.substring(1); + private static Path _getRealPath(String dir, Path solrHome) { + Path path = Path.of(dir); + SolrPaths.assertNotUnc(path); + + if (path.isAbsolute()) { + // Strip the path of from being absolute to become relative to resolve with SolrHome + path = path.subpath(0, path.getNameCount()); } + var finalPath = getFileStoreDirPath(solrHome).resolve(path); + // Guard against path traversal by asserting final path is sub path of filestore if (!finalPath.normalize().startsWith(getFileStoreDirPath(solrHome).normalize())) { throw new SolrException(BAD_REQUEST, "Illegal path " + path); @@ -111,7 +113,7 @@ class FileInfo { ByteBuffer getFileData(boolean validate) throws IOException { if (fileData == null) { - fileData = ByteBuffer.wrap(Files.readAllBytes(getRealpath(path))); + fileData = ByteBuffer.wrap(Files.readAllBytes(getRealPath(path))); } return fileData; } @@ -136,7 +138,7 @@ private void persistToFile(ByteBuffer data, ByteBuffer meta) throws IOException } public boolean exists(boolean validateContent, boolean fetchMissing) throws IOException { - Path file = getRealpath(path); + Path file = getRealPath(path); if (!Files.exists(file)) { if (fetchMissing) { return fetchFromAnyNode(); @@ -166,7 +168,7 @@ public boolean exists(boolean validateContent, boolean fetchMissing) throws IOEx private void deleteFile() { try { - IOUtils.deleteFilesIfExist(getRealpath(path), getRealpath(getMetaPath())); + IOUtils.deleteFilesIfExist(getRealPath(path), getRealPath(getMetaPath())); } catch (IOException e) { log.error("Unable to delete files: {}", path); } @@ -245,14 +247,14 @@ boolean fetchFromAnyNode() { } public Path realPath() { - return getRealpath(path); + return getRealPath(path); } @SuppressWarnings("unchecked") MetaData readMetaData() throws IOException { - File file = getRealpath(getMetaPath()).toFile(); - if (file.exists()) { - try (InputStream fis = new FileInputStream(file)) { + Path file = getRealPath(getMetaPath()); + if (Files.exists(file)) { + try (InputStream fis = Files.newInputStream(file)) { return new MetaData((Map) Utils.fromJSON(fis)); } } @@ -428,10 +430,10 @@ public boolean fetch(String path, String from) { @Override public void get(String path, Consumer consumer, boolean fetchmissing) throws IOException { - File file = getRealpath(path).toFile(); - String simpleName = file.getName(); + Path file = getRealPath(path); + String simpleName = file.getFileName().toString(); if (isMetaDataFile(simpleName)) { - try (InputStream is = new FileInputStream(file)) { + try (InputStream is = Files.newInputStream(file)) { consumer.accept( new FileEntry(null, null, path) { // no metadata for metadata file @@ -459,24 +461,26 @@ public void syncToAllNodes(String path) throws IOException { @Override public List list(String path, Predicate predicate) { - File file = getRealpath(path).toFile(); + Path file = getRealPath(path); List fileDetails = new ArrayList<>(); FileType type = getType(path, false); if (type == FileType.DIRECTORY) { - file.list( - (dir, name) -> { - if (predicate == null || predicate.test(name)) { - if (!isMetaDataFile(name)) { - fileDetails.add(new FileInfo(path + "/" + name).getDetails()); + try (Stream fileStream = Files.list(file)) { + fileStream.forEach( + (f) -> { + String fileName = f.getFileName().toString(); + if (predicate == null || predicate.test(fileName)) { + if (!isMetaDataFile(fileName)) { + fileDetails.add(new FileInfo(path + "/" + fileName).getDetails()); + } } - } - return false; - }); - + }); + } catch (IOException e) { + throw new SolrException(SERVER_ERROR, "Error listing files from provided path " + path, e); + } } else if (type == FileType.FILE) { fileDetails.add(new FileInfo(path).getDetails()); } - return fileDetails; } @@ -550,19 +554,19 @@ public void refresh(String path) { @Override public FileType getType(String path, boolean fetchMissing) { - File file = getRealpath(path).toFile(); - if (!file.exists() && fetchMissing) { + Path file = getRealPath(path); + if (!Files.exists(file) && fetchMissing) { if (fetch(path, null)) { - file = getRealpath(path).toFile(); + file = getRealPath(path); } } return _getFileType(file); } - public static FileType _getFileType(File file) { - if (!file.exists()) return FileType.NOFILE; - if (file.isDirectory()) return FileType.DIRECTORY; - return isMetaDataFile(file.getName()) ? FileType.METADATA : FileType.FILE; + public static FileType _getFileType(Path file) { + if (!Files.exists(file)) return FileType.NOFILE; + if (Files.isDirectory(file)) return FileType.DIRECTORY; + return isMetaDataFile(file.getFileName().toString()) ? FileType.METADATA : FileType.FILE; } public static boolean isMetaDataFile(String file) { @@ -620,13 +624,20 @@ public Map getKeys() throws IOException { private static Map _getKeys(Path solrHome) throws IOException { Map result = new HashMap<>(); Path keysDir = _getRealPath(ClusterFileStore.KEYS_DIR, solrHome); - - File[] keyFiles = keysDir.toFile().listFiles(); - if (keyFiles == null) return result; - for (File keyFile : keyFiles) { - if (keyFile.isFile() && !isMetaDataFile(keyFile.getName())) { - result.put(keyFile.getName(), Files.readAllBytes(keyFile.toPath())); - } + try (Stream fileStream = Files.list(keysDir)) { + fileStream.forEach( + (keyFile) -> { + if (Files.isRegularFile(keyFile) && !isMetaDataFile(keyFile.getFileName().toString())) { + try { + result.put(keyFile.getFileName().toString(), Files.readAllBytes(keyFile)); + } catch (IOException e) { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + "Unable to read all bytes file: " + keyFile.getFileName(), + e); + } + } + }); } return result; } diff --git a/solr/core/src/java/org/apache/solr/filestore/FileStore.java b/solr/core/src/java/org/apache/solr/filestore/FileStore.java index 916b8b33b3b..acdf979496d 100644 --- a/solr/core/src/java/org/apache/solr/filestore/FileStore.java +++ b/solr/core/src/java/org/apache/solr/filestore/FileStore.java @@ -50,7 +50,7 @@ public interface FileStore { void syncToAllNodes(String path) throws IOException; /** get the real path on filesystem */ - Path getRealpath(String path); + Path getRealPath(String path); /** The type of the resource */ FileType getType(String path, boolean fetchMissing); diff --git a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java index a5a467386db..80c4ba6f1f9 100644 --- a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java +++ b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java @@ -37,7 +37,6 @@ import static org.apache.solr.handler.admin.api.ReplicationAPIBase.GENERATION; import static org.apache.solr.handler.admin.api.ReplicationAPIBase.OFFSET; -import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -1061,25 +1060,23 @@ private void reloadCore() { } private void downloadConfFiles( - List> confFilesToDownload, long latestGeneration) throws Exception { + List> confFilesToDownload, long latestGeneration) { log.info("Starting download of configuration files from leader: {}", confFilesToDownload); confFilesDownloaded = Collections.synchronizedList(new ArrayList<>()); - Path tmpConfPath = + Path tmpConfDir = solrCore.getResourceLoader().getConfigPath().resolve("conf." + getDateAsStr(new Date())); - // TODO SOLR-8282 move to PATH - File tmpconfDir = tmpConfPath.toFile(); try { - boolean status = tmpconfDir.mkdirs(); - if (!status) { + try { + Files.createDirectories(tmpConfDir); + } catch (Exception e) { throw new SolrException( SolrException.ErrorCode.SERVER_ERROR, - "Failed to create temporary config folder: " + tmpconfDir.getName()); + "Failed to create temporary config folder: " + tmpConfDir.getFileName()); } for (Map file : confFilesToDownload) { String saveAs = (String) (file.get(ALIAS) == null ? file.get(NAME) : file.get(ALIAS)); localFileFetcher = - new LocalFsFileFetcher( - tmpconfDir.toPath(), file, saveAs, CONF_FILE_SHORT, latestGeneration); + new LocalFsFileFetcher(tmpConfDir, file, saveAs, CONF_FILE_SHORT, latestGeneration); currentFile = file; localFileFetcher.fetchFile(); confFilesDownloaded.add(new HashMap<>(file)); @@ -1087,9 +1084,13 @@ private void downloadConfFiles( // this is called before copying the files to the original conf dir // so that if there is an exception avoid corrupting the original files. terminateAndWaitFsyncService(); - copyTmpConfFiles2Conf(tmpConfPath); + copyTmpConfFiles2Conf(tmpConfDir); + + } catch (Exception e) { + throw new SolrException( + ErrorCode.SERVER_ERROR, "Failed to download configuration files from leader", e); } finally { - delTree(tmpconfDir.toPath()); + delTree(tmpConfDir); } } @@ -1156,23 +1157,22 @@ private long downloadIndexFiles( alwaysDownload); } if (!compareResult.equal || downloadCompleteIndex || alwaysDownload) { - // TODO SOLR-8282 move to PATH - File localFile = new File(indexDirPath, filename); + Path localFile = Path.of(indexDirPath, filename); if (downloadCompleteIndex && doDifferentialCopy && compareResult.equal && compareResult.checkSummed - && localFile.exists()) { + && Files.exists(localFile)) { if (log.isInfoEnabled()) { log.info( "Don't need to download this file. Local file's path is: {}, checksum is: {}", - localFile.getAbsolutePath(), + localFile.toAbsolutePath(), file.get(CHECKSUM)); } // A hard link here should survive the eventual directory move, and should be more space // efficient as compared to a file copy. TODO: Maybe we could do a move safely here? - Files.createLink(Path.of(tmpIndexDirPath, filename), localFile.toPath()); - bytesSkippedCopying += localFile.length(); + Files.createLink(Path.of(tmpIndexDirPath, filename), localFile); + bytesSkippedCopying += Files.size(localFile); } else { dirFileFetcher = new DirectoryFileFetcher( @@ -1484,6 +1484,7 @@ private void copyTmpConfFiles2Conf(Path tmpconfDir) { int numTempPathElements = tmpconfDir.getNameCount(); for (Path path : makeTmpConfDirFileList(tmpconfDir)) { Path oldPath = confPath.resolve(path.subpath(numTempPathElements, path.getNameCount())); + try { Files.createDirectories(oldPath.getParent()); } catch (IOException e) { @@ -1491,21 +1492,16 @@ private void copyTmpConfFiles2Conf(Path tmpconfDir) { ErrorCode.SERVER_ERROR, "Unable to mkdirs: " + oldPath.getParent(), e); } if (Files.exists(oldPath)) { - File oldFile = oldPath.toFile(); // TODO SOLR-8282 move to PATH - File backupFile = - new File(oldFile.getPath() + "." + getDateAsStr(new Date(oldFile.lastModified()))); - if (!backupFile.getParentFile().exists()) { - status = backupFile.getParentFile().mkdirs(); - if (!status) { - throw new SolrException( - ErrorCode.SERVER_ERROR, "Unable to mkdirs: " + backupFile.getParentFile()); - } - } - status = oldFile.renameTo(backupFile); - if (!status) { + try { + Path backupFile = + oldPath.resolveSibling( + oldPath.getFileName() + + "." + + getDateAsStr(new Date(Files.getLastModifiedTime(oldPath).toMillis()))); + Files.move(oldPath, backupFile); + } catch (Exception e) { throw new SolrException( - SolrException.ErrorCode.SERVER_ERROR, - "Unable to rename: " + oldFile + " to: " + backupFile); + SolrException.ErrorCode.SERVER_ERROR, "Unable to backup old file: " + oldPath, e); } } try { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java index c35117fb088..85ad5cdc4a9 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminHandler.java @@ -25,7 +25,6 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Expiry; import com.github.benmanes.caffeine.cache.Ticker; -import java.io.File; import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collection; @@ -319,13 +318,6 @@ protected static Map buildCoreParams(SolrParams params) { return coreParams; } - protected static String normalizePath(String path) { - if (path == null) return null; - path = path.replace('/', File.separatorChar); - path = path.replace('\\', File.separatorChar); - return path; - } - public static ModifiableSolrParams params(String... params) { ModifiableSolrParams msp = new ModifiableSolrParams(); for (int i = 0; i < params.length; i += 2) { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java index 0aafba7c70b..67bebb00efd 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/CoreAdminOperation.java @@ -45,7 +45,7 @@ import static org.apache.solr.common.params.CoreAdminParams.SHARD; import static org.apache.solr.handler.admin.CoreAdminHandler.CallInfo; import static org.apache.solr.handler.admin.CoreAdminHandler.buildCoreParams; -import static org.apache.solr.handler.admin.CoreAdminHandler.normalizePath; +import static org.apache.solr.util.FileUtils.normalizeToOsPathSeparator; import java.io.IOException; import java.lang.invoke.MethodHandles; @@ -357,7 +357,7 @@ public static NamedList getCoreStatus( if (core != null) { info.add(NAME, core.getName()); info.add("instanceDir", core.getInstancePath().toString()); - info.add("dataDir", normalizePath(core.getDataDir())); + info.add("dataDir", normalizeToOsPathSeparator(core.getDataDir())); info.add("config", core.getConfigResource()); info.add("schema", core.getSchemaResource()); info.add("startTime", core.getStartTimeStamp()); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java index 1ec536f3c22..340107e762f 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/ShowFileRequestHandler.java @@ -16,7 +16,6 @@ */ package org.apache.solr.handler.admin; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.invoke.MethodHandles; @@ -28,6 +27,7 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.stream.Stream; import org.apache.solr.cloud.ZkSolrResourceLoader; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; @@ -195,54 +195,65 @@ private void showFromZooKeeper( } // Return the file indicated (or the directory listing) from the local file system. - private void showFromFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) { - Path admin = getAdminFileFromFileSystem(req, rsp, hiddenFiles); + private void showFromFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException { + Path adminFile = getAdminFileFromFileSystem(req, rsp, hiddenFiles); - if (admin == null) { // exception already recorded + if (adminFile == null) { // exception already recorded return; } - // TODO SOLR-8282 move to PATH - File adminFile = admin.toFile(); // Make sure the file exists, is readable and is not a hidden file - if (!adminFile.exists()) { - log.error("Can not find: {} [{}]", adminFile.getName(), adminFile.getAbsolutePath()); + if (!Files.exists(adminFile)) { + log.error("Can not find: {} [{}]", adminFile.getFileName(), adminFile.toAbsolutePath()); rsp.setException( new SolrException( ErrorCode.NOT_FOUND, - "Can not find: " + adminFile.getName() + " [" + adminFile.getAbsolutePath() + "]")); + "Can not find: " + + adminFile.getFileName() + + " [" + + adminFile.toAbsolutePath() + + "]")); return; } - if (!adminFile.canRead() || adminFile.isHidden()) { - log.error("Can not show: {} [{}]", adminFile.getName(), adminFile.getAbsolutePath()); + if (!Files.isReadable(adminFile) || Files.isHidden(adminFile)) { + log.error("Can not show: {} [{}]", adminFile.getFileName(), adminFile.toAbsolutePath()); rsp.setException( new SolrException( ErrorCode.NOT_FOUND, - "Can not show: " + adminFile.getName() + " [" + adminFile.getAbsolutePath() + "]")); + "Can not show: " + + adminFile.getFileName() + + " [" + + adminFile.toAbsolutePath() + + "]")); return; } // Show a directory listing - if (adminFile.isDirectory()) { + if (Files.isDirectory(adminFile)) { // it's really a directory, just go for it. - int basePath = adminFile.getAbsolutePath().length() + 1; NamedList> files = new SimpleOrderedMap<>(); - for (File f : adminFile.listFiles()) { - String path = f.getAbsolutePath().substring(basePath); - path = path.replace('\\', '/'); // normalize slashes - - if (isHiddenFile(req, rsp, f.getName().replace('\\', '/'), false, hiddenFiles)) { - continue; - } - - SimpleOrderedMap fileInfo = new SimpleOrderedMap<>(); - files.add(path, fileInfo); - if (f.isDirectory()) { - fileInfo.add("directory", true); - } else { - // TODO? content type - fileInfo.add("size", f.length()); - } - fileInfo.add("modified", new Date(f.lastModified())); + try (Stream directoryFiles = Files.list(adminFile)) { + directoryFiles.forEach( + (f) -> { + if (isHiddenFile(req, rsp, f.getFileName().toString(), false, hiddenFiles)) { + return; + } + String path = f.getFileName().toString(); + SimpleOrderedMap fileInfo = new SimpleOrderedMap<>(); + files.add(path, fileInfo); + if (Files.isDirectory(f)) { + fileInfo.add("directory", true); + } else { + try { + fileInfo.add("size", Files.size(f)); + fileInfo.add("modified", new Date(Files.getLastModifiedTime(f).toMillis())); + } catch (Exception e) { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + "Unable to retrieve file metadata: " + f, + e); + } + } + }); } rsp.add("files", files); } else { @@ -252,7 +263,7 @@ private void showFromFileSystem(SolrQueryRequest req, SolrQueryResponse rsp) { params.set(CommonParams.WT, "raw"); req.setParams(params); - ContentStreamBase content = new ContentStreamBase.FileStream(adminFile.toPath()); + ContentStreamBase content = new ContentStreamBase.FileStream(adminFile); content.setContentType(getSafeContentType(req.getParams().get(USE_CONTENT_TYPE))); rsp.add(RawResponseWriter.CONTENT, content); @@ -399,6 +410,10 @@ public final Set getHiddenFiles() { return hiddenFiles; } + public static String toForwardSlashPathString(String path) { + return path.replace('\\', '/'); + } + //////////////////////// SolrInfoMBeans methods ////////////////////// @Override diff --git a/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java b/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java index 0d5294f1ae6..5e25d5f6fce 100644 --- a/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java +++ b/solr/core/src/java/org/apache/solr/pkg/SolrPackageLoader.java @@ -279,7 +279,7 @@ public void writeMap(EntryWriter ew) throws IOException { throw new RuntimeException("Cannot load package: " + errs); } for (String file : version.files) { - paths.add(coreContainer.getFileStore().getRealpath(file)); + paths.add(coreContainer.getFileStore().getRealPath(file)); } loader = diff --git a/solr/core/src/java/org/apache/solr/rest/ManagedResourceStorage.java b/solr/core/src/java/org/apache/solr/rest/ManagedResourceStorage.java index 038b9c5f421..b2f449aeb86 100644 --- a/solr/core/src/java/org/apache/solr/rest/ManagedResourceStorage.java +++ b/solr/core/src/java/org/apache/solr/rest/ManagedResourceStorage.java @@ -20,7 +20,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -203,9 +202,8 @@ public OutputStream openOutputStream(String storedResourceId) throws IOException @Override public boolean delete(String storedResourceId) throws IOException { - // TODO SOLR-8282 move to PATH - File storedFile = new File(storageDir, storedResourceId); - return deleteIfFile(storedFile.toPath()); + Path storedFile = Path.of(storageDir, storedResourceId); + return deleteIfFile(storedFile); } // TODO: this interface should probably be changed, this simulates the old behavior, diff --git a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java index df44cda12e2..dce62c012e5 100644 --- a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java +++ b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java @@ -17,7 +17,6 @@ package org.apache.solr.schema; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; @@ -401,8 +400,7 @@ private void upgradeToManagedSchema() { "On upgrading to managed schema, did not rename non-managed schema '{}' because it's the same as the managed schema's name.", resourceName); } else { - // TODO SOLR-8282 move to PATH - final File nonManagedSchemaFile = locateConfigFile(resourceName).toFile(); + final Path nonManagedSchemaFile = locateConfigFile(resourceName); if (null == nonManagedSchemaFile) { // Don't throw an exception for failure to rename the non-managed schema log.warn( @@ -412,17 +410,19 @@ private void upgradeToManagedSchema() { "nor under SolrConfig.getConfigDir() or the current directory. ", "PLEASE REMOVE THIS FILE."); } else { - File upgradedSchemaFile = new File(nonManagedSchemaFile + UPGRADED_SCHEMA_EXTENSION); - if (nonManagedSchemaFile.renameTo(upgradedSchemaFile)) { + Path upgradedSchemaFile = + nonManagedSchemaFile.resolveSibling( + nonManagedSchemaFile.getFileName() + UPGRADED_SCHEMA_EXTENSION); + try { + Files.move(nonManagedSchemaFile, upgradedSchemaFile); // Set the resource name to the managed schema so that the CoreAdminHandler returns a // findable filename schema.setResourceName(managedSchemaResourceName); - log.info( "After upgrading to managed schema, renamed the non-managed schema {} to {}", nonManagedSchemaFile, upgradedSchemaFile); - } else { + } catch (Exception e) { // Don't throw an exception for failure to rename the non-managed schema log.warn( "Can't rename {} to {} - PLEASE REMOVE THIS FILE.", diff --git a/solr/core/src/java/org/apache/solr/spelling/AbstractLuceneSpellChecker.java b/solr/core/src/java/org/apache/solr/spelling/AbstractLuceneSpellChecker.java index 1c0e7006185..61f99a84a61 100644 --- a/solr/core/src/java/org/apache/solr/spelling/AbstractLuceneSpellChecker.java +++ b/solr/core/src/java/org/apache/solr/spelling/AbstractLuceneSpellChecker.java @@ -16,7 +16,6 @@ */ package org.apache.solr.spelling; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Arrays; @@ -71,7 +70,7 @@ public abstract class AbstractLuceneSpellChecker extends SolrSpellChecker { protected Dictionary dictionary; public static final int DEFAULT_SUGGESTION_COUNT = 5; - protected String indexDir; + protected Path indexDir; protected float accuracy = 0.5f; public static final String FIELD = "field"; @@ -80,13 +79,14 @@ public abstract class AbstractLuceneSpellChecker extends SolrSpellChecker { @Override public String init(NamedList config, SolrCore core) { super.init(config, core); - indexDir = (String) config.get(INDEX_DIR); + String indexPath = (String) config.get(INDEX_DIR); + indexDir = (indexPath != null) ? Path.of(indexPath) : null; + // If indexDir is relative then create index inside core.getDataDir() if (indexDir != null) { - if (!Path.of(indexDir).isAbsolute()) { - indexDir = core.getDataDir() + File.separator + indexDir; - } + indexDir = Path.of(core.getDataDir()).resolve(indexDir); } + sourceLocation = (String) config.get(LOCATION); String compClass = (String) config.get(COMPARATOR_CLASS); Comparator comp = null; @@ -235,7 +235,7 @@ protected void initIndex() throws IOException { // files can't be opened. It would be better for SpellChecker to hold a single IW instance, // and close it on close, but Solr never seems to close its spell checkers. Wrapping as // FilterDirectory prevents IndexWriter from catching the pending deletions: - index = new FilterDirectory(FSDirectory.open(Path.of(indexDir))) {}; + index = new FilterDirectory(FSDirectory.open(indexDir)) {}; } else { index = new ByteBuffersDirectory(); } @@ -268,7 +268,7 @@ public String getFieldTypeName() { /* * @return the Index directory * */ - public String getIndexDir() { + public Path getIndexDir() { return indexDir; } diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java index 682b9a81a29..a6ba007ed13 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java @@ -16,7 +16,6 @@ */ package org.apache.solr.spelling.suggest.fst; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -86,11 +85,12 @@ public Lookup create(NamedList params, SolrCore core) { // optional parameters - String indexPath = - params.get(INDEX_PATH) != null ? params.get(INDEX_PATH).toString() : DEFAULT_INDEX_PATH; - if (!Path.of(indexPath).isAbsolute()) { - indexPath = core.getDataDir() + File.separator + indexPath; - } + Path indexPath = + params.get(INDEX_PATH) != null + ? Path.of(params.get(INDEX_PATH).toString()) + : Path.of(DEFAULT_INDEX_PATH); + + indexPath = Path.of(core.getDataDir()).resolve(indexPath); int minPrefixChars = params.get(MIN_PREFIX_CHARS) != null @@ -109,7 +109,7 @@ public Lookup create(NamedList params, SolrCore core) { try { return new AnalyzingInfixSuggester( - FSDirectory.open(Path.of(indexPath)), + FSDirectory.open(indexPath), indexAnalyzer, queryAnalyzer, minPrefixChars, diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/BlendedInfixLookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/BlendedInfixLookupFactory.java index 09a85917c94..f4c4131d561 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/BlendedInfixLookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/BlendedInfixLookupFactory.java @@ -16,7 +16,6 @@ */ package org.apache.solr.spelling.suggest.fst; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -77,11 +76,12 @@ public Lookup create(NamedList params, SolrCore core) { // optional parameters - String indexPath = - params.get(INDEX_PATH) != null ? params.get(INDEX_PATH).toString() : DEFAULT_INDEX_PATH; - if (!Path.of(indexPath).isAbsolute()) { - indexPath = core.getDataDir() + File.separator + indexPath; - } + Path indexPath = + params.get(INDEX_PATH) != null + ? Path.of(params.get(INDEX_PATH).toString()) + : Path.of(DEFAULT_INDEX_PATH); + + indexPath = Path.of(core.getDataDir()).resolve(indexPath); int minPrefixChars = params.get(MIN_PREFIX_CHARS) != null @@ -110,7 +110,7 @@ public Lookup create(NamedList params, SolrCore core) { try { return new BlendedInfixSuggester( - FSDirectory.open(Path.of(indexPath)), + FSDirectory.open(indexPath), indexAnalyzer, queryAnalyzer, minPrefixChars, diff --git a/solr/core/src/java/org/apache/solr/util/FileUtils.java b/solr/core/src/java/org/apache/solr/util/FileUtils.java index 0b221aae025..0f106a11f41 100644 --- a/solr/core/src/java/org/apache/solr/util/FileUtils.java +++ b/solr/core/src/java/org/apache/solr/util/FileUtils.java @@ -17,6 +17,7 @@ package org.apache.solr.util; import java.io.IOException; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import org.apache.commons.io.FileExistsException; @@ -69,4 +70,17 @@ public static boolean isPathAChildOfParent(Path parent, Path potentialChild) { return normalizedChild.startsWith(normalizedParent) && !normalizedChild.equals(normalizedParent); } + + /** + * Takes a path and normalizes it with the OS default filesystems separator + * + * @param path the path to normalize + * @return path normalized with the filesystems default separator + */ + public static String normalizeToOsPathSeparator(String path) { + if (path == null) return null; + path = path.replace("/", FileSystems.getDefault().getSeparator()); + path = path.replace("\\", FileSystems.getDefault().getSeparator()); + return path; + } } diff --git a/solr/core/src/java/org/apache/solr/util/RegexFileFilter.java b/solr/core/src/java/org/apache/solr/util/RegexFileFilter.java index 3ed84d93c20..78b938acf3b 100644 --- a/solr/core/src/java/org/apache/solr/util/RegexFileFilter.java +++ b/solr/core/src/java/org/apache/solr/util/RegexFileFilter.java @@ -16,12 +16,11 @@ */ package org.apache.solr.util; -import java.io.File; -import java.io.FileFilter; +import java.nio.file.Path; import java.util.regex.Pattern; /** Accepts any file whose name matches the pattern */ -public final class RegexFileFilter implements FileFilter { +public final class RegexFileFilter { final Pattern pattern; @@ -33,9 +32,8 @@ public RegexFileFilter(Pattern regex) { pattern = regex; } - @Override - public boolean accept(File f) { - return pattern.matcher(f.getName()).matches(); + public boolean accept(Path f) { + return pattern.matcher(f.getFileName().toString()).matches(); } @Override diff --git a/solr/core/src/java/org/apache/solr/util/SystemIdResolver.java b/solr/core/src/java/org/apache/solr/util/SystemIdResolver.java index 6d1267d76f4..f1b87325878 100644 --- a/solr/core/src/java/org/apache/solr/util/SystemIdResolver.java +++ b/solr/core/src/java/org/apache/solr/util/SystemIdResolver.java @@ -16,10 +16,10 @@ */ package org.apache.solr.util; -import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.FileSystems; import javax.xml.stream.XMLResolver; import javax.xml.stream.XMLStreamException; import javax.xml.transform.Source; @@ -167,7 +167,7 @@ public InputSource resolveEntity(String publicId, String systemId) throws IOExce } public static String createSystemIdFromResourceName(String name) { - name = name.replace(File.separatorChar, '/'); + name = name.replace(FileSystems.getDefault().getSeparator(), "/"); final String authority; if (name.startsWith("/")) { // a hack to preserve absolute filenames and keep them absolute after resolving, we set the diff --git a/solr/core/src/java/org/apache/solr/util/VersionedFile.java b/solr/core/src/java/org/apache/solr/util/VersionedFile.java index 12ffdb63b53..dbc9dc8430a 100644 --- a/solr/core/src/java/org/apache/solr/util/VersionedFile.java +++ b/solr/core/src/java/org/apache/solr/util/VersionedFile.java @@ -16,19 +16,17 @@ */ package org.apache.solr.util; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Stream; +import org.apache.solr.common.SolrException; /** * @since solr 1.3 @@ -38,13 +36,12 @@ public class VersionedFile { * the last fileName.* after being sorted lexicographically. * Older versions of the file are deleted (and queued for deletion if * that fails). + * TODO SOLR-8282 dirName should be a Path instead of string */ - public static InputStream getLatestFile(String dirName, String fileName) - throws FileNotFoundException { - // TODO SOLR-8282 move to PATH - Collection oldFiles = null; + public static InputStream getLatestFile(String dirName, String fileName) throws IOException { + Collection oldFiles = null; final String prefix = fileName + '.'; - File f = new File(dirName, fileName); + Path f = Path.of(dirName, fileName); InputStream is = null; // there can be a race between checking for a file and opening it... @@ -52,25 +49,29 @@ public static InputStream getLatestFile(String dirName, String fileName) // try multiple times in a row. for (int retry = 0; retry < 10 && is == null; retry++) { try { - if (!f.exists()) { - File dir = new File(dirName); - String[] names = - dir.list( - new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.startsWith(prefix); - } - }); - Arrays.sort(names); - f = new File(dir, names[names.length - 1]); + if (!Files.exists(f)) { + Path dir = Path.of(dirName); + List fileList; + + try (Stream files = Files.list(dir)) { + fileList = + files + .filter((file) -> file.getFileName().toString().startsWith(prefix)) + .sorted() + .toList(); + } catch (IOException e) { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, "Unable to list files in " + dir, e); + } + + f = dir.resolve(fileList.getLast()); oldFiles = new ArrayList<>(); - for (int i = 0; i < names.length - 1; i++) { - oldFiles.add(new File(dir, names[i])); + for (int i = 0; i < fileList.size() - 1; i++) { + oldFiles.add(dir.resolve(fileList.get(i))); } } - is = new FileInputStream(f); + is = Files.newInputStream(f); } catch (Exception e) { // swallow exception for now } @@ -78,7 +79,7 @@ public boolean accept(File dir, String name) { // allow exception to be thrown from the final try. if (is == null) { - is = new FileInputStream(f); + is = Files.newInputStream(f); } // delete old files only after we have successfully opened the newest @@ -89,16 +90,16 @@ public boolean accept(File dir, String name) { return is; } - private static final Set deleteList = new HashSet<>(); + private static final Set deleteList = new HashSet<>(); - private static synchronized void delete(Collection files) { + private static synchronized void delete(Collection files) { synchronized (deleteList) { deleteList.addAll(files); - List deleted = new ArrayList<>(); - for (File df : deleteList) { + List deleted = new ArrayList<>(); + for (Path df : deleteList) { try { try { - Files.deleteIfExists(df.toPath()); + Files.deleteIfExists(df); } catch (IOException cause) { // TODO: should this class care if a file couldn't be deleted? // this just emulates previous behavior, where only SecurityException would be handled. @@ -106,7 +107,7 @@ private static synchronized void delete(Collection files) { // deleteList.remove(df); deleted.add(df); } catch (SecurityException e) { - if (!df.exists()) { + if (!Files.exists(df)) { deleted.add(df); } } diff --git a/solr/core/src/test/org/apache/solr/util/RegexFileFilterTest.java b/solr/core/src/test/org/apache/solr/util/RegexFileFilterTest.java index 8e69018c941..ae90deb90ed 100644 --- a/solr/core/src/test/org/apache/solr/util/RegexFileFilterTest.java +++ b/solr/core/src/test/org/apache/solr/util/RegexFileFilterTest.java @@ -16,7 +16,7 @@ */ package org.apache.solr.util; -import java.io.File; +import java.nio.file.Path; import org.apache.solr.SolrTestCase; import org.junit.Test; @@ -27,23 +27,23 @@ public class RegexFileFilterTest extends SolrTestCase { @Test public void testAcceptTrue() { - assertTrue(endsWithDotTxt.accept(new File("/foo/bar/baz.txt"))); - assertTrue(endsWithDotTxt.accept(new File("/baz.txt"))); - assertTrue(endsWithDotTxt.accept(new File("~/baz.txt"))); - assertTrue(endsWithDotTxt.accept(new File("~/1234-abc_.txt"))); - assertTrue(endsWithDotTxt.accept(new File(".txt"))); - assertTrue(alphaWithTxtOrPdfExt.accept(new File("/foo/bar.txt"))); - assertTrue(alphaWithTxtOrPdfExt.accept(new File("/foo/baz.pdf"))); + assertTrue(endsWithDotTxt.accept(Path.of("/foo/bar/baz.txt"))); + assertTrue(endsWithDotTxt.accept(Path.of("/baz.txt"))); + assertTrue(endsWithDotTxt.accept(Path.of("~/baz.txt"))); + assertTrue(endsWithDotTxt.accept(Path.of("~/1234-abc_.txt"))); + assertTrue(endsWithDotTxt.accept(Path.of(".txt"))); + assertTrue(alphaWithTxtOrPdfExt.accept(Path.of("/foo/bar.txt"))); + assertTrue(alphaWithTxtOrPdfExt.accept(Path.of("/foo/baz.pdf"))); } @Test public void testAcceptFalse() { - assertFalse(endsWithDotTxt.accept(new File("/foo/bar.tx"))); - assertFalse(endsWithDotTxt.accept(new File("/foo/bar.txts"))); - assertFalse(endsWithDotTxt.accept(new File("/foo/bar.exe"))); - assertFalse(alphaWithTxtOrPdfExt.accept(new File("/foo/bar/b4z.txt"))); - assertFalse(alphaWithTxtOrPdfExt.accept(new File("/foo/bar/baz.jpg"))); - assertFalse(alphaWithTxtOrPdfExt.accept(new File("~/foo-bar.txt"))); - assertFalse(alphaWithTxtOrPdfExt.accept(new File("~/foobar123.txt"))); + assertFalse(endsWithDotTxt.accept(Path.of("/foo/bar.tx"))); + assertFalse(endsWithDotTxt.accept(Path.of("/foo/bar.txts"))); + assertFalse(endsWithDotTxt.accept(Path.of("/foo/bar.exe"))); + assertFalse(alphaWithTxtOrPdfExt.accept(Path.of("/foo/bar/b4z.txt"))); + assertFalse(alphaWithTxtOrPdfExt.accept(Path.of("/foo/bar/baz.jpg"))); + assertFalse(alphaWithTxtOrPdfExt.accept(Path.of("~/foo-bar.txt"))); + assertFalse(alphaWithTxtOrPdfExt.accept(Path.of("./~/foobar123.txt"))); } }