diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle index ab3342725dbc..678e848e382a 100644 --- a/documentation/documentation.gradle +++ b/documentation/documentation.gradle @@ -137,7 +137,7 @@ dependencies { reportAggregation project(':hibernate-spatial') reportAggregation project(':hibernate-vibur') reportAggregation project(':hibernate-ant') - reportAggregation project(':hibernate-enhance-maven-plugin') + reportAggregation project(':hibernate-maven-plugin') reportAggregation project(':hibernate-processor') asciidoctorGems 'rubygems:rouge:4.1.1' diff --git a/gradle/published-java-module.gradle b/gradle/published-java-module.gradle index b9ea71617b22..be7d9a156853 100644 --- a/gradle/published-java-module.gradle +++ b/gradle/published-java-module.gradle @@ -33,71 +33,6 @@ java { withSourcesJar() } -publishing { - publications { - // main publication - publishedArtifacts { - from components.java - } - - // relocation for the published artifacts based on the old groupId - relocationPom( MavenPublication ) { - pom { - name = project.name + ' - relocation' - groupId = 'org.hibernate' - artifactId = project.name - version = project.version - - description = project.description - url = 'https://hibernate.org/orm' - - organization { - name = 'Hibernate.org' - url = 'https://hibernate.org' - } - - licenses { - license { - name = 'GNU Library General Public License v2.1 or later' - url = 'https://www.opensource.org/licenses/LGPL-2.1' - comments = 'See discussion at https://hibernate.org/community/license/ for more details.' - distribution = 'repo' - } - } - - scm { - url = 'https://github.com/hibernate/hibernate-orm' - connection = 'scm:git:https://github.com/hibernate/hibernate-orm.git' - developerConnection = 'scm:git:git@github.com:hibernate/hibernate-orm.git' - } - - developers { - developer { - id = 'hibernate-team' - name = 'The Hibernate Development Team' - organization = 'Hibernate.org' - organizationUrl = 'https://hibernate.org' - } - } - - issueManagement { - system = 'jira' - url = 'https://hibernate.atlassian.net/browse/HHH' - } - - distributionManagement { - relocation { - groupId = 'org.hibernate.orm' - artifactId = project.name - version = project.version - } - } - } - } - } -} - - var signingKey = resolveSigningKey() var signingPassword = findSigningProperty( "signingPassword" ) @@ -237,7 +172,6 @@ tasks.release.dependsOn tasks.test, tasks.publishToSonatype tasks.preVerifyRelease.dependsOn build tasks.preVerifyRelease.dependsOn generateMetadataFileForPublishedArtifactsPublication tasks.preVerifyRelease.dependsOn generatePomFileForPublishedArtifactsPublication -tasks.preVerifyRelease.dependsOn generatePomFileForRelocationPomPublication tasks.publishToSonatype.mustRunAfter test diff --git a/gradle/relocated-published-java-module.gradle b/gradle/relocated-published-java-module.gradle new file mode 100644 index 000000000000..8aeee228a403 --- /dev/null +++ b/gradle/relocated-published-java-module.gradle @@ -0,0 +1,73 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +apply from: rootProject.file( 'gradle/published-java-module.gradle' ) + +publishing { + publications { + // main publication + publishedArtifacts { + from components.java + } + + // relocation for the published artifacts based on the old groupId + relocationPom( MavenPublication ) { + pom { + name = project.name + ' - relocation' + groupId = 'org.hibernate' + artifactId = project.name + version = project.version + + description = project.description + url = 'https://hibernate.org/orm' + + organization { + name = 'Hibernate.org' + url = 'https://hibernate.org' + } + + licenses { + license { + name = 'GNU Library General Public License v2.1 or later' + url = 'https://www.opensource.org/licenses/LGPL-2.1' + comments = 'See discussion at https://hibernate.org/community/license/ for more details.' + distribution = 'repo' + } + } + + scm { + url = 'https://github.com/hibernate/hibernate-orm' + connection = 'scm:git:https://github.com/hibernate/hibernate-orm.git' + developerConnection = 'scm:git:git@github.com:hibernate/hibernate-orm.git' + } + + developers { + developer { + id = 'hibernate-team' + name = 'The Hibernate Development Team' + organization = 'Hibernate.org' + organizationUrl = 'https://hibernate.org' + } + } + + issueManagement { + system = 'jira' + url = 'https://hibernate.atlassian.net/browse/HHH' + } + + distributionManagement { + relocation { + groupId = 'org.hibernate.orm' + artifactId = project.name + version = project.version + } + } + } + } + } +} + +tasks.preVerifyRelease.dependsOn generatePomFileForRelocationPomPublication \ No newline at end of file diff --git a/hibernate-agroal/hibernate-agroal.gradle b/hibernate-agroal/hibernate-agroal.gradle index 54ae8107dea3..f5534b84e453 100644 --- a/hibernate-agroal/hibernate-agroal.gradle +++ b/hibernate-agroal/hibernate-agroal.gradle @@ -7,7 +7,7 @@ description = 'Integration for Agroal as a ConnectionProvider for Hibernate ORM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/hibernate-c3p0/hibernate-c3p0.gradle b/hibernate-c3p0/hibernate-c3p0.gradle index b5bce3e18853..1ee183146ac3 100644 --- a/hibernate-c3p0/hibernate-c3p0.gradle +++ b/hibernate-c3p0/hibernate-c3p0.gradle @@ -7,7 +7,7 @@ description = 'Integration for c3p0 Connection pooling into Hibernate ORM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/hibernate-community-dialects/hibernate-community-dialects.gradle b/hibernate-community-dialects/hibernate-community-dialects.gradle index aa5b903582ff..df3ca41efd9c 100644 --- a/hibernate-community-dialects/hibernate-community-dialects.gradle +++ b/hibernate-community-dialects/hibernate-community-dialects.gradle @@ -7,7 +7,7 @@ description = 'Hibernate\'s community supported dialects' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { api project( ':hibernate-core' ) diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index d3b1634a216f..62b237fcec19 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -12,7 +12,7 @@ plugins { description = 'Hibernate\'s core ORM functionality' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) apply plugin: 'org.hibernate.orm.antlr' apply plugin: 'org.hibernate.matrix-test' diff --git a/hibernate-envers/hibernate-envers.gradle b/hibernate-envers/hibernate-envers.gradle index a6371f84e063..a358ad371f02 100644 --- a/hibernate-envers/hibernate-envers.gradle +++ b/hibernate-envers/hibernate-envers.gradle @@ -7,7 +7,7 @@ description = 'Hibernate\'s entity version (audit/history) support' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) apply plugin: 'org.hibernate.matrix-test' dependencies { diff --git a/hibernate-graalvm/hibernate-graalvm.gradle b/hibernate-graalvm/hibernate-graalvm.gradle index de9def002261..4d6e82e75fb4 100644 --- a/hibernate-graalvm/hibernate-graalvm.gradle +++ b/hibernate-graalvm/hibernate-graalvm.gradle @@ -7,7 +7,7 @@ description = "Experimental extension to make it easier to compile applications into a GraalVM native image" -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { //No need for transitive dependencies: this is all just metadata to be used as companion jar. diff --git a/hibernate-hikaricp/hibernate-hikaricp.gradle b/hibernate-hikaricp/hibernate-hikaricp.gradle index 0729d5cf8815..f1078a3de5b5 100644 --- a/hibernate-hikaricp/hibernate-hikaricp.gradle +++ b/hibernate-hikaricp/hibernate-hikaricp.gradle @@ -7,7 +7,7 @@ description = 'Integration for HikariCP into Hibernate O/RM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/hibernate-jcache/hibernate-jcache.gradle b/hibernate-jcache/hibernate-jcache.gradle index 1aa17a8ac251..aa205c868f1f 100644 --- a/hibernate-jcache/hibernate-jcache.gradle +++ b/hibernate-jcache/hibernate-jcache.gradle @@ -1,6 +1,6 @@ description = 'Integration for javax.cache into Hibernate as a second-level caching service' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { api project( ':hibernate-core' ) diff --git a/hibernate-jfr/hibernate-jfr.gradle b/hibernate-jfr/hibernate-jfr.gradle index b3c47e306bc5..560d5de739bb 100644 --- a/hibernate-jfr/hibernate-jfr.gradle +++ b/hibernate-jfr/hibernate-jfr.gradle @@ -7,7 +7,7 @@ description = 'Integration for JDK JFR into Hibernate O/RM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/hibernate-micrometer/hibernate-micrometer.gradle b/hibernate-micrometer/hibernate-micrometer.gradle index e4730808fab1..16e5c0f7a51f 100644 --- a/hibernate-micrometer/hibernate-micrometer.gradle +++ b/hibernate-micrometer/hibernate-micrometer.gradle @@ -1,6 +1,6 @@ description = 'Integration for Micrometer metrics into Hibernate as a metrics collection package' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/hibernate-platform/hibernate-platform.gradle b/hibernate-platform/hibernate-platform.gradle index 036b36d879c8..a4b633c17ceb 100644 --- a/hibernate-platform/hibernate-platform.gradle +++ b/hibernate-platform/hibernate-platform.gradle @@ -32,7 +32,7 @@ dependencies { api project( ":hibernate-processor" ) api project( ":hibernate-gradle-plugin" ) - api project( ":hibernate-enhance-maven-plugin" ) + api project( ":hibernate-maven-plugin" ) api project( ":hibernate-ant" ) api libs.hibernateModels diff --git a/hibernate-proxool/hibernate-proxool.gradle b/hibernate-proxool/hibernate-proxool.gradle index 679c5340dc04..3c9463ab716a 100644 --- a/hibernate-proxool/hibernate-proxool.gradle +++ b/hibernate-proxool/hibernate-proxool.gradle @@ -7,7 +7,7 @@ description = 'Integration for Proxool Connection pooling into Hibernate O/RM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { diff --git a/hibernate-spatial/hibernate-spatial.gradle b/hibernate-spatial/hibernate-spatial.gradle index f17821309323..671ff8064125 100644 --- a/hibernate-spatial/hibernate-spatial.gradle +++ b/hibernate-spatial/hibernate-spatial.gradle @@ -7,7 +7,7 @@ description = 'Integrate support for Spatial/GIS data into Hibernate O/RM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) apply plugin: 'org.hibernate.matrix-test' diff --git a/hibernate-testing/hibernate-testing.gradle b/hibernate-testing/hibernate-testing.gradle index ed0bf21cd5b6..68d556ff9a70 100644 --- a/hibernate-testing/hibernate-testing.gradle +++ b/hibernate-testing/hibernate-testing.gradle @@ -7,7 +7,7 @@ description = 'Support for testing Hibernate ORM functionality' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { api project( ':hibernate-core' ) diff --git a/hibernate-ucp/hibernate-ucp.gradle b/hibernate-ucp/hibernate-ucp.gradle index 1f99ed287d9d..7c5843e6ba7f 100644 --- a/hibernate-ucp/hibernate-ucp.gradle +++ b/hibernate-ucp/hibernate-ucp.gradle @@ -7,7 +7,7 @@ description = 'Integration for Oracle UCP into Hibernate O/RM' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/hibernate-vector/hibernate-vector.gradle b/hibernate-vector/hibernate-vector.gradle index f0277a6bde5a..ece85d16d1a3 100644 --- a/hibernate-vector/hibernate-vector.gradle +++ b/hibernate-vector/hibernate-vector.gradle @@ -7,7 +7,7 @@ description = 'Hibernate\'s extensions for vector support' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { api project( ':hibernate-core' ) diff --git a/hibernate-vibur/hibernate-vibur.gradle b/hibernate-vibur/hibernate-vibur.gradle index 1702a15b448f..0eaf50e210a6 100644 --- a/hibernate-vibur/hibernate-vibur.gradle +++ b/hibernate-vibur/hibernate-vibur.gradle @@ -7,7 +7,7 @@ description = 'Integration for Vibur Connection pooling as a Hibernate ORM ConnectionProvider' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) dependencies { implementation project( ':hibernate-core' ) diff --git a/local-build-plugins/build.gradle b/local-build-plugins/build.gradle index 1419125bfb6b..c5f42d6716db 100644 --- a/local-build-plugins/build.gradle +++ b/local-build-plugins/build.gradle @@ -22,6 +22,11 @@ dependencies { implementation 'jakarta.json:jakarta.json-api:2.0.1' implementation 'org.eclipse:yasson:2.0.4' implementation 'org.jsoup:jsoup:1.15.3' + + implementation "org.apache.maven:maven-embedder:3.9.9" + implementation "org.apache.maven:maven-compat:3.9.9" + implementation "org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18" + implementation "org.apache.maven.resolver:maven-resolver-transport-http:1.9.18" } tasks.compileJava { @@ -80,6 +85,10 @@ gradlePlugin { id = 'org.hibernate.orm.build.java-module' implementationClass = 'org.hibernate.orm.toolchains.JavaModulePlugin' } + register( "mavenEmbedder" ) { + id = "org.hibernate.build.maven-embedder" + implementationClass = "org.hibernate.build.maven.embedder.MavenEmbedderPlugin" + } } } diff --git a/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderConfig.java b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderConfig.java new file mode 100644 index 000000000000..2faf5320b7d3 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderConfig.java @@ -0,0 +1,29 @@ +package org.hibernate.build.maven.embedder; + +import org.gradle.api.Project; +import org.gradle.api.file.DirectoryProperty; + +import javax.inject.Inject; + +/** + * Gradle DSL extension for configuring the {@linkplain MavenEmbedderPlugin maven-embedder plugin} + * + * @author Steve Ebersole + */ +public class MavenEmbedderConfig { + private DirectoryProperty localRepositoryDirectory; + + @Inject + public MavenEmbedderConfig(Project project) { + localRepositoryDirectory = project.getObjects().directoryProperty(); + localRepositoryDirectory.convention( project.getLayout().getBuildDirectory().dir( "maven-embedder/maven-local" ) ); + } + + public DirectoryProperty getLocalRepositoryDirectory() { + return localRepositoryDirectory; + } + + public void setLocalRepositoryDirectory(DirectoryProperty localRepositoryDirectory) { + this.localRepositoryDirectory = localRepositoryDirectory; + } +} diff --git a/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderPlugin.java b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderPlugin.java new file mode 100644 index 000000000000..9aa4867c1a19 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderPlugin.java @@ -0,0 +1,71 @@ +package org.hibernate.build.maven.embedder; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.file.Directory; +import org.gradle.api.provider.Provider; +import org.gradle.api.services.BuildServiceRegistry; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskProvider; + +/** + * Plugin for integrating Maven Embedder into the Gradle build to execute + * some Maven tasks/goals/mojos. + * + * @author Steve Ebersole + */ +public class MavenEmbedderPlugin implements Plugin { + @Override + public void apply(Project project) { + // be sure to mirror the output dirs + project.getLayout().getBuildDirectory().set( project.getLayout().getProjectDirectory().dir( "target" ) ); + + final BuildServiceRegistry sharedServices = project.getGradle().getSharedServices(); + + // add a DSL extension for configuration + final MavenEmbedderConfig dsl = project.getExtensions().create( + "mavenEmbedder", + MavenEmbedderConfig.class + ); + + // add the MavenEmbedderService shared-build-service + final Provider embedderServiceProvider = sharedServices.registerIfAbsent( + "maven-embedder", + MavenEmbedderService.class, (spec) -> { + spec.getParameters().getProjectVersion().set( project.getVersion().toString() ); + spec.getParameters().getWorkingDirectory().set( project.getLayout().getProjectDirectory() ); + spec.getParameters().getMavenLocalDirectory().set( dsl.getLocalRepositoryDirectory() ); + } + ); + + // Via the plugin's POM, we tell Maven to generate the descriptors into + // `target/generated/sources/plugin-descriptors/META-INF/maven`. + // `META-INF/maven` is the relative path we need inside the jar, so we + // configure the "resource directory" in Gradle to be just the + // `target/generated/sources/plugin-descriptors` part. + final Provider descriptorsDir = project.getLayout().getBuildDirectory().dir( "generated/sources/plugin-descriptors" ); + + // create the "mirror" task which calls the appropriate Maven tasks/goals/mojos behind the scenes using the embedder service + final TaskProvider generatePluginDescriptorTask = project.getTasks().register( "generatePluginDescriptor", MavenPluginDescriptorTask.class, (task) -> { + task.setGroup( "maven embedder" ); + + task.getMavenEmbedderService().set( embedderServiceProvider ); + task.usesService( embedderServiceProvider ); + + // deal with the "descriptor directory" - + // 1. we need this on the Gradle side for up-to-date checking, etc + task.getDescriptorDirectory().set( descriptorsDir ); + // 2. add the resource dir to the main source-set's resources so that it is picked up for jar + final SourceSetContainer sourceSets = project.getExtensions().getByType( SourceSetContainer.class ); + final SourceSet mainSourceSet = sourceSets.getByName( "main" ); + mainSourceSet.getResources().srcDir( task.getDescriptorDirectory() ); + + // we need compilation to happen before we generate the descriptors + task.dependsOn( "compileJava" ); + } ); + + // we need the descriptor generation to happen before we jar + project.getTasks().named( "jar", (jarTask) -> jarTask.dependsOn( generatePluginDescriptorTask ) ); + } +} diff --git a/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderService.java b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderService.java new file mode 100644 index 000000000000..6309c07909b9 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenEmbedderService.java @@ -0,0 +1,51 @@ +package org.hibernate.build.maven.embedder; + +import org.apache.maven.cli.MavenCli; +import org.gradle.api.file.Directory; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wrapper around the Maven Embedder as a Gradle build-service + * + * @author Steve Ebersole + */ +public abstract class MavenEmbedderService implements BuildService { + interface Config extends BuildServiceParameters { + Property getProjectVersion(); + DirectoryProperty getWorkingDirectory(); + DirectoryProperty getMavenLocalDirectory(); + } + + private final MavenCli embedder; + + public MavenEmbedderService() { + // this needs to be set for some reason. + // NOTE : even though it is named "multi module", here we are only interested in the specific project + System.setProperty( "maven.multiModuleProjectDirectory", getParameters().getWorkingDirectory().toString() ); + embedder = new MavenCli(); + } + + public void execute(String... tasksAndArgs) { + final List cml = new ArrayList<>(); + Collections.addAll( cml, tasksAndArgs ); + + final Directory mavenLocalDirectory = getParameters().getMavenLocalDirectory().get(); + cml.add( "-Dmaven.repo.local=\"" + mavenLocalDirectory.getAsFile().getAbsolutePath() + "\"" ); + cml.add( "-Dorm.project.version=" + getParameters().getProjectVersion().get() ); + + final Directory workingDirectory = getParameters().getWorkingDirectory().get(); + final String workingDirectoryPath = workingDirectory.getAsFile().getAbsolutePath(); + + // todo : consider bridging Maven out/err to Gradle logging + + final int resultCode = embedder.doMain( cml.toArray(new String[0]), workingDirectoryPath, System.out, System.err ); + // todo : do something with result-code + } +} diff --git a/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenPluginDescriptorTask.java b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenPluginDescriptorTask.java new file mode 100644 index 000000000000..8a873934caf5 --- /dev/null +++ b/local-build-plugins/src/main/java/org/hibernate/build/maven/embedder/MavenPluginDescriptorTask.java @@ -0,0 +1,36 @@ +package org.hibernate.build.maven.embedder; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.services.ServiceReference; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; + +/** + * Task which "mirrors" the Maven task/goal/mojo for generating + * plugin descriptors. + * + * @author Steve Ebersole + */ +public abstract class MavenPluginDescriptorTask extends DefaultTask { + // This property provides access to the service instance + @ServiceReference + abstract Property getMavenEmbedderService(); + + @OutputDirectory + abstract DirectoryProperty getDescriptorDirectory(); + + public MavenPluginDescriptorTask() { + // todo : what else is the descriptor/pom dependent upon? + getInputs().property( "project-version", getProject().getVersion() ); + } + + @TaskAction + public void generateDescriptor() { + getMavenEmbedderService().get().execute( "plugin:descriptor" ); + // todo : anything else? e.g. + //getMavenEmbedderService().get().execute( "plugin:addPluginArtifactMetadata" ); + //getMavenEmbedderService().get().execute( "plugin:helpmojo" ); + } +} diff --git a/settings.gradle b/settings.gradle index 13b6b6b11f42..d81c0372fcf4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -354,8 +354,8 @@ project(':metamodel-generator').name = 'hibernate-processor' include 'hibernate-gradle-plugin' project(':hibernate-gradle-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-gradle-plugin") -include 'hibernate-enhance-maven-plugin' -project(':hibernate-enhance-maven-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-enhance-maven-plugin") +include 'hibernate-maven-plugin' +project(':hibernate-maven-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-maven-plugin") include 'hibernate-ant' project(':hibernate-ant').projectDir = new File(rootProject.projectDir, "tooling/hibernate-ant") diff --git a/tooling/hibernate-ant/hibernate-ant.gradle b/tooling/hibernate-ant/hibernate-ant.gradle index 279cbae869a9..20b9895ddb63 100644 --- a/tooling/hibernate-ant/hibernate-ant.gradle +++ b/tooling/hibernate-ant/hibernate-ant.gradle @@ -1,7 +1,7 @@ description = 'Annotation Processor to generate JPA 2 static metamodel classes' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) apply plugin: 'org.hibernate.build.version-injection' dependencies { diff --git a/tooling/hibernate-enhance-maven-plugin/hibernate-enhance-maven-plugin.gradle b/tooling/hibernate-enhance-maven-plugin/hibernate-enhance-maven-plugin.gradle deleted file mode 100644 index 373691241d58..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/hibernate-enhance-maven-plugin.gradle +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later. - * See the lgpl.txt file in the root directory or . - */ -description = 'Enhance Plugin of the Hibernate project for use with Maven build system.' - -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) -apply plugin: 'maven-publish' - - -import org.apache.tools.ant.filters.ReplaceTokens -group = 'org.hibernate.orm.tooling' - -processResources { - include "**/lifecycle-mapping-metadata.xml" - include "**/plugin-help.xml" - into processResources.destinationDir - filter(ReplaceTokens, tokens: ['version' : project.version]) -} - -dependencies { - implementation( project(':hibernate-core') ) { transitive = false } - implementation( jakartaLibs.jpa ) { transitive = false } - implementation( jakartaLibs.jta ) { transitive = false } - implementation libs.byteBuddy - - implementation( mavenLibs.mavenCore ) { transitive = false } - implementation( mavenLibs.mavenArtifact ) { transitive = false } - implementation( mavenLibs.mavenPlugin ) { transitive = false } - implementation( mavenLibs.mavenPluginTools ) { transitive = false } - - implementation 'org.codehaus.plexus:plexus-utils:3.0.24' - implementation 'org.sonatype.plexus:plexus-build-api:0.0.7' - - runtimeOnly mavenLibs.mavenCore - runtimeOnly mavenLibs.mavenArtifact - runtimeOnly mavenLibs.mavenPlugin - runtimeOnly mavenLibs.mavenPluginTools -} - -// Inject dependencies into plugin.xml -// Note: injecting the full dependency, rather than just the version, -// removing the need to maintain artifact names that might change with upgrades (JPA/JTA API version, etc.) -task processPluginXml(type: Copy) { - // force out-of-date if version changes - inputs.property("version", project.version) - - from "src/main/resources/META-INF/maven/plugin.xml" - into "$processResources.destinationDir/META-INF/maven" - filter(ReplaceTokens, tokens: ['version' : project.version, 'generated-dependencies' :\ - generateMavenDependency(jakartaLibs.jpa)\ - + generateMavenDependency(libs.antlr)\ - + generateMavenDependency(jakartaLibs.jta)\ - + generateMavenDependency(libs.hibernateModels)\ - + generateMavenDependency(libs.jandex)\ - + generateMavenDependency(libs.byteBuddy)\ - + generateMavenDependency(libs.logging)\ - + generateMavenDependency("org.hibernate:hibernate-core:" + project.version)]) -} - -// TODO: There may be a way to do this directly with Gradle's Maven plugin, but it's still incubating -// and I'd rather not rely on it yet. -def generateMavenDependency(String gradleDependency) { - String[] split = gradleDependency.split(":") - return "\n"\ - + "\n " + split[0] + ""\ - + "\n " + split[1] + ""\ - + "\n " + split[2] + ""\ - + "\n jar"\ - + "\n" -} - -def generateMavenDependency(Provider gradleDependencyProvider) { - String[] split = gradleDependencyProvider.get().toString().split(":") - return "\n"\ - + "\n " + split[0] + ""\ - + "\n " + split[1] + ""\ - + "\n " + split[2] + ""\ - + "\n jar"\ - + "\n" -} - -// Writes pom.xml using merged Gradle dependency and MavenPom configuration. -tasks.named('generatePomFileForPublishedArtifactsPublication') { - pom.with { - packaging 'maven-plugin' - name.set('Hibernate Enhance Maven Plugin') - description 'Enhance Plugin of the Hibernate project for use with Maven build system.' - properties.put( - 'project.build.sourceEncoding', 'UTF-8' - ) - // HHH-9679 --- build in pom{} conflicts with FactoryBuilderSupport#build so we write that portion of XML by hand - withXml { - asNode().appendNode('build').appendNode('plugins').appendNode('plugin').with { - appendNode('groupId', 'org.apache.maven.plugins') - appendNode('artifactId', 'maven-plugin-plugin') - appendNode('version', '3.2') - appendNode('configuration').appendNode('skipErrorNoDescriptorsFound', 'true') - appendNode('executions').appendNode('execution').with { - appendNode('id', 'mojo-descriptor') - appendNode('goals').appendNode('goal', 'descriptor') - } - } - } - } -} - -processResources.dependsOn processPluginXml -// We need this filter here, otherwise Gradle or the JUnit Jupiter platform will load classes at a point -// when they are not yet enhanced, leading to test failures -test.include '**/*Test.class' diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/MavenEnhancePlugin.java b/tooling/hibernate-enhance-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/MavenEnhancePlugin.java deleted file mode 100644 index 04c26425cf70..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/MavenEnhancePlugin.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.tooling.maven; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.*; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; - -import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; -import org.hibernate.bytecode.enhance.spi.EnhancementContext; -import org.hibernate.bytecode.enhance.spi.Enhancer; -import org.hibernate.bytecode.enhance.spi.UnloadedClass; -import org.hibernate.bytecode.enhance.spi.UnloadedField; -import org.hibernate.bytecode.spi.BytecodeProvider; - -import org.sonatype.plexus.build.incremental.BuildContext; - -import static org.hibernate.bytecode.internal.BytecodeProviderInitiator.buildDefaultBytecodeProvider; - -/** - * This plugin will enhance Entity objects. - * - * @author Jeremy Whiting - * @author Luis Barreiro - */ -@Mojo(name = "enhance", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class MavenEnhancePlugin extends AbstractMojo { - - /** - * The contexts to use during enhancement. - */ - private List sourceSet = new ArrayList(); - - @Component - private BuildContext buildContext; - - @Parameter(property = "base", defaultValue = "${project.build.outputDirectory}") - private String base; - - @Parameter(property = "dir", defaultValue = "${project.build.outputDirectory}") - private String dir; - - @Parameter(property = "classNames", defaultValue = "") - private String classNames; - - @Parameter(property = "failOnError", defaultValue = "true") - private boolean failOnError = true; - - @Parameter(property = "enableLazyInitialization", defaultValue = "true") - private boolean enableLazyInitialization; - - @Parameter(property = "enableDirtyTracking", defaultValue = "true") - private boolean enableDirtyTracking; - - @Parameter(property = "enableAssociationManagement", defaultValue = "false") - private boolean enableAssociationManagement; - - @Parameter(property = "enableExtendedEnhancement", defaultValue = "false") - private boolean enableExtendedEnhancement; - - private boolean shouldApply() { - return enableLazyInitialization || enableDirtyTracking || enableAssociationManagement || enableExtendedEnhancement; - } - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - final Log log = getLog(); - if ( !shouldApply() ) { - log.warn( "Skipping Hibernate bytecode enhancement plugin execution since no feature is enabled" ); - return; - } - - if ( !dir.startsWith( base ) ) { - throw new MojoExecutionException( "The enhancement directory 'dir' (" + dir + ") is no subdirectory of 'base' (" + base + ")" ); - } - - // Perform a depth first search for sourceSet - File root = new File( this.dir ); - if ( !root.exists() ) { - log.info( "Skipping Hibernate enhancement plugin execution since there is no classes dir " + dir ); - return; - } - walkDir( root ); - if ( sourceSet.isEmpty() ) { - log.info( "Skipping Hibernate enhancement plugin execution since there are no classes to enhance on " + dir ); - return; - } - - List classesToEnhance = new ArrayList<>(); - if(classNames != null && classNames.length() >= 1) { - classesToEnhance = Arrays.asList(classNames.split(",")); - } - - log.info( "Starting Hibernate enhancement for classes on " + dir ); - final ClassLoader classLoader = toClassLoader( Collections.singletonList( new File( base ) ) ); - - EnhancementContext enhancementContext = new DefaultEnhancementContext() { - @Override - public ClassLoader getLoadingClassLoader() { - return classLoader; - } - - @Override - public boolean doBiDirectionalAssociationManagement(UnloadedField field) { - return enableAssociationManagement; - } - - @Override - public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) { - return enableDirtyTracking; - } - - @Override - public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) { - return enableLazyInitialization; - } - - @Override - public boolean isLazyLoadable(UnloadedField field) { - return enableLazyInitialization; - } - - @Override - public boolean doExtendedEnhancement(UnloadedClass classDescriptor) { - return enableExtendedEnhancement; - } - }; - - if ( !enableLazyInitialization ) { - log.warn( "The 'enableLazyInitialization' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning" ); - } - if ( !enableDirtyTracking ) { - log.warn( "The 'enableDirtyTracking' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning" ); - } - if ( enableExtendedEnhancement ) { - log.warn( "Extended enhancement is enabled. Classes other than entities may be modified. You should consider access the entities using getter/setter methods and disable this property. Use at your own risk." ); - } - - //TODO allow the Maven plugin to configure the bytecode enhancer? - final BytecodeProvider bytecodeProvider = buildDefaultBytecodeProvider(); - try { - final Enhancer enhancer = bytecodeProvider.getEnhancer( enhancementContext ); - - for ( File file : sourceSet ) { - discoverTypes( file, enhancer ); - if ( log.isDebugEnabled() ) { - log.debug( "Successfully discovered types for class [" + file + "]" ); - } - } - for ( File file : sourceSet ) { - - String className = determineClassName(root, file); - - if(! (classesToEnhance.size()==0 || classesToEnhance.contains(className))) { - continue; - } - - final byte[] enhancedBytecode = doEnhancement( file, enhancer ); - - if ( enhancedBytecode == null ) { - continue; - } - - writeOutEnhancedClass( enhancedBytecode, file ); - if ( log.isDebugEnabled() ) { - log.debug( "Successfully enhanced class [" + file + "]" ); - } - } - } - finally { - bytecodeProvider.resetCaches(); - } - } - - private ClassLoader toClassLoader(List runtimeClasspath) throws MojoExecutionException { - List urls = new ArrayList( runtimeClasspath.size() ); - final Log log = getLog(); - for ( File file : runtimeClasspath ) { - try { - urls.add( file.toURI().toURL() ); - if ( log.isDebugEnabled() ) { - log.debug( "Adding classpath entry for classes root " + file.getAbsolutePath() ); - } - } - catch (MalformedURLException e) { - String msg = "Unable to resolve classpath entry to URL: " + file.getAbsolutePath(); - if ( failOnError ) { - throw new MojoExecutionException( msg, e ); - } - log.warn( msg ); - } - } - - // HHH-10145 Add dependencies to classpath as well - all but the ones used for testing purposes - MavenProject project = ( (MavenProject) getPluginContext().get( "project" ) ); - Set artifacts = project.getArtifacts(); - if ( artifacts != null) { - for ( Artifact a : artifacts ) { - if ( !Artifact.SCOPE_TEST.equals( a.getScope() ) ) { - try { - urls.add( a.getFile().toURI().toURL() ); - log.debug( "Adding classpath entry for dependency " + a.getId() ); - } - catch (MalformedURLException e) { - String msg = "Unable to resolve URL for dependency " + a.getId() + " at " + a.getFile().getAbsolutePath(); - if ( failOnError ) { - throw new MojoExecutionException( msg, e ); - } - log.warn( msg ); - } - } - } - } - - return new URLClassLoader( urls.toArray( new URL[urls.size()] ), Enhancer.class.getClassLoader() ); - } - - private byte[] doEnhancement(File javaClassFile, Enhancer enhancer) throws MojoExecutionException { - try { - String className = javaClassFile.getAbsolutePath().substring( - base.length() + 1, - javaClassFile.getAbsolutePath().length() - ".class".length() - ).replace( File.separatorChar, '.' ); - ByteArrayOutputStream originalBytes = new ByteArrayOutputStream(); - FileInputStream fileInputStream = new FileInputStream( javaClassFile ); - try { - byte[] buffer = new byte[1024]; - int length; - while ( ( length = fileInputStream.read( buffer ) ) != -1 ) { - originalBytes.write( buffer, 0, length ); - } - } - finally { - fileInputStream.close(); - } - return enhancer.enhance( className, originalBytes.toByteArray() ); - } - catch (Exception e) { - String msg = "Unable to enhance class: " + javaClassFile.getName(); - if ( failOnError ) { - throw new MojoExecutionException( msg, e ); - } - buildContext.addMessage( javaClassFile, 0, 0, msg, BuildContext.SEVERITY_WARNING, e ); - return null; - } - } - - private void discoverTypes(File javaClassFile, Enhancer enhancer) throws MojoExecutionException { - try { - String className = javaClassFile.getAbsolutePath().substring( - base.length() + 1, - javaClassFile.getAbsolutePath().length() - ".class".length() - ).replace( File.separatorChar, '.' ); - ByteArrayOutputStream originalBytes = new ByteArrayOutputStream(); - FileInputStream fileInputStream = new FileInputStream( javaClassFile ); - try { - byte[] buffer = new byte[1024]; - int length; - while ( ( length = fileInputStream.read( buffer ) ) != -1 ) { - originalBytes.write( buffer, 0, length ); - } - } - finally { - fileInputStream.close(); - } - enhancer.discoverTypes( className, originalBytes.toByteArray() ); - } - catch (Exception e) { - String msg = "Unable to discover types for class: " + javaClassFile.getName(); - if ( failOnError ) { - throw new MojoExecutionException( msg, e ); - } - buildContext.addMessage( javaClassFile, 0, 0, msg, BuildContext.SEVERITY_WARNING, e ); - } - } - - /** - * Expects a directory. - */ - private void walkDir(File dir) { - walkDir( - dir, - new FileFilter() { - @Override - public boolean accept(File pathname) { - return ( pathname.isFile() && pathname.getName().endsWith( ".class" ) ); - } - }, - new FileFilter() { - @Override - public boolean accept(File pathname) { - return ( pathname.isDirectory() ); - } - } - ); - } - - private void walkDir(File dir, FileFilter classesFilter, FileFilter dirFilter) { - File[] dirs = dir.listFiles( dirFilter ); - for ( File dir1 : dirs ) { - walkDir( dir1, classesFilter, dirFilter ); - } - File[] files = dir.listFiles( classesFilter ); - Collections.addAll( this.sourceSet, files ); - } - - private void writeOutEnhancedClass(byte[] enhancedBytecode, File file) throws MojoExecutionException { - try { - if ( file.delete() ) { - if ( !file.createNewFile() ) { - buildContext.addMessage( file, 0, 0, "Unable to recreate class file", BuildContext.SEVERITY_ERROR, null ); - } - } - else { - buildContext.addMessage( file, 0, 0, "Unable to delete class file", BuildContext.SEVERITY_ERROR, null ); - } - } - catch (IOException e) { - buildContext.addMessage( file, 0, 0, "Problem preparing class file for writing out enhancements", BuildContext.SEVERITY_WARNING, e ); - } - - OutputStream outputStream = null; - try { - outputStream = buildContext.newFileOutputStream( file ); - outputStream.write( enhancedBytecode ); - outputStream.flush(); - } - catch (IOException e) { - String msg = String.format( "Error writing to enhanced class [%s] to file [%s]", file.getName(), file.getAbsolutePath() ); - if ( failOnError ) { - throw new MojoExecutionException( msg, e ); - } - buildContext.addMessage( file, 0, 0, msg, BuildContext.SEVERITY_WARNING, e ); - } - finally { - try { - if ( outputStream != null ) { - outputStream.close(); - } - } - catch (IOException ignore) { - } - } - } - - private String determineClassName(File root, File javaClassFile) { - final Path relativeClassPath = root.toPath().relativize( javaClassFile.toPath() ); - final String relativeClassPathString = relativeClassPath.toString(); - final String classNameBase = relativeClassPathString.substring( - 0, - relativeClassPathString.length() - ".class".length() - ); - return classNameBase.replace( File.separatorChar, '.' ); - } -} diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml deleted file mode 100644 index 7ae74429fcff..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/m2e/lifecycle-mapping-metadata.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - enhance - - - - - true - false - - - - - - diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml deleted file mode 100644 index 8f31bbbc1427..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/hibernate-enhance-maven-plugin/plugin-help.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - Hibernate Enhance Maven Plugin - Enhance Plugin of the Hibernate project for use with Maven build system. - org.hibernate.orm.tooling - hibernate-enhance-maven-plugin - @version@ - hibernate-enhance - - - enhance - This plugin will enhance Entity objects. - false - true - false - false - false - true - compile - compile - enhance - org.hibernate.orm.tooling.maven.MavenEnhancePlugin - java - per-lookup - once-per-session - false - - - base - java.lang.String - false - true - The root folder for .class files - - - dir - java.lang.String - false - true - Base directory where to search for .class files - - - classNames - java.lang.String - false - true - Comma separated string of class names for which enhancement needs to be done - - - failOnError - java.lang.Boolean - false - true - Indicates whether the build will continue even if there are enhancement errors - - - enableLazyInitialization - java.lang.Boolean - false - true - DEPRECATED: Enable enhancement for lazy loading of attributes. This setting is deprecated for removal without a replacement. - - - enableDirtyTracking - java.lang.Boolean - false - true - DEPRECATED: Enable enhancement for tracking of dirty attributes. This setting is deprecated for removal without a replacement. - - - enableAssociationManagement - java.lang.Boolean - false - true - Enable enhancement for management of bi-direction associations - - - enableExtendedEnhancement - java.lang.Boolean - false - true - Enable enhancement of field access - - - - ${project.build.outputDirectory} - ${project.build.outputDirectory} - true - true - true - false - false - - - - \ No newline at end of file diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/plugin-help.xml b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/plugin-help.xml deleted file mode 100644 index 9d37cb7d782b..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/org.hibernate.orm.tooling/plugin-help.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - Hibernate Enhance Maven Plugin - Enhance Plugin of the Hibernate project for use with Maven build system. - org.hibernate.orm.tooling - hibernate-enhance-maven-plugin - @version@ - hibernate-enhance - - \ No newline at end of file diff --git a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml b/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml deleted file mode 100644 index 7a4fc7869fbd..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/main/resources/META-INF/maven/plugin.xml +++ /dev/null @@ -1,186 +0,0 @@ - - - - Hibernate Enhance Maven Plugin - Enhance Plugin of the Hibernate project for use with Maven build system. - org.hibernate.orm.tooling - hibernate-enhance-maven-plugin - @version@ - hibernate-enhance - false - true - - - enhance - This plugin will enhance Entity objects. - false - true - false - false - false - true - compile+runtime - compile - org.hibernate.orm.tooling.maven.MavenEnhancePlugin - java - per-lookup - once-per-session - false - - - base - java.lang.String - false - true - The root folder for .class files - - - dir - java.lang.String - false - true - Base directory where to search for .class files - - - classNames - java.lang.String - false - true - Comma separated string of class names for which enhancement needs to be done - - - failOnError - java.lang.Boolean - false - true - Indicates whether the build will continue even if there are enhancement errors - - - enableLazyInitialization - java.lang.Boolean - false - true - DEPRECATED: Enable enhancement for lazy loading of attributes. This setting is deprecated for removal without a replacement. - - - enableDirtyTracking - java.lang.Boolean - false - true - DEPRECATED: Enable enhancement for tracking of dirty attributes. This setting is deprecated for removal without a replacement. - - - enableAssociationManagement - java.lang.Boolean - false - true - Enable enhancement for management of bi-direction associations - - - enableExtendedEnhancement - java.lang.Boolean - false - true - Enable enhancement of field access - - - - ${project.build.outputDirectory} - ${project.build.outputDirectory} - true - true - true - false - false - - - - org.sonatype.plexus.build.incremental.BuildContext - buildContext - - - - - - - @generated-dependencies@ - - - org.codehaus.plexus - plexus-utils - jar - 3.0.24 - - - org.codehaus.plexus - plexus-build-api - jar - 0.0.7 - - - org.apache.maven.plugin-tools - maven-plugin-annotations - jar - 3.2 - - - org.apache.maven - maven-artifact - jar - 3.0 - - - org.apache.maven - maven-plugin-api - jar - 3.0.5 - - - org.apache.maven - maven-model - jar - 3.0.5 - - - org.sonatype.sisu - sisu-inject-plexus - jar - 2.3.0 - - - org.codehaus.plexus - plexus-component-annotations - jar - 1.5.5 - - - org.codehaus.plexus - plexus-classworlds - jar - 2.4 - - - org.sonatype.sisu - sisu-inject-bean - jar - 2.3.0 - - - org.sonatype.sisu - sisu-guice - jar - 3.1.0 - - - org.sonatype.sisu - sisu-guava - jar - 0.9.9 - - - diff --git a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/ChildEntity.java b/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/ChildEntity.java deleted file mode 100644 index 185eb5d7a95e..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/ChildEntity.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.tooling.maven; - -import jakarta.persistence.MappedSuperclass; - -@MappedSuperclass -public class ChildEntity extends ParentEntity { - - String childValue; - -} diff --git a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/MavenEnhancePluginTest.java b/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/MavenEnhancePluginTest.java deleted file mode 100644 index 6e2cd4940700..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/MavenEnhancePluginTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.tooling.maven; - -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.ReflectionUtils; -import org.hibernate.engine.spi.Managed; -import org.junit.Assert; -import org.junit.Test; -import org.sonatype.plexus.build.incremental.DefaultBuildContext; - -import java.io.File; -import java.lang.reflect.Field; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.HashMap; -import java.util.Map; - -/** - * Test case for Maven Enhance Plugin - * - * @author Luis Barreiro - */ -public class MavenEnhancePluginTest { - - @Test - public void testEnhancePlugin() throws Exception { - File baseDir = new File("target/classes/java/test"); - URL[] baseURLs = { baseDir.toURI().toURL() }; - - MavenEnhancePlugin plugin = new MavenEnhancePlugin(); - - Map pluginContext = new HashMap<>(); - pluginContext.put( "project", new MavenProject() ); - - setVariableValueToObject( plugin, "pluginContext", pluginContext ); - setVariableValueToObject( plugin, "buildContext", new DefaultBuildContext() ); - - setVariableValueToObject( plugin, "base", baseDir.getAbsolutePath() ); - setVariableValueToObject( plugin, "dir", baseDir.getAbsolutePath() ); - setVariableValueToObject( plugin, "classNames", "" ); - - setVariableValueToObject( plugin, "failOnError", true ); - setVariableValueToObject( plugin, "enableLazyInitialization", true ); - setVariableValueToObject( plugin, "enableDirtyTracking", true ); - setVariableValueToObject( plugin, "enableAssociationManagement", true ); - setVariableValueToObject( plugin, "enableExtendedEnhancement", false ); - - plugin.execute(); - - try ( URLClassLoader classLoader = new URLClassLoader( baseURLs , getClass().getClassLoader() ) ) { - - Assert.assertTrue( declaresManaged( classLoader.loadClass( ParentEntity.class.getName() ) ) ); - Assert.assertTrue( declaresManaged( classLoader.loadClass( ChildEntity.class.getName() ) ) ); - Assert.assertTrue( declaresManaged( classLoader.loadClass( TestEntity.class.getName() ) ) ); - - } - - } - - private void setVariableValueToObject( Object object, String variable, Object value ) throws IllegalAccessException { - Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() ); - field.setAccessible( true ); - field.set( object, value ); - } - - private boolean declaresManaged(Class clazz) { - for ( Class interfaceClazz : clazz.getInterfaces() ) { - if ( Managed.class.isAssignableFrom( interfaceClazz ) ) { - return true; - } - } - return false; - } - -} diff --git a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/ParentEntity.java b/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/ParentEntity.java deleted file mode 100644 index 3027f694b058..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/ParentEntity.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.tooling.maven; - -import jakarta.persistence.MappedSuperclass; - -@MappedSuperclass -public class ParentEntity { - - String parentValue; - -} diff --git a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/TestEntity.java b/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/TestEntity.java deleted file mode 100644 index e39e83be2195..000000000000 --- a/tooling/hibernate-enhance-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/TestEntity.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.tooling.maven; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -@Entity -public class TestEntity extends ChildEntity { - - @Id - long id; - - String testValue; - -} diff --git a/tooling/hibernate-maven-plugin/.gitignore b/tooling/hibernate-maven-plugin/.gitignore new file mode 100644 index 000000000000..3eae4110cba0 --- /dev/null +++ b/tooling/hibernate-maven-plugin/.gitignore @@ -0,0 +1,7 @@ +.classpath +.gradle +.java-version +.project +.vscode +.settings +target diff --git a/tooling/hibernate-maven-plugin/.mvn/wrapper/maven-wrapper.properties b/tooling/hibernate-maven-plugin/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000000..23c7e5999d74 --- /dev/null +++ b/tooling/hibernate-maven-plugin/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip diff --git a/tooling/hibernate-maven-plugin/build.gradle b/tooling/hibernate-maven-plugin/build.gradle new file mode 100644 index 000000000000..5bcd90978ae7 --- /dev/null +++ b/tooling/hibernate-maven-plugin/build.gradle @@ -0,0 +1,8 @@ +tasks.register("build") { + doLast { + exec { + executable = './mvnw' + args = ['clean', 'install'] + } + } +} diff --git a/tooling/hibernate-maven-plugin/hibernate-maven-plugin.gradle b/tooling/hibernate-maven-plugin/hibernate-maven-plugin.gradle new file mode 100644 index 000000000000..0593bff64279 --- /dev/null +++ b/tooling/hibernate-maven-plugin/hibernate-maven-plugin.gradle @@ -0,0 +1,19 @@ +description = 'Maven plugin to integrate aspects of Hibernate into your build.' + +apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply plugin: 'org.hibernate.build.maven-embedder' + +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ + +dependencies { + implementation project( ":hibernate-core" ) + + implementation "org.apache.maven:maven-plugin-api:3.6.3" + implementation "org.apache.maven.plugin-tools:maven-plugin-annotations:3.6.0" + implementation "org.apache.maven:maven-project:2.2.1" +} \ No newline at end of file diff --git a/tooling/hibernate-maven-plugin/mvnw b/tooling/hibernate-maven-plugin/mvnw new file mode 100755 index 000000000000..19529ddf8c6e --- /dev/null +++ b/tooling/hibernate-maven-plugin/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/tooling/hibernate-maven-plugin/mvnw.cmd b/tooling/hibernate-maven-plugin/mvnw.cmd new file mode 100644 index 000000000000..b150b91ed500 --- /dev/null +++ b/tooling/hibernate-maven-plugin/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/tooling/hibernate-maven-plugin/pom.xml b/tooling/hibernate-maven-plugin/pom.xml new file mode 100644 index 000000000000..c23374dc63a7 --- /dev/null +++ b/tooling/hibernate-maven-plugin/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + org.hibernate.orm + hibernate-maven-plugin + maven-plugin + 0.0.1-SNAPSHOT + + Hbernate Maven Plugin + + http://hibernate.org + + + 7.0.0.Beta1 + 3.1.0 + 3.8.0 + 3.9.9 + 3.15.0 + 5.11.2 + UTF-8 + UTF-8 + 17 + 17 + + + + + org.apache.maven + maven-plugin-api + ${maven-plugin-api.version} + provided + + + org.apache.maven.shared + file-management + ${maven-file-management.version} + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-annotations.version} + provided + + + org.hibernate.orm + hibernate-core + ${hibernate-core.version} + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter.version} + test + + + + + + + maven-invoker-plugin + ${maven-invoker-plugin.version} + + true + ${project.build.directory}/it + src/it/settings.xml + ${project.build.directory}/local-repo + verify + install + + + + integration-test + + install + run + + + + + + + + diff --git a/tooling/hibernate-maven-plugin/src/it/enhance/pom.xml b/tooling/hibernate-maven-plugin/src/it/enhance/pom.xml new file mode 100644 index 000000000000..9a41e5c02e3c --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/it/enhance/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + org.hibernate.orm.tooling.maven + enhance-test + 0.0.1-SNAPSHOT + + + + org.hibernate.orm + hibernate-core + @hibernate-core.version@ + + + + + + + org.hibernate.orm + hibernate-maven-plugin + @project.version@ + + + enhance + process-classes + + + + ${project.build.directory}/classes + + **/Baz.class + + + + true + + + enhance + + + + + + + + \ No newline at end of file diff --git a/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Bar.java b/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Bar.java new file mode 100644 index 000000000000..304efc24dbc8 --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Bar.java @@ -0,0 +1,18 @@ +package org.foo; + +import jakarta.persistence.Entity; + +@Entity +public class Bar { + + private String foo; + + String getFoo() { + return foo; + } + + public void setFoo(String f) { + foo = f; + } + +} diff --git a/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Baz.java b/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Baz.java new file mode 100644 index 000000000000..169826b58d5c --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Baz.java @@ -0,0 +1,18 @@ +package org.foo; + +import jakarta.persistence.Entity; + +@Entity +public class Baz { + + private String foo; + + String getFoo() { + return foo; + } + + public void setFoo(String f) { + foo = f; + } + +} diff --git a/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Foo.java b/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Foo.java new file mode 100644 index 000000000000..8427ed7439b5 --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/it/enhance/src/main/java/org/foo/Foo.java @@ -0,0 +1,15 @@ +package org.foo; + +public class Foo { + + private Bar bar; + + Bar getBar() { + return bar; + } + + public void setBar(Bar b) { + bar = b; + } + +} diff --git a/tooling/hibernate-maven-plugin/src/it/enhance/verify.groovy b/tooling/hibernate-maven-plugin/src/it/enhance/verify.groovy new file mode 100644 index 000000000000..edd5ae5f66d1 --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/it/enhance/verify.groovy @@ -0,0 +1,75 @@ +import java.io.File; +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.util.List; +import java.util.ArrayList; + + +File targetFolder = new File(basedir, "target"); +if (!targetFolder.exists()) { + throw new FileNotFoundException("Folder should exist: " + targetFolder); +} +if (targetFolder.isFile()) { + throw new FileNotFoundException("Folder should be a folder: " + targetFolder); +} +File classesFolder = new File(targetFolder, "classes"); +if (!classesFolder.exists()) { + throw new FileNotFoundException("Folder should exist: " + classesFolder); +} +File barClassFile = new File(classesFolder, "org/foo/Bar.class"); +if (!barClassFile.exists()) { + throw new FileNotFoundException("File should exist: " + barClassFile); +} +int amountOfBytes = Files.readAllBytes(barClassFile.toPath()).length; + +File fooClassFile = new File(classesFolder, "org/foo/Foo.class"); +if (!fooClassFile.exists()) { + throw new FileNotFoundException("File should exist: " + fooClassFile); +} + +File bazClassFile = new File(classesFolder, "org/foo/Baz.class"); +if (!bazClassFile.exists()) { + throw new FileNotFoundException("File should exist: " + bazClassFile); +} + +File buildLog = new File(basedir, "build.log"); +if (!buildLog.exists()) { + throw new FileNotFoundException("File should exist: " + buildLog); +} +List listOfStrings = new ArrayList(); +listOfStrings = Files.readAllLines(buildLog.toPath()); +assert listOfStrings.contains("[DEBUG] Configuring mojo execution 'org.hibernate.orm:hibernate-maven-plugin:0.0.1-SNAPSHOT:enhance:enhance' with basic configurator -->"); +assert listOfStrings.contains("[DEBUG] (f) classesDirectory = " + classesFolder); +assert listOfStrings.contains("[DEBUG] (f) enableAssociationManagement = false"); +assert listOfStrings.contains("[DEBUG] (f) enableDirtyTracking = false"); +assert listOfStrings.contains("[DEBUG] (f) enableLazyInitialization = true"); +assert listOfStrings.contains("[DEBUG] (f) enableExtendedEnhancement = false"); +assert listOfStrings.contains("[DEBUG] Starting execution of enhance mojo"); +assert listOfStrings.contains("[DEBUG] Starting assembly of the source set"); +assert listOfStrings.contains("[DEBUG] Processing FileSet"); +assert listOfStrings.contains("[DEBUG] Using base directory: " + classesFolder); +assert listOfStrings.contains("[INFO] Added file to source set: " + barClassFile); +assert listOfStrings.contains("[INFO] Added file to source set: " + fooClassFile); +assert !listOfStrings.contains("[INFO] Added file to source set: " + bazClassFile); +assert listOfStrings.contains("[DEBUG] FileSet was processed succesfully"); +assert listOfStrings.contains("[DEBUG] Ending the assembly of the source set"); +assert listOfStrings.contains("[DEBUG] Creating bytecode enhancer"); +assert listOfStrings.contains("[DEBUG] Creating enhancement context"); +assert listOfStrings.contains("[DEBUG] Creating URL ClassLoader for folder: " + classesFolder); +assert listOfStrings.contains("[DEBUG] Starting type discovery"); +assert listOfStrings.contains("[DEBUG] Trying to discover types for classes in file: " + barClassFile); +assert listOfStrings.contains("[DEBUG] Determining class name for file: " + barClassFile); +assert listOfStrings.contains("[INFO] Succesfully discovered types for classes in file: " + barClassFile); +assert listOfStrings.contains("[DEBUG] Trying to discover types for classes in file: " + fooClassFile); +assert listOfStrings.contains("[DEBUG] Determining class name for file: " + fooClassFile); +assert listOfStrings.contains("[INFO] Succesfully discovered types for classes in file: " + fooClassFile); +assert listOfStrings.contains("[DEBUG] Ending type discovery"); +assert listOfStrings.contains("[DEBUG] Starting class enhancement"); +assert listOfStrings.contains("[DEBUG] Trying to enhance class file: " + barClassFile); +assert listOfStrings.contains("[INFO] Succesfully cleared the contents of file: " + barClassFile); +assert listOfStrings.contains("[DEBUG] " + amountOfBytes + " bytes were succesfully written to file: " +barClassFile); +assert listOfStrings.contains("[INFO] Succesfully enhanced class file: " + barClassFile); +assert listOfStrings.contains("[DEBUG] Trying to enhance class file: " + fooClassFile); +assert listOfStrings.contains("[INFO] Skipping file: " + fooClassFile); +assert listOfStrings.contains("[DEBUG] Ending class enhancement"); +assert listOfStrings.contains("[DEBUG] Ending execution of enhance mojo"); diff --git a/tooling/hibernate-maven-plugin/src/it/settings.xml b/tooling/hibernate-maven-plugin/src/it/settings.xml new file mode 100644 index 000000000000..15b3aee62bbb --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/it/settings.xml @@ -0,0 +1,46 @@ + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + false + + + true + + plexus-snapshots + Plexus Snapshot Repository + https://oss.sonatype.org/content/repositories/plexus-snapshots + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + \ No newline at end of file diff --git a/tooling/hibernate-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/enhance/EnhanceMojo.java b/tooling/hibernate-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/enhance/EnhanceMojo.java new file mode 100644 index 000000000000..630e2f1fca9f --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/enhance/EnhanceMojo.java @@ -0,0 +1,287 @@ +/* + * Copyright 20024 The Apache Software Foundation. + * + * 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.hibernate.orm.tooling.maven.enhance; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.shared.model.fileset.FileSet; +import org.apache.maven.shared.model.fileset.util.FileSetManager; +import org.hibernate.bytecode.enhance.spi.EnhancementException; +import org.hibernate.bytecode.enhance.spi.Enhancer; +import org.hibernate.bytecode.internal.BytecodeProviderInitiator; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/** + * Maven mojo for performing build-time enhancement of entity objects. + */ +@Mojo(name = "enhance", defaultPhase = LifecyclePhase.PROCESS_CLASSES) +public class EnhanceMojo extends AbstractMojo { + + private List sourceSet = new ArrayList(); + private Enhancer enhancer; + + @Parameter + private FileSet[] fileSets; + + @Parameter( + defaultValue = "${project.build.directory}/classes", + readonly = true, + required = true) + private File classesDirectory; + + @Parameter( + defaultValue = "false", + readonly = true, + required = true) + private boolean enableAssociationManagement; + + @Parameter( + defaultValue = "false", + readonly = true, + required = true) + private boolean enableDirtyTracking; + + @Parameter( + defaultValue = "false", + readonly = true, + required = true) + private boolean enableLazyInitialization; + + @Parameter( + defaultValue = "false", + readonly = true, + required = true) + private boolean enableExtendedEnhancement; + + public void execute() { + getLog().debug("Starting execution of enhance mojo"); + processParameters(); + assembleSourceSet(); + createEnhancer(); + discoverTypes(); + performEnhancement(); + getLog().debug("Ending execution of enhance mojo"); + } + + private void processParameters() { + if (!enableLazyInitialization) { + getLog().warn( "The 'enableLazyInitialization' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning" ); + } + if (!enableDirtyTracking) { + getLog().warn( "The 'enableDirtyTracking' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning" ); + } + if (fileSets == null) { + fileSets = new FileSet[1]; + fileSets[0] = new FileSet(); + fileSets[0].setDirectory(classesDirectory.getAbsolutePath()); + getLog().debug("Addded a default FileSet with base directory: " + fileSets[0].getDirectory()); + } + } + + private void assembleSourceSet() { + getLog().debug("Starting assembly of the source set"); + for (FileSet fileSet : fileSets) { + addFileSetToSourceSet(fileSet); + } + getLog().debug("Ending the assembly of the source set"); + } + + private void addFileSetToSourceSet(FileSet fileSet) { + getLog().debug("Processing FileSet"); + String directory = fileSet.getDirectory(); + FileSetManager fileSetManager = new FileSetManager(); + File baseDir = classesDirectory; + if (directory != null && classesDirectory != null) { + baseDir = new File(directory); + } + getLog().debug("Using base directory: " + baseDir); + for (String fileName : fileSetManager.getIncludedFiles(fileSet)) { + File candidateFile = new File(baseDir, fileName); + if (fileName.endsWith(".class")) { + sourceSet.add(candidateFile); + getLog().info("Added file to source set: " + candidateFile); + } else { + getLog().debug("Skipping non '.class' file: " + candidateFile); + } + } + getLog().debug("FileSet was processed succesfully"); + } + + private ClassLoader createClassLoader() { + getLog().debug("Creating URL ClassLoader for folder: " + classesDirectory) ; + List urls = new ArrayList<>(); + try { + urls.add(classesDirectory.toURI().toURL()); + } catch (MalformedURLException e) { + getLog().error("An unexpected error occurred while constructing the classloader", e); + } + return new URLClassLoader( + urls.toArray(new URL[urls.size()]), + Enhancer.class.getClassLoader()); + } + + private EnhancementContext createEnhancementContext() { + getLog().debug("Creating enhancement context") ; + return new EnhancementContext( + createClassLoader(), + enableAssociationManagement, + enableDirtyTracking, + enableLazyInitialization, + enableExtendedEnhancement); + } + + private void createEnhancer() { + getLog().debug("Creating bytecode enhancer") ; + enhancer = BytecodeProviderInitiator + .buildDefaultBytecodeProvider() + .getEnhancer(createEnhancementContext()); + } + + private void discoverTypes() { + getLog().debug("Starting type discovery") ; + for (File classFile : sourceSet) { + discoverTypesForClass(classFile); + } + getLog().debug("Ending type discovery") ; + } + + private void discoverTypesForClass(File classFile) { + getLog().debug("Trying to discover types for classes in file: " + classFile); + try { + enhancer.discoverTypes( + determineClassName(classFile), + Files.readAllBytes( classFile.toPath())); + getLog().info("Succesfully discovered types for classes in file: " + classFile); + } catch (IOException e) { + getLog().error("Unable to discover types for classes in file: " + classFile, e); + } + } + + private String determineClassName(File classFile) { + getLog().debug(DETERMINE_CLASS_NAME_FOR_FILE.formatted(classFile)); + String classFilePath = classFile.getAbsolutePath(); + String classesDirectoryPath = classesDirectory.getAbsolutePath(); + return classFilePath.substring( + classesDirectoryPath.length() + 1, + classFilePath.length() - ".class".length()) + .replace(File.separatorChar, '.'); + } + + private void performEnhancement() { + getLog().debug(STARTING_CLASS_ENHANCEMENT) ; + for (File classFile : sourceSet) { + long lastModified = classFile.lastModified(); + enhanceClass(classFile); + final boolean timestampReset = classFile.setLastModified( lastModified ); + if ( !timestampReset ) { + getLog().debug(SETTING_LASTMODIFIED_FAILED_FOR_CLASS_FILE.formatted(classFile)); + } + } + getLog().debug("Ending class enhancement") ; + } + + private void enhanceClass(File classFile) { + getLog().debug(TRYING_TO_ENHANCE_CLASS_FILE.formatted(classFile)); + try { + byte[] newBytes = enhancer.enhance( + determineClassName(classFile), + Files.readAllBytes(classFile.toPath())); + if (newBytes != null) { + writeByteCodeToFile(newBytes, classFile); + getLog().info(SUCCESFULLY_ENHANCED_CLASS_FILE.formatted(classFile)); + } else { + getLog().info(SKIPPING_FILE.formatted(classFile)); + } + } catch (EnhancementException | IOException e) { + getLog().error(ERROR_WHILE_ENHANCING_CLASS_FILE.formatted(classFile), e);; + } + } + + private void writeByteCodeToFile(byte[] bytes, File file) { + getLog().debug(WRITING_BYTE_CODE_TO_FILE.formatted(file)); + if (clearFile(file)) { + try { + Files.write( file.toPath(), bytes); + getLog().debug(AMOUNT_BYTES_WRITTEN_TO_FILE.formatted(bytes.length, file)); + } + catch (FileNotFoundException e) { + getLog().error(ERROR_OPENING_FILE_FOR_WRITING.formatted(file), e ); + } + catch (IOException e) { + getLog().error(ERROR_WRITING_BYTES_TO_FILE.formatted(file), e ); + } + } + } + + private boolean clearFile(File file) { + getLog().debug(TRYING_TO_CLEAR_FILE.formatted(file)); + boolean success = false; + if ( file.delete() ) { + try { + if ( !file.createNewFile() ) { + getLog().error(UNABLE_TO_CREATE_FILE.formatted(file)); + } else { + getLog().info(SUCCESFULLY_CLEARED_FILE.formatted(file)); + success = true; + } + } + catch (IOException e) { + getLog().warn(PROBLEM_CLEARING_FILE.formatted(file), e); + } + } + else { + getLog().error(UNABLE_TO_DELETE_FILE.formatted(file)); + } + return success; + } + + // info messages + static final String SUCCESFULLY_CLEARED_FILE = "Succesfully cleared the contents of file: %s"; + static final String SUCCESFULLY_ENHANCED_CLASS_FILE = "Succesfully enhanced class file: %s"; + static final String SKIPPING_FILE = "Skipping file: %s"; + + // warning messages + static final String PROBLEM_CLEARING_FILE = "Problem clearing file for writing out enhancements [ %s ]"; + + // error messages + static final String UNABLE_TO_CREATE_FILE = "Unable to create file: %s"; + static final String UNABLE_TO_DELETE_FILE = "Unable to delete file: %s"; + static final String ERROR_WRITING_BYTES_TO_FILE = "Error writing bytes to file : %s"; + static final String ERROR_OPENING_FILE_FOR_WRITING = "Error opening file for writing : %s"; + static final String ERROR_WHILE_ENHANCING_CLASS_FILE = "An exception occurred while trying to class file: %s"; + + // debug messages + static final String TRYING_TO_CLEAR_FILE = "Trying to clear the contents of file: %s"; + static final String AMOUNT_BYTES_WRITTEN_TO_FILE = "%s bytes were succesfully written to file: %s"; + static final String WRITING_BYTE_CODE_TO_FILE = "Writing byte code to file: %s"; + static final String DETERMINE_CLASS_NAME_FOR_FILE = "Determining class name for file: %s"; + static final String TRYING_TO_ENHANCE_CLASS_FILE = "Trying to enhance class file: %s"; + static final String STARTING_CLASS_ENHANCEMENT = "Starting class enhancement"; + static final String SETTING_LASTMODIFIED_FAILED_FOR_CLASS_FILE = "Setting lastModified failed for class file: %s"; + static final String ENDING_CLASS_ENHANCEMENT = "Ending class enhancement"; + +} diff --git a/tooling/hibernate-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/enhance/EnhancementContext.java b/tooling/hibernate-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/enhance/EnhancementContext.java new file mode 100644 index 000000000000..844c927e147d --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/main/java/org/hibernate/orm/tooling/maven/enhance/EnhancementContext.java @@ -0,0 +1,58 @@ +package org.hibernate.orm.tooling.maven.enhance; + +import org.hibernate.bytecode.enhance.spi.DefaultEnhancementContext; +import org.hibernate.bytecode.enhance.spi.UnloadedClass; +import org.hibernate.bytecode.enhance.spi.UnloadedField; + +public class EnhancementContext extends DefaultEnhancementContext { + + private ClassLoader classLoader = null; + private boolean enableAssociationManagement = false; + private boolean enableDirtyTracking = false; + private boolean enableLazyInitialization = false; + private boolean enableExtendedEnhancement = false; + + public EnhancementContext( + ClassLoader classLoader, + boolean enableAssociationManagement, + boolean enableDirtyTracking, + boolean enableLazyInitialization, + boolean enableExtendedEnhancement) { + this.classLoader = classLoader; + this.enableAssociationManagement = enableAssociationManagement; + this.enableDirtyTracking = enableDirtyTracking; + this.enableLazyInitialization = enableLazyInitialization; + this.enableExtendedEnhancement = enableExtendedEnhancement; + } + + @Override + public ClassLoader getLoadingClassLoader() { + return classLoader; + } + + @Override + public boolean doBiDirectionalAssociationManagement(UnloadedField field) { + return enableAssociationManagement; + } + + @Override + public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) { + return enableDirtyTracking; + } + + @Override + public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) { + return enableLazyInitialization; + } + + @Override + public boolean isLazyLoadable(UnloadedField field) { + return enableLazyInitialization; + } + + @Override + public boolean doExtendedEnhancement(UnloadedClass classDescriptor) { + return enableExtendedEnhancement; + } + +} diff --git a/tooling/hibernate-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/enhance/EnhanceMojoTest.java b/tooling/hibernate-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/enhance/EnhanceMojoTest.java new file mode 100644 index 000000000000..9c4479137f4c --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/enhance/EnhanceMojoTest.java @@ -0,0 +1,598 @@ +/* + * Copyright 20024 The Apache Software Foundation. + * + * 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.hibernate.orm.tooling.maven.enhance; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; + +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.shared.model.fileset.FileSet; +import org.hibernate.bytecode.enhance.internal.bytebuddy.EnhancerImpl; +import org.hibernate.bytecode.enhance.spi.EnhancementException; +import org.hibernate.bytecode.enhance.spi.Enhancer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import jakarta.persistence.Entity; + +public class EnhanceMojoTest { + + @TempDir + File tempDir; + + private List logMessages = new ArrayList(); + + private Field classesDirectoryField; + private Field fileSetsField; + private Field sourceSetField; + private Field enhancerField; + + private File classesDirectory; // folder '${tempDir}/classes' + private File fooFolder; // folder '${classesDirectory}/org/foo' + private File barFolder; // folder '${classesDirectory}/bar' + private File barClassFile; // file '${fooFolder}/Bar.class' + private File fooTxtFile; // file '${barFolder}/Foo.txt' + + private EnhanceMojo enhanceMojo; + + @BeforeEach + void beforeEach() throws Exception { + classesDirectoryField = EnhanceMojo.class.getDeclaredField("classesDirectory"); + classesDirectoryField.setAccessible(true); + fileSetsField = EnhanceMojo.class.getDeclaredField("fileSets"); + fileSetsField.setAccessible(true); + sourceSetField = EnhanceMojo.class.getDeclaredField("sourceSet"); + sourceSetField.setAccessible(true); + enhancerField = EnhanceMojo.class.getDeclaredField("enhancer"); + enhancerField.setAccessible(true); + enhanceMojo = new EnhanceMojo(); + enhanceMojo.setLog(createLog()); + classesDirectory = new File(tempDir, "classes"); + classesDirectory.mkdirs(); + classesDirectoryField.set(enhanceMojo, classesDirectory); + fooFolder = new File(classesDirectory, "org/foo"); + fooFolder.mkdirs(); + barFolder = new File(classesDirectory, "bar"); + barFolder.mkdirs(); + barClassFile = new File(fooFolder, "Bar.class"); + barClassFile.createNewFile(); + fooTxtFile = new File (barFolder, "Foo.txt"); + fooTxtFile.createNewFile(); + } + + @Test + void testAssembleSourceSet() throws Exception { + Method assembleSourceSetMethod = EnhanceMojo.class.getDeclaredMethod("assembleSourceSet"); + assembleSourceSetMethod.setAccessible(true); + FileSet[] fileSets = new FileSet[1]; + fileSets[0] = new FileSet(); + fileSets[0].setDirectory(classesDirectory.getAbsolutePath()); + fileSetsField.set(enhanceMojo, fileSets); + List sourceSet = (List)sourceSetField.get(enhanceMojo); + assertTrue(sourceSet.isEmpty()); + assembleSourceSetMethod.invoke(enhanceMojo); + assertFalse(sourceSet.isEmpty()); + assertTrue(sourceSet.contains(barClassFile)); + assertFalse(sourceSet.contains(fooTxtFile)); + assertEquals(1, sourceSet.size()); + } + + @Test + void testAddFileSetToSourceSet() throws Exception { + Method addFileSetToSourceSetMethod = EnhanceMojo.class.getDeclaredMethod( + "addFileSetToSourceSet", + new Class[] { FileSet.class}); + addFileSetToSourceSetMethod.setAccessible(true); + File fooClassFile = new File(fooFolder, "Foo.class"); + fooClassFile.createNewFile(); + File bazFolder = new File(classesDirectory, "org/baz"); + bazFolder.mkdirs(); + File bazClassFile = new File(bazFolder, "Baz.class"); + bazClassFile.createNewFile(); + FileSet fileSet = new FileSet(); + fileSet.setDirectory(classesDirectory.getAbsolutePath()); + fileSet.addInclude("**/Foo*"); + fileSet.addInclude("**/*.class"); + fileSet.addExclude("**/baz/**"); + addFileSetToSourceSetMethod.invoke(enhanceMojo, fileSet); + assertTrue(logMessages.contains("[DEBUG] Processing FileSet")); + assertTrue(logMessages.contains("[DEBUG] Using base directory: " + classesDirectory)); + assertTrue(logMessages.contains("[INFO] Added file to source set: " + barClassFile)); + assertTrue(logMessages.contains("[INFO] Added file to source set: " + fooClassFile)); + assertFalse(logMessages.contains("[INFO] Added file to source set: " + bazClassFile)); + assertTrue(logMessages.contains("[DEBUG] Skipping non '.class' file: " + fooTxtFile)); + assertTrue(logMessages.contains("[DEBUG] FileSet was processed succesfully")); + } + + @Test + void testCreateClassLoader() throws Exception { + Method createClassLoaderMethod = EnhanceMojo.class.getDeclaredMethod("createClassLoader"); + createClassLoaderMethod.setAccessible(true); + ClassLoader classLoader = (ClassLoader)createClassLoaderMethod.invoke(enhanceMojo); + assertNotNull(classLoader); + URL fooResource = classLoader.getResource("bar/Foo.txt"); + assertNotNull(fooResource); + assertEquals(fooTxtFile.toURI().toURL(), fooResource); + } + + @Test + void testCreateEnhancementContext() throws Exception { + Method createEnhancementContextMethod = EnhanceMojo.class.getDeclaredMethod("createEnhancementContext"); + createEnhancementContextMethod.setAccessible(true); + EnhancementContext enhancementContext = (EnhancementContext)createEnhancementContextMethod.invoke(enhanceMojo); + URLClassLoader classLoader = (URLClassLoader)enhancementContext.getLoadingClassLoader(); + assertEquals(classesDirectory.toURI().toURL(), classLoader.getURLs()[0]); + assertFalse(enhancementContext.doBiDirectionalAssociationManagement(null)); + assertFalse(enhancementContext.doDirtyCheckingInline(null)); + assertFalse(enhancementContext.hasLazyLoadableAttributes(null)); + assertFalse(enhancementContext.isLazyLoadable(null)); + assertFalse(enhancementContext.doExtendedEnhancement(null)); + Field enableAssociationManagementField = EnhanceMojo.class.getDeclaredField("enableAssociationManagement"); + enableAssociationManagementField.setAccessible(true); + enableAssociationManagementField.set(enhanceMojo, Boolean.TRUE); + enhancementContext = (EnhancementContext)createEnhancementContextMethod.invoke(enhanceMojo); + assertEquals(classesDirectory.toURI().toURL(), classLoader.getURLs()[0]); + assertTrue(enhancementContext.doBiDirectionalAssociationManagement(null)); + assertFalse(enhancementContext.doDirtyCheckingInline(null)); + assertFalse(enhancementContext.hasLazyLoadableAttributes(null)); + assertFalse(enhancementContext.isLazyLoadable(null)); + assertFalse(enhancementContext.doExtendedEnhancement(null)); + Field enableDirtyTrackingField = EnhanceMojo.class.getDeclaredField("enableDirtyTracking"); + enableDirtyTrackingField.setAccessible(true); + enableDirtyTrackingField.set(enhanceMojo, Boolean.TRUE); + enhancementContext = (EnhancementContext)createEnhancementContextMethod.invoke(enhanceMojo); + assertEquals(classesDirectory.toURI().toURL(), classLoader.getURLs()[0]); + assertTrue(enhancementContext.doBiDirectionalAssociationManagement(null)); + assertTrue(enhancementContext.doDirtyCheckingInline(null)); + assertFalse(enhancementContext.hasLazyLoadableAttributes(null)); + assertFalse(enhancementContext.isLazyLoadable(null)); + assertFalse(enhancementContext.doExtendedEnhancement(null)); + Field enableLazyInitializationField = EnhanceMojo.class.getDeclaredField("enableLazyInitialization"); + enableLazyInitializationField.setAccessible(true); + enableLazyInitializationField.set(enhanceMojo, Boolean.TRUE); + enhancementContext = (EnhancementContext)createEnhancementContextMethod.invoke(enhanceMojo); + assertEquals(classesDirectory.toURI().toURL(), classLoader.getURLs()[0]); + assertTrue(enhancementContext.doBiDirectionalAssociationManagement(null)); + assertTrue(enhancementContext.doDirtyCheckingInline(null)); + assertTrue(enhancementContext.hasLazyLoadableAttributes(null)); + assertTrue(enhancementContext.isLazyLoadable(null)); + assertFalse(enhancementContext.doExtendedEnhancement(null)); + Field enableExtendedEnhancementField = EnhanceMojo.class.getDeclaredField("enableExtendedEnhancement"); + enableExtendedEnhancementField.setAccessible(true); + enableExtendedEnhancementField.set(enhanceMojo, Boolean.TRUE); + enhancementContext = (EnhancementContext)createEnhancementContextMethod.invoke(enhanceMojo); + assertEquals(classesDirectory.toURI().toURL(), classLoader.getURLs()[0]); + assertTrue(enhancementContext.doBiDirectionalAssociationManagement(null)); + assertTrue(enhancementContext.doDirtyCheckingInline(null)); + assertTrue(enhancementContext.hasLazyLoadableAttributes(null)); + assertTrue(enhancementContext.isLazyLoadable(null)); + assertTrue(enhancementContext.doExtendedEnhancement(null)); + } + + @Test + void testCreateEnhancer() throws Exception { + Method createEnhancerMethod = EnhanceMojo.class.getDeclaredMethod("createEnhancer"); + createEnhancerMethod.setAccessible(true); + Enhancer enhancer = (Enhancer)enhancerField.get(enhanceMojo); + assertNull(enhancer); + createEnhancerMethod.invoke(enhanceMojo); + enhancer = (Enhancer)enhancerField.get(enhanceMojo); + assertNotNull(enhancer); + Field byteByddyEnhancementContextField = EnhancerImpl.class.getDeclaredField("enhancementContext"); + byteByddyEnhancementContextField.setAccessible(true); + Object byteByddyEnhancementContext = byteByddyEnhancementContextField.get(enhancer); + assertNotNull(byteByddyEnhancementContext); + Field enhancementContextField = byteByddyEnhancementContext.getClass().getDeclaredField("enhancementContext"); + enhancementContextField.setAccessible(true); + EnhancementContext enhancementContext = (EnhancementContext)enhancementContextField.get(byteByddyEnhancementContext); + assertNotNull(enhancementContext); + ClassLoader classLoader = enhancementContext.getLoadingClassLoader(); + assertNotNull(classLoader); + assertNotNull(classLoader); + URL fooResource = classLoader.getResource("bar/Foo.txt"); + assertNotNull(fooResource); + assertEquals(fooTxtFile.toURI().toURL(), fooResource); + } + + @Test + void testDetermineClassName() throws Exception { + Method determineClassNameMethod = EnhanceMojo.class.getDeclaredMethod( + "determineClassName", + new Class[] { File.class }); + determineClassNameMethod.setAccessible(true); + assertEquals("org.foo.Bar", determineClassNameMethod.invoke(enhanceMojo, barClassFile)); + // check log messages + assertEquals(1, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.DETERMINE_CLASS_NAME_FOR_FILE.formatted(barClassFile))); + } + + @Test + void testDiscoverTypesForClass() throws Exception { + final List hasRun = new ArrayList(); + Method discoverTypesForClassMethod = EnhanceMojo.class.getDeclaredMethod( + "discoverTypesForClass", + new Class[] { File.class }); + discoverTypesForClassMethod.setAccessible(true); + Enhancer enhancer = (Enhancer)Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Enhancer.class }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("discoverTypes")) { + assertEquals("org.foo.Bar", args[0]); + hasRun.add(0, true); + } + return null; + } + }); + enhancerField.set(enhanceMojo, enhancer); + assertFalse(hasRun.contains(true)); + discoverTypesForClassMethod.invoke(enhanceMojo, barClassFile); + assertTrue(hasRun.contains(true)); + } + + @Test + void testDiscoverTypes() throws Exception { + final List hasRun = new ArrayList(); + Method discoverTypesMethod = EnhanceMojo.class.getDeclaredMethod( + "discoverTypes", + new Class[] { }); + discoverTypesMethod.setAccessible(true); + Enhancer enhancer = (Enhancer)Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Enhancer.class }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("discoverTypes")) { + assertEquals("org.foo.Bar", args[0]); + hasRun.add(0, true); + } + return null; + } + }); + enhancerField.set(enhanceMojo, enhancer); + assertFalse(hasRun.contains(true)); + discoverTypesMethod.invoke(enhanceMojo); + assertFalse(hasRun.contains(true)); + List sourceSet = new ArrayList(); + sourceSet.add(barClassFile); + sourceSetField.set(enhanceMojo, sourceSet); + discoverTypesMethod.invoke(enhanceMojo); + assertTrue(hasRun.contains(true)); + } + + @Test + void testClearFile() throws Exception { + Method clearFileMethod = EnhanceMojo.class.getDeclaredMethod( + "clearFile", + new Class[] { File.class }); + clearFileMethod.setAccessible(true); + Files.writeString(fooTxtFile.toPath(), "foobar"); + fooTxtFile.setLastModified(0); + assertEquals("foobar", new String(Files.readAllBytes(fooTxtFile.toPath()))); + boolean result = (boolean)clearFileMethod.invoke(enhanceMojo, new File("foobar")); + assertFalse(result); + result = (boolean)clearFileMethod.invoke(enhanceMojo, fooTxtFile); + long modified = fooTxtFile.lastModified(); + assertTrue(result); + // File should be empty + assertTrue(Files.readAllBytes(fooTxtFile.toPath()).length == 0); + // last modification 'after' should be after 'before' + assertNotEquals(0, modified); + assertTrue(modified > 0); + // check log messages + assertEquals(4, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_CLEAR_FILE.formatted("foobar"))); + assertTrue(logMessages.contains(ERROR + EnhanceMojo.UNABLE_TO_DELETE_FILE.formatted("foobar"))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_CLEAR_FILE.formatted(fooTxtFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SUCCESFULLY_CLEARED_FILE.formatted(fooTxtFile))); + } + + @Test + void testWriteByteCodeToFile() throws Exception { + Method writeByteCodeToFileMethod = EnhanceMojo.class.getDeclaredMethod( + "writeByteCodeToFile", + new Class[] { byte[].class, File.class}); + writeByteCodeToFileMethod.setAccessible(true); + fooTxtFile.setLastModified(0); + // File fooTxtFile is empty + assertTrue(Files.readAllBytes(fooTxtFile.toPath()).length == 0); + writeByteCodeToFileMethod.invoke(enhanceMojo, "foobar".getBytes(), fooTxtFile); + long modified = fooTxtFile.lastModified(); + // last modification 'after' should be after 'before' + assertNotEquals(0, modified); + assertTrue(modified > 0); + // File should be contain 'foobar' + assertEquals(new String(Files.readAllBytes(fooTxtFile.toPath())), "foobar"); + // check log messages + assertEquals(4, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.WRITING_BYTE_CODE_TO_FILE.formatted(fooTxtFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_CLEAR_FILE.formatted(fooTxtFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SUCCESFULLY_CLEARED_FILE.formatted(fooTxtFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.AMOUNT_BYTES_WRITTEN_TO_FILE.formatted(6, fooTxtFile))); + } + + @Test + void testEnhanceClass() throws Exception { + final List calls = new ArrayList(); + calls.add(0, 0); + Method enhanceClassMethod = EnhanceMojo.class.getDeclaredMethod( + "enhanceClass", + new Class[] { File.class }); + enhanceClassMethod.setAccessible(true); + Enhancer enhancer = (Enhancer)Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Enhancer.class }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + calls.set(0, calls.get(0) + 1); + if (method.getName().equals("enhance")) { + assertEquals("org.foo.Bar", args[0]); + } + if (calls.get(0) == 1) { + return "foobar".getBytes(); + } else if (calls.get(0) == 2) { + return null; + } else { + throw new EnhancementException("foobar"); + } + } + }); + long beforeRuns = barClassFile.lastModified(); + // First Run -> file is modified + enhancerField.set(enhanceMojo, enhancer); + assertEquals(0, calls.get(0)); + enhanceClassMethod.invoke(enhanceMojo, barClassFile); + long afterFirstRun = barClassFile.lastModified(); + assertEquals(1, calls.get(0)); + assertTrue(afterFirstRun >= beforeRuns); + assertEquals("foobar", new String(Files.readAllBytes(barClassFile.toPath()))); + // verify log messages + assertEquals(7, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_ENHANCE_CLASS_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.DETERMINE_CLASS_NAME_FOR_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.WRITING_BYTE_CODE_TO_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_CLEAR_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SUCCESFULLY_CLEARED_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.AMOUNT_BYTES_WRITTEN_TO_FILE.formatted("foobar".length(), barClassFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SUCCESFULLY_ENHANCED_CLASS_FILE.formatted(barClassFile))); + // Second Run -> file is not modified + logMessages.clear(); + enhanceClassMethod.invoke(enhanceMojo, barClassFile); + long afterSecondRun = barClassFile.lastModified(); + assertEquals(2, calls.get(0)); + assertEquals(afterSecondRun, afterFirstRun); + assertEquals("foobar", new String(Files.readAllBytes(barClassFile.toPath()))); + // verify log messages + assertEquals(3, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_ENHANCE_CLASS_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.DETERMINE_CLASS_NAME_FOR_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SKIPPING_FILE.formatted(barClassFile))); + // Third Run -> exception! + logMessages.clear(); + try { + enhanceClassMethod.invoke(enhanceMojo, barClassFile); + fail(); + } catch (Throwable e) { + long afterThirdRun = barClassFile.lastModified(); + assertEquals(3, calls.get(0)); + assertEquals(afterThirdRun, afterFirstRun); + assertEquals("foobar", new String(Files.readAllBytes(barClassFile.toPath()))); + // verify log messages + assertEquals(3, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_ENHANCE_CLASS_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.DETERMINE_CLASS_NAME_FOR_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(ERROR + EnhanceMojo.ERROR_WHILE_ENHANCING_CLASS_FILE.formatted(barClassFile))); + } + } + + @Test + void testPerformEnhancement() throws Exception { + final List hasRun = new ArrayList(); + Method performEnhancementMethod = EnhanceMojo.class.getDeclaredMethod( + "performEnhancement", + new Class[] { }); + performEnhancementMethod.setAccessible(true); + Enhancer enhancer = (Enhancer)Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Enhancer.class }, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("enhance")) { + assertEquals("org.foo.Bar", args[0]); + hasRun.add(0, true); + } + return "foobar".getBytes(); + } + }); + enhancerField.set(enhanceMojo, enhancer); + List sourceSet = new ArrayList(); + sourceSet.add(barClassFile); + sourceSetField.set(enhanceMojo, sourceSet); + long lastModified = barClassFile.lastModified(); + assertFalse(hasRun.contains(true)); + assertNotEquals("foobar", new String(Files.readAllBytes(barClassFile.toPath()))); + performEnhancementMethod.invoke(enhanceMojo); + assertTrue(hasRun.contains(true)); + assertEquals("foobar", new String(Files.readAllBytes(barClassFile.toPath()))); + assertEquals(lastModified, barClassFile.lastModified()); + // verify the log messages + assertEquals(9, logMessages.size()); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.STARTING_CLASS_ENHANCEMENT)); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_ENHANCE_CLASS_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.DETERMINE_CLASS_NAME_FOR_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.WRITING_BYTE_CODE_TO_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.TRYING_TO_CLEAR_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SUCCESFULLY_CLEARED_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.AMOUNT_BYTES_WRITTEN_TO_FILE.formatted("foobar".length(), barClassFile))); + assertTrue(logMessages.contains(INFO + EnhanceMojo.SUCCESFULLY_ENHANCED_CLASS_FILE.formatted(barClassFile))); + assertTrue(logMessages.contains(DEBUG + EnhanceMojo.ENDING_CLASS_ENHANCEMENT)); + } + + @Test + void testExecute() throws Exception { + Method executeMethod = EnhanceMojo.class.getDeclaredMethod("execute", new Class[] {}); + executeMethod.setAccessible(true); + String lazyInitializationDeprecatedWarning = + "[WARNING] The 'enableLazyInitialization' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning"; + String dirtyTrackingDeprecatedWarning = + "[WARNING] The 'enableDirtyTracking' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning"; + final String barSource = + "package org.foo;" + + "import jakarta.persistence.Entity;" + + "@Entity public class Bar { "+ + " private String foo; " + + " String getFoo() { return foo; } " + + " public void setFoo(String f) { foo = f; } " + + "}"; + File barJavaFile = new File(fooFolder, "Bar.java"); + Files.writeString(barJavaFile.toPath(), barSource); + final String fooSource = + "package org.foo;" + + "public class Foo { "+ + " private Bar bar; " + + " Bar getBar() { return bar; } " + + " public void setBar(Bar b) { bar = b; } " + + "}"; + File fooJavaFile = new File(fooFolder, "Foo.java"); + Files.writeString(fooJavaFile.toPath(), fooSource); + File fooClassFile = new File(fooFolder, "Foo.class"); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + URL url = Entity.class.getProtectionDomain().getCodeSource().getLocation(); + String classpath = new File(url.toURI()).getAbsolutePath(); + String[] options = List.of( + "-cp", + classpath, + barJavaFile.getAbsolutePath(), + fooJavaFile.getAbsolutePath()).toArray(new String[] {}); + compiler.run(null, null, null, options); + String barBytesString = new String(Files.readAllBytes(barClassFile.toPath())); + String fooBytesString = new String(Files.readAllBytes(fooClassFile.toPath())); + List sourceSet = new ArrayList(); + sourceSet.add(barClassFile); + sourceSet.add(fooClassFile); + sourceSetField.set(enhanceMojo, sourceSet); + assertTrue(logMessages.isEmpty()); + executeMethod.invoke(enhanceMojo); + assertTrue(logMessages.contains(lazyInitializationDeprecatedWarning)); + assertTrue(logMessages.contains(dirtyTrackingDeprecatedWarning)); + assertNotEquals(barBytesString, new String(Files.readAllBytes(barClassFile.toPath()))); + assertEquals(fooBytesString, new String(Files.readAllBytes(fooClassFile.toPath()))); + URLClassLoader classLoader = new URLClassLoader( + new URL[] {classesDirectory.toURI().toURL()}, + getClass().getClassLoader()); + Class barClass = classLoader.loadClass("org.foo.Bar"); + assertNotNull(barClass); + Method m = barClass.getMethod("$$_hibernate_getEntityInstance", new Class[]{}); + assertNotNull(m); + Class fooClass = classLoader.loadClass("org.foo.Foo"); + try { + m = fooClass.getMethod("$$_hibernate_getEntityInstance", new Class[]{}); + fail(); + } catch (NoSuchMethodException e) { + assertEquals("org.foo.Foo.$$_hibernate_getEntityInstance()", e.getMessage()); + } + classLoader.close(); + } + + @Test + void testProcessParameters() throws Exception { + String lazyInitializationDeprecatedWarning = + "[WARNING] The 'enableLazyInitialization' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning"; + String dirtyTrackingDeprecatedWarning = + "[WARNING] The 'enableDirtyTracking' configuration is deprecated and will be removed. Set the value to 'true' to get rid of this warning"; + String defaultFileSetAddedMessage = + "[DEBUG] Addded a default FileSet with base directory: " + classesDirectory.getAbsolutePath(); + Method verifyParametersMethod = EnhanceMojo.class.getDeclaredMethod( + "processParameters", + new Class[] {}); + verifyParametersMethod.setAccessible(true); + Field enableLazyInitializationField = EnhanceMojo.class.getDeclaredField("enableLazyInitialization"); + enableLazyInitializationField.setAccessible(true); + Field enableDirtyTrackingField = EnhanceMojo.class.getDeclaredField("enableDirtyTracking"); + enableDirtyTrackingField.setAccessible(true); + assertTrue(logMessages.isEmpty()); + assertNull(fileSetsField.get(enhanceMojo)); + verifyParametersMethod.invoke(enhanceMojo); + assertEquals(3, logMessages.size()); + assertTrue(logMessages.contains(lazyInitializationDeprecatedWarning)); + assertTrue(logMessages.contains(dirtyTrackingDeprecatedWarning)); + assertTrue(logMessages.contains(defaultFileSetAddedMessage)); + FileSet[] fileSets = (FileSet[])fileSetsField.get(enhanceMojo); + assertNotNull(fileSets); + assertEquals(1, fileSets.length); + assertEquals(classesDirectory.getAbsolutePath(), fileSets[0].getDirectory()); + fileSetsField.set(enhanceMojo, null); + logMessages.clear(); + assertTrue(logMessages.isEmpty()); + enableLazyInitializationField.set(enhanceMojo, Boolean.TRUE); + enableDirtyTrackingField.set(enhanceMojo, Boolean.TRUE); + verifyParametersMethod.invoke(enhanceMojo); + assertEquals(1, logMessages.size()); + assertTrue(logMessages.contains(defaultFileSetAddedMessage)); + } + + private Log createLog() { + return (Log)Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { Log.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("info".equals(method.getName())) { + logMessages.add(INFO + args[0]); + } else if ("warn".equals(method.getName())) { + logMessages.add(WARNING + args[0]); + } else if ("error".equals(method.getName())) { + logMessages.add(ERROR + args[0]); + } else if ("debug".equals(method.getName())) { + logMessages.add(DEBUG + args[0]); + } + return null; + } + }); + } + + static final String DEBUG = "[DEBUG] "; + static final String ERROR = "[ERROR] "; + static final String WARNING = "[WARNING] "; + static final String INFO = "[INFO] "; + +} diff --git a/tooling/hibernate-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/enhance/EnhancementContextTest.java b/tooling/hibernate-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/enhance/EnhancementContextTest.java new file mode 100644 index 000000000000..6730bbcae216 --- /dev/null +++ b/tooling/hibernate-maven-plugin/src/test/java/org/hibernate/orm/tooling/maven/enhance/EnhancementContextTest.java @@ -0,0 +1,64 @@ +package org.hibernate.orm.tooling.maven.enhance; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.junit.jupiter.api.Test; + +public class EnhancementContextTest { + + @Test + void testGetClassLoader() { + ClassLoader testLoader = new URLClassLoader(new URL[0]); + EnhancementContext context = new EnhancementContext(null, false, false, false, false); + assertNull(context.getLoadingClassLoader()); + context = new EnhancementContext(testLoader, false, false, false, false); + assertSame(testLoader, context.getLoadingClassLoader()); + } + + @Test + void testDoBiDirectionalAssociationManagement() { + EnhancementContext context = new EnhancementContext(null, false, false, false, false); + assertFalse(context.doBiDirectionalAssociationManagement(null)); + context = new EnhancementContext(null, true, false, false, false); + assertTrue(context.doBiDirectionalAssociationManagement(null)); + } + + @Test + void testDoDirtyCheckingInline() { + EnhancementContext context = new EnhancementContext(null, false, false, false, false); + assertFalse(context.doDirtyCheckingInline(null)); + context = new EnhancementContext(null, false, true, false, false); + assertTrue(context.doDirtyCheckingInline(null)); + } + + @Test + void testHasLazyLoadableAttributes() { + EnhancementContext context = new EnhancementContext(null, false, false, false, false); + assertFalse(context.hasLazyLoadableAttributes(null)); + context = new EnhancementContext(null, false, false, true, false); + assertTrue(context.hasLazyLoadableAttributes(null)); + } + + @Test + void testIsLazyLoadable() { + EnhancementContext context = new EnhancementContext(null, false, false, false, false); + assertFalse(context.isLazyLoadable(null)); + context = new EnhancementContext(null, false, false, true, false); + assertTrue(context.isLazyLoadable(null)); + } + + @Test + void testDoExtendedEnhancement() { + EnhancementContext context = new EnhancementContext(null, false, false, false, false); + assertFalse(context.doExtendedEnhancement(null)); + context = new EnhancementContext(null, false, false, false, true); + assertTrue(context.doExtendedEnhancement(null)); + } + +} diff --git a/tooling/metamodel-generator/hibernate-processor.gradle b/tooling/metamodel-generator/hibernate-processor.gradle index 5dca8ed1227d..54aeb9b78b19 100644 --- a/tooling/metamodel-generator/hibernate-processor.gradle +++ b/tooling/metamodel-generator/hibernate-processor.gradle @@ -8,7 +8,7 @@ import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis */ description = 'Hibernate compile-time tooling' -apply from: rootProject.file( 'gradle/published-java-module.gradle' ) +apply from: rootProject.file( 'gradle/relocated-published-java-module.gradle' ) apply plugin: 'org.hibernate.build.version-injection' //java {