[bs-62] Add integration tests for CLI samples

[Fixes #48658503]
This commit is contained in:
Dave Syer 2013-04-24 16:09:17 +01:00
parent c91e83c7d2
commit 38f0cf1ed2
14 changed files with 265 additions and 33 deletions

View File

@ -1,12 +1,6 @@
package org.test
@GrabResolver(name='spring-milestone', root='http://repo.springframework.org/milestone')
@GrabResolver(name='spring-snapshot', root='http://repo.springframework.org/snapshot')
@Grab("org.springframework.bootstrap:spring-bootstrap:0.0.1-SNAPSHOT")
@Grab("org.springframework:spring-context:4.0.0.BOOTSTRAP-SNAPSHOT")
@org.springframework.bootstrap.context.annotation.EnableAutoConfiguration
@org.springframework.stereotype.Component
@Component
class Example implements org.springframework.bootstrap.CommandLineRunner {
@org.springframework.beans.factory.annotation.Autowired
@ -18,7 +12,7 @@ class Example implements org.springframework.bootstrap.CommandLineRunner {
}
@org.springframework.stereotype.Service
@Service
class MyService {
public String sayWorld() {

View File

@ -0,0 +1,16 @@
package org.test
@GrabResolver(name='spring-snapshot', root='http://repo.springframework.org/snapshot')
@Grab("org.springframework.bootstrap:spring-bootstrap-service:0.0.1-SNAPSHOT")
@Controller
class SampleController {
@RequestMapping("/")
@ResponseBody
public def hello() {
[message: "Hello World!"]
}
}

View File

@ -1,4 +1,3 @@
@org.springframework.bootstrap.context.annotation.EnableAutoConfiguration
@Controller
class Example {

View File

@ -48,7 +48,9 @@ public class RunCommand extends OptionParsingCommand {
private OptionSpec<Void> verboseOption;
private OptionSpec<Void> quiteOption;
private OptionSpec<Void> quietOption;
private BootstrapRunner runner;
public RunCommand() {
super("run", "Run a spring groovy script");
@ -59,6 +61,12 @@ public class RunCommand extends OptionParsingCommand {
return "[options] <file>";
}
public void stop() {
if (this.runner != null) {
this.runner.stop();
}
}
@Override
protected OptionParser createOptionParser() {
OptionParser parser = new OptionParser();
@ -71,7 +79,7 @@ public class RunCommand extends OptionParsingCommand {
this.noGuessDependenciesOption = parser.accepts("no-guess-dependencies",
"Do not attempt to guess dependencies");
this.verboseOption = parser.acceptsAll(asList("verbose", "v"), "Verbose logging");
this.quiteOption = parser.acceptsAll(asList("quiet", "q"), "Quiet logging");
this.quietOption = parser.acceptsAll(asList("quiet", "q"), "Quiet logging");
return parser;
}
@ -87,8 +95,9 @@ public class RunCommand extends OptionParsingCommand {
BootstrapRunnerConfiguration configuration = new BootstrapRunnerConfigurationAdapter(
options);
new BootstrapRunner(configuration, file, args.toArray(new String[args.size()]))
.compileAndRun();
this.runner = new BootstrapRunner(configuration, file,
args.toArray(new String[args.size()]));
this.runner.compileAndRun();
}
private File getFileArgument(List<String> nonOptionArguments) {
@ -136,7 +145,7 @@ public class RunCommand extends OptionParsingCommand {
if (this.options.has(RunCommand.this.verboseOption)) {
return Level.FINEST;
}
if (this.options.has(RunCommand.this.quiteOption)) {
if (this.options.has(RunCommand.this.quietOption)) {
return Level.OFF;
}
return Level.INFO;

View File

@ -60,12 +60,34 @@ public class DependencyCustomizer {
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if the specified
* class names are not on the class path.
* Create a nested {@link DependencyCustomizer} that only applies if any of the
* specified class names are not on the class path.
* @param classNames the class names to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifMissingClasses(final String... classNames) {
public DependencyCustomizer ifAnyMissingClasses(final String... classNames) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String classname : classNames) {
try {
DependencyCustomizer.this.loader.loadClass(classname);
} catch (Exception e) {
return true;
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if all of the
* specified class names are not on the class path.
* @param classNames the class names to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAllMissingClasses(final String... classNames) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
@ -81,6 +103,86 @@ public class DependencyCustomizer {
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if the specified
* paths are on the class path.
* @param paths the paths to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAllResourcesPresent(final String... paths) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String path : paths) {
try {
if (DependencyCustomizer.this.loader.getResource(path) == null) {
return false;
}
return true;
} catch (Exception e) {
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies at least one of the
* specified paths is on the class path.
* @param paths the paths to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAnyResourcesPresent(final String... paths) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String path : paths) {
try {
if (DependencyCustomizer.this.loader.getResource(path) != null) {
return true;
}
return false;
} catch (Exception e) {
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies the specified one
* was not yet added.
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifNotAdded(final String group, final String module) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
if (DependencyCustomizer.this.contains(group, module)) {
return false;
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* @param group the group ID
* @param module the module ID
* @return true if this module is already in the dependencies
*/
protected boolean contains(String group, String module) {
for (Map<String, Object> dependency : this.dependencies) {
if (group.equals(dependency.get("group"))
&& module.equals(dependency.get("module"))) {
return true;
}
}
return false;
}
/**
* Add a single dependencies.
* @param group the group ID
@ -113,7 +215,7 @@ public class DependencyCustomizer {
/**
* Strategy called to test if dependencies can be added. Subclasses override as
* requred.
* required.
*/
protected boolean canAdd() {
return true;

View File

@ -37,7 +37,7 @@ public class SpringBatchCompilerAutoConfiguration extends CompilerAutoConfigurat
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifMissingClasses("org.springframework.batch.core.Job").add(
dependencies.ifAnyMissingClasses("org.springframework.batch.core.Job").add(
"org.springframework.batch", "spring-batch-core", "2.1.9.RELEASE");
}

View File

@ -38,15 +38,27 @@ public class SpringBootstrapCompilerAutoConfiguration extends CompilerAutoConfig
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifMissingClasses("org.springframework.bootstrap.SpringApplication")
.add("org.springframework.bootstrap", "spring-bootstrap-application",
"0.0.1-SNAPSHOT");
DependencyCustomizer customizer = dependencies.ifAnyMissingClasses(
"org.springframework.bootstrap.SpringApplication").add(
"org.springframework.bootstrap", "spring-bootstrap", "0.0.1-SNAPSHOT");
customizer = customizer.ifAnyResourcesPresent("logback.xml").add(
"ch.qos.logback", "logback-classic", "1.0.7");
customizer = customizer.ifNotAdded("cg.qos.logback", "logback-classic")
.ifAnyResourcesPresent("log4j.properties", "log4j.xml")
.add("org.slf4j", "slf4j-log4j12", "1.7.1")
.add("log4j", "log4j", "1.2.16");
customizer = customizer.ifNotAdded("ch.qos.logback", "logback-classic")
.ifNotAdded("org.slf4j", "slf4j-log4j12")
.add("org.slf4j", "slf4j-jdk14", "1.7.1");
// FIXME get the version
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("javax.sql.DataSource",
"org.springframework.stereotype.Controller",
"org.springframework.stereotype.Service",
"org.springframework.stereotype.Component",
"org.springframework.beans.factory.annotation.Autowired",
"org.springframework.beans.factory.annotation.Value",
"org.springframework.context.annotation.Import",

View File

@ -32,10 +32,10 @@ public class SpringMvcCompilerAutoConfiguration extends CompilerAutoConfiguratio
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifMissingClasses("org.springframework.web.servlet.mvc.Controller")
dependencies.ifAnyMissingClasses("org.springframework.web.servlet.mvc.Controller")
.add("org.springframework", "spring-webmvc", "4.0.0.BOOTSTRAP-SNAPSHOT");
dependencies.ifMissingClasses("org.apache.catalina.startup.Tomcat",
dependencies.ifAnyMissingClasses("org.apache.catalina.startup.Tomcat",
"org.eclipse.jetty.server.Server").add("org.eclipse.jetty",
"jetty-webapp", "8.1.10.v20130312");

View File

@ -29,6 +29,7 @@ import org.springframework.bootstrap.cli.compiler.GroovyCompiler;
* changes.
*
* @author Phillip Webb
* @author Dave Syer
*/
public class BootstrapRunner {
@ -71,11 +72,7 @@ public class BootstrapRunner {
public synchronized void compileAndRun() throws Exception {
try {
// Shutdown gracefully any running container
if (this.runThread != null) {
this.runThread.shutdown();
this.runThread = null;
}
stop();
// Compile
Class<?>[] classes = this.compiler.compile(this.file);
@ -140,7 +137,7 @@ public class BootstrapRunner {
}
/**
* Shutdown the thread, closing any previously opened appplication context.
* Shutdown the thread, closing any previously opened application context.
*/
public synchronized void shutdown() {
if (this.applicationContext != null) {
@ -188,4 +185,12 @@ public class BootstrapRunner {
}
}
public void stop() {
if (this.runThread != null) {
this.runThread.shutdown();
this.runThread = null;
}
}
}

View File

@ -0,0 +1,76 @@
/*
* 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 org.springframework.bootstrap.cli;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* @author Dave Syer
*
*/
public class SampleIntegrationTests {
private RunCommand command;
private void start(final String sample) throws Exception {
Future<RunCommand> future = Executors.newSingleThreadExecutor().submit(
new Callable<RunCommand>() {
@Override
public RunCommand call() throws Exception {
RunCommand command = new RunCommand();
command.run(sample);
return command;
}
});
this.command = future.get(10, TimeUnit.SECONDS);
}
@After
public void stop() {
if (this.command != null) {
this.command.stop();
}
}
@BeforeClass
public static void clean() {
// SpringBootstrapCli.main("clean");
// System.setProperty("ivy.message.logger.level", "4");
}
@Test
public void appSample() throws Exception {
start("samples/app.groovy");
}
@Test
public void webSample() throws Exception {
start("samples/web.groovy");
}
@Test
public void serviceSample() throws Exception {
start("samples/service.groovy");
}
}

View File

@ -0,0 +1,7 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.category.org.springframework.bootstrap=DEBUG

View File

@ -7,7 +7,7 @@
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-trad-sample</artifactId>
<name>Spring Bootstrap Trad Sample</name>
<name>spring-bootstrap-trad-sample</name>
<packaging>war</packaging>
<properties>

View File

@ -15,7 +15,6 @@
*/
package org.springframework.bootstrap.autoconfigure.service;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
@ -41,7 +40,7 @@ public class ManagementAutoConfiguration implements ApplicationContextAware,
private ApplicationContext parent;
private ConfigurableApplicationContext context;
@ConditionalOnExpression("${container.port} == ${container.management_port}")
@ConditionalOnExpression("${container.port:8080} == ${container.management_port:8080}")
@Configuration
@Import({ VarzAutoConfiguration.class, HealthzAutoConfiguration.class })
public static class ManagementEndpointsConfiguration {

View File

@ -15,12 +15,15 @@
*/
package org.springframework.bootstrap.context.annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.ClassMetadata;
/**
* A Condition that evaluates a SpEL expression.
@ -30,6 +33,8 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
*/
public class ExpressionCondition implements Condition {
private static Log logger = LogFactory.getLog(ExpressionCondition.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String value = (String) metadata.getAnnotationAttributes(
@ -38,6 +43,14 @@ public class ExpressionCondition implements Condition {
// For convenience allow user to provide bare expression with no #{} wrapper
value = "#{" + value + "}";
}
if (logger.isDebugEnabled()) {
StringBuilder builder = new StringBuilder("Evaluating expression");
if (metadata instanceof ClassMetadata) {
builder.append(" on " + ((ClassMetadata) metadata).getClassName());
}
builder.append(": " + value);
logger.debug(builder.toString());
}
// Explicitly allow environment placeholders inside the expression
value = context.getEnvironment().resolvePlaceholders(value);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();