Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
1e75c0a5
Commit
1e75c0a5
authored
Jan 04, 2014
by
Dave Syer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extend programming model for script commands
parent
b5db4d3f
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
468 additions
and
257 deletions
+468
-257
SpringCli.java
...src/main/java/org/springframework/boot/cli/SpringCli.java
+8
-1
InitCommand.java
...ava/org/springframework/boot/cli/command/InitCommand.java
+27
-2
ScriptCommand.java
...a/org/springframework/boot/cli/command/ScriptCommand.java
+46
-199
ScriptCompilationCustomizer.java
...amework/boot/cli/command/ScriptCompilationCustomizer.java
+91
-29
ShellCommand.java
...va/org/springframework/boot/cli/command/ShellCommand.java
+1
-1
ScriptCompilationCustomizerTests.java
...st/java/cli/command/ScriptCompilationCustomizerTests.java
+131
-0
InitCommandTests.java
...rg/springframework/boot/cli/command/InitCommandTests.java
+7
-1
ScriptCommandTests.java
.../springframework/boot/cli/command/ScriptCommandTests.java
+79
-0
command.groovy
spring-boot-cli/src/test/resources/command.groovy
+19
-0
command.groovy
spring-boot-cli/src/test/resources/commands/command.groovy
+6
-1
handler.groovy
spring-boot-cli/src/test/resources/commands/handler.groovy
+3
-3
options.groovy
spring-boot-cli/src/test/resources/commands/options.groovy
+3
-4
init.groovy
spring-boot-cli/src/test/resources/init.groovy
+1
-1
command.groovy
spring-boot-cli/src/test/resources/scripts/command.groovy
+21
-9
handler.groovy
spring-boot-cli/src/test/resources/scripts/handler.groovy
+20
-5
options.groovy
spring-boot-cli/src/test/resources/scripts/options.groovy
+5
-1
No files found.
spring-boot-cli/src/main/java/org/springframework/boot/cli/SpringCli.java
View file @
1e75c0a5
...
@@ -52,12 +52,15 @@ public class SpringCli {
...
@@ -52,12 +52,15 @@ public class SpringCli {
private
String
displayName
=
CLI_APP
+
" "
;
private
String
displayName
=
CLI_APP
+
" "
;
private
InitCommand
init
;
/**
/**
* Create a new {@link SpringCli} implementation with the default set of commands.
* Create a new {@link SpringCli} implementation with the default set of commands.
*/
*/
public
SpringCli
()
{
public
SpringCli
()
{
try
{
try
{
new
InitCommand
(
this
).
run
();
this
.
init
=
new
InitCommand
(
this
);
this
.
init
.
run
();
}
}
catch
(
Exception
e
)
{
catch
(
Exception
e
)
{
throw
new
IllegalStateException
(
"Cannot init with those args"
,
e
);
throw
new
IllegalStateException
(
"Cannot init with those args"
,
e
);
...
@@ -66,6 +69,10 @@ public class SpringCli {
...
@@ -66,6 +69,10 @@ public class SpringCli {
this
.
commands
.
add
(
new
HintCommand
());
this
.
commands
.
add
(
new
HintCommand
());
}
}
public
InitCommand
getInitCommand
()
{
return
this
.
init
;
}
/**
/**
* Set the command available to the CLI. Primarily used to support testing. NOTE: The
* Set the command available to the CLI. Primarily used to support testing. NOTE: The
* 'help' command will be automatically provided in addition to this list.
* 'help' command will be automatically provided in addition to this list.
...
...
spring-boot-cli/src/main/java/org/springframework/boot/cli/command/InitCommand.java
View file @
1e75c0a5
...
@@ -16,11 +16,13 @@
...
@@ -16,11 +16,13 @@
package
org
.
springframework
.
boot
.
cli
.
command
;
package
org
.
springframework
.
boot
.
cli
.
command
;
import
groovy.lang.Closure
;
import
groovy.lang.GroovyClassLoader
;
import
groovy.lang.GroovyClassLoader
;
import
groovy.lang.Script
;
import
groovy.lang.Script
;
import
java.io.File
;
import
java.io.File
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.ServiceLoader
;
import
java.util.ServiceLoader
;
import
joptsimple.OptionSet
;
import
joptsimple.OptionSet
;
...
@@ -87,6 +89,8 @@ public class InitCommand extends OptionParsingCommand {
...
@@ -87,6 +89,8 @@ public class InitCommand extends OptionParsingCommand {
options
,
this
,
repositoryConfiguration
);
options
,
this
,
repositoryConfiguration
);
this
.
compiler
=
new
GroovyCompiler
(
configuration
);
this
.
compiler
=
new
GroovyCompiler
(
configuration
);
this
.
compiler
.
addCompilationCustomizers
(
new
ScriptCompilationCustomizer
());
loader
=
this
.
compiler
.
getLoader
();
loader
=
this
.
compiler
.
getLoader
();
Thread
.
currentThread
().
setContextClassLoader
(
loader
);
Thread
.
currentThread
().
setContextClassLoader
(
loader
);
...
@@ -102,7 +106,25 @@ public class InitCommand extends OptionParsingCommand {
...
@@ -102,7 +106,25 @@ public class InitCommand extends OptionParsingCommand {
if
(
this
.
compiler
!=
null
&&
files
.
length
>
0
)
{
if
(
this
.
compiler
!=
null
&&
files
.
length
>
0
)
{
Class
<?>[]
classes
=
this
.
compiler
.
compile
(
files
);
Class
<?>[]
classes
=
this
.
compiler
.
compile
(
files
);
for
(
Class
<?>
type
:
classes
)
{
for
(
Class
<?>
type
:
classes
)
{
if
(
Script
.
class
.
isAssignableFrom
(
type
))
{
Command
script
=
ScriptCommand
.
command
(
type
);
if
(
script
!=
null
)
{
this
.
cli
.
register
(
script
);
}
else
if
(
CommandFactory
.
class
.
isAssignableFrom
(
type
))
{
for
(
Command
command
:
((
CommandFactory
)
type
.
newInstance
())
.
getCommands
(
this
.
cli
))
{
this
.
cli
.
register
(
command
);
}
}
else
if
(
Commands
.
class
.
isAssignableFrom
(
type
))
{
Map
<
String
,
Closure
<?>>
commands
=
((
Commands
)
type
.
newInstance
())
.
getCommands
();
for
(
String
command
:
commands
.
keySet
())
{
this
.
cli
.
register
(
new
ScriptCommand
(
command
,
commands
.
get
(
command
)));
}
}
else
if
(
Script
.
class
.
isAssignableFrom
(
type
))
{
((
Script
)
type
.
newInstance
()).
run
();
((
Script
)
type
.
newInstance
()).
run
();
}
}
}
}
...
@@ -121,7 +143,6 @@ public class InitCommand extends OptionParsingCommand {
...
@@ -121,7 +143,6 @@ public class InitCommand extends OptionParsingCommand {
}
}
}
}
}
}
private
static
class
InitGroovyCompilerConfigurationAdapter
extends
private
static
class
InitGroovyCompilerConfigurationAdapter
extends
...
@@ -138,4 +159,8 @@ public class InitCommand extends OptionParsingCommand {
...
@@ -138,4 +159,8 @@ public class InitCommand extends OptionParsingCommand {
}
}
}
}
public
static
interface
Commands
{
Map
<
String
,
Closure
<?>>
getCommands
();
}
}
}
spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCommand.java
View file @
1e75c0a5
...
@@ -17,30 +17,13 @@
...
@@ -17,30 +17,13 @@
package
org
.
springframework
.
boot
.
cli
.
command
;
package
org
.
springframework
.
boot
.
cli
.
command
;
import
groovy.lang.Closure
;
import
groovy.lang.Closure
;
import
groovy.lang.GroovyObjectSupport
;
import
groovy.lang.GroovyObject
;
import
groovy.lang.MetaClass
;
import
groovy.lang.MetaMethod
;
import
groovy.lang.Script
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.net.URL
;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
joptsimple.OptionParser
;
import
org.codehaus.groovy.control.CompilationFailedException
;
import
org.springframework.boot.cli.Command
;
import
org.springframework.boot.cli.Command
;
import
org.springframework.boot.cli.OptionHelp
;
import
org.springframework.boot.cli.OptionHelp
;
import
org.springframework.boot.cli.compiler.GroovyCompiler
;
import
org.springframework.boot.cli.compiler.GroovyCompilerConfiguration
;
import
org.springframework.boot.cli.compiler.GroovyCompilerScope
;
import
org.springframework.boot.cli.compiler.RepositoryConfigurationFactory
;
import
org.springframework.boot.cli.compiler.grape.RepositoryConfiguration
;
import
org.springframework.util.FileCopyUtils
;
/**
/**
* {@link Command} to run a Groovy script.
* {@link Command} to run a Groovy script.
...
@@ -49,27 +32,28 @@ import org.springframework.util.FileCopyUtils;
...
@@ -49,27 +32,28 @@ import org.springframework.util.FileCopyUtils;
*/
*/
public
class
ScriptCommand
implements
Command
{
public
class
ScriptCommand
implements
Command
{
private
static
final
String
[]
DEFAULT_PATHS
=
new
String
[]
{
"${SPRING_HOME}/ext"
,
"${SPRING_HOME}/bin"
};
private
String
[]
paths
=
DEFAULT_PATHS
;
private
Class
<?>
mainClass
;
private
Object
main
;
private
Object
main
;
private
String
n
ame
;
private
String
defaultN
ame
;
public
ScriptCommand
(
String
script
)
{
public
ScriptCommand
(
String
name
,
Object
main
)
{
this
.
name
=
script
;
this
.
main
=
main
;
this
.
defaultName
=
name
;
}
}
@Override
@Override
public
String
getName
()
{
public
String
getName
()
{
if
(
getMain
()
instanceof
Command
)
{
if
(
this
.
main
instanceof
Command
)
{
return
((
Command
)
getMain
()
).
getName
();
return
((
Command
)
this
.
main
).
getName
();
}
}
return
this
.
name
;
else
if
(
this
.
main
instanceof
GroovyObject
)
{
GroovyObject
object
=
(
GroovyObject
)
this
.
main
;
if
(
object
.
getMetaClass
().
hasProperty
(
object
,
"name"
)
!=
null
)
{
return
(
String
)
object
.
getProperty
(
"name"
);
}
}
return
this
.
defaultName
;
}
}
@Override
@Override
...
@@ -79,210 +63,73 @@ public class ScriptCommand implements Command {
...
@@ -79,210 +63,73 @@ public class ScriptCommand implements Command {
@Override
@Override
public
String
getDescription
()
{
public
String
getDescription
()
{
if
(
getMain
()
instanceof
Command
)
{
if
(
this
.
main
instanceof
Command
)
{
return
((
Command
)
getMain
()
).
getDescription
();
return
((
Command
)
this
.
main
).
getDescription
();
}
}
return
this
.
n
ame
;
return
this
.
defaultN
ame
;
}
}
@Override
@Override
public
String
getHelp
()
{
public
String
getHelp
()
{
if
(
getMain
()
instanceof
OptionHandler
)
{
if
(
this
.
main
instanceof
OptionHandler
)
{
return
((
OptionHandler
)
getMain
()
).
getHelp
();
return
((
OptionHandler
)
this
.
main
).
getHelp
();
}
}
if
(
getMain
()
instanceof
Command
)
{
if
(
this
.
main
instanceof
Command
)
{
return
((
Command
)
getMain
()
).
getHelp
();
return
((
Command
)
this
.
main
).
getHelp
();
}
}
return
null
;
return
null
;
}
}
@Override
@Override
public
Collection
<
OptionHelp
>
getOptionsHelp
()
{
public
Collection
<
OptionHelp
>
getOptionsHelp
()
{
if
(
getMain
()
instanceof
OptionHandler
)
{
if
(
this
.
main
instanceof
OptionHandler
)
{
return
((
OptionHandler
)
getMain
()
).
getOptionsHelp
();
return
((
OptionHandler
)
this
.
main
).
getOptionsHelp
();
}
}
if
(
getMain
()
instanceof
Command
)
{
if
(
this
.
main
instanceof
Command
)
{
return
((
Command
)
getMain
()
).
getOptionsHelp
();
return
((
Command
)
this
.
main
).
getOptionsHelp
();
}
}
return
Collections
.
emptyList
();
return
Collections
.
emptyList
();
}
}
@Override
@Override
public
void
run
(
String
...
args
)
throws
Exception
{
public
void
run
(
String
...
args
)
throws
Exception
{
run
(
getMain
(),
args
);
if
(
this
.
main
instanceof
Command
)
{
}
((
Command
)
this
.
main
).
run
(
args
);
private
void
run
(
Object
main
,
String
[]
args
)
throws
Exception
{
if
(
main
instanceof
Command
)
{
((
Command
)
main
).
run
(
args
);
}
else
if
(
main
instanceof
OptionHandler
)
{
((
OptionHandler
)
getMain
()).
run
(
args
);
}
}
else
if
(
main
instanceof
Closure
)
{
else
if
(
this
.
main
instanceof
OptionHandler
)
{
((
Closure
<?>)
main
).
call
((
Object
[])
args
);
((
OptionHandler
)
this
.
main
).
run
(
args
);
}
}
else
if
(
main
instanceof
Runnabl
e
)
{
else
if
(
this
.
main
instanceof
Closur
e
)
{
((
Runnable
)
main
).
run
(
);
((
Closure
<?>)
this
.
main
).
call
((
Object
[])
args
);
}
}
else
if
(
main
instanceof
Script
)
{
Script
script
=
(
Script
)
this
.
main
;
script
.
setProperty
(
"args"
,
args
);
if
(
this
.
main
instanceof
GroovyObjectSupport
)
{
GroovyObjectSupport
object
=
(
GroovyObjectSupport
)
this
.
main
;
if
(
object
.
getMetaClass
().
hasProperty
(
object
,
"parser"
)
!=
null
)
{
OptionParser
parser
=
(
OptionParser
)
object
.
getProperty
(
"parser"
);
if
(
parser
!=
null
)
{
script
.
setProperty
(
"options"
,
parser
.
parse
(
args
));
}
}
}
Object
result
=
script
.
run
();
run
(
result
,
args
);
}
}
/**
* Paths to search for script files.
*
* @param paths the paths to set
*/
public
void
setPaths
(
String
[]
paths
)
{
this
.
paths
=
(
paths
==
null
?
null
:
paths
.
clone
());
}
}
@Override
@Override
public
String
getUsageHelp
()
{
public
String
getUsageHelp
()
{
if
(
getMain
()
instanceof
Command
)
{
if
(
this
.
main
instanceof
Command
)
{
return
((
Command
)
getMain
()
).
getDescription
();
return
((
Command
)
this
.
main
).
getDescription
();
}
}
return
"[options] <args>"
;
return
"[options] <args>"
;
}
}
protected
Object
getMain
()
{
public
static
ScriptCommand
command
(
Class
<?>
type
)
{
if
(
this
.
main
==
null
)
{
try
{
this
.
main
=
getMainClass
().
newInstance
();
}
catch
(
Exception
ex
)
{
throw
new
IllegalStateException
(
"Cannot create main class: "
+
this
.
name
,
ex
);
}
if
(
this
.
main
instanceof
OptionHandler
)
{
((
OptionHandler
)
this
.
main
).
options
();
}
else
if
(
this
.
main
instanceof
GroovyObjectSupport
)
{
GroovyObjectSupport
object
=
(
GroovyObjectSupport
)
this
.
main
;
MetaClass
metaClass
=
object
.
getMetaClass
();
MetaMethod
options
=
metaClass
.
getMetaMethod
(
"options"
,
null
);
if
(
options
!=
null
)
{
options
.
doMethodInvoke
(
this
.
main
,
null
);
}
}
}
return
this
.
main
;
}
private
void
compile
()
{
Object
main
=
null
;
GroovyCompiler
compiler
=
new
GroovyCompiler
(
new
ScriptConfiguration
());
compiler
.
addCompilationCustomizers
(
new
ScriptCompilationCustomizer
());
File
source
=
locateSource
(
this
.
name
);
Class
<?>[]
classes
;
try
{
try
{
classes
=
compiler
.
compile
(
source
);
main
=
type
.
newInstance
();
}
catch
(
CompilationFailedException
ex
)
{
throw
new
IllegalStateException
(
"Could not compile script"
,
ex
);
}
}
catch
(
IOException
ex
)
{
catch
(
Exception
ex
)
{
throw
new
IllegalStateException
(
"Could not compile script"
,
ex
);
// Inner classes and closures will not be instantiatable
return
null
;
}
}
this
.
mainClass
=
classes
[
0
];
if
(
main
instanceof
Command
)
{
}
return
new
ScriptCommand
(
type
.
getSimpleName
(),
main
);
private
Class
<?>
getMainClass
()
{
if
(
this
.
mainClass
==
null
)
{
compile
();
}
return
this
.
mainClass
;
}
private
File
locateSource
(
String
name
)
{
String
resource
=
name
;
if
(!
name
.
endsWith
(
".groovy"
))
{
resource
=
"commands/"
+
name
+
".groovy"
;
}
URL
url
=
getClass
().
getClassLoader
().
getResource
(
resource
);
if
(
url
!=
null
)
{
return
locateSourceFromUrl
(
name
,
url
);
}
String
home
=
System
.
getProperty
(
"SPRING_HOME"
,
System
.
getenv
(
"SPRING_HOME"
));
if
(
home
==
null
)
{
home
=
"."
;
}
for
(
String
path
:
this
.
paths
)
{
String
subbed
=
path
.
replace
(
"${SPRING_HOME}"
,
home
);
File
file
=
new
File
(
subbed
,
resource
);
if
(
file
.
exists
())
{
return
file
;
}
}
throw
new
IllegalStateException
(
"No script found for : "
+
name
);
}
private
File
locateSourceFromUrl
(
String
name
,
URL
url
)
{
if
(
url
.
toString
().
startsWith
(
"file:"
))
{
return
new
File
(
url
.
toString
().
substring
(
"file:"
.
length
()));
}
// probably in JAR file
try
{
File
file
=
File
.
createTempFile
(
name
,
".groovy"
);
file
.
deleteOnExit
();
FileCopyUtils
.
copy
(
url
.
openStream
(),
new
FileOutputStream
(
file
));
return
file
;
}
catch
(
IOException
ex
)
{
throw
new
IllegalStateException
(
"Could not create temp file for source: "
+
name
);
}
}
private
static
class
ScriptConfiguration
implements
GroovyCompilerConfiguration
{
@Override
public
GroovyCompilerScope
getScope
()
{
return
GroovyCompilerScope
.
EXTENSION
;
}
@Override
public
boolean
isGuessImports
()
{
return
true
;
}
@Override
public
boolean
isAutoconfigure
()
{
return
true
;
}
@Override
public
boolean
isGuessDependencies
()
{
return
true
;
}
@Override
public
String
[]
getClasspath
()
{
return
DEFAULT_CLASSPATH
;
}
}
else
if
(
main
instanceof
OptionHandler
)
{
@Override
((
OptionHandler
)
main
).
options
();
public
List
<
RepositoryConfiguration
>
getRepositoryConfiguration
()
{
return
new
ScriptCommand
(
type
.
getSimpleName
(),
main
);
return
RepositoryConfigurationFactory
.
createDefaultRepositoryConfiguration
();
}
}
return
null
;
}
}
...
...
spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ScriptCompilationCustomizer.java
View file @
1e75c0a5
...
@@ -16,8 +16,7 @@
...
@@ -16,8 +16,7 @@
package
org
.
springframework
.
boot
.
cli
.
command
;
package
org
.
springframework
.
boot
.
cli
.
command
;
import
groovy.lang.Mixin
;
import
java.lang.reflect.Modifier
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
...
@@ -25,15 +24,17 @@ import joptsimple.OptionParser;
...
@@ -25,15 +24,17 @@ import joptsimple.OptionParser;
import
joptsimple.OptionSet
;
import
joptsimple.OptionSet
;
import
joptsimple.OptionSpecBuilder
;
import
joptsimple.OptionSpecBuilder
;
import
org.codehaus.groovy.ast.
AnnotationNode
;
import
org.codehaus.groovy.ast.
ClassCodeVisitorSupport
;
import
org.codehaus.groovy.ast.ClassHelper
;
import
org.codehaus.groovy.ast.ClassHelper
;
import
org.codehaus.groovy.ast.ClassNode
;
import
org.codehaus.groovy.ast.ClassNode
;
import
org.codehaus.groovy.ast.MethodNode
;
import
org.codehaus.groovy.ast.MethodNode
;
import
org.codehaus.groovy.ast.Parameter
;
import
org.codehaus.groovy.ast.Parameter
;
import
org.codehaus.groovy.ast.PropertyNode
;
import
org.codehaus.groovy.ast.expr.ArgumentListExpression
;
import
org.codehaus.groovy.ast.expr.ArgumentListExpression
;
import
org.codehaus.groovy.ast.expr.ClassExpression
;
import
org.codehaus.groovy.ast.expr.ClosureExpression
;
import
org.codehaus.groovy.ast.expr.ClosureExpression
;
import
org.codehaus.groovy.ast.expr.ConstantExpression
;
import
org.codehaus.groovy.ast.expr.Expression
;
import
org.codehaus.groovy.ast.expr.Expression
;
import
org.codehaus.groovy.ast.expr.MapExpression
;
import
org.codehaus.groovy.ast.expr.MethodCallExpression
;
import
org.codehaus.groovy.ast.expr.MethodCallExpression
;
import
org.codehaus.groovy.ast.stmt.BlockStatement
;
import
org.codehaus.groovy.ast.stmt.BlockStatement
;
import
org.codehaus.groovy.ast.stmt.ExpressionStatement
;
import
org.codehaus.groovy.ast.stmt.ExpressionStatement
;
...
@@ -46,6 +47,7 @@ import org.codehaus.groovy.control.customizers.CompilationCustomizer;
...
@@ -46,6 +47,7 @@ import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import
org.codehaus.groovy.control.customizers.ImportCustomizer
;
import
org.codehaus.groovy.control.customizers.ImportCustomizer
;
import
org.springframework.asm.Opcodes
;
import
org.springframework.asm.Opcodes
;
import
org.springframework.boot.cli.Command
;
import
org.springframework.boot.cli.Command
;
import
org.springframework.boot.cli.command.InitCommand.Commands
;
/**
/**
* Customizer for the compilation of CLI commands.
* Customizer for the compilation of CLI commands.
...
@@ -61,11 +63,80 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer {
...
@@ -61,11 +63,80 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer {
@Override
@Override
public
void
call
(
SourceUnit
source
,
GeneratorContext
context
,
ClassNode
classNode
)
public
void
call
(
SourceUnit
source
,
GeneratorContext
context
,
ClassNode
classNode
)
throws
CompilationFailedException
{
throws
CompilationFailedException
{
addOptionHandlerMixin
(
classNode
);
findCommands
(
source
,
classNode
);
overrideOptionsMethod
(
source
,
classNode
);
overrideOptionsMethod
(
source
,
classNode
);
addImports
(
source
,
context
,
classNode
);
addImports
(
source
,
context
,
classNode
);
}
}
private
void
findCommands
(
SourceUnit
source
,
ClassNode
classNode
)
{
CommandVisitor
visitor
=
new
CommandVisitor
(
source
);
classNode
.
visitContents
(
visitor
);
visitor
.
addFactory
(
classNode
);
}
private
static
class
CommandVisitor
extends
ClassCodeVisitorSupport
{
private
SourceUnit
source
;
private
MapExpression
map
=
new
MapExpression
();
private
List
<
ExpressionStatement
>
statements
=
new
ArrayList
<
ExpressionStatement
>();
private
ExpressionStatement
statement
;
public
CommandVisitor
(
SourceUnit
source
)
{
this
.
source
=
source
;
}
private
boolean
hasCommands
()
{
return
!
this
.
map
.
getMapEntryExpressions
().
isEmpty
();
}
private
void
addFactory
(
ClassNode
classNode
)
{
if
(!
hasCommands
())
{
return
;
}
classNode
.
addInterface
(
ClassHelper
.
make
(
Commands
.
class
));
classNode
.
addProperty
(
new
PropertyNode
(
"commands"
,
Modifier
.
PUBLIC
|
Modifier
.
FINAL
,
ClassHelper
.
MAP_TYPE
.
getPlainNodeReference
(),
classNode
,
this
.
map
,
null
,
null
));
}
@Override
protected
SourceUnit
getSourceUnit
()
{
return
this
.
source
;
}
@Override
public
void
visitBlockStatement
(
BlockStatement
block
)
{
this
.
statements
.
clear
();
super
.
visitBlockStatement
(
block
);
block
.
getStatements
().
removeAll
(
this
.
statements
);
}
@Override
public
void
visitExpressionStatement
(
ExpressionStatement
statement
)
{
this
.
statement
=
statement
;
super
.
visitExpressionStatement
(
statement
);
}
@Override
public
void
visitMethodCallExpression
(
MethodCallExpression
call
)
{
Expression
methodCall
=
call
.
getMethod
();
if
(
methodCall
instanceof
ConstantExpression
)
{
ConstantExpression
method
=
(
ConstantExpression
)
methodCall
;
if
(
"command"
.
equals
(
method
.
getValue
()))
{
ArgumentListExpression
arguments
=
(
ArgumentListExpression
)
call
.
getArguments
();
this
.
statements
.
add
(
this
.
statement
);
ConstantExpression
name
=
(
ConstantExpression
)
arguments
.
getExpression
(
0
);
ClosureExpression
closure
=
(
ClosureExpression
)
arguments
.
getExpression
(
1
);
this
.
map
.
addMapEntryExpression
(
name
,
closure
);
}
}
}
}
/**
/**
* Add imports to the class node to make writing simple commands easier. No need to
* Add imports to the class node to make writing simple commands easier. No need to
* import {@link OptionParser}, {@link OptionSet}, {@link Command} or
* import {@link OptionParser}, {@link OptionSet}, {@link Command} or
...
@@ -104,6 +175,18 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer {
...
@@ -104,6 +175,18 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer {
*/
*/
private
void
overrideOptionsMethod
(
SourceUnit
source
,
ClassNode
classNode
)
{
private
void
overrideOptionsMethod
(
SourceUnit
source
,
ClassNode
classNode
)
{
ClosureExpression
closure
=
options
(
source
,
classNode
);
if
(
closure
!=
null
)
{
classNode
.
addMethod
(
new
MethodNode
(
"options"
,
Opcodes
.
ACC_PROTECTED
,
ClassHelper
.
VOID_TYPE
,
new
Parameter
[
0
],
new
ClassNode
[
0
],
closure
.
getCode
()));
classNode
.
setSuperClass
(
ClassHelper
.
make
(
OptionHandler
.
class
));
}
}
private
ClosureExpression
options
(
SourceUnit
source
,
ClassNode
classNode
)
{
BlockStatement
block
=
source
.
getAST
().
getStatementBlock
();
BlockStatement
block
=
source
.
getAST
().
getStatementBlock
();
List
<
Statement
>
statements
=
block
.
getStatements
();
List
<
Statement
>
statements
=
block
.
getStatements
();
...
@@ -114,43 +197,22 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer {
...
@@ -114,43 +197,22 @@ public class ScriptCompilationCustomizer extends CompilationCustomizer {
if
(
expression
instanceof
MethodCallExpression
)
{
if
(
expression
instanceof
MethodCallExpression
)
{
MethodCallExpression
method
=
(
MethodCallExpression
)
expression
;
MethodCallExpression
method
=
(
MethodCallExpression
)
expression
;
if
(
method
.
getMethod
().
getText
().
equals
(
"options"
))
{
if
(
method
.
getMethod
().
getText
().
equals
(
"options"
))
{
statements
.
remove
(
statement
);
expression
=
method
.
getArguments
();
expression
=
method
.
getArguments
();
if
(
expression
instanceof
ArgumentListExpression
)
{
if
(
expression
instanceof
ArgumentListExpression
)
{
ArgumentListExpression
arguments
=
(
ArgumentListExpression
)
expression
;
ArgumentListExpression
arguments
=
(
ArgumentListExpression
)
expression
;
expression
=
arguments
.
getExpression
(
0
);
expression
=
arguments
.
getExpression
(
0
);
if
(
expression
instanceof
ClosureExpression
)
{
if
(
expression
instanceof
ClosureExpression
)
{
ClosureExpression
closure
=
(
ClosureExpression
)
expression
;
return
(
ClosureExpression
)
expression
;
classNode
.
addMethod
(
new
MethodNode
(
"options"
,
Opcodes
.
ACC_PROTECTED
,
ClassHelper
.
VOID_TYPE
,
new
Parameter
[
0
],
new
ClassNode
[
0
],
closure
.
getCode
()));
statements
.
remove
(
statement
);
}
}
}
}
}
}
}
}
}
}
}
}
}
return
null
;
/**
* Add {@link OptionHandler} as a mixin to the class node if it doesn't already
* declare it as a super class.
*
* @param classNode the class node to manipulate
*/
private
void
addOptionHandlerMixin
(
ClassNode
classNode
)
{
// If we are not an OptionHandler then add that class as a mixin
if
(!
classNode
.
isDerivedFrom
(
ClassHelper
.
make
(
OptionHandler
.
class
))
&&
!
classNode
.
isDerivedFrom
(
ClassHelper
.
make
(
"OptionHandler"
)))
{
AnnotationNode
mixin
=
new
AnnotationNode
(
ClassHelper
.
make
(
Mixin
.
class
));
mixin
.
addMember
(
"value"
,
new
ClassExpression
(
ClassHelper
.
make
(
OptionHandler
.
class
)));
classNode
.
addAnnotation
(
mixin
);
}
}
}
}
}
spring-boot-cli/src/main/java/org/springframework/boot/cli/command/ShellCommand.java
View file @
1e75c0a5
...
@@ -99,7 +99,7 @@ public class ShellCommand extends AbstractCommand {
...
@@ -99,7 +99,7 @@ public class ShellCommand extends AbstractCommand {
PromptCommand
prompt
=
new
PromptCommand
(
this
);
PromptCommand
prompt
=
new
PromptCommand
(
this
);
cli
.
register
(
prompt
);
cli
.
register
(
prompt
);
cli
.
register
(
new
InitCommand
(
cli
));
cli
.
register
(
cli
.
getInitCommand
(
));
}
}
private
ConsoleReader
createConsoleReader
()
throws
IOException
{
private
ConsoleReader
createConsoleReader
()
throws
IOException
{
...
...
spring-boot-cli/src/test/java/cli/command/ScriptCompilationCustomizerTests.java
0 → 100644
View file @
1e75c0a5
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
cli
.
command
;
import
groovy.lang.Closure
;
import
java.io.File
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
org.junit.Before
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.springframework.boot.OutputCapture
;
import
org.springframework.boot.cli.Command
;
import
org.springframework.boot.cli.command.InitCommand.Commands
;
import
org.springframework.boot.cli.command.OptionHandler
;
import
org.springframework.boot.cli.command.ScriptCompilationCustomizer
;
import
org.springframework.boot.cli.compiler.GroovyCompiler
;
import
org.springframework.boot.cli.compiler.GroovyCompilerConfiguration
;
import
org.springframework.boot.cli.compiler.GroovyCompilerScope
;
import
org.springframework.boot.cli.compiler.grape.RepositoryConfiguration
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
/**
* @author Dave Syer
*/
public
class
ScriptCompilationCustomizerTests
{
private
TestGroovyCompilerConfiguration
configuration
=
new
TestGroovyCompilerConfiguration
();
private
GroovyCompiler
compiler
=
new
GroovyCompiler
(
this
.
configuration
);
@Rule
public
OutputCapture
output
=
new
OutputCapture
();
@Before
public
void
init
()
{
this
.
compiler
.
addCompilationCustomizers
(
new
ScriptCompilationCustomizer
());
}
@Test
public
void
simpleCompile
()
throws
Exception
{
Class
<?>[]
types
=
this
.
compiler
.
compile
(
new
File
(
"src/test/resources/scripts/command.groovy"
));
Class
<?>
main
=
types
[
0
];
assertEquals
(
"org.test.command.TestCommand"
,
main
.
getName
());
assertTrue
(
Command
.
class
.
isAssignableFrom
(
main
));
}
@Test
public
void
addsOptionHandler
()
throws
Exception
{
Class
<?>[]
types
=
this
.
compiler
.
compile
(
new
File
(
"src/test/resources/scripts/handler.groovy"
));
Class
<?>
main
=
types
[
0
];
assertTrue
(
OptionHandler
.
class
.
isAssignableFrom
(
main
));
}
@Test
public
void
addsCommands
()
throws
Exception
{
Class
<?>[]
types
=
this
.
compiler
.
compile
(
new
File
(
"src/test/resources/scripts/options.groovy"
));
Class
<?>
main
=
types
[
0
];
assertTrue
(
Commands
.
class
.
isAssignableFrom
(
main
));
}
@Test
public
void
commandsExecutable
()
throws
Exception
{
Class
<?>[]
types
=
this
.
compiler
.
compile
(
new
File
(
"src/test/resources/scripts/options.groovy"
));
Class
<?>
main
=
types
[
0
];
Map
<
String
,
Closure
<?>>
commands
=
((
Commands
)
main
.
newInstance
()).
getCommands
();
assertEquals
(
1
,
commands
.
size
());
assertEquals
(
"foo"
,
commands
.
keySet
().
iterator
().
next
());
Closure
<?>
closure
=
commands
.
values
().
iterator
().
next
();
closure
.
call
();
// what about args?
assertTrue
(
this
.
output
.
toString
().
contains
(
"Hello Command"
));
}
private
static
class
TestGroovyCompilerConfiguration
implements
GroovyCompilerConfiguration
{
@Override
public
GroovyCompilerScope
getScope
()
{
return
GroovyCompilerScope
.
EXTENSION
;
}
@Override
public
boolean
isGuessImports
()
{
return
true
;
}
@Override
public
boolean
isGuessDependencies
()
{
return
false
;
}
@Override
public
boolean
isAutoconfigure
()
{
return
true
;
}
@Override
public
String
[]
getClasspath
()
{
return
new
String
[
0
];
}
@Override
public
List
<
RepositoryConfiguration
>
getRepositoryConfiguration
()
{
return
Collections
.
emptyList
();
}
}
}
spring-boot-cli/src/test/java/org/springframework/boot/cli/command/InitCommandTests.java
View file @
1e75c0a5
...
@@ -69,6 +69,12 @@ public class InitCommandTests {
...
@@ -69,6 +69,12 @@ public class InitCommandTests {
assertTrue
(
this
.
output
.
toString
().
contains
(
"Hello Grab"
));
assertTrue
(
this
.
output
.
toString
().
contains
(
"Hello Grab"
));
}
}
@Test
public
void
initCommand
()
throws
Exception
{
this
.
command
.
run
(
"src/test/resources/command.groovy"
);
verify
(
this
.
cli
,
times
(
this
.
defaultCount
+
1
)).
register
(
any
(
Command
.
class
));
}
@Test
(
expected
=
IllegalArgumentException
.
class
)
@Test
(
expected
=
IllegalArgumentException
.
class
)
public
void
initNonExistentScript
()
throws
Exception
{
public
void
initNonExistentScript
()
throws
Exception
{
this
.
command
.
run
(
"nonexistent.groovy"
);
this
.
command
.
run
(
"nonexistent.groovy"
);
...
@@ -78,7 +84,7 @@ public class InitCommandTests {
...
@@ -78,7 +84,7 @@ public class InitCommandTests {
@Test
@Test
public
void
initDefault
()
throws
Exception
{
public
void
initDefault
()
throws
Exception
{
this
.
command
.
run
();
this
.
command
.
run
();
assertTrue
(
this
.
output
.
toString
().
contains
(
"Hello
World
"
));
assertTrue
(
this
.
output
.
toString
().
contains
(
"Hello
Init
"
));
}
}
}
}
spring-boot-cli/src/test/
resources/commands/test.groovy
→
spring-boot-cli/src/test/
java/org/springframework/boot/cli/command/ScriptCommandTests.java
View file @
1e75c0a5
...
@@ -14,10 +14,66 @@
...
@@ -14,10 +14,66 @@
* limitations under the License.
* limitations under the License.
*/
*/
options
{
package
org
.
springframework
.
boot
.
cli
.
command
;
option
"foo"
,
"Foo set"
option
"bar"
,
"Bar has an argument of type int"
withOptionalArg
()
ofType
Integer
import
org.junit.After
;
}
import
org.junit.Before
;
import
org.junit.Ignore
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.springframework.boot.OutputCapture
;
import
org.springframework.boot.cli.SpringCli
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
/**
* @author Dave Syer
*/
public
class
ScriptCommandTests
{
@Rule
public
OutputCapture
output
=
new
OutputCapture
();
public
static
boolean
executed
=
false
;
private
SpringCli
cli
;
private
InitCommand
init
;
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
private
ClassLoader
classLoader
;
println
"Hello ${options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')}"
@Before
public
void
init
()
{
this
.
classLoader
=
Thread
.
currentThread
().
getContextClassLoader
();
this
.
cli
=
new
SpringCli
();
this
.
init
=
this
.
cli
.
getInitCommand
();
executed
=
false
;
}
@After
public
void
close
()
{
Thread
.
currentThread
().
setContextClassLoader
(
this
.
classLoader
);
}
@Test
public
void
command
()
throws
Exception
{
this
.
init
.
run
(
"src/test/resources/commands/command.groovy"
);
this
.
cli
.
find
(
"foo"
).
run
(
"Foo"
);
assertTrue
(
this
.
output
.
toString
().
contains
(
"Hello Foo"
));
}
@Test
public
void
handler
()
throws
Exception
{
this
.
init
.
run
(
"src/test/resources/commands/handler.groovy"
);
this
.
cli
.
find
(
"foo"
).
run
(
"Foo"
,
"--foo=bar"
);
assertTrue
(
executed
);
}
@Test
@Ignore
public
void
options
()
throws
Exception
{
this
.
init
.
run
(
"src/test/resources/commands/options.groovy"
);
this
.
cli
.
find
(
"foo"
).
run
(
"Foo"
,
"--foo=bar"
);
assertTrue
(
executed
);
}
}
spring-boot-cli/src/test/resources/command.groovy
0 → 100644
View file @
1e75c0a5
class
MyCommand
implements
Command
{
String
name
=
"foo"
String
description
=
"My script command"
String
help
=
"No options"
String
usageHelp
=
"Not very useful"
Collection
<
String
>
optionsHelp
=
[
"No options"
]
boolean
optionCommand
=
false
void
run
(
String
...
args
)
{
println
"Hello ${args[0]}"
}
}
\ No newline at end of file
spring-boot-cli/src/test/resources/commands/command.groovy
View file @
1e75c0a5
...
@@ -16,6 +16,8 @@
...
@@ -16,6 +16,8 @@
package
org.test.command
package
org.test.command
import
java.util.Collection
;
class
TestCommand
implements
Command
{
class
TestCommand
implements
Command
{
String
name
=
"foo"
String
name
=
"foo"
...
@@ -25,9 +27,12 @@ class TestCommand implements Command {
...
@@ -25,9 +27,12 @@ class TestCommand implements Command {
String
help
=
"No options"
String
help
=
"No options"
String
usageHelp
=
"Not very useful"
String
usageHelp
=
"Not very useful"
Collection
<
String
>
optionsHelp
=
[
"No options"
]
boolean
optionCommand
=
false
void
run
(
String
...
args
)
{
void
run
(
String
...
args
)
{
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
println
"Hello ${args[0]}"
println
"Hello ${args[0]}"
}
}
...
...
spring-boot-cli/src/test/resources/commands/handler.groovy
View file @
1e75c0a5
...
@@ -16,13 +16,13 @@
...
@@ -16,13 +16,13 @@
package
org.test.command
package
org.test.command
import
joptsimple.OptionSet
@Grab
(
"org.eclipse.jgit:org.eclipse.jgit:2.3.1.201302201838-r"
)
@Grab
(
"org.eclipse.jgit:org.eclipse.jgit:2.3.1.201302201838-r"
)
import
org.eclipse.jgit.api.Git
import
org.eclipse.jgit.api.Git
class
TestCommand
extends
OptionHandler
{
class
TestCommand
extends
OptionHandler
{
String
name
=
"foo"
void
options
()
{
void
options
()
{
option
"foo"
,
"Foo set"
option
"foo"
,
"Foo set"
...
@@ -30,7 +30,7 @@ class TestCommand extends OptionHandler {
...
@@ -30,7 +30,7 @@ class TestCommand extends OptionHandler {
void
run
(
OptionSet
options
)
{
void
run
(
OptionSet
options
)
{
// Demonstrate use of Grape.grab to load dependencies before running
// Demonstrate use of Grape.grab to load dependencies before running
println
"Clean
: "
+
Git
.
open
(
".."
as
File
).
status
().
call
().
isClean
()
println
"Clean: "
+
Git
.
open
(
".."
as
File
).
status
().
call
().
isClean
()
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
println
"Hello ${options.nonOptionArguments()}: ${options.has('foo')}"
println
"Hello ${options.nonOptionArguments()}: ${options.has('foo')}"
}
}
...
...
spring-boot-cli/src/test/resources/commands/
closure
.groovy
→
spring-boot-cli/src/test/resources/commands/
options
.groovy
View file @
1e75c0a5
...
@@ -14,9 +14,8 @@
...
@@ -14,9 +14,8 @@
* limitations under the License.
* limitations under the License.
*/
*/
def
run
=
{
msg
->
command
(
"foo"
)
{
args
->
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
println
"Hello ${
msg
}"
println
"Hello ${
options.nonOptionArguments()}: ${options.has('foo')} ${options.valueOf('bar')
}"
}
}
run
spring-boot-cli/src/test/resources/init.groovy
View file @
1e75c0a5
println
"Hello World"
println
"Hello Init"
\ No newline at end of file
\ No newline at end of file
spring-boot-cli/src/test/resources/
commands/runnable
.groovy
→
spring-boot-cli/src/test/resources/
scripts/command
.groovy
View file @
1e75c0a5
...
@@ -14,14 +14,26 @@
...
@@ -14,14 +14,26 @@
* limitations under the License.
* limitations under the License.
*/
*/
class
TestCommand
implements
Runnable
{
package
org.test.command
def
msg
TestCommand
(
String
msg
)
{
import
java.util.Collection
;
this
.
msg
=
msg
}
class
TestCommand
implements
Command
{
void
run
()
{
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
String
name
=
"foo"
println
"Hello ${msg}"
String
description
=
"My script command"
String
help
=
"No options"
String
usageHelp
=
"Not very useful"
Collection
<
String
>
optionsHelp
=
[
"No options"
]
boolean
optionCommand
=
false
void
run
(
String
...
args
)
{
println
"Hello ${args[0]}"
}
}
}
}
new
TestCommand
(
args
[
0
])
spring-boot-cli/src/test/resources/
commands/mixin
.groovy
→
spring-boot-cli/src/test/resources/
scripts/handler
.groovy
View file @
1e75c0a5
...
@@ -14,9 +14,24 @@
...
@@ -14,9 +14,24 @@
* limitations under the License.
* limitations under the License.
*/
*/
void
options
()
{
package
org.test.command
option
"foo"
,
"Foo set"
}
@Grab
(
"org.eclipse.jgit:org.eclipse.jgit:2.3.1.201302201838-r"
)
import
org.eclipse.jgit.api.Git
class
TestCommand
extends
OptionHandler
{
String
name
=
"foo"
org
.
springframework
.
boot
.
cli
.
command
.
ScriptCommandTests
.
executed
=
true
void
options
()
{
println
"Hello ${options.nonOptionArguments()}: ${options.has('foo')}"
option
"foo"
,
"Foo set"
}
void
run
(
OptionSet
options
)
{
// Demonstrate use of Grape.grab to load dependencies before running
println
"Clean: "
+
Git
.
open
(
".."
as
File
).
status
().
call
().
isClean
()
println
"Hello ${options.nonOptionArguments()}: ${options.has('foo')}"
}
}
spring-boot-cli/src/test/resources/
commands/script
.groovy
→
spring-boot-cli/src/test/resources/
scripts/options
.groovy
View file @
1e75c0a5
...
@@ -14,4 +14,8 @@
...
@@ -14,4 +14,8 @@
* limitations under the License.
* limitations under the License.
*/
*/
println
"Hello ${args[0]}"
command
(
"foo"
)
{
args
->
println
"Hello Command"
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment