Groovy
  1. Groovy
  2. GROOVY-6156

Grapes/@Grab does not work with JDK8

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 2.1.3, 2.2.x
    • Fix Version/s: 2.1.8, 1.8.10
    • Component/s: None
    • Labels:
      None

      Description

      Groovy appears not to be able to run TestNG tests. The code:

      @Grab('org.testng:testng:6.8.5')
      @Grab('org.codehaus.groovy:groovy-testng:2.2.0-SNAPSHOT')
      import org.testng.annotations.Test
      import static org.testng.Assert.assertEquals
      
      class testngFails {
        @Test void calculateTheResult() {
          assertEquals('Hello World.', 'Hello World.')
        }
      }
      

      when executed results in the output:

      Caught: org.testng.TestNGException:
      An error occurred while instantiating class testngFails: null
      org.testng.TestNGException:
      An error occurred while instantiating class testngFails: null
      at org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:398)
      at org.testng.internal.ClassHelper.createInstance(ClassHelper.java:299)
      at org.testng.internal.ClassImpl.getDefaultInstance(ClassImpl.java:110)
      at org.testng.internal.ClassImpl.getInstances(ClassImpl.java:186)
      at org.testng.internal.TestNGClassFinder.<init>(TestNGClassFinder.java:120)
      at org.testng.TestRunner.initMethods(TestRunner.java:409)
      at org.testng.TestRunner.init(TestRunner.java:235)
      at org.testng.TestRunner.init(TestRunner.java:205)
      at org.testng.TestRunner.<init>(TestRunner.java:153)
      at org.testng.SuiteRunner$DefaultTestRunnerFactory.newTestRunner(SuiteRunner.java:522)
      at org.testng.SuiteRunner.init(SuiteRunner.java:157)
      at org.testng.SuiteRunner.<init>(SuiteRunner.java:111)
      at org.testng.TestNG.createSuiteRunner(TestNG.java:1273)
      at org.testng.TestNG.createSuiteRunners(TestNG.java:1260)
      at org.testng.TestNG.runSuitesLocally(TestNG.java:1114)
      at org.testng.TestNG.run(TestNG.java:1031)
      Caused by: java.lang.ExceptionInInitializerError
      at org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:29)
      at org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:387)
      ... 15 more
      Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
      at testngFails.<clinit>(testngFails.groovy)
      ... 17 more

        Issue Links

          Activity

          Hide
          Russel Winder added a comment -

          I find that GINT does not work either so changed the title of the bug report.

          The GINT script:

          @GrabResolver(name='atlassian', root='https://maven.atlassian.com/content/groups/public/')
          @Grab('org.swift.tools:gint:1.8.0')
          import org.swift.tools.Gint
          
          includeTool << Gint
          
          gint.initialize(this)
          
          //  GINT always trims the received data so the trailing newline will be removed.
          expectedResult = 'Hello World.'
          
          new File('.').listFiles([accept: {directory, name -> name.startsWith('helloWorld_') && name.endsWith('.groovy')}] as FilenameFilter).each{script ->
            gint.setCmdGenerator('groovy',  [file: script])
            gint.add(name: script, data: expectedResult)
          }
          
          gint.finalizeTest()
          

          fails with the error:

          java.lang.reflect.InvocationTargetException
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(Method.java:491)
          at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:106)
          at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:128)
          Caused by: java.lang.ExceptionInInitializerError
          at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
          at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
          at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
          at java.lang.reflect.Constructor.newInstance(Constructor.java:414)
          at java.lang.Class.newInstance(Class.java:443)
          at org.codehaus.groovy.runtime.InvokerHelper.createScript(InvokerHelper.java:420)
          at groovy.lang.GroovyShell.parse(GroovyShell.java:625)
          at groovy.lang.GroovyShell.parse(GroovyShell.java:605)
          at groovy.lang.GroovyShell$parse.call(Unknown Source)
          at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
          at gant.Gant.loadScript(Gant.groovy:235)
          at gant.Gant$loadScript$1.callCurrent(Unknown Source)
          at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
          at gant.Gant.loadScript(Gant.groovy:268)
          at gant.Gant$loadScript$0.callCurrent(Unknown Source)
          at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
          at gant.Gant.loadScript(Gant.groovy:248)
          at gant.Gant$loadScript.callCurrent(Unknown Source)
          at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
          at gant.Gant.processArgs(Gant.groovy:536)
          at gant.Gant$processArgs.call(Unknown Source)
          at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
          at gant.Gant.main(Gant.groovy:668)
          ... 6 more
          Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
          at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:212)
          at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:182)
          at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:248)
          at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:212)
          at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:237)
          at groovy.grape.Grape.grab(Grape.java:127)
          at groovy.grape.Grape$grab.callStatic(Unknown Source)
          at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:53)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:157)
          at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165)
          at test_all_gint.<clinit>(test_all_gint)
          ... 39 more

          Show
          Russel Winder added a comment - I find that GINT does not work either so changed the title of the bug report. The GINT script: @GrabResolver(name='atlassian', root='https: //maven.atlassian.com/content/groups/ public /') @Grab('org.swift.tools:gint:1.8.0') import org.swift.tools.Gint includeTool << Gint gint.initialize( this ) // GINT always trims the received data so the trailing newline will be removed. expectedResult = 'Hello World.' new File('.').listFiles([accept: {directory, name -> name.startsWith('helloWorld_') && name.endsWith('.groovy')}] as FilenameFilter).each{script -> gint.setCmdGenerator('groovy', [file: script]) gint.add(name: script, data: expectedResult) } gint.finalizeTest() fails with the error: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:491) at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:106) at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:128) Caused by: java.lang.ExceptionInInitializerError at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:414) at java.lang.Class.newInstance(Class.java:443) at org.codehaus.groovy.runtime.InvokerHelper.createScript(InvokerHelper.java:420) at groovy.lang.GroovyShell.parse(GroovyShell.java:625) at groovy.lang.GroovyShell.parse(GroovyShell.java:605) at groovy.lang.GroovyShell$parse.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120) at gant.Gant.loadScript(Gant.groovy:235) at gant.Gant$loadScript$1.callCurrent(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141) at gant.Gant.loadScript(Gant.groovy:268) at gant.Gant$loadScript$0.callCurrent(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141) at gant.Gant.loadScript(Gant.groovy:248) at gant.Gant$loadScript.callCurrent(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141) at gant.Gant.processArgs(Gant.groovy:536) at gant.Gant$processArgs.call(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116) at gant.Gant.main(Gant.groovy:668) ... 6 more Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:212) at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:182) at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:248) at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:212) at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:237) at groovy.grape.Grape.grab(Grape.java:127) at groovy.grape.Grape$grab.callStatic(Unknown Source) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:53) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:157) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165) at test_all_gint.<clinit>(test_all_gint) ... 39 more
          Hide
          Russel Winder added a comment - - edited

          Fails with a standard install, so not an indy artefact problem.

          Show
          Russel Winder added a comment - - edited Fails with a standard install, so not an indy artefact problem.
          Hide
          Paul King added a comment -

          Hmmm. Both scripts worked fine for me using Java 7. Are you perhaps using Java 8?

          Show
          Paul King added a comment - Hmmm. Both scripts worked fine for me using Java 7. Are you perhaps using Java 8?
          Hide
          Russel Winder added a comment -

          Hummm? likewise, I thought I had tried JDK8, JDK8_Lambda and JDK7 and found it failed with all three. This was wrong, JDK7 does seem to work. I have changed the title accordingly.

          Show
          Russel Winder added a comment - Hummm? likewise, I thought I had tried JDK8, JDK8_Lambda and JDK7 and found it failed with all three. This was wrong, JDK7 does seem to work. I have changed the title accordingly.
          Hide
          Jochen Theodorou added a comment -

          There is no way to solve this of yet, since the guys at the jdk did not yet decide for a replacement for jdk8

          Show
          Jochen Theodorou added a comment - There is no way to solve this of yet, since the guys at the jdk did not yet decide for a replacement for jdk8
          Hide
          Russel Winder added a comment -

          I think it is not an option to say we cannot solve this if Groovy is to be used for scripting. Which method is it that is causing the problem? Is it part of a public API or an internal method someone found? I cannot believe a public API method got removed, there are deprecated methods 18 years old that have not been removed: Sun and now Oracle seem to believe public API methods should never be removed, so why mark anything as deprecated?

          I have now stopped using Groovy for scripting because of this @Grab and JDK8 problem.

          Show
          Russel Winder added a comment - I think it is not an option to say we cannot solve this if Groovy is to be used for scripting. Which method is it that is causing the problem? Is it part of a public API or an internal method someone found? I cannot believe a public API method got removed, there are deprecated methods 18 years old that have not been removed: Sun and now Oracle seem to believe public API methods should never be removed, so why mark anything as deprecated? I have now stopped using Groovy for scripting because of this @Grab and JDK8 problem.
          Hide
          Jochen Theodorou added a comment -

          Russel, I think you didn't get my comment fully... The method that is causing the problem is sun.reflect.Reflection#getCallerClass(int). Yes, it is an internal method, and yes, we should maybe have never used it and find other ways. It is not only Groovy that has now a problem because of that, it is also for example log4j (and several other frameworks). Many alternatives have been suggested, but none of them offers the same capability.

          They promised to find a solution for this in jdk8, so we are actually waiting for them to act. It is of course unfortunate, that they did not put it back in jdk8 before that developer release even though they did know long before this is causing problems.

          Show
          Jochen Theodorou added a comment - Russel, I think you didn't get my comment fully... The method that is causing the problem is sun.reflect.Reflection#getCallerClass(int). Yes, it is an internal method, and yes, we should maybe have never used it and find other ways. It is not only Groovy that has now a problem because of that, it is also for example log4j (and several other frameworks). Many alternatives have been suggested, but none of them offers the same capability. They promised to find a solution for this in jdk8, so we are actually waiting for them to act. It is of course unfortunate, that they did not put it back in jdk8 before that developer release even though they did know long before this is causing problems.
          Hide
          Russel Winder added a comment -

          The developer release, b106, isn't a release candidate, it is effectively beta 1. I am therefore hopeful that appropriate changes are possible. I feel a Twitter campaign coming on?

          Show
          Russel Winder added a comment - The developer release, b106, isn't a release candidate, it is effectively beta 1. I am therefore hopeful that appropriate changes are possible. I feel a Twitter campaign coming on?
          Hide
          Guillaume Delcroix added a comment -

          We're actually thinking of applying that patch from Cédric:
          https://github.com/melix/groovy-core/commit/ab73b582f8aebfad13ffe7a032dc4ad6b1479b3c

          It's a work around that we found using Security Managers.
          We will likely integrate that change across all branches, and update that once JDK 8 comes up with a nicer solution (if ever).

          Perhaps you could try this change, and see how (well) it works in the context of JDK 8?

          Thanks for your help testing this.

          Show
          Guillaume Delcroix added a comment - We're actually thinking of applying that patch from Cédric: https://github.com/melix/groovy-core/commit/ab73b582f8aebfad13ffe7a032dc4ad6b1479b3c It's a work around that we found using Security Managers. We will likely integrate that change across all branches, and update that once JDK 8 comes up with a nicer solution (if ever). Perhaps you could try this change, and see how (well) it works in the context of JDK 8? Thanks for your help testing this.
          Hide
          Russel Winder added a comment -

          If Cédric can check to ensure there are no conflicting changes in the interim and publish a feature branch on a Git repository, I can pull that feature branch, try things and report in.

          Show
          Russel Winder added a comment - If Cédric can check to ensure there are no conflicting changes in the interim and publish a feature branch on a Git repository, I can pull that feature branch, try things and report in.
          Hide
          Guillaume Delcroix added a comment -

          Or simply just cherry-pick that commit!
          Let's keep things simple.

          Show
          Guillaume Delcroix added a comment - Or simply just cherry-pick that commit! Let's keep things simple.
          Hide
          Russel Winder added a comment -

          Pulling a ready prepared feature branch is a lot more simple for me that fiddling with cherry picking.

          Let's keep things simple, for the testers.

          Show
          Russel Winder added a comment - Pulling a ready prepared feature branch is a lot more simple for me that fiddling with cherry picking. Let's keep things simple, for the testers.
          Hide
          Russel Winder added a comment -

          I pulled the latest master/HEAD and built 2.3.0-SNAPSHOT. I then ran:

          @Grab('org.testng:testng:6.8.7')
          @Grab('org.codehaus.groovy:groovy-testng:2.3.0-SNAPSHOT')
          import org.testng.annotations.Test
          import static org.testng.Assert.assertEquals
          
          class GROOVY_6156 {
            @Test void calculateTheResult() {
              assertEquals('Hello World.', 'Hello World.')
            }
          }
          

          but got the result:

          Caught: java.lang.NoClassDefFoundError: com/google/inject/Module
          java.lang.NoClassDefFoundError: com/google/inject/Module
          Caused by: java.lang.ClassNotFoundException: com.google.inject.Module

          Of course, the problem may not actually be related.

          Show
          Russel Winder added a comment - I pulled the latest master/HEAD and built 2.3.0-SNAPSHOT. I then ran: @Grab('org.testng:testng:6.8.7') @Grab('org.codehaus.groovy:groovy-testng:2.3.0-SNAPSHOT') import org.testng.annotations.Test import static org.testng.Assert.assertEquals class GROOVY_6156 { @Test void calculateTheResult() { assertEquals('Hello World.', 'Hello World.') } } but got the result: Caught: java.lang.NoClassDefFoundError: com/google/inject/Module java.lang.NoClassDefFoundError: com/google/inject/Module Caused by: java.lang.ClassNotFoundException: com.google.inject.Module Of course, the problem may not actually be related.
          Hide
          Guillaume Delcroix added a comment -

          Sounds unrelated, and seems to be a missing dependency to Guice.

          Show
          Guillaume Delcroix added a comment - Sounds unrelated, and seems to be a missing dependency to Guice.
          Hide
          Paul King added a comment - - edited

          Yes, I get the same error with Java 7 and Russel's script but adding in the required dependency (which is strangely listed as provided in the pom - though TestNG's POM has been broken for many years, so what is one more error!) fixes the problem with Java 7 and 8:

          @Grab('org.testng:testng:6.8.5')
          @Grab('com.google.inject:guice:2.0')
          @Grab('org.codehaus.groovy:groovy-testng:2.2.0-SNAPSHOT')
          @GrabConfig(systemClassLoader=true)
          import org.testng.annotations.Test
          import static org.testng.Assert.assertEquals
          
          class testngFails {
            @Test void calculateTheResult() {
              assertEquals('Hello World.', 'Hello World.')
            }
          }
          
          Show
          Paul King added a comment - - edited Yes, I get the same error with Java 7 and Russel's script but adding in the required dependency (which is strangely listed as provided in the pom - though TestNG's POM has been broken for many years, so what is one more error!) fixes the problem with Java 7 and 8: @Grab('org.testng:testng:6.8.5') @Grab('com.google.inject:guice:2.0') @Grab('org.codehaus.groovy:groovy-testng:2.2.0-SNAPSHOT') @GrabConfig(systemClassLoader= true ) import org.testng.annotations.Test import static org.testng.Assert.assertEquals class testngFails { @Test void calculateTheResult() { assertEquals('Hello World.', 'Hello World.') } }
          Hide
          Russel Winder added a comment -

          Paul, Your fix does not work for me.

          Show
          Russel Winder added a comment - Paul, Your fix does not work for me.
          Hide
          Russel Winder added a comment -

          If the TestNG POM had been broken wouldn't Cédric have fixed it by now?

          Show
          Russel Winder added a comment - If the TestNG POM had been broken wouldn't Cédric have fixed it by now?
          Hide
          Paul King added a comment -

          Russel, what error do you get now? I was using b108 and master?

          I would have thought fixing the POM a useful idea but Cédric seems to have his own ideas of what an optional dependency is and now it seems a provided one too.

          Show
          Paul King added a comment - Russel, what error do you get now? I was using b108 and master? I would have thought fixing the POM a useful idea but Cédric seems to have his own ideas of what an optional dependency is and now it seems a provided one too.
          Hide
          Cédric Champeau added a comment - - edited

          What do you mean by "my own ideas" ?

          A provided dependency is something that is, as is states, provided in the environment you run on. Typically, servlet API is required to build, but it is provided by the application server so you don't want to provide it in your war. If guice is a provided dependency, then IMHO it's a problem in the POM, not in Groovy, which behaves normally here: it will not download nor add the dependency on classpath, since it's supposed to be there...

          As for an optional dependency: it's a dependency that you need to build your project, but that not everyone depends on. For example, you build a bridge with several databases, but you don't want you users to download the drivers for every database, you you put the dependencies as optional. The user then is required to explicitely add a dependency on the driver.

          Show
          Cédric Champeau added a comment - - edited What do you mean by "my own ideas" ? A provided dependency is something that is, as is states, provided in the environment you run on. Typically, servlet API is required to build, but it is provided by the application server so you don't want to provide it in your war. If guice is a provided dependency, then IMHO it's a problem in the POM, not in Groovy, which behaves normally here: it will not download nor add the dependency on classpath, since it's supposed to be there... As for an optional dependency: it's a dependency that you need to build your project, but that not everyone depends on. For example, you build a bridge with several databases, but you don't want you users to download the drivers for every database, you you put the dependencies as optional. The user then is required to explicitely add a dependency on the driver.
          Hide
          Cédric Champeau added a comment -

          Oh, I'm not the Cédric you were referring to

          Show
          Cédric Champeau added a comment - Oh, I'm not the Cédric you were referring to
          Hide
          Paul King added a comment - - edited

          @Groovy Cedric: I agree with your definitions.

          At the moment, TestNG assumes you have already some dependency containing Guice injection in your classpath. I suspect some JSR330 containers might now provide this but I would be surprised if many test environments where I would expect TestNG to be used would normally (or at least exclusively) be run in such containers. Also, TestNG pollutes your classpath with numerous unneeded jars, e.g. junit, hamcrest-core, beanshell, snakeyaml. I understand (partially) the reasoning: to allow lazy developers not to work out which of the optional bits they might want and where to get them. But you could use profiles (admittedly slightly broken in earlier times) or a testng-all artifact if you really wanted to accommodate that scenario without penalising your vigilant users.

          Show
          Paul King added a comment - - edited @Groovy Cedric: I agree with your definitions. At the moment, TestNG assumes you have already some dependency containing Guice injection in your classpath. I suspect some JSR330 containers might now provide this but I would be surprised if many test environments where I would expect TestNG to be used would normally (or at least exclusively) be run in such containers. Also, TestNG pollutes your classpath with numerous unneeded jars, e.g. junit, hamcrest-core, beanshell, snakeyaml. I understand (partially) the reasoning: to allow lazy developers not to work out which of the optional bits they might want and where to get them. But you could use profiles (admittedly slightly broken in earlier times) or a testng-all artifact if you really wanted to accommodate that scenario without penalising your vigilant users.
          Hide
          Russel Winder added a comment -

          Paul, things are exactly the same.

          @Grab('org.testng:testng:6.8.7')
          @Grab('com.google.inject:guice:3.0')
          @Grab('org.codehaus.groovy:groovy-testng:2.3.0-SNAPSHOT')
          import org.testng.annotations.Test
          import static org.testng.Assert.assertEquals
          
          class GROOVY_6156 {
            @Test void calculateTheResult() {
              assertEquals('Hello World.', 'Hello World.')
            }
          }
          

          gives me:

          Caught: java.lang.NoClassDefFoundError: com/google/inject/Module
          java.lang.NoClassDefFoundError: com/google/inject/Module
          Caused by: java.lang.ClassNotFoundException: com.google.inject.Module

          whether I use Java 7 or Java 8.

          I have other @Grabs working on Java 8 so it is something specific to this combination.

          Show
          Russel Winder added a comment - Paul, things are exactly the same. @Grab('org.testng:testng:6.8.7') @Grab('com.google.inject:guice:3.0') @Grab('org.codehaus.groovy:groovy-testng:2.3.0-SNAPSHOT') import org.testng.annotations.Test import static org.testng.Assert.assertEquals class GROOVY_6156 { @Test void calculateTheResult() { assertEquals('Hello World.', 'Hello World.') } } gives me: Caught: java.lang.NoClassDefFoundError: com/google/inject/Module java.lang.NoClassDefFoundError: com/google/inject/Module Caused by: java.lang.ClassNotFoundException: com.google.inject.Module whether I use Java 7 or Java 8. I have other @Grabs working on Java 8 so it is something specific to this combination.
          Hide
          Paul King added a comment -

          Russel, does the @GrabConfig as per my example help?

          Show
          Paul King added a comment - Russel, does the @GrabConfig as per my example help?
          Hide
          Russel Winder added a comment -

          Yes it does. Sorry I had completely missed that.

          So the question here is, can the Guice dependency alone be put in via the system class loader or is it required to have TestNG and the Groovy-TestNG loaded there as well?

          Show
          Russel Winder added a comment - Yes it does. Sorry I had completely missed that. So the question here is, can the Guice dependency alone be put in via the system class loader or is it required to have TestNG and the Groovy-TestNG loaded there as well?
          Hide
          Russel Winder added a comment -

          Just did the experiment, it is only Guice that has to go in via the system class loader. The following works fine:

          @Grab('com.google.inject:guice:3.0')
          @GrabConfig(systemClassLoader=true)
          import java.util.concurrent.ConcurrentHashMap
          
          @Grab('org.testng:testng:6.8.7')
          @Grab('org.codehaus.groovy:groovy-testng:2.3.0-SNAPSHOT')
          import org.testng.annotations.Test
          import static org.testng.Assert.assertEquals
          
          class GROOVY_6156 {
            @Test void calculateTheResult() {
              assertEquals('Hello World.', 'Hello World.')
            }
          }
          

          Assuming the @GrabConfig is local to the one @Grab and not a global setting.

          Show
          Russel Winder added a comment - Just did the experiment, it is only Guice that has to go in via the system class loader. The following works fine: @Grab('com.google.inject:guice:3.0') @GrabConfig(systemClassLoader= true ) import java.util.concurrent.ConcurrentHashMap @Grab('org.testng:testng:6.8.7') @Grab('org.codehaus.groovy:groovy-testng:2.3.0-SNAPSHOT') import org.testng.annotations.Test import static org.testng.Assert.assertEquals class GROOVY_6156 { @Test void calculateTheResult() { assertEquals('Hello World.', 'Hello World.') } } Assuming the @GrabConfig is local to the one @Grab and not a global setting.
          Hide
          Paul King added a comment - - edited

          Unfortunately it's global at the moment. The plan was for the @Grapes annotation to be the grouping mechanism which meant you could have put the guice @Grab and @GrabConfig inside a @Grapes and it would have been isolated - but we haven't implemented it to date. The thinking has been that @Grab is meant for simple dependency declarations. If you have something more complex than what is currently supported, perhaps in general you are better off leveraging a build system like gradle or even considering OSGi if you need strict isolation and can justify the added complexity.

          So, today we just merge all @Grab related annotations together - making the @Grapes annotation effectively a no-op at the moment and hence why we don't encourage it much these days. If we had needed to keep the @Grab annotations around until runtime we would have needed @Grapes as a holder but as of Java 8 we could even hide that away with the @Repeatable meta-annotation.

          I still think we should eventually add the @Grapes isolation capability, so I'm not advocating deprecating @Grapes - just indicating the current state of play.

          Show
          Paul King added a comment - - edited Unfortunately it's global at the moment. The plan was for the @Grapes annotation to be the grouping mechanism which meant you could have put the guice @Grab and @GrabConfig inside a @Grapes and it would have been isolated - but we haven't implemented it to date. The thinking has been that @Grab is meant for simple dependency declarations. If you have something more complex than what is currently supported, perhaps in general you are better off leveraging a build system like gradle or even considering OSGi if you need strict isolation and can justify the added complexity. So, today we just merge all @Grab related annotations together - making the @Grapes annotation effectively a no-op at the moment and hence why we don't encourage it much these days. If we had needed to keep the @Grab annotations around until runtime we would have needed @Grapes as a holder but as of Java 8 we could even hide that away with the @Repeatable meta-annotation. I still think we should eventually add the @Grapes isolation capability, so I'm not advocating deprecating @Grapes - just indicating the current state of play.

            People

            • Assignee:
              Cédric Champeau
              Reporter:
              Russel Winder
            • Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development