From e56c0814eda1864e1dbe57fe7f4254c4081ea3b3 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 14 Nov 2024 21:14:49 +0100 Subject: [PATCH 1/3] feature: create project configuration exclusion filter implementation, fix #1581 --- ...jectGenerationConfigurationTypeFilter.java | 33 +++++++++++++++++ .../generator/project/ProjectGenerator.java | 36 +++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerationConfigurationTypeFilter.java diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerationConfigurationTypeFilter.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerationConfigurationTypeFilter.java new file mode 100644 index 0000000000..87d25bd716 --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerationConfigurationTypeFilter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project; + +/** + * Allows to filter {@link ProjectGenerationConfiguration}. + * + * @author Simon Zambrovski + */ +public interface ProjectGenerationConfigurationTypeFilter { + + /** + * Determines if the provided class matches the filter. + * @param type type to match. + * @return true, if the type matches. + */ + boolean match(Class type); + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java index 59d111662d..a5d01e35aa 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.support.SpringFactoriesLoader; +import org.springframework.util.ClassUtils; /** * Main entry point for project generation that processes a {@link ProjectDescription} by @@ -116,15 +117,44 @@ public T generate(ProjectDescription description, ProjectAssetGenerator p /** * Return the {@link ProjectGenerationConfiguration} class names that should be - * considered. By default this method will load candidates using - * {@link SpringFactoriesLoader} with {@link ProjectGenerationConfiguration}. + * considered. By default, this method will load candidates using + * {@link SpringFactoriesLoader} with {@link ProjectGenerationConfiguration} and + * exclude those which are matched by the + * {@link ProjectGenerationConfigurationTypeFilter} * @param description the description of the project to generate * @return a list of candidate configurations */ @SuppressWarnings("deprecation") protected List getCandidateProjectGenerationConfigurations(ProjectDescription description) { - return SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class, + List candidates = SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class, getClass().getClassLoader()); + ProjectGenerationConfigurationTypeFilter filter = getProjectGenerationConfigurationExclusionFilter(); + return candidates.stream().filter((candidate) -> { + Class type = this.resolveClass(candidate); + return type != null && !filter.match(type); + }).toList(); + } + + private Class resolveClass(String candidate) { + try { + return ClassUtils.forName(candidate, getClass().getClassLoader()); + } + catch (ClassNotFoundException ex) { + return null; + } + } + + ProjectGenerationConfigurationTypeFilter getProjectGenerationConfigurationExclusionFilter() { + List filters = SpringFactoriesLoader + .loadFactories(ProjectGenerationConfigurationTypeFilter.class, getClass().getClassLoader()); + // Build a composite filter, combining results with a logical OR + return (configurationClass) -> { + boolean excluded = false; + for (ProjectGenerationConfigurationTypeFilter filter : filters) { + excluded = excluded || filter.match(configurationClass); + } + return excluded; + }; } private void registerProjectDescription(ProjectGenerationContext context, ProjectDescription description) { From 56024068fa9c8e2f4a7e13e308fe4684ab7c24d0 Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 14 Nov 2024 21:15:04 +0100 Subject: [PATCH 2/3] fix test setup --- .../initializr/generator/project/ProjectGeneratorTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java index abf738f5d9..a9ce7bb6d3 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java @@ -181,8 +181,9 @@ void generateCanBeExtendedToFilterProjectContributors(@TempDir Path projectDir) given(description.getBuildSystem()).willReturn(new MavenBuildSystem()); ProjectGenerator generator = new ProjectGenerator(mockContextInitializr()) { @Override - protected List getCandidateProjectGenerationConfigurations(ProjectDescription description) { - assertThat(description).isSameAs(description); + protected List getCandidateProjectGenerationConfigurations( + ProjectDescription generatorDescription) { + assertThat(description).isSameAs(generatorDescription); return Collections.singletonList(TestProjectGenerationConfiguration.class.getName()); } }; From 0bd078e721bafc59839cf677a28f5898a967e24d Mon Sep 17 00:00:00 2001 From: Simon Zambrovski Date: Thu, 14 Nov 2024 21:40:08 +0100 Subject: [PATCH 3/3] added tests, improved generator methods --- .../generator/project/ProjectGenerator.java | 11 +++++-- .../project/ProjectGeneratorTests.java | 33 +++++++++++++++++++ .../TestProjectGenerationConfiguration2.java | 27 +++++++++++++++ ...tionConfiguration2ExcludingTypeFilter.java | 29 ++++++++++++++++ ...ationConfigurationExcludingTypeFilter.java | 28 ++++++++++++++++ .../test/resources/META-INF/spring.factories | 4 +++ 6 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2.java create mode 100644 initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2ExcludingTypeFilter.java create mode 100644 initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfigurationExcludingTypeFilter.java create mode 100644 initializr-generator/src/test/resources/META-INF/spring.factories diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java index a5d01e35aa..565015005c 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java @@ -124,10 +124,9 @@ public T generate(ProjectDescription description, ProjectAssetGenerator p * @param description the description of the project to generate * @return a list of candidate configurations */ - @SuppressWarnings("deprecation") + protected List getCandidateProjectGenerationConfigurations(ProjectDescription description) { - List candidates = SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class, - getClass().getClassLoader()); + List candidates = getProjectGenerationConfigurationFactoryNames(); ProjectGenerationConfigurationTypeFilter filter = getProjectGenerationConfigurationExclusionFilter(); return candidates.stream().filter((candidate) -> { Class type = this.resolveClass(candidate); @@ -144,6 +143,12 @@ private Class resolveClass(String candidate) { } } + @SuppressWarnings("deprecation") + List getProjectGenerationConfigurationFactoryNames() { + return SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class, + getClass().getClassLoader()); + } + ProjectGenerationConfigurationTypeFilter getProjectGenerationConfigurationExclusionFilter() { List filters = SpringFactoriesLoader .loadFactories(ProjectGenerationConfigurationTypeFilter.class, getClass().getClassLoader()); diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java index a9ce7bb6d3..02eb013112 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java @@ -25,6 +25,8 @@ import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem; import io.spring.initializr.generator.project.contributor.TestProjectGenerationConfiguration; +import io.spring.initializr.generator.project.contributor.TestProjectGenerationConfiguration2; +import org.assertj.core.util.Lists; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.mockito.InOrder; @@ -196,6 +198,37 @@ protected List getCandidateProjectGenerationConfigurations( verify(description).getBuildSystem(); } + @Test + void loadAndConstructProjectGenerationTypeExclusionFilter() { + ProjectGenerator generator = new ProjectGenerator(mockContextInitializr()); + ProjectGenerationConfigurationTypeFilter filter = generator.getProjectGenerationConfigurationExclusionFilter(); + assertThat(filter).isNotNull(); + assertThat(filter.match(TestProjectGenerationConfiguration.class)).isTrue(); + assertThat(filter.match(TestProjectGenerationConfiguration2.class)).isTrue(); + assertThat(filter.match(Integer.class)).isFalse(); + } + + @Test + void filterProjectContributorsCorrectly(@TempDir Path projectDir) { + ProjectDescription description = mock(ProjectDescription.class); + given(description.getArtifactId()).willReturn("test-custom-contributor"); + given(description.getBuildSystem()).willReturn(new MavenBuildSystem()); + ProjectGenerator generator = new ProjectGenerator(mockContextInitializr()) { + @Override + List getProjectGenerationConfigurationFactoryNames() { + return Lists.list(TestProjectGenerationConfiguration.class.getName(), + TestProjectGenerationConfiguration2.class.getName()); + } + + @Override + ProjectGenerationConfigurationTypeFilter getProjectGenerationConfigurationExclusionFilter() { + return TestProjectGenerationConfiguration2.class::equals; + } + }; + List candidates = generator.getCandidateProjectGenerationConfigurations(description); + assertThat(candidates).containsOnly(TestProjectGenerationConfiguration.class.getCanonicalName()); + } + @SuppressWarnings("unchecked") private Consumer mockContextInitializr() { return mock(Consumer.class); diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2.java new file mode 100644 index 0000000000..b23ffe6621 --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2.java @@ -0,0 +1,27 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project.contributor; + +import io.spring.initializr.generator.project.ProjectGenerationConfiguration; + +/** + * Test contributor. + */ +@ProjectGenerationConfiguration +public class TestProjectGenerationConfiguration2 { + +} diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2ExcludingTypeFilter.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2ExcludingTypeFilter.java new file mode 100644 index 0000000000..1fa713b32a --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration2ExcludingTypeFilter.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project.contributor; + +import io.spring.initializr.generator.project.ProjectGenerationConfigurationTypeFilter; + +public class TestProjectGenerationConfiguration2ExcludingTypeFilter + implements ProjectGenerationConfigurationTypeFilter { + + @Override + public boolean match(Class type) { + return TestProjectGenerationConfiguration2.class.equals(type); + } + +} diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfigurationExcludingTypeFilter.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfigurationExcludingTypeFilter.java new file mode 100644 index 0000000000..42884f9717 --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfigurationExcludingTypeFilter.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.spring.initializr.generator.project.contributor; + +import io.spring.initializr.generator.project.ProjectGenerationConfigurationTypeFilter; + +public class TestProjectGenerationConfigurationExcludingTypeFilter implements ProjectGenerationConfigurationTypeFilter { + + @Override + public boolean match(Class type) { + return TestProjectGenerationConfiguration.class.equals(type); + } + +} diff --git a/initializr-generator/src/test/resources/META-INF/spring.factories b/initializr-generator/src/test/resources/META-INF/spring.factories new file mode 100644 index 0000000000..20d001923c --- /dev/null +++ b/initializr-generator/src/test/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +io.spring.initializr.generator.project.ProjectGenerationConfigurationTypeFilter=\ + io.spring.initializr.generator.project.contributor.TestProjectGenerationConfigurationExcludingTypeFilter,\ + io.spring.initializr.generator.project.contributor.TestProjectGenerationConfiguration2ExcludingTypeFilter +