Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spring-restdocs-asciidoctor is incompatible with AsciidoctorJ 1.6 and later #581

Closed
jnizet opened this issue Feb 15, 2019 · 28 comments
Closed

Comments

@jnizet
Copy link

jnizet commented Feb 15, 2019

Using the latest version (1.5.10) of the gradle plugin org.asciidoctor.convert causes the following exception to be thrown:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Found interface org.asciidoctor.extension.JavaExtensionRegistry, but class was expected
        at org.springframework.restdocs.asciidoctor.RestDocsExtensionRegistry.register(RestDocsExtensionRegistry.java:32)
        at org.asciidoctor.extension.internal.ExtensionRegistryExecutor.registerAllExtensions(ExtensionRegistryExecutor.java:21)
        at org.asciidoctor.internal.JRubyAsciidoctor.registerExtensions(JRubyAsciidoctor.java:113)
        at org.asciidoctor.internal.JRubyAsciidoctor.processRegistrations(JRubyAsciidoctor.java:102)
        at org.asciidoctor.internal.JRubyAsciidoctor.create(JRubyAsciidoctor.java:82)
        at org.asciidoctor.internal.JRubyAsciidoctor.create(JRubyAsciidoctor.java:78)
        at org.asciidoctor.Asciidoctor$Factory.create(Asciidoctor.java:713)
        at org.asciidoctor.gradle.backported.AsciidoctorJavaExec.getAsciidoctorInstance(AsciidoctorJavaExec.groovy:71)
        at org.asciidoctor.gradle.backported.AsciidoctorJavaExec.run(AsciidoctorJavaExec.groovy:39)
        at org.asciidoctor.gradle.backported.AsciidoctorJavaExec.main(AsciidoctorJavaExec.groovy:194)

Using the previous version (1.5.9.2) works fine.

It would be a good idea I guess to release a new version of spring-restdocs that is built against the newest stable version of AsciidoctorJ (the gradle plugin currently uses AsciidoctorJ version 1.6.1, and the latest version is 1.6.2). Unfortunately, the Maven plugin still uses AsciidoctorJ version 1.5.8. Not sure if it's possible to be compatible with the two versions.

@wilkinsona
Copy link
Member

wilkinsona commented Feb 18, 2019

Ouch. Thanks for the report. I wonder how much in common the root cause of this and the root cause of spring-io/spring-asciidoctor-extensions#9 have in common? I'll see what I can do. Hopefully it'll be possible to be compatible with the two versions. I really don't want to have to have Maven- and Gradle-specific variants of the spring-restdocs-asciidoctor module.

@jnizet
Copy link
Author

jnizet commented Feb 18, 2019

I guess the main problem here is actually the Maven asciidoctor plugin, which still uses 1.5.8. If it used 1.6.x as the gradle plugin does, a new version of Spring-REST-Docs could just depend on 1.6.x for everything.
The root cause is not the same, but it's similar: a huge binary incompatibility was introduced (the class JavaExtensionRegistry became an interface)

@wilkinsona
Copy link
Member

I can work around the binary incompatibilities of JavaExtensionRegistry and RubyExtensionRegistry becoming interfaces by using reflection:

@Override
public void register(Asciidoctor asciidoctor) {
    try {
        Object javaExtensionRegistry = asciidoctor.getClass()
                .getMethod("javaExtensionRegistry").invoke(asciidoctor);
        javaExtensionRegistry.getClass().getMethod("preprocessor", Preprocessor.class)
                .invoke(javaExtensionRegistry, new DefaultAttributesPreprocessor());
        Object rubyExtensionRegistry = asciidoctor.getClass()
                .getMethod("rubyExtensionRegistry").invoke(asciidoctor);
        rubyExtensionRegistry.getClass().getMethod("loadClass", InputStream.class)
                .invoke(rubyExtensionRegistry, RestDocsExtensionRegistry.class
                        .getResourceAsStream("/extensions/operation_block_macro.rb"));
        rubyExtensionRegistry.getClass()
                .getMethod("blockMacro", String.class, String.class)
                .invoke(rubyExtensionRegistry, "operation", "OperationBlockMacro");
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to register Asciidoctor extensions",
                ex);
    }
}

