Plugins and classloader isolation

The Hibernate build has been stuck at m2/m3 for many reasons. Recently I decided to get it moved up to m7. But I am running into class loader isolation problems in many of our plugins. As far as I can tell based on forums.gradle.org/gradle/topics/gradle_runtime_classpath et al, starting in m5 there was a change in how classes for plugins get loaded.

This has led to lots of ClassCastExceptions for me in plugins that use libraries that Gradle also uses under the covers. I only know most of this because Adam helped me with one such issue: https://github.com/sebersole/gradle-upload-auth-plugin/pull/2 That change was specifically dealing with access to Maven jars. The “solution” was to use reflection to access that Maven stuff I needed. It works, but obviously that is a PITA if you need to do that each and every time your plugin uses a jar or library that Maven also uses, but does not expose.

Take for example, dom4j. The plugin mentioned above is also using dom4j and now I get the same types of errors pertaining to that library: Nested exception:

java.lang.ClassCastException: org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory

at org.dom4j.DocumentFactory.getInstance(DocumentFactory.java:97)

at org.dom4j.io.SAXReader.getDocumentFactory(SAXReader.java:645)

at org.dom4j.io.SAXReader.createContentHandler(SAXReader.java:969)

at org.dom4j.io.SAXReader.read(SAXReader.java:449)

at org.hibernate.build.gradle.upload.StandardMavenAuthenticationProvider.loadRepositoryAuthenticationMap(StandardMavenAuthenticationProvider.java:104)

So what is the solution?

Do you have dom4j specified as a dependency of your plugin?

In general, I wonder if a parent-last delegation model would be a better solution than completely hiding away all those libraries. For example, it would solve the mentioned Maven problem immediately (I think).

Hey Peter, thanks for the response.

So if I understand correctly there is nothing I can do within the constraints of Gradle releases up through m7 at least? I will have to use reflection and remove the shared library (dom4j in this case) off my plugins explicit class-path?

Not sure parent-last delegation is appropriate in all cases either since it could lead to issues when the plugin class-path has access to the shared lib. Maybe I am missing something, but in the Maven deal wouldn’t it still be a problem if my plugin defines a dep on Maven jars?

Can’t you just have your own dom4j library in your plugin? You don’t need to share dom4j objects with anyone else, do you?

With parent-last delegation, plugins wouldn’t themselves declare dependencies for libraries that need to be shared with Gradle and/or other plugins. But they could still have their own version of libraries that don’t need to be shared.

Can’t you just have your own dom4j library in your plugin?

Note sure I follow. That is exactly what I do today and what causes problems. When I say “shared” I simply mean that I need to use a library that Gradle is using also, dom4j in this case. dom4j is defined in my plugin’s compile configuration. I tried removing it from compile thinking I would then pick up the dom4j classes from Gradle’s class loader. But of course then I cannot compile because the dom4j cannot be found for compilation (as a side-note, I am using gradleApi() also to define compile configuration, but gradleApi() does not account for dom4j)

I guess maybe could get away with adding dom4j in a “provided” configuration (you know, needed for compilation, but not exported to runtime classpath). Too bad Gradle does not think provided is useful :wink:

You don’t need to share dom4j objects with anyone else, do you?

No. Hence why I add it to my compile classpath. Though actually I follow the belief that any library I explicitly bind to in my code should be explicitly added to my dependency configuration.

I didn’t look at your code, so I didn’t know exactly what you do. However, the problem seems different from the Maven case. In the dom4j case you don’t need to share objects with Gradle or other plugins, and having a compile dependency in your plugin should suffice. Or so I thought. A deeper look into org.dom4j.DocumentFactory might reveal what the problem is.

Well looking at the stack trace I assume that it tries to reuse JAXP components that are loaded/cached as part of Gradle’s using dom4j.

My usage it about as basic as it gets:

private void loadRepositoryAuthenticationMap() {
        ...
        InputSource inputSource = new InputSource( new FileInputStream( settingsFile ) );
        final Document document = buildSAXReader().read( inputSource );
        ...
    }
      private SAXReader buildSAXReader() {
        SAXReader saxReader = new SAXReader();
        saxReader.setMergeAdjacentText( true );
        return saxReader;
    }

Thats it.

@Steve, do you have the rest of that stack trace?

