diff --git a/changelog.txt b/changelog.txt index a8e25523b7..21df231fa2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,90 @@ Hibernate 5 Changelog Note: Please refer to JIRA to learn more about each issue. +Changes in 5.4.5.Final (September 17, 2019) +------------------------------------------------------------------------------------------------------------------------ + +https://hibernate.atlassian.net/projects/HHH/versions/31779/tab/release-report-done + +** Bug + * [HHH-13259] - StackOverflowError from StringHelper + * [HHH-13466] - ClassCastException when changing a Collection association to a Set if @PreUpdate listener exists + * [HHH-13544] - Restore logged warning on jdbc code mapping issue in NationalizedTypeMappings + * [HHH-13550] - Fix Oracle failure for test added by HHH-13424 + * [HHH-13551] - StrategyRegistrationProvider does not properly handle implementations from different classloader + * [HHH-13554] - QueryAndSQLTest.testNativeQueryWithFormulaAttributeWithoutAlias() fails on Oracle, MSSQL, Sybase, DB2, MariaDB + * [HHH-13555] - FetchGraphTest, MergeProxyTest and ProxyDeletionTest fail due to ConstraintViolationException + * [HHH-13556] - Tests doing dynamic fetch scrolling a collection fail on DB2 + * [HHH-13557] - LocalTimeTest#writeThenNativeRead and OffsetTimeTest#writeThenNativeRead tests are failing on SQL Server + * [HHH-13558] - InstantTest, LocalDateTimeTest, OffsetDateTimeTest, ZonedDateTimeTest failing on Sybase for year 1600 + * [HHH-13564] - Envers - Getting NPE while reading revisions of entity with @EmbeddedId composite key located in parent @MappedSuperclass + * [HHH-13569] - org.hibernate.test.annotations.embedded.EmbeddedTest failures on Sybase + * [HHH-13570] - Test failures due to Sybase not supporting UPDATE statement with WITH(NOWAIT) + * [HHH-13571] - Test failures due to cross joined table out of scope of a subsequent JOIN on Sybase + * [HHH-13573] - Test failure due to Sybase not supporting cascade delete on foreign key definitions + * [HHH-13574] - SybaseASE does not support PARTITION BY + * [HHH-13577] - LockTest.testContendedPessimisticLock and StatementIsClosedAfterALockExceptionTest.testStatementIsClosed tests fail on Sybase + * [HHH-13580] - LocalTimeTest#writeThenNativeRead* and OffsetTimeTest#writeThenNativeRead* failing on MySQL + * [HHH-13581] - LocalTimeTest#writeThenRead* and OffsetTimeTest#writeThenRead* failing on MariaDB + * [HHH-13582] - LocalDateTest failures on MySQL + * [HHH-13586] - ClassCastException when using a single region name for both entity and query results + * [HHH-13590] - TransientObjectException merging a non-proxy association to a HibernateProxy + * [HHH-13592] - AutoFlushEvent#isFlushRequired is always false + * [HHH-13607] - Exception thrown while flushing uninitialized enhanced proxy with immutable natural ID + * [HHH-13611] - Restore EntityMetamodel constructor to take SessionFactoryImplementor argument instead of PersisterCreationContext. + * [HHH-13616] - Enable the hibernate-orm-modules test for JDK 11 + * [HHH-13621] - Exception if spaces after value of javax.persistence.schema-generation.scripts.action in hibernate.properties + +** New Feature + * [HHH-13249] - Introduce an option to Log slow queries instead of all queries + +** Task + * [HHH-13525] - Make test SessionDelegatorBaseImplTest more resilient to previously existing alias definition + * [HHH-13526] - Optimise ResourceRegistryStandardImpl#release + * [HHH-13527] - Performance regression in org.hibernate.stat.internal.StatisticsImpl + * [HHH-13528] - Invoke afterStatements only at the end of releasing all statements for a batch + * [HHH-13529] - Performance regression in org.hibernate.engine.spi.SessionFactoryImplementor#getDialect + * [HHH-13531] - Some more opportunities to reuse the constants pool in AliasConstantsHelper + * [HHH-13534] - AbstractLoadPlanBasedLoader never needs a List of AfterLoadAction + * [HHH-13546] - Make the sessionFactory field in StatisticsImpl required + * [HHH-13549] - Cleanup dead code in StringHelper + * [HHH-13552] - CollectionType needs a direct reference to its Persister + * [HHH-13553] - Fix test failures on SAP HANA + * [HHH-13561] - Do not retrieve the same ActionQueue multiple times + * [HHH-13562] - List of TransactionObserver for JdbcResourceLocalTransactionCoordinatorImpl should be lazily initialized + * [HHH-13563] - ResultSetReturnImpl is looking up JdbcServices on each construction + * [HHH-13565] - Improve Session opening efficiency + * [HHH-13568] - Instances of NaturalIdXrefDelegate should be lazily initialized if possible + * [HHH-13605] - InstantTest, OffsetDateTimeTest, ZonedDateTimeTest fail for MariaDB on CI + * [HHH-13606] - LocalDateTimeTest fails for HANA on CI + * [HHH-13622] - Upgrade the WildFly Transaction Client to 1.1.7.Final + +** Improvement + * [HHH-13133] - Print message about 'successfully enhanced class' as debug in Maven enhancement plugin + * [HHH-13412] - Move hibernate.connection description out of c3p0 section + * [HHH-13512] - Avoid allocating an array in org.hibernate.internal.util.StringHelper#unquote(String[], Dialect) if there are no changes to be applied + * [HHH-13521] - Avoid excessive validation of enabled filters + * [HHH-13522] - Optimise LoadQueryInfluencers by making maps lazily initialized + * [HHH-13523] - StatementPreparerImpl should not need to retrieve the JDBCService as often + * [HHH-13524] - Remove unused fields xref,unassociatedResultSets from JdbcCoordinatorImpl + * [HHH-13541] - ExceptionConverter instance in AbstractSharedSessionContract should be lazily initialized + * [HHH-13548] - Since SessionOwner is deprecated several fields in SessionImpl can be removed + * [HHH-13576] - Invoking tracef() or debugf() w/o an array of parameters actually allocates an empty Object[] + * [HHH-13579] - Cleanup of resources in ResourceRegistryStandardImpl allocates many Iterators + * [HHH-13584] - Reduce ServiceRegistry lookups in LocalConnectionAccess in SessionFactory + * [HHH-13585] - Duplicate resource release in PessimisticReadSelectLockingStrategy + * [HHH-13587] - Initialize selected collections of StatefulPersistenceContext lazily + * [HHH-13588] - MySQL Dialect: missed functions: weight_string, to_base64, from_base64, regexp_replace, regexp_instr, regexp_substr + * [HHH-13589] - Minor memory allocation improvements in ActionQueue + * [HHH-13591] - Replaces simple uses of array iteration with a corresponding for-each loop + * [HHH-13594] - ResourceRegistryStandardImpl#release could avoid allocating a capturing lambda + * [HHH-13599] - Avoid ArrayList allocation in JtaTransactionCoordinatorImp in common scenario + * [HHH-13600] - Avoid allocation of capturing lambdas in ParameterTranslationsImpl and AbstractDomainDataRegion + +** Deprecation + * [HHH-13595] - Deprecate ConnectionObserver + + Changes in 5.4.4.Final (July 29, 2019) ------------------------------------------------------------------------------------------------------------------------ diff --git a/databases/hana/matrix.gradle b/databases/hana/matrix.gradle index 8782b540d7..59d7bc269e 100644 --- a/databases/hana/matrix.gradle +++ b/databases/hana/matrix.gradle @@ -5,4 +5,4 @@ * See the lgpl.txt file in the root directory or . */ -jdbcDependency 'com.sap.cloud.db.jdbc:ngdbc:2.2.16' \ No newline at end of file +jdbcDependency 'com.sap.cloud.db.jdbc:ngdbc:2.4.59' \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/bytecode/BytecodeEnhancement.adoc b/documentation/src/main/asciidoc/userguide/chapters/bytecode/BytecodeEnhancement.adoc new file mode 100644 index 0000000000..f79afe18da --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/bytecode/BytecodeEnhancement.adoc @@ -0,0 +1,23 @@ +[[enhancement]] +== Enhancement +:sourcedir: ../../../../../test/java/org/hibernate/userguide/bytecode +:extrasdir: extras + +Hibernate offers a number of services that can be added into an application's domain model classes +through bytecode enhancement... + + +[[enhancement-laziness]] +=== Laziness + + +[[enhancement-bidir]] +=== Bi-directionality + + +[[enhancement-dirty]] +=== In-line dirty checking + + +[[enhancement-extended]] +=== Extended enhancement \ No newline at end of file diff --git a/gradle/libraries.gradle b/gradle/libraries.gradle index 719bf0f25e..d526809639 100644 --- a/gradle/libraries.gradle +++ b/gradle/libraries.gradle @@ -168,7 +168,7 @@ ext { wildfly_arquillian_container_managed: "org.wildfly.arquillian:wildfly-arquillian-container-managed:${wildflyArquillianContainerVersion}", jboss_vfs: "org.jboss:jboss-vfs:3.2.11.Final", jipijapa_spi: "org.wildfly:jipijapa-spi:${wildflyVersion}", - wildfly_transaction_client : 'org.wildfly.transaction:wildfly-transaction-client:1.0.3.Final', + wildfly_transaction_client : 'org.wildfly.transaction:wildfly-transaction-client:1.1.7.Final', jboss_ejb_spec_jar : 'org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Final', jboss_annotation_spec_jar : 'org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:1.0.0.Final', diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java index ee0e533be0..37bec4a5f7 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java @@ -74,7 +74,7 @@ public final class Settings { LOG.debugf( "Check Nullability in Core (should be disabled when Bean Validation is on): %s", enabledDisabled( sessionFactoryOptions.isCheckNullability() ) ); LOG.debugf( "Allow initialization of lazy state outside session : %s", enabledDisabled( sessionFactoryOptions.isInitializeLazyStateOutsideTransactionsEnabled() ) ); - LOG.debugf( "Using BatchFetchStyle : %", sessionFactoryOptions.getBatchFetchStyle().name() ); + LOG.debugf( "Using BatchFetchStyle : %s", sessionFactoryOptions.getBatchFetchStyle().name() ); LOG.debugf( "Default batch fetch size: %s", sessionFactoryOptions.getDefaultBatchFetchSize() ); LOG.debugf( "Maximum outer join fetch depth: %s", sessionFactoryOptions.getMaximumFetchDepth() ); LOG.debugf( "Default null ordering: %s", sessionFactoryOptions.getDefaultNullPrecedence() ); diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java index d1a3e9b4c7..cb7ed8d2a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java @@ -109,7 +109,7 @@ public enum Action { return (Action) value; } - final String name = value.toString(); + final String name = value.toString().trim(); if ( name.isEmpty() || NONE.externalJpaName.equals( name ) ) { // default is NONE return NONE; diff --git a/hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.java b/hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.java new file mode 100644 index 0000000000..70b35aaacb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/tool/schema/SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest.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.test.tool.schema; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl; +import org.hibernate.cfg.Configuration; +import org.hibernate.internal.util.config.ConfigurationHelper; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.fail; + +/** + * @author Andrea Boriero + */ +@TestForIssue(jiraKey = "HHH-13621") +public class SchemaGenetationSciptsActionPropertyValueEndingWithSpaceTest extends BaseCoreFunctionalTestCase { + + private File dropOutput; + private File createOutput; + + @Override + protected StandardServiceRegistryImpl buildServiceRegistry( + BootstrapServiceRegistry bootRegistry, + Configuration configuration) { + try { + dropOutput = File.createTempFile( "drop_script", ".sql" ); + createOutput = File.createTempFile( "create_script", ".sql" ); + dropOutput.deleteOnExit(); + createOutput.deleteOnExit(); + } + catch (IOException e) { + fail( "unable to create temp file" + e ); + } + Properties properties = new Properties(); + properties.putAll( configuration.getProperties() ); + // the value of the property ends with a space + properties.setProperty( "javax.persistence.schema-generation.scripts.action", "drop-and-create " ); + properties.setProperty( + "javax.persistence.schema-generation.scripts.create-target", + createOutput.getAbsolutePath() + ); + properties.setProperty( + "javax.persistence.schema-generation.scripts.drop-target", + dropOutput.getAbsolutePath() + ); + ConfigurationHelper.resolvePlaceHolders( properties ); + + StandardServiceRegistryBuilder cfgRegistryBuilder = configuration.getStandardServiceRegistryBuilder(); + + StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder( + bootRegistry, + cfgRegistryBuilder.getAggregatedCfgXml() + ).applySettings( properties ); + + prepareBasicRegistryBuilder( registryBuilder ); + return (StandardServiceRegistryImpl) registryBuilder.build(); + } + + @Test + public void testValueEndingWithSpaceDoesNotCauseExceptionDuringBootstrap() { + } +} 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 new file mode 100644 index 0000000000..f1e624ce72 --- /dev/null +++ b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhanceTask.groovy @@ -0,0 +1,33 @@ +/* + * 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.SourceSet +import org.gradle.api.tasks.TaskAction +import org.gradle.util.ConfigureUtil + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("unused") +class EnhanceTask extends DefaultTask { + EnhanceExtension options + 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 new file mode 100644 index 0000000000..6b5023edc4 --- /dev/null +++ b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/EnhancementHelper.java @@ -0,0 +1,174 @@ +/* + * 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(); + } + }; + + 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/HibernatePlugin.java b/tooling/hibernate-gradle-plugin/src/main/groovy/org/hibernate/orm/tooling/gradle/HibernatePlugin.java index e4a7f0d3f6..8265adb302 100644 --- 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 @@ -55,12 +55,9 @@ public class HibernatePlugin implements Plugin { project.getExtensions().add( "hibernate", hibernateExtension ); project.afterEvaluate( - new Action() { - @Override - public void execute(Project project) { - if ( hibernateExtension.enhance != null ) { - applyEnhancement( project, hibernateExtension ); - } + project1 -> { + if ( hibernateExtension.enhance != null ) { + applyEnhancement( project1, hibernateExtension ); } } ); @@ -76,150 +73,11 @@ public class HibernatePlugin implements Plugin { 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 Action() { - @Override - public void execute(Task task) { - project.getLogger().debug( "Starting Hibernate enhancement on SourceSet.{}", sourceSet.getName() ); - - final ClassLoader classLoader = toClassLoader( sourceSet.getRuntimeClasspath() ); - - EnhancementContext enhancementContext = new DefaultEnhancementContext() { - @Override - public ClassLoader getLoadingClassLoader() { - return classLoader; - } - - @Override - public boolean doBiDirectionalAssociationManagement(UnloadedField field) { - return hibernateExtension.enhance.getEnableAssociationManagement(); - } - - @Override - public boolean doDirtyCheckingInline(UnloadedClass classDescriptor) { - return hibernateExtension.enhance.getEnableDirtyTracking(); - } - - @Override - public boolean hasLazyLoadableAttributes(UnloadedClass classDescriptor) { - return hibernateExtension.enhance.getEnableLazyInitialization(); - } - - @Override - public boolean isLazyLoadable(UnloadedField field) { - return hibernateExtension.enhance.getEnableLazyInitialization(); - } - - @Override - public boolean doExtendedEnhancement(UnloadedClass classDescriptor) { - return hibernateExtension.enhance.getEnableExtendedEnhancement(); - } - }; - - if ( hibernateExtension.enhance.getEnableExtendedEnhancement() ) { - logger.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 ); - logger.info( "Successfully enhanced class [" + file + "]" ); - } - else { - logger.info( "Skipping class [" + file.getAbsolutePath() + "], not an entity nor embeddable" ); - } - } - } - } - } + task -> EnhancementHelper.enhance( sourceSet, hibernateExtension.enhance, project ) ); } } - private ClassLoader toClassLoader(FileCollection runtimeClasspath) { - List urls = new ArrayList(); - for ( File file : runtimeClasspath ) { - try { - urls.add( file.toURI().toURL() ); - logger.debug( "Adding classpath entry for " + file.getAbsolutePath() ); - } - catch (MalformedURLException e) { - throw new GradleException( "Unable to resolve classpath entry to URL : " + file.getAbsolutePath(), e ); - } - } - return new URLClassLoader( urls.toArray( new URL[urls.size()] ), Enhancer.class.getClassLoader() ); - } - - private byte[] doEnhancement(File root, File javaClassFile, Enhancer enhancer) { - try { - String className = javaClassFile.getAbsolutePath().substring( - root.getAbsolutePath().length() + 1, - javaClassFile.getAbsolutePath().length() - ".class".length() - ).replace( File.separatorChar, '.' ); - ByteArrayOutputStream originalBytes = new ByteArrayOutputStream(); - FileInputStream fileInputStream = new FileInputStream( javaClassFile ); - try { - byte[] buffer = new byte[1024]; - int length; - while ( ( length = fileInputStream.read( buffer ) ) != -1 ) { - originalBytes.write( buffer, 0, length ); - } - } - finally { - fileInputStream.close(); - } - return enhancer.enhance( className, originalBytes.toByteArray() ); - } - catch (Exception e) { - throw new GradleException( "Unable to enhance class : " + javaClassFile, e ); - } - } - - private void writeOutEnhancedClass(byte[] enhancedBytecode, File file) { - 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 ); - } - - } - } 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 index d2eedd8562..f2e57885c5 100644 --- 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 @@ -6,6 +6,10 @@ */ 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 @@ -38,4 +42,46 @@ class HibernatePluginTest { 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 + ) + } + + compileTestTask.execute() + } }