From b1352ca264af9de6de92776c4e510d697b542efc Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 29 Jan 2016 16:38:33 +0100 Subject: [PATCH] Add ProjectGeneratedEvent Replace `ProjectGenerationListener` by an event that is thrown via the regular `ApplicationEventPublisher`. This allows any arbitrary bean to listen to the event without the need of implementing an interface. Closes gh-184 --- .../config/InitializrAutoConfiguration.groovy | 6 +-- ...er.groovy => ProjectGeneratedEvent.groovy} | 21 +++++++--- .../ProjectGenerationMetricsListener.groovy | 14 ++++--- .../generator/ProjectGenerator.groovy | 21 +++++----- ...ojectGenerationMetricsListenerTests.groovy | 42 ++++++++++--------- .../generator/ProjectGeneratorTests.groovy | 38 +++++++++++------ 6 files changed, 84 insertions(+), 58 deletions(-) rename initializr/src/main/groovy/io/spring/initializr/generator/{ProjectGenerationListener.groovy => ProjectGeneratedEvent.groovy} (58%) diff --git a/initializr/src/main/groovy/io/spring/initializr/config/InitializrAutoConfiguration.groovy b/initializr/src/main/groovy/io/spring/initializr/config/InitializrAutoConfiguration.groovy index 8c6b6433..712dc5f8 100644 --- a/initializr/src/main/groovy/io/spring/initializr/config/InitializrAutoConfiguration.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/config/InitializrAutoConfiguration.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -83,9 +83,7 @@ class InitializrAutoConfiguration { @Bean @ConditionalOnMissingBean ProjectGenerator projectGenerator() { - def generator = new ProjectGenerator() - generator.listeners << metricsListener() - generator + new ProjectGenerator() } @Bean diff --git a/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationListener.groovy b/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGeneratedEvent.groovy similarity index 58% rename from initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationListener.groovy rename to initializr/src/main/groovy/io/spring/initializr/generator/ProjectGeneratedEvent.groovy index 23d80c8b..d6e264fb 100644 --- a/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationListener.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGeneratedEvent.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -17,17 +17,26 @@ package io.spring.initializr.generator /** - * Interface to be implemented by components that need to be aware of project generation - * related events. + * Event fired when a new project has been generated successfully. * * @author Stephane Nicoll * @since 1.0 */ -public interface ProjectGenerationListener { +class ProjectGeneratedEvent { /** - * Invoked when a project has been generated for the specified {@link ProjectRequest}. + * The {@link ProjectRequest} used to generate the project. */ - void onGeneratedProject(ProjectRequest request) + final ProjectRequest projectRequest + + /** + * The timestamp at which the project was generated + */ + final long timestamp + + ProjectGeneratedEvent(ProjectRequest projectRequest) { + this.projectRequest = projectRequest + this.timestamp = System.currentTimeMillis() + } } diff --git a/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListener.groovy b/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListener.groovy index 0d58d064..812c6274 100644 --- a/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListener.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListener.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -18,16 +18,17 @@ package io.spring.initializr.generator import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.actuate.metrics.CounterService +import org.springframework.context.event.EventListener import org.springframework.util.StringUtils /** - * A {@link ProjectGenerationListener} implementation that uses a {@link CounterService} - * to update various project related metrics. + * A {@link ProjectGeneratedEvent} listener that uses a {@link CounterService} to update + * various project related metrics. * * @author Stephane Nicoll * @since 1.0 */ -class ProjectGenerationMetricsListener implements ProjectGenerationListener { +class ProjectGenerationMetricsListener { private final CounterService counterService @@ -36,8 +37,9 @@ class ProjectGenerationMetricsListener implements ProjectGenerationListener { this.counterService = counterService } - @Override - void onGeneratedProject(ProjectRequest request) { + @EventListener + void onGeneratedProject(ProjectGeneratedEvent event) { + def request = event.projectRequest increment(key('requests')) // Total number of requests handleDependencies(request) handleType(request) diff --git a/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerator.groovy b/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerator.groovy index 209efdb2..a12f5938 100644 --- a/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerator.groovy +++ b/initializr/src/main/groovy/io/spring/initializr/generator/ProjectGenerator.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -23,6 +23,7 @@ import io.spring.initializr.util.Version import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value +import org.springframework.context.ApplicationEventPublisher import org.springframework.util.Assert import static io.spring.initializr.util.GroovyTemplate.template @@ -41,6 +42,9 @@ class ProjectGenerator { private static final VERSION_1_3_0_M1 = Version.parse('1.3.0.M1') + @Autowired + ApplicationEventPublisher eventPublisher + @Autowired InitializrMetadataProvider metadataProvider @@ -50,8 +54,6 @@ class ProjectGenerator { @Value('${TMPDIR:.}') String tmpdir - final Set listeners = [] - private transient Map> temporaryFiles = [:] /** @@ -60,7 +62,7 @@ class ProjectGenerator { byte[] generateMavenPom(ProjectRequest request) { def model = initializeModel(request) def content = doGenerateMavenPom(model) - invokeListeners(request) + publishProjectGeneratedEvent(request) content } @@ -70,7 +72,7 @@ class ProjectGenerator { byte[] generateGradleBuild(ProjectRequest request) { def model = initializeModel(request) def content = doGenerateGradleBuild(model) - invokeListeners(request) + publishProjectGeneratedEvent(request) content } @@ -131,7 +133,7 @@ class ProjectGenerator { new File(dir, 'src/main/resources/templates').mkdirs() new File(dir, 'src/main/resources/static').mkdirs() } - invokeListeners(request) + publishProjectGeneratedEvent(request) rootDir } @@ -164,10 +166,9 @@ class ProjectGenerator { } } - private void invokeListeners(ProjectRequest request) { - listeners.each { - it.onGeneratedProject(request) - } + private void publishProjectGeneratedEvent(ProjectRequest request) { + ProjectGeneratedEvent event = new ProjectGeneratedEvent(request) + eventPublisher.publishEvent(event) } protected Map initializeModel(ProjectRequest request) { diff --git a/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListenerTests.groovy b/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListenerTests.groovy index 9b54c950..849bf3ff 100644 --- a/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListenerTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGenerationMetricsListenerTests.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -47,7 +47,7 @@ class ProjectGenerationMetricsListenerTests { void projectGenerationCount() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.requests') } @@ -56,7 +56,7 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.style << 'security' << 'spring-data' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.dependency.security', 'initializr.dependency.spring-data') } @@ -65,7 +65,7 @@ class ProjectGenerationMetricsListenerTests { void noDependencies() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasNoValue('initializr.dependency.') } @@ -75,7 +75,7 @@ class ProjectGenerationMetricsListenerTests { request.style << 'spring-data' request.packaging = 'war' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.dependency.web', 'initializr.dependency.spring-data') } @@ -91,7 +91,7 @@ class ProjectGenerationMetricsListenerTests { request.initialize(metadata) request.style << 'foo-old' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.dependency.foo') // standard id is used } @@ -99,7 +99,7 @@ class ProjectGenerationMetricsListenerTests { void defaultType() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.type.maven-project') } @@ -108,7 +108,7 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.type = 'gradle-build' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.type.gradle-build') } @@ -116,7 +116,7 @@ class ProjectGenerationMetricsListenerTests { void defaultPackaging() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.packaging.jar') } @@ -125,7 +125,7 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.packaging = 'war' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.packaging.war') } @@ -133,7 +133,7 @@ class ProjectGenerationMetricsListenerTests { void defaultJavaVersion() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.java_version.1_8') } @@ -142,7 +142,7 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.javaVersion = '1.7' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.java_version.1_7') } @@ -150,7 +150,7 @@ class ProjectGenerationMetricsListenerTests { void defaultLanguage() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.language.java') } @@ -159,7 +159,7 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.language = 'groovy' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.language.groovy') } @@ -167,7 +167,7 @@ class ProjectGenerationMetricsListenerTests { void defaultBootVersion() { def request = initialize() request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.boot_version.1_2_3_RELEASE') } @@ -176,7 +176,7 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.bootVersion = '1.0.2.RELEASE' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.boot_version.1_0_2_RELEASE') } @@ -191,7 +191,7 @@ class ProjectGenerationMetricsListenerTests { request.bootVersion = '1.0.2.RELEASE' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.requests', 'initializr.dependency.web', 'initializr.dependency.security', 'initializr.type.gradle-project', 'initializr.packaging.jar', @@ -204,20 +204,24 @@ class ProjectGenerationMetricsListenerTests { def request = initialize() request.style << 'security' << 'spring-data' request.resolve(metadata) - listener.onGeneratedProject(request) + fireEvent(request) metricsAssert.hasValue(1, 'initializr.requests', 'initializr.dependency.security', 'initializr.dependency.spring-data') def anotherRequest = initialize() anotherRequest.style << 'web' << 'spring-data' anotherRequest.resolve(metadata) - listener.onGeneratedProject(anotherRequest) + fireEvent(anotherRequest) metricsAssert.hasValue(2, 'initializr.dependency.spring-data', 'initializr.dependency.spring-data') metricsAssert.hasValue(1, 'initializr.dependency.web', 'initializr.dependency.security') } + private fireEvent(ProjectRequest projectRequest) { + listener.onGeneratedProject(new ProjectGeneratedEvent(projectRequest)) + } + private ProjectRequest initialize() { def request = new ProjectRequest() request.initialize(metadata) diff --git a/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGeneratorTests.groovy b/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGeneratorTests.groovy index 36d9aa8b..3ff20a16 100644 --- a/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGeneratorTests.groovy +++ b/initializr/src/test/groovy/io/spring/initializr/generator/ProjectGeneratorTests.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -28,12 +28,15 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import org.mockito.ArgumentMatcher import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.ApplicationEventPublisher import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration +import static org.mockito.Matchers.argThat import static org.mockito.Mockito.mock import static org.mockito.Mockito.times import static org.mockito.Mockito.verify @@ -48,44 +51,38 @@ class ProjectGeneratorTests { private final ProjectGenerator projectGenerator = new ProjectGenerator() + private final ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher) + @Before void setup() { def metadata = InitializrMetadataTestBuilder.withDefaults() .addDependencyGroup('test', 'web', 'security', 'data-jpa', 'aop', 'batch', 'integration').build() applyMetadata(metadata) + projectGenerator.eventPublisher = eventPublisher projectGenerator.tmpdir = folder.newFolder().absolutePath } @Test void defaultMavenPom() { - def listener = mock(ProjectGenerationListener) - projectGenerator.listeners << listener - def request = createProjectRequest('web') generateMavenPom(request).hasNoRepository() .hasSpringBootStarterDependency('web') - verify(listener, times(1)).onGeneratedProject(request) + verify(eventPublisher, times(1)).publishEvent(argThat(new EventMatcher(request))) } @Test void defaultGradleBuild() { - def listener = mock(ProjectGenerationListener) - projectGenerator.listeners << listener - def request = createProjectRequest('web') generateGradleBuild(request) - verify(listener, times(1)).onGeneratedProject(request) + verify(eventPublisher, times(1)).publishEvent(argThat(new EventMatcher(request))) } @Test void defaultProject() { - def listener = mock(ProjectGenerationListener) - projectGenerator.listeners << listener - def request = createProjectRequest('web') generateProject(request).isJavaProject().isMavenProject().pomAssert() .hasNoRepository().hasSpringBootStarterDependency('web') - verify(listener, times(1)).onGeneratedProject(request) + verify(eventPublisher, times(1)).publishEvent(argThat(new EventMatcher(request))) } @Test @@ -558,4 +555,19 @@ class ProjectGeneratorTests { } } + private static class EventMatcher extends ArgumentMatcher { + + private final ProjectRequest request + + EventMatcher(ProjectRequest request) { + this.request = request + } + + @Override + boolean matches(Object argument) { + ProjectGeneratedEvent event = (ProjectGeneratedEvent) argument + return request.equals(event.getProjectRequest()) + } + } + }