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
This commit is contained in:
Stephane Nicoll 2016-01-29 16:38:33 +01:00
parent b443a08370
commit b1352ca264
6 changed files with 84 additions and 58 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -83,9 +83,7 @@ class InitializrAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
ProjectGenerator projectGenerator() { ProjectGenerator projectGenerator() {
def generator = new ProjectGenerator() new ProjectGenerator()
generator.listeners << metricsListener()
generator
} }
@Bean @Bean

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,17 +17,26 @@
package io.spring.initializr.generator package io.spring.initializr.generator
/** /**
* Interface to be implemented by components that need to be aware of project generation * Event fired when a new project has been generated successfully.
* related events.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.0 * @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()
}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.metrics.CounterService import org.springframework.boot.actuate.metrics.CounterService
import org.springframework.context.event.EventListener
import org.springframework.util.StringUtils import org.springframework.util.StringUtils
/** /**
* A {@link ProjectGenerationListener} implementation that uses a {@link CounterService} * A {@link ProjectGeneratedEvent} listener that uses a {@link CounterService} to update
* to update various project related metrics. * various project related metrics.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.0 * @since 1.0
*/ */
class ProjectGenerationMetricsListener implements ProjectGenerationListener { class ProjectGenerationMetricsListener {
private final CounterService counterService private final CounterService counterService
@ -36,8 +37,9 @@ class ProjectGenerationMetricsListener implements ProjectGenerationListener {
this.counterService = counterService this.counterService = counterService
} }
@Override @EventListener
void onGeneratedProject(ProjectRequest request) { void onGeneratedProject(ProjectGeneratedEvent event) {
def request = event.projectRequest
increment(key('requests')) // Total number of requests increment(key('requests')) // Total number of requests
handleDependencies(request) handleDependencies(request)
handleType(request) handleType(request)

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Autowired
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.context.ApplicationEventPublisher
import org.springframework.util.Assert import org.springframework.util.Assert
import static io.spring.initializr.util.GroovyTemplate.template 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') private static final VERSION_1_3_0_M1 = Version.parse('1.3.0.M1')
@Autowired
ApplicationEventPublisher eventPublisher
@Autowired @Autowired
InitializrMetadataProvider metadataProvider InitializrMetadataProvider metadataProvider
@ -50,8 +54,6 @@ class ProjectGenerator {
@Value('${TMPDIR:.}') @Value('${TMPDIR:.}')
String tmpdir String tmpdir
final Set<ProjectGenerationListener> listeners = []
private transient Map<String, List<File>> temporaryFiles = [:] private transient Map<String, List<File>> temporaryFiles = [:]
/** /**
@ -60,7 +62,7 @@ class ProjectGenerator {
byte[] generateMavenPom(ProjectRequest request) { byte[] generateMavenPom(ProjectRequest request) {
def model = initializeModel(request) def model = initializeModel(request)
def content = doGenerateMavenPom(model) def content = doGenerateMavenPom(model)
invokeListeners(request) publishProjectGeneratedEvent(request)
content content
} }
@ -70,7 +72,7 @@ class ProjectGenerator {
byte[] generateGradleBuild(ProjectRequest request) { byte[] generateGradleBuild(ProjectRequest request) {
def model = initializeModel(request) def model = initializeModel(request)
def content = doGenerateGradleBuild(model) def content = doGenerateGradleBuild(model)
invokeListeners(request) publishProjectGeneratedEvent(request)
content content
} }
@ -131,7 +133,7 @@ class ProjectGenerator {
new File(dir, 'src/main/resources/templates').mkdirs() new File(dir, 'src/main/resources/templates').mkdirs()
new File(dir, 'src/main/resources/static').mkdirs() new File(dir, 'src/main/resources/static').mkdirs()
} }
invokeListeners(request) publishProjectGeneratedEvent(request)
rootDir rootDir
} }
@ -164,10 +166,9 @@ class ProjectGenerator {
} }
} }
private void invokeListeners(ProjectRequest request) { private void publishProjectGeneratedEvent(ProjectRequest request) {
listeners.each { ProjectGeneratedEvent event = new ProjectGeneratedEvent(request)
it.onGeneratedProject(request) eventPublisher.publishEvent(event)
}
} }
protected Map initializeModel(ProjectRequest request) { protected Map initializeModel(ProjectRequest request) {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -47,7 +47,7 @@ class ProjectGenerationMetricsListenerTests {
void projectGenerationCount() { void projectGenerationCount() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.requests') metricsAssert.hasValue(1, 'initializr.requests')
} }
@ -56,7 +56,7 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.style << 'security' << 'spring-data' request.style << 'security' << 'spring-data'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.dependency.security', metricsAssert.hasValue(1, 'initializr.dependency.security',
'initializr.dependency.spring-data') 'initializr.dependency.spring-data')
} }
@ -65,7 +65,7 @@ class ProjectGenerationMetricsListenerTests {
void noDependencies() { void noDependencies() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasNoValue('initializr.dependency.') metricsAssert.hasNoValue('initializr.dependency.')
} }
@ -75,7 +75,7 @@ class ProjectGenerationMetricsListenerTests {
request.style << 'spring-data' request.style << 'spring-data'
request.packaging = 'war' request.packaging = 'war'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.dependency.web', metricsAssert.hasValue(1, 'initializr.dependency.web',
'initializr.dependency.spring-data') 'initializr.dependency.spring-data')
} }
@ -91,7 +91,7 @@ class ProjectGenerationMetricsListenerTests {
request.initialize(metadata) request.initialize(metadata)
request.style << 'foo-old' request.style << 'foo-old'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.dependency.foo') // standard id is used metricsAssert.hasValue(1, 'initializr.dependency.foo') // standard id is used
} }
@ -99,7 +99,7 @@ class ProjectGenerationMetricsListenerTests {
void defaultType() { void defaultType() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.type.maven-project') metricsAssert.hasValue(1, 'initializr.type.maven-project')
} }
@ -108,7 +108,7 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.type = 'gradle-build' request.type = 'gradle-build'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.type.gradle-build') metricsAssert.hasValue(1, 'initializr.type.gradle-build')
} }
@ -116,7 +116,7 @@ class ProjectGenerationMetricsListenerTests {
void defaultPackaging() { void defaultPackaging() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.packaging.jar') metricsAssert.hasValue(1, 'initializr.packaging.jar')
} }
@ -125,7 +125,7 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.packaging = 'war' request.packaging = 'war'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.packaging.war') metricsAssert.hasValue(1, 'initializr.packaging.war')
} }
@ -133,7 +133,7 @@ class ProjectGenerationMetricsListenerTests {
void defaultJavaVersion() { void defaultJavaVersion() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.java_version.1_8') metricsAssert.hasValue(1, 'initializr.java_version.1_8')
} }
@ -142,7 +142,7 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.javaVersion = '1.7' request.javaVersion = '1.7'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.java_version.1_7') metricsAssert.hasValue(1, 'initializr.java_version.1_7')
} }
@ -150,7 +150,7 @@ class ProjectGenerationMetricsListenerTests {
void defaultLanguage() { void defaultLanguage() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.language.java') metricsAssert.hasValue(1, 'initializr.language.java')
} }
@ -159,7 +159,7 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.language = 'groovy' request.language = 'groovy'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.language.groovy') metricsAssert.hasValue(1, 'initializr.language.groovy')
} }
@ -167,7 +167,7 @@ class ProjectGenerationMetricsListenerTests {
void defaultBootVersion() { void defaultBootVersion() {
def request = initialize() def request = initialize()
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.boot_version.1_2_3_RELEASE') metricsAssert.hasValue(1, 'initializr.boot_version.1_2_3_RELEASE')
} }
@ -176,7 +176,7 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.bootVersion = '1.0.2.RELEASE' request.bootVersion = '1.0.2.RELEASE'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.boot_version.1_0_2_RELEASE') metricsAssert.hasValue(1, 'initializr.boot_version.1_0_2_RELEASE')
} }
@ -191,7 +191,7 @@ class ProjectGenerationMetricsListenerTests {
request.bootVersion = '1.0.2.RELEASE' request.bootVersion = '1.0.2.RELEASE'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.requests', metricsAssert.hasValue(1, 'initializr.requests',
'initializr.dependency.web', 'initializr.dependency.security', 'initializr.dependency.web', 'initializr.dependency.security',
'initializr.type.gradle-project', 'initializr.packaging.jar', 'initializr.type.gradle-project', 'initializr.packaging.jar',
@ -204,20 +204,24 @@ class ProjectGenerationMetricsListenerTests {
def request = initialize() def request = initialize()
request.style << 'security' << 'spring-data' request.style << 'security' << 'spring-data'
request.resolve(metadata) request.resolve(metadata)
listener.onGeneratedProject(request) fireEvent(request)
metricsAssert.hasValue(1, 'initializr.requests', metricsAssert.hasValue(1, 'initializr.requests',
'initializr.dependency.security', 'initializr.dependency.spring-data') 'initializr.dependency.security', 'initializr.dependency.spring-data')
def anotherRequest = initialize() def anotherRequest = initialize()
anotherRequest.style << 'web' << 'spring-data' anotherRequest.style << 'web' << 'spring-data'
anotherRequest.resolve(metadata) anotherRequest.resolve(metadata)
listener.onGeneratedProject(anotherRequest) fireEvent(anotherRequest)
metricsAssert.hasValue(2, 'initializr.dependency.spring-data', metricsAssert.hasValue(2, 'initializr.dependency.spring-data',
'initializr.dependency.spring-data') 'initializr.dependency.spring-data')
metricsAssert.hasValue(1, 'initializr.dependency.web', metricsAssert.hasValue(1, 'initializr.dependency.web',
'initializr.dependency.security') 'initializr.dependency.security')
} }
private fireEvent(ProjectRequest projectRequest) {
listener.onGeneratedProject(new ProjectGeneratedEvent(projectRequest))
}
private ProjectRequest initialize() { private ProjectRequest initialize() {
def request = new ProjectRequest() def request = new ProjectRequest()
request.initialize(metadata) request.initialize(metadata)

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import org.mockito.ArgumentMatcher
import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.ApplicationEventPublisher
import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import static org.mockito.Matchers.argThat
import static org.mockito.Mockito.mock import static org.mockito.Mockito.mock
import static org.mockito.Mockito.times import static org.mockito.Mockito.times
import static org.mockito.Mockito.verify import static org.mockito.Mockito.verify
@ -48,44 +51,38 @@ class ProjectGeneratorTests {
private final ProjectGenerator projectGenerator = new ProjectGenerator() private final ProjectGenerator projectGenerator = new ProjectGenerator()
private final ApplicationEventPublisher eventPublisher = mock(ApplicationEventPublisher)
@Before @Before
void setup() { void setup() {
def metadata = InitializrMetadataTestBuilder.withDefaults() def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('test', 'web', 'security', 'data-jpa', 'aop', 'batch', 'integration').build() .addDependencyGroup('test', 'web', 'security', 'data-jpa', 'aop', 'batch', 'integration').build()
applyMetadata(metadata) applyMetadata(metadata)
projectGenerator.eventPublisher = eventPublisher
projectGenerator.tmpdir = folder.newFolder().absolutePath projectGenerator.tmpdir = folder.newFolder().absolutePath
} }
@Test @Test
void defaultMavenPom() { void defaultMavenPom() {
def listener = mock(ProjectGenerationListener)
projectGenerator.listeners << listener
def request = createProjectRequest('web') def request = createProjectRequest('web')
generateMavenPom(request).hasNoRepository() generateMavenPom(request).hasNoRepository()
.hasSpringBootStarterDependency('web') .hasSpringBootStarterDependency('web')
verify(listener, times(1)).onGeneratedProject(request) verify(eventPublisher, times(1)).publishEvent(argThat(new EventMatcher(request)))
} }
@Test @Test
void defaultGradleBuild() { void defaultGradleBuild() {
def listener = mock(ProjectGenerationListener)
projectGenerator.listeners << listener
def request = createProjectRequest('web') def request = createProjectRequest('web')
generateGradleBuild(request) generateGradleBuild(request)
verify(listener, times(1)).onGeneratedProject(request) verify(eventPublisher, times(1)).publishEvent(argThat(new EventMatcher(request)))
} }
@Test @Test
void defaultProject() { void defaultProject() {
def listener = mock(ProjectGenerationListener)
projectGenerator.listeners << listener
def request = createProjectRequest('web') def request = createProjectRequest('web')
generateProject(request).isJavaProject().isMavenProject().pomAssert() generateProject(request).isJavaProject().isMavenProject().pomAssert()
.hasNoRepository().hasSpringBootStarterDependency('web') .hasNoRepository().hasSpringBootStarterDependency('web')
verify(listener, times(1)).onGeneratedProject(request) verify(eventPublisher, times(1)).publishEvent(argThat(new EventMatcher(request)))
} }
@Test @Test
@ -558,4 +555,19 @@ class ProjectGeneratorTests {
} }
} }
private static class EventMatcher extends ArgumentMatcher<ProjectGeneratedEvent> {
private final ProjectRequest request
EventMatcher(ProjectRequest request) {
this.request = request
}
@Override
boolean matches(Object argument) {
ProjectGeneratedEvent event = (ProjectGeneratedEvent) argument
return request.equals(event.getProjectRequest())
}
}
} }