You probably know about using Annotation Types for your custom Gradle plugin input values:

class MyTask extends DefaultTask {
  @Input
  String aSimpleProperty
  @InputFile
  File anInputFile
  @Input
  @Optional
  String[] optionalProperty
  @OutputFile
  File anOutputFile

  @TaskAction
  def performTask() {
    // …
  }
}

Using complex task input

When using a more complex input model, you can try something like this:

class TaskConfiguration {
  String aSimpleProperty
  File anInputFile
  String[] optionalProperty
}

class MyTask extends DefaultTask {
  @Input
  TaskConfiguration myConfiguration
  @OutputFile
  File anOutputFile

  @TaskAction
  def performTask() {
    // …
  }
}

After running that task Gradle will try to serialize your TaskConfiguration, but will fail due to the missing Serializable interface declaration. Gradle needs to serialize your input so that it can detemine in following builds whether any input has changed. You get error messages like:

org.gradle.api.UncheckedIOException: Could not add entry :projectname:taskname to cache taskArtifacts.bin (/home/user/projectname/.gradle/1.8/taskArtifacts/taskArtifacts.bin).
 at org.gradle.cache.internal.btree.BTreePersistentIndexedCache.put(BTreePersistentIndexedCache.java:156)
 at org.gradle.cache.internal.MultiProcessSafePersistentIndexedCache$2.run(MultiProcessSafePersistentIndexedCache.java:53)
 Caused by: java.io.NotSerializableException: de.projectname.TaskConfiguration
 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)

Making the task input serializable…

Adding the java.io.Serializable interface to your TaskConfiguration won’t be enough, because Gradle also needs an equals() implementation to compare the serialized model with the current one. That’s quite much compared to the properties in your configuration model. Especially when using the daemon you can get error messages like:

org.gradle.api.UncheckedIOException: Could not read entry :projectname:taskname from cache taskArtifacts.bin (/home/user/projectname/.gradle/1.8/taskArtifacts/taskArtifacts.bin).
 at org.gradle.cache.internal.btree.BTreePersistentIndexedCache.get(BTreePersistentIndexedCache.java:129)
 Caused by: java.lang.ClassNotFoundException: de.projectname.TaskConfiguration
 at java.net.URLClassLoader$1.run(URLClassLoader.java:366)

… the Gradle way

Looking at the Gradle documentation you’ll step over the @Nested annotation. The @Nested annotation allows you to configure each property of your task input like this:

class TaskConfiguration {
  @Input
  String aSimpleProperty
  @InputFile
  File anInputFile
  @Input
  @Optional
  String[] optionalProperty
}

class MyTask extends DefaultTask {
  @Nested
  TaskConfiguration myConfiguration
  @OutputFile
  File anOutputFile

  @TaskAction
  def performTask() {
    // …
  }
}

You might as well use @Nested to create a more complex model:

class NestedConfiguration {
  @Input
  String yetAnotherProperty
}

class TaskConfiguration {
  @Input
  String aSimpleProperty
  @InputFile
  File anInputFile
  @Input
  @Optional
  String[] optionalProperty
  @Nested
  NestedConfiguration nestedConfig
}

class MyTask extends DefaultTask {
  @Nested
  TaskConfiguration myConfiguration
  @OutputFile
  File anOutputFile

  @TaskAction
  def performTask() {
    // …
  }
}

That way you don’t need any Serializable or equals() boilerplate code and Gradle can still check your task configuration for changes. You can find an example at the gradle-debian-plugin repository.