Skip to content

Commit 2e64a07

Browse files
committed
HHH-19519: Document breaking changes in new Hibernate Maven Plugin
- Rename 'HibernateEnhancerMojo' to 'EnhancerMojo' - Rename 'HibernateEnhancerMojoTest' to 'EnhancerMojoTest' - Add integration test 'HibernateEnhancerMojoTestIT' to test the different configuration options - Make sure that the enhancer is not doing anything if all the enablement parameters are false Signed-off-by: Koen Aers <[email protected]>
1 parent 6ebf347 commit 2e64a07

File tree

8 files changed

+597
-114
lines changed

8 files changed

+597
-114
lines changed

tooling/hibernate-maven-plugin/hibernate-maven-plugin.gradle

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,33 @@ plugins {
1212

1313
description = 'Maven plugin to integrate aspects of Hibernate into your build.'
1414

15+
sourceSets {
16+
intTest {
17+
compileClasspath += sourceSets.main.output
18+
runtimeClasspath += sourceSets.main.output
19+
}
20+
}
21+
22+
configurations {
23+
intTestImplementation.extendsFrom implementation
24+
intTestRuntimeOnly.extendsFrom runtimeOnly
25+
}
26+
1527
dependencies {
1628
implementation project( ":hibernate-core" )
1729

18-
implementation "org.apache.maven:maven-plugin-api:3.6.3"
30+
implementation "org.apache.maven:maven-plugin-api:3.9.11"
1931
implementation "org.apache.maven:maven-project:2.2.1"
2032
implementation "org.apache.maven.shared:file-management:3.1.0"
2133

22-
compileOnly "org.apache.maven.plugin-tools:maven-plugin-annotations:3.6.0"
34+
compileOnly "org.apache.maven.plugin-tools:maven-plugin-tools-annotations:3.15.1"
35+
36+
intTestImplementation 'org.junit.jupiter:junit-jupiter:5.13.4'
37+
intTestImplementation 'org.apache.maven:maven-embedder:3.9.11'
38+
intTestRuntimeOnly 'org.junit.platform:junit-platform-launcher'
39+
intTestRuntimeOnly 'ch.qos.logback:logback-classic:1.5.18'
40+
intTestRuntimeOnly 'org.apache.maven:maven-compat:3.9.11'
41+
2342
}
2443

2544
def releasePrepareTask = tasks.register("releasePrepare") {
@@ -37,6 +56,25 @@ tasks.register("preVerifyRelease") {
3756
dependsOn releasePrepareTask
3857
}
3958

59+
tasks.register('integrationTest', Test) {
60+
description = 'Runs integration tests.'
61+
group = 'verification'
62+
63+
testClassesDirs = sourceSets.intTest.output.classesDirs
64+
classpath = sourceSets.intTest.runtimeClasspath
65+
shouldRunAfter test
66+
67+
useJUnitPlatform()
68+
69+
testLogging {
70+
events "passed"
71+
}
72+
}
73+
74+
tasks.forbiddenApisIntTest {
75+
enabled = false
76+
}
77+
4078
var publishingExtension = project.getExtensions().getByType(PublishingExtension) as PublishingExtension
4179
publishingExtension.publications.named("publishedArtifacts") {
4280
from components.java
@@ -64,3 +102,11 @@ publishingExtension.publications.named("publishedArtifacts") {
64102
}
65103
}
66104

105+
integrationTest {
106+
environment "hibernateVersion", project.version
107+
}
108+
109+
integrationTest.dependsOn rootProject.childProjects.'hibernate-core'.tasks.publishToMavenLocal
110+
integrationTest.dependsOn publishToMavenLocal
111+
check.dependsOn integrationTest
112+
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.tooling.maven;
6+
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
import static org.junit.jupiter.api.Assertions.assertFalse;
9+
10+
import org.apache.maven.cli.MavenCli;
11+
import org.hibernate.bytecode.enhance.spi.EnhancementInfo;
12+
import org.junit.jupiter.api.BeforeEach;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.io.TempDir;
15+
16+
import java.io.File;
17+
import java.lang.reflect.Field;
18+
import java.lang.reflect.Method;
19+
import java.net.URL;
20+
import java.net.URLClassLoader;
21+
import java.nio.file.Files;
22+
import java.util.List;
23+
24+
public class EnhancerMojoTestIT {
25+
26+
public static final String MVN_HOME = "maven.multiModuleProjectDirectory";
27+
28+
@TempDir
29+
File projectDir;
30+
31+
private MavenCli mavenCli;
32+
33+
@BeforeEach
34+
public void beforeEach() throws Exception {
35+
copyJavFiles();
36+
System.setProperty(MVN_HOME, projectDir.getAbsolutePath());
37+
mavenCli = new MavenCli();
38+
}
39+
40+
@Test
41+
public void testEnhancementDefault() throws Exception {
42+
// The default configuration for the enhance goal are as follows:
43+
// enableLazyInitialization = 'true'
44+
// enableDirtyTracking = 'true'
45+
// enableAssociationManagement = 'false'
46+
// enableExtendedEnhancement = 'false'
47+
// classesDirectory = 'target/classes'
48+
String configurationElement = "<configuration/>\n";
49+
preparePomXml(configurationElement);
50+
executeCompileGoal();
51+
executeEnhanceGoal();
52+
// Both Bar and Baz should be enhanced
53+
assertTrue(isEnhanced( "Bar" ));
54+
assertTrue(isEnhanced( "Baz" ));
55+
// Both Bar and Baz contain the method '$$_hibernate_getInterceptor'
56+
// because of the default setting of 'enableLazyInitialization'
57+
assertTrue( methodIsPresentInClass("$$_hibernate_getInterceptor", "Bar"));
58+
assertTrue( methodIsPresentInClass("$$_hibernate_getInterceptor", "Baz"));
59+
// Both Bar and Baz contain the method '$$_hibernate_hasDirtyAttributes'
60+
// because of the default setting of 'enableDirtyTracking'
61+
assertTrue( methodIsPresentInClass("$$_hibernate_hasDirtyAttributes", "Bar"));
62+
assertTrue( methodIsPresentInClass("$$_hibernate_hasDirtyAttributes", "Baz"));
63+
// Foo is not an entity and extended enhancement is not enabled so the class is not enhanced
64+
assertFalse(isEnhanced("Foo"));
65+
// Association management should not be present
66+
assertFalse(isAssociationManagementPresent());
67+
}
68+
69+
@Test
70+
public void testEnhancementFileSet() throws Exception {
71+
// Use the defaults settings for enhancement (see #testEnhancementDefault)
72+
// The files are read from the specified 'fileset' element:
73+
// - the folder specified by 'dir'
74+
// - the 'Baz.class' file is excluded
75+
String configurationElement =
76+
"<configuration>\n" +
77+
" <fileSets>\n"+
78+
" <fileset>\n" +
79+
" <directory>" + projectDir.getAbsolutePath() + "/target" + "</directory>\n" +
80+
" <excludes>\n" +
81+
" <exclude>**/Baz.class</exclude>\n" +
82+
" </excludes>\n" +
83+
" </fileset>\n" +
84+
" </fileSets>\n" +
85+
"</configuration>\n";
86+
preparePomXml(configurationElement);
87+
executeCompileGoal();
88+
executeEnhanceGoal();
89+
// Bar is enhanced
90+
assertTrue(isEnhanced( "Bar" ));
91+
// Baz is not enhanced because it was excluded from the file set
92+
assertFalse(isEnhanced( "Baz" ));
93+
// Foo is not enhanced because it is not an entity and extended enhancement was not enabled
94+
assertFalse( isEnhanced( "Foo" ) );
95+
// Association management should not be present
96+
assertFalse(isAssociationManagementPresent());
97+
}
98+
99+
@Test
100+
public void testEnhancementNoLazyInitialization() throws Exception {
101+
// Change the default setting for 'enableLazyInitialization' to 'false'
102+
// Otherwise use the settings of #testEnhancementDefault
103+
String configurationElement =
104+
"<configuration>\n" +
105+
" <enableLazyInitialization>false</enableLazyInitialization>\n"+
106+
"</configuration>\n";
107+
preparePomXml(configurationElement);
108+
executeCompileGoal();
109+
executeEnhanceGoal();
110+
// Both Bar and Baz are enhanced, Foo is not
111+
assertTrue( isEnhanced( "Bar" ));
112+
assertTrue( isEnhanced( "Baz" ));
113+
// Foo is not enhanced because it is not an entity and extended enhancement was not enabled
114+
assertFalse( isEnhanced( "Foo" ) );
115+
// but $$_hibernate_getInterceptor is not present in the enhanced classes
116+
// because of the 'false' value of 'enableLazyInitialization'
117+
assertFalse( methodIsPresentInClass("$$_hibernate_getInterceptor", "Bar"));
118+
assertFalse( methodIsPresentInClass("$$_hibernate_getInterceptor", "Baz"));
119+
// Association management should not be present
120+
assertFalse(isAssociationManagementPresent());
121+
}
122+
123+
@Test
124+
public void testEnhancementNoDirtyTracking() throws Exception {
125+
// Change the default setting for 'enableDirtyTracking' to 'false'
126+
// Otherwise use the settings of #testEnhancementDefault
127+
String configurationElement =
128+
"<configuration>\n" +
129+
" <enableDirtyTracking>false</enableDirtyTracking>\n"+
130+
"</configuration>\n";
131+
preparePomXml(configurationElement);
132+
executeCompileGoal();
133+
executeEnhanceGoal();
134+
// Both Bar and Baz should be enhanced
135+
assertTrue( isEnhanced( "Bar" ));
136+
assertTrue( isEnhanced( "Baz" ));
137+
// Foo is not enhanced because it is not an entity and extended enhancement was not enabled
138+
assertFalse( isEnhanced( "Foo" ) );
139+
// $$_hibernate_hasDirtyAttributes is not present in the enhanced classes
140+
// because of the 'false' value of 'enableLazyInitialization'
141+
assertFalse( methodIsPresentInClass("$$_hibernate_hasDirtyAttributes", "Bar"));
142+
assertFalse( methodIsPresentInClass("$$_hibernate_hasDirtyAttributes", "Baz"));
143+
// Association management should not be present
144+
assertFalse(isAssociationManagementPresent());
145+
}
146+
147+
@Test
148+
public void testEnhancementEnableAssociationManagement() throws Exception {
149+
// Change the default setting for 'enableAssociationManagement' to 'true'
150+
// Otherwise use the settings of #testEnhancementDefault
151+
String configurationElement =
152+
"<configuration>\n" +
153+
" <enableAssociationManagement>true</enableAssociationManagement>\n"+
154+
"</configuration>\n";
155+
preparePomXml(configurationElement);
156+
executeCompileGoal();
157+
executeEnhanceGoal();
158+
// Both Bar and Baz are enhanced, Foo is not
159+
assertTrue( isEnhanced( "Bar" ));
160+
assertTrue( isEnhanced( "Baz" ));
161+
assertFalse( isEnhanced( "Foo" ) );
162+
// Now verify that the association management is in place;
163+
assertTrue(isAssociationManagementPresent());
164+
}
165+
166+
@Test
167+
public void testEnhancementEnableExtendedEnhancement() throws Exception {
168+
// Change the default setting for 'enableExtendedEnhancement' to 'true'
169+
// Otherwise use the settings of #testEnhancementDefault
170+
String configurationElement =
171+
"<configuration>\n" +
172+
" <enableExtendedEnhancement>true</enableExtendedEnhancement>\n"+
173+
"</configuration>\n";
174+
preparePomXml(configurationElement);
175+
executeCompileGoal();
176+
executeEnhanceGoal();
177+
// Both Bar and Baz are enhanced because they are entities
178+
assertTrue( isEnhanced( "Bar" ));
179+
assertTrue( isEnhanced( "Baz" ));
180+
// Though Foo is not an entity, it is enhanced because of the setting of 'enableExtendedEnhancement'
181+
assertTrue( isEnhanced( "Foo" ) );
182+
// No association management is in place;
183+
assertFalse(isAssociationManagementPresent());
184+
}
185+
186+
187+
@Test
188+
public void testNoEnhancement() throws Exception {
189+
// Setting the values of all the settings to 'false' has the effect
190+
// of not executing the enhancement at all.
191+
// The setting of 'enableAssociationManagement' and 'enableExtendedEnhancement' to
192+
// false is not really needed in this case as that's what their default is
193+
String configurationElement =
194+
"<configuration>\n" +
195+
" <enableLazyInitialization>false</enableLazyInitialization>\n"+
196+
" <enableDirtyTracking>false</enableDirtyTracking>\n"+
197+
" <enableAssociationManagement>false</enableAssociationManagement>\n"+
198+
" <enableExtendedEnhancement>false</enableExtendedEnhancement>\n"+
199+
"</configuration>\n";
200+
preparePomXml(configurationElement);
201+
executeCompileGoal();
202+
executeEnhanceGoal();
203+
// None of the classes should be enhanced
204+
assertFalse( isEnhanced( "Bar" ));
205+
assertFalse( isEnhanced( "Baz" ));
206+
assertFalse( isEnhanced( "Foo" ) );
207+
// No association management is in place;
208+
assertFalse(isAssociationManagementPresent());
209+
}
210+
211+
private void executeCompileGoal() {
212+
// The class files should not exist
213+
assertFalse(fileExists("target/classes/Bar.class"));
214+
assertFalse(fileExists("target/classes/Baz.class"));
215+
assertFalse(fileExists("target/classes/Foo.class"));
216+
// Execute the 'compile' target
217+
new MavenCli().doMain(
218+
new String[]{"compile"},
219+
projectDir.getAbsolutePath(),
220+
null,
221+
null);
222+
// The class files should exist now
223+
assertTrue( fileExists( "target/classes/Bar.class" ) );
224+
assertTrue( fileExists( "target/classes/Baz.class" ) );
225+
assertTrue( fileExists( "target/classes/Foo.class" ) );
226+
}
227+
228+
private void executeEnhanceGoal() throws Exception {
229+
// The class files should not be enhanced at this point
230+
assertFalse( isEnhanced( "Bar" ));
231+
assertFalse( isEnhanced( "Baz" ));
232+
assertFalse( isEnhanced( "Foo" ));
233+
// Execute the 'enhance' target
234+
mavenCli.doMain(
235+
new String[]{"process-classes"},
236+
projectDir.getAbsolutePath(),
237+
null,
238+
null);
239+
// The results are verified in the respective tests
240+
}
241+
242+
private void preparePomXml(String configurationElement) throws Exception {
243+
URL url = getClass().getClassLoader().getResource("pom.xm_");
244+
File source = new File(url.toURI());
245+
assertFalse( fileExists( "pom.xml" ));
246+
String pomXmlContents = new String(Files.readAllBytes( source.toPath() ));
247+
pomXmlContents = pomXmlContents.replace( "@hibernate-version@", System.getenv("hibernateVersion"));
248+
pomXmlContents = pomXmlContents.replace( "@configuration@", configurationElement);
249+
File destination = new File(projectDir, "pom.xml");
250+
Files.writeString(destination.toPath(), pomXmlContents);
251+
assertTrue( fileExists( "pom.xml" ) );
252+
}
253+
254+
private void copyJavFiles() throws Exception {
255+
File srcDir = new File(projectDir, "src/main/java");
256+
srcDir.mkdirs();
257+
String[] javFileNames = {"Bar.jav_", "Baz.jav_", "Foo.jav_"};
258+
for (String javFileName : javFileNames) {
259+
copyJavFile( javFileName, srcDir );
260+
}
261+
}
262+
263+
private void copyJavFile(String javFileName, File toFolder) throws Exception {
264+
URL url = getClass().getClassLoader().getResource( javFileName );
265+
assert url != null;
266+
File source = new File(url.toURI());
267+
File destination = new File(toFolder, javFileName.replace( '_', 'a' ));
268+
assertTrue(source.exists());
269+
assertTrue(source.isFile());
270+
Files.copy(source.toPath(), destination.toPath());
271+
assertTrue(destination.exists());
272+
assertTrue(destination.isFile());
273+
}
274+
275+
private ClassLoader getTestClassLoader() throws Exception {
276+
return new URLClassLoader( new URL[] { new File(projectDir, "target/classes").toURI().toURL() } );
277+
}
278+
279+
private boolean isEnhanced(String className) throws Exception {
280+
return getTestClassLoader().loadClass( className ).isAnnotationPresent( EnhancementInfo.class );
281+
}
282+
283+
private boolean methodIsPresentInClass(String methodName, String className) throws Exception {
284+
Class<?> classToCheck = getTestClassLoader().loadClass( className );
285+
try {
286+
Object m = classToCheck.getMethod( methodName, new Class[] {} );
287+
return true;
288+
} catch (NoSuchMethodException e) {
289+
return false;
290+
}
291+
}
292+
293+
private boolean isAssociationManagementPresent() throws Exception {
294+
// Some dynamic programming
295+
ClassLoader loader = getTestClassLoader();
296+
// Obtain the class objects for 'Baz' and 'Bar'
297+
Class<?> bazClass = loader.loadClass( "Baz" );
298+
Class<?> barClass = loader.loadClass( "Bar" );
299+
// Create an instance of both 'Baz' and 'Bar'
300+
Object bazObject = bazClass.getDeclaredConstructor().newInstance();
301+
Object barObject = barClass.getDeclaredConstructor().newInstance();
302+
// Lookup the 'bars' field of class 'Baz' (an ArrayList of 'Bar' objects)
303+
Field bazBarsField = bazClass.getDeclaredField( "bars" );
304+
bazBarsField.setAccessible( true );
305+
// Obtain the 'bars' list of the 'Baz' object; it should be empty
306+
List<?> bazBarsList = (List<?>) bazBarsField.get( bazObject ); // baz.bars
307+
assertTrue(bazBarsList.isEmpty());
308+
// Lookup the 'setBaz' method of class 'Bar' and invoke it on the 'Bar' object
309+
Method barSetBazMethod = barClass.getDeclaredMethod( "setBaz", new Class[] { bazClass } );
310+
barSetBazMethod.invoke( barObject, bazObject ); // bar.setBaz(baz)
311+
// Reobtain the 'bars' list of the 'Baz' object
312+
bazBarsList = (List<?>) bazBarsField.get( bazObject );
313+
// If there is association management, the 'bars' list should contain the 'Bar' object
314+
return bazBarsList.contains( barObject ); // baz.bars.contains(bar)
315+
}
316+
317+
private boolean fileExists(String relativePath) {
318+
return new File( projectDir, relativePath ).exists();
319+
}
320+
321+
}

0 commit comments

Comments
 (0)