diff --git a/TomcatGrailsPlugin.groovy b/TomcatGrailsPlugin.groovy index 5e60d84..08fc3bc 100644 --- a/TomcatGrailsPlugin.groovy +++ b/TomcatGrailsPlugin.groovy @@ -14,11 +14,11 @@ * limitations under the License. */ class TomcatGrailsPlugin { - def version = '8.0.30' - def grailsVersion = '2.3 > *' + def version = '8.5.2' + def grailsVersion = '2.5 > *' def scopes = [excludes: 'war'] def title = 'Apache Tomcat plugin' - def description = 'Makes Tomcat 8.x the servlet container for Grails at development time' + def description = 'Makes Tomcat 8.5.x the servlet container for Grails at development time' def documentation = 'http://grails.org/plugin/tomcat' def license = 'APACHE' def organization = [name: 'Grails', url: 'http://www.grails.org'] @@ -26,6 +26,6 @@ class TomcatGrailsPlugin { [name: 'Graeme Rocher', email: 'rocherg@ociweb.com'], [name: 'Burt Beckwith', email: 'burt@burtbeckwith.com'] ] - def issueManagement = [system: 'JIRA', url: 'http://jira.grails.org/browse/GPTOMCAT'] + def issueManagement = [url: 'https://github.com/grails-plugins/grails-tomcat-plugin/issues'] def scm = [url: 'https://github.com/grails-plugins/grails-tomcat-plugin'] } diff --git a/application.properties b/application.properties index 0723244..8e4494e 100644 --- a/application.properties +++ b/application.properties @@ -1 +1 @@ -app.grails.version=2.5.1 +app.grails.version=2.5.3 diff --git a/grails-app/conf/BuildConfig.groovy b/grails-app/conf/BuildConfig.groovy index 8181754..7b85f8f 100644 --- a/grails-app/conf/BuildConfig.groovy +++ b/grails-app/conf/BuildConfig.groovy @@ -4,28 +4,25 @@ if (System.getenv('TRAVIS_BRANCH')) { } grails.project.work.dir = 'target' -grails.project.dependency.resolver = 'maven' +grails.project.dependency.resolver = 'maven' grails.project.dependency.resolution = { inherits 'global' log 'warn' repositories { - grailsCentral() mavenLocal() + grailsCentral() mavenCentral() } dependencies { - String tomcatVersion = '8.0.30' - + String tomcatVersion = '8.5.2' compile "org.apache.tomcat.embed:tomcat-embed-core:$tomcatVersion" ['el', 'jasper', 'logging-log4j', 'logging-juli', 'websocket'].each { runtime "org.apache.tomcat.embed:tomcat-embed-$it:$tomcatVersion" } - - provided 'javax.servlet:javax.servlet-api:3.1.0' } plugins { diff --git a/grailsw b/grailsw index 99ab2f9..309e8b3 100755 --- a/grailsw +++ b/grailsw @@ -132,7 +132,7 @@ fi if [ -z "$STARTER_CONF" ]; then STARTER_CONF="$GRAILS_HOME/conf/groovy-starter.conf" fi -STARTER_CLASSPATH="wrapper/grails-wrapper-runtime-2.5.1.jar:wrapper:." +STARTER_CLASSPATH="wrapper/grails-wrapper-runtime-2.5.3.jar:wrapper:." # Allow access to Cocoa classes on OS X if $darwin; then @@ -200,7 +200,7 @@ if $mingw ; then fi if [ -z "$GRAILS_AGENT_CACHE_DIR" ]; then - GRAILS_AGENT_CACHE_DIR=~/.grails/2.5.1/ + GRAILS_AGENT_CACHE_DIR=~/.grails/2.5.3/ fi SPRINGLOADED_PARAMS=profile=grails\;cacheDir=$GRAILS_AGENT_CACHE_DIR if [ ! -d "$GRAILS_AGENT_CACHE_DIR" ]; then diff --git a/grailsw.bat b/grailsw.bat index 3e762c0..32f7032 100644 --- a/grailsw.bat +++ b/grailsw.bat @@ -60,7 +60,7 @@ if "%GRAILS_HOME:~-1%"=="\" SET GRAILS_HOME=%GRAILS_HOME:~0,-1% :init for %%x in ("%USERPROFILE%") do set SHORTHOME=%%~fsx -if "x%GRAILS_AGENT_CACHE_DIR%" == "x" set GRAILS_AGENT_CACHE_DIR=%SHORTHOME%/.grails/2.5.1/ +if "x%GRAILS_AGENT_CACHE_DIR%" == "x" set GRAILS_AGENT_CACHE_DIR=%SHORTHOME%/.grails/2.5.3/ set SPRINGLOADED_PARAMS="profile=grails;cacheDir=%GRAILS_AGENT_CACHE_DIR%" if not exist "%GRAILS_AGENT_CACHE_DIR%" mkdir "%GRAILS_AGENT_CACHE_DIR%" @@ -140,7 +140,7 @@ set CMD_LINE_ARGS=%$ :execute @rem Setup the command line -set STARTER_CLASSPATH=wrapper/grails-wrapper-runtime-2.5.1.jar;wrapper;. +set STARTER_CLASSPATH=wrapper/grails-wrapper-runtime-2.5.3.jar;wrapper;. if exist "%USERPROFILE%/.groovy/init.bat" call "%USERPROFILE%/.groovy/init.bat" @@ -169,7 +169,7 @@ set JAVA_OPTS=%GRAILS_OPTS% %JAVA_OPTS% %AGENT% set JAVA_OPTS=%JAVA_OPTS% -Dprogram.name="%PROGNAME%" set JAVA_OPTS=%JAVA_OPTS% -Dgrails.home="%GRAILS_HOME%" -set JAVA_OPTS=%JAVA_OPTS% -Dgrails.version=2.5.1 +set JAVA_OPTS=%JAVA_OPTS% -Dgrails.version=2.5.3 set JAVA_OPTS=%JAVA_OPTS% -Dbase.dir=. set JAVA_OPTS=%JAVA_OPTS% -Dtools.jar="%TOOLS_JAR%" set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%" diff --git a/src/groovy/org/grails/plugins/tomcat/InlineExplodedTomcatServer.groovy b/src/groovy/org/grails/plugins/tomcat/InlineExplodedTomcatServer.groovy index 52cf73f..e85bf84 100644 --- a/src/groovy/org/grails/plugins/tomcat/InlineExplodedTomcatServer.groovy +++ b/src/groovy/org/grails/plugins/tomcat/InlineExplodedTomcatServer.groovy @@ -25,10 +25,9 @@ import groovy.transform.TypeCheckingMode import org.apache.catalina.Context import org.apache.catalina.Loader import org.apache.catalina.WebResourceRoot -import org.apache.catalina.connector.Connector import org.apache.catalina.startup.Tomcat import org.apache.catalina.webresources.StandardRoot -import org.apache.coyote.http11.Http11NioProtocol +import org.apache.tomcat.util.descriptor.web.ContextResource import org.codehaus.groovy.grails.plugins.GrailsPluginUtils import org.grails.plugins.tomcat.fork.ForkedTomcatServer @@ -38,20 +37,13 @@ import org.grails.plugins.tomcat.fork.ForkedTomcatServer @CompileStatic class InlineExplodedTomcatServer extends TomcatServer { - final Tomcat tomcat = new Tomcat() - - Context context - InlineExplodedTomcatServer(String basedir, String webXml, String contextPath, ClassLoader classLoader) { if (contextPath == '/') { contextPath = '' } - tomcat.baseDir = tomcatDir.absolutePath context = tomcat.addWebapp(contextPath, basedir) - configureJarScanner context - tomcat.enableNaming() // we handle reloading manually context.reloadable = false @@ -106,25 +98,7 @@ class InlineExplodedTomcatServer extends TomcatServer { void doStart(String host, int httpPort, int httpsPort) { preStart() - if (host != "localhost") { - tomcat.connector.setAttribute "address", host - tomcat.connector.setAttribute "port", httpPort - } - - if (getConfigParam("nio")) { - CONSOLE.updateStatus "Enabling Tomcat NIO connector" - def connector = new Connector(Http11NioProtocol.name) - connector.port = httpPort - tomcat.service.addConnector connector - tomcat.connector = connector - } - - tomcat.port = httpPort - tomcat.connector.URIEncoding = 'UTF-8' - - if (httpsPort) { - configureSsl host, httpsPort - } + super.doStart host, httpPort, httpsPort if (Environment.isFork()) { ForkedTomcatServer.startKillSwitch tomcat, httpPort @@ -133,41 +107,12 @@ class InlineExplodedTomcatServer extends TomcatServer { tomcat.start() } - @CompileStatic(TypeCheckingMode.SKIP) - protected void configureSsl(String host, int httpsPort) { - def sslConnector = loadInstance('org.apache.catalina.connector.Connector') - sslConnector.scheme = "https" - sslConnector.secure = true - sslConnector.port = httpsPort - sslConnector.setProperty "SSLEnabled", "true" - sslConnector.setAttribute "keystoreFile", keystoreFile.absolutePath - sslConnector.setAttribute "keystorePass", keyPassword - sslConnector.URIEncoding = 'UTF-8' - - if (host != "localhost") { - sslConnector.setAttribute "address", host - } - - if (truststoreFile.exists()) { - CONSOLE.addStatus "Using truststore $truststore" - sslConnector.setAttribute "truststoreFile", truststore - sslConnector.setAttribute "truststorePass", trustPassword - sslConnector.setAttribute "clientAuth", getConfigParam("clientAuth") ?: "want" - } - - tomcat.service.addConnector sslConnector - } - void stop() { tomcat.stop() tomcat.destroy() GrailsPluginUtils.clearCaches() } - protected loadInstance(String name) { - tomcat.getClass().classLoader.loadClass(name).newInstance() - } - @CompileStatic(TypeCheckingMode.SKIP) protected void preStart() { eventListener?.triggerEvent "ConfigureTomcat", tomcat @@ -184,16 +129,16 @@ class InlineExplodedTomcatServer extends TomcatServer { return } - if (!resCfg["type"]) { + if (!resCfg.type) { throw new IllegalArgumentException("Must supply a resource type for JNDI configuration") } - def res = loadInstance('org.apache.catalina.deploy.ContextResource') - res.name = name - res.type = resCfg.remove("type") - res.auth = resCfg.remove("auth") - res.description = resCfg.remove("description") - res.scope = resCfg.remove("scope") + def res = new ContextResource( + auth: resCfg.remove('auth'), + description: resCfg.remove("description"), + name: name, + scope: resCfg.remove('scope'), + type: resCfg.remove('type')) // now it's only the custom properties left in the Map... resCfg.each { key, value -> res.setProperty key, value } diff --git a/src/groovy/org/grails/plugins/tomcat/TomcatLoader.groovy b/src/groovy/org/grails/plugins/tomcat/TomcatLoader.groovy index 6d2da97..2e901dc 100644 --- a/src/groovy/org/grails/plugins/tomcat/TomcatLoader.groovy +++ b/src/groovy/org/grails/plugins/tomcat/TomcatLoader.groovy @@ -28,7 +28,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory /** - * A loader instance used for the embedded version of Tomcat 8. + * A loader instance used for the embedded version of Tomcat. * * @author Graeme Rocher * @since 2.0 diff --git a/src/groovy/org/grails/plugins/tomcat/TomcatServer.groovy b/src/groovy/org/grails/plugins/tomcat/TomcatServer.groovy index c795702..50af93b 100644 --- a/src/groovy/org/grails/plugins/tomcat/TomcatServer.groovy +++ b/src/groovy/org/grails/plugins/tomcat/TomcatServer.groovy @@ -24,6 +24,10 @@ import groovy.transform.CompileStatic import groovy.transform.TypeCheckingMode import org.apache.catalina.Context +import org.apache.catalina.connector.Connector +import org.apache.catalina.core.AprLifecycleListener +import org.apache.catalina.startup.Tomcat +import org.apache.coyote.http11.Http11NioProtocol import org.apache.tomcat.util.scan.StandardJarScanner import org.codehaus.groovy.grails.cli.support.GrailsBuildEventListener import org.codehaus.groovy.grails.plugins.GrailsPluginUtils @@ -47,15 +51,18 @@ abstract class TomcatServer implements EmbeddableServer { protected final File workDir protected final File tomcatDir - protected final boolean usingUserKeystore - protected final File keystoreFile - protected final String keyPassword + protected boolean usingUserKeystore + protected File keystoreFile + protected String keyPassword protected String truststore protected File truststoreFile protected String trustPassword protected Boolean shouldScan = false protected Set extraJarsToSkip + Context context + final Tomcat tomcat = new Tomcat() + // These are set from the outside in _GrailsRun def grailsConfig GrailsBuildEventListener eventListener @@ -65,48 +72,111 @@ abstract class TomcatServer implements EmbeddableServer { pluginSettings = GrailsPluginUtils.getPluginBuildSettings() workDir = buildSettings.projectWorkDir - tomcatDir = getWorkDirFile("tomcat") + tomcatDir = getWorkDirFile('tomcat') + tomcat.baseDir = tomcatDir.absolutePath + + initKeystore() + + System.setProperty 'org.mortbay.xml.XmlParser.NotValidating', 'true' + + Map scanConfig = (Map)getConfigParam('scan') + if (scanConfig) { + shouldScan = (Boolean) (scanConfig.enabled instanceof Boolean ? scanConfig.enabled : false) + extraJarsToSkip = (Set)((scanConfig.excludes instanceof Collection) ? scanConfig.excludes.collect { it.toString() } : []) + } + + tomcatDir.deleteDir() + new File(tomcatDir, 'webapps').mkdirs() + + initListeners() + } - def userKeystore = getConfigParam("keystorePath") + protected void initKeystore() { + def userKeystore = getConfigParam('keystorePath') if (userKeystore) { usingUserKeystore = true keystoreFile = new File(userKeystore.toString()) - keyPassword = getConfigParam("keystorePassword") ?: "changeit" // changeit is the keystore default + keyPassword = getConfigParam('keystorePassword') ?: 'changeit' // changeit is the keystore default } else { usingUserKeystore = false - keystoreFile = getWorkDirFile("ssl/keystore") - keyPassword = "123456" + keystoreFile = getWorkDirFile('ssl/keystore') + keyPassword = '123456' } - def userTruststore = getConfigParam("truststorePath") - if (userKeystore) { + def userTruststore = getConfigParam('truststorePath') + if (userTruststore) { truststore = userTruststore - trustPassword = getConfigParam("truststorePassword") ?: "changeit" + trustPassword = getConfigParam('truststorePassword') ?: 'changeit' } else { truststore = "${buildSettings.grailsWorkDir}/ssl/truststore" - trustPassword = "123456" + trustPassword = '123456' } truststoreFile = new File(truststore) + } - System.setProperty 'org.mortbay.xml.XmlParser.NotValidating', 'true' + protected void initListeners() { + tomcat.server.addLifecycleListener new AprLifecycleListener(SSLEngine: 'on', useAprConnector: true) + } - Map scanConfig = (Map)getConfigParam("scan") - if (scanConfig) { - shouldScan = (Boolean) (scanConfig.enabled instanceof Boolean ? scanConfig.enabled : false) - extraJarsToSkip = (Set)((scanConfig.excludes instanceof Collection) ? scanConfig.excludes.collect { it.toString() } : []) + @CompileStatic(TypeCheckingMode.SKIP) + protected void configureSsl(String host, int httpsPort) { + def sslConnector + try { + sslConnector = loadInstance('org.apache.catalina.connector.Connector') + } + catch (Exception e) { + throw new RuntimeException("Couldn't create HTTPS connector", e) } - tomcatDir.deleteDir() - new File(tomcatDir, 'webapps').mkdirs() + sslConnector.scheme = 'https' + sslConnector.secure = true + sslConnector.port = httpsPort + sslConnector.setProperty 'SSLEnabled', 'true' + sslConnector.URIEncoding = 'UTF-8' + + if (host != 'localhost') { + sslConnector.setAttribute 'address', host + } + + def certificateKeyFile = getConfigParam('certificateKeyFile') ?: '' + def certificateFile = getConfigParam('certificateFile') ?: '' + if (certificateKeyFile && certificateFile) { + sslConnector.setAttribute 'SSLHonorCipherOrder', false + sslConnector.setAttribute 'SSLCertificateKeyFile', certificateKeyFile + sslConnector.setAttribute 'SSLCertificateFile', certificateFile + def certificateKeyPassword = getConfigParam('certificateKeyPassword') ?: '' + if (certificateKeyPassword) { + sslConnector.setAttribute 'SSLPassword', certificateKeyPassword + } + } + else { + sslConnector.setAttribute 'keystoreFile', keystoreFile.absolutePath + sslConnector.setAttribute 'keystorePass', keyPassword + + if (truststoreFile.exists()) { + CONSOLE.addStatus "Using truststore $truststore" + sslConnector.setAttribute 'truststoreFile', truststore + sslConnector.setAttribute 'truststorePass', trustPassword + sslConnector.setAttribute 'clientAuth', getConfigParam('clientAuth') ?: 'want' + } + } + + sslConnector.addUpgradeProtocol loadInstance('org.apache.coyote.http2.Http2Protocol') + + tomcat.service.addConnector sslConnector + } + + protected loadInstance(String name) { + tomcat.getClass().classLoader.loadClass(name).newInstance() } protected void configureJarScanner(Context context) { if (extraJarsToSkip && shouldScan) { try { - def jarsToSkipField = ReflectionUtils.findField(StandardJarScanner, "defaultJarsToSkip", Set) + def jarsToSkipField = ReflectionUtils.findField(StandardJarScanner, 'defaultJarsToSkip', Set) ReflectionUtils.makeAccessible jarsToSkipField Set jarsToSkip = (Set)jarsToSkipField.get(StandardJarScanner) jarsToSkip.addAll extraJarsToSkip @@ -122,7 +192,41 @@ abstract class TomcatServer implements EmbeddableServer { * * If httpsPort is > 0, the server should listen for https requests on that port. */ - protected abstract void doStart(String host, int httpPort, int httpsPort) + protected void doStart(String host, int httpPort, int httpsPort) { + tomcat.port = httpPort + + if (getConfigParam("nio")) { + CONSOLE.updateStatus "Enabling Tomcat NIO Connector" + def connector = new Connector(Http11NioProtocol.name) + connector.port = httpPort + tomcat.service.addConnector connector + tomcat.connector = connector + } + + try { + configureJarScanner context + } + catch (Throwable e) { + CONSOLE.error "Error loading Tomcat: $e.message", e + System.exit 1 + } + + tomcat.enableNaming() + + final Connector connector = tomcat.connector + + // Only bind to host name if we aren't using the default + if (host != "localhost") { + connector.setAttribute "address", host + connector.setAttribute "port", httpPort + } + + connector.URIEncoding = "UTF-8" + + if (httpsPort) { + configureSsl host, httpsPort + } + } /** * Shutdown the server. @@ -192,15 +296,15 @@ abstract class TomcatServer implements EmbeddableServer { } getKeyToolClass().main( - "-genkey", - "-alias", "localhost", - "-dname", "CN=localhost,OU=Test,O=Test,C=US", - "-keyalg", "RSA", - "-validity", "365", - "-storepass", "key", - "-keystore", keystoreFile.absolutePath, - "-storepass", keyPassword, - "-keypass", keyPassword) + '-genkey', + '-alias', 'tomcat', + '-dname', 'CN=localhost,OU=Test,O=Test,C=US', + '-keyalg', 'RSA', + '-validity', '365', + '-storepass', 'key', + '-keystore', keystoreFile.absolutePath, + '-storepass', keyPassword, + '-keypass', keyPassword) println 'Created SSL Certificate.' } diff --git a/src/groovy/org/grails/plugins/tomcat/fork/TomcatWarRunner.groovy b/src/groovy/org/grails/plugins/tomcat/fork/TomcatWarRunner.groovy index c80a777..4a5d48b 100644 --- a/src/groovy/org/grails/plugins/tomcat/fork/TomcatWarRunner.groovy +++ b/src/groovy/org/grails/plugins/tomcat/fork/TomcatWarRunner.groovy @@ -21,9 +21,7 @@ import groovy.transform.CompileStatic import org.apache.catalina.Context import org.apache.catalina.LifecycleException -import org.apache.catalina.connector.Connector import org.apache.catalina.startup.Tomcat -import org.apache.coyote.http11.Http11NioProtocol import org.grails.plugins.tomcat.TomcatServer /** @@ -37,7 +35,6 @@ class TomcatWarRunner extends TomcatServer { protected static final GrailsConsole CONSOLE = GrailsConsole.getInstance() - protected Tomcat tomcat = new Tomcat() protected String warPath protected String contextPath @@ -46,68 +43,21 @@ class TomcatWarRunner extends TomcatServer { this.contextPath = contextPath } - protected void enableSslConnector(String host, int httpsPort) { - Connector sslConnector - try { - sslConnector = new Connector() - } - catch (Exception e) { - throw new RuntimeException("Couldn't create HTTPS connector", e) - } - - sslConnector.scheme = "https" - sslConnector.secure = true - sslConnector.port = httpsPort - sslConnector.setProperty "SSLEnabled", "true" - sslConnector.setAttribute "keystoreFile", keystoreFile - sslConnector.setAttribute "keystorePass", keyPassword - sslConnector.URIEncoding = "UTF-8" - - if (!host.equals("localhost")) { - sslConnector.setAttribute "address", host - } - - tomcat.service.addConnector sslConnector - } - @Override protected void doStart(String host, int httpPort, int httpsPort) { Metadata.getCurrent()[Metadata.WAR_DEPLOYED] = "true" - tomcat.port = httpPort tomcat.silent = true - if (getConfigParam("nio")) { - CONSOLE.updateStatus "Enabling Tomcat NIO Connector" - def connector = new Connector(Http11NioProtocol.name) - connector.port = httpPort - tomcat.service.addConnector connector - tomcat.connector = connector - } - - tomcat.baseDir = tomcatDir try { - configureJarScanner tomcat.addWebapp(contextPath, warPath) + context = tomcat.addWebapp(contextPath, warPath) } catch (Throwable e) { CONSOLE.error "Error loading Tomcat: $e.message", e System.exit 1 } - tomcat.enableNaming() - - final Connector connector = tomcat.connector - - // Only bind to host name if we aren't using the default - if (!host.equals("localhost")) { - connector.setAttribute "address", host - } - - connector.URIEncoding = "UTF-8" - - if (httpsPort) { - enableSslConnector host, httpsPort - } + super.doStart host, httpPort, httpsPort ForkedTomcatServer.startKillSwitch tomcat, httpPort diff --git a/wrapper/grails-wrapper-runtime-2.5.1.jar b/wrapper/grails-wrapper-runtime-2.5.1.jar deleted file mode 100644 index 32f84e5..0000000 Binary files a/wrapper/grails-wrapper-runtime-2.5.1.jar and /dev/null differ diff --git a/wrapper/grails-wrapper-runtime-2.5.3.jar b/wrapper/grails-wrapper-runtime-2.5.3.jar new file mode 100644 index 0000000..e17a047 Binary files /dev/null and b/wrapper/grails-wrapper-runtime-2.5.3.jar differ diff --git a/wrapper/grails-wrapper.properties b/wrapper/grails-wrapper.properties index 74391ce..0185c46 100644 --- a/wrapper/grails-wrapper.properties +++ b/wrapper/grails-wrapper.properties @@ -1 +1 @@ -wrapper.dist.url=http://dist.springframework.org.s3.amazonaws.com/release/GRAILS/ +wrapper.dist.url=https://github.com/grails/grails-core/releases/download/v2.5.3/