This moves things along, but a failure then occurs because the signature of DefaultAttributesPreprocessor is wrong. The process method used to be the following:

public abstract PreprocessorReader process(Document document, PreprocessorReader reader);

In AsciidoctorJ 1.6, it is now the following:

public void PreprocessorReader process(Document document, PreprocessorReader reader);

I can't implement those two methods in the same class, or even in the same module. This leaves me with a few choices:

  1. Use something like ByteBuddy to generate the preprocessor at runtime
  2. Merge modules together at build time as Spring Framework does for its Hibernate support
  3. Provide separate spring-restdocs-asciidoctor modules for the different versions of AsciidoctorJ

I don't really like any of these, but I like 3 the least. It requires users to be aware of the differences and to figure out the right module to use. It's particularly bad as moving to a new patch release of Asciidoctor's Gradle plugin would require moving to a different variant of the spring-restdocs-asciidoctor module.

@wilkinsona
Copy link
Member

I've opened asciidoctor/asciidoctor-maven-plugin#385 to explore the possibility of the Maven plugin aligning its AsciidoctorJ dependency with that of the Gradle plugin.

@abelsromero
Copy link

asciidoctor-maven-plugin 1.6.0 (using AsciidoctorJ 1.6.2) has been released.

However, this is a temporal fix.
At some point, rest-docs should adapt to support AsciidoctorJ (and maven & gradle) v2.x changes. Most notably maybe is the change in name of the SPI file.

Given AsciidoctorJ is still in Release candidate cycle, so maybe rest-docs v2.0.4 should hold to 1.6.x and start a 2.1.0 pointing to AsciidoctorJ 2.x, once a stable release is available?

@bric3
Copy link
Contributor

bric3 commented Apr 6, 2019

@wilkinsona @abelsromero think about the spring restdocs 1.x users too that need a migration path to asciidoc 1.6 then 2.x but also a migration path to spring boot 2.x as well.

@bric3
Copy link
Contributor

bric3 commented Jun 4, 2019

Any update on this ?

@wilkinsona
Copy link
Member

Not yet, no. I've yet to have the time to figure out exactly what the right approach is going to be. Ideally, a maintenance release of REST Docs could add support for new versions of AsciidoctorJ but all the ways of achieving that [that I've thought of thus far] are pretty unpleasant. An alternative would be a new minor release of REST Docs that requires AsciidoctorJ 1.6, but with AsciidoctorJ 2.0 approaching, that's only going to be a short-term solution.

@bric3
Copy link
Contributor

bric3 commented Jun 7, 2019

Yes, indeed, thanks @wilkinsona for the insight in this issue !

@XhstormR
Copy link

XhstormR commented Jun 16, 2019

I have the same problem, use org.springframework.restdocs:spring-restdocs-asciidoctor with plugin id("org.asciidoctor.jvm.convert") version "2.2.0".

Exception log:

