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

Unable to pass java.util.Properties object using new Worker API (Java) in Gradle 5.6 #10342

Closed
steffenyount opened this issue Aug 22, 2019 · 5 comments
Labels
Milestone

Comments

@steffenyount
Copy link

steffenyount commented Aug 22, 2019

To workaround #10323 I tried an implementation using the new Worker API with Java based class definition. Passing a java.util.Properties object with the new Worker API (Java) fails in Gradle 5.6

E.g. a simple project defined across two files
~/tstProj/buildSrc/src/main/java/MyTask.java:

import org.gradle.api.provider.Property;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
import org.gradle.workers.IsolationMode;
import org.gradle.workers.WorkAction;
import org.gradle.workers.WorkParameters;
import org.gradle.workers.WorkQueue;
import org.gradle.workers.WorkerExecutor;

import java.io.IOException;
import java.util.Properties;
import javax.inject.Inject;

public class MyTask extends DefaultTask {
    final WorkerExecutor workerExecutor;

    @Inject
    public MyTask(WorkerExecutor workerExecutor) {
        this.workerExecutor = workerExecutor;
    }

    @TaskAction
    public void run() {
        Properties myProps = new Properties();
        myProps.setProperty("key1", "value1");
        myProps.setProperty("key2", "value2");
        myProps.setProperty("key3", "value3");

        final WorkQueue workQueue = workerExecutor.processIsolation();

        workQueue.submit(MyRunner.class, (MyParameters params) -> {
            params.getMyProps().set(myProps);
        });
    }

    interface MyParameters extends WorkParameters {
        Property<Properties> getMyProps();
    }

   public static abstract class MyRunner implements WorkAction<MyParameters> {
        @Override
        public void execute() {
            final MyParameters params = getParameters();

            final Properties myProps = params.getMyProps().get();

            try {
                myProps.store(System.out, null);
            } catch (IOException e) {
                // do nothing
            }
        }
    }
}

~/tstProj/build.gradle:

import MyTask

task myTask(type: MyTask) {
    description 'My Task'
}

result:

./gradlew myTask
...
> Task :myTask FAILED
Caching disabled for task ':myTask' because:
  Build cache is disabled
Task ':myTask' is not up-to-date because:
  Task has not declared any outputs despite executing actions.
Using method ObjectFactory.property() method to create a property of type Map<K, V> has been deprecated. This will fail with an error in Gradle 6.0. Please use the ObjectFactory.mapProperty() method instead.
:myTask (Thread[Execution worker for ':',5,main]) completed. Took 0.013 secs.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':myTask'.
> A failure occurred while executing MyTask$MyRunner
   > java.util.LinkedHashMap cannot be cast to java.util.Properties

* Try:
Run with --stacktrace option to get the stack trace. Run with --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 737ms
1 actionable task: 1 executed

Expected Behavior

BUILD SUCCESSFUL

Current Behavior

BUILD FAILED

Context

I'm attempting to upgrade our large multi-project build from 5.4.1 -> 5.6 and one of our custom build tasks has been using the old Worker API. I attempted a replacement like I've shown in the example code above and encountered this failure.

The upgrade to 5.6 breaks this build task.

Steps to Reproduce

See example above.

Your Environment

OSX

@ghale
Copy link
Member

ghale commented Aug 23, 2019

Similar to #10341, the solution here is to use either a MapProperty (in which case you will need to treat your properties object like a Map in your work action):

    interface MyParameters extends WorkParameters {
        MapProperty getMyProps();
    }

Or with 5.6.1, we've fixed the problem with using a straight Properties object (note that you will need both a getter/setter since it's not a Property object):

    interface MyParameters extends WorkParameters {
        Properties getMyProps();
        void setMyProps(Properties myProps);
    }

You can try out the second option now with the latest release nightly (https://gradle.org/release-nightly/).

At least at the moment, we don't plan to support Property<Properties>.

@steffenyount
Copy link
Author

steffenyount commented Aug 23, 2019

verified using distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-5.6.1-20190823130927+0000-bin.zip

The code provided above now results in the following successful build and includes an ObjectFactory.property() deprecation warning:

./gradlew myTask
...
> Task :myTask
Caching disabled for task ':myTask' because:
  Build cache is disabled
Task ':myTask' is not up-to-date because:
  Task has not declared any outputs despite executing actions.
Using method ObjectFactory.property() method to create a property of type Map<K, V> has been deprecated. This will fail with an error in Gradle 6.0. Please use the ObjectFactory.mapProperty() method instead.
#Fri Aug 23 09:18:30 PDT 2019
key3=value3
key2=value2
key1=value1
:myTask (Thread[Execution worker for ':',5,main]) completed. Took 0.639 secs.

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

Using a straight Properties object in the interface as suggested above fails with 5.6, but is successful with the new snapshot and there is no deprecation warning:

./gradlew myTask
...
> Task :myTask
Caching disabled for task ':myTask' because:
  Build cache is disabled
Task ':myTask' is not up-to-date because:
  Task has not declared any outputs despite executing actions.
#Fri Aug 23 09:14:40 PDT 2019
key3=value3
key2=value2
key1=value1
:myTask (Thread[Execution worker for ':',5,main]) completed. Took 0.631 secs.

BUILD SUCCESSFUL in 1s
1 actionable task: 1 executed

@steffenyount
Copy link
Author

steffenyount commented Aug 23, 2019

@ghale - thanks for the quick turnaround on this!

I was wondering: Instead of (or in addition to) the straight Properties support, would it make sense for the new Worker API to support a PropertiesProperty type and ObjectFactory.propertiesProperty() method similar to the MapProperty type and ObjectFactory.mapProperty() method already provided by the API?

Since java.util.Properties and *.properties files are pretty ubiquitous in build script usage, it doesn't seem like such a stretch for the Gradle APIs to provide first class support for their data structures.

Thoughts?

@ghale
Copy link
Member

ghale commented Aug 26, 2019

Yeah, we've discussed a PropertiesProperty and I wouldn't say that we ruled that out or anything, but we felt like between MapProperty or using a plain Properties object, there were enough tools to handle the majority of use cases. Do you feel like it's more difficult to do what you want to do without PropertiesProperty or is it more that it would be less confusing to get things right with PropertiesProperty?

@steffenyount
Copy link
Author

steffenyount commented Aug 27, 2019

@ghale - It's more the later of the two: that it would seem more consistent and less confusing to get things right

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

No branches or pull requests

3 participants