Skip to content

Commit 6220a37

Browse files
committed
Merge branch 'bug/springfox#1207/fixes-context-refresh-event'
fixes springfox#1207
2 parents 664e4df + b7449ba commit 6220a37

7 files changed

Lines changed: 99 additions & 44 deletions

File tree

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import springfox.gradlebuild.utils.ProjectDefinitions
22

33
apply from: "$rootDir/gradle/dependencies.gradle"
4-
apply from: "$rootDir/gradle/idea.gradle"
4+
apply from: "$rootDir/gradle/ide.gradle"
55

66
buildscript {
77
repositories {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
allprojects {
22
apply plugin: "idea"
3+
apply plugin: "eclipse"
34
}
45

56
idea {

springfox-spring-web/src/main/java/springfox/documentation/spring/web/DocumentationCache.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ public Map<String, Documentation> all() {
4141
return Collections.unmodifiableMap(documentationLookup);
4242
}
4343

44+
public void clear() {
45+
documentationLookup.clear();
46+
}
4447
}

springfox-spring-web/src/main/java/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapper.java

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
2626
import org.springframework.beans.factory.annotation.Autowired;
27-
import org.springframework.context.ApplicationListener;
28-
import org.springframework.context.event.ContextRefreshedEvent;
27+
import org.springframework.context.SmartLifecycle;
2928
import org.springframework.stereotype.Component;
3029
import springfox.documentation.RequestHandler;
3130
import springfox.documentation.spi.DocumentationType;
@@ -51,7 +50,7 @@
5150
* If no instances DocumentationConfigurer are found a default one is created and executed.
5251
*/
5352
@Component
54-
public class DocumentationPluginsBootstrapper implements ApplicationListener<ContextRefreshedEvent> {
53+
public class DocumentationPluginsBootstrapper implements SmartLifecycle {
5554
private static final Logger log = LoggerFactory.getLogger(DocumentationPluginsBootstrapper.class);
5655
private final DocumentationPluginsManager documentationPluginsManager;
5756
private final List<RequestHandlerProvider> handlerProviders;
@@ -78,32 +77,6 @@ public DocumentationPluginsBootstrapper(
7877
this.defaultConfiguration = new DefaultConfiguration(defaults, typeResolver, servletContext);
7978
}
8079

81-
@Override
82-
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
83-
84-
// run the bootstrapper only if the event is from the root context
85-
if(contextRefreshedEvent.getApplicationContext().getParent() != null) {
86-
log.info("contextRefreshedEvent {} not from root application context. So skipping this event.", contextRefreshedEvent);
87-
return;
88-
}
89-
90-
if (initialized.compareAndSet(false, true)) {
91-
log.info("Context refreshed");
92-
List<DocumentationPlugin> plugins = pluginOrdering()
93-
.sortedCopy(documentationPluginsManager.documentationPlugins());
94-
log.info("Found {} custom documentation plugin(s)", plugins.size());
95-
for (DocumentationPlugin each : plugins) {
96-
DocumentationType documentationType = each.getDocumentationType();
97-
if (each.isEnabled()) {
98-
scanDocumentation(buildContext(each));
99-
} else {
100-
log.info("Skipping initializing disabled plugin bean {} v{}",
101-
documentationType.getName(), documentationType.getVersion());
102-
}
103-
}
104-
}
105-
}
106-
10780
private DocumentationContext buildContext(DocumentationPlugin each) {
10881
return each.configure(defaultContextBuilder(each));
10982
}
@@ -131,4 +104,48 @@ public Iterable<RequestHandler> apply(RequestHandlerProvider input) {
131104
};
132105
}
133106

107+
@Override
108+
public boolean isAutoStartup() {
109+
return true;
110+
}
111+
112+
@Override
113+
public void stop(Runnable callback) {
114+
callback.run();
115+
}
116+
117+
@Override
118+
public void start() {
119+
if (initialized.compareAndSet(false, true)) {
120+
log.info("Context refreshed");
121+
List<DocumentationPlugin> plugins = pluginOrdering()
122+
.sortedCopy(documentationPluginsManager.documentationPlugins());
123+
log.info("Found {} custom documentation plugin(s)", plugins.size());
124+
for (DocumentationPlugin each : plugins) {
125+
DocumentationType documentationType = each.getDocumentationType();
126+
if (each.isEnabled()) {
127+
scanDocumentation(buildContext(each));
128+
} else {
129+
log.info("Skipping initializing disabled plugin bean {} v{}",
130+
documentationType.getName(), documentationType.getVersion());
131+
}
132+
}
133+
}
134+
}
135+
136+
@Override
137+
public void stop() {
138+
initialized.getAndSet(false);
139+
scanned.clear();
140+
}
141+
142+
@Override
143+
public boolean isRunning() {
144+
return initialized.get();
145+
}
146+
147+
@Override
148+
public int getPhase() {
149+
return Integer.MAX_VALUE;
150+
}
134151
}

springfox-spring-web/src/test/groovy/springfox/documentation/spring/web/DocumentationCacheSpec.groovy

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ class DocumentationCacheSpec extends Specification {
3636
group.groupName == "test"
3737
and:
3838
sut.documentationByGroup("non-existent") == null
39+
}
3940

41+
def "Cache can be cleared " () {
42+
given:
43+
def sut = new DocumentationCache()
44+
and:
45+
sut.addDocumentation(new DocumentationBuilder().name("test").build())
46+
when:
47+
def group = sut.documentationByGroup("test")
48+
then:
49+
group != null
50+
group.groupName == "test"
51+
and:
52+
sut.clear()
53+
sut.documentationByGroup("test") == null
4054
}
55+
4156
}

