A few days ago, I needed to launch Groovlets on a web server. I have decided to go with the latest Jetty for this task, developed by the Eclipse Foundation. It turned out to be a non-trivial task, but in the end, it all worked, so I decided to share my experience with you.
The straightforward way to start a recent stable Jetty version would be:
import org.eclipse.jetty.server.Server
@Grab('org.eclipse.jetty:jetty-server:8.1.8.v20121106')
def startJetty() {
def jetty = new Server(9090)
jetty.start()
}
println "Starting Jetty, press Ctrl+C to stop."
startJetty()After running this, we will get the following result:
java.lang.RuntimeException:
Error grabbing Grapes -- [download failed: org.eclipse.jetty.orbit#javax.servlet;3.0.0.v201112011016!javax.servlet.orbit]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
...
at groovy.grape.GrapeIvy.getDependencies(GrapeIvy.groovy:411)
...There is a well-known problem with this Servlet 3.0 implementation by Eclipse. To overcome it, we might try using a well-known solution, e.g., specify that the packaging type of this artifact is jar rather than orbit, as specified in the pom file. To do this, we need to disable transitive dependency loading of jetty-server and to download all of its dependencies manually:
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.*
import groovy.servlet.*
@Grapes([
@Grab(group='org.eclipse.jetty.orbit', module='javax.servlet', version='3.0.0.v201112011016', ext='jar'),
@Grab(group='org.eclipse.jetty', module='jetty-server', version='8.1.8.v20121106', transitive=false),
@Grab(group='org.eclipse.jetty', module='jetty-continuation', version='8.1.8.v20121106'),
@Grab(group='org.eclipse.jetty', module='jetty-http', version='8.1.8.v20121106'),
@Grab(group='org.eclipse.jetty', module='jetty-servlet', version='8.1.8.v20121106', transitive=false),
@Grab(group='org.eclipse.jetty', module='jetty-security', version='8.1.8.v20121106', transitive=false)
])
...This solution will work if you run it from the command line, but if you run it from IntelliJ IDEA, you might face the following issue:
java.lang.SecurityException:
class "javax.servlet.ServletRegistration$Dynamic"'s signer information does not match signer information of other classes in the same package
at java.lang.ClassLoader.checkCerts(ClassLoader.java:806)
...
at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:108)One solution to defeat this problem is to use the stock Servlet 3.0 implementation provided by Oracle (in this case, the Grab list can be simplified, because we can use GrabExclude and transitive dependency resolution):
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.*
import groovy.servlet.*
@Grapes([
@Grab('org.eclipse.jetty:jetty-server:8.1.8.v20121106'),
@Grab('org.eclipse.jetty:jetty-servlet:8.1.8.v20121106'),
@Grab('javax.servlet:javax.servlet-api:3.0.1'),
@GrabExclude('org.eclipse.jetty.orbit:javax.servlet')
])
def startJetty() {
def jetty = new Server(9090)
def context = new ServletContextHandler(jetty, '/', ServletContextHandler.SESSIONS) // Allow sessions.
context.resourceBase = '.' // Look in current dir for Groovy scripts.
context.addServlet(GroovyServlet, '*.groovy') // All files ending with .groovy will be served.
context.setAttribute('version', '1.0') // Set an context attribute.
jetty.start()
}
println "Starting Jetty, press Ctrl+C to stop."
startJetty()This solution works fine in IntelliJ IDEA. You can create a sample Groovlet and place it in the same folder as the script above to test this server:
println """
<html>
<head>
<title>Example Groovy Servlet</title>
</head>
<body>
Hello from Groovy, ${request.remoteHost}: ${new Date()}
</body>
</html>
"""Now, if you open https://localhost:9090/example.groovy you should see the output of this Groovlet. You don’t need to relaunch the script when you edit the code of Groovlet, which is very nice.
To cut down the number of manual dependencies, one can also use aggregate Jetty jars:
@Grapes([
@Grab(group='javax.servlet', module='javax.servlet-api', version='3.0.1'),
@Grab(group='org.eclipse.jetty.aggregate', module='jetty-all-server', version='8.1.8.v20121106', transitive=false)
])
