2 * Copyright 2004-2005 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org
.codehaus
.groovy
.grails
.cli
;
19 import org
.codehaus
.groovy
.grails
.commons
.GrailsClassUtils as GCU
22 import grails
.util
.GrailsUtil
23 import org
.springframework
.core
.io
.support
.PathMatchingResourcePatternResolver
24 import org
.codehaus
.groovy
.grails
.commons
.GrailsClassUtils
25 import org
.codehaus
.groovy
.grails
.commons
.GrailsApplication
28 * Class that handles Grails command line interface for running scripts
30 * @author Graeme Rocher
35 class GrailsScriptRunner
{
36 static final ANT
= new AntBuilder()
37 static final RESOLVER
= new PathMatchingResourcePatternResolver()
41 static userHome
= System
.properties
.'user.home'
42 static version
= GrailsUtil
.getGrailsVersion()
46 static main(String
[] args
) {
47 MetaClassRegistry registry
= GroovySystem
.metaClassRegistry
49 if(!(registry
.getMetaClassCreationHandler() instanceof ExpandoMetaClassCreationHandle
))
50 registry
.setMetaClassCreationHandle(new ExpandoMetaClassCreationHandle());
52 ANT
.property(environment
:"env")
53 grailsHome
= ANT
.antProject
.properties
.'env.GRAILS_HOME'
56 println
"Environment variable GRAILS_HOME not set. Please set it to the location of your Grails installation and try again."
60 ANT
.property(file
:"${grailsHome}/build.properties")
61 def grailsVersion
= ANT
.antProject
.properties
.'grails.version'
64 Welcome to Grails ${grailsVersion} - http://grails.org/
65 Licensed under Apache Standard License 2.0
66 Grails home is set to: ${grailsHome}
68 if(args
.size() && args
[0].trim()) {
71 baseDir
= establishBaseDir()
72 println
"Base Directory: ${baseDir.absolutePath}"
73 System
.setProperty("base.dir", baseDir
.absolutePath
)
75 rootLoader
= getClass().classLoader ?
getClass().classLoader
.rootLoader
: Thread
.currentThread().getContextClassLoader().rootLoader
76 def baseName
= new File(baseDir
.absolutePath
).name
77 classesDir
= new File("${userHome}/.grails/${grailsVersion}/projects/${baseName}/classes")
79 def allArgs
= args
[0].trim()
81 def scriptName
= processArgumentsAndReturnScriptName(allArgs
)
85 def scriptsAllowedOutsideProject
= ['CreateApp','CreatePlugin','PackagePlugin','Help','ListPlugins','PluginInfo','SetProxy']
86 if(!new File(baseDir
.absolutePath
, "grails-app").exists() && (!scriptsAllowedOutsideProject
.contains(scriptName
))) {
87 println
"${baseDir.absolutePath} does not appear to be part of a Grails application."
88 println
'The following commands are supported outside of a project:'
89 scriptsAllowedOutsideProject
.sort().each
{
90 println
"\t${GCU.getScriptName(it)}"
92 println
"Run 'grails help' for a complete list of available scripts."
100 if(scriptName
.equalsIgnoreCase('interactive')) {
102 System
.metaClass
.static.exit
= { int code
->}
103 System
.setProperty("grails.interactive.mode", "true")
104 int messageNumber
= 0
106 println
"--------------------------------------------------------"
107 ANT
.input(message
:"Interactive mode ready, type your command name in to continue (hit ENTER to run the last command):", addproperty
:"grails.script.name${messageNumber}")
109 def enteredName
= ANT
.antProject
.properties
."grails.script.name${messageNumber++}"
111 scriptName
= processArgumentsAndReturnScriptName(enteredName
)
113 def now
= System
.currentTimeMillis()
114 callPluginOrGrailsScript(scriptName
)
115 def end
= System
.currentTimeMillis()
116 println
"--------------------------------------------------------"
117 println
"Command [$scriptName] completed in ${end-now}ms"
121 System
.exit(callPluginOrGrailsScript(scriptName
))
127 println
"Error executing script ${scriptName}: ${t.message}"
128 t
.printStackTrace(System
.out
)
134 println
"No script name specified. Use 'grails help' for more info or 'grails interactive' to enter interactive mode"
139 static processArgumentsAndReturnScriptName(allArgs
) {
140 allArgs
= processSystemArguments(allArgs
).trim().split(" ")
141 def currentParamIndex
= 0
142 if( isEnvironmentArgs(allArgs
[currentParamIndex
]) ) {
143 // use first argument as environment name and step further
144 calculateEnvironment(allArgs
[currentParamIndex
++])
146 // first argument is a script name so check for default environment
147 setDefaultEnvironment(allArgs
[currentParamIndex
])
150 if( currentParamIndex
>= allArgs
.size() ) {
151 println
"You should specify a script to run. Run 'grails help' for a complete list of available scripts."
155 // use current argument as script name and step further
156 def paramName
= allArgs
[currentParamIndex
++]
157 if (paramName
[0] == '-') {
158 paramName
= paramName
[1..-1]
160 System
.setProperty("current.gant.script", paramName
)
161 def scriptName
= GCU
.getNameFromScript(paramName
)
163 if( currentParamIndex
< allArgs
.size() ) {
164 // if we have additional params provided - store it in system property
165 System
.setProperty("grails.cli.args", allArgs
[currentParamIndex
..-1].join("\n"))
170 static ENV_ARGS
= [dev
:GrailsApplication
.ENV_DEVELOPMENT
,prod
:GrailsApplication
.ENV_PRODUCTION
,test
:GrailsApplication
.ENV_TEST
]
171 // this map contains default environments for several scripts in form 'script-name':'env-code'
172 static DEFAULT_ENVS
= ['war': GrailsApplication
.ENV_PRODUCTION
,'test-app':GrailsApplication
.ENV_TEST
,'run-webtest':GrailsApplication
.ENV_TEST
]
173 private static isEnvironmentArgs(env
) {
174 ENV_ARGS
.keySet().contains(env
)
176 private static setDefaultEnvironment(args
) {
177 if(!System
.properties
."${GrailsApplication.ENVIRONMENT}") {
178 def environment
= DEFAULT_ENVS
[args
.toLowerCase()]
179 environment
= environment ?
: GrailsApplication
.ENV_DEVELOPMENT
180 System
.setProperty(GrailsApplication
.ENVIRONMENT
, environment
)
181 System
.setProperty(GrailsApplication
.ENVIRONMENT_DEFAULT
, "true")
184 private static calculateEnvironment(env
) {
185 def environment
= ENV_ARGS
[env
]
187 System
.setProperty(GrailsApplication
.ENVIRONMENT
, environment
)
189 setDefaultEnvironment("prod")
193 static SCRIPT_CACHE
= [:]
194 static callPluginOrGrailsScript(scriptName
) {
197 if(SCRIPT_CACHE
[scriptName
]) {
198 def cachedScript
= SCRIPT_CACHE
[scriptName
]
199 potentialScripts
= cachedScript
.potentialScripts
200 binding
= cachedScript
.binding
203 potentialScripts
= []
204 def userHome
= ANT
.antProject
.properties
."user.home"
206 def scriptLocations
= ["${baseDir.absolutePath}/scripts", "${grailsHome}/scripts", "${userHome}/.grails/scripts"]
207 scriptLocations
.each
{
208 def scriptFile
= new File("${it}/${scriptName}.groovy")
209 if(scriptFile
.exists()) {
210 potentialScripts
<< scriptFile
215 def pluginScripts
= RESOLVER
.getResources("file:${baseDir.absolutePath}/plugins/*/scripts/${scriptName}.groovy")
216 potentialScripts
+= pluginScripts
.collect
{ it
.file
}
219 println
"Note: No plugin scripts found"
222 // Get the paths of any installed plugins and add them to the
223 // initial binding as '<pluginName>PluginDir'.
224 binding
= new Binding()
227 def plugins
= RESOLVER
.getResources("file:${baseDir.absolutePath}/plugins/*/*GrailsPlugin.groovy")
229 plugins
.each
{ resource
->
230 def matcher
= resource
.filename
=~
/(\S+)GrailsPlugin
.groovy
/
231 def pluginName
= GrailsClassUtils
.getPropertyName(matcher
[0][1])
233 // Add the plugin path to the binding.
234 binding
.setVariable("${pluginName}PluginDir", resource
.file
.parentFile
)
240 SCRIPT_CACHE
[scriptName
] = new CachedScript(binding
:binding
, potentialScripts
:potentialScripts
)
245 if(potentialScripts
.size()>0) {
246 potentialScripts
= potentialScripts
.unique()
247 if(potentialScripts
.size() == 1) {
248 println
"Running script ${potentialScripts[0].absolutePath}"
250 def gant
= new Gant(binding
, new URLClassLoader([classesDir
.toURI().toURL()] as URL
[], rootLoader
))
251 return gant
.processArgs(["-f", potentialScripts
[0].absolutePath
,"-c","-d","${userHome}/.grails/${version}/scriptCache"] as String
[])
254 println
"Multiple options please select:"
256 potentialScripts
.eachWithIndex
{ f
, i
->
257 println
"[${i+1}] $f "
260 ANT
.input(message
: "Enter # ",validargs
:validArgs
.join(","), addproperty
:"grails.script.number")
261 def number
= ANT
.antProject
.properties
."grails.script.number".toInteger()
263 println
"Running script ${potentialScripts[number-1].absolutePath}"
264 def gant
= new Gant(binding
, new URLClassLoader([classesDir
.toURI().toURL()] as URL
[], rootLoader
))
265 return gant
.processArgs(["-f", potentialScripts
[number
-1].absolutePath
] as String
[])
269 println
"Script $scriptName not found."
270 println
"Run 'grails help' for a complete list of available scripts."
275 private static processSystemArguments(allArgs
) {
277 allArgs
.eachMatch( /-D(.+?
)=(.+?
)\s+?
/ ) { match
->
278 System
.setProperty(match
[1].trim(),match
[2].trim())
283 def i
= allArgs
.lastIndexOf(lastMatch
)+lastMatch
.size()
284 allArgs
= allArgs
[i
..-1]
289 private static establishBaseDir() {
290 def sysProp
= System
.getProperty("base.dir")
293 baseDir
= sysProp
== '.' ?
new File("") : new File(sysProp
)
296 baseDir
= new File("")
297 if(!new File(baseDir
.absolutePath
, "grails-app").exists()) {
299 // be careful with this next step...
300 // baseDir.parentFile will return null since baseDir is new File("")
301 // baseDir.absoluteFile needs to happen before retrieving the parentFile
302 def parentDir
= baseDir
.absoluteFile
.parentFile
304 // keep moving up one directory until we find
305 // one that contains the grails-app dir or get
306 // to the top of the filesystem...
307 while(parentDir
!= null && !new File(parentDir
, "grails-app").exists()) {
308 parentDir
= parentDir
.parentFile
311 if(parentDir
!= null) {
312 // if we found the project root, use it
325 List potentialScripts