From: Kai Moritz Date: Sun, 20 Jun 2010 18:17:07 +0000 (+0200) Subject: Implementd maven-plugin "Hibernate 4" X-Git-Tag: hibernate4-maven-plugin-1.0~16 X-Git-Url: http://juplo.de/gitweb/?p=hibernate4-maven-plugin;a=commitdiff_plain;h=bfa1870703b400d8664fa91b41d8186fb24162c2 Implementd maven-plugin "Hibernate 4" --- bfa1870703b400d8664fa91b41d8186fb24162c2 diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..3b8c444e --- /dev/null +++ b/pom.xml @@ -0,0 +1,134 @@ + + + + 4.0.0 + + de.juplo + hibernate4-maven-plugin + Hibernate 4 Maven Plugin + Plugin for generating a database-schema from Hibernate-4-Mapping-Annotations + 1.0-SNAPSHOT + maven-plugin + http://juplo.de/hibernate4-maven-plugin + + + 2.0.6 + + + + + kai + Kai Moritz + kai@juplo.de + + + + + + UTF-8 + + 4.1.5.SP1 + 3.0.4 + 1.0.1 + 1.0.2 + + + + + org.apache.maven + maven-core + ${maven.version} + + + org.codehaus.plexus + plexus-utils + + + + + org.apache.maven + maven-plugin-api + ${maven.version} + + + org.hibernate + hibernate-core + ${hibernate-core.version} + + + net.sf.scannotation + scannotation + ${scannotation.version} + + + com.pyx4j + maven-plugin-log4j + ${maven-plugin-log4j.version} + + + org.apache.maven + maven-artifact + + + org.apache.maven + maven-plugin-api + + + + + + + + juplo.internal + Internal Release Repository + http://juplo.de/archiva/repository/internal/ + + + juplo.snapshots + Internal Snapshot Repository + http://juplo.de/archiva/repository/snapshots/ + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + utf8 + true + + + + org.apache.maven.plugins + maven-dependency-plugin + + + install + install + + sources + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + verify + + jar + + + + + + + + diff --git a/src/main/java/de/juplo/plugins/hibernate4/Hbm2DdlMojo.java b/src/main/java/de/juplo/plugins/hibernate4/Hbm2DdlMojo.java new file mode 100644 index 00000000..f2fd2320 --- /dev/null +++ b/src/main/java/de/juplo/plugins/hibernate4/Hbm2DdlMojo.java @@ -0,0 +1,433 @@ +package de.juplo.plugins.hibernate4; + +/* + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.pyx4j.log4j.MavenLogAppender; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import javax.persistence.Entity; +import javax.persistence.Embeddable; +import javax.persistence.MappedSuperclass; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.project.MavenProject; +import org.hibernate.cfg.Configuration; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.hibernate.tool.hbm2ddl.SchemaExport.Type; +import org.hibernate.tool.hbm2ddl.Target; +import org.scannotation.AnnotationDB; + + +/** + * Goal which extracts the hibernate-mapping-configuration and + * exports an according SQL-database-schema. + * + * @goal export + * @phase process-classes + * @threadSafe + * @requiresDependencyResolution runtime + */ +public class Hbm2DdlMojo extends AbstractMojo +{ + /** + * The project whose project files to create. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + private MavenProject project; + + /** + * Directories to scan. + * + * @parameter expression="${project.build.outputDirectory}" + */ + private String outputDirectory; + + /** + * Skip execution + * + * @parameter default-value="false" + */ + private boolean skip; + + /** + * SQL-Driver name. + * + * @parameter expression="${hibernate.connection.driver_class} + */ + private String driverClassName; + + /** + * Database URL. + * + * @parameter expression="${hibernate.connection.url}" + */ + private String url; + + /** + * Database username + * + * @parameter expression="${hibernate.connection.username}" + */ + private String username; + + /** + * Database password + * + * @parameter expression="${hibernate.connection.password}" + */ + private String password; + + /** + * Hibernate dialect. + * + * @parameter expression="${hibernate.dialect}" + */ + private String hibernateDialect; + + /** + * Hibernate configuration file. + * + * @parameter default-value="${project.build.outputDirectory}/hibernate.properties" + */ + private String hibernateProperties; + + /** + * Target of execution: + * + * @parameter default-value="EXPORT" + */ + private String target; + + /** + * Type of export. + * + * @parameter default-value="BOTH" + */ + private String type; + + /** + * Output file. + * + * @parameter default-value="${project.build.outputDirectory}/schema.sql" + */ + private String outputFile; + + /** + * Delimiter in output-file. + * + * @parameter default-value=";" + */ + private String delimiter; + + /** + * Format output-file. + * + * @parameter default-value="true" + */ + private boolean format; + + + @Override + public void execute() + throws + MojoFailureException, + MojoExecutionException + { + if (skip) + return; + + File dir = new File(outputDirectory); + if (!dir.exists()) + throw new MojoExecutionException("Cannot scan for annotated classes in " + outputDirectory + ": directory does not exist!"); + + + Set classes = new HashSet(); + URL dirUrl = null; + try { + AnnotationDB db = new AnnotationDB(); + getLog().info("Scanning directory " + outputDirectory + " for annotated classes..."); + dirUrl = dir.toURI().toURL(); + db.scanArchives(dirUrl); + if (db.getAnnotationIndex().containsKey(Entity.class.getName())) + classes.addAll(db.getAnnotationIndex().get(Entity.class.getName())); + if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName())) + classes.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName())); + if (db.getAnnotationIndex().containsKey(Embeddable.class.getName())) + classes.addAll(db.getAnnotationIndex().get(Embeddable.class.getName())); + } + catch (IOException e) { + getLog().error("Error while scanning!", e); + throw new MojoFailureException(e.getMessage()); + } + if (classes.isEmpty()) + throw new MojoFailureException("No annotated classes found in directory " + outputDirectory); + + Properties properties = new Properties(); + + /** Try to read configuration from properties-file */ + try { + File file = new File(hibernateProperties); + if (file.exists()) { + getLog().info("Reading properties from file " + hibernateProperties + "..."); + properties.load(new FileInputStream(file)); + } + else + getLog().info("Ignoring nonexistent properties-file " + hibernateProperties + "!"); + } + catch (IOException e) { + getLog().error("Error while reading properties!", e); + throw new MojoExecutionException(e.getMessage()); + } + + /** Overwrite values from propertie-file or set if given */ + if (driverClassName != null) + properties.setProperty("hibernate.connection.driver_class", driverClassName); + if (url != null) + properties.setProperty("hibernate.connection.url", url); + if (username != null) + properties.setProperty("hibernate.connection.username", username); + if (password != null) + properties.setProperty("hibernate.connection.password", password); + if (hibernateDialect != null) + properties.setProperty("hibernate.dialect", hibernateDialect); + + if (properties.isEmpty()) + getLog().warn("No properties set!"); + for (Entry entry : properties.entrySet()) + getLog().debug(entry.getKey() + " = " + entry.getValue()); + + ClassLoader classLoader = null; + try { + getLog().debug("Creating ClassLoader for project-dependencies..."); + List classpathFiles = project.getCompileClasspathElements(); + URL[] urls = new URL[classpathFiles.size()]; + for (int i = 0; i < classpathFiles.size(); ++i) { + getLog().debug("Dependency: " + classpathFiles.get(i)); + urls[i] = new File(classpathFiles.get(i)).toURI().toURL(); + } + classLoader = new URLClassLoader(urls, getClass().getClassLoader()); + } + catch (Exception e) { + getLog().error("Error while creating ClassLoader!", e); + throw new MojoExecutionException(e.getMessage()); + } + + Configuration config = new Configuration(); + config.setProperties(properties); + try { + getLog().debug("Adding annotated classes to hibernate-mapping-configuration..."); + for (String annotatedClass : classes) { + getLog().debug("Class " + annotatedClass); + config.addAnnotatedClass(classLoader.loadClass(annotatedClass)); + } + } + catch (ClassNotFoundException e) { + getLog().error("Error while adding annotated classes!", e); + throw new MojoExecutionException(e.getMessage()); + } + + Target target = null; + try { + target = Target.valueOf(this.target); + } + catch (IllegalArgumentException e) { + getLog().error("Invalid value for configuration-option \"target\": " + this.target); + getLog().error("Valid values are: NONE, SCRIPT, EXPORT, BOTH"); + throw new MojoExecutionException("Invalid value for configuration-option \"target\""); + } + Type type = null; + try { + type = Type.valueOf(this.type); + } + catch (IllegalArgumentException e) { + getLog().error("Invalid value for configuration-option \"type\": " + this.type); + getLog().error("Valid values are: NONE, CREATE, DROP, BOTH"); + throw new MojoExecutionException("Invalid value for configuration-option \"type\""); + } + + Connection connection = null; + try { + /** + * The connection must be established outside of hibernate, because + * hibernate does not use the context-classloader of the current + * thread and, hence, would not be able to resolve the driver-class! + */ + switch (target) { + case EXPORT: + case BOTH: + switch (type) { + case CREATE: + case DROP: + case BOTH: + Class driverClass = classLoader.loadClass(driverClassName); + getLog().debug("Registering JDBC-driver " + driverClass.getName()); + DriverManager.registerDriver(new DriverProxy((Driver)driverClass.newInstance())); + getLog().debug("Opening JDBC-connection to " + url + " as " + username + " with password " + password); + connection = DriverManager.getConnection(url, username, password); + } + } + } + catch (ClassNotFoundException e) { + getLog().error("Dependency for driver-class " + driverClassName + " is missing!"); + throw new MojoExecutionException(e.getMessage()); + } + catch (Exception e) { + getLog().error("Cannot establish connection to database!"); + Enumeration drivers = DriverManager.getDrivers(); + if (!drivers.hasMoreElements()) + getLog().error("No drivers registered!"); + while (drivers.hasMoreElements()) + getLog().debug("Driver: " + drivers.nextElement()); + throw new MojoExecutionException(e.getMessage()); + } + + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + MavenLogAppender.startPluginLog(this); + try { + /** + * Change class-loader of current thread, so that hibernate can + * see all dependencies! + */ + Thread.currentThread().setContextClassLoader(classLoader); + + SchemaExport export = new SchemaExport(config, connection); + export.setOutputFile(outputFile); + export.setDelimiter(delimiter); + export.setFormat(format); + export.execute(target, type); + + for (Object exception : export.getExceptions()) + getLog().debug(exception.toString()); + } + finally { + /** Stop Log-Capturing */ + MavenLogAppender.endPluginLog(this); + + /** Restore the old class-loader (TODO: is this really necessary?) */ + Thread.currentThread().setContextClassLoader(contextClassLoader); + + /** Close the connection */ + try { + connection.close(); + } + catch (SQLException e) { + getLog().error("Error while closing connection: " + e.getMessage()); + } + } + } + + /** + * Needed, because DriverManager won't pick up drivers, that were not + * loaded by the system-classloader! + * See: + * http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-from-an-arbitrary-location + */ + static final class DriverProxy implements Driver { + + private final Driver target; + + DriverProxy(Driver target) { + if (target == null) { + throw new NullPointerException(); + } + this.target = target; + } + + public java.sql.Driver getTarget() { + return target; + } + + @Override + public boolean acceptsURL(String url) throws SQLException { + return target.acceptsURL(url); + } + + @Override + public java.sql.Connection connect( + String url, java.util.Properties info) throws SQLException { + return target.connect(url, info); + } + + @Override + public int getMajorVersion() { + return target.getMajorVersion(); + } + + @Override + public int getMinorVersion() { + return target.getMinorVersion(); + } + + @Override + public DriverPropertyInfo[] getPropertyInfo( + String url, Properties info) throws SQLException { + return target.getPropertyInfo(url, info); + } + + @Override + public boolean jdbcCompliant() { + return target.jdbcCompliant(); + } + + @Override + public String toString() { + return "Proxy: " + target; + } + + @Override + public int hashCode() { + return target.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DriverProxy)) { + return false; + } + DriverProxy other = (DriverProxy) obj; + return this.target.equals(other.target); + } +} +}