> Task :asciidoctor
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v7.Java7$1 (file:/C:/Users/Leo/.gradle/wrapper/dists/gradle-5.4.1-all/3221gyojl5jsh0helicew7rwx/gradle-5.4.1/lib/groovy-all-1.0-2.5.4.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int)
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.vmplugin.v7.Java7$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Exception in thread "main" java.lang.IncompatibleClassChangeError: Found interface org.asciidoctor.extension.JavaExtensionRegistry, but class was expected
	at org.springframework.restdocs.asciidoctor.RestDocsExtensionRegistry.register(RestDocsExtensionRegistry.java:32)
	at org.asciidoctor.extension.internal.ExtensionRegistryExecutor.registerAllExtensions(ExtensionRegistryExecutor.java:21)
	at org.asciidoctor.internal.JRubyAsciidoctor.registerExtensions(JRubyAsciidoctor.java:113)
	at org.asciidoctor.internal.JRubyAsciidoctor.processRegistrations(JRubyAsciidoctor.java:102)
	at org.asciidoctor.internal.JRubyAsciidoctor.create(JRubyAsciidoctor.java:82)
	at org.asciidoctor.internal.JRubyAsciidoctor.create(JRubyAsciidoctor.java:78)
	at org.asciidoctor.Asciidoctor$Factory.create(Asciidoctor.java:713)
	at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.getAsciidoctorInstance(AsciidoctorJavaExec.groovy:97)
	at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.run(AsciidoctorJavaExec.groovy:56)
	at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.main(AsciidoctorJavaExec.groovy:47)

> Task :asciidoctor FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':asciidoctor'.
> Process 'command 'D:\Work\jdk-12\bin\java.exe'' finished with non-zero exit value 1

I noticed that the version of asciidoctorj 2.0 has been officially released.

@ysb33r
Copy link

ysb33r commented Jun 16, 2019

Even though I am not a user of spring-restdocs, I am willing to help to get this resolved if someone of the Spring side can collaborate with me on this.

@wilkinsona
Copy link
Member

Thanks very much, @ysb33r. I'd love to collaborate on a solution.

As it stands, the problem is caused by my desire for Spring REST Docs' Asciidoctor extensions to support AsciidoctorJ 1.5.x (as they have always done) and 1.6.x at the same time, and the breaking changes made in AsciidoctorJ 1.6.x. The nature of the breaking changes is such that a reflection-based solution will not work. Without any compatibility improvements in AsciidoctorJ, that leaves a few options as listed above:

  1. Use something like ByteBuddy to generate the preprocessor at runtime
  2. Merge modules together at build time as Spring Framework does for its Hibernate support
  3. Provide separate spring-restdocs-asciidoctor modules for the different versions of AsciidoctorJ

Another option would be a new minor release of REST Docs that raises the minimum supported version of AsciidoctorJ to 1.6, but there aren't enough other new features to warrant such a release at the moment.

Of the other three options, I still dislike 3 the most. I'm probably leaning towards 2 as the complexity is then dealt with at build time rather than at runtime.

I still strongly prefer a solution at the AsciidoctorJ level so that this can be solved for the whole ecosystem in a single, central location.

@ysb33r
Copy link

ysb33r commented Jun 17, 2019

If I understand it correctly the real problem itself is the AciidoctorJ API changes and not the actual Gradle plugin. Is that a correct assumption?

@wilkinsona
Copy link
Member

Yes, that's right. REST Docs doesn't interact at all with the Gradle (or Maven) plugin. The API incompatibility is in AsciidoctorJ. That said, the Gradle Plugin did exacerbate the problem by upgrading to AsciidoctorJ 1.6 in a 1.5.x plugin release.

@jnizet
Copy link
Author

jnizet commented Jun 17, 2019

there aren't enough other new features to warrant such a release at the moment.

Would a Kotlin DSL warrant such a release? I have one waiting to be reviewed ;-)
See #547 (comment) and https://github.com/Ninja-Squad/spring-rest-docs-kotlin

@ysb33r
Copy link

ysb33r commented Jun 17, 2019

Yes, that's right. REST Docs doesn't interact at all with the Gradle (or Maven) plugin. The API incompatibility is in AsciidoctorJ. That said, the Gradle Plugin did exacerbate the problem by upgrading to AsciidoctorJ 1.6 in a 1.5.x plugin release.

If people use 1.5.11 or 1.5.12 of the Gradle plugin, they will not have this problem as that puts AsciidoctorJ 1.5 back as the default version.

