diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/Metrics.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/Metrics.java index a63d1695573..831a9ef5bec 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/Metrics.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/Metrics.java @@ -35,12 +35,12 @@ import io.micrometer.statsd.StatsdConfig; import io.micrometer.statsd.StatsdMeterRegistry; import io.micrometer.statsd.StatsdFlavor; +import org.opengrok.indexer.configuration.Project; import org.opengrok.indexer.configuration.RuntimeEnvironment; -import org.opengrok.indexer.index.Indexer; import org.opengrok.indexer.logger.LoggerFactory; import java.util.Collections; -import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -108,13 +108,12 @@ public boolean buffered() { private Metrics() { } - public static void updateSubFiles(List subFiles) { + public static void updateProjects(Set projects) { // Add tag for per-project reindex. - if (statsdRegistry != null && !subFiles.isEmpty()) { - String projects = subFiles.stream(). - map(s -> s.startsWith(Indexer.PATH_SEPARATOR_STRING) ? s.substring(1) : s). + if (statsdRegistry != null && !projects.isEmpty()) { + String projectNames = projects.stream().map(Project::getName). collect(Collectors.joining(",")); - Tag commonTag = Tag.of("projects", projects); + Tag commonTag = Tag.of("projects", projectNames); LOGGER.log(Level.FINE, "updating statsdRegistry with common tag: {}", commonTag); statsdRegistry.config().commonTags(Collections.singleton(commonTag)); } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/RuntimeEnvironment.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/RuntimeEnvironment.java index 2f04697971d..941eee11f87 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/RuntimeEnvironment.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/RuntimeEnvironment.java @@ -143,12 +143,6 @@ public final class RuntimeEnvironment { private final Set listeners = new CopyOnWriteArraySet<>(); - public List getSubFiles() { - return subFiles; - } - - private final List subFiles = new ArrayList<>(); - /** * Maps project name to FileCollector object. This is used to pass the list of files acquired when * generating history cache in the first phase of indexing to the second phase of indexing. @@ -507,17 +501,15 @@ public String getPathRelativeToSourceRoot(File file) throws IOException, Forbidd return maybeRelPath; } - throw new FileNotFoundException("Failed to resolve [" + file.getPath() - + "] relative to source root [" + sourceRoot + "]"); + throw new FileNotFoundException(String.format("Failed to resolve '%s' relative to source root '%s'", + file.getPath(), sourceRoot)); } /** - * Do we have any projects ? - * - * @return true if we have projects + * @return true if projects are enabled and there are some projects present */ public boolean hasProjects() { - return (this.isProjectsEnabled() && getProjects().size() > 0); + return (this.isProjectsEnabled() && !getProjects().isEmpty()); } /** diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexDatabase.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexDatabase.java index de89aa00b81..2c85ca2a340 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexDatabase.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexDatabase.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. * Portions Copyright (c) 2017, 2020, Chris Fraire . */ package org.opengrok.indexer.index; @@ -186,7 +186,6 @@ public class IndexDatabase { private PathAccepter pathAccepter; private AnalyzerGuru analyzerGuru; private File xrefDir; - private boolean interrupted; private CopyOnWriteArrayList listeners; private File dirtyFile; private final Object lock = new Object(); @@ -194,7 +193,7 @@ public class IndexDatabase { private boolean running; private boolean isCountingDeltas; private boolean isWithDirectoryCounts; - private List directories; + private String directory; private LockFactory lockFactory; private final BytesRef emptyBR = new BytesRef(""); private final Set deletedUids = new HashSet<>(); @@ -225,7 +224,6 @@ public IndexDatabase() throws IOException { * @param writer index writer * @throws IOException on error */ - @VisibleForTesting IndexDatabase(Project project, TermsEnum uidIter, IndexWriter writer) throws IOException { this(project, new IndexDownArgsFactory()); this.uidIter = uidIter; @@ -241,7 +239,7 @@ public IndexDatabase() throws IOException { * @param factory {@link IndexDownArgsFactory} instance * @throws java.io.IOException if an error occurs while creating directories */ - public IndexDatabase(Project project, IndexDownArgsFactory factory) throws IOException { + IndexDatabase(Project project, IndexDownArgsFactory factory) throws IOException { indexDownArgsFactory = factory; this.project = project; lockFactory = NoLockFactory.INSTANCE; @@ -257,7 +255,7 @@ public IndexDatabase(Project project, IndexDownArgsFactory factory) throws IOExc * @param indexWriterConfigFactory {@link IndexWriterConfigFactory} instance * @throws java.io.IOException if an error occurs while creating directories */ - public IndexDatabase(Project project, IndexDownArgsFactory indexDownArgsFactory, + IndexDatabase(@NotNull Project project, IndexDownArgsFactory indexDownArgsFactory, IndexWriterConfigFactory indexWriterConfigFactory) throws IOException { this.indexDownArgsFactory = indexDownArgsFactory; this.project = project; @@ -266,7 +264,6 @@ public IndexDatabase(Project project, IndexDownArgsFactory indexDownArgsFactory, initialize(); } - @VisibleForTesting IndexDatabase(Project project) throws IOException { this(project, new IndexDownArgsFactory()); } @@ -324,14 +321,16 @@ public static void addIndexDatabase(@Nullable IndexDatabase db, List> historyCacheResults) throws IOException { + static void updateAll(IndexChangedListener listener, + Map> historyCacheResults) + throws IOException, IndexerException { RuntimeEnvironment env = RuntimeEnvironment.getInstance(); List dbs = new ArrayList<>(); - if (env.hasProjects()) { + if (env.isProjectsEnabled()) { for (Project project : env.getProjectList()) { addIndexDatabaseForProject(null, project, dbs, historyCacheResults); } @@ -341,6 +340,7 @@ static CountDownLatch updateAll(IndexChangedListener listener, IndexerParallelizer parallelizer = RuntimeEnvironment.getInstance().getIndexerParallelizer(); CountDownLatch latch = new CountDownLatch(dbs.size()); + IndexerException exception = new IndexerException(); for (IndexDatabase d : dbs) { final IndexDatabase db = d; if (listener != null) { @@ -351,70 +351,22 @@ static CountDownLatch updateAll(IndexChangedListener listener, try { db.update(); } catch (Throwable e) { - LOGGER.log(Level.SEVERE, - String.format("Problem updating index database in directory %s: ", - db.indexDirectory.getDirectory()), e); + exception.addSuppressed(e); + LOGGER.log(Level.SEVERE, String.format("Problem updating index database in directory '%s': ", + db.indexDirectory.getDirectory()), e); } finally { latch.countDown(); } }); } - return latch; - } - - /** - * Update the index database for a number of sub-directories. - * - * @param listener where to signal the changes to the database - * @param paths list of paths to be indexed - */ - public static void update(IndexChangedListener listener, List paths) { - RuntimeEnvironment env = RuntimeEnvironment.getInstance(); - IndexerParallelizer parallelizer = env.getIndexerParallelizer(); - List dbs = new ArrayList<>(); - - for (String path : paths) { - Project project = Project.getProject(path); - if (project == null && env.hasProjects()) { - LOGGER.log(Level.WARNING, "Could not find a project for \"{0}\"", path); - } else { - IndexDatabase db; - - try { - if (project == null) { - db = new IndexDatabase(); - } else { - db = new IndexDatabase(project); - } - - int idx = dbs.indexOf(db); - if (idx != -1) { - db = dbs.get(idx); - } - - if (db.addDirectory(path)) { - if (idx == -1) { - dbs.add(db); - } - } else { - LOGGER.log(Level.WARNING, "Directory does not exist \"{0}\" .", path); - } - } catch (IOException e) { - LOGGER.log(Level.WARNING, "An error occurred while updating index", e); - - } - } + try { + latch.await(); + } catch (InterruptedException e) { + exception.addSuppressed(e); + } - for (final IndexDatabase db : dbs) { - db.addIndexChangedListener(listener); - parallelizer.getFixedExecutor().submit(() -> { - try { - db.update(); - } catch (Throwable e) { - LOGGER.log(Level.SEVERE, "An error occurred while updating index", e); - } - }); - } + if (exception.getSuppressed().length > 0) { + throw exception; } } @@ -442,7 +394,11 @@ private void initialize() throws IOException { listeners = new CopyOnWriteArrayList<>(); dirtyFile = new File(indexDir, "dirty"); dirty = dirtyFile.exists(); - directories = new ArrayList<>(); + if (project == null) { + directory = ""; + } else { + directory = project.getPath(); + } if (dirty) { LOGGER.log(Level.WARNING, "Index in ''{0}'' is dirty, the last indexing was likely interrupted." + @@ -451,30 +407,6 @@ private void initialize() throws IOException { } } - /** - * By default the indexer will traverse all directories in the project. If - * you add directories with this function update will just process the - * specified directories. - * - * @param dir The directory to scan - * @return true if the file is added, false otherwise - */ - @SuppressWarnings("PMD.UseStringBufferForStringAppends") - public boolean addDirectory(String dir) { - String directory = dir; - if (directory.startsWith("\\")) { - directory = directory.replace('\\', '/'); - } else if (directory.charAt(0) != '/') { - directory = "/" + directory; - } - File file = new File(RuntimeEnvironment.getInstance().getSourceRootFile(), directory); - if (file.exists()) { - directories.add(directory); - return true; - } - return false; - } - private void showFileCount(String dir, IndexDownArgs args) { if (RuntimeEnvironment.getInstance().isPrintProgress()) { LOGGER.log(Level.INFO, String.format("Need to process: %d files for %s", args.curCount, dir)); @@ -664,14 +596,14 @@ boolean isReadyForHistoryBasedReindex(Repository repository) { * Update the content of this index database. * * @throws IOException if an error occurs + * @throws IndexerException if the indexing was incomplete/failed */ - public void update() throws IOException { + public void update() throws IOException, IndexerException { synchronized (lock) { if (running) { - throw new IOException("Indexer already running!"); + throw new IndexerException("Indexer already running!"); } running = true; - interrupted = false; } RuntimeEnvironment env = RuntimeEnvironment.getInstance(); @@ -689,108 +621,99 @@ public void update() throws IOException { writer.commit(); // to make sure index exists on the disk completer = new PendingFileCompleter(); - if (directories.isEmpty()) { - if (project == null) { - directories.add(""); - } else { - directories.add(project.getPath()); - } + String dir = this.directory; + File sourceRoot; + if ("".equals(dir)) { + sourceRoot = env.getSourceRootFile(); + } else { + sourceRoot = new File(env.getSourceRootFile(), dir); } - for (String dir : directories) { - File sourceRoot; - if ("".equals(dir)) { - sourceRoot = env.getSourceRootFile(); - } else { - sourceRoot = new File(env.getSourceRootFile(), dir); - } + dir = Util.fixPathIfWindows(dir); - dir = Util.fixPathIfWindows(dir); + String startUid = Util.path2uid(dir, ""); + reader = DirectoryReader.open(indexDirectory); // open existing index + setupDeletedUids(); + countsAggregator = new NumLinesLOCAggregator(); + settings = readAnalysisSettings(); + if (settings == null) { + settings = new IndexAnalysisSettings3(); + } + Terms terms = null; + if (reader.numDocs() > 0) { + terms = MultiTerms.getTerms(reader, QueryBuilder.U); - String startUid = Util.path2uid(dir, ""); - reader = DirectoryReader.open(indexDirectory); // open existing index - setupDeletedUids(); - countsAggregator = new NumLinesLOCAggregator(); - settings = readAnalysisSettings(); - if (settings == null) { - settings = new IndexAnalysisSettings3(); - } - Terms terms = null; - if (reader.numDocs() > 0) { - terms = MultiTerms.getTerms(reader, QueryBuilder.U); - - NumLinesLOCAccessor countsAccessor = new NumLinesLOCAccessor(); - if (countsAccessor.hasStored(reader)) { - isWithDirectoryCounts = true; - isCountingDeltas = true; - } else { - boolean foundCounts = countsAccessor.register(countsAggregator, reader); - isWithDirectoryCounts = false; - isCountingDeltas = foundCounts; - if (!isCountingDeltas) { - LOGGER.info("Forcing reindexing to fully compute directory counts"); - } - } + NumLinesLOCAccessor countsAccessor = new NumLinesLOCAccessor(); + if (countsAccessor.hasStored(reader)) { + isWithDirectoryCounts = true; + isCountingDeltas = true; } else { + boolean foundCounts = countsAccessor.register(countsAggregator, reader); isWithDirectoryCounts = false; - isCountingDeltas = false; + isCountingDeltas = foundCounts; + if (!isCountingDeltas) { + LOGGER.info("Forcing reindexing to fully compute directory counts"); + } } + } else { + isWithDirectoryCounts = false; + isCountingDeltas = false; + } - try { - if (terms != null) { - uidIter = terms.iterator(); - // The seekCeil() is pretty important because it makes uidIter.term() to become non-null. - // Various indexer methods rely on this when working with the uidIter iterator - rather - // than calling uidIter.next() first thing, they check uidIter.term(). - TermsEnum.SeekStatus stat = uidIter.seekCeil(new BytesRef(startUid)); - if (stat == TermsEnum.SeekStatus.END) { - uidIter = null; - LOGGER.log(Level.WARNING, - "Couldn''t find a start term for {0}, empty u field?", startUid); - } + try { + if (terms != null) { + uidIter = terms.iterator(); + // The seekCeil() is pretty important because it makes uidIter.term() to become non-null. + // Various indexer methods rely on this when working with the uidIter iterator - rather + // than calling uidIter.next() first thing, they check uidIter.term(). + TermsEnum.SeekStatus stat = uidIter.seekCeil(new BytesRef(startUid)); + if (stat == TermsEnum.SeekStatus.END) { + uidIter = null; + LOGGER.log(Level.WARNING, + "Could not find a start term for {0}, empty u field?", startUid); } + } - // The actual indexing happens in indexParallel(). Here we merely collect the files - // that need to be indexed and the files that should be removed. - IndexDownArgs args = indexDownArgsFactory.getIndexDownArgs(); - boolean usedHistory = getIndexDownArgs(dir, sourceRoot, args); - - // Traverse the trailing terms. This needs to be done before indexParallel() because - // in some cases it can add items to the args parameter. - processTrailingTerms(startUid, usedHistory, args); - - args.curCount = 0; - Statistics elapsed = new Statistics(); - LOGGER.log(Level.INFO, "Starting indexing of directory ''{0}''", dir); - indexParallel(dir, args); - elapsed.report(LOGGER, String.format("Done indexing of directory '%s'", dir), - "indexer.db.directory.index"); - - /* - * As a signifier that #Lines/LOC are comprehensively - * stored so that later calculation is in deltas mode, we - * need at least one D-document saved. For a repo with only - * non-code files, however, no true #Lines/LOC will have - * been saved. Subsequent re-indexing will do more work - * than necessary (until a source code file is placed). We - * can record zeroes for a fake file under the root to get - * a D-document even for this special repo situation. - * - * Metrics are aggregated for directories up to the root, - * so it suffices to put the fake directly under the root. - */ - if (!isWithDirectoryCounts) { - final String ROOT_FAKE_FILE = "/.OpenGrok_fake_file"; - countsAggregator.register(new NumLinesLOC(ROOT_FAKE_FILE, 0, 0)); - } - NumLinesLOCAccessor countsAccessor = new NumLinesLOCAccessor(); - countsAccessor.store(writer, reader, countsAggregator, - isWithDirectoryCounts && isCountingDeltas); + // The actual indexing happens in indexParallel(). Here we merely collect the files + // that need to be indexed and the files that should be removed. + IndexDownArgs args = indexDownArgsFactory.getIndexDownArgs(); + boolean usedHistory = getIndexDownArgs(dir, sourceRoot, args); - markProjectIndexed(project); - } finally { - reader.close(); + // Traverse the trailing terms. This needs to be done before indexParallel() because + // in some cases it can add items to the args parameter. + processTrailingTerms(startUid, usedHistory, args); + + args.curCount = 0; + Statistics elapsed = new Statistics(); + LOGGER.log(Level.INFO, "Starting indexing of directory ''{0}''", dir); + indexParallel(dir, args); + elapsed.report(LOGGER, String.format("Done indexing of directory '%s'", dir), + "indexer.db.directory.index"); + + /* + * As a signifier that #Lines/LOC are comprehensively + * stored so that later calculation is in deltas mode, we + * need at least one D-document saved. For a repo with only + * non-code files, however, no true #Lines/LOC will have + * been saved. Subsequent re-indexing will do more work + * than necessary (until a source code file is placed). We + * can record zeroes for a fake file under the root to get + * a D-document even for this special repo situation. + * + * Metrics are aggregated for directories up to the root, + * so it suffices to put the fake directly under the root. + */ + if (!isWithDirectoryCounts) { + final String ROOT_FAKE_FILE = "/.OpenGrok_fake_file"; + countsAggregator.register(new NumLinesLOC(ROOT_FAKE_FILE, 0, 0)); } + NumLinesLOCAccessor countsAccessor = new NumLinesLOCAccessor(); + countsAccessor.store(writer, reader, countsAggregator, + isWithDirectoryCounts && isCountingDeltas); + + markProjectIndexed(project); + } finally { + reader.close(); } // The RuntimeException thrown from the block above can prevent the writing from completing. @@ -801,8 +724,7 @@ public void update() throws IOException { finishingException = e; } } catch (RuntimeException ex) { - LOGGER.log(Level.SEVERE, - "Failed with unexpected RuntimeException", ex); + LOGGER.log(Level.SEVERE, "Failed with unexpected RuntimeException", ex); throw ex; } finally { completer = null; @@ -814,8 +736,7 @@ public void update() throws IOException { if (finishingException == null) { finishingException = e; } - LOGGER.log(Level.WARNING, - "An error occurred while closing writer", e); + LOGGER.log(Level.WARNING, "An error occurred while closing writer", e); } finally { writer = null; synchronized (lock) { @@ -828,7 +749,7 @@ public void update() throws IOException { throw finishingException; } - if (!isInterrupted() && isDirty()) { + if (isDirty()) { unsetDirty(); env.setIndexTimestamp(); } @@ -1011,9 +932,6 @@ void indexDownUsingHistory(File sourceRoot, IndexDownArgs args) throws IOExcepti collect(Collectors.toList()); LOGGER.log(Level.FINEST, "collected sorted files: {0}", paths); for (Path path : paths) { - if (isInterrupted()) { - return; - } File file = new File(sourceRoot, path.toString()); progress.increment(); // @@ -1714,11 +1632,6 @@ private void handleSymlink(String path, AcceptSymlinkRet ret) { */ @VisibleForTesting void indexDown(File dir, String parent, IndexDownArgs args, Progress progress) throws IOException { - - if (isInterrupted()) { - return; - } - AcceptSymlinkRet ret = new AcceptSymlinkRet(); if (!accept(dir, ret)) { handleSymlink(parent, ret); @@ -1727,7 +1640,7 @@ void indexDown(File dir, String parent, IndexDownArgs args, Progress progress) t File[] files = dir.listFiles(); if (files == null) { - LOGGER.log(Level.SEVERE, "Failed to get file listing for: ''{0}''", dir.getPath()); + LOGGER.log(Level.SEVERE, "Failed to get file listing for ''{0}''", dir.getPath()); return; } Arrays.sort(files, FILENAME_COMPARATOR); @@ -1938,8 +1851,9 @@ void processFile(IndexDownArgs args, File file, String path) throws IOException * Executes the second, parallel stage of indexing. * @param dir the parent directory (when appended to SOURCE_ROOT) * @param args contains a list of files to index, found during the earlier stage + * @throws IndexerException in case the indexing failed or was interrupted */ - private void indexParallel(String dir, IndexDownArgs args) { + private void indexParallel(String dir, IndexDownArgs args) throws IndexerException { int worksCount = args.works.size(); if (worksCount < 1) { @@ -2005,12 +1919,11 @@ private void indexParallel(String dir, IndexDownArgs args) { bySuccess.computeIfAbsent(work.ret, key -> new ArrayList<>()).add(work); } } catch (InterruptedException | ExecutionException e) { - interrupted = true; int successCount = successCounter.intValue(); double successPct = 100.0 * successCount / worksCount; - String exmsg = String.format("%d successes (%.1f%%) after aborting parallel-indexing", - successCount, successPct); - LOGGER.log(Level.SEVERE, exmsg, e); + LOGGER.log(Level.SEVERE, String.format("%d successes (%.1f%%) after aborting parallel-indexing", + successCount, successPct)); + throw new IndexerException(e); } args.curCount = currentCounter.intValue(); @@ -2034,12 +1947,6 @@ private void indexParallel(String dir, IndexDownArgs args) { } } - private boolean isInterrupted() { - synchronized (lock) { - return interrupted; - } - } - /** * Register an object to receive events when modifications is done to the * index database. diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/Indexer.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/Indexer.java index c235674f9a4..ccbdc0d03fa 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/Indexer.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/Indexer.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved. * Portions Copyright (c) 2011, Jens Elkner. * Portions Copyright (c) 2017, 2020, Chris Fraire . */ @@ -61,6 +61,7 @@ import org.apache.commons.lang3.SystemUtils; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import org.jetbrains.annotations.VisibleForTesting; import org.opengrok.indexer.Info; import org.opengrok.indexer.Metrics; import org.opengrok.indexer.analysis.AnalyzerGuru; @@ -84,6 +85,7 @@ import org.opengrok.indexer.util.Executor; import org.opengrok.indexer.util.HostUtil; import org.opengrok.indexer.util.OptionParser; +import org.opengrok.indexer.util.RuntimeUtil; import org.opengrok.indexer.util.Statistics; import static org.opengrok.indexer.util.RuntimeUtil.checkJavaVersion; @@ -164,14 +166,25 @@ public static Indexer getInstance() { * * @param argv argument vector */ - @SuppressWarnings("PMD.UseStringBufferForStringAppends") public static void main(String[] argv) { + System.exit(runMain(argv)); + } + + /** + * The body of {@link #main(String[])}. Avoids {@code System.exit()} so that it can be used for testing + * without the test runner thinking the VM went away unexpectedly. + * @param argv argument vector passed from the wrapper + * @return 0 on success, positive number on error + */ + @SuppressWarnings("PMD.UseStringBufferForStringAppends") + @VisibleForTesting + public static int runMain(String[] argv) { Statistics stats = new Statistics(); //this won't count JVM creation though Executor.registerErrorHandler(); - List subFiles = RuntimeEnvironment.getInstance().getSubFiles(); - Set subFilePaths = new HashSet<>(); - Set subFileArgs = new HashSet<>(); + Set subFilePaths = new HashSet<>(); // absolute paths + Set subFileArgs = new HashSet<>(); // relative to source root + int exitCode = 0; try { argv = parseOptions(argv); @@ -179,7 +192,7 @@ public static void main(String[] argv) { if (webappURI != null && !HostUtil.isReachable(webappURI, cfg.getConnectTimeout(), cfg.getIndexerAuthenticationToken())) { System.err.println(webappURI + " is not reachable and the -U option was specified, exiting."); - System.exit(1); + return 1; } /* @@ -275,30 +288,7 @@ public static void main(String[] argv) { // Check index(es) and exit. Use distinct return code upon failure. if (indexCheckMode.ordinal() > IndexCheck.IndexCheckMode.NO_CHECK.ordinal()) { - if (cfg.getDataRoot() == null || cfg.getDataRoot().isEmpty()) { - System.err.println("Empty data root in configuration"); - System.exit(1); - } - - try (IndexCheck indexCheck = new IndexCheck(cfg, subFileArgs)) { - indexCheck.check(indexCheckMode); - } catch (IOException e) { - // Use separate return code for cases where the index could not be read. - // This avoids problems with wiping out the index based on the check. - LOGGER.log(Level.WARNING, String.format("Could not perform index check for '%s'", subFileArgs), e); - System.exit(2); - } catch (IndexCheckException e) { - System.err.printf("Index check failed%n"); - if (!e.getFailedPaths().isEmpty()) { - System.err.print("You might want to remove " + e.getFailedPaths()); - } else { - System.err.println("with exception: " + e); - e.printStackTrace(System.err); - } - System.exit(1); - } - - System.exit(0); + checkIndexAndExit(subFileArgs); } if (bareConfig) { @@ -307,56 +297,58 @@ public static void main(String[] argv) { getInstance().sendToConfigHost(env, webappURI); writeConfigToFile(env, configFilename); - System.exit(0); + return 0; + } + + // The indexer does not support partial reindex, unless this is a case of per project reindex. + if (!subFilePaths.isEmpty() && !env.isProjectsEnabled()) { + System.err.println("Need to have projects enabled for the extra paths specified"); + return 1; } /* - * Add paths to directories under source root. If projects - * are enabled the path should correspond to a project because - * project path is necessary to correctly set index directory - * (otherwise the index files will end up in index data root + * Add paths to directories under source root. Each path must correspond to a project, + * because project path is necessary to correctly set index directory + * (otherwise the index files will end up in the 'index' directory directly underneath the data root * directory and not per project data root directory). - * For the check we need to have 'env' already set. + * For the check we need to have the 'env' variable already set. */ + Set projects = new HashSet<>(); for (String path : subFilePaths) { String srcPath = env.getSourceRootPath(); if (srcPath == null) { System.err.println("Error getting source root from environment. Exiting."); - System.exit(1); + return 1; } path = path.substring(srcPath.length()); - if (env.hasProjects()) { - // The paths need to correspond to a project. - Project project; - if ((project = Project.getProject(path)) != null) { - subFiles.add(path); - List repoList = env.getProjectRepositoriesMap().get(project); - if (repoList != null) { - repositories.addAll(repoList. - stream().map(RepositoryInfo::getDirectoryNameRelative).collect(Collectors.toSet())); - } - } else { - System.err.println("The path " + path - + " does not correspond to a project"); + // The paths must correspond to a project. + Project project; + if ((project = Project.getProject(path)) != null) { + projects.add(project); + List repoList = env.getProjectRepositoriesMap().get(project); + if (repoList != null) { + repositories.addAll(repoList. + stream().map(RepositoryInfo::getDirectoryNameRelative).collect(Collectors.toSet())); } } else { - subFiles.add(path); + System.err.println(String.format("The path '%s' does not correspond to a project", path)); + return 1; } } - if (!subFilePaths.isEmpty() && subFiles.isEmpty()) { + if (!subFilePaths.isEmpty() && projects.isEmpty()) { System.err.println("None of the paths were added, exiting"); - System.exit(1); + return 1; } - if (!subFiles.isEmpty() && configFilename != null) { - LOGGER.log(Level.WARNING, "The collection of entries to process is non empty ({0}), seems like " + + if (!projects.isEmpty() && configFilename != null) { + LOGGER.log(Level.WARNING, "The collection of paths to process is non empty ({0}), seems like " + "the intention is to perform per project reindex, however the -W option is used. " + - "This will likely not work.", subFiles); + "This will likely not work.", projects); } - Metrics.updateSubFiles(subFiles); + Metrics.updateProjects(projects); // If the webapp is running with a config that does not contain // 'projectsEnabled' property (case of upgrade or transition @@ -364,16 +356,12 @@ public static void main(String[] argv) { // so that the 'project/indexed' messages // emitted during indexing do not cause validation error. if (addProjects && webappURI != null) { - try { - IndexerUtil.enableProjects(webappURI); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, String.format("Couldn't notify the webapp on %s.", webappURI), e); - System.err.printf("Couldn't notify the webapp on %s: %s.%n", webappURI, e.getLocalizedMessage()); - } + enableProjectsInWebApp(); } - LOGGER.log(Level.INFO, "Indexer version {0} ({1}) running on Java {2}", - new Object[]{Info.getVersion(), Info.getRevision(), Runtime.version()}); + LOGGER.log(Level.INFO, "Indexer version {0} ({1}) running on Java {2} with properties: {3}", + new Object[]{Info.getVersion(), Info.getRevision(), RuntimeUtil.getJavaVersion(), + RuntimeUtil.getJavaProperties()}); checkJavaVersion(); @@ -398,7 +386,7 @@ public static void main(String[] argv) { collect(Collectors.toSet()); } Map> historyCacheResults = getInstance().prepareIndexer(env, - searchPaths, addProjects, runIndex, subFiles, new ArrayList<>(repositories)); + searchPaths, addProjects, runIndex, new ArrayList<>(repositories)); // Set updated configuration in RuntimeEnvironment. This is called so that repositories discovered // in prepareIndexer() are stored in the Configuration used by RuntimeEnvironment. @@ -413,12 +401,12 @@ public static void main(String[] argv) { if (ignoreHistoryCacheFailures) { if (historyCacheResults.values().stream().anyMatch(Optional::isPresent)) { LOGGER.log(Level.INFO, "There have been history cache creation failures, " + - "however --ignoreHistoryCacheFailures was used, hence ignoring them: {0}", + "however --ignoreHistoryCacheFailures was used, hence ignoring them: {0}", historyCacheResults); } historyCacheResults = Collections.emptyMap(); } - getInstance().doIndexerExecution(subFiles, progress, historyCacheResults); + getInstance().doIndexerExecution(projects, progress, historyCacheResults); } if (reduceSegmentCount) { @@ -428,21 +416,30 @@ public static void main(String[] argv) { writeConfigToFile(env, configFilename); // Finally, send new configuration to the web application in the case of full reindex. - if (webappURI != null && subFiles.isEmpty()) { + if (webappURI != null && projects.isEmpty()) { getInstance().sendToConfigHost(env, webappURI); } } catch (ParseException e) { + // This is likely a problem with processing command line arguments, hence print the error to standard + // error output. System.err.println("** " + e.getMessage()); - System.exit(1); + exitCode = 1; } catch (IndexerException ex) { - LOGGER.log(Level.SEVERE, "Exception running indexer", ex); - System.err.println("Exception: " + ex.getLocalizedMessage()); - System.err.println(optParser.getUsage()); - System.exit(1); + // The exception(s) were logged already however it does not hurt to reiterate them + // at the very end of indexing (sans the stack trace) since they might have been buried in the log. + LOGGER.log(Level.SEVERE, "Indexer failed with IndexerException"); + int i = 0; + if (ex.getSuppressed().length > 0) { + LOGGER.log(Level.INFO, "Suppressed exceptions ({0} in total):", ex.getSuppressed().length); + for (Throwable throwable : ex.getSuppressed()) { + LOGGER.log(Level.INFO, "{0}: {1}", new Object[]{++i, throwable.getLocalizedMessage()}); + } + } + exitCode = 1; } catch (Throwable e) { LOGGER.log(Level.SEVERE, "Unexpected Exception", e); System.err.println("Exception: " + e.getLocalizedMessage()); - System.exit(1); + exitCode = 1; } finally { env.shutdownSearchExecutor(); /* @@ -453,6 +450,48 @@ public static void main(String[] argv) { env.getIndexerParallelizer().bounce(); stats.report(LOGGER, "Indexer finished", "indexer.total"); } + + if (exitCode == 0) { + LOGGER.log(Level.INFO, "Indexer finished with success"); + } + + return exitCode; + } + + private static void enableProjectsInWebApp() { + try { + IndexerUtil.enableProjects(webappURI); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, String.format("Couldn't notify the webapp on %s.", webappURI), e); + System.err.printf("Couldn't notify the webapp on %s: %s.%n", webappURI, e.getLocalizedMessage()); + } + } + + private static void checkIndexAndExit(Set subFileArgs) { + if (cfg.getDataRoot() == null || cfg.getDataRoot().isEmpty()) { + System.err.println("Empty data root in configuration"); + System.exit(1); + } + + try (IndexCheck indexCheck = new IndexCheck(cfg, subFileArgs)) { + indexCheck.check(indexCheckMode); + } catch (IOException e) { + // Use separate return code for cases where the index could not be read. + // This avoids problems with wiping out the index based on the check. + LOGGER.log(Level.WARNING, String.format("Could not perform index check for '%s'", subFileArgs), e); + System.exit(2); + } catch (IndexCheckException e) { + System.err.printf("Index check failed%n"); + if (!e.getFailedPaths().isEmpty()) { + System.err.print("You might want to remove " + e.getFailedPaths()); + } else { + System.err.println("with exception: " + e); + e.printStackTrace(System.err); + } + System.exit(1); + } + + System.exit(0); } /** @@ -1068,7 +1107,7 @@ public Map> prepareIndexer(RuntimeEnvironment en return prepareIndexer(env, searchRepositories ? Collections.singleton(env.getSourceRootPath()) : Collections.emptySet(), - addProjects, true, subFiles, repositories); + addProjects, true, repositories); } /** @@ -1083,7 +1122,6 @@ public Map> prepareIndexer(RuntimeEnvironment en * @param searchPaths list of paths relative to source root in which to search for repositories * @param addProjects if true, add projects * @param createHistoryCache create history cache flag - * @param subFiles list of directories * @param repositories list of repository paths relative to source root * @return map of repository to exception * @throws IndexerException indexer exception @@ -1093,7 +1131,6 @@ public Map> prepareIndexer(RuntimeEnvironment en Set searchPaths, boolean addProjects, boolean createHistoryCache, - List subFiles, List repositories) throws IndexerException, IOException { if (!env.validateUniversalCtags()) { @@ -1168,21 +1205,25 @@ private void addProjects(File[] files, Map projects) { } } - public void doIndexerExecution(List subFiles, IndexChangedListener progress) throws IOException { - doIndexerExecution(subFiles, progress, Collections.emptyMap()); + @VisibleForTesting + public void doIndexerExecution(Set projects, IndexChangedListener progress) throws IOException, IndexerException { + doIndexerExecution(projects, progress, Collections.emptyMap()); } /** * This is the second phase of the indexer which generates Lucene index - * by passing source code files through ctags, generating xrefs + * by passing source code files through {@code ctags}, generating xrefs * and storing data from the source files in the index (along with history, if any). * - * @param subFiles if not {@code null}, index just some subdirectories + * @param projects if not {@code null}, index just the projects specified * @param progress if not {@code null}, an object to receive notifications as indexer progress is made + * @param historyCacheResults per repository results of history cache update * @throws IOException if I/O exception occurred + * @throws IndexerException if the indexing has failed for any reason */ - public void doIndexerExecution(@Nullable List subFiles, @Nullable IndexChangedListener progress, - Map> historyCacheResults) throws IOException { + public void doIndexerExecution(@Nullable Set projects, @Nullable IndexChangedListener progress, + Map> historyCacheResults) + throws IOException, IndexerException { Statistics elapsed = new Statistics(); LOGGER.info("Starting indexing"); @@ -1190,20 +1231,16 @@ public void doIndexerExecution(@Nullable List subFiles, @Nullable IndexC RuntimeEnvironment env = RuntimeEnvironment.getInstance(); try (IndexerParallelizer parallelizer = env.getIndexerParallelizer()) { final CountDownLatch latch; - if (subFiles == null || subFiles.isEmpty()) { - latch = IndexDatabase.updateAll(progress, historyCacheResults); + if (projects == null || projects.isEmpty()) { + IndexDatabase.updateAll(progress, historyCacheResults); } else { + // Setup with projects enabled is assumed here. List dbs = new ArrayList<>(); - - for (String path : subFiles) { - Project project = Project.getProject(path); - if (project == null && env.hasProjects()) { - LOGGER.log(Level.WARNING, "Could not find a project for \"{0}\"", path); - } else { - addIndexDatabase(path, project, dbs, historyCacheResults); - } + for (Project project : projects) { + addIndexDatabase(project, dbs, historyCacheResults); } + final IndexerException indexerException = new IndexerException(); latch = new CountDownLatch(dbs.size()); for (final IndexDatabase db : dbs) { db.addIndexChangedListener(progress); @@ -1211,51 +1248,43 @@ public void doIndexerExecution(@Nullable List subFiles, @Nullable IndexC try { db.update(); } catch (Throwable e) { + indexerException.addSuppressed(e); LOGGER.log(Level.SEVERE, "An error occurred while updating index", e); } finally { latch.countDown(); } }); } - } - // Wait forever for the executors to finish. - try { - LOGGER.info("Waiting for the executors to finish"); - latch.await(); - } catch (InterruptedException exp) { - LOGGER.log(Level.WARNING, "Received interrupt while waiting" + - " for executor to finish", exp); + // Wait forever for the executors to finish. + try { + LOGGER.info("Waiting for the executors to finish"); + latch.await(); + } catch (InterruptedException exp) { + LOGGER.log(Level.WARNING, "Received interrupt while waiting for executor to finish", exp); + indexerException.addSuppressed(exp); + } + + if (indexerException.getSuppressed().length > 0) { + throw indexerException; + } } + elapsed.report(LOGGER, "Done indexing data of all repositories", "indexer.repository.indexing"); } finally { CtagsUtil.deleteTempFiles(); } } - private static void addIndexDatabase(String path, Project project, List dbs, + private static void addIndexDatabase(Project project, List dbs, Map> historyCacheResults) throws IOException { IndexDatabase db; if (project == null) { db = new IndexDatabase(); + IndexDatabase.addIndexDatabase(db, dbs, historyCacheResults); } else { db = new IndexDatabase(project); - } - int idx = dbs.indexOf(db); - if (idx != -1) { - db = dbs.get(idx); - } - - if (db.addDirectory(path)) { - if (idx == -1) { - if (project == null) { - IndexDatabase.addIndexDatabase(db, dbs, historyCacheResults); - } else { - IndexDatabase.addIndexDatabaseForProject(db, project, dbs, historyCacheResults); - } - } - } else { - LOGGER.log(Level.WARNING, "Directory does not exist \"{0}\"", path); + IndexDatabase.addIndexDatabaseForProject(db, project, dbs, historyCacheResults); } } @@ -1266,9 +1295,9 @@ public void sendToConfigHost(RuntimeEnvironment env, String webAppURI) { } catch (IOException | IllegalArgumentException ex) { LOGGER.log(Level.SEVERE, String.format( "Failed to send configuration to %s " - + "(is web application server running with opengrok deployed?)", webAppURI), ex); + + "(is web application server running with OpenGrok deployed?)", webAppURI), ex); } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, "interrupted while sending configuration"); + LOGGER.log(Level.WARNING, "interrupted while sending configuration to {0}", webAppURI); } LOGGER.info("Configuration update routine done, check log output for errors."); } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerException.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerException.java index ff447b12933..1a9e8f981f8 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerException.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerException.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved. */ package org.opengrok.indexer.index; @@ -30,8 +30,15 @@ public class IndexerException extends Exception { private static final long serialVersionUID = -3009093549932264215L; + IndexerException() { + super(); + } + IndexerException(String string) { super(string); } + IndexerException(Exception e) { + super(e); + } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RuntimeUtil.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RuntimeUtil.java index 3669e47d624..2709c2bd956 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RuntimeUtil.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RuntimeUtil.java @@ -22,6 +22,7 @@ */ package org.opengrok.indexer.util; + public class RuntimeUtil { private RuntimeUtil() { // private for static @@ -45,4 +46,43 @@ public static void checkJavaVersion() throws RuntimeException { majorVersion, JAVA_VERSION_MIN, JAVA_VERSION_MAX)); } } + + /** + * @return string representing Java version and some properties + */ + public static String getJavaVersion() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("version: ").append(Runtime.version()); + stringBuilder.append(", name: ").append(System.getProperty("java.vm.name")); + stringBuilder.append(", vendor: ").append(System.getProperty("java.vendor")); + stringBuilder.append(", arch: ").append(System.getProperty("os.arch")); + return stringBuilder.toString(); + } + + private static String bytesToHumanReadable(long size) { + if (size < 0) { + throw new IllegalArgumentException("Invalid size: " + size); + } + if (size < 1024) { + return size + " bytes"; + } + String units = " KMGTPE"; + int idx = 0; + float value = size; + while (value > 1024) { + value /= 1024; + idx++; + } + return String.format("%.1f %siB", value, units.substring(idx, idx + 1)); + } + + /** + * @return string representing Java properties of interest + */ + public static String getJavaProperties() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("ncpu: ").append(Runtime.getRuntime().availableProcessors()); + stringBuilder.append(", maxMemory: ").append(bytesToHumanReadable(Runtime.getRuntime().maxMemory())); + return stringBuilder.toString(); + } } diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexDatabaseTest.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexDatabaseTest.java index fba238b8a87..da056fc9452 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexDatabaseTest.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexDatabaseTest.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * Portions Copyright (c) 2018, 2020, Chris Fraire . */ package org.opengrok.indexer.index; @@ -1126,7 +1126,6 @@ void testNullifiedChanges() throws Exception { final String barName = "bar.txt"; final String repoName = "gitNoChange"; Path repositoryRootPath = Path.of(env.getSourceRootPath(), repoName); - List projectList = List.of(File.separator + repoName); try (Git gitParent = Git.init().setDirectory(parentRepositoryRoot).call()) { // Create initial commits for the files in the parent repository. final String fooName = "foo.txt"; @@ -1164,7 +1163,7 @@ void testNullifiedChanges() throws Exception { List repositoryInfos = env.getProjectRepositoriesMap().get(project); assertEquals(1, repositoryInfos.size()); assertEquals("git", repositoryInfos.get(0).getType()); - indexer.doIndexerExecution(projectList, null); + indexer.doIndexerExecution(Set.of(project), null); // Change the parent repository so that it contains nullified change to the foo.txt file. final String data = "change foo"; diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexerMainTest.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexerMainTest.java index eb4b0a68ed1..6606975e50d 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexerMainTest.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/index/IndexerMainTest.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved. * Portions Copyright (c) 2017, 2019, Chris Fraire . */ package org.opengrok.indexer.index; @@ -34,6 +34,7 @@ import java.net.URISyntaxException; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; class IndexerMainTest { private TestRepository repository; @@ -77,7 +78,7 @@ void testMainWithH() { RuntimeEnvironment env = RuntimeEnvironment.getInstance(); String[] argv = {"-S", "-H", "-s", repository.getSourceRoot(), "-d", repository.getDataRoot(), "-v", "-c", env.getCtags()}; - Indexer.main(argv); + Indexer.runMain(argv); checkNumberOfThreads(); } @@ -90,7 +91,16 @@ void testMainWithoutH() { RuntimeEnvironment env = RuntimeEnvironment.getInstance(); String[] argv = {"-S", "-P", "-s", repository.getSourceRoot(), "-d", repository.getDataRoot(), "-v", "-c", env.getCtags()}; - Indexer.main(argv); + Indexer.runMain(argv); checkNumberOfThreads(); } + + @Test + void testSubFilesWithProjectsDisabled() { + RuntimeEnvironment env = RuntimeEnvironment.getInstance(); + env.setProjectsEnabled(false); + String[] argv = {"-s", repository.getSourceRoot(), + "-d", repository.getDataRoot(), "-v", "-c", env.getCtags(), "foo"}; + assertNotEquals(0, Indexer.runMain(argv)); + } } diff --git a/opengrok-web/src/main/java/org/opengrok/web/WebappListener.java b/opengrok-web/src/main/java/org/opengrok/web/WebappListener.java index 7c806d9c5e4..ea0e69577ac 100644 --- a/opengrok-web/src/main/java/org/opengrok/web/WebappListener.java +++ b/opengrok-web/src/main/java/org/opengrok/web/WebappListener.java @@ -40,6 +40,7 @@ import org.opengrok.indexer.index.IndexCheck; import org.opengrok.indexer.index.IndexCheckException; import org.opengrok.indexer.logger.LoggerFactory; +import org.opengrok.indexer.util.RuntimeUtil; import org.opengrok.indexer.web.SearchHelper; import org.opengrok.web.api.ApiTaskManager; import org.opengrok.web.api.v1.controller.ConfigurationController; @@ -79,8 +80,9 @@ public void contextInitialized(final ServletContextEvent servletContextEvent) { ServletContext context = servletContextEvent.getServletContext(); RuntimeEnvironment env = RuntimeEnvironment.getInstance(); - LOGGER.log(Level.INFO, "Starting webapp with version {0} ({1}) on Java {2}", - new Object[]{Info.getVersion(), Info.getRevision(), Runtime.version()}); + LOGGER.log(Level.INFO, "Starting webapp with version {0} ({1}) on Java {2} with properties {3}", + new Object[]{Info.getVersion(), Info.getRevision(), RuntimeUtil.getJavaVersion(), + RuntimeUtil.getJavaProperties()}); checkJavaVersion(); diff --git a/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/DirectoryListingControllerTest.java b/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/DirectoryListingControllerTest.java index 890fd91272a..a2cb4dee895 100644 --- a/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/DirectoryListingControllerTest.java +++ b/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/DirectoryListingControllerTest.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. */ package org.opengrok.web.api.v1.controller; @@ -52,9 +52,9 @@ import java.io.File; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -101,7 +101,9 @@ public void setUp() throws Exception { null); // repositories - needed when refreshing history partially // Run the indexer so that LOC fields are populated. - Indexer.getInstance().doIndexerExecution(Collections.singletonList("/git"), null); + Project project = Project.getProject("/git"); + assertNotNull(project); + Indexer.getInstance().doIndexerExecution(Set.of(project), null); } @AfterEach diff --git a/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/FileControllerTest.java b/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/FileControllerTest.java index 34ca34bb267..8e68fa5ec7f 100644 --- a/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/FileControllerTest.java +++ b/opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/FileControllerTest.java @@ -18,7 +18,7 @@ */ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * Portions Copyright (c) 2020, Chris Fraire . */ package org.opengrok.web.api.v1.controller; @@ -39,6 +39,7 @@ import org.opengrok.indexer.authorization.AuthorizationFramework; import org.opengrok.indexer.authorization.AuthorizationPlugin; import org.opengrok.indexer.authorization.AuthorizationStack; +import org.opengrok.indexer.configuration.Project; import org.opengrok.indexer.configuration.RuntimeEnvironment; import org.opengrok.indexer.history.HistoryGuru; import org.opengrok.indexer.history.RepositoryFactory; @@ -54,9 +55,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static org.junit.jupiter.api.Assertions.assertAll; @@ -105,7 +106,9 @@ public void setUp() throws Exception { // don't create dictionary null, // subFiles - needed when refreshing history partially null); // repositories - needed when refreshing history partially - Indexer.getInstance().doIndexerExecution(Collections.singletonList("/git"), null); + Project project = Project.getProject("/git"); + assertNotNull(project); + Indexer.getInstance().doIndexerExecution(Set.of(project), null); } @AfterEach