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 93ab626edd2..c0bff1ea9cd 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 @@ -38,6 +38,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashMap; @@ -54,16 +55,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.zip.GZIPOutputStream; -import jakarta.ws.rs.client.ClientBuilder; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.Response; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.DateTools; @@ -124,9 +121,6 @@ import org.opengrok.indexer.util.TandemPath; import org.opengrok.indexer.web.Util; -import static org.opengrok.indexer.index.IndexerUtil.getWebAppHeaders; -import static org.opengrok.indexer.web.ApiUtils.waitForAsyncApi; - /** * This class is used to create / update the index databases. Currently, we use * one index database per project. @@ -423,44 +417,21 @@ private void markProjectIndexed(Project project) { return; } - // Also need to store the correct value in configuration - // when indexer writes it to a file. + // Also need to store the correct value in configuration when indexer writes it to a file. project.setIndexed(true); if (env.getConfigURI() == null) { return; } - Response response; - try { - response = ClientBuilder.newBuilder().connectTimeout(env.getConnectTimeout(), TimeUnit.SECONDS).build() - .target(env.getConfigURI()) - .path("api") - .path("v1") - .path("projects") - .path(Util.uriEncode(project.getName())) - .path("indexed") - .request() - .headers(getWebAppHeaders()) - .put(Entity.text("")); - } catch (RuntimeException e) { - LOGGER.log(Level.WARNING, String.format("Could not notify the webapp that project %s was indexed", - project), e); + // If this project is not known to the webapp yet, there is no point in setting its indexed property. + Collection webappProjects = IndexerUtil.getProjects(env.getConfigURI()); + if (!webappProjects.contains(project.getName())) { + LOGGER.log(Level.FINEST, "Project {0} is not known to the webapp", project); return; } - if (response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) { - try { - response = waitForAsyncApi(response); - } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, "interrupted while waiting for API response", e); - } - } - - if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) { - LOGGER.log(Level.WARNING, "Could not notify the webapp that project {0} was indexed: {1}", - new Object[] {project, response}); - } + IndexerUtil.markProjectIndexed(env.getConfigURI(), project); } private static List getRepositoriesForProject(Project project) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerUtil.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerUtil.java index 8a2aa9d02d8..4dc26befebc 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerUtil.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/IndexerUtil.java @@ -18,11 +18,14 @@ */ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. */ package org.opengrok.indexer.index; import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.GenericType; +import jakarta.ws.rs.core.MediaType; +import org.opengrok.indexer.configuration.Project; import org.opengrok.indexer.configuration.RuntimeEnvironment; import jakarta.ws.rs.ProcessingException; @@ -35,11 +38,21 @@ import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; +import org.opengrok.indexer.logger.LoggerFactory; +import org.opengrok.indexer.web.Util; +import java.util.Collection; +import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.opengrok.indexer.web.ApiUtils.waitForAsyncApi; public class IndexerUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(IndexerUtil.class); + private IndexerUtil() { } @@ -57,24 +70,24 @@ public static MultivaluedMap getWebAppHeaders() { } /** - * Enable projects in the remote host application. + * Enable projects in the remote application. *

* NOTE: performs a check if the projects are already enabled, * before making the change request * - * @param host the url to the remote host + * @param webappUri the url to the remote web application * @throws ResponseProcessingException in case processing of a received HTTP response fails * @throws ProcessingException in case the request processing or subsequent I/O operation fails * @throws WebApplicationException in case the response status code of the response returned by the server is not successful */ - public static void enableProjects(final String host) throws + public static void enableProjects(final String webappUri) throws ResponseProcessingException, ProcessingException, WebApplicationException { try (Client client = ClientBuilder.newBuilder(). connectTimeout(RuntimeEnvironment.getInstance().getConnectTimeout(), TimeUnit.SECONDS).build()) { - final Invocation.Builder request = client.target(host) + final Invocation.Builder request = client.target(webappUri) .path("api") .path("v1") .path("configuration") @@ -92,4 +105,58 @@ public static void enableProjects(final String host) throws } } } + + /** + * Mark project as indexed via API call. Assumes the project is already known to the webapp. + * @param webappUri URI for the webapp + * @param project project to mark as indexed + */ + public static void markProjectIndexed(String webappUri, Project project) { + Response response; + try (Client client = ClientBuilder.newBuilder(). + connectTimeout(RuntimeEnvironment.getInstance().getConnectTimeout(), TimeUnit.SECONDS).build()) { + response = client.target(webappUri) + .path("api") + .path("v1") + .path("projects") + .path(Util.uriEncode(project.getName())) + .path("indexed") + .request() + .headers(getWebAppHeaders()) + .put(Entity.text("")); + + if (response.getStatus() == Response.Status.ACCEPTED.getStatusCode()) { + try { + response = waitForAsyncApi(response); + } catch (InterruptedException e) { + LOGGER.log(Level.WARNING, "interrupted while waiting for API response", e); + } + } + + if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) { + LOGGER.log(Level.WARNING, "Could not notify the webapp that project {0} was indexed: {1}", + new Object[] {project, response}); + } + } catch (RuntimeException e) { + LOGGER.log(Level.WARNING, String.format("Could not notify the webapp that project %s was indexed", + project), e); + } + } + + /** + * @param webappUri URI for the webapp + * @return list of projects known to the webapp + */ + public static Collection getProjects(String webappUri) { + try (Client client = ClientBuilder.newBuilder(). + connectTimeout(RuntimeEnvironment.getInstance().getConnectTimeout(), TimeUnit.SECONDS).build()) { + final Invocation.Builder request = client.target(webappUri) + .path("api") + .path("v1") + .path("projects") + .request(MediaType.APPLICATION_JSON) + .headers(getWebAppHeaders()); + return request.get(new GenericType>() { } ); + } + } } diff --git a/opengrok-web/src/main/java/org/opengrok/web/api/v1/controller/ProjectsController.java b/opengrok-web/src/main/java/org/opengrok/web/api/v1/controller/ProjectsController.java index 0af2aba8f81..61d6f583370 100644 --- a/opengrok-web/src/main/java/org/opengrok/web/api/v1/controller/ProjectsController.java +++ b/opengrok-web/src/main/java/org/opengrok/web/api/v1/controller/ProjectsController.java @@ -52,7 +52,6 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -135,17 +134,17 @@ private void addProjectWorkHorse(String projectName) { // This is the goal of this action: if an existing project // is re-added, this means its list of repositories has changed. List repos = getRepositoriesInDir(projDir); - List allrepos = env.getRepositories(); - synchronized (allrepos) { + List allRepositories = env.getRepositories(); + synchronized (allRepositories) { // newly added repository repos.stream() - .filter(repo -> !allrepos.contains(repo)) - .forEach(allrepos::add); + .filter(repo -> !allRepositories.contains(repo)) + .forEach(allRepositories::add); // deleted repository Optional.ofNullable(map.get(project)) .stream().flatMap(Collection::stream) .filter(repo -> !repos.contains(repo)) - .forEach(allrepos::remove); + .forEach(allRepositories::remove); } map.put(project, repos); @@ -163,10 +162,8 @@ private List getRepositoriesInDir(final File projDir) { } private Project disableProject(String projectName) { - Project project = env.getProjects().get(projectName); - if (project == null) { - throw new IllegalStateException("cannot get project \"" + projectName + "\""); - } + Project project = Optional.ofNullable(env.getProjects().get(projectName)). + orElseThrow(() -> new NotFoundException("cannot get project \"" + projectName + "\"")); // Remove the project from searches so no one can trip over incomplete index data. project.setIndexed(false); @@ -288,12 +285,9 @@ public Response deleteAnnotationCache(@Context HttpServletRequest request, private Project getProjectFromName(String projectNameParam) { // Avoid classification as a taint bug. final String projectName = Laundromat.launderInput(projectNameParam); - Project project = env.getProjects().get(projectName); - if (project == null) { - throw new IllegalStateException("cannot get project \"" + projectName + "\""); - } - return project; + return Optional.ofNullable(env.getProjects().get(projectName)). + orElseThrow(() -> new NotFoundException("cannot get project \"" + projectName + "\"")); } @DELETE @@ -340,11 +334,8 @@ public Response markIndexed(@Context HttpServletRequest request, @PathParam("pro // Avoid classification as a taint bug. final String projectName = Laundromat.launderInput(projectNameParam); - Project project = env.getProjects().get(projectName); - if (project == null) { - LOGGER.log(Level.WARNING, "cannot find project ''{0}'' to mark as indexed", projectName); - throw new NotFoundException(String.format("project '%s' does not exist", projectName)); - } + Project project = Optional.ofNullable(env.getProjects().get(projectName)). + orElseThrow(() -> new NotFoundException("cannot get project \"" + projectName + "\"")); project.setIndexed(true); @@ -358,7 +349,7 @@ public Response markIndexed(@Context HttpServletRequest request, @PathParam("pro Repository repo = getRepository(ri, CommandTimeoutType.RESTFUL); if (repo != null && repo.getCurrentVersion() != null && - repo.getCurrentVersion().length() > 0) { + !repo.getCurrentVersion().isEmpty()) { // getRepository() always creates fresh instance // of the Repository object so there is no need // to call setCurrentVersion() on it. @@ -412,17 +403,14 @@ public void set( @GET @Path("/{project}/property/{field}") @Produces(MediaType.APPLICATION_JSON) - public Object get(@PathParam("project") String projectName, @PathParam("field") String field) + public Object get(@PathParam("project") String projectNameParam, @PathParam("field") String field) throws IOException { // Avoid classification as a taint bug. - projectName = Laundromat.launderInput(projectName); + final String projectName = Laundromat.launderInput(projectNameParam); field = Laundromat.launderInput(field); - Project project = env.getProjects().get(projectName); - if (project == null) { - throw new WebApplicationException( - "cannot find project '" + projectName + "' to get a property", Response.Status.BAD_REQUEST); - } + Project project = Optional.ofNullable(env.getProjects().get(projectName)). + orElseThrow(() -> new NotFoundException("cannot get project \"" + projectName + "\"")); return ClassUtil.getFieldValue(project, field); }