Now for the longer term, you would probably want to look to upgrade everything to rather have all extensions compile against AsciidoctorJ 2.0 instead. Due to this, I would suggest not working with Asciidoctor Gradle 2.0, but rather look at being prepared for when Asciidoctor Gradle 3.0 is released as it supports AsciidoctorJ 2.0. (Asciidoctor Gradle 2.0 can use AsciidoctorJ 2.0, but only if no additional GEMs are specified).

We areadly have two releases of Asciidoctor Gradle 3.0 in alpha, and the next version should go out this week of 17 June 2019.

If you would consider creating an integration branch that could be Asciidoctor 2.x compatible, I could definitely help to iron out integration issues.

@ysb33r
Copy link

ysb33r commented Jun 17, 2019

As to the problem potentially also being excerbated by the Maven plugin, I am not sure where the state of that is yet, but I know @abelsromero is working towards getting a Maven plugin out which is AsciidoctorJ 2.0 compatible.

@abelsromero
Copy link

The released 2.0.0-RC.1 maven plugin is already compatible with AsciidoctorJ 2.0.0. To "For all intents and purposes"the plugin has moved on to 2.0.0 (which is the final version for 1.6.x).
Note the final 2.0.0 will introduce some non-backward compatible additional changes listed here asciidoctor/asciidoctor-maven-plugin#391, but other than that there's not much really.

In my opinion, while it's nice and desirable to support all AsciidoctorJ versions, the documentation format itself is compatible. From a user stand point the only requirement to upgrade will be updating the pom configurations and maybe some custom extension. The third-party extensions listed in the README will need some work too, but they will be faced with the same dilemma here at some point.
What I am trying to say is I really struggle to see how keeping compatibility adds any value to the final user. Current version works fine works 1.5.x, next one will require only 2.x.x components. Users are free to upgrade at their own pace provided third-party extension upgrade also.

@wilkinsona
Copy link
Member

wilkinsona commented Jun 18, 2019

If people use 1.5.11 or 1.5.12 of the Gradle plugin, they will not have this problem as that puts AsciidoctorJ 1.5 back as the default version.

Thanks, I hadn't noticed they'd reverted to 1.5 by default. That simplifies things quite a bit as it avoids the problem that @jnizet originally reported above.

@wilkinsona
Copy link
Member

Would a Kotlin DSL warrant such a release? I have one waiting to be reviewed ;-)

It would if we had the Java counterpart to go with it. I've still not found the time that I need to work on that unfortunately.

@wilkinsona
Copy link
Member

What I am trying to say is I really struggle to see how keeping compatibility adds any value to the final user. Current version works fine works 1.5.x, next one will require only 2.x.x components. Users are free to upgrade at their own pace provided third-party extension upgrade also.

The problem is that users won't all upgrade at the same pace. This is less likely at the moment now that the 1.5.x Gradle plugin has dropped back to AsciidoctorJ 1.5, but will become a bigger problem again in the future. I am sure that some users will want to be able to use the new major version of the Asciidoctor build plugins with REST Docs while others will want to stick with the current versions of the build plugins as they're working fine for them. This leaves REST Docs either having to support two version of AsciidoctorJ at the same time, or having a new minor (or even major) release of REST Docs that requires the new major release of AsciidoctorJ and the associated build plugins.

@ysb33r
Copy link

ysb33r commented Jun 18, 2019

@wilkinsona The READMEs on Asciidoctor Gradle for the various versions (1.5.x, 1.6.x, 2.x and 3.x) now contain notes w.r.t. to spring-restdocs compatibility. Basically it boils down to people should remain using 1.5.11 or 1.5.12 if they use spring-restdocs. Once the latter is upgraded to support AsciidoctorJ 2.0 (not 1.6) they will be able to use the 3.x range of asciidoctor-gradle. (In a similar note asciidoctor-maven 2.x).

Just to clarify the versioning on Asciidoctor Gradle:

  • 1.5.x & 1.6.x are both in maintenance mode and will only take critical bug fixes. They are also only officially supported on Gradle 3.x & 4.x.
  • 2.x is the main released (production) version and is AsciidoctorJ 1.6 compatible.
  • 3.x is the new version in development and is AsciidoctorJ 2.0 compatible. (there have only been alpha releases so far)