Error reading Maven settings.xml
org.dom4j.DocumentException: org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory Nested exception: org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory
 at org.dom4j.io.SAXReader.read(SAXReader.java:484)
 at org.hibernate.build.gradle.upload.StandardMavenAuthenticationProvider.loadRepositoryAuthenticationMap(StandardMavenAuthenticationProvider.java:104)
 at org.hibernate.build.gradle.upload.StandardMavenAuthenticationProvider.determineAuthentication(StandardMavenAuthenticationProvider.java:64)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.locateAuthenticationDetails(AuthenticationHandler.java:77)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.access$000(AuthenticationHandler.java:41)
 at org.hibernate.build.gradle.upload.AuthenticationHandler$1.execute(AuthenticationHandler.java:57)
 at org.hibernate.build.gradle.upload.AuthenticationHandler$1.execute(AuthenticationHandler.java:52)
 at org.gradle.api.internal.FilteredAction.execute(FilteredAction.java:33)
 at org.gradle.api.internal.DefaultDomainObjectCollection.all(DefaultDomainObjectCollection.java:144)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.execute(AuthenticationHandler.java:51)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.execute(AuthenticationHandler.java:41)
 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:63)
 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:48)
 at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:34)
 at org.gradle.api.internal.changedetection.CacheLockHandlingTaskExecuter$1.run(CacheLockHandlingTaskExecuter.java:34)
 at org.gradle.cache.internal.DefaultCacheAccess$2.create(DefaultCacheAccess.java:200)
 at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:172)
 at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:198)
 at org.gradle.cache.internal.DefaultPersistentDirectoryStore.longRunningOperation(DefaultPersistentDirectoryStore.java:111)
 at org.gradle.api.internal.changedetection.DefaultTaskArtifactStateCacheAccess.longRunningOperation(DefaultTaskArtifactStateCacheAccess.java:83)
 at org.gradle.api.internal.changedetection.CacheLockHandlingTaskExecuter.execute(CacheLockHandlingTaskExecuter.java:32)
 at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:55)
 at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:57)
 at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:41)
 at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
 at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:52)
 at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:42)
 at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:243)
 at org.gradle.execution.DefaultTaskGraphExecuter.executeTask(DefaultTaskGraphExecuter.java:192)
 at org.gradle.execution.DefaultTaskGraphExecuter.doExecute(DefaultTaskGraphExecuter.java:177)
 at org.gradle.execution.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:83)
 at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:36)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
 at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
 at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
 at org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter$1.run(TaskCacheLockHandlingBuildExecuter.java:31)
 at org.gradle.cache.internal.DefaultCacheAccess$1.create(DefaultCacheAccess.java:111)
 at org.gradle.cache.internal.DefaultCacheAccess.useCache(DefaultCacheAccess.java:126)
 at org.gradle.cache.internal.DefaultCacheAccess.useCache(DefaultCacheAccess.java:109)
 at org.gradle.cache.internal.DefaultPersistentDirectoryStore.useCache(DefaultPersistentDirectoryStore.java:103)
 at org.gradle.api.internal.changedetection.DefaultTaskArtifactStateCacheAccess.useCache(DefaultTaskArtifactStateCacheAccess.java:79)
 at org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter.execute(TaskCacheLockHandlingBuildExecuter.java:29)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
 at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
 at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
 at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:54)
 at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:152)
 at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:108)
 at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:76)
 at org.gradle.launcher.cli.RunBuildAction.execute(RunBuildAction.java:42)
 at org.gradle.launcher.cli.RunBuildAction.execute(RunBuildAction.java:28)
 at org.gradle.launcher.exec.ExceptionReportingAction.execute(ExceptionReportingAction.java:32)
 at org.gradle.launcher.exec.ExceptionReportingAction.execute(ExceptionReportingAction.java:21)
 at org.gradle.launcher.cli.CommandLineActionFactory$WithLoggingAction.execute(CommandLineActionFactory.java:238)
 at org.gradle.launcher.cli.CommandLineActionFactory$WithLoggingAction.execute(CommandLineActionFactory.java:222)
 at org.gradle.launcher.Main.doAction(Main.java:48)
 at org.gradle.launcher.exec.EntryPoint$1.execute(EntryPoint.java:53)
 at org.gradle.launcher.exec.EntryPoint$1.execute(EntryPoint.java:51)
 at org.gradle.launcher.exec.Execution.execute(Execution.java:28)
 at org.gradle.launcher.exec.EntryPoint.run(EntryPoint.java:39)
 at org.gradle.launcher.Main.main(Main.java:39)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.gradle.launcher.ProcessBootstrap.runNoExit(ProcessBootstrap.java:51)
 at org.gradle.launcher.ProcessBootstrap.run(ProcessBootstrap.java:33)
 at org.gradle.launcher.GradleMain.main(GradleMain.java:24)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:33)
 at org.gradle.wrapper.Wrapper.execute(Wrapper.java:51)
 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:37)
