Skip to content

Commit dcfa7e1

Browse files
committed
Merge branch '3.1.x'
2 parents 1bf8627 + ef87ee7 commit dcfa7e1

File tree

8 files changed

+212
-19
lines changed

8 files changed

+212
-19
lines changed

grails-console/src/main/groovy/grails/ui/command/GrailsApplicationContextCommandRunner.groovy

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package grails.ui.command
1717

18+
import grails.config.Settings
1819
import grails.dev.commands.ApplicationContextCommandRegistry
1920
import grails.dev.commands.ExecutionContext
2021
import grails.ui.support.DevelopmentGrailsApplication
@@ -44,6 +45,11 @@ class GrailsApplicationContextCommandRunner extends DevelopmentGrailsApplication
4445
def command = ApplicationContextCommandRegistry.findCommand(commandName)
4546
if(command) {
4647

48+
Object skipBootstrap = command.hasProperty("skipBootstrap")?.getProperty(command)
49+
if (skipBootstrap instanceof Boolean && !System.getProperty(Settings.SETTING_SKIP_BOOTSTRAP)) {
50+
System.setProperty(Settings.SETTING_SKIP_BOOTSTRAP, skipBootstrap.toString())
51+
}
52+
4753
ConfigurableApplicationContext ctx = null
4854
try {
4955
ctx = super.run(args)

grails-core/src/main/groovy/grails/config/Settings.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,5 +210,9 @@ interface Settings {
210210
* Whether to use the legacy JSON builder
211211
*/
212212
String SETTING_LEGACY_JSON_BUILDER = "grails.json.legacy.builder";
213+
/**
214+
* Whether to execute Bootstrap classes
215+
*/
216+
String SETTING_SKIP_BOOTSTRAP = "grails.bootstrap.skip";
213217

214218
}

grails-core/src/main/groovy/org/grails/transaction/transform/TransactionalTransform.groovy

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class TransactionalTransform implements ASTTransformation{
7575
private static final String METHOD_EXECUTE = "execute"
7676
private static final Set<String> METHOD_NAME_EXCLUDES = new HashSet<String>(Arrays.asList("afterPropertiesSet", "destroy"));
7777
private static final Set<String> ANNOTATION_NAME_EXCLUDES = new HashSet<String>(Arrays.asList(PostConstruct.class.getName(), PreDestroy.class.getName(), Transactional.class.getName(), Rollback.class.getName(), "grails.web.controllers.ControllerMethod", NotTransactional.class.getName()));
78+
private static final Set<String> JUNIT_ANNOTATION_NAMES = new HashSet<String>(Arrays.asList("org.junit.Before", "org.junit.After"));
7879
private static final String SPEC_CLASS = "spock.lang.Specification";
7980
public static final String PROPERTY_DATA_SOURCE = "datasource"
8081

@@ -116,7 +117,8 @@ class TransactionalTransform implements ASTTransformation{
116117
for (MethodNode md in methods) {
117118
String methodName = md.getName()
118119
int modifiers = md.modifiers
119-
if (!md.isSynthetic() && Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers) && !Modifier.isStatic(modifiers)) {
120+
if (!md.isSynthetic() && Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers) &&
121+
!Modifier.isStatic(modifiers) && !hasJunitAnnotation(md)) {
120122
if(hasExcludedAnnotation(md)) continue
121123

122124
def startsWithSpock = methodName.startsWith('$spock')
@@ -138,7 +140,8 @@ class TransactionalTransform implements ASTTransformation{
138140
if(hasAnnotation(md, DelegatingMethod.class)) continue
139141
weaveTransactionalMethod(source, classNode, annotationNode, md);
140142
}
141-
else if(("setup".equals(methodName) || "cleanup".equals(methodName)) && isSpockTest(classNode)) {
143+
else if ((("setup".equals(methodName) || "cleanup".equals(methodName)) && isSpockTest(classNode)) ||
144+
hasJunitAnnotation(md)) {
142145
def requiresNewTransaction = new AnnotationNode(annotationNode.classNode)
143146
requiresNewTransaction.addMember("propagation", new PropertyExpression(new ClassExpression(ClassHelper.make(Propagation.class)), "REQUIRES_NEW"))
144147
weaveTransactionalMethod(source, classNode, requiresNewTransaction, md, "execute")
@@ -161,6 +164,17 @@ class TransactionalTransform implements ASTTransformation{
161164
excludedAnnotation
162165
}
163166

167+
private boolean hasJunitAnnotation(MethodNode md) {
168+
boolean excludedAnnotation = false;
169+
for (AnnotationNode annotation : md.getAnnotations()) {
170+
if(JUNIT_ANNOTATION_NAMES.contains(annotation.getClassNode().getName())) {
171+
excludedAnnotation = true;
172+
break;
173+
}
174+
}
175+
excludedAnnotation
176+
}
177+
164178
ClassNode getAnnotationClassNode(String annotationName) {
165179
try {
166180
final classLoader = Thread.currentThread().contextClassLoader

grails-core/src/test/groovy/grails/transaction/TransactionalTransformSpec.groovy

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,46 @@ class TransactionalTransformSpec extends Specification {
130130

131131
}
132132

133+
void "Test @Rollback when applied to JUnit specifications"() {
134+
when:
135+
Class mySpec = new GroovyShell().evaluate('''
136+
import grails.transaction.*
137+
import org.junit.Test
138+
import org.junit.Before
139+
import org.junit.After
140+
141+
@Rollback
142+
class MyJunitTest {
143+
@Before
144+
def junitSetup() {
145+
146+
}
147+
148+
@After
149+
def junitCleanup() {
150+
151+
}
152+
153+
@Test
154+
void junitTest() {
155+
expect:
156+
1 == 1
157+
}
158+
}
159+
MyJunitTest
160+
''')
161+
162+
then: "It implements TransactionManagerAware"
163+
TransactionManagerAware.isAssignableFrom(mySpec)
164+
mySpec.getDeclaredMethod('junitSetup')
165+
mySpec.getDeclaredMethod('$tt__junitSetup', TransactionStatus)
166+
mySpec.getDeclaredMethod('junitCleanup')
167+
mySpec.getDeclaredMethod('$tt__junitCleanup', TransactionStatus)
168+
169+
mySpec.getDeclaredMethod('junitTest')
170+
mySpec.getDeclaredMethod('$tt__junitTest', TransactionStatus)
171+
}
172+
133173
void "Test @Rollback when applied to Spock specifications"() {
134174
when: "A new instance of a class with a @Transactional method is created that subclasses another transactional class"
135175
Class mySpec = new GroovyShell().evaluate('''

grails-gradle-plugin/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import org.gradle.api.artifacts.ConfigurationContainer
3434
import org.gradle.api.artifacts.Dependency
3535
import org.gradle.api.artifacts.DependencyResolveDetails
3636
import org.gradle.api.file.CopySpec
37+
import org.gradle.api.file.FileCollection
3738
import org.gradle.api.java.archives.Manifest
3839
import org.gradle.api.plugins.GroovyPlugin
3940
import org.gradle.api.plugins.WarPlugin
@@ -494,7 +495,7 @@ class GrailsGradlePlugin extends GroovyPlugin {
494495

495496
protected void configurePathingJar(Project project) {
496497
project.afterEvaluate {
497-
ConfigurationContainer configurations = project.configurations
498+
ConfigurationContainer configurations = project.configurations
498499
Configuration runtime = configurations.getByName('runtime')
499500
Configuration console = configurations.getByName('console')
500501

@@ -512,25 +513,28 @@ class GrailsGradlePlugin extends GroovyPlugin {
512513
}
513514

514515
Jar pathingJar = createPathingJarTask(project, "pathingJar", runtime)
516+
FileCollection pathingClasspath = project.files("${project.buildDir}/classes/main",
517+
"${project.buildDir}/resources/main", "${project.projectDir}/gsp-classes", pathingJar.archivePath)
515518
Jar pathingJarCommand = createPathingJarTask(project, "pathingJarCommand", runtime, console)
519+
FileCollection pathingClasspathCommand = project.files("${project.buildDir}/classes/main",
520+
"${project.buildDir}/resources/main", "${project.projectDir}/gsp-classes", pathingJarCommand.archivePath)
516521

517522
GrailsExtension grailsExt = project.extensions.getByType(GrailsExtension)
518523

519524
if (grailsExt.pathingJar && Os.isFamily(Os.FAMILY_WINDOWS)) {
520-
521-
TaskContainer tasks = project.tasks
522-
523-
tasks.withType(JavaExec) { JavaExec task ->
524-
task.dependsOn(pathingJar)
525-
task.doFirst {
526-
classpath = project.files("${project.buildDir}/classes/main", "${project.buildDir}/resources/main", "${project.projectDir}/gsp-classes", pathingJar.archivePath)
525+
project.tasks.withType(JavaExec) { JavaExec task ->
526+
if (task.name in ['console', 'shell'] || task instanceof ApplicationContextCommandTask) {
527+
task.dependsOn(pathingJarCommand)
528+
task.doFirst {
529+
classpath = pathingClasspathCommand
530+
}
531+
} else {
532+
task.dependsOn(pathingJar)
533+
task.doFirst {
534+
classpath = pathingClasspath
535+
}
527536
}
528537
}
529-
530-
tasks.withType(ApplicationContextCommandTask) { ApplicationContextCommandTask task ->
531-
task.dependsOn(pathingJarCommand)
532-
task.systemProperties = ['grails.env': 'development']
533-
}
534538
}
535539
}
536540
}

grails-plugin-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersGrailsPlugin.groovy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ class ControllersGrailsPlugin extends Plugin {
8484
boolean resourcesEnabled = config.getProperty(Settings.RESOURCES_ENABLED, Boolean, true)
8585
String resourcesPattern = config.getProperty(Settings.RESOURCES_PATTERN, String, Settings.DEFAULT_RESOURCE_PATTERN)
8686

87-
bootStrapClassRunner(BootStrapClassRunner)
87+
if (!Boolean.parseBoolean(System.getProperty(Settings.SETTING_SKIP_BOOTSTRAP))) {
88+
bootStrapClassRunner(BootStrapClassRunner)
89+
}
90+
8891
tokenResponseActionResultTransformer(TokenResponseActionResultTransformer)
8992

9093
def catchAllMapping = [Settings.DEFAULT_WEB_SERVLET_PATH]

grails-shell/src/main/groovy/org/grails/cli/profile/commands/CreateAppCommand.groovy

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,22 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
314314
}
315315

316316
protected Iterable<Feature> evaluateFeatures(Profile profile, CommandLine commandLine) {
317-
def requestedFeatures = commandLine.optionValue("features")?.toString()?.split(',')
317+
String[] requestedFeatures = commandLine.optionValue("features")?.toString()?.split(',')
318318
if(requestedFeatures) {
319-
def featureNames = Arrays.asList(requestedFeatures)
320-
return (profile.features.findAll() { Feature f -> featureNames.contains(f.name)} + profile.requiredFeatures).unique()
319+
List<String> requestedFeaturesList = requestedFeatures.toList()
320+
List<String> allFeatureNames = profile.features*.name
321+
List<String> validFeatureNames = requestedFeaturesList.intersect(allFeatureNames)
322+
requestedFeaturesList.removeAll(allFeatureNames)
323+
requestedFeaturesList.each { String invalidFeature ->
324+
List possibleSolutions = allFeatureNames.findAll { it.substring(0, 2) == invalidFeature.substring(0, 2) }
325+
StringBuilder warning = new StringBuilder("Feature ${invalidFeature} does not exist in the profile ${profile.name}!")
326+
if (possibleSolutions) {
327+
warning.append(" Possible solutions: ")
328+
warning.append(possibleSolutions.join(", "))
329+
}
330+
GrailsConsole.getInstance().warn(warning.toString())
331+
}
332+
return (profile.features.findAll() { Feature f -> validFeatureNames.contains(f.name) } + profile.requiredFeatures).unique()
321333
}
322334
else {
323335
return (profile.defaultFeatures + profile.requiredFeatures).unique()
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package org.grails.cli.profile.commands
2+
3+
import grails.build.logging.GrailsConsole
4+
import org.grails.build.parsing.CommandLine
5+
import org.grails.build.parsing.DefaultCommandLine
6+
import org.grails.cli.profile.Feature
7+
import org.grails.cli.profile.Profile
8+
import org.spockframework.util.StringMessagePrintStream
9+
import spock.lang.Shared
10+
import spock.lang.Specification
11+
12+
/**
13+
* Created by Jim on 7/18/2016.
14+
*/
15+
class CreateAppCommandSpec extends Specification {
16+
17+
@Shared
18+
StringPrintStream sps
19+
20+
PrintStream originalOut
21+
22+
void setup() {
23+
originalOut = GrailsConsole.instance.out
24+
sps = new StringPrintStream()
25+
GrailsConsole.instance.out = sps
26+
}
27+
28+
void cleanup() {
29+
GrailsConsole.instance.out = originalOut
30+
}
31+
32+
void "test evaluateFeatures - multiple, some valid"() {
33+
given:
34+
Feature bar = Mock(Feature) {
35+
2 * getName() >> "bar"
36+
}
37+
Profile profile = Mock(Profile) {
38+
1 * getName() >> "web"
39+
2 * getFeatures() >> [bar]
40+
1 * getRequiredFeatures() >> []
41+
}
42+
CommandLine commandLine = new DefaultCommandLine().parseNew(["create-app", "foo", "-features=foo,bar"] as String[])
43+
44+
when:
45+
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, commandLine)
46+
47+
then:
48+
features.size() == 1
49+
features[0] == bar
50+
sps.toString() == "Warning |\nFeature foo does not exist in the profile web!\n"
51+
}
52+
53+
void "test evaluateFeatures - multiple, all valid"() {
54+
given:
55+
Feature foo = Mock(Feature) {
56+
2 * getName() >> "foo"
57+
}
58+
Feature bar = Mock(Feature) {
59+
2 * getName() >> "bar"
60+
}
61+
Profile profile = Mock(Profile) {
62+
0 * getName()
63+
2 * getFeatures() >> [foo, bar]
64+
1 * getRequiredFeatures() >> []
65+
}
66+
CommandLine commandLine = new DefaultCommandLine().parseNew(["create-app", "foo", "-features=foo,bar"] as String[])
67+
68+
when:
69+
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, commandLine)
70+
71+
then:
72+
features.size() == 2
73+
features[0] == foo
74+
features[1] == bar
75+
sps.toString() == ""
76+
}
77+
78+
void "test evaluateFeatures fat finger"() {
79+
given:
80+
Feature bar = Mock(Feature) {
81+
2 * getName() >> "mongodb"
82+
}
83+
Profile profile = Mock(Profile) {
84+
1 * getName() >> "web"
85+
2 * getFeatures() >> [bar]
86+
1 * getRequiredFeatures() >> []
87+
}
88+
CommandLine commandLine = new DefaultCommandLine().parseNew(["create-app", "foo", "-features=mongo"] as String[])
89+
90+
when:
91+
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, commandLine)
92+
93+
94+
then:
95+
features.size() == 0
96+
sps.toString() == "Warning |\nFeature mongo does not exist in the profile web! Possible solutions: mongodb\n"
97+
}
98+
99+
class StringPrintStream extends StringMessagePrintStream {
100+
StringBuilder stringBuilder = new StringBuilder()
101+
@Override
102+
protected void printed(String message) {
103+
stringBuilder.append(message)
104+
}
105+
106+
String toString() {
107+
stringBuilder.toString()
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)