@wilkinsona wilkinsona changed the title Exception when using latest AsciidoctorJ version spring-restdocs-asciidoctor is incompatible with AsciidoctorJ 1.6 and later Jun 18, 2019
@wilkinsona wilkinsona added this to the 2.0.4.RELEASE milestone Jun 18, 2019
@wilkinsona
Copy link
Member

I've bitten the bullet and split things out into several modules so that I can compile against AsciidoctorJ 1.5, 1.6 and 2.0 and deal with their various incompatibles. The existing spring-restdocs-asciidoctor module then pulls these separate modules back together into a single jar that's hopefully compatible with AsciidoctorJ 1.5, 1.6, and 2.0.

@wilkinsona
Copy link
Member

This should hopefully now be fixed in the latest 2.0.4 snapshot available from https://repo.spring.io/libs-snapshot. If anyone affected by the incompatibilities has a chance to give it a try and provide some feedback, that would be much appreciated.

@jnizet
Copy link
Author

jnizet commented Jun 19, 2019

Here's my feedback on the 2.0.4 snapshot release (I tested it with gradle 5.1).

I started by setting the version of the spring-restdocs-mockmvc and spring-restdocs-asciidoctor dependencies to 2.0.4.BUILD-SNAPSHOT.

  • using it with the "org.asciidoctor.convert" plugin, version 1.5.9.2 that I was using already still works fine
  • using it with the "org.asciidoctor.convert" plugin, version 1.6.0 works fine too
  • using it with the "org.asciidoctor.convert" plugin, version 2.2.0 fails with the deprecation warning and error below. But (TL;DR;), it's possible, by making some simple adaptations to the build, to get it working. See below for details.

So, @wilkinsona, everything works fine with the snapshot 👍 , with all the tested versions of the asciidoctor plugin, even though using the latest version needs adaptations (and thus changes to the spring-restdocs documentation)

> Configure project :backend
You are using one or more deprecated Asciidoctor task or plugins. To help with migration run with --warnings=all

> Task :backend:asciidoctor
Exception in thread "main" org.asciidoctor.gradle.remote.AsciidoctorRemoteExecutionException: Error running Asciidoctor whilst attempting to process /Users/jb/projects/amies-server/backend/src/main/asciidoc/index.adoc using backend html5
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec$_convertFiles_closure4.doCall(AsciidoctorJavaExec.groovy:87)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:104)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:326)
        at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:264)
        at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1041)
        at groovy.lang.Closure.call(Closure.java:411)
        at groovy.lang.Closure.call(Closure.java:427)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2296)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2281)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2334)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.convertFiles(AsciidoctorJavaExec.groovy:78)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.access$1(AsciidoctorJavaExec.groovy)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec$_run_closure3.doCall(AsciidoctorJavaExec.groovy:70)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec$_run_closure3.call(AsciidoctorJavaExec.groovy)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2296)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2281)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2322)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.run(AsciidoctorJavaExec.groovy:65)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec.main(AsciidoctorJavaExec.groovy:47)
