diff --git a/gradle/changelog/index_type_npe.yaml b/gradle/changelog/index_type_npe.yaml new file mode 100644 index 0000000000..cf2b2fbbed --- /dev/null +++ b/gradle/changelog/index_type_npe.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Do not process index types which no longer exist ([#1985](https://github.com/scm-manager/scm-manager/pull/1985)) diff --git a/scm-webapp/src/main/java/sonia/scm/search/LuceneSearchEngine.java b/scm-webapp/src/main/java/sonia/scm/search/LuceneSearchEngine.java index 1f1e65c699..227098d9f5 100644 --- a/scm-webapp/src/main/java/sonia/scm/search/LuceneSearchEngine.java +++ b/scm-webapp/src/main/java/sonia/scm/search/LuceneSearchEngine.java @@ -27,6 +27,8 @@ package sonia.scm.search; import com.google.common.base.Joiner; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sonia.scm.work.CentralWorkQueue; import sonia.scm.work.CentralWorkQueue.Enqueue; import sonia.scm.work.Task; @@ -41,6 +43,8 @@ import java.util.stream.Collectors; public class LuceneSearchEngine implements SearchEngine { + private static final Logger LOG = LoggerFactory.getLogger(LuceneSearchEngine.class); + private final IndexManager indexManager; private final SearchableTypeResolver resolver; private final LuceneQueryBuilderFactory queryBuilderFactory; @@ -128,10 +132,19 @@ public class LuceneSearchEngine implements SearchEngine { indexManager.all() .stream() .filter(predicate) + .filter(this::isTypeAvailable) .map(details -> new IndexParams(details.getName(), resolver.resolve(details.getType()))) .forEach(consumer); } + private boolean isTypeAvailable(IndexDetails details) { + if (details.getType() == null) { + LOG.info("no type found for index with name '{}'; index will not be updated", details.getName()); + return false; + } + return true; + } + private void batch(IndexParams params, Task task) { LuceneSearchEngine.this.enqueue(params.getSearchableType(), params.getIndex(), resources, task); } diff --git a/scm-webapp/src/main/java/sonia/scm/search/SearchableTypeResolver.java b/scm-webapp/src/main/java/sonia/scm/search/SearchableTypeResolver.java index f1fe8f256e..fdc70929b5 100644 --- a/scm-webapp/src/main/java/sonia/scm/search/SearchableTypeResolver.java +++ b/scm-webapp/src/main/java/sonia/scm/search/SearchableTypeResolver.java @@ -86,9 +86,6 @@ class SearchableTypeResolver { } public LuceneSearchableType resolve(Class type) { - if (type == null) { - throw notFound(entity("type", "null")); - } LuceneSearchableType searchableType = classToSearchableType.get(type); if (searchableType == null) { throw notFound(entity("type", type.getName())); diff --git a/scm-webapp/src/test/java/sonia/scm/search/LuceneSearchEngineTest.java b/scm-webapp/src/test/java/sonia/scm/search/LuceneSearchEngineTest.java index 5ea60f4421..4a69badfb0 100644 --- a/scm-webapp/src/test/java/sonia/scm/search/LuceneSearchEngineTest.java +++ b/scm-webapp/src/test/java/sonia/scm/search/LuceneSearchEngineTest.java @@ -54,6 +54,7 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -341,6 +342,15 @@ class LuceneSearchEngineTest { verify(enqueue.runAsAdmin()).enqueue(any(Task.class)); } + @Test + void shouldIgnoreDetailsWithMissingType() { + mockDetails(new LuceneIndexDetails(null, "default")); + + searchEngine.forIndices().batch(index -> {}); + + verify(enqueue.runAsAdmin(), never()).enqueue(any(Task.class)); + } + private void mockDetails(LuceneIndexDetails... details) { for (LuceneIndexDetails detail : details) { mockType(detail.getType()); @@ -360,20 +370,19 @@ class LuceneSearchEngineTest { } private void mockType(Class type){ - LuceneSearchableType searchableType = mock(LuceneSearchableType.class); - lenient().when(searchableType.getType()).thenAnswer(ic -> type); - lenient().when(searchableType.getName()).thenReturn(type.getSimpleName().toLowerCase(Locale.ENGLISH)); - lenient().when(resolver.resolve(type)).thenReturn(searchableType); + if (type != null) { + LuceneSearchableType searchableType = mock(LuceneSearchableType.class); + lenient().when(searchableType.getType()).thenAnswer(ic -> type); + lenient().when(searchableType.getName()).thenReturn(type.getSimpleName().toLowerCase(Locale.ENGLISH)); + lenient().when(resolver.resolve(type)).thenReturn(searchableType); + } } - } - public static class DummyIndexTask implements IndexTask { @Override public void update(Index index) { - } } diff --git a/scm-webapp/src/test/java/sonia/scm/search/SearchableTypeResolverTest.java b/scm-webapp/src/test/java/sonia/scm/search/SearchableTypeResolverTest.java deleted file mode 100644 index 68759555c1..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/search/SearchableTypeResolverTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2020-present Cloudogu GmbH and Contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package sonia.scm.search; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import sonia.scm.NotFoundException; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -@ExtendWith(MockitoExtension.class) -class SearchableTypeResolverTest { - - private final SearchableTypeResolver resolver = new SearchableTypeResolver(); - - @Test - void shouldThrowNotFoundForNullValue() { - assertThrows(NotFoundException.class, () -> resolver.resolve(null)); - } -}