Nested exception:
 java.lang.ClassCastException: org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory
 at org.dom4j.DocumentFactory.getInstance(DocumentFactory.java:97)
 at org.dom4j.io.SAXReader.getDocumentFactory(SAXReader.java:645)
 at org.dom4j.io.SAXReader.createContentHandler(SAXReader.java:969)
 at org.dom4j.io.SAXReader.read(SAXReader.java:449)
 at org.hibernate.build.gradle.upload.StandardMavenAuthenticationProvider.loadRepositoryAuthenticationMap(StandardMavenAuthenticationProvider.java:104)
 at org.hibernate.build.gradle.upload.StandardMavenAuthenticationProvider.determineAuthentication(StandardMavenAuthenticationProvider.java:64)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.locateAuthenticationDetails(AuthenticationHandler.java:77)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.access$000(AuthenticationHandler.java:41)
 at org.hibernate.build.gradle.upload.AuthenticationHandler$1.execute(AuthenticationHandler.java:57)
 at org.hibernate.build.gradle.upload.AuthenticationHandler$1.execute(AuthenticationHandler.java:52)
 at org.gradle.api.internal.FilteredAction.execute(FilteredAction.java:33)
 at org.gradle.api.internal.DefaultDomainObjectCollection.all(DefaultDomainObjectCollection.java:144)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.execute(AuthenticationHandler.java:51)
 at org.hibernate.build.gradle.upload.AuthenticationHandler.execute(AuthenticationHandler.java:41)
 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:63)
 at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:48)
 at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:34)
 at org.gradle.api.internal.changedetection.CacheLockHandlingTaskExecuter$1.run(CacheLockHandlingTaskExecuter.java:34)
 at org.gradle.cache.internal.DefaultCacheAccess$2.create(DefaultCacheAccess.java:200)
 at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:172)
 at org.gradle.cache.internal.DefaultCacheAccess.longRunningOperation(DefaultCacheAccess.java:198)
 at org.gradle.cache.internal.DefaultPersistentDirectoryStore.longRunningOperation(DefaultPersistentDirectoryStore.java:111)
 at org.gradle.api.internal.changedetection.DefaultTaskArtifactStateCacheAccess.longRunningOperation(DefaultTaskArtifactStateCacheAccess.java:83)
 at org.gradle.api.internal.changedetection.CacheLockHandlingTaskExecuter.execute(CacheLockHandlingTaskExecuter.java:32)
 at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:55)
 at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:57)
 at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:41)
 at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
 at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:52)
 at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:42)
 at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:243)
 at org.gradle.execution.DefaultTaskGraphExecuter.executeTask(DefaultTaskGraphExecuter.java:192)
 at org.gradle.execution.DefaultTaskGraphExecuter.doExecute(DefaultTaskGraphExecuter.java:177)
 at org.gradle.execution.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:83)
 at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:36)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
 at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
 at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
 at org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter$1.run(TaskCacheLockHandlingBuildExecuter.java:31)
 at org.gradle.cache.internal.DefaultCacheAccess$1.create(DefaultCacheAccess.java:111)
 at org.gradle.cache.internal.DefaultCacheAccess.useCache(DefaultCacheAccess.java:126)
 at org.gradle.cache.internal.DefaultCacheAccess.useCache(DefaultCacheAccess.java:109)
 at org.gradle.cache.internal.DefaultPersistentDirectoryStore.useCache(DefaultPersistentDirectoryStore.java:103)
 at org.gradle.api.internal.changedetection.DefaultTaskArtifactStateCacheAccess.useCache(DefaultTaskArtifactStateCacheAccess.java:79)
 at org.gradle.api.internal.changedetection.TaskCacheLockHandlingBuildExecuter.execute(TaskCacheLockHandlingBuildExecuter.java:29)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
 at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
 at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
 at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
 at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:54)
 at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:152)
 at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:108)
 at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:76)
 at org.gradle.launcher.cli.RunBuildAction.execute(RunBuildAction.java:42)
 at org.gradle.launcher.cli.RunBuildAction.execute(RunBuildAction.java:28)
 at org.gradle.launcher.exec.ExceptionReportingAction.execute(ExceptionReportingAction.java:32)
 at org.gradle.launcher.exec.ExceptionReportingAction.execute(ExceptionReportingAction.java:21)
 at org.gradle.launcher.cli.CommandLineActionFactory$WithLoggingAction.execute(CommandLineActionFactory.java:238)
 at org.gradle.launcher.cli.CommandLineActionFactory$WithLoggingAction.execute(CommandLineActionFactory.java:222)
 at org.gradle.launcher.Main.doAction(Main.java:48)
 at org.gradle.launcher.exec.EntryPoint$1.execute(EntryPoint.java:53)
 at org.gradle.launcher.exec.EntryPoint$1.execute(EntryPoint.java:51)
 at org.gradle.launcher.exec.Execution.execute(Execution.java:28)
 at org.gradle.launcher.exec.EntryPoint.run(EntryPoint.java:39)
 at org.gradle.launcher.Main.main(Main.java:39)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.gradle.launcher.ProcessBootstrap.runNoExit(ProcessBootstrap.java:51)
 at org.gradle.launcher.ProcessBootstrap.run(ProcessBootstrap.java:33)
 at org.gradle.launcher.GradleMain.main(GradleMain.java:24)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.gradle.wrapper.BootstrapMainStarter.start(BootstrapMainStarter.java:33)
 at org.gradle.wrapper.Wrapper.execute(Wrapper.java:51)
 at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:37)

