/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2011, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.gradle.testing.database; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import groovy.lang.Closure; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; import org.hibernate.gradle.testing.matrix.AbstractMatrixNode; import org.hibernate.gradle.testing.matrix.MatrixNode; import org.hibernate.gradle.testing.matrix.MatrixNodeProvider; import org.hibernate.gradle.util.DuplicatedDBConfigException; import org.hibernate.gradle.util.FileUtil; import org.hibernate.gradle.util.Jdk; /** * TODO : javadoc * * @author Steve Ebersole * @author Strong Liu */ public class DatabaseMatrixPlugin implements Plugin<Project>, MatrixNodeProvider { private static final Logger log = Logging.getLogger( DatabaseMatrixPlugin.class ); public static final String DEFAULT_DATABASE_DIRECTORY = "databases"; public static final String MATRIX_DATABASES_LOCATION = "hibernate-matrix-databases"; public static final String HIBERNATE_MATRIX_IGNORE = "hibernate-matrix-ignore"; private static final String MATRIX_NODE_CONVENTION_KEY = "matrixNode"; private static final String MATRIX_BUILD_FILE = "matrix.gradle"; private Project project; private Map<String, MatrixNode> matrixNodeMap = new HashMap(); public void apply(Project project) { this.project = project; applyDatabaseDirectories( getRootProject( project ).file( DEFAULT_DATABASE_DIRECTORY ) , true); String databasesDirPath = System.getProperty( MATRIX_DATABASES_LOCATION ); if ( databasesDirPath != null ) { File databaseDir = new File( databasesDirPath ); applyDatabaseDirectories( databaseDir, false ); } } private Project getRootProject(Project project) { return project.getParent() != null ? getRootProject( project.getParent() ) : project; } private void applyDatabaseDirectories(File databasesBaseDirObject, boolean isDefault) { if ( !FileUtil.isDirectory( databasesBaseDirObject ) ) { log.warn( "Giving DB profile location [" + databasesBaseDirObject + "] is not a valid directory, ignored." ); return; } log.debug( "Applying database directory: " + databasesBaseDirObject ); for ( File entry : databasesBaseDirObject.listFiles() ) { if ( FileUtil.isDirectory(entry) ) { applyPossibleDatabaseDirectory( entry, isDefault ); } } } private boolean ignoreDefault(String databaseName) { String value = System.getProperty( HIBERNATE_MATRIX_IGNORE ); return ( value != null && ( value.equals( "all" ) || value.contains( databaseName ) ) ); } private void applyPossibleDatabaseDirectory(final File databaseDir, final boolean isDefault) { final String databaseName = databaseDir.getName(); if ( isDefault && ignoreDefault( databaseName ) ) { log.debug( "Ignore default DB profile [{}]", databaseName ); return; } log.debug( "Checking potential database directory : {}", databaseName ); MatrixNode node = null; // 3 types of support here: // 1) directory contains a file named 'matrix.gradle' // 2) directory contains a file named 'ivy.xml' (we prefer matrix.gradle --stliu) // 3) directory contains a sub-directory named 'jdbc' containing the driver artifacts final File matrixFile = new File( databaseDir, MATRIX_BUILD_FILE ); if ( FileUtil.isFile( matrixFile ) ) { // (1) we found the 'matrix.gradle' file node = prepareFromGradleFile( matrixFile ); } else { // final File ivyXmlFile = new File( databaseDir, IVY_XML_FILE ); // if ( FileUtil.isFile( ivyXmlFile ) ) { // // (2) we found the 'ivy.xml' file // node = prepareFromIvyXmlFile( ivyXmlFile ); // } // else { final File jdbcDir = new File( databaseDir, "jdbc" ); if ( FileUtil.isDirectory( jdbcDir ) ) { node = prepareFromJdbcDir( jdbcDir ); } // } } if ( node == null ) { log.info( "Doesn't found valid Matrix database configuration file in directory : {}", databaseDir ); return; } final File propertiesFile = new File( new File( databaseDir, "resources" ), "hibernate.properties" ); if ( FileUtil.isFile( propertiesFile ) ) { node.setHibernatePropertyFile( propertiesFile ); } else { log.warn( "No 'hibernate.properties' found in {}/resources", databaseDir ); } log.debug( "Adding node[{}] " + node.getName() ); if ( matrixNodeMap.containsKey( node.getName() ) ) { throw new DuplicatedDBConfigException( "There is already a Matrix node named " + node.getName() ); } else { matrixNodeMap.put( node.getName(), node ); } } private MatrixNode prepareFromGradleFile(File matrixFile) { log.debug( "Found matrix file : " + matrixFile ); MatrixDotGradleMatrixNodeImpl matrixNode = new MatrixDotGradleMatrixNodeImpl( matrixFile.getParentFile() .getName() ); MatrixDotGradleMatrixNodeConvention convention = new MatrixDotGradleMatrixNodeConvention( matrixNode ); project.getConvention().getPlugins().put( MATRIX_NODE_CONVENTION_KEY, convention ); try { project.apply( Collections.singletonMap( "from", matrixFile ) ); } finally { project.getConvention().getPlugins().remove( MATRIX_NODE_CONVENTION_KEY ); } return matrixNode; } /** * {@link MatrixNode} implementation for handling 'matrix.gradle' files */ private class MatrixDotGradleMatrixNodeImpl extends AbstractMatrixNode { private final Configuration jdbcDependencies; private Jdk jdk; public MatrixDotGradleMatrixNodeImpl(String name) { super( project, name ); this.jdbcDependencies = prepareConfiguration( name ); this.jdk = getDefaultJdk(); } public Jdk getTestingRuntimeJdk() { return jdk; } @Override public DependencyResolver getDependencyResolver() { return new DependencyResolver() { @Override public Project getProject() { return project; } @Override public Configuration resolve() { return jdbcDependencies; } }; } } /** * Provides simplified convention object to the database-specific script for convenient configuration. */ private class MatrixDotGradleMatrixNodeConvention { private final MatrixDotGradleMatrixNodeImpl matrixNode; private MatrixDotGradleMatrixNodeConvention(MatrixDotGradleMatrixNodeImpl matrixNode) { this.matrixNode = matrixNode; } public void jdbcDependency(Object dependencyNotation, Closure closure) { project.getDependencies().add( matrixNode.jdbcDependencies.getName(), dependencyNotation, closure ); } public void jdbcDependency(Object dependencyNotation) { log.debug( "Adding JDBC dependency[{}] resolved from matrix.gradle", matrixNode.jdbcDependencies.getName() ); project.getDependencies().add( matrixNode.jdbcDependencies.getName(), dependencyNotation ); } public void jdk(Jdk jdk) { matrixNode.jdk = jdk; } } private MatrixNode prepareFromJdbcDir(File jdbcDir) { log.debug( "Found local jdbc dir : " + jdbcDir ); return new LocalMatrixNode( jdbcDir ); } private class LocalMatrixNode extends AbstractMatrixNode { private final Jdk jdk = getDefaultJdk(); private final LocalJdbcDependencyResolver resolver; private LocalMatrixNode(File jdbcDir) { super( project, jdbcDir.getParentFile().getName() ); resolver = new LocalJdbcDependencyResolver( project, jdbcDir.getParentFile() ); } public Jdk getTestingRuntimeJdk() { return jdk; } @Override public DependencyResolver getDependencyResolver() { return resolver; } } public List<MatrixNode> getMatrixNodes() { return Collections.unmodifiableList( new ArrayList<MatrixNode>( matrixNodeMap.values() ) ); } private Configuration prepareConfiguration(String name) { Configuration configuration = getOrCreateConfiguration( name ); configuration.setDescription( "The [" + name + "] JDBC dependency configuration" ); return configuration; } private Configuration getOrCreateConfiguration(String configurationName) { Configuration configuration = project.getConfigurations().findByName( configurationName ); if ( configuration == null ) { configuration = project.getConfigurations().add( configurationName ); } return configuration; } private Jdk getDefaultJdk() { return new Jdk(); } }