springfox-spring-web/src/test/groovy/springfox/documentation/spring/web/plugins/DocketSpec.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ import springfox.documentation.schema.CodeGenGenericTypeNamingStrategy
3131
import springfox.documentation.schema.DefaultGenericTypeNamingStrategy
3232
import springfox.documentation.service.ApiDescription
3333
import springfox.documentation.service.ApiInfo
34+
import springfox.documentation.service.Parameter
3435
import springfox.documentation.service.ResponseMessage
3536
import springfox.documentation.service.SecurityScheme
37+
import springfox.documentation.service.Tag
3638
import springfox.documentation.spi.DocumentationType
3739
import springfox.documentation.spi.service.contexts.Defaults
3840
import springfox.documentation.spi.service.contexts.SecurityContext
@@ -173,6 +175,8 @@ class DocketSpec extends DocumentationContextSpec {
173175
'protocols' | ['application/json'] as Set | 'protocols'
174176
'additionalModels' | Mock(ResolvedType) | 'additionalModels'
175177
'enableUrlTemplating' | true | 'isUriTemplatesEnabled'
178+
'tags' | new Tag("test", "test") | 'tags'
179+
'globalOperationParameters' | [Mock(Parameter)] | 'globalOperationParameters'
176180
}
177181

178182
def "Code generation strategy property is set"() {

springfox-spring-web/src/test/groovy/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapperSpec.groovy

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
package springfox.documentation.spring.web.plugins
2121

2222
import com.fasterxml.classmate.TypeResolver
23-
import org.springframework.context.ApplicationContext
24-
import org.springframework.context.event.ContextRefreshedEvent
2523
import spock.lang.Specification
2624
import springfox.documentation.service.Documentation
2725
import springfox.documentation.spi.DocumentationType
@@ -36,13 +34,11 @@ import javax.servlet.ServletContext
3634

3735
class DocumentationPluginsBootstrapperSpec extends Specification {
3836

39-
ApplicationContext applicationContext = Mock(ApplicationContext)
4037
DocumentationPluginsManager pluginManager = Mock(DocumentationPluginsManager)
4138
Documentation group = Mock(Documentation)
4239
ApiDocumentationScanner apiGroup = Mock(ApiDocumentationScanner)
4340
RequestHandlerProvider handlerProvider = Mock(RequestHandlerProvider)
4441

45-
ContextRefreshedEvent contextRefreshedEvent = new ContextRefreshedEvent(applicationContext)
4642
DocumentationPluginsBootstrapper bootstrapper =
4743
new DocumentationPluginsBootstrapper(pluginManager,
4844
[handlerProvider],
@@ -73,7 +69,7 @@ class DocumentationPluginsBootstrapperSpec extends Specification {
7369
pluginManager.documentationPlugins() >> [enabledPlugin, disabledPlugin]
7470

7571
and:
76-
bootstrapper.onApplicationEvent(contextRefreshedEvent)
72+
bootstrapper.start()
7773

7874
then:
7975
1 * enabledPlugin.configure(_)
@@ -89,23 +85,42 @@ class DocumentationPluginsBootstrapperSpec extends Specification {
8985
plugin.isEnabled() >> true
9086

9187
and:
92-
bootstrapper.onApplicationEvent(contextRefreshedEvent)
88+
bootstrapper.start()
9389

9490
then:
9591
1 * plugin.configure(_)
9692
}
9793

98-
def "bootstrapper only if the event is from the root context"() {
99-
given: "ContextRefreshedEvent from a non-root application context."
100-
ApplicationContext appCtx = Mock(ApplicationContext)
101-
appCtx.getParent() >> Mock(ApplicationContext)
102-
ContextRefreshedEvent rootAppCtxEvent = new ContextRefreshedEvent(appCtx)
103-
pluginManager.documentationPlugins() >> []
94+
def "Bootstrapper now supports starting and stopping"() {
95+
given:
96+
DocumentationPlugin plugin = Mock(DocumentationPlugin)
97+
plugin.documentationType >> DocumentationType.SWAGGER_12
98+
when:
99+
pluginManager.documentationPlugins() >> [plugin]
100+
plugin.isEnabled() >> true
101+
102+
and:
103+
bootstrapper.start()
104+
bootstrapper.stop()
105+
bootstrapper.start()
104106

107+
then:
108+
2 * plugin.configure(_)
109+
}
110+
111+
def "Starting the Bootstrapper only configures the plugin once"() {
112+
given:
113+
DocumentationPlugin plugin = Mock(DocumentationPlugin)
114+
plugin.documentationType >> DocumentationType.SWAGGER_12
105115
when:
106-
bootstrapper.onApplicationEvent(rootAppCtxEvent)
116+
pluginManager.documentationPlugins() >> [plugin]
117+
plugin.isEnabled() >> true
118+
119+
and:
120+
bootstrapper.start()
121+
bootstrapper.start()
107122

108123
then:
109-
bootstrapper.initialized.get() == false
124+
1 * plugin.configure(_)
110125
}
111126
}

0 commit comments

Comments
 (0)