Add a Dialect report

Also added
* H2Dialect#getMinimumSupportedVersion
* DerbyDialect#getMinimumSupportedVersion
This commit is contained in:
Steve Ebersole 2023-04-27 11:27:26 -05:00
parent 3ae07666ab
commit c6389efb1b
6 changed files with 356 additions and 1 deletions

View File

@ -267,6 +267,33 @@ tasks.register('renderLoggingReport', AsciidoctorTask) { task ->
}
}
tasks.register('renderDialectReport', AsciidoctorTask) { task ->
group 'Documentation'
description = 'Renders the ORM Dialect report in HTML format using Asciidoctor.'
dependsOn tasks.generateDialectReport
tasks.renderOrmReports.dependsOn task
inputs.property "version", project.ormVersion
sourceDir = layout.buildDirectory.dir('orm/reports')
sources {
include 'dialect.adoc'
}
outputDir = project.layout.buildDirectory.dir('asciidoc/dialect')
attributes linkcss: true,
stylesheet: "css/hibernate.css"
resources {
from('src/main/style/asciidoctor') {
include 'images/**'
}
from('src/main/style/asciidoctor') {
include 'css/**'
}
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -130,6 +130,11 @@ public class DerbyDialect extends Dialect {
super(info);
}
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
@Override
protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {

View File

@ -74,7 +74,6 @@ import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampWithTimeZoneJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
@ -139,6 +138,9 @@ public class H2Dialect extends Dialect {
this( MINIMUM_VERSION );
}
public static void main(String[] args) {
System.out.println( new H2Dialect().getMinimumSupportedVersion() );
}
public H2Dialect(DatabaseVersion version) {
super(version);
@ -177,6 +179,11 @@ public class H2Dialect extends Dialect {
return bits.length > 2 ? Integer.parseInt( bits[2] ) : 0;
}
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
@Override
public boolean getDefaultNonContextualLobCreation() {
// http://code.google.com/p/h2database/issues/detail?id=235

View File

@ -0,0 +1,261 @@
/*
* 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.post;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import org.gradle.api.Project;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskAction;
import org.hibernate.orm.env.HibernateVersion;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.Index;
/**
* Generates a report on Dialect information
*
* @author Steve Ebersole
*/
public abstract class DialectReportTask extends AbstractJandexAwareTask {
@Inject
public DialectReportTask(IndexManager indexManager, Project project) {
super(
indexManager,
project.getLayout().getBuildDirectory().file( "orm/reports/dialect.adoc" )
);
}
@TaskAction
public void generateDialectReport() {
// the ones we want are all in the hibernate-core project
final Project coreProject = getProject().getRootProject().project( "hibernate-core" );
final SourceSetContainer sourceSets = coreProject.getExtensions().getByType( SourceSetContainer.class );
final SourceSet sourceSet = sourceSets.getByName( SourceSet.MAIN_SOURCE_SET_NAME );
final ClassLoader classLoader = Helper.asClassLoader( sourceSet, coreProject.getConfigurations().getByName( "testRuntimeClasspath" ) );
final DialectClassDelegate dialectClassDelegate = new DialectClassDelegate( classLoader );
final Index index = getIndexManager().getIndex();
final Collection<ClassInfo> allDialectClasses = index.getAllKnownSubclasses( DialectClassDelegate.DIALECT_CLASS_NAME );
if ( allDialectClasses.isEmpty() ) {
throw new RuntimeException( "Unable to find Dialects" );
}
final List<DialectDelegate> dialectDelegates = collectDialectInfo( dialectClassDelegate, allDialectClasses );
generateReport( dialectDelegates );
}
private List<DialectDelegate> collectDialectInfo(
DialectClassDelegate dialectClassDelegate,
Collection<ClassInfo> allDialectClasses) {
final List<DialectDelegate> results = new ArrayList<>();
allDialectClasses.forEach( (dialectImplClassInfo) -> {
final String dialectImplClassName = dialectImplClassInfo.name().toString();
final DialectDelegate dialectDelegate = dialectClassDelegate.createDialectDelegate( dialectImplClassName );
if ( dialectDelegate == null ) {
return;
}
results.add( dialectDelegate );
} );
results.sort( Comparator.comparing( DialectDelegate::getSimpleName ) );
return results;
}
private void generateReport(List<DialectDelegate> dialectDelegates) {
final File reportFile = prepareReportFile();
try ( final OutputStreamWriter fileWriter = new OutputStreamWriter( new FileOutputStream( reportFile ) ) ) {
writeDialectReport( dialectDelegates, fileWriter );
}
catch (FileNotFoundException e) {
throw new RuntimeException( "Should never happen" );
}
catch (IOException e) {
throw new RuntimeException( "Error writing to report file", e );
}
}
private void writeDialectReport(
List<DialectDelegate> dialectDelegates,
OutputStreamWriter fileWriter) {
writeDialectReportHeader( fileWriter );
dialectDelegates.forEach( (dialectDelegate) -> writeDialectReportEntry( dialectDelegate, fileWriter ) );
writeDialectReportFooter( fileWriter );
}
private void writeDialectReportHeader(OutputStreamWriter fileWriter) {
try {
fileWriter.write( "= Supported Dialects\n\n" );
fileWriter.write( "Supported Dialects along with the minimum supported version of the underlying database.\n\n\n" );
HibernateVersion ormVersion = (HibernateVersion) getProject().getRootProject().getExtensions().getByName( "ormVersion" );
fileWriter.write( "NOTE: Hibernate version " + ormVersion.getFamily() + "\n\n" );
fileWriter.write( "[cols=\"a,a\", options=\"header\"]\n" );
fileWriter.write( "|===\n" );
fileWriter.write( "|Dialect |Minimum Database Version\n" );
}
catch (Exception e) {
throw new RuntimeException( "Error writing report header", e );
}
}
private void writeDialectReportEntry(DialectDelegate dialectDelegate, OutputStreamWriter fileWriter) {
try {
final String version = dialectDelegate.getMinimumVersion();
fileWriter.write( '|' );
fileWriter.write( dialectDelegate.getDialectImplClass().getSimpleName() );
fileWriter.write( '|' );
fileWriter.write( version );
fileWriter.write( "\n" );
}
catch (Exception e) {
throw new RuntimeException( "Unable to access Dialect : " + dialectDelegate.getDialectReference(), e );
}
}
private void writeDialectReportFooter(OutputStreamWriter fileWriter) {
try {
fileWriter.write( "|===\n" );
}
catch (Exception e) {
throw new RuntimeException( "Error writing report footer", e );
}
}
private class DialectClassDelegate {
public static final String DIALECT_CLASS_NAME = "org.hibernate.dialect.Dialect";
public static final String MIN_VERSION_METHOD_NAME = "getMinimumSupportedVersion";
private final Class<?> loadedDialectClass;
private final Method versionMethod;
private final ClassLoader classLoader;
public DialectClassDelegate(ClassLoader classLoader) {
this.classLoader = classLoader;
try {
loadedDialectClass = classLoader.loadClass( DIALECT_CLASS_NAME );
versionMethod = loadedDialectClass.getDeclaredMethod( MIN_VERSION_METHOD_NAME );
versionMethod.setAccessible( true );
}
catch (ClassNotFoundException e) {
throw new RuntimeException( "Could not load " + DIALECT_CLASS_NAME, e );
}
catch (NoSuchMethodException e) {
throw new RuntimeException( "Could not locate method " + MIN_VERSION_METHOD_NAME, e );
}
}
public DialectDelegate createDialectDelegate(String dialectImplClassName) {
if ( dialectImplClassName.endsWith( "DialectDelegateWrapper" ) ) {
return null;
}
try {
final Class<?> dialectImplClass;
try {
dialectImplClass = classLoader.loadClass( dialectImplClassName );
}
catch (Exception e) {
// assume it is from project other than hibernate-core
getLogger().debug( "Skipping Dialect " + dialectImplClassName + " - could not instantiate", e );
return null;
}
if ( Modifier.isAbstract( dialectImplClass.getModifiers() ) ) {
getLogger().debug( "Skipping Dialect " + dialectImplClassName + " - abstract" );
return null;
}
if ( dialectImplClass.isAnnotationPresent( Deprecated.class ) ) {
getLogger().debug( "Skipping Dialect " + dialectImplClassName + " - deprecated" );
return null;
}
return DialectDelegate.from( dialectImplClass, this );
}
catch (Exception e) {
throw new RuntimeException( "Unable to access Dialect class : " + dialectImplClassName, e );
}
}
public Class<?> getLoadedDialectClass() {
return loadedDialectClass;
}
public Method getVersionMethod() {
return versionMethod;
}
}
private static class DialectDelegate {
private final Class<?> dialectImplClass;
private final DialectClassDelegate dialectClassDelegate;
private final Object dialectRef;
public static DialectDelegate from(Class<?> dialectImplClass, DialectClassDelegate dialectClassDelegate) {
return new DialectDelegate( dialectImplClass, dialectClassDelegate );
}
public DialectDelegate(Class<?> dialectImplClass, DialectClassDelegate dialectClassDelegate) {
this.dialectImplClass = dialectImplClass;
this.dialectClassDelegate = dialectClassDelegate;
try {
this.dialectRef = dialectImplClass.getConstructor().newInstance();
}
catch (Exception e) {
throw new RuntimeException( "Unable to create DialectDelegate for " + dialectImplClass.getName(), e );
}
}
public String getSimpleName() {
return dialectImplClass.getSimpleName();
}
public DialectClassDelegate getDialectClassDelegate() {
return dialectClassDelegate;
}
public Class<?> getDialectImplClass() {
return dialectImplClass;
}
public Object getDialectReference() {
return dialectRef;
}
public String getMinimumVersion() {
try {
final Object versionRef = dialectClassDelegate.getVersionMethod().invoke( dialectRef );
return versionRef.toString();
}
catch (Exception e) {
throw new RuntimeException( "Unable to access " + DialectClassDelegate.MIN_VERSION_METHOD_NAME + " for " + dialectClassDelegate.loadedDialectClass.getName(), e );
}
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.post;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.Directory;
import org.gradle.api.tasks.SourceSet;
/**
* @author Steve Ebersole
*/
public class Helper {
public static ClassLoader asClassLoader(SourceSet sourceSet, Configuration gradleClasspath) {
final List<URL> urls = new ArrayList<>();
final Directory classesDirectory = sourceSet.getJava().getClassesDirectory().get();
final File classesDir = classesDirectory.getAsFile();
addElement( urls, classesDir );
for ( File dependencyFile : gradleClasspath.resolve() ) {
addElement( urls, dependencyFile );
}
return new URLClassLoader( urls.toArray( new URL[0] ) );
}
private static void addElement(List<URL> urls, File element) {
try {
urls.add( element.toURI().toURL() );
}
catch (MalformedURLException e) {
throw new RuntimeException( "Unable to create URL for ClassLoader: " + element.getAbsolutePath(), e );
}
}
}

View File

@ -66,5 +66,13 @@ public class ReportGenerationPlugin implements Plugin<Project> {
);
loggingTask.dependsOn( indexerTask );
groupingTask.dependsOn( loggingTask );
final DialectReportTask dialectTask = project.getTasks().create(
"generateDialectReport",
DialectReportTask.class,
indexManager
);
dialectTask.dependsOn( indexerTask );
groupingTask.dependsOn( dialectTask );
}
}