conventionMapping woes... what am I doing wrong?

I am working on writing a SoapUI plugin for Gradle and I’m having a bad time with conventionMapping.

@Override
    void apply(Project project) {
        // Create and register the extension object with name EXTENSION_NAME
        SoapUIPluginExtension soapUIPluginExtension = project.extensions.create(EXTENSION_NAME, SoapUIPluginExtension)
          configSoapTest(project, soapUIPluginExtension)
      }
      private void configSoapTest(Project project, SoapUIPluginExtension soapUIPluginExtension) {
        project.task(SOAP_TEST_TASK, type: TestTask) {
            conventionMapping.projectFile = { soapUIPluginExtension.test.projectFile }
            conventionMapping.settingsFile = { soapUIPluginExtension.test.settingsFile }
            conventionMapping.projectPassword = { soapUIPluginExtension.test.projectPassword }
            ...
        }
    }

Everything I have seen and read would indicate this is correct, and my unit test for a thrown exception when projectFile isn’t set also passes. However, when I step through the code in debug mode I can see the values are null, and when I compile and try running the project in a test build, it fails. Ignoring the duff unit tests for a moment, is there anything strikingly obvious that I’m doing wrong?

Any help gratefully appreciated.

dont know if this is the cause but according to this example (might be outdated?): http://code4reference.com/2012/08/gradle-custom-plugin-part-2/

you have to explicitly create the nested Extensions like this:

project.extensions.create("c4rArgs", Code4ReferencePluginExtension)
project.c4rArgs.extensions.create("nestedArgs",C4RNestedPluginExtention)

for some reason this seems to work:

SoapUIPluginExtension soapUIPluginExtension = project.extensions.create(EXTENSION_NAME, SoapUIPluginExtension)
project.soapui.extensions.create("tool", SoapUIToolConvention)
project.soapui.extensions.create("security", SoapUISecurityConvention)
project.soapui.extensions.create("load", SoapUILoadConvention)
project.soapui.extensions.create("test", SoapUITestConvention)
project.soapui.extensions.create("mock", SoapUIMockConvention)

but only with SoapUIPluginExtension being empty and thus all extensions being added dynamically like this:

class SoapUIPluginExtension {
}

see http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtensionAware.html for a similar example.

having it like this will not work (object will be null):

class SoapUIPluginExtension {
    SoapUIToolConvention tool
    SoapUISecurityConvention security
    SoapUILoadConvention load
    SoapUITestConvention test
    SoapUIMockConvention mock
}

this will also not work (properties of these objects will be null):

class SoapUIPluginExtension {
    SoapUIToolConvention tool = new SoapUIToolConvention()
    SoapUISecurityConvention security = new SoapUISecurityConvention()
    SoapUILoadConvention load = new SoapUILoadConvention()
    SoapUITestConvention test = new SoapUITestConvention()
    SoapUIMockConvention mock = new SoapUIMockConvention()
}

Smells like a bug or at least there is room for improvement: - maybe add an “Extension”-Annotation and then automatically initialize nested extensions - at least warn if People try to use the 2nd or 3rd Version of the Extension class (shadowing the actual nested-extensions with null objects or uninitalized Extension objects)

I was sincerely hoping this would work but still no satisfaction.

:soaptest FAILED
  FAILURE: Build failed with an exception.
  * What went wrong:
Execution failed for task ':soaptest'.
> soapui-project-file setting is required
  * Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
  BUILD FAILED
  Total time: 2.855 secs

When I run my test that ive now added to the project I get the error above

hi Sion,

as stated in this thread: Convention mapping for plugins - Old Forum - Gradle Forums

Convention mapping only works if the getter is called. Groovy automatically calls the getter if you use > property syntax, except when the property is declared in the same class.

so when you change your code from

if (!projectFile) {
 new GradleException('soapui-project-file setting is required')
}

to

if (!getProjectFile()) {
 new GradleException('soapui-project-file setting is required')
}

it works. This is needed since the projectFile property is defined in the same class and groovy will not call the conventionMapinng-getter.

Of course this has to be done in all methods of classes that rely on convention mapping and currently use properties directly without the getter.

result:

:soaptest (Thread[main,5,main]) started.
:soaptest
Executing task ':soaptest' (up-to-date check took 0.0 secs) due to:
  Task has not declared any outputs.
:soaptest (Thread[main,5,main]) completed. Took 0.018 secs.
  BUILD SUCCESSFUL
  Total time: 2.503 secs

Thanks Chris.

Unfortunately the project still doesn’t run properly but I can do some debugging from here. Thanks!