Programmatically configuring Jetty Plugin causes ClassCastException

In gradle-1.0-milestone-3 I use to be able to configure the Jetty connectors programatically using the following build.gradle

apply plugin: 'jetty'
  [jettyRun, jettyRunWar]*.configure {
    def httpsConnector = new org.mortbay.jetty.security.SslSocketConnector()
    connectors = [httpsConnector]
}

Now I’m trying to do something similar in gradle-1.0-milestone-8. Initially I had problems due to the fact that milestone 4 had breaking classpath changes. I got past this by updating my build as shown below:

apply plugin: 'jetty'
  buildscript {
   repositories {
       mavenCentral()
   }
   dependencies {
        classpath 'org.mortbay.jetty:jetty:6.1.25'
   }
}
[jettyRun, jettyRunWar]*.configure {
    def httpsConnector = new org.mortbay.jetty.security.SslSocketConnector()
    connectors = [httpsConnector]
}

As you might have guessed, running a gradle build on the above build.groovy causes the ClassCastException displayed below. I believe this is since the two objects are loaded by different classloaders.

$ gradle -v
  ------------------------------------------------------------
Gradle 1.0-milestone-8
------------------------------------------------------------
  Gradle build time: Monday, February 13, 2012 11:53:32 PM UTC
Groovy: 1.8.4
Ant: Apache Ant(TM) version 1.8.2 compiled on December 20 2010
Ivy: 2.2.0
JVM: 1.6.0_30 (Sun Microsystems Inc. 20.5-b03)
OS: Linux 3.0.0-16-generic-pae i386
  $ gradle --stacktrace
  FAILURE: Build failed with an exception.
  * Where:
Build file '/home/rwinch/tmp/gradlejetty/build.gradle' line: 15
  * What went wrong:
A problem occurred evaluating root project 'gradlejetty'.
> Cannot cast object 'SslSocketConnector@0.0.0.0:0' with class 'org.mortbay.jetty.security.SslSocketConnector' to class 'org.mortbay.jetty.Connector'
  * Try:
Run with --info or --debug option to get more log output.
  * Exception is:
org.gradle.api.GradleScriptException: A problem occurred evaluating root project 'gradlejetty'.
 at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:54)
 at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:127)
 at org.gradle.configuration.BuildScriptProcessor.evaluate(BuildScriptProcessor.java:38)
 at org.gradle.configuration.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:43)
 at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:478)
 at org.gradle.api.internal.project.AbstractProject.evaluate(AbstractProject.java:75)
 at org.gradle.configuration.ProjectEvaluationConfigurer.execute(ProjectEvaluationConfigurer.java:23)
 at org.gradle.configuration.ProjectEvaluationConfigurer.execute(ProjectEvaluationConfigurer.java:21)
 at org.gradle.configuration.DefaultBuildConfigurer$1.execute(DefaultBuildConfigurer.java:38)
 at org.gradle.configuration.DefaultBuildConfigurer$1.execute(DefaultBuildConfigurer.java:35)
 at org.gradle.api.internal.project.AbstractProject.configure(AbstractProject.java:454)
 at org.gradle.api.internal.project.AbstractProject.allprojects(AbstractProject.java:449)
 at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:35)
 at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:139)
 at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:110)
 at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:78)
 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 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)
Caused by: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'SslSocketConnector@0.0.0.0:0' with class 'org.mortbay.jetty.security.SslSocketConnector' to class 'org.mortbay.jetty.Connector'
 at org.gradle.api.internal.BeanDynamicObject$MetaClassAdapter.setProperty(BeanDynamicObject.java:158)
 at org.gradle.api.internal.BeanDynamicObject.setProperty(BeanDynamicObject.java:87)
 at org.gradle.api.internal.CompositeDynamicObject.setProperty(CompositeDynamicObject.java:61)
 at org.gradle.api.plugins.jetty.JettyRun_Decorated.setProperty(Unknown Source)
 at build_573uimf509544bc987ptkhtenr$_run_closure1.doCall(/home/rwinch/tmp/gradlejetty/build.gradle:15)
 at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:136)
 at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:104)
 at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:410)
 at org.gradle.api.internal.AbstractTask.configure(AbstractTask.java:60)
 at build_573uimf509544bc987ptkhtenr.run(/home/rwinch/tmp/gradlejetty/build.gradle:12)
 at org.gradle.groovy.scripts.internal.DefaultScriptRunnerFactory$ScriptRunnerImpl.run(DefaultScriptRunnerFactory.java:52)
 ... 30 more