I should note that I also tried changing up [new SAXReader()] to be [new SAXReader( new DocumentFactory() )] thinking that might get around the problem even if I think its NotRight that plugin developers would need to manage this. But I still got errors. For completeness…

Peter, btw you asked for the code/project… Its in one of my github repos: https://github.com/sebersole/gradle-upload-auth-plugin

As you can see in build.gradle, I define compile dep on dom4j (using the @jar syntax so as to not pull in all of JAXP iirc). And the usage of dom4j is very limited and very usual: https://github.com/sebersole/gradle-upload-auth-plugin/blob/master/src/main/java/org/hibernate/build/gradle/upload/StandardMavenAuthenticationProvider.java

This should be fixed now. Can you try the latest nightly: http://gradle.org/nightly

Let us know if you’re seeing other ClassCastExceptions. It would be good to get you migrated to m8.

Adam, I will try and let you know. BTW, do you have a JIRA for whatever this is?

GRADLE-2052

Adam, that seems to do the trick! Thanks. So 2 questions: 1) Should I be able to go back to the non-reflection stuff you helped me with in there for Maven stuff? 2) When can we expect m8?

  1. Should I be able to go back to the non-reflection stuff you helped me with in there for Maven stuff?

Not yet. We’d like to get rid of the need to use the maven classes at all, as part of cleaning up the publication dsl. Is it getting in your way of upgrading?

  1. When can we expect m8?

There will be a snapshot in the next few days (maybe early next week), and ideally, a release a few days after that.

The Maven stuff is not getting in the way. Just figured if I was in there would be a good time to clean.

Btw, I personally think the code in this particular plugin should be part of the Gradle codebase in the Maven plugin. WDYT?

I think you’re right. It would make perfect sense in the maven plugin.

Adam, as a prep for y’all to hopefully pick up this code, what is the “proper” way to add username/password information to a “MavenDeployer”?

Currently I use the following code to limit this to MavenDeployers:

upload.getRepositories().withType( MavenDeployer.class ).all(
        new Action<MavenDeployer>() {
            public void execute(MavenDeployer deployer) {
                final Object repositoryDelegate = deployer.getRepository();
                if ( repositoryDelegate != null ) {
                    // If we found authentication information in settings.xml, add it to the
                     // org.apache.maven.artifact.ant.RemoteRepository delegate object
                     // using that reflection code
                    ...
                }
                final Object snapshotRepositoryDelegate = deployer.getSnapshotRepository();
                ...
            }
        }
);

But for example, I see org.gradle.api.artifacts.repositories.MavenArtifactRepository has non-Maven-class-dependent means to set user/pass information. Should I use that instead? If so, how do I get from an Upload task to a MavenArtifactRepository?

Or is there something else I should be doing completely?

At the moment, the way you’re doing it is the only option (you could use groovy, to get rid of the reflective stuff and simplify things, but the approach would still be the same). ‘MavenArtifactRepository’ is currently only used for resolving dependencies, not publishing, and ‘MavenDeployer’ is not yet connected up to ‘MavenArtifactRepository’.

When we get into cleaning up the publication DSL, ‘MavenDeployer’ will have some association with ‘MavenArtifactRepository’ (or, even better, will be replaced by ‘MavenArtifactRepository’), in which case the reflective code can go away.