diff --git a/.travis.yml b/.travis.yml
index 6fc0e918dc..437679a4be 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ before_script:
- java -version
- ./gradlew assemble
script:
- - ./gradlew check -Plog-test-progress=true
+ - ./gradlew check -Plog-test-progress=true --stacktrace
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
diff --git a/build.gradle b/build.gradle
index 7a2923b1eb..cdcb0e2e9a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,6 +10,7 @@ buildscript {
jcenter()
mavenCentral()
}
+
dependencies {
classpath 'org.hibernate.build.gradle:hibernate-matrix-testing:3.0.0.Final'
classpath 'org.hibernate.build.gradle:version-injection-plugin:1.0.0'
diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle
index a473decd0e..b1fc929bf0 100644
--- a/documentation/documentation.gradle
+++ b/documentation/documentation.gradle
@@ -12,6 +12,8 @@ ext {
projectsToSkipWhenAggregatingJavadocs = [
'documentation',
'hibernate-entitymanager',
+ 'hibernate-gradle-plugin',
+ 'project-template',
'hibernate-infinispan',
'hibernate-ehcache',
'hibernate-java8',
diff --git a/gradle/javadoc.gradle b/gradle/javadoc.gradle
new file mode 100644
index 0000000000..9c8930f66e
--- /dev/null
+++ b/gradle/javadoc.gradle
@@ -0,0 +1,60 @@
+/*
+ * 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
+ */
+
+// make sure Java plugin is applied
+apply plugin : 'java'
+
+apply from: rootProject.file( 'gradle/base-information.gradle' )
+
+javadoc {
+ exclude( "**/internal/*" )
+ exclude( "**/generated-src/**" )
+
+ final int currentYear = new GregorianCalendar().get( Calendar.YEAR )
+
+ configure( options ) {
+ // this is the config needed to use asciidoclet for Javadoc rendering. It relies on a build from John's PR @ https://github.com/asciidoctor/asciidoclet/pull/91
+ // however, the PR does not work for me in that Javadocs with `@asciidoclet` are not rendered using asciidoc(tor/let). Also tried the preferable `@asciidoc`
+ // with the same result. Leaving all this config in place however as the outcome is the same as not enabling it.
+ // todo (6.0) : need to find out why the asciidoclet PR does not work
+ //
+ // Travis CI JDK 11 build did not like this
+// docletpath = configurations.asciidoclet.files.asType(List)
+// doclet = 'org.asciidoctor.Asciidoclet'
+ windowTitle = "$project.name JavaDocs"
+ docTitle = "$project.name JavaDocs ($project.version)"
+ bottom = "Copyright © 2001-$currentYear Red Hat, Inc. All Rights Reserved."
+ use = true
+ encoding = 'UTF-8'
+ links += [
+ 'https://docs.oracle.com/javase/8/docs/api/',
+ 'http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/',
+ 'http://docs.jboss.org/cdi/api/2.0/',
+ 'https://javaee.github.io/javaee-spec/javadocs/'
+ ]
+ tags = [ "apiNote", 'implSpec', 'implNote', 'todo' ]
+
+ if ( JavaVersion.current().isJava11Compatible() ) {
+ //The need to set `--source 1.8` applies to all JVMs after 11, and also to 11
+ // but after excluding the first two builds; see also specific comments on
+ // https://bugs.openjdk.java.net/browse/JDK-8212233?focusedCommentId=14245762
+ // For now, let's be compatible with JDK 11.0.3+. We can improve on it if people
+ // complain they cannot build with JDK 11.0.0, 11.0.1 and 11.0.2.
+ System.out.println("Forcing Javadoc in Java 8 compatible mode");
+ options.source = project.baselineJavaVersion
+ }
+
+ addStringOption( 'Xdoclint:none', '-quiet' )
+
+ tags(
+ 'todo:X"',
+ 'apiNote:a:"API Note:"',
+ 'implSpec:a:"Implementation Specification:"',
+ 'implNote:a:"Implementation Note:"'
+ )
+ }
+}
\ No newline at end of file
diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle
index 7c94f74a13..a7c8ba0d99 100644
--- a/gradle/libraries.gradle
+++ b/gradle/libraries.gradle
@@ -10,8 +10,8 @@
ext {
junitVersion = '4.12'
- junitVintageVersion = '5.3.1'
- junit5Version = '5.3.1'
+ junitVintageVersion = '5.4.2'
+ junit5Version = '5.4.2'
h2Version = '1.4.199'
bytemanVersion = '4.0.13' //Compatible with JDK16
diff --git a/gradle/published-java-module.gradle b/gradle/published-java-module.gradle
index 9f1a181764..d971564513 100644
--- a/gradle/published-java-module.gradle
+++ b/gradle/published-java-module.gradle
@@ -117,61 +117,9 @@ task javadocJar(type: Jar) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Javadoc
+apply from: rootProject.file( 'gradle/javadoc.gradle' )
+
javadoc {
- exclude( "**/internal/*" )
- exclude( "**/generated-src/**" )
-
- final int currentYear = new GregorianCalendar().get( Calendar.YEAR )
-
- configure( options ) {
- // this is the config needed to use asciidoclet for Javadoc rendering. It relies on a build from John's PR @ https://github.com/asciidoctor/asciidoclet/pull/91
- // however, the PR does not work for me in that Javadocs with `@asciidoclet` are not rendered using asciidoc(tor/let). Also tried the preferable `@asciidoc`
- // with the same result. Leaving all this config in place however as the outcome is the same as not enabling it.
- // todo (6.0) : need to find out why the asciidoclet PR does not work
- //
- // Travis CI JDK 11 build did not like this
-// docletpath = configurations.asciidoclet.files.asType(List)
-// doclet = 'org.asciidoctor.Asciidoclet'
- windowTitle = "$project.name JavaDocs"
- docTitle = "$project.name JavaDocs ($project.version)"
- bottom = "Copyright © 2001-$currentYear Red Hat, Inc. All Rights Reserved."
- use = true
- encoding = 'UTF-8'
- links += [
- 'https://docs.oracle.com/javase/8/docs/api/',
- 'http://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/',
- 'http://docs.jboss.org/cdi/api/2.0/',
- 'https://javaee.github.io/javaee-spec/javadocs/'
- ]
- tags = [ "apiNote", 'implSpec', 'implNote', 'todo' ]
-
- if ( JavaVersion.current().isJava11Compatible() ) {
- //The need to set `--source 1.8` applies to all JVMs after 11, and also to 11
- // but after excluding the first two builds; see also specific comments on
- // https://bugs.openjdk.java.net/browse/JDK-8212233?focusedCommentId=14245762
- // For now, let's be compatible with JDK 11.0.3+. We can improve on it if people
- // complain they cannot build with JDK 11.0.0, 11.0.1 and 11.0.2.
- System.out.println("Forcing Javadoc in Java 8 compatible mode");
- options.source = project.baselineJavaVersion
- }
-
- if ( JavaVersion.current().isJava8Compatible() ) {
- addStringOption( 'Xdoclint:none', '-quiet' )
- }
-
-// // by default, exclude the files from Asciidoclet processing
-// // add the @asciidoclet tag to enable Asciidoclet on a particular file
-// options.addStringOption( '-exclude-asciidoclet-process', '**' )
-
- tags(
- 'todo:X"',
- 'apiNote:a:"API Note:"',
- 'implSpec:a:"Implementation Specification:"',
- 'implNote:a:"Implementation Note:"'
- )
- }
- options.addStringOption( 'Xdoclint:none', '-quiet' )
-
doFirst {
// ordering problems if we try to do this during config phase :(
classpath += project.sourceSets.main.output.classesDirs + project.sourceSets.main.compileClasspath + project.configurations.provided
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java
index 6d902d046d..59c195782d 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java
@@ -194,8 +194,7 @@ abstract class AggregatedServiceLoader {
Set result = new LinkedHashSet<>();
// Always try the aggregated class loader first
- Iterator extends Supplier> providerIterator = providerStream( aggregatedClassLoaderServiceLoader )
- .iterator();
+ Iterator extends Supplier> providerIterator = providerStream( aggregatedClassLoaderServiceLoader ).iterator();
while ( providerIterator.hasNext() ) {
Supplier provider = providerIterator.next();
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );
diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java
index 2cf9064368..7f105070ca 100644
--- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java
+++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java
@@ -1198,6 +1198,26 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
return metadata;
}
+ @Override
+ public ManagedResources getManagedResources() {
+ return managedResources;
+ }
+
+ /**
+ * Used by extensions : Hibernate Reactive
+ */
+ @Override
+ public MetadataImplementor metadata() {
+ if ( this.metadata == null ) {
+ this.metadata = MetadataBuildingProcess.complete(
+ managedResources,
+ metamodelBuilder.getBootstrapContext(),
+ metamodelBuilder.getMetadataBuildingOptions()
+ );
+ }
+ return metadata;
+ }
+
@Override
public EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory) {
this.validatorFactory = validatorFactory;
@@ -1220,20 +1240,6 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
// todo : close the bootstrap registry (not critical, but nice to do)
}
- /**
- * Used by extensions : Hibernate Reactive
- */
- protected MetadataImplementor metadata() {
- if ( this.metadata == null ) {
- this.metadata = MetadataBuildingProcess.complete(
- managedResources,
- metamodelBuilder.getBootstrapContext(),
- metamodelBuilder.getMetadataBuildingOptions()
- );
- }
- return metadata;
- }
-
@Override
public void generateSchema() {
// This seems overkill, but building the SF is necessary to get the Integrators to kick in.
diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/boot/spi/EntityManagerFactoryBuilder.java b/hibernate-core/src/main/java/org/hibernate/jpa/boot/spi/EntityManagerFactoryBuilder.java
index 212b8e429e..5c9ad9de57 100644
--- a/hibernate-core/src/main/java/org/hibernate/jpa/boot/spi/EntityManagerFactoryBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/jpa/boot/spi/EntityManagerFactoryBuilder.java
@@ -9,6 +9,9 @@ package org.hibernate.jpa.boot.spi;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
+import org.hibernate.boot.model.process.spi.ManagedResources;
+import org.hibernate.boot.spi.MetadataImplementor;
+
/**
* Represents a 2-phase JPA bootstrap process for building a Hibernate EntityManagerFactory.
*
@@ -24,6 +27,10 @@ import javax.sql.DataSource;
* @author Scott Marlow
*/
public interface EntityManagerFactoryBuilder {
+ ManagedResources getManagedResources();
+
+ MetadataImplementor metadata();
+
/**
* Allows passing in a Java EE ValidatorFactory (delayed from constructing the builder, AKA phase 2) to be used
* in building the EntityManagerFactory
@@ -32,7 +39,7 @@ public interface EntityManagerFactoryBuilder {
*
* @return {@code this}, for method chaining
*/
- public EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory);
+ EntityManagerFactoryBuilder withValidatorFactory(Object validatorFactory);
/**
* Allows passing in a DataSource (delayed from constructing the builder, AKA phase 2) to be used
@@ -42,23 +49,23 @@ public interface EntityManagerFactoryBuilder {
*
* @return {@code this}, for method chaining
*/
- public EntityManagerFactoryBuilder withDataSource(DataSource dataSource);
+ EntityManagerFactoryBuilder withDataSource(DataSource dataSource);
/**
* Build {@link EntityManagerFactory} instance
*
* @return The built {@link EntityManagerFactory}
*/
- public EntityManagerFactory build();
+ EntityManagerFactory build();
/**
* Cancel the building processing. This is used to signal the builder to release any resources in the case of
* something having gone wrong during the bootstrap process
*/
- public void cancel();
+ void cancel();
/**
* Perform an explicit schema generation (rather than an "auto" one) based on the
*/
- public void generateSchema();
+ void generateSchema();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/DependantValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/DependantValue.java
index 9bc6ac5345..69fcd02bd2 100644
--- a/hibernate-core/src/main/java/org/hibernate/mapping/DependantValue.java
+++ b/hibernate-core/src/main/java/org/hibernate/mapping/DependantValue.java
@@ -27,6 +27,10 @@ public class DependantValue extends SimpleValue implements Resolvable {
this.wrappedValue = prototype;
}
+ public KeyValue getWrappedValue() {
+ return wrappedValue;
+ }
+
public Type getType() throws MappingException {
return wrappedValue.getType();
}
diff --git a/release/release.gradle b/release/release.gradle
index c016d4235e..85bee50c81 100644
--- a/release/release.gradle
+++ b/release/release.gradle
@@ -22,7 +22,8 @@ ext {
idea.module {
}
-final File documentationDir = mkdir( "${project.buildDir}/documentation" );
+final File documentationDir = mkdir( "${project.buildDir}/documentation" )
+final File projectTemplateStagingDir = mkdir( "${project.buildDir}/projectTemplate" )
task releaseChecks() {
doFirst {
@@ -104,6 +105,12 @@ task assembleDocumentation(type: Task, dependsOn: [rootProject.project( 'documen
}
}
+task assembleProjectTemplates(type:Copy, dependsOn: project( ":project-template" ).tasks.assembleDist) {
+ def templateProject = project( ":project-template" )
+ from templateProject.layout.buildDirectory.dir( "distributions" )
+ into projectTemplateStagingDir
+}
+
/**
* Upload the documentation to the JBoss doc server
*/
@@ -150,9 +157,10 @@ distributions {
from parent.project( 'hibernate-core' ).configurations.provided.files { dep -> dep.name == 'javassist' }
}
-// into( 'lib/jpa' ) {
-// from parent.project( 'hibernate-entitymanager' ).configurations.archives.allArtifacts.files.filter{ file -> !file.name.endsWith('-sources.jar') }
-// }
+ into( 'project-template' ) {
+ // todo : hook in some form of variable replacement - especially for version
+ from project( ':project-template' ).files( 'src/main/dist' )
+ }
// todo (6.0) - add back spatial
// into( 'lib/spatial' ) {
@@ -241,11 +249,9 @@ distributions {
}
}
-distZip.dependsOn assembleDocumentation
-distTar.dependsOn assembleDocumentation
-distTar {
- compression = Compression.GZIP
-}
+// this is the common task between distTar and distZip
+assembleDist.dependsOn assembleDocumentation
+distTar.compression = Compression.GZIP
/**
* "virtual" task for building both types of dist bundles
diff --git a/settings.gradle b/settings.gradle
index eef2aa5596..5662d9c003 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -82,6 +82,9 @@ project(':metamodel-generator').name = 'hibernate-jpamodelgen'
include 'hibernate-gradle-plugin'
project(':hibernate-gradle-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-gradle-plugin")
+include 'project-template'
+project(':project-template').projectDir = new File(rootProject.projectDir, "tooling/project-template")
+
include 'hibernate-enhance-maven-plugin'
project(':hibernate-enhance-maven-plugin').projectDir = new File(rootProject.projectDir, "tooling/hibernate-enhance-maven-plugin")
@@ -90,4 +93,4 @@ rootProject.children.each { project ->
assert project.projectDir.isDirectory()
assert project.buildFile.exists()
assert project.buildFile.isFile()
-}
+}
\ No newline at end of file
diff --git a/tooling/hibernate-gradle-plugin/README.adoc b/tooling/hibernate-gradle-plugin/README.adoc
new file mode 100644
index 0000000000..0fcb6e84e8
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/README.adoc
@@ -0,0 +1,51 @@
+= Hibernate ORM Gradle Plugin
+
+A Gradle plugin for introducing Hibernate tasks and capabilities into a build.
+
+
+== Set up
+
+```
+plugins {
+ id 'org.hibernate.orm' version='X'
+}
+
+// HibernateOrmSpec
+hibernate {
+ ...
+}
+```
+
+== Bytecode Enhancement
+
+The plugin can perform build-time enhancement of the domain classes. This is controlled
+by the `enhancement` portion of the `hibernate` extension:
+
+```
+hibernate {
+ // EnhancementSpec
+ enhancement {
+ // available options - all default to false
+ lazyInitialization( true )
+ }
+}
+```
+
+== DSL
+
+The `hibernate` DSL extension is the main entry into configuring the plugin
+
+```
+hibernate {
+}
+```
+
+At this time, there is no configuration at this level.
+
+== Capa
+
+== Tasks
+
+== Additional Resources
+
+* https://plugins.gradle.org/plugin/org.hibernate.orm
diff --git a/tooling/hibernate-gradle-plugin/README.md b/tooling/hibernate-gradle-plugin/README.md
deleted file mode 100644
index 39b1daf8ac..0000000000
--- a/tooling/hibernate-gradle-plugin/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Defines a Gradle plugin for introducing Hibernate specific tasks and capabilities into and end-user build.
-
-Currently the only capability added is for bytecode enhancement of the user domain model, although other capabilities are
-planned.
-
-todo : usage
\ No newline at end of file
diff --git a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
index f7f018bcd8..e5d52b76b0 100644
--- a/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
+++ b/tooling/hibernate-gradle-plugin/hibernate-gradle-plugin.gradle
@@ -1,29 +1,121 @@
+import org.apache.tools.ant.filters.ReplaceTokens
+
/*
* 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 .
*/
-apply plugin: 'groovy'
-description = "Gradle plugin for integrating Hibernate functionality into your build"
+plugins {
+ id 'java-gradle-plugin'
+ id 'com.github.sebersole.testkit-junit5' version '0.9.5'
-apply from: rootProject.file( 'gradle/published-java-module.gradle' )
-apply plugin: 'java-gradle-plugin'
-apply plugin: 'maven'
+ // for portal publishing
+ id "com.gradle.plugin-publish" version "0.12.0"
+ id "nu.studer.credentials" version "2.1"
+ // for publishing snapshots
+ id 'maven-publish'
+ id 'org.hibernate.build.maven-repo-auth'
+
+ id 'idea'
+ id 'eclipse'
+}
+
+description = "Gradle plugin for integrating Hibernate aspects into your build"
+
+apply from: rootProject.file( 'gradle/base-information.gradle' )
+apply from: rootProject.file( 'gradle/libraries.gradle' )
+apply from: rootProject.file( 'gradle/javadoc.gradle' )
+
+ext {
+ pluginId = 'org.hibernate.orm'
+ pluginVersion = project.version
+
+ //noinspection GrUnresolvedAccess
+ publishKey = credentials.'hibernate.gradle.publish.key'
+ //noinspection GrUnresolvedAccess
+ publishSecret = credentials.'hibernate.gradle.publish.secret'
+
+ if ( publishKey != null && publishSecret != null ) {
+ project.'gradle.publish.key' = publishKey
+ project.'gradle.publish.secret' = publishSecret
+ }
+}
dependencies {
- compile( project( ':hibernate-core' ) )
- compile( libraries.jpa )
- compile( libraries.javassist )
- compile( libraries.byteBuddy )
- compile gradleApi()
- compile localGroovy()
+ implementation( project( ':hibernate-core' ) )
+ implementation project( ':hibernate-testing' )
+ implementation( libraries.jpa )
+ implementation( libraries.javassist )
+ implementation( libraries.byteBuddy )
}
-tasks.withType( GroovyCompile ) {
- options.encoding = 'UTF-8'
- sourceCompatibility = project.baselineJavaVersion
- targetCompatibility = project.baselineJavaVersion
+gradlePlugin {
+ plugins {
+ ormPlugin {
+ id = project.pluginId
+ implementationClass = 'org.hibernate.orm.tooling.gradle.HibernateOrmPlugin'
+ }
+ }
}
+
+pluginBundle {
+ website = 'https://github.com/hibernate/hibernate-orm/tree/master/tooling/hibernate-gradle-plugin'
+ vcsUrl = 'https://github.com/hibernate/hibernate-orm/tree/master/tooling/hibernate-gradle-plugin'
+ tags = ['hibernate','orm','bytecode','enhancement','bytebuddy']
+
+ plugins {
+ ormPlugin {
+ id = project.pluginId
+ displayName = 'Gradle plugin for Hibernate ORM'
+ description = 'Applies Hibernate aspects into the build'
+ }
+ }
+}
+
+processResources {
+ filter( ReplaceTokens, tokens: [ 'hibernateVersion': getVersion() ] )
+}
+
+
+task release
+
+if ( project.version.toString().endsWith( '-SNAPSHOT' ) ) {
+ tasks.publishPlugins.enabled = false
+ tasks.release.dependsOn tasks.publish
+}
+else {
+ tasks.publish.enabled = false
+ tasks.release.dependsOn( tasks.publishPlugins )
+}
+
+tasks.publishPlugins {
+ doFirst {
+ if ( project.'gradle.publish.key' == null ) {
+ throw new RuntimeException( "`gradle.publish.key` not found" )
+ }
+ if ( project.'gradle.publish.secret' == null ) {
+ throw new RuntimeException( "`gradle.publish.secret` not found" )
+ }
+ }
+}
+
+
+// for SNAPSHOT version
+publishing {
+ publications {
+ plugin( MavenPublication ) {
+ from components.java
+ }
+ }
+
+ repositories {
+ maven {
+ name 'jboss-snapshots-repository'
+ url 'https://repository.jboss.org/nexus/content/repositories/snapshots'
+ }
+ }
+}
+
diff --git a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceExtension.groovy b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceExtension.groovy
deleted file mode 100644
index 6c2638f2c9..0000000000
--- a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceExtension.groovy
+++ /dev/null
@@ -1,24 +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 .
- */
-package org.hibernate.orm.tooling.gradle
-
-/**
- * Gradle DSL extension for configuring various Hibernate bytecode enhancement. Registered
- * under "hibernate.enhance".
- *
- * @author Steve Ebersole
- */
-class EnhanceExtension implements Serializable {
- def boolean enableLazyInitialization = false
- def boolean enableDirtyTracking = false
- def boolean enableAssociationManagement = false
- def boolean enableExtendedEnhancement = false
-
- boolean shouldApply() {
- return enableLazyInitialization || enableDirtyTracking || enableAssociationManagement || enableExtendedEnhancement;
- }
-}
diff --git a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceTask.groovy b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceTask.groovy
deleted file mode 100644
index 9179de58bb..0000000000
--- a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceTask.groovy
+++ /dev/null
@@ -1,37 +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 http://www.gnu.org/licenses/lgpl-2.1.html
- */
-package org.hibernate.orm.tooling.gradle
-
-import org.gradle.api.DefaultTask
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.SourceSet
-import org.gradle.api.tasks.TaskAction
-import org.gradle.util.ConfigureUtil
-
-/**
- * @author Steve Ebersole
- */
-@SuppressWarnings("unused")
-class EnhanceTask extends DefaultTask {
- @Input
- EnhanceExtension options
- @InputFiles
- SourceSet[] sourceSets
-
- @TaskAction
- void enhance() {
- for ( SourceSet sourceSet: sourceSets ) {
- EnhancementHelper.enhance( sourceSet, options, project )
- }
- }
-
- void options(Closure closure) {
- options = new EnhanceExtension()
- ConfigureUtil.configure( closure, options )
- }
-}
diff --git a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhancementHelper.java b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhancementHelper.java
deleted file mode 100644
index 927fa254e5..0000000000
--- a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhancementHelper.java
+++ /dev/null
@@ -1,178 +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 http://www.gnu.org/licenses/lgpl-2.1.html
- */
-package org.hibernate.orm.tooling.gradle;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.gradle.api.GradleException;
-import org.gradle.api.Project;
-import org.gradle.api.file.FileCollection;
-import org.gradle.api.file.FileTree;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.tasks.SourceSet;
-
-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.cfg.Environment;
-
-/**
- * @author Steve Ebersole
- */
-public class EnhancementHelper {
- static void enhance(SourceSet sourceSet, EnhanceExtension options, Project project) {
- final ClassLoader classLoader = toClassLoader( sourceSet.getRuntimeClasspath() );
-
- final EnhancementContext enhancementContext = new DefaultEnhancementContext() {
- @Override
- public ClassLoader getLoadingClassLoader() {
- return classLoader;
- }
-
- @Override
- public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
- return options.getEnableAssociationManagement();
- }
-
- @Override
- public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
- return options.getEnableDirtyTracking();
- }
-
- @Override
- public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
- return options.getEnableLazyInitialization();
- }
-
- @Override
- public boolean isLazyLoadable(UnloadedField field) {
- return options.getEnableLazyInitialization();
- }
-
- @Override
- public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
- return options.getEnableExtendedEnhancement();
- }
- };
-
- if ( options.getEnableExtendedEnhancement() ) {
- project.getLogger().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." );
- }
-
- final Enhancer enhancer = Environment.getBytecodeProvider().getEnhancer( enhancementContext );
-
- for ( File classesDir: sourceSet.getOutput().getClassesDirs() ) {
- final FileTree fileTree = project.fileTree( classesDir );
- for ( File file : fileTree ) {
- if ( !file.getName().endsWith( ".class" ) ) {
- continue;
- }
-
- final byte[] enhancedBytecode = doEnhancement( classesDir, file, enhancer );
- if ( enhancedBytecode != null ) {
- writeOutEnhancedClass( enhancedBytecode, file, project.getLogger() );
- project.getLogger().info( "Successfully enhanced class [" + file + "]" );
- }
- else {
- project.getLogger().info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" );
- }
- }
- }
- }
-
- public static ClassLoader toClassLoader(FileCollection runtimeClasspath) {
- List urls = new ArrayList<>();
- for ( File file : runtimeClasspath ) {
- try {
- urls.add( file.toURI().toURL() );
- }
- catch (MalformedURLException e) {
- throw new GradleException( "Unable to resolve classpath entry to URL : " + file.getAbsolutePath(), e );
- }
- }
- return new URLClassLoader( urls.toArray( new URL[0] ), Enhancer.class.getClassLoader() );
- }
-
- @SuppressWarnings("WeakerAccess")
- static byte[] doEnhancement(File root, File javaClassFile, Enhancer enhancer) {
- try {
- final String className = determineClassName( root, javaClassFile );
- final ByteArrayOutputStream originalBytes = new ByteArrayOutputStream();
- try (final FileInputStream fileInputStream = new FileInputStream( javaClassFile )) {
- byte[] buffer = new byte[1024];
- int length;
- while ( ( length = fileInputStream.read( buffer ) ) != -1 ) {
- originalBytes.write( buffer, 0, length );
- }
- }
- return enhancer.enhance( className, originalBytes.toByteArray() );
- }
- catch (Exception e) {
- throw new GradleException( "Unable to enhance class : " + javaClassFile, e );
- }
- }
-
- private static String determineClassName(File root, File javaClassFile) {
- return javaClassFile.getAbsolutePath().substring(
- root.getAbsolutePath().length() + 1,
- javaClassFile.getAbsolutePath().length() - ".class".length()
- ).replace( File.separatorChar, '.' );
- }
-
- private static void writeOutEnhancedClass(byte[] enhancedBytecode, File file, Logger logger) {
- try {
- if ( file.delete() ) {
- if ( !file.createNewFile() ) {
- logger.error( "Unable to recreate class file [" + file.getName() + "]" );
- }
- }
- else {
- logger.error( "Unable to delete class file [" + file.getName() + "]" );
- }
- }
- catch (IOException e) {
- logger.warn( "Problem preparing class file for writing out enhancements [" + file.getName() + "]" );
- }
-
- try {
- FileOutputStream outputStream = new FileOutputStream( file, false );
- try {
- outputStream.write( enhancedBytecode );
- outputStream.flush();
- }
- catch (IOException e) {
- throw new GradleException( "Error writing to enhanced class [" + file.getName() + "] to file [" + file.getAbsolutePath() + "]", e );
- }
- finally {
- try {
- outputStream.close();
- }
- catch (IOException ignore) {
- }
- }
- }
- catch (FileNotFoundException e) {
- throw new GradleException( "Error opening class file for writing : " + file.getAbsolutePath(), e );
- }
-
- }
-
- private EnhancementHelper() {
- }
-}
diff --git a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernateExtension.groovy b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernateExtension.groovy
deleted file mode 100644
index 5cb900c9c3..0000000000
--- a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernateExtension.groovy
+++ /dev/null
@@ -1,54 +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 .
- */
-package org.hibernate.orm.tooling.gradle
-
-import org.gradle.api.Project
-import org.gradle.api.plugins.JavaPluginConvention
-import org.gradle.api.tasks.SourceSet
-import org.gradle.util.ConfigureUtil
-
-/**
- * Gradle DSL extension for configuring various Hibernate built-time tasks. Registered
- * under "hibernate".
- *
- * @author Steve Ebersole
- */
-class HibernateExtension {
- private final Project project
-
- /**
- * The source sets that hold persistent model. Default is project.sourceSets.main
- */
- def SourceSet[] sourceSets
-
- /**
- * Configuration for bytecode enhancement. Private; see instead {@link #enhance(groovy.lang.Closure)}
- */
- protected EnhanceExtension enhance
-
- HibernateExtension(Project project) {
- this.project = project
- this.sourceSet( project.getConvention().getPlugin( JavaPluginConvention ).sourceSets.main )
- }
-
- /**
- * Add a single SourceSet.
- *
- * @param sourceSet The SourceSet to add
- */
- void sourceSet(SourceSet sourceSet) {
- if ( sourceSets == null ) {
- sourceSets = []
- }
- sourceSets += sourceSet
- }
-
- void enhance(Closure closure) {
- enhance = new EnhanceExtension()
- ConfigureUtil.configure( closure, enhance )
- }
-}
diff --git a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java
deleted file mode 100644
index d35037fc40..0000000000
--- a/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java
+++ /dev/null
@@ -1,78 +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 .
- */
-package org.hibernate.orm.tooling.gradle;
-
-import org.gradle.api.Action;
-import org.gradle.api.Plugin;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.SourceSet;
-
-/**
- * The Hibernate Gradle plugin. Adds Hibernate build-time capabilities into your Gradle-based build.
- *
- * @author Jeremy Whiting
- * @author Steve Ebersole
- */
-@SuppressWarnings("serial")
-public class HibernatePlugin implements Plugin {
- public void apply(Project project) {
- project.getPlugins().apply( "java" );
-
- final HibernateExtension hibernateExtension = new HibernateExtension( project );
-
- project.getLogger().debug( "Adding Hibernate extensions to the build [{}]", project.getName() );
- project.getExtensions().add( "hibernate", hibernateExtension );
-
- project.afterEvaluate(
- p -> applyEnhancement( p, hibernateExtension )
- );
- }
-
- private void applyEnhancement(final Project project, final HibernateExtension hibernateExtension) {
- if ( hibernateExtension.enhance == null || ! hibernateExtension.enhance.shouldApply() ) {
- project.getLogger().warn( "Skipping Hibernate bytecode enhancement since no feature is enabled" );
- return;
- }
-
- for ( final SourceSet sourceSet : hibernateExtension.getSourceSets() ) {
- project.getLogger().debug( "Applying Hibernate enhancement action to SourceSet.{}", sourceSet.getName() );
-
- final Task compileTask = project.getTasks().findByName( sourceSet.getCompileJavaTaskName() );
- assert compileTask != null;
- compileTask.doLast(new EnhancerAction( sourceSet, hibernateExtension, project ));
- }
- }
-
- /**
- * Gradle doesn't allow lambdas in doLast or doFirst configurations and causing up-to-date checks
- * to fail. Extracting the lambda to an inner class works around this issue.
- *
- * @link https://github.com/gradle/gradle/issues/5510
- */
- private static class EnhancerAction implements Action {
-
- private final SourceSet sourceSet;
-
- private final HibernateExtension hibernateExtension;
-
- private final Project project;
-
- private EnhancerAction(SourceSet sourceSet, HibernateExtension hibernateExtension, Project project) {
- this.sourceSet = sourceSet;
- this.hibernateExtension = hibernateExtension;
- this.project = project;
- }
-
- @Override
- public void execute(Task task) {
- EnhancementHelper.enhance( sourceSet, hibernateExtension.enhance, project );
- }
-
- }
-
-}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/Helper.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/Helper.java
new file mode 100644
index 0000000000..5c59274aaf
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/Helper.java
@@ -0,0 +1,48 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Path;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Project;
+import org.gradle.api.file.Directory;
+
+import org.hibernate.bytecode.enhance.spi.Enhancer;
+
+/**
+ * @author Steve Ebersole
+ */
+public class Helper {
+
+ public static ClassLoader toClassLoader(Directory classesDir) {
+ final File classesDirFile = classesDir.getAsFile();
+ final URI classesDirUri = classesDirFile.toURI();
+ try {
+ final URL url = classesDirUri.toURL();
+ return new URLClassLoader( new URL[] { url }, Enhancer.class.getClassLoader() );
+ }
+ catch (MalformedURLException e) {
+ throw new GradleException( "Unable to resolve classpath entry to URL : " + classesDirFile.getAbsolutePath(), e );
+ }
+ }
+
+ public static 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-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateOrmPlugin.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateOrmPlugin.java
new file mode 100644
index 0000000000..356334eb67
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateOrmPlugin.java
@@ -0,0 +1,52 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.SourceSetContainer;
+
+import org.hibernate.orm.tooling.gradle.enhance.EnhancementTask;
+import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationTask;
+
+/**
+ * Hibernate ORM Gradle plugin
+ */
+public class HibernateOrmPlugin implements Plugin {
+ @Override
+ public void apply(Project project) {
+ project.getPlugins().apply( JavaPlugin.class );
+
+ project.getLogger().debug( "Adding Hibernate extensions to the build [{}]", project.getPath() );
+ final HibernateOrmSpec ormDsl = project.getExtensions().create( HibernateOrmSpec.DSL_NAME, HibernateOrmSpec.class, project );
+
+ final Configuration hibernateOrm = project.getConfigurations().maybeCreate( "hibernateOrm" );
+ project.getDependencies().add(
+ "hibernateOrm",
+ project.provider( () -> "org.hibernate.orm:hibernate-core:" + HibernateVersion.version )
+ );
+ project.getConfigurations().getByName( "implementation" ).extendsFrom( hibernateOrm );
+
+ final JavaPluginConvention javaPluginConvention = project.getConvention().findPlugin( JavaPluginConvention.class );
+ assert javaPluginConvention != null;
+
+ final SourceSetContainer sourceSets = javaPluginConvention.getSourceSets();
+ final SourceSet mainSourceSet = sourceSets.getByName( SourceSet.MAIN_SOURCE_SET_NAME );
+
+ EnhancementTask.apply( ormDsl, mainSourceSet, project );
+ JpaMetamodelGenerationTask.apply( ormDsl, mainSourceSet, project );
+
+ project.getDependencies().add(
+ "implementation",
+ project.provider( () -> "org.hibernate.orm:hibernate-core:" + ormDsl.getHibernateVersionProperty().get() )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateOrmSpec.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateOrmSpec.java
new file mode 100644
index 0000000000..fd238be553
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateOrmSpec.java
@@ -0,0 +1,98 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle;
+
+import javax.inject.Inject;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.api.plugins.ExtensionContainer;
+import org.gradle.api.provider.Property;
+
+import org.hibernate.orm.tooling.gradle.enhance.EnhancementSpec;
+import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationSpec;
+
+/**
+ * Main DSL extension for Hibernate ORM. Available as `project.hibernate`
+ */
+public abstract class HibernateOrmSpec implements ExtensionAware {
+ public static final String HIBERNATE = "hibernate";
+
+ public static final String DSL_NAME = HIBERNATE;
+
+ private final Property hibernateVersionProperty;
+ private final Property supportEnhancementProperty;
+ private final Property supportJpaMetamodelProperty;
+
+ private final EnhancementSpec enhancementDsl;
+ private final JpaMetamodelGenerationSpec jpaMetamodelDsl;
+
+
+ @Inject
+ @SuppressWarnings( "UnstableApiUsage" )
+ public HibernateOrmSpec(Project project) {
+ hibernateVersionProperty = project.getObjects().property( String.class );
+ hibernateVersionProperty.convention( HibernateVersion.version );
+
+ supportEnhancementProperty = project.getObjects().property( Boolean.class );
+ supportEnhancementProperty.convention( true );
+
+ supportJpaMetamodelProperty = project.getObjects().property( Boolean.class );
+ supportJpaMetamodelProperty.convention( true );
+
+ enhancementDsl = getExtensions().create( EnhancementSpec.DSL_NAME, EnhancementSpec.class, this, project );
+ jpaMetamodelDsl = getExtensions().create( JpaMetamodelGenerationSpec.DSL_NAME, JpaMetamodelGenerationSpec.class, this, project );
+ }
+
+ public Property getHibernateVersionProperty() {
+ return hibernateVersionProperty;
+ }
+
+ public void hibernateVersion(String version) {
+ setHibernateVersion( version );
+ }
+
+ public void setHibernateVersion(String version) {
+ hibernateVersionProperty.set( version );
+ }
+
+ public Property getSupportEnhancementProperty() {
+ return supportEnhancementProperty;
+ }
+
+ public void disableEnhancement() {
+ supportEnhancementProperty.set( false );
+ }
+
+ public Property getSupportJpaMetamodelProperty() {
+ return supportJpaMetamodelProperty;
+ }
+
+ public void disableJpaMetamodel() {
+ supportJpaMetamodelProperty.set( false );
+ }
+
+ public EnhancementSpec getEnhancementSpec() {
+ return enhancementDsl;
+ }
+
+ public void enhancement(Action action) {
+ action.execute( enhancementDsl );
+ }
+
+ public JpaMetamodelGenerationSpec getJpaMetamodelSpec() {
+ return jpaMetamodelDsl;
+ }
+
+ public void jpaMetamodel(Actionaction) {
+ action.execute( jpaMetamodelDsl );
+ }
+
+ @Override
+ public abstract ExtensionContainer getExtensions();
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateVersion.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateVersion.java
new file mode 100644
index 0000000000..7589ebf142
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/HibernateVersion.java
@@ -0,0 +1,50 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.net.URL;
+
+import org.gradle.api.GradleException;
+
+/**
+ * @author Steve Ebersole
+ */
+public class HibernateVersion {
+ public static volatile String version;
+
+ static {
+ version = determineHibernateVersion();
+ }
+
+ private static String determineHibernateVersion() {
+ final URL versionFileUrl = findVersionFile();
+ try ( final InputStream inputStream = versionFileUrl.openStream() ) {
+ try ( final InputStreamReader inputStreamReader = new InputStreamReader( inputStream ) ) {
+ return new LineNumberReader( inputStreamReader ).readLine();
+ }
+ }
+ catch (IOException e) {
+ throw new GradleException( "Unable to read `META-INF/hibernate-orm.version` resource" );
+ }
+ }
+
+ private static URL findVersionFile() {
+ final URL badGunsAndRoses = HibernateOrmSpec.class.getClassLoader().getResource( "META-INF/hibernate-orm.version" );
+ if ( badGunsAndRoses != null ) {
+ return badGunsAndRoses;
+ }
+
+ //noinspection UnnecessaryLocalVariable
+ final URL goodGunsAndRoses = HibernateOrmSpec.class.getClassLoader().getResource( "/META-INF/hibernate-orm.version" );
+
+ return goodGunsAndRoses;
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementHelper.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementHelper.java
new file mode 100644
index 0000000000..bc8022e252
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementHelper.java
@@ -0,0 +1,167 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.enhance;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Project;
+import org.gradle.api.file.Directory;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.logging.Logger;
+import org.gradle.work.InputChanges;
+
+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.cfg.Environment;
+import org.hibernate.orm.tooling.gradle.Helper;
+
+import static org.hibernate.orm.tooling.gradle.Helper.determineClassName;
+
+/**
+ * @author Steve Ebersole
+ */
+public class EnhancementHelper {
+ public static void enhance(
+ DirectoryProperty classesDirectoryProperty,
+ InputChanges inputChanges,
+ EnhancementSpec enhancementDsl,
+ Project project) {
+ final Directory classesDir = classesDirectoryProperty.get();
+ final File classesDirFile = classesDir.getAsFile();
+
+ final Enhancer enhancer = generateEnhancer( classesDir, enhancementDsl );
+
+ final String classesDirPath = classesDirFile.getAbsolutePath();
+
+ inputChanges.getFileChanges( classesDirectoryProperty ).forEach(
+ change -> {
+ switch ( change.getChangeType() ) {
+ case ADDED:
+ case MODIFIED: {
+ final File changedFile = change.getFile();
+ if ( changedFile.getName().endsWith( ".class" ) ) {
+ final String classFilePath = changedFile.getAbsolutePath();
+ if ( classFilePath.startsWith( classesDirPath ) ) {
+ // we found the directory it came from
+ // -use that to determine the class name
+ enhance( changedFile, determineClassName( classesDirFile, changedFile ), enhancer, project );
+ break;
+ }
+ }
+ break;
+ }
+ case REMOVED: {
+ // nothing to do
+ break;
+ }
+ default: {
+ throw new UnsupportedOperationException( "Unexpected ChangeType : " + change.getChangeType().name() );
+ }
+ }
+ }
+ );
+ }
+
+ private static void enhance(
+ File javaClassFile,
+ String className,
+ Enhancer enhancer,
+ Project project) {
+ final byte[] enhancedBytecode = doEnhancement( javaClassFile, className, enhancer );
+ if ( enhancedBytecode != null ) {
+ writeOutEnhancedClass( enhancedBytecode, javaClassFile, project.getLogger() );
+ project.getLogger().info( "Successfully enhanced class : " + className );
+ }
+ else {
+ project.getLogger().info( "Skipping class : " + className );
+ }
+ }
+
+ private static byte[] doEnhancement(File javaClassFile, String className, Enhancer enhancer) {
+ try {
+ return enhancer.enhance( className, Files.readAllBytes( javaClassFile.toPath() ) );
+ }
+ catch (Exception e) {
+ throw new GradleException( "Unable to enhance class : " + className, e );
+ }
+ }
+
+ private static Enhancer generateEnhancer(Directory classesDir, EnhancementSpec enhancementDsl) {
+ final ClassLoader classLoader = Helper.toClassLoader( classesDir );
+
+ final EnhancementContext enhancementContext = new DefaultEnhancementContext() {
+ @Override
+ public ClassLoader getLoadingClassLoader() {
+ return classLoader;
+ }
+
+ @Override
+ public boolean doBiDirectionalAssociationManagement(UnloadedField field) {
+ return enhancementDsl.getEnableAssociationManagement().get();
+ }
+
+ @Override
+ public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) {
+ return enhancementDsl.getEnableDirtyTracking().get();
+ }
+
+ @Override
+ public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) {
+ return enhancementDsl.getEnableLazyInitialization().get();
+ }
+
+ @Override
+ public boolean isLazyLoadable(UnloadedField field) {
+ return enhancementDsl.getEnableLazyInitialization().get();
+ }
+
+ @Override
+ public boolean doExtendedEnhancement(UnloadedClass classDescriptor) {
+ return enhancementDsl.getEnableExtendedEnhancement().get();
+ }
+ };
+
+ //noinspection deprecation
+ return Environment.getBytecodeProvider().getEnhancer( enhancementContext );
+ }
+
+ private static void writeOutEnhancedClass(byte[] enhancedBytecode, File file, Logger logger) {
+ try {
+ if ( file.delete() ) {
+ if ( !file.createNewFile() ) {
+ logger.error( "Unable to recreate class file : " + file.getAbsolutePath() );
+ }
+ }
+ else {
+ logger.error( "Unable to delete class file : " + file.getAbsolutePath() );
+ }
+ }
+ catch (IOException e) {
+ logger.warn( "Problem preparing class file for writing out enhancements [" + file.getAbsolutePath() + "]" );
+ }
+
+ try {
+ Files.write( file.toPath(), enhancedBytecode );
+ }
+ catch (FileNotFoundException e) {
+ throw new GradleException( "Error opening class file for writing : " + file.getAbsolutePath(), e );
+ }
+ catch (IOException e) {
+ throw new GradleException( "Error writing enhanced class to file [" + file.getAbsolutePath() + "]", e );
+ }
+ }
+
+ private EnhancementHelper() {
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementSpec.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementSpec.java
new file mode 100644
index 0000000000..ab46075d59
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementSpec.java
@@ -0,0 +1,129 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.enhance;
+
+import javax.inject.Inject;
+
+import org.gradle.api.Project;
+import org.gradle.api.provider.Property;
+
+import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
+
+/**
+ * DSL extension for configuring bytecode enhancement - available as `project.hibernateOrm.enhancement`
+ */
+@SuppressWarnings( { "unused", "RedundantSuppression" } )
+public class EnhancementSpec {
+ public static final String ENHANCE = "enhance";
+ public static final String ENHANCEMENT = "enhancement";
+
+ public static final String DSL_NAME = ENHANCEMENT;
+
+ private final Property enableLazyInitialization;
+ private final Property enableDirtyTracking;
+ private final Property enableAssociationManagement;
+ private final Property enableExtendedEnhancement;
+
+
+ @Inject
+ public EnhancementSpec(HibernateOrmSpec ormDsl, Project project) {
+ enableLazyInitialization = makeProperty( project );
+ enableDirtyTracking = makeProperty( project );
+ enableAssociationManagement = makeProperty( project );
+ enableExtendedEnhancement = makeProperty( project );
+ }
+
+ public boolean hasAnythingToDo() {
+ return enableLazyInitialization.get()
+ || enableDirtyTracking.get()
+ || enableAssociationManagement.get()
+ || enableExtendedEnhancement.get();
+ }
+
+ public Property getEnableLazyInitialization() {
+ return enableLazyInitialization;
+ }
+
+ public void setEnableLazyInitialization(boolean enable) {
+ enableLazyInitialization.set( enable );
+ }
+
+ public void enableLazyInitialization(boolean enable) {
+ setEnableLazyInitialization( enable );
+ }
+
+ public void lazyInitialization(boolean enable) {
+ setEnableLazyInitialization( enable );
+ }
+
+ public void setLazyInitialization(boolean enable) {
+ setEnableLazyInitialization( enable );
+ }
+
+
+ public Property getEnableDirtyTracking() {
+ return enableDirtyTracking;
+ }
+
+ public void setEnableDirtyTracking(boolean enable) {
+ enableDirtyTracking.set( enable );
+ }
+
+ public void enableDirtyTracking(boolean enable) {
+ setEnableDirtyTracking( enable );
+ }
+
+ public void dirtyTracking(boolean enable) {
+ setEnableDirtyTracking( enable );
+ }
+
+ public void setDirtyTracking(boolean enable) {
+ setEnableDirtyTracking( enable );
+ }
+
+
+ public Property getEnableAssociationManagement() {
+ return enableAssociationManagement;
+ }
+
+ public void setEnableAssociationManagement(boolean enable) {
+ enableAssociationManagement.set( enable );
+ }
+
+ public void enableAssociationManagement(boolean enable) {
+ setEnableAssociationManagement( enable );
+ }
+
+ public void associationManagement(boolean enable) {
+ setEnableAssociationManagement( enable );
+ }
+
+
+ public Property getEnableExtendedEnhancement() {
+ return enableExtendedEnhancement;
+ }
+
+ public void setEnableExtendedEnhancement(boolean enable) {
+ enableExtendedEnhancement.set( enable );
+ }
+
+ public void enableExtendedEnhancement(boolean enable) {
+ setEnableExtendedEnhancement( enable );
+ }
+
+ public void extendedEnhancement(boolean enable) {
+ setEnableExtendedEnhancement( enable );
+ }
+
+ @SuppressWarnings( "UnstableApiUsage" )
+ public static Property makeProperty(Project project) {
+ final Property createdProperty = project.getObjects().property( Boolean.class );
+ // default to false
+ createdProperty.convention( false );
+ return createdProperty;
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementTask.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementTask.java
new file mode 100644
index 0000000000..d56048936d
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/enhance/EnhancementTask.java
@@ -0,0 +1,119 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.enhance;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.inject.Inject;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.execution.TaskExecutionAdapter;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.TaskState;
+import org.gradle.work.Incremental;
+import org.gradle.work.InputChanges;
+
+import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
+
+import static org.hibernate.orm.tooling.gradle.HibernateOrmSpec.HIBERNATE;
+
+/**
+ * @author Steve Ebersole
+ */
+public class EnhancementTask extends DefaultTask {
+ public static final String DSL_NAME = "hibernateEnhance";
+
+ public static void apply(HibernateOrmSpec ormDsl, SourceSet mainSourceSet, Project project) {
+ final EnhancementTask enhancementTask = project.getTasks().create(
+ DSL_NAME,
+ EnhancementTask.class,
+ ormDsl,
+ mainSourceSet,
+ project
+ );
+ enhancementTask.setGroup( HIBERNATE );
+ enhancementTask.setDescription( "Performs Hibernate ORM enhancement of the project's compiled classes" );
+
+ final String compileJavaTaskName = mainSourceSet.getCompileJavaTaskName();
+ final Task compileJavaTask = project.getTasks().getByName( compileJavaTaskName );
+ enhancementTask.dependsOn( compileJavaTask );
+ compileJavaTask.finalizedBy( enhancementTask );
+ }
+
+ private final EnhancementSpec enhancementDsl;
+ private final DirectoryProperty javaCompileOutputDirectory;
+ private final DirectoryProperty outputDirectory;
+
+ @Inject
+ @SuppressWarnings( "UnstableApiUsage" )
+ public EnhancementTask(HibernateOrmSpec ormSpec, SourceSet mainSourceSet, Project project) {
+ this.enhancementDsl = ormSpec.getEnhancementSpec();
+
+ javaCompileOutputDirectory = mainSourceSet.getJava().getDestinationDirectory();
+
+ outputDirectory = project.getObjects().directoryProperty();
+ outputDirectory.set( project.getLayout().getBuildDirectory().dir( "tmp/hibernateEnhancement" ) );
+
+ final AtomicBoolean didCompileRun = new AtomicBoolean( false );
+
+ final TaskExecutionGraph taskGraph = project.getGradle().getTaskGraph();
+
+ taskGraph.addTaskExecutionListener(
+ new TaskExecutionAdapter() {
+ @Override
+ public void afterExecute(Task task, TaskState state) {
+ super.afterExecute( task, state );
+ if ( "compileJava".equals( task.getName() ) ) {
+ if ( state.getDidWork() ) {
+ didCompileRun.set( true );
+ }
+ }
+
+ taskGraph.removeTaskExecutionListener( this );
+ }
+ }
+ );
+
+ getOutputs().upToDateWhen( (task) -> ! didCompileRun.get() );
+ }
+
+ @InputDirectory
+ @Incremental
+ public DirectoryProperty getJavaCompileDirectory() {
+ return javaCompileOutputDirectory;
+ }
+
+ @OutputDirectory
+ public DirectoryProperty getOutputDirectory() {
+ return outputDirectory;
+ }
+
+ @TaskAction
+ public void enhanceClasses(InputChanges inputChanges) {
+ if ( !enhancementDsl.hasAnythingToDo() ) {
+ return;
+ }
+
+ if ( !inputChanges.isIncremental() ) {
+ getProject().getLogger().debug( "EnhancementTask inputs were not incremental" );
+ }
+
+ if ( enhancementDsl.getEnableExtendedEnhancement().get() ) {
+ // for extended enhancement, we may need to enhance everything...
+ // for now, assume we don't
+ getProject().getLogger().info( "Performing extended enhancement" );
+ }
+
+ EnhancementHelper.enhance( javaCompileOutputDirectory, inputChanges, enhancementDsl, getProject() );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/JpaMetamodelGenerationSpec.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/JpaMetamodelGenerationSpec.java
new file mode 100644
index 0000000000..5fc18fc4c2
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/JpaMetamodelGenerationSpec.java
@@ -0,0 +1,88 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel;
+
+import java.util.Arrays;
+import javax.inject.Inject;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.api.Project;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.provider.Property;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.provider.SetProperty;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
+
+/**
+ * @author Steve Ebersole
+ */
+public class JpaMetamodelGenerationSpec {
+ public static final String JPA_METAMODEL = "jpaMetamodel";
+ public static final String DSL_NAME = JPA_METAMODEL;
+
+ private final Property applyGeneratedAnnotation;
+ private final SetProperty suppressions;
+ private final DirectoryProperty generationOutputDirectory;
+ private final DirectoryProperty compileOutputDirectory;
+
+ private final Provider targetJavaVersionAccess;
+
+ @Inject
+ @SuppressWarnings( "UnstableApiUsage" )
+ public JpaMetamodelGenerationSpec(HibernateOrmSpec ormDsl, Project project) {
+ applyGeneratedAnnotation = project.getObjects().property( Boolean.class );
+ applyGeneratedAnnotation.convention( true );
+
+ suppressions = project.getObjects().setProperty( String.class );
+ suppressions.convention( Arrays.asList( "raw", "deprecation" ) );
+
+ generationOutputDirectory = project.getObjects().directoryProperty();
+ generationOutputDirectory.convention(
+ project.getLayout().getBuildDirectory().dir( "generated/sources/" + JPA_METAMODEL )
+ );
+
+ compileOutputDirectory = project.getObjects().directoryProperty();
+ compileOutputDirectory.convention(
+ project.getLayout().getBuildDirectory().dir( "classes/java/" + JPA_METAMODEL )
+ );
+
+ targetJavaVersionAccess = project.provider(
+ () -> {
+ final JavaPluginConvention javaPluginConvention = project.getConvention().findPlugin( JavaPluginConvention.class );
+ assert javaPluginConvention != null;
+ final SourceSet sourceSet = javaPluginConvention.getSourceSets().getByName( SourceSet.MAIN_SOURCE_SET_NAME );
+ final String compileTaskName = sourceSet.getCompileJavaTaskName();
+ final JavaCompile compileTask = (JavaCompile) project.getTasks().getByName( compileTaskName );
+ return JavaVersion.toVersion( compileTask.getTargetCompatibility() );
+ }
+ );
+ }
+
+ public Provider getTargetJavaVersionAccess() {
+ return targetJavaVersionAccess;
+ }
+
+ public Property getApplyGeneratedAnnotation() {
+ return applyGeneratedAnnotation;
+ }
+
+ public SetProperty getSuppressions() {
+ return suppressions;
+ }
+
+ public DirectoryProperty getGenerationOutputDirectory() {
+ return generationOutputDirectory;
+ }
+
+ public DirectoryProperty getCompileOutputDirectory() {
+ return compileOutputDirectory;
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/JpaMetamodelGenerationTask.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/JpaMetamodelGenerationTask.java
new file mode 100644
index 0000000000..abdf36bf2e
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/JpaMetamodelGenerationTask.java
@@ -0,0 +1,346 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import javax.inject.Inject;
+import javax.persistence.SharedCacheMode;
+import javax.persistence.ValidationMode;
+import javax.persistence.spi.ClassTransformer;
+import javax.persistence.spi.PersistenceUnitInfo;
+import javax.persistence.spi.PersistenceUnitTransactionType;
+import javax.sql.DataSource;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.file.ConfigurableFileTree;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.SkipWhenEmpty;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.SourceSetOutput;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.compile.JavaCompile;
+
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.jpa.HibernatePersistenceProvider;
+import org.hibernate.orm.tooling.gradle.Helper;
+import org.hibernate.orm.tooling.gradle.HibernateOrmSpec;
+import org.hibernate.orm.tooling.gradle.metamodel.model.JpaStaticMetamodelGenerator;
+import org.hibernate.orm.tooling.gradle.metamodel.model.MetamodelClass;
+
+import static org.hibernate.orm.tooling.gradle.HibernateOrmSpec.HIBERNATE;
+
+/**
+ * Generates the "JPA static metamodel" from the domain model defined by the project
+ * via classes and possibly XML mappings
+ *
+ * @apiNote While there is also an annotation-processor that performs the same
+ * general function, that approach is limited in that it can only process compiled
+ * classes based on annotations. This task accounts for both classes and XML mappings
+ */
+public class JpaMetamodelGenerationTask extends DefaultTask {
+ public static final String DSL_NAME = "generateJpaMetamodel";
+ public static final String COMPILE_DSL_NAME = "compileJpaMetamodel";
+
+
+ private final HibernateOrmSpec ormSpec;
+ private final DirectoryProperty resourcesOutputDir;
+ private final SourceSet mainSourceSet;
+
+ @Inject
+ @SuppressWarnings( "UnstableApiUsage" )
+ public JpaMetamodelGenerationTask(
+ HibernateOrmSpec ormSpec,
+ SourceSet mainSourceSet,
+ JavaCompile mainCompileTask,
+ Project project) {
+ this.ormSpec = ormSpec;
+ dependsOn( mainCompileTask );
+
+ this.mainSourceSet = mainSourceSet;
+
+ final SourceSetOutput mainSourceSetOutput = mainSourceSet.getOutput();
+
+ resourcesOutputDir = project.getObjects().directoryProperty();
+ resourcesOutputDir.set( project.getLayout().dir( project.provider( mainSourceSetOutput::getResourcesDir ) ) );
+
+ }
+
+ @InputFiles
+ @SkipWhenEmpty
+ public FileCollection getJavaClassDirs() {
+ return mainSourceSet.getOutput();
+ }
+
+ @InputFiles
+ @SkipWhenEmpty
+ public DirectoryProperty getResourcesOutputDir() {
+ // for access to XML mappings
+ return resourcesOutputDir;
+ }
+
+ @OutputDirectory
+ public DirectoryProperty getGenerationOutputDirectory() {
+ return ormSpec.getJpaMetamodelSpec().getGenerationOutputDirectory();
+ }
+
+ @TaskAction
+ public void generateJpaMetamodel() {
+ final ClassLoader classLoader = determineUnitClassLoader( getProject(), mainSourceSet );
+ final PersistenceUnitInfoImpl unitInfo = new PersistenceUnitInfoImpl(
+ determineUnitUrl(),
+ generateIntegrationSettings(),
+ classLoader
+ );
+
+ getJavaClassDirs().forEach(
+ classesDir -> {
+ final ConfigurableFileTree files = getProject().fileTree( classesDir );
+ files.forEach(
+ file -> {
+ if ( file.getName().endsWith( ".class" ) ) {
+ final String className = Helper.determineClassName( classesDir, file );
+ unitInfo.addManagedClassName( className );
+ }
+ else if ( isMappingFile( file ) ) {
+ unitInfo.addMappingFile( file.getName() );
+ }
+ }
+ );
+ }
+ );
+
+ resourcesOutputDir.getAsFileTree().forEach(
+ file -> {
+ if ( isMappingFile( file ) ) {
+ unitInfo.addMappingFile( file.getName() );
+ }
+ }
+ );
+
+ JpaStaticMetamodelGenerator.processMetamodel( unitInfo, ormSpec.getJpaMetamodelSpec() );
+ }
+
+ private URL determineUnitUrl() {
+ try {
+ // NOTE : we just need *a* URL - we used the project dir
+ return getProject().getProjectDir().toURI().toURL();
+ }
+ catch (MalformedURLException e) {
+ throw new IllegalStateException( "Could not interpret project directory as URL" );
+ }
+ }
+
+ @SuppressWarnings( "UnstableApiUsage" )
+ private static ClassLoader determineUnitClassLoader(Project project, SourceSet mainSourceSet) {
+ final String compileJavaTaskName = mainSourceSet.getCompileJavaTaskName();
+ final JavaCompile javaCompileTask = (JavaCompile) project.getTasks().getByName( compileJavaTaskName );
+ final URL projectClassesDirUrl = toUrl( javaCompileTask.getDestinationDirectory().get().getAsFile() );
+
+ return new URLClassLoader( new URL[] { projectClassesDirUrl }, MetamodelClass.class.getClassLoader() );
+ }
+
+ private static URL toUrl(File file) {
+ final URI uri = file.toURI();
+ try {
+ return uri.toURL();
+ }
+ catch (MalformedURLException e) {
+ throw new GradleException( "Could not convert classpath entry into URL : " + file.getAbsolutePath(), e );
+ }
+ }
+
+ private Properties generateIntegrationSettings() {
+ final Properties settings = new Properties();
+
+ settings.put( "hibernate.temp.use_jdbc_metadata_defaults", "false" );
+ settings.put( AvailableSettings.DIALECT, "H2" );
+ settings.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, false );
+ settings.put( AvailableSettings.USE_QUERY_CACHE, false );
+
+ return settings;
+ }
+
+ private boolean isMappingFile(File file) {
+ final String fileName = file.getName();
+
+ // convention
+ // - we could allow filters for flexibility?
+ return fileName.endsWith( ".hbm.xml" ) || fileName.endsWith( ".orm.xml" );
+ }
+
+ private static class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
+ private final URL unitRoot;
+ private final Properties properties;
+ private final ClassLoader classLoader;
+ private final List managedClassNames = new ArrayList<>();
+ private final List mappingFileNames = new ArrayList<>();
+
+ public PersistenceUnitInfoImpl(URL unitRoot, Properties properties, ClassLoader classLoader) {
+ this.unitRoot = unitRoot;
+ this.properties = properties;
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public String getPersistenceUnitName() {
+ return "jpa-static-metamodel-gen";
+ }
+
+ @Override
+ public URL getPersistenceUnitRootUrl() {
+ return unitRoot;
+ }
+
+ @Override
+ public Properties getProperties() {
+ return properties;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ @Override
+ public List getManagedClassNames() {
+ return managedClassNames;
+ }
+
+ public void addManagedClassName(String className) {
+ getManagedClassNames().add( className );
+ }
+
+ @Override
+ public List getMappingFileNames() {
+ return mappingFileNames;
+ }
+
+ public void addMappingFile(String fileName) {
+ getMappingFileNames().add( fileName );
+ }
+
+
+
+
+
+ @Override
+ public String getPersistenceProviderClassName() {
+ return HibernatePersistenceProvider.class.getName();
+ }
+
+ @Override
+ public PersistenceUnitTransactionType getTransactionType() {
+ return null;
+ }
+
+ @Override
+ public DataSource getJtaDataSource() {
+ return null;
+ }
+
+ @Override
+ public DataSource getNonJtaDataSource() {
+ return null;
+ }
+
+ @Override
+ public List getJarFileUrls() {
+ return null;
+ }
+
+ @Override
+ public boolean excludeUnlistedClasses() {
+ return true;
+ }
+
+ @Override
+ public SharedCacheMode getSharedCacheMode() {
+ return null;
+ }
+
+ @Override
+ public ValidationMode getValidationMode() {
+ return null;
+ }
+
+ @Override
+ public String getPersistenceXMLSchemaVersion() {
+ return null;
+ }
+
+ @Override
+ public void addTransformer(ClassTransformer transformer) {
+
+ }
+
+ @Override
+ public ClassLoader getNewTempClassLoader() {
+ return null;
+ }
+ }
+
+ @SuppressWarnings( "UnstableApiUsage" )
+ public static void apply(HibernateOrmSpec ormDsl, SourceSet mainSourceSet, Project project) {
+ final String mainCompileTaskName = mainSourceSet.getCompileJavaTaskName();
+ final JavaCompile mainCompileTask = (JavaCompile) project.getTasks().getByName( mainCompileTaskName );
+
+ final JpaMetamodelGenerationTask genTask = project.getTasks().create(
+ DSL_NAME,
+ JpaMetamodelGenerationTask.class,
+ ormDsl,
+ mainSourceSet,
+ mainCompileTask,
+ project
+ );
+ genTask.setGroup( HIBERNATE );
+ genTask.setDescription( "Generates the JPA 'static metamodel'" );
+
+ genTask.dependsOn( mainCompileTask );
+
+ final Task compileResourcesTask = project.getTasks().getByName( "processResources" );
+ genTask.dependsOn( compileResourcesTask );
+
+ final JavaCompile compileJpaMetamodelTask = project.getTasks().create( COMPILE_DSL_NAME, JavaCompile.class );
+ compileJpaMetamodelTask.setGroup( HIBERNATE );
+ compileJpaMetamodelTask.setDescription( "Compiles the JPA static metamodel generated by `" + DSL_NAME + "`" );
+ compileJpaMetamodelTask.setSourceCompatibility( mainCompileTask.getSourceCompatibility() );
+ compileJpaMetamodelTask.setTargetCompatibility( mainCompileTask.getTargetCompatibility() );
+ genTask.finalizedBy( compileJpaMetamodelTask );
+ compileJpaMetamodelTask.dependsOn( genTask );
+ compileJpaMetamodelTask.source( project.files( ormDsl.getJpaMetamodelSpec().getGenerationOutputDirectory() ) );
+ compileJpaMetamodelTask.getDestinationDirectory().set( ormDsl.getJpaMetamodelSpec().getCompileOutputDirectory() );
+ compileJpaMetamodelTask.setClasspath(
+ project.getConfigurations().getByName( "runtimeClasspath" ).plus( mainSourceSet.getRuntimeClasspath() )
+ );
+
+ compileJpaMetamodelTask.doFirst(
+ (task) -> {
+ project.getLogger().lifecycle( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
+ project.getLogger().lifecycle( "compileJpaMetamodel classpath" );
+ project.getLogger().lifecycle( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
+ ( (JavaCompile) task ).getClasspath().forEach(
+ entry -> project.getLogger().lifecycle( " > {}", entry.getAbsolutePath() )
+ );
+ project.getLogger().lifecycle( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" );
+ }
+ );
+ }
+
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeBag.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeBag.java
new file mode 100644
index 0000000000..6cdf182035
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeBag.java
@@ -0,0 +1,34 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Collection;
+
+public class AttributeBag extends AttributeSupport {
+ private final Class> elementJavaType;
+
+ public AttributeBag(
+ MetamodelClass metamodelClass,
+ String attributeName,
+ Class> elementJavaType) {
+ super( metamodelClass, attributeName, Collection.class );
+ this.elementJavaType = elementJavaType;
+ }
+
+ @Override
+ public void renderAttributeType(BufferedWriter writer) throws IOException {
+ writer.write(
+ format(
+ "CollectionAttribute<%s,%s>",
+ getOwnerDomainClassName(),
+ elementJavaType.getName()
+ )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeList.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeList.java
new file mode 100644
index 0000000000..d9f4ef4db8
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeList.java
@@ -0,0 +1,34 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Set;
+
+public class AttributeList extends AttributeSupport {
+ private final Class> elementJavaType;
+
+ public AttributeList(
+ MetamodelClass metamodelClass,
+ String attributeName,
+ Class> elementJavaType) {
+ super( metamodelClass, attributeName, Set.class );
+ this.elementJavaType = elementJavaType;
+ }
+
+ @Override
+ public void renderAttributeType(BufferedWriter writer) throws IOException {
+ writer.write(
+ format(
+ "ListAttribute<%s,%s>",
+ getOwnerDomainClassName(),
+ elementJavaType.getName()
+ )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeMap.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeMap.java
new file mode 100644
index 0000000000..c1d47e2c1c
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeMap.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Set;
+
+public class AttributeMap extends AttributeSupport {
+ private final Class> keyJavaType;
+ private final Class> elementJavaType;
+
+ public AttributeMap(
+ MetamodelClass metamodelClass,
+ String attributeName,
+ Class> keyJavaType,
+ Class> elementJavaType) {
+ super( metamodelClass, attributeName, Set.class );
+ this.keyJavaType = keyJavaType;
+ this.elementJavaType = elementJavaType;
+ }
+
+ @Override
+ public void renderAttributeType(BufferedWriter writer) throws IOException {
+ writer.write(
+ format(
+ "MapAttribute<%s,%s,%s>",
+ getOwnerDomainClassName(),
+ keyJavaType.getName(),
+ elementJavaType.getName()
+ )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSet.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSet.java
new file mode 100644
index 0000000000..4cfb15fc9e
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSet.java
@@ -0,0 +1,34 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Set;
+
+public class AttributeSet extends AttributeSupport {
+ private final Class> elementJavaType;
+
+ public AttributeSet(
+ MetamodelClass metamodelClass,
+ String attributeName,
+ Class> elementJavaType) {
+ super( metamodelClass, attributeName, Set.class );
+ this.elementJavaType = elementJavaType;
+ }
+
+ @Override
+ public void renderAttributeType(BufferedWriter writer) throws IOException {
+ writer.write(
+ format(
+ "SetAttribute<%s,%s>",
+ getOwnerDomainClassName(),
+ elementJavaType.getName()
+ )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSingular.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSingular.java
new file mode 100644
index 0000000000..95cb61c33b
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSingular.java
@@ -0,0 +1,29 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+public class AttributeSingular extends AttributeSupport {
+
+ public AttributeSingular(MetamodelClass metamodelClass, String name, Class javaType) {
+ super( metamodelClass, name, javaType );
+ }
+
+ @Override
+ public void renderAttributeType(BufferedWriter writer) throws IOException {
+ // JPA stuff already imported
+ writer.write(
+ format(
+ "SingularAttribute<%s,%s>",
+ getOwnerDomainClassName(),
+ getAttributeJavaType().getName()
+ )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSupport.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSupport.java
new file mode 100644
index 0000000000..0c0055ff15
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/AttributeSupport.java
@@ -0,0 +1,75 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.util.Locale;
+
+import static java.lang.Character.LINE_SEPARATOR;
+
+public abstract class AttributeSupport implements MetamodelAttribute {
+ private final MetamodelClass metamodelClass;
+ private final String name;
+ private final Class> javaType;
+
+ public AttributeSupport(
+ MetamodelClass metamodelClass,
+ String name,
+ Class> javaType) {
+ this.metamodelClass = metamodelClass;
+ this.name = name;
+ this.javaType = javaType;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Class> getAttributeJavaType() {
+ return javaType;
+ }
+
+ public String getOwnerDomainClassName() {
+ return metamodelClass.getMetamodelClassName();
+ }
+
+ @Override
+ public void renderJpaMembers(BufferedWriter writer) {
+ try {
+ writer.write( " public static volatile " );
+ renderAttributeType( writer );
+ writer.write( " " + name );
+ writer.write( ';' );
+ writer.write( LINE_SEPARATOR );
+ }
+ catch (IOException e) {
+ throw new IllegalStateException( "Problem writing attribute `" + metamodelClass.getMetamodelClassName() + "#" + name + "` to output stream", e );
+ }
+ }
+
+ public abstract void renderAttributeType(BufferedWriter writer) throws IOException;
+
+ protected String format(String pattern, Object... args) {
+ return String.format( Locale.ROOT, pattern, args );
+ }
+
+ @Override
+ public void renderNameConstant(BufferedWriter writer) {
+ try {
+ writer.write( " public static final String " );
+ writer.write( getName().toUpperCase( Locale.ROOT ) );
+ writer.write( " = \"" + getName() + "\";" );
+ writer.write( LINE_SEPARATOR );
+ }
+ catch (IOException e) {
+ throw new IllegalStateException( "Problem writing attribute `" + metamodelClass.getMetamodelClassName() + "#" + name + "` to output stream", e );
+ }
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/JpaStaticMetamodelGenerator.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/JpaStaticMetamodelGenerator.java
new file mode 100644
index 0000000000..645d766a51
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/JpaStaticMetamodelGenerator.java
@@ -0,0 +1,105 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import javax.persistence.spi.PersistenceUnitInfo;
+
+import org.gradle.api.file.Directory;
+import org.gradle.api.file.RegularFile;
+
+import org.hibernate.boot.spi.MetadataImplementor;
+import org.hibernate.jpa.boot.spi.Bootstrap;
+import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.MappedSuperclass;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationSpec;
+
+public class JpaStaticMetamodelGenerator {
+ public static void processMetamodel(
+ PersistenceUnitInfo persistenceUnitInfo,
+ JpaMetamodelGenerationSpec spec) {
+ EntityManagerFactoryBuilder target = Bootstrap.getEntityManagerFactoryBuilder( persistenceUnitInfo, Collections.emptyMap() );
+ try {
+ new JpaStaticMetamodelGenerator( spec, target.metadata() ).process();
+ }
+ finally {
+ target.cancel();
+ }
+ }
+
+ private final JpaMetamodelGenerationSpec spec;
+ private final MetadataImplementor metadata;
+
+ private final Directory generationOutputDirectory;
+ private final ObjectFactory objectFactory;
+
+ private final Set processedDomainTypeNames = new HashSet<>();
+
+ private JpaStaticMetamodelGenerator(JpaMetamodelGenerationSpec spec, MetadataImplementor metadata) {
+ this.spec = spec;
+ this.metadata = metadata;
+ this.generationOutputDirectory = spec.getGenerationOutputDirectory().get();
+ this.objectFactory = new ObjectFactory( metadata );
+ }
+
+ private void process() {
+ final Set mappedSuperclasses = metadata.getMappedSuperclassMappingsCopy();
+ if ( mappedSuperclasses != null ) {
+ mappedSuperclasses.forEach( this::handleMappedClass );
+ }
+
+ final java.util.Collection entityBindings = metadata.getEntityBindings();
+ if ( entityBindings != null ) {
+ entityBindings.forEach( this::handlePersistentClass );
+ }
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private void handleMappedClass(MappedSuperclass mappingDescriptor) {
+ final MetamodelClass metamodelClass = objectFactory.metamodelClass( mappingDescriptor );
+ handleManagedClass( metamodelClass, mappingDescriptor.getDeclaredPropertyIterator() );
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private void handlePersistentClass(PersistentClass persistentClass) {
+ final MetamodelClass metamodelClass = objectFactory.metamodelClass( persistentClass );
+ handleManagedClass( metamodelClass, persistentClass.getDeclaredPropertyIterator() );
+ }
+
+ private void handleManagedClass(MetamodelClass metamodelClass, Iterator propertyIterator) {
+ if ( ! processedDomainTypeNames.add( metamodelClass.getDomainClassName() ) ) {
+ // already processed
+ return;
+ }
+
+ propertyIterator.forEachRemaining(
+ property -> metamodelClass.addAttribute(
+ objectFactory.attribute( property, property.getValue(), metamodelClass, this::handleEmbeddable )
+ )
+ );
+
+ final String replaced = metamodelClass.getMetamodelClassName().replace( '.', '/' );
+ final String metamodelClassJavaFileName = replaced + ".java";
+ final RegularFile metamodelClassJavaFile = generationOutputDirectory.file( metamodelClassJavaFileName );
+
+ final File metamodelClassJavaFileAsFile = metamodelClassJavaFile.getAsFile();
+ metamodelClass.writeToFile( metamodelClassJavaFileAsFile, spec );
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private void handleEmbeddable(Component embeddedValueMapping) {
+ final MetamodelClass metamodelClass = objectFactory.metamodelClass( embeddedValueMapping );
+ handleManagedClass( metamodelClass, embeddedValueMapping.getPropertyIterator() );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/MetamodelAttribute.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/MetamodelAttribute.java
new file mode 100644
index 0000000000..c3ec077149
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/MetamodelAttribute.java
@@ -0,0 +1,19 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+
+public interface MetamodelAttribute {
+ String getName();
+
+ Class> getAttributeJavaType();
+
+ void renderJpaMembers(BufferedWriter writer);
+
+ void renderNameConstant(BufferedWriter writer);
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/MetamodelClass.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/MetamodelClass.java
new file mode 100644
index 0000000000..bcde5938fd
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/MetamodelClass.java
@@ -0,0 +1,156 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.text.DateFormat;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.JavaVersion;
+
+import org.hibernate.orm.tooling.gradle.metamodel.JpaMetamodelGenerationSpec;
+
+import static java.lang.Character.LINE_SEPARATOR;
+
+/**
+ * Descriptor for a class in the JPA static metamodel being generated
+ */
+public class MetamodelClass {
+
+ private final String domainClassName;
+ private final String metamodelClassName;
+
+ private final String metamodelSuperClassName;
+
+ private final TreeSet attributes = new TreeSet<>( Comparator.comparing( MetamodelAttribute::getName ) );
+
+ public MetamodelClass(String domainClassName, String superTypeName) {
+ this.domainClassName = domainClassName;
+ this.metamodelClassName = domainClassName + "_";
+ this.metamodelSuperClassName = superTypeName == null ? null : superTypeName + "_";
+ }
+
+ public String getMetamodelClassName() {
+ return metamodelClassName;
+ }
+
+ public String getDomainClassName() {
+ return domainClassName;
+ }
+
+ public void addAttribute(MetamodelAttribute attribute) {
+ assert attribute != null;
+ attributes.add( attribute );
+ }
+
+ public void writeToFile(File outputFile, JpaMetamodelGenerationSpec spec) {
+ prepareOutputFile( outputFile );
+
+ final Path path = outputFile.toPath();
+
+ try ( final BufferedWriter writer = Files.newBufferedWriter( path, StandardCharsets.UTF_8, StandardOpenOption.WRITE ) ) {
+
+ renderClassPreamble( writer, spec );
+ writer.write( LINE_SEPARATOR );
+
+ writer.write( "public abstract class " + metamodelClassName );
+ if ( metamodelSuperClassName != null ) {
+ writer.write( " extends " + metamodelSuperClassName );
+ }
+ writer.write( " {" );
+ writer.write( LINE_SEPARATOR );
+ writer.write( LINE_SEPARATOR );
+
+ writer.write( " // Attribute name constants" );
+ writer.write( LINE_SEPARATOR );
+ attributes.forEach( attribute -> attribute.renderNameConstant( writer ) );
+ writer.write( LINE_SEPARATOR );
+ writer.write( LINE_SEPARATOR );
+
+ writer.write( " // JPA static metamodel fields" );
+ writer.write( LINE_SEPARATOR );
+ attributes.forEach( attribute -> attribute.renderJpaMembers( writer ) );
+ writer.write( LINE_SEPARATOR );
+
+ writer.write( "}" );
+ writer.write( LINE_SEPARATOR );
+ }
+ catch (IOException e) {
+ throw new IllegalStateException( "Unable to open file : " + outputFile.getAbsolutePath(), e );
+ }
+ }
+
+ private void renderClassPreamble(BufferedWriter writer, JpaMetamodelGenerationSpec spec) throws IOException {
+ final String nowFormatted = DateFormat.getDateInstance().format( new Date() );
+
+ writer.write( "// Generated by Hibernate ORM Gradle tooling - " + nowFormatted );
+ writer.write( LINE_SEPARATOR );
+ writer.write( LINE_SEPARATOR );
+
+ writer.write( "import javax.persistence.*;" );
+ writer.write( LINE_SEPARATOR );
+ writer.write( "import javax.persistence.metamodel.*;" );
+ writer.write( LINE_SEPARATOR );
+ writer.write( LINE_SEPARATOR );
+
+ writer.write( "/** JPA static metamodel descriptor for the `" + domainClassName + "` domain class */" );
+ writer.write( LINE_SEPARATOR );
+
+ // first, the generated annotation
+ if ( spec.getApplyGeneratedAnnotation().getOrElse( true ) ) {
+ final JavaVersion javaVersion = spec.getTargetJavaVersionAccess().getOrElse( JavaVersion.current() );
+ final String qualifiedAnnotationName = javaVersion.isJava9Compatible()
+ ? "javax.annotation.processing.Generated"
+ : "javax.annotation.Generated";
+ final String generatedAnnotationFragment = String.format(
+ Locale.ROOT,
+ "@%s( value=\"%s\", date=\"%s\", comments=\"%s\" )",
+ qualifiedAnnotationName,
+ JpaStaticMetamodelGenerator.class.getName(),
+ nowFormatted,
+ "Generated by Hibernate ORM Gradle tooling"
+ );
+ writer.write( generatedAnnotationFragment );
+ writer.write( LINE_SEPARATOR );
+ }
+
+ final Set suppressions = spec.getSuppressions().getOrElse( Collections.emptySet() );
+ if ( ! suppressions.isEmpty() ) {
+ writer.write( "@SuppressWarnings( { " );
+ for ( String suppression : suppressions ) {
+ writer.write( "\"" + suppression + "\", " );
+ }
+ writer.write( " } )" );
+ writer.write( LINE_SEPARATOR );
+ }
+
+ writer.write( "@StaticMetamodel( " + domainClassName + ".class )" );
+ }
+
+ @SuppressWarnings( "ResultOfMethodCallIgnored" )
+ public void prepareOutputFile(File outputFile) {
+ try {
+ outputFile.getParentFile().mkdirs();
+ outputFile.createNewFile();
+ }
+ catch (IOException e) {
+ throw new GradleException( "Unable to prepare output file `" + outputFile.getAbsolutePath() + "`", e );
+ }
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/ObjectFactory.java b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/ObjectFactory.java
new file mode 100644
index 0000000000..51b3e785e9
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/java/org/hibernate/orm/tooling/gradle/metamodel/model/ObjectFactory.java
@@ -0,0 +1,237 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle.metamodel.model;
+
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import org.gradle.api.GradleException;
+
+import org.hibernate.boot.spi.MetadataImplementor;
+import org.hibernate.mapping.Any;
+import org.hibernate.mapping.Bag;
+import org.hibernate.mapping.BasicValue;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.DependantValue;
+import org.hibernate.mapping.KeyValue;
+import org.hibernate.mapping.List;
+import org.hibernate.mapping.Map;
+import org.hibernate.mapping.MappedSuperclass;
+import org.hibernate.mapping.OneToMany;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Set;
+import org.hibernate.mapping.ToOne;
+import org.hibernate.mapping.Value;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ObjectFactory {
+ private final MetadataImplementor metadata;
+
+ public ObjectFactory(MetadataImplementor metadata) {
+ this.metadata = metadata;
+ }
+
+ public MetamodelClass metamodelClass(PersistentClass entityDescriptor) {
+ return new MetamodelClass( entityDescriptor.getMappedClass().getName(), determineSuperTypeName( entityDescriptor ) );
+ }
+
+ private String determineSuperTypeName(PersistentClass entityDescriptor) {
+ if ( entityDescriptor.getSuperMappedSuperclass() != null ) {
+ return entityDescriptor.getSuperMappedSuperclass().getMappedClass().getName();
+ }
+
+ if ( entityDescriptor.getSuperclass() != null ) {
+ return entityDescriptor.getSuperclass().getMappedClass().getName();
+ }
+
+ return null;
+ }
+
+ public MetamodelClass metamodelClass(MappedSuperclass mappedSuperclassDescriptor) {
+ return new MetamodelClass( mappedSuperclassDescriptor.getMappedClass().getName(), determineSuperTypeName( mappedSuperclassDescriptor ) );
+ }
+
+ private String determineSuperTypeName(MappedSuperclass mappedSuperclassDescriptor) {
+ if ( mappedSuperclassDescriptor.getSuperMappedSuperclass() != null ) {
+ return mappedSuperclassDescriptor.getSuperMappedSuperclass().getMappedClass().getName();
+ }
+
+ if ( mappedSuperclassDescriptor.getSuperPersistentClass() != null ) {
+ return mappedSuperclassDescriptor.getSuperPersistentClass().getMappedClass().getName();
+ }
+
+
+ return null;
+ }
+
+ public MetamodelClass metamodelClass(Component embeddedMapping) {
+ return new MetamodelClass( embeddedMapping.getComponentClassName(), null );
+ }
+
+ public MetamodelAttribute attribute(
+ Property property,
+ Value propertyValueMapping,
+ MetamodelClass metamodelClass,
+ Consumer componentConsumer) {
+ if ( propertyValueMapping instanceof DependantValue ) {
+ final DependantValue dependantValue = (DependantValue) propertyValueMapping;
+ final KeyValue wrappedValue = dependantValue.getWrappedValue();
+ return attribute( property, wrappedValue, metamodelClass, componentConsumer );
+ }
+
+ if ( propertyValueMapping instanceof Collection ) {
+ return pluralAttribute( property, (Collection) propertyValueMapping, metamodelClass, componentConsumer );
+ }
+
+ final Class> propertyJavaType = determineSingularJavaType( property, propertyValueMapping, metamodelClass, componentConsumer );
+ return new AttributeSingular( metamodelClass, property.getName(), propertyJavaType );
+ }
+
+ private Class> determineSingularJavaType(
+ Property property,
+ Value propertyValueMapping,
+ MetamodelClass metamodelClass,
+ Consumer componentConsumer) {
+ if ( propertyValueMapping instanceof BasicValue ) {
+ final BasicValue basicValue = (BasicValue) propertyValueMapping;
+ return basicValue.resolve().getDomainJavaDescriptor().getJavaType();
+ }
+
+ if ( propertyValueMapping instanceof Component ) {
+ final Component component = (Component) propertyValueMapping;
+ componentConsumer.accept( component );
+ return component.getComponentClass();
+ }
+
+ if ( propertyValueMapping instanceof Any ) {
+ return Object.class;
+ }
+
+ if ( propertyValueMapping instanceof ToOne ) {
+ final ToOne toOne = (ToOne) propertyValueMapping;
+ final String referencedEntityName = toOne.getReferencedEntityName();
+ final PersistentClass entityBinding = metadata.getEntityBinding( referencedEntityName );
+ final Class> mappedClass = entityBinding.getMappedClass();
+ if ( mappedClass == null ) {
+ throw new GradleException(
+ String.format(
+ Locale.ROOT,
+ "Could not determine ToOne java type : %s#%s",
+ metamodelClass.getDomainClassName(),
+ property.getName()
+ )
+ );
+ }
+ return mappedClass;
+ }
+
+ propertyValueMapping.setTypeUsingReflection( metamodelClass.getDomainClassName(), property.getName() );
+ return propertyValueMapping.getType().getReturnedClass();
+ }
+
+ private MetamodelAttribute pluralAttribute(
+ Property property,
+ Collection collectionMapping,
+ MetamodelClass metamodelClass,
+ Consumer componentConsumer) {
+ if ( collectionMapping instanceof Set ) {
+ return new AttributeSet(
+ metamodelClass,
+ property.getName(),
+ determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
+ );
+ }
+
+ if ( collectionMapping instanceof Bag ) {
+ return new AttributeBag(
+ metamodelClass,
+ property.getName(),
+ determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
+ );
+ }
+
+ if ( collectionMapping instanceof List ) {
+ return new AttributeList(
+ metamodelClass,
+ property.getName(),
+ determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
+ );
+ }
+
+ if ( collectionMapping instanceof Map ) {
+ return new AttributeMap(
+ metamodelClass,
+ property.getName(),
+ determineCollectionPartJavaType( property, ( (Map) collectionMapping ).getIndex(), metamodelClass, componentConsumer ),
+ determineCollectionPartJavaType( property, collectionMapping.getElement(), metamodelClass, componentConsumer )
+ );
+ }
+
+ throw new UnsupportedOperationException( "Unsupported plural value type : " + collectionMapping.getClass().getName() );
+ }
+
+ private Class> determineCollectionPartJavaType(
+ Property property,
+ Value partJavaType,
+ MetamodelClass metamodelClass,
+ Consumer componentConsumer) {
+ if ( partJavaType instanceof DependantValue ) {
+ final DependantValue dependantValue = (DependantValue) partJavaType;
+ final KeyValue wrappedValue = dependantValue.getWrappedValue();
+ return determineCollectionPartJavaType( property, wrappedValue, metamodelClass, componentConsumer );
+ }
+
+ if ( partJavaType instanceof BasicValue ) {
+ final BasicValue basicValue = (BasicValue) partJavaType;
+ return basicValue.resolve().getDomainJavaDescriptor().getJavaType();
+ }
+
+ if ( partJavaType instanceof Component ) {
+ final Component component = (Component) partJavaType;
+ componentConsumer.accept( component );
+ return component.getComponentClass();
+ }
+
+ if ( partJavaType instanceof Any ) {
+ return Object.class;
+ }
+
+ if ( partJavaType instanceof OneToMany ) {
+ final OneToMany oneToMany = (OneToMany) partJavaType;
+ final PersistentClass associatedClass = oneToMany.getAssociatedClass();
+ return associatedClass.getMappedClass();
+ }
+
+ if ( partJavaType instanceof ToOne ) {
+ final ToOne toOne = (ToOne) partJavaType;
+ final String referencedEntityName = toOne.getReferencedEntityName();
+ if ( referencedEntityName != null ) {
+ final PersistentClass entityBinding = metadata.getEntityBinding( referencedEntityName );
+ if ( entityBinding != null ) {
+ final Class> mappedClass = entityBinding.getMappedClass();
+ if ( mappedClass != null ) {
+ return mappedClass;
+ }
+ }
+ }
+ throw new GradleException(
+ String.format(
+ Locale.ROOT,
+ "Could not determine ToOne java type : %s#%s",
+ metamodelClass.getDomainClassName(),
+ property.getName()
+ )
+ );
+ }
+
+ return partJavaType.getType().getReturnedClass();
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.hibernate.orm.properties b/tooling/hibernate-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.hibernate.orm.properties
deleted file mode 100644
index 0fb1d5fd7b..0000000000
--- a/tooling/hibernate-gradle-plugin/src/main/resources/META-INF/gradle-plugins/org.hibernate.orm.properties
+++ /dev/null
@@ -1,7 +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 .
-#
-implementation-class=org.hibernate.orm.tooling.gradle.HibernatePlugin
diff --git a/tooling/hibernate-gradle-plugin/src/main/resources/META-INF/hibernate-orm.version b/tooling/hibernate-gradle-plugin/src/main/resources/META-INF/hibernate-orm.version
new file mode 100644
index 0000000000..a20c6c25ae
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/main/resources/META-INF/hibernate-orm.version
@@ -0,0 +1 @@
+@hibernateVersion@
\ No newline at end of file
diff --git a/tooling/hibernate-gradle-plugin/src/test/groovy/org/hibernate/orm/tooling/gradle/HibernatePluginTest.groovy b/tooling/hibernate-gradle-plugin/src/test/groovy/org/hibernate/orm/tooling/gradle/HibernatePluginTest.groovy
deleted file mode 100644
index 845e4a681b..0000000000
--- a/tooling/hibernate-gradle-plugin/src/test/groovy/org/hibernate/orm/tooling/gradle/HibernatePluginTest.groovy
+++ /dev/null
@@ -1,89 +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 .
- */
-package org.hibernate.orm.tooling.gradle
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.plugins.JavaPluginConvention
-import org.gradle.api.tasks.SourceSet
-import org.gradle.api.tasks.compile.JavaCompile
-import org.gradle.testfixtures.ProjectBuilder
-
-import org.junit.Test
-
-import static org.junit.Assert.assertNotNull
-/**
- * Test what we can. ProjectBuilder is better than nothing, but still quited limited in what
- * you can test (e.g. you cannot test task execution).
- *
- * @author Steve Ebersole
- */
-class HibernatePluginTest {
- @Test
- public void testHibernatePluginAddsExtension() {
- Project project = ProjectBuilder.builder().build()
- project.plugins.apply 'org.hibernate.orm'
-
- assertNotNull( project.extensions.findByName( "hibernate" ) )
- }
-
- @Test
- public void testHibernateExtensionConfig() {
- Project project = ProjectBuilder.builder().build()
- project.plugins.apply 'org.hibernate.orm'
-
- project.extensions.findByType( HibernateExtension.class ).enhance {
- enableLazyInitialization = true
- enableDirtyTracking = true
- enableAssociationManagement = false
- enableExtendedEnhancement = false
- }
- }
-
- @Test
- public void testEnhanceTask() {
- Project project = ProjectBuilder.builder().build()
- project.plugins.apply 'org.hibernate.orm'
-
- def task = project.tasks.create( "finishHim", EnhanceTask )
-
- task.options {
- enableLazyInitialization = true
- enableDirtyTracking = true
- enableAssociationManagement = false
- enableExtendedEnhancement = false
- }
-
- task.sourceSets = project.getConvention().getPlugin( JavaPluginConvention ).sourceSets.main
-
- task.enhance()
- }
-
- @Test
- public void testTaskAction() {
- Project project = ProjectBuilder.builder().build()
- project.plugins.apply 'org.hibernate.orm'
-
- // the test sourceSet
- def sourceSet = project.getConvention().getPlugin( JavaPluginConvention ).sourceSets.test;
-
- // The compile task for the test sourceSet
- final JavaCompile compileTestTask = project.getTasks().findByName( sourceSet.getCompileJavaTaskName() );
-
- // Lets add our enhancer to enhance the test classes after the test are compiled
- compileTestTask.doLast {
- EnhancementHelper.enhance(
- sourceSet,
- project.extensions.findByType( HibernateExtension.class ).enhance,
- project
- )
- }
-
- // TODO find how to do this in Gradle 5
- // the class-level javadoc says it's not possible, and it was there in Gradle 4.x...
- //compileTestTask.execute()
- }
-}
diff --git a/tooling/hibernate-gradle-plugin/src/testKit/java/org/hibernate/orm/tooling/gradle/HibernateOrmPluginTest.java b/tooling/hibernate-gradle-plugin/src/testKit/java/org/hibernate/orm/tooling/gradle/HibernateOrmPluginTest.java
new file mode 100644
index 0000000000..e31eea697b
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/testKit/java/org/hibernate/orm/tooling/gradle/HibernateOrmPluginTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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
+ */
+package org.hibernate.orm.tooling.gradle;
+
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.BuildTask;
+import org.gradle.testkit.runner.GradleRunner;
+import org.gradle.testkit.runner.TaskOutcome;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import com.github.sebersole.testkit.Project;
+import com.github.sebersole.testkit.ProjectScope;
+import com.github.sebersole.testkit.TestKit;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test what we can. TestKit is better than nothing, but still somewhat limited in what
+ * you can test in my experience
+ *
+ * @author Steve Ebersole
+ */
+@TestKit
+class HibernateOrmPluginTest {
+ @Test
+ public void testEnhancementTaskAsFinalizer(@Project( "simple" ) ProjectScope projectScope) {
+ final GradleRunner gradleRunner = projectScope.createGradleRunner( "clean", "compileJava" );
+ final BuildResult result = gradleRunner.build();
+ final BuildTask task = result.task( ":hibernateEnhance" );
+ assert task != null;
+
+ assertThat(
+ task.getOutcome(),
+ anyOf( is( TaskOutcome.SUCCESS ), is( TaskOutcome.UP_TO_DATE ) )
+ );
+ }
+
+ @Test
+ public void testEnhancementTask(@Project( "simple" ) ProjectScope projectScope) {
+ final GradleRunner gradleRunner = projectScope.createGradleRunner(
+ "clean",
+ "hibernateEnhance"
+ );
+ final BuildResult result = gradleRunner.build();
+ final BuildTask task = result.task( ":hibernateEnhance" );
+ assert task != null;
+
+ assertThat( task.getOutcome(), is( TaskOutcome.SUCCESS ) );
+ }
+
+ @Test
+ public void testEnhancementTaskUpToDate(@Project( "simple" ) ProjectScope projectScope) {
+ final GradleRunner gradleRunner = projectScope.createGradleRunner(
+ "clean",
+ "hibernateEnhance"
+ );
+ final BuildResult result = gradleRunner.build();
+ final BuildTask task = result.task( ":hibernateEnhance" );
+ assert task != null;
+
+ assertThat(
+ task.getOutcome(),
+ anyOf( is( TaskOutcome.SUCCESS ), is( TaskOutcome.UP_TO_DATE ) )
+ );
+ }
+
+ @Test
+// @Disabled( "Problem with ClassPathAndModulePathAggregatedServiceLoader and loading Java services" )
+ public void testJpaMetamodelGen(@Project( "simple" ) ProjectScope projectScope) {
+ final GradleRunner gradleRunner = projectScope.createGradleRunner(
+ "clean",
+ "generateJpaMetamodel"
+ );
+ final BuildResult result = gradleRunner.build();
+ final BuildTask task = result.task( ":generateJpaMetamodel" );
+ assert task != null;
+
+ assertThat(
+ task.getOutcome(),
+ anyOf( is( TaskOutcome.SUCCESS ), is( TaskOutcome.UP_TO_DATE ) )
+ );
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/build.gradle b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/build.gradle
new file mode 100644
index 0000000000..7ef328e56e
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/build.gradle
@@ -0,0 +1,29 @@
+/*
+ * 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
+ */
+
+plugins {
+ id 'java'
+ id 'org.hibernate.orm'
+}
+
+repositories {
+ mavenCentral()
+
+ maven {
+ name 'jboss-snapshots-repository'
+ url 'https://repository.jboss.org/nexus/content/repositories/snapshots'
+ }
+}
+
+hibernate {
+ enhancement {
+ lazyInitialization( true )
+ dirtyTracking = true
+ }
+ jpaMetamodel {
+ }
+}
diff --git a/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/settings.gradle b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/settings.gradle
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/src/main/java/TheEmbeddable.java b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/src/main/java/TheEmbeddable.java
new file mode 100644
index 0000000000..78827d75f9
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/src/main/java/TheEmbeddable.java
@@ -0,0 +1,23 @@
+import javax.persistence.Embeddable;
+
+@Embeddable
+public class TheEmbeddable {
+ private String valueOne;
+ private String valueTwo;
+
+ public String getValueOne() {
+ return valueOne;
+ }
+
+ public void setValueOne(String valueOne) {
+ this.valueOne = valueOne;
+ }
+
+ public String getValueTwo() {
+ return valueTwo;
+ }
+
+ public void setValueTwo(String valueTwo) {
+ this.valueTwo = valueTwo;
+ }
+}
\ No newline at end of file
diff --git a/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/src/main/java/TheEntity.java b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/src/main/java/TheEntity.java
new file mode 100644
index 0000000000..4e84a53205
--- /dev/null
+++ b/tooling/hibernate-gradle-plugin/src/testKit/resources/simple/src/main/java/TheEntity.java
@@ -0,0 +1,79 @@
+import javax.persistence.ElementCollection;
+import javax.persistence.Embedded;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+
+import java.util.Set;
+
+@Entity
+public class TheEntity {
+ @Id
+ private Integer id;
+ private String name;
+
+ @Embedded
+ private TheEmbeddable theEmbeddable;
+
+ @ManyToOne
+ @JoinColumn
+ private TheEntity theManyToOne;
+
+ @OneToMany( mappedBy = "theManyToOne" )
+ private Set theOneToMany;
+
+ @ElementCollection
+ @JoinColumn( name = "owner_id" )
+ private Set theEmbeddableCollection;
+
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public TheEmbeddable getTheEmbeddable() {
+ return theEmbeddable;
+ }
+
+ public void setTheEmbeddable(TheEmbeddable theEmbeddable) {
+ this.theEmbeddable = theEmbeddable;
+ }
+
+ public TheEntity getTheManyToOne() {
+ return theManyToOne;
+ }
+
+ public void setTheManyToOne(TheEntity theManyToOne) {
+ this.theManyToOne = theManyToOne;
+ }
+
+ public Set getTheOneToMany() {
+ return theOneToMany;
+ }
+
+ public void setTheOneToMany(Set theOneToMany) {
+ this.theOneToMany = theOneToMany;
+ }
+
+ public Set getTheEmbeddableCollection() {
+ return theEmbeddableCollection;
+ }
+
+ public void setTheEmbeddableCollection(Set theEmbeddableCollection) {
+ this.theEmbeddableCollection = theEmbeddableCollection;
+ }
+}
\ No newline at end of file
diff --git a/tooling/project-template/README.adoc b/tooling/project-template/README.adoc
new file mode 100644
index 0000000000..ee9a399e11
--- /dev/null
+++ b/tooling/project-template/README.adoc
@@ -0,0 +1,6 @@
+A template for projects wanting to use Hibernate ORM.
+
+Useful to either:
+
+1. Bootstrap a new user project
+2. Bootstrap a test-case project
diff --git a/tooling/project-template/project-template.gradle b/tooling/project-template/project-template.gradle
new file mode 100644
index 0000000000..7addf7d7dd
--- /dev/null
+++ b/tooling/project-template/project-template.gradle
@@ -0,0 +1,112 @@
+import org.apache.tools.ant.filters.ReplaceTokens
+
+/*
+ * 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
+ */
+plugins {
+ id 'base'
+
+ // for publishing snapshots
+ id 'maven-publish'
+ id 'org.hibernate.build.maven-repo-auth'
+
+ // publishing to BinTray
+ id "com.jfrog.bintray"
+ id "nu.studer.credentials" version "2.1"
+}
+
+// NOTE : Had trouble using the `distribution` plugin, so manually using Jar/Tar tasks
+
+version = '0.1'
+
+ext {
+ processedTemplateDir = project.layout.buildDirectory.dir('resources/template')
+ archiveDir = project.layout.buildDirectory.dir('distributions')
+}
+
+task processTemplateResources(type:Copy) {
+ inputs.files( 'src/template/resources' )
+ outputs.dir( processedTemplateDir )
+
+ description = 'Copies the template sources into the build dir, performing some replacements'
+
+ from( 'src/template/resources' ) {
+ filter( ReplaceTokens, tokens: [ 'ormVersion' : project.version.toString() ] )
+ }
+ into processedTemplateDir.get().asFile
+}
+
+task templateTgz(type:Tar) {
+ description = 'Bundles the template project into a TGZ archive'
+
+ inputs.files( processedTemplateDir )
+ outputs.dir( archiveDir )
+
+ dependsOn project.tasks.processTemplateResources
+
+ compression = Compression.GZIP
+
+ from processedTemplateDir.get().asFile
+
+ destinationDirectory = archiveDir
+}
+
+task templateZip(type:Zip) {
+ description = 'Bundles the template project into a Zip archive'
+
+ inputs.files( processedTemplateDir )
+ outputs.dir( archiveDir )
+
+ dependsOn project.tasks.processTemplateResources
+
+ from processedTemplateDir.get().asFile
+
+ destinationDirectory = archiveDir
+}
+
+bintray {
+ user = credentials.'personal.bintray.user'
+ key = credentials.'personal.bintray.key'
+
+ filesSpec {
+ from templateTgz
+ from templateZip
+ }
+
+ pkg {
+ userOrg = 'hibernate'
+ repo = 'generic'
+ name = 'orm-project-template'
+ }
+}
+
+task assembleDist( dependsOn: [tasks.templateTgz, tasks.templateZip] )
+task release( dependsOn: tasks.assembleDist )
+
+tasks.publish {
+ dependsOn tasks.assembleDist
+}
+
+tasks.bintrayUpload {
+ dependsOn tasks.assembleDist
+ doFirst {
+ if ( credentials.'personal.bintray.user' == null ) {
+ throw new GradleException( "BinTray user not known, cannot perform upload" );
+ }
+ if ( credentials.'personal.bintray.key' == null ) {
+ throw new GradleException( "BinTray API key not known, cannot perform upload" );
+ }
+ }
+}
+
+if ( version.toString().endsWith( "-SNAPSHOT" ) ) {
+ tasks.bintrayUpload.enabled = false
+ tasks.release.dependsOn tasks.publish
+}
+else {
+ tasks.publish.enabled = false
+ tasks.release.dependsOn tasks.bintrayUpload
+}
\ No newline at end of file
diff --git a/tooling/project-template/src/template/resources/README.adoc b/tooling/project-template/src/template/resources/README.adoc
new file mode 100644
index 0000000000..d447ebe99e
--- /dev/null
+++ b/tooling/project-template/src/template/resources/README.adoc
@@ -0,0 +1,5 @@
+A template project to help bootstrap a Gradle project with support for Hibernate
+specific extensions.
+
+This might be useful for new users wanting to get started as well as a quick way to
+bootstrap project for test cases (bug reports, etc)
diff --git a/tooling/project-template/src/template/resources/build.gradle.kts b/tooling/project-template/src/template/resources/build.gradle.kts
new file mode 100644
index 0000000000..9a1afd7d5a
--- /dev/null
+++ b/tooling/project-template/src/template/resources/build.gradle.kts
@@ -0,0 +1,80 @@
+/*
+ * 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
+ */
+
+// ###################################################################################
+// again, needed to be able to consume `org.hibernate.orm` plugin SNAPSHOTS
+buildscript {
+ configurations {
+ classpath {
+ resolutionStrategy {
+ cacheChangingModulesFor(0, java.util.concurrent.TimeUnit.SECONDS )
+ }
+ }
+ }
+}
+// ###################################################################################
+
+
+plugins {
+ java
+
+ // todo : find a way to inject this version
+ // - this is yet another example of where lazybones
+ // (or proper Gradle build-init feature) would be
+ // incredibly useful. Same with groupId, package-name,
+ // etc.
+ id( "org.hibernate.orm" ) version "@ormVersion@"
+}
+
+group = "your.org"
+version = "the-version"
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ val ormVersion = "@ormVersion@"
+ val junit5Version = "5.3.1"
+ val h2Version = "1.4.199"
+
+ implementation( "org.hibernate.orm", "hibernate-core", ormVersion )
+
+ testImplementation( "org.hibernate.orm", "hibernate-testing", ormVersion )
+ testImplementation( "org.junit.jupiter", "junit-jupiter-api", junit5Version )
+ testImplementation( "org.junit.jupiter", "junit-jupiter-params", junit5Version )
+
+ testRuntimeOnly( "org.junit.jupiter", "junit-jupiter-engine", junit5Version )
+ testRuntimeOnly( "com.h2database", "h2", h2Version )
+ testRuntimeOnly( "org.jboss.logging", "jboss-logging", "3.3.2.Final" )
+ testRuntimeOnly( "log4j", "log4j", "1.2.17" )
+}
+
+hibernate {
+ enhancement {
+ // all false by default
+ lazyInitialization = true
+ dirtyTracking = true
+ }
+}
+
+tasks {
+ test {
+ useJUnitPlatform()
+ }
+}
+
+task( "compile" ) {
+ dependsOn( tasks.compileJava )
+ dependsOn( tasks.compileTestJava )
+ dependsOn( tasks.processResources )
+ dependsOn( tasks.processTestResources )
+}
+
+configure {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+}
diff --git a/tooling/project-template/src/template/resources/settings.gradle.kts b/tooling/project-template/src/template/resources/settings.gradle.kts
new file mode 100644
index 0000000000..edbb06527f
--- /dev/null
+++ b/tooling/project-template/src/template/resources/settings.gradle.kts
@@ -0,0 +1,32 @@
+/*
+ * 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
+ */
+rootProject.name = "my-hibernate-project"
+
+// ###################################################################################
+// A lot of magic to be able to consumer SNAPSHOT versions of the Hibernate ORM plugin...
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ maven {
+ name = "jboss-snapshots-repository"
+ url = uri( "https://repository.jboss.org/nexus/content/repositories/snapshots" )
+ }
+ }
+
+ resolutionStrategy {
+ eachPlugin {
+ if ( requested.id.namespace == "org.hibernate"
+ && requested.id.name == "orm"
+ && requested.version.orEmpty().endsWith("-SNAPSHOT" ) ) {
+ val notation = "org.hibernate.orm:hibernate-gradle-plugin:${requested.version}"
+ logger.lifecycle( "Swapping SNAPSHOT version of plugin : {}", notation )
+ useModule( notation )
+ }
+ }
+ }
+}
+// ###################################################################################
\ No newline at end of file
diff --git a/tooling/project-template/src/template/resources/src/main/java/org/your/domain/SimpleEntity.java b/tooling/project-template/src/template/resources/src/main/java/org/your/domain/SimpleEntity.java
new file mode 100644
index 0000000000..f97493db5c
--- /dev/null
+++ b/tooling/project-template/src/template/resources/src/main/java/org/your/domain/SimpleEntity.java
@@ -0,0 +1,38 @@
+package org.your.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * A simple example entity
+ */
+@Entity
+public class SimpleEntity {
+ @Id
+ private Integer id;
+ private String name;
+
+ private SimpleEntity() {
+ }
+
+ public SimpleEntity(Integer id) {
+ this.id = id;
+ }
+
+ public SimpleEntity(Integer id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/tooling/project-template/src/template/resources/src/test/java/org/your/domain/EntityTests.java b/tooling/project-template/src/template/resources/src/test/java/org/your/domain/EntityTests.java
new file mode 100644
index 0000000000..5c038859be
--- /dev/null
+++ b/tooling/project-template/src/template/resources/src/test/java/org/your/domain/EntityTests.java
@@ -0,0 +1,53 @@
+package org.your.domain;
+
+import org.hibernate.testing.orm.junit.DomainModel;
+import org.hibernate.testing.orm.junit.ServiceRegistry;
+import org.hibernate.testing.orm.junit.SessionFactory;
+import org.hibernate.testing.orm.junit.SessionFactoryScope;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static com.arjuna.ats.internal.jdbc.recovery.JDBCXARecovery.PASSWORD;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hibernate.cfg.AvailableSettings.URL;
+import static org.hibernate.cfg.AvailableSettings.USER;
+
+/**
+ * Tests for SimpleEntity
+ */
+@ServiceRegistry(
+ settings = {
+ // can define settings here, or in `hibernate.properties` file
+ @ServiceRegistry.Setting( name = URL, value = "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000" ),
+ @ServiceRegistry.Setting( name = USER, value = "sa" ),
+ @ServiceRegistry.Setting( name = PASSWORD, value = "" )
+ }
+)
+@DomainModel( annotatedClasses = SimpleEntity.class )
+@SessionFactory()
+public class EntityTests {
+ @Test
+ public void basicTest(SessionFactoryScope scope) {
+ scope.inTransaction(
+ session -> {
+ final SimpleEntity entity = session.createQuery( "from SimpleEntity", SimpleEntity.class ).uniqueResult();
+ assertThat( entity, notNullValue() );
+ }
+ );
+ }
+
+ @BeforeEach
+ public void createTestData(SessionFactoryScope scope) {
+ scope.inTransaction(
+ session -> session.persist( new SimpleEntity( 1, "the first" ) )
+ );
+ }
+
+ @BeforeEach
+ public void dropTestData(SessionFactoryScope scope) {
+ scope.inTransaction(
+ session -> session.createQuery( "delete SimpleEntity" ).executeUpdate()
+ );
+ }
+}
diff --git a/tooling/project-template/src/template/resources/src/test/resources/log4j.properties b/tooling/project-template/src/template/resources/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..9863d25fbe
--- /dev/null
+++ b/tooling/project-template/src/template/resources/src/test/resources/log4j.properties
@@ -0,0 +1,7 @@
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+log4j.rootLogger=warn, stdout
+log4j.logger.org.hibernate=info
\ No newline at end of file