I would prefer to programtically configure it since I want to dynamically allocate my ports for a CI build and doing so in configuration requires more hoops (filtering) to jump through. Is there a simple way to configure HTTPS for the Jetty Plugin programatically, or do I need to resort to using the XML configuration?

Note that I have not yet had the opportunity to try with m9, but plan to. It seems the download is going to take over 2 hours despite having fast connection to other sites. In short, I’m not certain yet if this issue is present in M9 and If someone has suggestions for getting this to work in M9 I would be happy to hear that as well.

PS: The closest issue on the forums and in JIRA I could find was GRADLE-1960

Thanks in advance, Rob

For those interested, I was able to produce a very similar error with M9.

I was also able to reproduce this issue on M9. Any ideas on how to configure SSL other than programatically.

I kind of figured out how to set it it up programatically :

casServer.configure {
    contextPath = "/cas"
          jettyConfig = file("src/test/resources/jetty-web.xml")
         webApp = casServerOverlay.customWar
    inputs.file casServerOverlay.explodedWar
      doFirst() {
        System.setProperty('javax.net.ssl.trustStore', keystore)
        System.setProperty('javax.net.ssl.trustStorePassword', password)
    }
}

Hi Rob,

First off, thanks for the great report.

This is somewhat different to GRADLE-1960 so I’ve raised GRADLE-2235 to track this. The classloader changes that happened in m5 (I think) explain this behaviour, but I’m unsure what the fix will be at this point.

Perhaps if you can give some context on why you need to configure the connectors we might be able to find an interim workaround.

My apologies, I meant using a configuration file.

Luke,

Thanks for the reply and raising the new issue. I’m not sure if your mention of m5 was a typo or not, but to be certain the issue I mention is first found in milestone 4 (the release with the breaking classpath changes).

I’d be glad to provide a context of what I am trying to do. I am trying to implement the following requirements using the latest Gradle release for the Spring Security build:

  • I need to run Jetty using HTTPS * I need to dynamically allocate the ports that Jetty uses only during a CI build. This is to prevent conflicts with other projects. * The ports should be static if not ran as part of integration tests to allow it to be easily used by users locally

Note: These requirements have been implemented using Gradle M3 and can be seen in the Spring Security CAS integration tests.

As I alluded to in my original post (Amit also mentions this solution), one option is to use the Jetty XML configuration. I really prefer programmatic configuration to using XML because I feel it is simpler than setting up a number of dependent tasks (i.e. find new port and set system property, filter xml, start jetty, etc). This obviously is a preference, but I really find it nice to just write groovy code (one of Gradles strengths).

An option that does not configure Jetty through XML (what I am looking for) would be to use the ClassLoader of the Jetty Plugin to create the instance of the connector. The other advantage to this solution is that I do not need to include Jetty in my buildscript either. The disadvantage is I doubt this is officially supported. Initially I wasn’t sure how to do this, but after taking a break from the problem it is rather intuitive. The workaround is shown below:

apply plugin: 'jetty'
  [jettyRun, jettyRunWar]*.configure {
    def httpsConnector = jettyRun.class.classLoader.loadClass('org.mortbay.jetty.security.SslSocketConnector').newInstance()
    connectors = [httpsConnector]
}

I think it would be nice if the solution would use the plugin’s ClassLoader to invoke the closures for a particular plugin. I’m not all that up to date on the internals of Gradle, so I’m not sure how good this suggestion is. Perhaps you and/or some of the other Gradle developers could say if this is a reasonable approach to resolving this issue. Thoughts?

Cheers, Rob

Is there a way I can set the JVM parameters programatically? or even with jetty config xml?