Caused by: java.lang.IllegalStateException: asciidoctor: FAILED: /Users/jb/projects/amies-server/backend/src/main/asciidoc/index.adoc: Failed to load AsciiDoc document
        at org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.getRequiredAttribute(SnippetsDirectoryResolver.java:78)
        at org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.getRequiredAttribute(SnippetsDirectoryResolver.java:68)
        at org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.lambda$getGradleSnippetsDirectory$0(SnippetsDirectoryResolver.java:63)
        at org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.getRequiredAttribute(SnippetsDirectoryResolver.java:76)
        at org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.getGradleSnippetsDirectory(SnippetsDirectoryResolver.java:62)
        at org.springframework.restdocs.asciidoctor.SnippetsDirectoryResolver.getSnippetsDirectory(SnippetsDirectoryResolver.java:39)
        at org.springframework.restdocs.asciidoctor.DefaultAttributesAsciidoctorJ16Preprocessor.process(DefaultAttributesAsciidoctorJ16Preprocessor.java:36)
        at org.asciidoctor.extension.processorproxies.PreprocessorProxy.process(PreprocessorProxy.java:95)
        at org.asciidoctor.extension.processorproxies.PreprocessorProxy$INVOKER$i$2$0$process.call(PreprocessorProxy$INVOKER$i$2$0$process.gen)
        at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodTwoOrN.call(JavaMethod.java:1008)
        at org.jruby.RubyMethod.call(RubyMethod.java:121)
        at org.jruby.RubyMethod$INVOKER$i$call.call(RubyMethod$INVOKER$i$call.gen)
        at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodZeroOrOneOrTwoOrNBlock.call(JavaMethod.java:353)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:201)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:326)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:128)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:151)
        at org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:187)
        at org.jruby.runtime.BlockBody.yield(BlockBody.java:116)
        at org.jruby.runtime.Block.yield(Block.java:165)
        at org.jruby.RubyArray.each(RubyArray.java:1792)
        at org.jruby.RubyArray$INVOKER$i$0$0$each.call(RubyArray$INVOKER$i$0$0$each.gen)
        at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodZeroBlock.call(JavaMethod.java:537)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:82)
        at org.jruby.runtime.callsite.CachingCallSite.callIter(CachingCallSite.java:91)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:544)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:80)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:138)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:125)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:191)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:325)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:141)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:346)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:92)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:204)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:191)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:207)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:367)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:203)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:326)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:303)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.RubyMethod.call(RubyMethod.java:129)
        at org.jruby.RubyMethod$INVOKER$i$call.call(RubyMethod$INVOKER$i$call.gen)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:303)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:82)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:82)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:82)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:303)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:303)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:547)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:80)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:138)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:125)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:191)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:325)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:141)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:346)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:92)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:204)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:191)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:207)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:367)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:203)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:326)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:92)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:204)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:191)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:207)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:367)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:203)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:326)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:128)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:151)
        at org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:187)
        at org.jruby.runtime.BlockBody.yield(BlockBody.java:116)
        at org.jruby.runtime.Block.yield(Block.java:165)
        at org.jruby.RubyIO.ensureYieldClose(RubyIO.java:1163)
        at org.jruby.RubyIO.open(RubyIO.java:1157)
        at org.jruby.RubyIO$INVOKER$s$0$0$open.call(RubyIO$INVOKER$s$0$0$open.gen)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:303)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:84)
        at org.jruby.runtime.callsite.CachingCallSite.callIter(CachingCallSite.java:91)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:544)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:362)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:72)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:105)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:92)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:173)
        at org.jruby.RubyClass.finvoke(RubyClass.java:888)
        at org.jruby.runtime.Helpers.invoke(Helpers.java:442)
        at org.jruby.RubyBasicObject.callMethod(RubyBasicObject.java:374)
        at org.asciidoctor.internal.JRubyAsciidoctor.convertFile(JRubyAsciidoctor.java:488)
        at org.asciidoctor.internal.JRubyAsciidoctor.convertFile(JRubyAsciidoctor.java:469)
        at org.asciidoctor.gradle.remote.AsciidoctorJavaExec$_convertFiles_closure4.doCall(AsciidoctorJavaExec.groovy:83)
        ... 22 more
Caused by: java.lang.IllegalStateException: asciidoctor: FAILED: <stdin>: Failed to load AsciiDoc document
        ... 162 more
Caused by: java.lang.IllegalStateException: projectdir attribute not found
        ... 162 more

Fixing the error is easy: the asciidoctor task must be modified to explicitly add the projectdir attribute:

