diff --git a/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/actions/Boot_27_30_AddLoggingDateFormat.java b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/actions/Boot_27_30_AddLoggingDateFormat.java new file mode 100644 index 000000000..8f1721394 --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/actions/Boot_27_30_AddLoggingDateFormat.java @@ -0,0 +1,29 @@ +package org.springframework.sbm.boot.upgrade_27_30.actions; + +import org.springframework.sbm.boot.properties.finder.SpringBootDefaultPropertiesFinder; +import org.springframework.sbm.engine.context.ProjectContext; +import org.springframework.sbm.engine.recipe.AbstractAction; + +import java.util.Optional; + +public class Boot_27_30_AddLoggingDateFormat extends AbstractAction { + + public static final String LOGGING_PATTERN_DATEFORMAT = "logging.pattern.dateformat"; + + @Override + public void apply(ProjectContext context) { + SpringBootDefaultPropertiesFinder springBootDefaultPropertiesFinder = new SpringBootDefaultPropertiesFinder(); + + context.getApplicationModules() + .getTopmostApplicationModules() + .stream() + .map(m -> m.searchMainResources(springBootDefaultPropertiesFinder)) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(p -> { + if(!p.getProperty(LOGGING_PATTERN_DATEFORMAT).isPresent()) { + p.setProperty(LOGGING_PATTERN_DATEFORMAT, "yyyy-MM-dd HH:mm:ss.SSS"); + } + }); + } +} diff --git a/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/checks/LoggingDateFormatOverrideSectionBuilder.java b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/checks/LoggingDateFormatOverrideSectionBuilder.java new file mode 100644 index 000000000..c35ed0eb9 --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/checks/LoggingDateFormatOverrideSectionBuilder.java @@ -0,0 +1,48 @@ +package org.springframework.sbm.boot.upgrade_27_30.checks; + +import org.springframework.sbm.boot.asciidoctor.ChangeSection; +import org.springframework.sbm.boot.asciidoctor.Section; +import org.springframework.sbm.boot.asciidoctor.TodoList; +import org.springframework.sbm.boot.upgrade_27_30.Sbu30_PreconditionCheck; +import org.springframework.sbm.boot.upgrade_27_30.Sbu30_PreconditionCheckResult; +import org.springframework.sbm.boot.upgrade_27_30.Sbu30_UpgradeSectionBuilder; +import org.springframework.sbm.boot.upgrade_27_30.filter.LoggingDateFormatPropertyFinder; +import org.springframework.sbm.engine.context.ProjectContext; + +import static org.springframework.sbm.engine.precondition.PreconditionCheck.ResultState.PASSED; + +public class LoggingDateFormatOverrideSectionBuilder implements Sbu30_PreconditionCheck, Sbu30_UpgradeSectionBuilder { + + @Override + public boolean isApplicable(ProjectContext projectContext) { + return projectContext + .search(new LoggingDateFormatPropertyFinder()) + .isEmpty(); + } + + @Override + public Section build(ProjectContext projectContext) { + return ChangeSection.RelevantChangeSection.builder() + .title("Changes in logging date format of log messages to align with the ISO-8601") + .paragraph("The new default format yyyy-MM-dd’T’HH:mm:ss.SSSXXX uses a T to separate the date and time instead of a space character and adds the timezone offset to the end. ") + .relevanceSection() + .paragraph("The scan found there is no existing override. Hence a default boot property will be created with date format set to old style i.e. yyyy-MM-dd HH:mm:ss.SSSXXX") + .todoSection() + .todoList( + TodoList.builder() + .todo( + TodoList.Todo.builder() + .text("If you wish to continue using new style remove the logging.pattern.dateformat from the applications.properties file.") + .build() + ) + .build() + ) + .build(); + } + + @Override + public Sbu30_PreconditionCheckResult run(ProjectContext context) { + return isApplicable(context) ? new Sbu30_PreconditionCheckResult(PASSED, "Override logging date format to yyyy-MM-dd HH:mm:ss.SSSXXX") + : new Sbu30_PreconditionCheckResult(PASSED, "Already logging format provided. No overrides required."); + } +} diff --git a/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/conditions/LoggingDateFormatCondition.java b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/conditions/LoggingDateFormatCondition.java new file mode 100644 index 000000000..695e2cf2a --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/conditions/LoggingDateFormatCondition.java @@ -0,0 +1,20 @@ +package org.springframework.sbm.boot.upgrade_27_30.conditions; + +import org.springframework.sbm.boot.upgrade_27_30.filter.LoggingDateFormatPropertyFinder; +import org.springframework.sbm.engine.context.ProjectContext; +import org.springframework.sbm.engine.recipe.Condition; + +import java.util.List; + +public class LoggingDateFormatCondition implements Condition { + + @Override + public String getDescription() { + return null; + } + + @Override + public boolean evaluate(ProjectContext context) { + return context.search(new LoggingDateFormatPropertyFinder()).isEmpty(); + } +} diff --git a/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/filter/LoggingDateFormatPropertyFinder.java b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/filter/LoggingDateFormatPropertyFinder.java new file mode 100644 index 000000000..bc6db3c8e --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/main/java/org/springframework/sbm/boot/upgrade_27_30/filter/LoggingDateFormatPropertyFinder.java @@ -0,0 +1,31 @@ +package org.springframework.sbm.boot.upgrade_27_30.filter; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.sbm.boot.properties.api.SpringBootApplicationProperties; +import org.springframework.sbm.boot.properties.search.SpringBootApplicationPropertiesResourceListFilter; +import org.springframework.sbm.common.filter.PathPatternMatchingProjectResourceFinder; +import org.springframework.sbm.project.resource.ProjectResource; +import org.springframework.sbm.project.resource.ProjectResourceSet; +import org.springframework.sbm.project.resource.filter.ProjectResourceFinder; +import org.springframework.sbm.properties.api.PropertiesSource; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + +@Slf4j +public class LoggingDateFormatPropertyFinder implements ProjectResourceFinder> { + + private static final String LOGGING_DATE_FORMAT_KEY = "logging.pattern.dateformat"; + + @Override + public List apply(ProjectResourceSet projectResourceSet) { + List springBootApplicationProperties = new SpringBootApplicationPropertiesResourceListFilter().apply(projectResourceSet); + + return springBootApplicationProperties.stream() + .filter(x -> x.getProperty(LOGGING_DATE_FORMAT_KEY).isPresent()) + .toList(); + } +} diff --git a/components/sbm-recipes-boot-upgrade/src/main/resources/recipes/boot-2.7-3.0-upgrade-report.yaml b/components/sbm-recipes-boot-upgrade/src/main/resources/recipes/boot-2.7-3.0-upgrade-report.yaml index c41126a1f..d9f878ff0 100644 --- a/components/sbm-recipes-boot-upgrade/src/main/resources/recipes/boot-2.7-3.0-upgrade-report.yaml +++ b/components/sbm-recipes-boot-upgrade/src/main/resources/recipes/boot-2.7-3.0-upgrade-report.yaml @@ -8,4 +8,13 @@ - type: org.springframework.sbm.boot.upgrade_27_30.SpringBoot30UpgradeReport description: "Create report" condition: - type: org.springframework.sbm.common.migration.conditions.TrueCondition \ No newline at end of file + type: org.springframework.sbm.common.migration.conditions.TrueCondition + - type: org.springframework.sbm.boot.properties.actions.AddSpringBootApplicationPropertiesAction + description: "Adds default spring boot properties to project. For multi-module project, adds default spring boot properties to every module with jar packaging" + condition: + type: org.springframework.sbm.boot.upgrade_27_30.conditions.LoggingDateFormatCondition + addDefaultPropertiesFileToTopModules: true + - type: org.springframework.sbm.boot.upgrade_27_30.actions.Boot_27_30_AddLoggingDateFormat + description: "Sets logging date format to yyyy-MM-dd HH:mm:ss.SSS" + condition: + type: org.springframework.sbm.boot.upgrade_27_30.conditions.LoggingDateFormatCondition \ No newline at end of file diff --git a/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/actions/Boot_27_30_AddLoggingDateFormatTest.java b/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/actions/Boot_27_30_AddLoggingDateFormatTest.java new file mode 100644 index 000000000..d651f047c --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/actions/Boot_27_30_AddLoggingDateFormatTest.java @@ -0,0 +1,34 @@ +package org.springframework.sbm.boot.upgrade_27_30.actions; + +import org.junit.jupiter.api.Test; +import org.springframework.sbm.boot.properties.SpringApplicationPropertiesPathMatcher; +import org.springframework.sbm.boot.properties.SpringBootApplicationPropertiesRegistrar; +import org.springframework.sbm.boot.properties.api.SpringBootApplicationProperties; +import org.springframework.sbm.boot.properties.search.SpringBootApplicationPropertiesResourceListFilter; +import org.springframework.sbm.engine.context.ProjectContext; +import org.springframework.sbm.project.resource.TestProjectContext; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class Boot_27_30_AddLoggingDateFormatTest { + + private static final String DUMMY_PROPERTY_FILE = "foo=bar\n" + + "defaultBasePackage=org.springframework.sbm"; + + @Test + public void givenAProjectWithoutLoggingDateFormatOverride_andSpringBootProperties_applyAction_expectPropertyAdded(){ + ProjectContext projectContext = TestProjectContext.buildProjectContext() + .addRegistrar(new SpringBootApplicationPropertiesRegistrar(new SpringApplicationPropertiesPathMatcher())) + .addProjectResource("src/main/resources/application.properties", DUMMY_PROPERTY_FILE) + .build(); + + Boot_27_30_AddLoggingDateFormat action = new Boot_27_30_AddLoggingDateFormat(); + action.apply(projectContext); + + List bootApplicationProperties = new SpringBootApplicationPropertiesResourceListFilter().apply(projectContext.getProjectResources()); + assertThat(bootApplicationProperties.size()).isEqualTo(1); + assertThat(bootApplicationProperties.get(0).getProperty("logging.pattern.dateformat").isPresent()).isTrue(); + } +} diff --git a/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/confitions/LoggingDateFormatConditionTest.java b/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/confitions/LoggingDateFormatConditionTest.java new file mode 100644 index 000000000..67c36f201 --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/confitions/LoggingDateFormatConditionTest.java @@ -0,0 +1,47 @@ +package org.springframework.sbm.boot.upgrade_27_30.confitions; + +import org.junit.jupiter.api.Test; +import org.springframework.sbm.boot.properties.SpringApplicationPropertiesPathMatcher; +import org.springframework.sbm.boot.properties.SpringBootApplicationPropertiesRegistrar; +import org.springframework.sbm.boot.upgrade_27_30.conditions.LoggingDateFormatCondition; +import org.springframework.sbm.engine.context.ProjectContext; +import org.springframework.sbm.project.resource.TestProjectContext; + +import java.nio.file.Path; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class LoggingDateFormatConditionTest { + + private static final String APPLICATION_PROPERTIES_WITH_LOG_DATE_FORMAT = "foo=bar\n" + + "migrate=true\n" + + "logging.pattern.dateformat=xyz\n"; + + private static final String APPLICATION_PROPERTIES_WITHOUT_LOG_DATE_FORMAT = "foo=bar\n" + + "migrate=true\n"; + + + @Test + public void givenProjectWithLogDateFormatCustomization_evaluateCondition_expectFalse(){ + ProjectContext projectContext = TestProjectContext.buildProjectContext() + .addRegistrar(new SpringBootApplicationPropertiesRegistrar(new SpringApplicationPropertiesPathMatcher())) + .addProjectResource(Path.of("src", "main", "resources", "application.properties"), APPLICATION_PROPERTIES_WITH_LOG_DATE_FORMAT) + .build(); + + LoggingDateFormatCondition condition = new LoggingDateFormatCondition(); + + assertThat(condition.evaluate(projectContext)).isFalse(); + } + + @Test + public void givenProjectWithoutLogDateFormatCustomization_evaluateCondition_expectTrue(){ + ProjectContext projectContext = TestProjectContext.buildProjectContext() + .addRegistrar(new SpringBootApplicationPropertiesRegistrar(new SpringApplicationPropertiesPathMatcher())) + .addProjectResource(Path.of("src", "main", "resources", "application.properties"), APPLICATION_PROPERTIES_WITHOUT_LOG_DATE_FORMAT) + .build(); + + LoggingDateFormatCondition condition = new LoggingDateFormatCondition(); + + assertThat(condition.evaluate(projectContext)).isTrue(); + } +} diff --git a/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/filter/LoggingDateFormatPropertyFinderTest.java b/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/filter/LoggingDateFormatPropertyFinderTest.java new file mode 100644 index 000000000..cc70b2a10 --- /dev/null +++ b/components/sbm-recipes-boot-upgrade/src/test/java/org/springframework/sbm/boot/upgrade_27_30/filter/LoggingDateFormatPropertyFinderTest.java @@ -0,0 +1,84 @@ +package org.springframework.sbm.boot.upgrade_27_30.filter; + +import org.junit.jupiter.api.Test; +import org.openrewrite.SourceFile; +import org.springframework.sbm.boot.properties.SpringApplicationPropertiesPathMatcher; +import org.springframework.sbm.boot.properties.SpringBootApplicationPropertiesRegistrar; +import org.springframework.sbm.boot.upgrade_27_30.filter.LoggingDateFormatPropertyFinder; +import org.springframework.sbm.build.api.BuildFile; +import org.springframework.sbm.build.impl.OpenRewriteMavenBuildFile; +import org.springframework.sbm.engine.context.ProjectContext; +import org.springframework.sbm.project.resource.*; +import org.springframework.sbm.properties.api.PropertiesSource; + +import java.nio.file.Path; +import java.util.List; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class LoggingDateFormatPropertyFinderTest { + + private static final String APPLICATION_PROPERTIES_WITH_LOG_DATE_FORMAT = "foo=bar\n" + + "migrate=true\n" + + "logging.pattern.dateformat=xyz\n"; + + private static final String MULTI_MODULE_POM_XML = "\n" + + " 4.0.0\n" + + "\n" + + " org.springframework.sbm\n" + + " spring-boot-migrator\n" + + " 0.11.2-SNAPSHOT\n" + + " pom\n" + + " \n" + + " module1\n" + + " module2\n" + + " \n" + + ""; + + private static final String SUB_MODULE_POM_XML = "\n" + + " \n" + + " spring-boot-migrator\n" + + " org.springframework.sbm\n" + + " 0.11.2-SNAPSHOT\n" + + " ../../pom.xml\n" + + " \n" + + " 4.0.0\n" + + "\n" + + " {{module}}\n" + + ""; + + @Test + public void givenProjectWithLogDateFormatCustomization_findResources_returnResource(){ + ProjectContext projectContext = TestProjectContext.buildProjectContext() + .addRegistrar(new SpringBootApplicationPropertiesRegistrar(new SpringApplicationPropertiesPathMatcher())) + .addProjectResource(Path.of("src", "main", "resources", "application.properties"), APPLICATION_PROPERTIES_WITH_LOG_DATE_FORMAT) + .build(); + + LoggingDateFormatPropertyFinder loggingDateFormatPropertyFinder = new LoggingDateFormatPropertyFinder(); + List propertiesSources = loggingDateFormatPropertyFinder.apply(projectContext.getProjectResources()); + + assertThat(propertiesSources.size()).isEqualTo(1); + assertThat(propertiesSources.get(0).getProperty("logging.pattern.dateformat").isPresent()).isTrue(); + } + + @Test + public void givenMultiModuleProjectWithLogDateFormatCustomization_findResources_returnResource(){ + ProjectContext projectContext = TestProjectContext.buildProjectContext() + .withMavenRootBuildFileSource(MULTI_MODULE_POM_XML) + .addProjectResource(Path.of("module1","pom.xml"),SUB_MODULE_POM_XML.replace("{{module}}", "module1")) + .addProjectResource(Path.of("module2","pom.xml"),SUB_MODULE_POM_XML.replace("{{module}}", "module2")) + .addRegistrar(new SpringBootApplicationPropertiesRegistrar(new SpringApplicationPropertiesPathMatcher())) + .addProjectResource(Path.of("module1","src", "main", "resources", "application.properties"), APPLICATION_PROPERTIES_WITH_LOG_DATE_FORMAT) + .addProjectResource(Path.of("module2","src", "main", "resources", "application.properties"), APPLICATION_PROPERTIES_WITH_LOG_DATE_FORMAT) + .build(); + + LoggingDateFormatPropertyFinder loggingDateFormatPropertyFinder = new LoggingDateFormatPropertyFinder(); + List propertiesSources = loggingDateFormatPropertyFinder.apply(projectContext.getProjectResources()); + + assertThat(propertiesSources.size()).isEqualTo(2); + assertThat(propertiesSources.get(0).getProperty("logging.pattern.dateformat").isPresent()).isTrue(); + assertThat(propertiesSources.get(1).getProperty("logging.pattern.dateformat").isPresent()).isTrue(); + } +} diff --git a/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/actions/AddSpringBootApplicationPropertiesAction.java b/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/actions/AddSpringBootApplicationPropertiesAction.java index c280ab07e..8e0fb4858 100644 --- a/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/actions/AddSpringBootApplicationPropertiesAction.java +++ b/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/actions/AddSpringBootApplicationPropertiesAction.java @@ -15,6 +15,8 @@ */ package org.springframework.sbm.boot.properties.actions; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; import org.springframework.sbm.boot.properties.api.SpringBootApplicationProperties; import org.springframework.sbm.boot.properties.search.SpringBootApplicationPropertiesResourceListFilter; import org.springframework.sbm.build.api.ApplicationModule; @@ -23,16 +25,27 @@ import java.nio.file.Path; +@NoArgsConstructor +@AllArgsConstructor public class AddSpringBootApplicationPropertiesAction extends AbstractAction { public static final Path APPLICATION_PROPERTIES_PATH = Path.of("src/main/resources/application.properties"); + private Boolean addDefaultPropertiesFileToTopModules = Boolean.FALSE; + @Override public void apply(ProjectContext context) { - SpringBootApplicationProperties springBootApplicationProperties = - SpringBootApplicationProperties.newApplicationProperties( - context.getProjectRootDirectory(), APPLICATION_PROPERTIES_PATH); - context.getProjectResources().add(springBootApplicationProperties); + if(addDefaultPropertiesFileToTopModules){ + context.getApplicationModules() + .getTopmostApplicationModules() + .stream() + .forEach(this::apply); + } else { + SpringBootApplicationProperties springBootApplicationProperties = + SpringBootApplicationProperties.newApplicationProperties( + context.getProjectRootDirectory(), APPLICATION_PROPERTIES_PATH); + context.getProjectResources().add(springBootApplicationProperties); + } } public void apply(ApplicationModule module) { diff --git a/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinder.java b/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinder.java index efee0572d..964992180 100644 --- a/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinder.java +++ b/components/sbm-support-boot/src/main/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinder.java @@ -28,8 +28,8 @@ public class SpringBootDefaultPropertiesFinder implements ProjectResourceFinder< @Override public Optional apply(ProjectResourceSet projectResourceSet) { return projectResourceSet.stream() - .filter(r -> r.getSourceFile() instanceof Properties.File) - .map(r -> new SpringBootApplicationProperties(r.getAbsoluteProjectDir(), (Properties.File) r.getSourceFile())) + .filter(r -> r instanceof SpringBootApplicationProperties) + .map(SpringBootApplicationProperties.class::cast) .filter(SpringBootApplicationProperties::isDefaultProperties) .findFirst(); } diff --git a/components/sbm-support-boot/src/test/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinderTest.java b/components/sbm-support-boot/src/test/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinderTest.java index 4570dda77..4d7dde42c 100644 --- a/components/sbm-support-boot/src/test/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinderTest.java +++ b/components/sbm-support-boot/src/test/java/org/springframework/sbm/boot/properties/finder/SpringBootDefaultPropertiesFinderTest.java @@ -16,6 +16,8 @@ package org.springframework.sbm.boot.properties.finder; import org.junit.jupiter.api.Test; +import org.springframework.sbm.boot.properties.SpringApplicationPropertiesPathMatcher; +import org.springframework.sbm.boot.properties.SpringBootApplicationPropertiesRegistrar; import org.springframework.sbm.engine.context.ProjectContext; import org.springframework.sbm.project.resource.TestProjectContext; @@ -28,6 +30,7 @@ public class SpringBootDefaultPropertiesFinderTest { @Test public void givenAProjectWithDefaultSpringBootProperties_applyFinder_expectPropertyFile(){ ProjectContext projectContext = TestProjectContext.buildProjectContext() + .addRegistrar(new SpringBootApplicationPropertiesRegistrar(new SpringApplicationPropertiesPathMatcher())) .addProjectResource(Path.of("src","main", "resources", "application.properties"), "foo=bar") .build();