Accessing Spring from custom extension: Change global extension execution order
Issue description
Currently global extensions are executed in the order they are discovered in the class path. That poses a problem when writing custom extensions which try to access fields in the Specification which will be initialized by Spring via @Autowired.
When I have MyGlobalExtension in MyProject, which depends on spock-spring and last on spock-core, the Spring extension will always add its IMethodInterceptors after MyGlobalExtension.
As far as I understand, there are a few possible workarounds (solutions) to the problem:
- Allow defining the order other extensions by listing them
META-INF/services/org.spockframework.runtime.extension.IGlobalExtension, so theglobalExtensionClassesinGlobalExtensionRegistrywould probably become aLinkedHashSet<Class<?>>. This would make the order of extensions configurable explicitly. (BUG: Currently the SpringExtension will be loaded twice if you do add it to your own extension configuration file) - Reverse the order of Extensions before execution in
ExtensionRunner:
private void runGlobalExtensions() {
final List<IGlobalExtension> globalExtensions = new ArrayList<IGlobalExtension>(extensionRegistry.getGlobalExtensions());
Collections.reverse(globalExtensions);
for (IGlobalExtension extension : globalExtensions) {
extension.visitSpec(spec);
}
}
This ensures that all extensions in dependencies are loaded first, but would not fix the problem between extensions in different dependencies on the classpath
- Always load the Spring extension first programatically
I will happily contribute a fix along with tests and a Gist if required :)
Java/JDK
java -version
1.7+
Groovy version
Note that versions older than 2.0 are no longer supported.
groovy -version
2.4
Build tool version
Gradle
gradle -version
Gradle wrapper from spock-example project
Operating System
Windows 10
IDE
IntelliJ
Build-tool dependencies used
Gradle/Grails
compile 'org.spockframework:spock-core:1.1-groovy-2.4'
compile 'org.spockframework:spock-spring:1.1-groovy-2.4'
releates #646
Thanks a lot for replying so quickly! Your are perfectly right in your comment in the pull request, sorry for wasting your time :-/
So as a result I took a step back, reconsidering what needs to be done.
In my opinion the problem could be split into three more or less independent components:
- Declaring dependencies either via annotaiton or interface
- Building and error-checking a dependency graph (keyword: Cycle detection)
- Resolving and loading dependencies
I would like to suggest solving the first one and then moving on to the others 👍
Declaring dependencies of an extension
As an extension developer I would like to declare dependencies on other extensions so these are loaded first (happens-before relationship).
This has to be done before starting the extension and can be implemented
-
via an annotation, executed after all Extensions were discovered, but not instantiated
@DependsOn([Foo, Bar, Baz]) class MyExtension {}- Advantages: Static resolution: Dependencies can be resolved before the extension is instanciatiated
- Disadvantages: Less flexible, cannot declare dependencies conditionally (e.g. depending on System environment)
-
via an interface, executed after the extensions are loaded, but not started yet
interface IExtensionDependencies { Set<Class<?>> getDependencies() } class MyExtension extends AbstractGlobalExtension implements IExtensionDependencies { @Override Set<Class<?>> getDependencies(List<Class<?>> availableExtensions) { return [Foo, Bar].toSet() } }The
availableExtensionsparameter contains a list of all known extensions. This allows declaring a dependency if it is present (i.e. "load SpringExtension, if available").- Advantages: Most flexible solution, can declare dependencies flexibly within normal groovy code
- Disadvantages: No static dependency analysis on the extension class (instance only).
As far as I understand both mechanisms could be used on global and annotation driven extensions. What do you think?
P.S.: I'm also available on Gitter.im (gitter.im/tburny) if you prefer discussing this on the spock channel/via DM :)
I'd like to avoid having to do a full dependency tree calculation for each test, for global extensions this is so much of an issue, but annotation based ones can vary for each test. I was thinking of using the simple ordering style (simple int), but that too has limitations.
Only having dependencies might be a bit limiting, you can not express that you'd like to run before a certain extension.
Ordering just the extensions might also be not enough, since extensions usually work by adding interceptors, which do the actual work. Normally you'd want to have a certain order going in (setup...) and the reverse order going out (cleanup...). So you actually want to order interceptors and not the extensions themselves.
how to use this change "Fix spockframework#817" by using maven (1.3-groovy-2.x)