Before:

    asciidoctor {
        inputs.dir(snippetsDir)
        dependsOn(docTest)
        sourceDir("src/main/asciidoc")
        attributes(
            "stylesheet=amies.css",
            "stylesdir=styles",
            "springbootversion=$springBootVersion"
        )
    }

After:

    asciidoctor {
        inputs.dir(snippetsDir)
        dependsOn(docTest)
        sourceDir("src/main/asciidoc")
        attributes(
            "stylesheet=amies.css",
            "stylesdir=styles",
            "springbootversion=$springBootVersion",
            "projectdir=$projectDir" // this attribute needs to be added
        )
    }

I tried running with --warnings=all to know how to avoid using deprecated tasks. But there is no such option. The correct option is (I guess) --warning-mode=all. Running it with that option gives

You are using one or more deprecated Asciidoctor task or plugins. These will be removed in a future release. To help you migrate we have compiled some tips for you based upon your current usage:

  - 'org.asciidoctor.convert' is deprecated. When you have time please switch over to 'org.asciidoctor.jvm.convert'.
  - jcenter() is no longer added by default. If you relied on this behaviour in the past, please add jcenter() to the repositories block.

@ysb33r fixing the warning message to use the correct command line option would be nice.

So I tried using the plugin "org.asciidoctor.jvm.convert" instead of "org.asciidoctor.convert". This plugin doesn't create the asciidoctor gradle configuration anymore, which is used to add the spring-restdocs-asciidoctor dependency. And the way to configure the asciidoctor task has changed. I managed to get everything working though, by applying the following changes (note: this uses the Gradle Kotlin DSL):

  1. recreate the asciidoctor configuration
configurations {
    create("asciidoctor")
}
  1. Add the spring rest docs extension to the configuration:
dependencies {
    // ...
    "asciidoctor"("org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.4.BUILD-SNAPSHOT")
}
  1. Change the asciidoctor task configuration.
    Before:
    asciidoctor {
        inputs.dir(snippetsDir)
        dependsOn(docTest)
        sourceDir("src/main/asciidoc")
        attributes(
            "stylesheet=amies.css",
            "stylesdir=styles",
            "springbootversion=$springBootVersion"
        )
    }

After:

    asciidoctor {
        inputs.dir(snippetsDir)
        dependsOn(docTest)
        setSourceDir(file("src/main/asciidoc")) // different way of setting the source dir
        attributes( // different way of setting the attributes
            mapOf(
                "stylesheet" to "amies.css",
                "stylesdir" to "styles",
                "springbootversion" to "$springBootVersion",
                "projectdir" to projectDir // this attribute needs to be added
            )
        )
        baseDirFollowsSourceDir() // this needs to be called otherwise the styles are not looked up in the right directory
        configurations("asciidoctor") // this needs to be added to add the spring rest docs extension
    }

Also note that the generated html file is now located under build/docs/asciidoc instead of build/asciidoc/html5.

@wilkinsona
Copy link
Member

wilkinsona commented Jun 19, 2019

Thanks very much, @jnizet.

The need to create the asciidoctor configuration and configure it on the task feels like a small step backwards to me. It also doesn't feel idiomatic to me as it's no longer aligned with things like Gradle's own Checkstyle plugin that automatically creates and uses the checkstyle configuration. I've opened asciidoctor/asciidoctor-gradle-plugin#403 to see if it could be reinstated.

projectdir is now named gradle-projectdir. REST Docs itself should cope with this thanks to the changes made in #562. If you were seeing a failure in REST Docs because it was still requiring projectdir, can you please open a new issue with the details?

@ysb33r
Copy link

ysb33r commented Jun 19, 2019

@ysb33r fixing the warning message to use the correct command line option would be nice.

@jzinet That has been fixed, but not released yet.

@ysb33r
Copy link

ysb33r commented Jun 19, 2019

@jzinet Also see asciidoctor/asciidoctor-gradle-plugin#404 regarding your Kotlin DSL discoveries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants