X-Git-Url: https://juplo.de/gitweb/?p=website;a=blobdiff_plain;f=dist%2Fhibernate4-maven-plugin-1.0.5%2Fxref%2Fde%2Fjuplo%2Fplugins%2Fhibernate4%2FHbm2DdlMojo.html;fp=dist%2Fhibernate4-maven-plugin-1.0.5%2Fxref%2Fde%2Fjuplo%2Fplugins%2Fhibernate4%2FHbm2DdlMojo.html;h=6189278dc615963287549c82341f19f407e3506b;hp=0000000000000000000000000000000000000000;hb=a53595184bd6e57bdc45292cc92c393c4e2dfe6e;hpb=c48c9ee0e9faa89a4c0a5323b367b9f5a6abe602 diff --git a/dist/hibernate4-maven-plugin-1.0.5/xref/de/juplo/plugins/hibernate4/Hbm2DdlMojo.html b/dist/hibernate4-maven-plugin-1.0.5/xref/de/juplo/plugins/hibernate4/Hbm2DdlMojo.html new file mode 100644 index 00000000..6189278d --- /dev/null +++ b/dist/hibernate4-maven-plugin-1.0.5/xref/de/juplo/plugins/hibernate4/Hbm2DdlMojo.html @@ -0,0 +1,1075 @@ + + +
+ ++ +1 package de.juplo.plugins.hibernate4; +2 +3 /* +4 * Copyright 2001-2005 The Apache Software Foundation. +5 * +6 * Licensed under the Apache License, Version 2.0 (the "License"); +7 * you may not use this file except in compliance with the License. +8 * You may obtain a copy of the License at +9 * +10 * http://www.apache.org/licenses/LICENSE-2.0 +11 * +12 * Unless required by applicable law or agreed to in writing, software +13 * distributed under the License is distributed on an "AS IS" BASIS, +14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +15 * See the License for the specific language governing permissions and +16 * limitations under the License. +17 */ +18 +19 import com.pyx4j.log4j.MavenLogAppender; +20 import java.io.File; +21 import java.io.FileInputStream; +22 import java.io.FileNotFoundException; +23 import java.io.FileOutputStream; +24 import java.io.IOException; +25 import java.io.InputStream; +26 import java.io.ObjectInputStream; +27 import java.io.ObjectOutputStream; +28 import java.math.BigInteger; +29 import java.net.URL; +30 import java.net.URLClassLoader; +31 import java.security.MessageDigest; +32 import java.security.NoSuchAlgorithmException; +33 import java.sql.Connection; +34 import java.sql.Driver; +35 import java.sql.DriverManager; +36 import java.sql.DriverPropertyInfo; +37 import java.sql.SQLException; +38 import java.sql.SQLFeatureNotSupportedException; +39 import java.util.Comparator; +40 import java.util.Enumeration; +41 import java.util.HashMap; +42 import java.util.HashSet; +43 import java.util.List; +44 import java.util.Map; +45 import java.util.Map.Entry; +46 import java.util.Properties; +47 import java.util.Set; +48 import java.util.TreeSet; +49 import java.util.logging.Logger; +50 import java.util.regex.Matcher; +51 import java.util.regex.Pattern; +52 import javax.persistence.Embeddable; +53 import javax.persistence.Entity; +54 import javax.persistence.MappedSuperclass; +55 import org.apache.maven.artifact.Artifact; +56 import org.apache.maven.model.Resource; +57 import org.apache.maven.plugin.AbstractMojo; +58 import org.apache.maven.plugin.MojoExecutionException; +59 import org.apache.maven.plugin.MojoFailureException; +60 import org.apache.maven.project.MavenProject; +61 import org.hibernate.cfg.NamingStrategy; +62 import org.hibernate.envers.configuration.spi.AuditConfiguration; +63 import org.hibernate.tool.hbm2ddl.SchemaExport; +64 import org.hibernate.tool.hbm2ddl.SchemaExport.Type; +65 import org.hibernate.tool.hbm2ddl.Target; +66 import org.scannotation.AnnotationDB; +67 +68 +69 /** +70 * Goal which extracts the hibernate-mapping-configuration and +71 * exports an according SQL-database-schema. +72 * +73 * @goal export +74 * @phase process-classes +75 * @threadSafe +76 * @requiresDependencyResolution runtime +77 */ +78 public class Hbm2DdlMojo extends AbstractMojo +79 { +80 public final static String EXPORT_SKIPPED_PROPERTY = "hibernate.export.skipped"; +81 +82 public final static String DRIVER_CLASS = "hibernate.connection.driver_class"; +83 public final static String URL = "hibernate.connection.url"; +84 public final static String USERNAME = "hibernate.connection.username"; +85 public final static String PASSWORD = "hibernate.connection.password"; +86 public final static String DIALECT = "hibernate.dialect"; +87 public final static String NAMING_STRATEGY="hibernate.ejb.naming_strategy"; +88 public final static String ENVERS = "hibernate.export.envers"; +89 +90 public final static String MD5S = "hibernate4-generatedschema.md5s"; +91 +92 private final static Pattern split = Pattern.compile("[^,\\s]+"); +93 +94 +95 /** +96 * The maven project. +97 * <p> +98 * Only needed internally. +99 * +100 * @parameter property="project" +101 * @required +102 * @readonly +103 */ +104 private MavenProject project; +105 +106 /** +107 * Build-directory. +108 * <p> +109 * Only needed internally. +110 * +111 * @parameter property="project.build.directory" +112 * @required +113 * @readonly +114 */ +115 private String buildDirectory; +116 +117 /** +118 * Classes-Directory to scan. +119 * <p> +120 * This parameter defaults to the maven build-output-directory for classes. +121 * Additionally, all dependencies are scanned for annotated classes. +122 * +123 * @parameter property="project.build.outputDirectory" +124 * @since 1.0 +125 */ +126 private String outputDirectory; +127 +128 /** +129 * Whether to scan test-classes too, or not. +130 * <p> +131 * If this parameter is set to <code>true</code> the test-classes of the +132 * artifact will be scanned for hibernate-annotated classes additionally. +133 * +134 * @parameter property="hibernate.export.scan_testclasses" default-value="false" +135 * @since 1.0.1 +136 */ +137 private boolean scanTestClasses; +138 +139 /** +140 * Dependency-Scopes, that should be scanned for annotated classes. +141 * <p> +142 * By default, only dependencies in the scope <code>compile</code> are +143 * scanned for annotated classes. Multiple scopes can be seperated by +144 * white space or commas. +145 * <p> +146 * If you do not want any dependencies to be scanned for annotated +147 * classes, set this parameter to <code>none</code>. +148 * <p> +149 * The plugin does not scan for annotated classes in transitive +150 * dependencies. If some of your annotated classes are hidden in a +151 * transitive dependency, you can simply add that dependency explicitly. +152 * +153 * @parameter property="hibernate.export.scan_dependencies" default-value="compile" +154 * @since 1.0.3 +155 */ +156 private String scanDependencies; +157 +158 /** +159 * Test-Classes-Directory to scan. +160 * <p> +161 * This parameter defaults to the maven build-output-directory for +162 * test-classes. +163 * <p> +164 * This parameter is only used, when <code>scanTestClasses</code> is set +165 * to <code>true</code>! +166 * +167 * @parameter property="project.build.testOutputDirectory" +168 * @since 1.0.2 +169 */ +170 private String testOutputDirectory; +171 +172 /** +173 * Skip execution +174 * <p> +175 * If set to <code>true</code>, the execution is skipped. +176 * <p> +177 * A skipped execution is signaled via the maven-property +178 * <code>${hibernate.export.skipped}</code>. +179 * <p> +180 * The execution is skipped automatically, if no modified or newly added +181 * annotated classes are found and the dialect was not changed. +182 * +183 * @parameter property="hibernate.skip" default-value="${maven.test.skip}" +184 * @since 1.0 +185 */ +186 private boolean skip; +187 +188 /** +189 * Force execution +190 * <p> +191 * Force execution, even if no modified or newly added annotated classes +192 * where found and the dialect was not changed. +193 * <p> +194 * <code>skip</code> takes precedence over <code>force</code>. +195 * +196 * @parameter property="hibernate.export.force" default-value="false" +197 * @since 1.0 +198 */ +199 private boolean force; +200 +201 /** +202 * SQL-Driver name. +203 * +204 * @parameter property="hibernate.connection.driver_class" +205 * @since 1.0 +206 */ +207 private String driverClassName; +208 +209 /** +210 * Database URL. +211 * +212 * @parameter property="hibernate.connection.url" +213 * @since 1.0 +214 */ +215 private String url; +216 +217 /** +218 * Database username +219 * +220 * @parameter property="hibernate.connection.username" +221 * @since 1.0 +222 */ +223 private String username; +224 +225 /** +226 * Database password +227 * +228 * @parameter property="hibernate.connection.password" +229 * @since 1.0 +230 */ +231 private String password; +232 +233 /** +234 * Hibernate dialect. +235 * +236 * @parameter property="hibernate.dialect" +237 * @since 1.0 +238 */ +239 private String hibernateDialect; +240 +241 /** +242 * Hibernate Naming Strategy +243 * +244 * @parameter property="hibernate.ejb.naming_strategy" +245 * @since 1.0.2 +246 */ +247 private String hibernateNamingStrategy; +248 +249 /** +250 * Path to Hibernate configuration file. +251 * +252 * @parameter default-value="${project.build.outputDirectory}/hibernate.properties" +253 * @since 1.0 +254 */ +255 private String hibernateProperties; +256 +257 /** +258 * List of Hibernate-Mapping-Files (XML). +259 * Multiple files can be separated with white-spaces and/or commas. +260 * +261 * @parameter property="hibernate.mapping" +262 * @since 1.0.2 +263 */ +264 private String hibernateMapping; +265 +266 /** +267 * Target of execution: +268 * <ul> +269 * <li><strong>NONE</strong> only export schema to SQL-script (forces execution, signals skip)</li> +270 * <li><strong>EXPORT</strong> create database (<strong>DEFAULT!</strong>). forces execution, signals skip)</li> +271 * <li><strong>SCRIPT</strong> export schema to SQL-script and print it to STDOUT</li> +272 * <li><strong>BOTH</strong></li> +273 * </ul> +274 * +275 * A database connection is only needed for EXPORT and BOTH, but a +276 * Hibernate-Dialect must always be chosen. +277 * +278 * @parameter property="hibernate.export.target" default-value="EXPORT" +279 * @since 1.0 +280 */ +281 private String target; +282 +283 /** +284 * Type of execution. +285 * <ul> +286 * <li><strong>NONE</strong> do nothing - just validate the configuration</li> +287 * <li><strong>CREATE</strong> create database-schema</li> +288 * <li><strong>DROP</strong> drop database-schema</li> +289 * <li><strong>BOTH</strong> (<strong>DEFAULT!</strong>)</li> +290 * </ul> +291 * +292 * If NONE is choosen, no databaseconnection is needed. +293 * +294 * @parameter property="hibernate.export.type" default-value="BOTH" +295 * @since 1.0 +296 */ +297 private String type; +298 +299 /** +300 * Output file. +301 * +302 * @parameter property="hibernate.export.schema.filename" default-value="${project.build.directory}/schema.sql" +303 * @since 1.0 +304 */ +305 private String outputFile; +306 +307 /** +308 * Delimiter in output-file. +309 * +310 * @parameter property="hibernate.export.schema.delimiter" default-value=";" +311 * @since 1.0 +312 */ +313 private String delimiter; +314 +315 /** +316 * Format output-file. +317 * +318 * @parameter property="hibernate.export.schema.format" default-value="true" +319 * @since 1.0 +320 */ +321 private boolean format; +322 +323 /** +324 * Generate envers schema for auditing tables. +325 * +326 * @parameter property="hibernate.export.envers" default-value="false" +327 * @since 1.0.3 +328 */ +329 private boolean envers; +330 +331 +332 @Override +333 public void execute() +334 throws +335 MojoFailureException, +336 MojoExecutionException +337 { +338 if (skip) +339 { +340 getLog().info("Execution of hibernate4-maven-plugin:export was skipped!"); +341 project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true"); +342 return; +343 } +344 +345 Map<String,String> md5s; +346 boolean modified = false; +347 File saved = new File(buildDirectory + File.separator + MD5S); +348 +349 if (saved.isFile() && saved.length() > 0) +350 { +351 try +352 { +353 FileInputStream fis = new FileInputStream(saved); +354 ObjectInputStream ois = new ObjectInputStream(fis); +355 md5s = (HashMap<String,String>)ois.readObject(); +356 ois.close(); +357 } +358 catch (Exception e) +359 { +360 md5s = new HashMap<String,String>(); +361 getLog().warn("Cannot read timestamps from saved: " + e); +362 } +363 } +364 else +365 { +366 md5s = new HashMap<String,String>(); +367 try +368 { +369 saved.createNewFile(); +370 } +371 catch (IOException e) +372 { +373 getLog().debug("Cannot create file \"" + saved.getPath() + "\" for timestamps: " + e); +374 } +375 } +376 +377 ClassLoader classLoader = null; +378 try +379 { +380 getLog().debug("Creating ClassLoader for project-dependencies..."); +381 List<String> classpathFiles = project.getCompileClasspathElements(); +382 if (scanTestClasses) +383 classpathFiles.addAll(project.getTestClasspathElements()); +384 URL[] urls = new URL[classpathFiles.size()]; +385 for (int i = 0; i < classpathFiles.size(); ++i) +386 { +387 getLog().debug("Dependency: " + classpathFiles.get(i)); +388 urls[i] = new File(classpathFiles.get(i)).toURI().toURL(); +389 } +390 classLoader = new URLClassLoader(urls, getClass().getClassLoader()); +391 } +392 catch (Exception e) +393 { +394 getLog().error("Error while creating ClassLoader!", e); +395 throw new MojoExecutionException(e.getMessage()); +396 } +397 +398 Set<Class<?>> classes = +399 new TreeSet<Class<?>>( +400 new Comparator<Class<?>>() { +401 @Override +402 public int compare(Class<?> a, Class<?> b) +403 { +404 return a.getName().compareTo(b.getName()); +405 } +406 } +407 ); +408 +409 try +410 { +411 AnnotationDB db = new AnnotationDB(); +412 File dir = new File(outputDirectory); +413 if (dir.exists()) +414 { +415 getLog().info("Scanning directory " + outputDirectory + " for annotated classes..."); +416 URL dirUrl = dir.toURI().toURL(); +417 db.scanArchives(dirUrl); +418 } +419 if (scanTestClasses) +420 { +421 dir = new File(testOutputDirectory); +422 if (dir.exists()) +423 { +424 getLog().info("Scanning directory " + testOutputDirectory + " for annotated classes..."); +425 URL dirUrl = dir.toURI().toURL(); +426 db.scanArchives(dirUrl); +427 } +428 } +429 if (scanDependencies != null) +430 { +431 Matcher matcher = split.matcher(scanDependencies); +432 while (matcher.find()) +433 { +434 getLog().info("Scanning dependencies for scope " + matcher.group()); +435 for (Artifact artifact : project.getDependencyArtifacts()) +436 { +437 if (!artifact.getScope().equalsIgnoreCase(matcher.group())) +438 continue; +439 if (artifact.getFile() == null) +440 { +441 getLog().warn( +442 "Cannot scan dependency " + +443 artifact.getId() + +444 ": no JAR-file available!" +445 ); +446 continue; +447 } +448 getLog().info( +449 "Scanning dependency " + +450 artifact.getId() + +451 " for annotated classes..." +452 ); +453 db.scanArchives(artifact.getFile().toURI().toURL()); +454 } +455 } +456 } +457 +458 Set<String> classNames = new HashSet<String>(); +459 if (db.getAnnotationIndex().containsKey(Entity.class.getName())) +460 classNames.addAll(db.getAnnotationIndex().get(Entity.class.getName())); +461 if (db.getAnnotationIndex().containsKey(MappedSuperclass.class.getName())) +462 classNames.addAll(db.getAnnotationIndex().get(MappedSuperclass.class.getName())); +463 if (db.getAnnotationIndex().containsKey(Embeddable.class.getName())) +464 classNames.addAll(db.getAnnotationIndex().get(Embeddable.class.getName())); +465 +466 MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); +467 for (String name : classNames) +468 { +469 Class<?> annotatedClass = classLoader.loadClass(name); +470 classes.add(annotatedClass); +471 String resourceName = annotatedClass.getName(); +472 resourceName = resourceName.substring(resourceName.lastIndexOf(".") + 1, resourceName.length()) + ".class"; +473 InputStream is = +474 annotatedClass +475 .getResourceAsStream(resourceName); +476 byte[] buffer = new byte[1024*4]; // copy data in 4MB-chunks +477 int i; +478 while((i = is.read(buffer)) > -1) +479 digest.update(buffer, 0, i); +480 is.close(); +481 byte[] bytes = digest.digest(); +482 BigInteger bi = new BigInteger(1, bytes); +483 String newMd5 = String.format("%0" + (bytes.length << 1) + "x", bi); +484 String oldMd5 = !md5s.containsKey(name) ? "" : md5s.get(name); +485 if (!newMd5.equals(oldMd5)) +486 { +487 getLog().debug("Found new or modified annotated class: " + name); +488 modified = true; +489 md5s.put(name, newMd5); +490 } +491 else +492 { +493 getLog().debug(oldMd5 + " -> class unchanged: " + name); +494 } +495 } +496 } +497 catch (ClassNotFoundException e) +498 { +499 getLog().error("Error while adding annotated classes!", e); +500 throw new MojoExecutionException(e.getMessage()); +501 } +502 catch (Exception e) +503 { +504 getLog().error("Error while scanning!", e); +505 throw new MojoFailureException(e.getMessage()); +506 } +507 +508 if (classes.isEmpty()) +509 { +510 if (hibernateMapping == null || hibernateMapping.isEmpty()) +511 throw new MojoFailureException("No annotated classes found in directory " + outputDirectory); +512 } +513 else +514 { +515 getLog().debug("Detected classes with mapping-annotations:"); +516 for (Class<?> annotatedClass : classes) +517 getLog().debug(" " + annotatedClass.getName()); +518 } +519 +520 +521 Properties properties = new Properties(); +522 +523 /** Try to read configuration from properties-file */ +524 try +525 { +526 File file = new File(hibernateProperties); +527 if (file.exists()) +528 { +529 getLog().info("Reading properties from file " + hibernateProperties + "..."); +530 properties.load(new FileInputStream(file)); +531 } +532 else +533 getLog().info("No hibernate-properties-file found! (Checked path: " + hibernateProperties + ")"); +534 } +535 catch (IOException e) +536 { +537 getLog().error("Error while reading properties!", e); +538 throw new MojoExecutionException(e.getMessage()); +539 } +540 +541 /** Overwrite values from properties-file or set, if given */ +542 if (driverClassName != null) +543 { +544 if (properties.containsKey(DRIVER_CLASS)) +545 getLog().debug( +546 "Overwriting property " + +547 DRIVER_CLASS + "=" + properties.getProperty(DRIVER_CLASS) + +548 " with the value " + driverClassName +549 ); +550 else +551 getLog().debug("Using the value " + driverClassName); +552 properties.setProperty(DRIVER_CLASS, driverClassName); +553 } +554 if (url != null) +555 { +556 if (properties.containsKey(URL)) +557 getLog().debug( +558 "Overwriting property " + +559 URL + "=" + properties.getProperty(URL) + +560 " with the value " + url +561 ); +562 else +563 getLog().debug("Using the value " + url); +564 properties.setProperty(URL, url); +565 } +566 if (username != null) +567 { +568 if (properties.containsKey(USERNAME)) +569 getLog().debug( +570 "Overwriting property " + +571 USERNAME + "=" + properties.getProperty(USERNAME) + +572 " with the value " + username +573 ); +574 else +575 getLog().debug("Using the value " + username); +576 properties.setProperty(USERNAME, username); +577 } +578 if (password != null) +579 { +580 if (properties.containsKey(PASSWORD)) +581 getLog().debug( +582 "Overwriting property " + +583 PASSWORD + "=" + properties.getProperty(PASSWORD) + +584 " with value " + password +585 ); +586 else +587 getLog().debug("Using value " + password + " for property " + PASSWORD); +588 properties.setProperty(PASSWORD, password); +589 } +590 if (hibernateDialect != null) +591 { +592 if (properties.containsKey(DIALECT)) +593 getLog().debug( +594 "Overwriting property " + +595 DIALECT + "=" + properties.getProperty(DIALECT) + +596 " with value " + hibernateDialect +597 ); +598 else +599 getLog().debug( +600 "Using value " + hibernateDialect + " for property " + DIALECT +601 ); +602 properties.setProperty(DIALECT, hibernateDialect); +603 } +604 else +605 { +606 hibernateDialect = properties.getProperty(DIALECT); +607 } +608 if ( hibernateNamingStrategy != null ) +609 { +610 if ( properties.contains(NAMING_STRATEGY)) +611 getLog().debug( +612 "Overwriting property " + +613 NAMING_STRATEGY + "=" + properties.getProperty(NAMING_STRATEGY) + +614 " with value " + hibernateNamingStrategy +615 ); +616 else +617 getLog().debug( +618 "Using value " + hibernateNamingStrategy + " for property " + +619 NAMING_STRATEGY +620 ); +621 properties.setProperty(NAMING_STRATEGY, hibernateNamingStrategy); +622 } +623 +624 /** The generated SQL varies with the dialect! */ +625 if (md5s.containsKey(DIALECT)) +626 { +627 String dialect = properties.getProperty(DIALECT); +628 if (md5s.get(DIALECT).equals(dialect)) +629 getLog().debug("SQL-dialect unchanged."); +630 else +631 { +632 modified = true; +633 if (dialect == null) +634 { +635 getLog().debug("SQL-dialect was unset."); +636 md5s.remove(DIALECT); +637 } +638 else +639 { +640 getLog().debug("SQL-dialect changed: " + dialect); +641 md5s.put(DIALECT, dialect); +642 } +643 } +644 } +645 else +646 { +647 String dialect = properties.getProperty(DIALECT); +648 if (dialect != null) +649 { +650 modified = true; +651 md5s.put(DIALECT, properties.getProperty(DIALECT)); +652 } +653 } +654 +655 /** The generated SQL varies with the envers-configuration */ +656 if (md5s.get(ENVERS) != null) +657 { +658 if (md5s.get(ENVERS).equals(Boolean.toString(envers))) +659 getLog().debug("Envers-Configuration unchanged. Enabled: " + envers); +660 else +661 { +662 getLog().debug("Envers-Configuration changed. Enabled: " + envers); +663 modified = true; +664 md5s.put(ENVERS, Boolean.toString(envers)); +665 } +666 } +667 else +668 { +669 modified = true; +670 md5s.put(ENVERS, Boolean.toString(envers)); +671 } +672 +673 if (properties.isEmpty()) +674 { +675 getLog().error("No properties set!"); +676 throw new MojoFailureException("Hibernate configuration is missing!"); +677 } +678 +679 final ValidationConfiguration config = new ValidationConfiguration(hibernateDialect); +680 +681 config.setProperties(properties); +682 +683 if ( properties.containsKey(NAMING_STRATEGY)) +684 { +685 String namingStrategy = properties.getProperty(NAMING_STRATEGY); +686 getLog().debug("Explicitly set NamingStrategy: " + namingStrategy); +687 try +688 { +689 @SuppressWarnings("unchecked") +690 Class<NamingStrategy> namingStrategyClass = (Class<NamingStrategy>) Class.forName(namingStrategy); +691 config.setNamingStrategy(namingStrategyClass.newInstance()); +692 } +693 catch (Exception e) +694 { +695 getLog().error("Error setting NamingStrategy", e); +696 throw new MojoExecutionException(e.getMessage()); +697 } +698 } +699 +700 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); +701 Connection connection = null; +702 MavenLogAppender.startPluginLog(this); +703 try +704 { +705 /** +706 * Change class-loader of current thread, so that hibernate can +707 * see all dependencies! +708 */ +709 Thread.currentThread().setContextClassLoader(classLoader); +710 +711 getLog().debug("Adding annotated classes to hibernate-mapping-configuration..."); +712 // build annotated packages +713 Set<String> packages = new HashSet<String>(); +714 for (Class<?> annotatedClass : classes) +715 { +716 String packageName = annotatedClass.getPackage().getName(); +717 if (!packages.contains(packageName)) +718 { +719 getLog().debug("Add package " + packageName); +720 packages.add(packageName); +721 config.addPackage(packageName); +722 getLog().debug("type definintions" + config.getTypeDefs()); +723 } +724 getLog().debug("Class " + annotatedClass); +725 config.addAnnotatedClass(annotatedClass); +726 } +727 +728 if (hibernateMapping != null) +729 { +730 try +731 { +732 MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); +733 for (String filename : hibernateMapping.split("[\\s,]+")) +734 { +735 // First try the filename as absolute/relative path +736 File file = new File(filename); +737 if (!file.exists()) +738 { +739 // If the file was not found, search for it in the resource-directories +740 for (Resource resource : project.getResources()) +741 { +742 file = new File(resource.getDirectory() + File.separator + filename); +743 if (file.exists()) +744 break; +745 } +746 } +747 if (file != null && file.exists()) +748 { +749 InputStream is = new FileInputStream(file); +750 byte[] buffer = new byte[1024*4]; // copy data in 4MB-chunks +751 int i; +752 while((i = is.read(buffer)) > -1) +753 digest.update(buffer, 0, i); +754 is.close(); +755 byte[] bytes = digest.digest(); +756 BigInteger bi = new BigInteger(1, bytes); +757 String newMd5 = String.format("%0" + (bytes.length << 1) + "x", bi); +758 String oldMd5 = !md5s.containsKey(filename) ? "" : md5s.get(filename); +759 if (!newMd5.equals(oldMd5)) +760 { +761 getLog().debug("Found new or modified mapping-file: " + filename); +762 modified = true; +763 md5s.put(filename, newMd5); +764 } +765 else +766 { +767 getLog().debug(oldMd5 + " -> mapping-file unchanged: " + filename); +768 } +769 getLog().debug("Adding mappings from XML-configurationfile: " + file); +770 config.addFile(file); +771 } +772 else +773 throw new MojoFailureException("File " + filename + " could not be found in any of the configured resource-directories!"); +774 } +775 } +776 catch (NoSuchAlgorithmException e) +777 { +778 throw new MojoFailureException("Cannot calculate MD5 sums!", e); +779 } +780 catch (FileNotFoundException e) +781 { +782 throw new MojoFailureException("Cannot calculate MD5 sums!", e); +783 } +784 catch (IOException e) +785 { +786 throw new MojoFailureException("Cannot calculate MD5 sums!", e); +787 } +788 } +789 +790 Target target = null; +791 try +792 { +793 target = Target.valueOf(this.target.toUpperCase()); +794 } +795 catch (IllegalArgumentException e) +796 { +797 getLog().error("Invalid value for configuration-option \"target\": " + this.target); +798 getLog().error("Valid values are: NONE, SCRIPT, EXPORT, BOTH"); +799 throw new MojoExecutionException("Invalid value for configuration-option \"target\""); +800 } +801 Type type = null; +802 try +803 { +804 type = Type.valueOf(this.type.toUpperCase()); +805 } +806 catch (IllegalArgumentException e) +807 { +808 getLog().error("Invalid value for configuration-option \"type\": " + this.type); +809 getLog().error("Valid values are: NONE, CREATE, DROP, BOTH"); +810 throw new MojoExecutionException("Invalid value for configuration-option \"type\""); +811 } +812 +813 if (target.equals(Target.SCRIPT) || target.equals(Target.NONE)) +814 { +815 project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true"); +816 } +817 if ( +818 !modified +819 && !target.equals(Target.SCRIPT) +820 && !target.equals(Target.NONE) +821 && !force +822 ) +823 { +824 getLog().info("No modified annotated classes or mapping-files found and dialect unchanged."); +825 getLog().info("Skipping schema generation!"); +826 project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true"); +827 return; +828 } +829 +830 getLog().info("Gathered hibernate-configuration (turn on debugging for details):"); +831 for (Entry<Object,Object> entry : properties.entrySet()) +832 getLog().info(" " + entry.getKey() + " = " + entry.getValue()); +833 +834 try +835 { +836 /** +837 * The connection must be established outside of hibernate, because +838 * hibernate does not use the context-classloader of the current +839 * thread and, hence, would not be able to resolve the driver-class! +840 */ +841 getLog().debug("Target: " + target + ", Type: " + type); +842 switch (target) +843 { +844 case EXPORT: +845 case BOTH: +846 switch (type) +847 { +848 case CREATE: +849 case DROP: +850 case BOTH: +851 Class driverClass = classLoader.loadClass(properties.getProperty(DRIVER_CLASS)); +852 getLog().debug("Registering JDBC-driver " + driverClass.getName()); +853 DriverManager.registerDriver(new DriverProxy((Driver)driverClass.newInstance())); +854 getLog().debug( +855 "Opening JDBC-connection to " +856 + properties.getProperty(URL) +857 + " as " +858 + properties.getProperty(USERNAME) +859 + " with password " +860 + properties.getProperty(PASSWORD) +861 ); +862 connection = DriverManager.getConnection( +863 properties.getProperty(URL), +864 properties.getProperty(USERNAME), +865 properties.getProperty(PASSWORD) +866 ); +867 } +868 } +869 } +870 catch (ClassNotFoundException e) +871 { +872 getLog().error("Dependency for driver-class " + properties.getProperty(DRIVER_CLASS) + " is missing!"); +873 throw new MojoExecutionException(e.getMessage()); +874 } +875 catch (Exception e) +876 { +877 getLog().error("Cannot establish connection to database!"); +878 Enumeration<Driver> drivers = DriverManager.getDrivers(); +879 if (!drivers.hasMoreElements()) +880 getLog().error("No drivers registered!"); +881 while (drivers.hasMoreElements()) +882 getLog().debug("Driver: " + drivers.nextElement()); +883 throw new MojoExecutionException(e.getMessage()); +884 } +885 +886 config.buildMappings(); +887 +888 if (envers) +889 { +890 getLog().info("Automatic auditing via hibernate-envers enabled!"); +891 AuditConfiguration.getFor(config); +892 } +893 +894 SchemaExport export = new SchemaExport(config, connection); +895 export.setDelimiter(delimiter); +896 export.setFormat(format); +897 +898 File outF = new File(outputFile); +899 +900 if (!outF.isAbsolute()) +901 { +902 // Interpret relative file path relative to build directory +903 outF = new File(buildDirectory, outputFile); +904 getLog().info("Adjusted relative path, resulting path is " + outF.getPath()); +905 } +906 +907 // Ensure that directory path for specified file exists +908 File outFileParentDir = outF.getParentFile(); +909 if (null != outFileParentDir && !outFileParentDir.exists()) +910 { +911 try +912 { +913 getLog().info("Creating directory path for output file:" + outFileParentDir.getPath()); +914 outFileParentDir.mkdirs(); +915 } +916 catch (Exception e) +917 { +918 getLog().error("Error creating directory path for output file: " + e.getLocalizedMessage()); +919 } +920 } +921 +922 export.setOutputFile(outF.getPath()); +923 export.execute(target, type); +924 +925 for (Object exception : export.getExceptions()) +926 getLog().debug(exception.toString()); +927 } +928 finally +929 { +930 /** Stop Log-Capturing */ +931 MavenLogAppender.endPluginLog(this); +932 +933 /** Restore the old class-loader (TODO: is this really necessary?) */ +934 Thread.currentThread().setContextClassLoader(contextClassLoader); +935 +936 /** Close the connection */ +937 try +938 { +939 if (connection != null) +940 connection.close(); +941 } +942 catch (SQLException e) +943 { +944 getLog().error("Error while closing connection: " + e.getMessage()); +945 } +946 } +947 +948 /** Write md5-sums for annotated classes to file */ +949 try +950 { +951 FileOutputStream fos = new FileOutputStream(saved); +952 ObjectOutputStream oos = new ObjectOutputStream(fos); +953 oos.writeObject(md5s); +954 oos.close(); +955 fos.close(); +956 } +957 catch (Exception e) +958 { +959 getLog().error("Cannot write md5-sums to file: " + e); +960 } +961 } +962 +963 /** +964 * Needed, because DriverManager won't pick up drivers, that were not +965 * loaded by the system-classloader! +966 * See: +967 * http://stackoverflow.com/questions/288828/how-to-use-a-jdbc-driver-fromodifiedm-an-arbitrary-location +968 */ +969 static final class DriverProxy implements Driver +970 { +971 private final Driver target; +972 +973 DriverProxy(Driver target) +974 { +975 if (target == null) +976 throw new NullPointerException(); +977 this.target = target; +978 } +979 +980 public java.sql.Driver getTarget() +981 { +982 return target; +983 } +984 +985 @Override +986 public boolean acceptsURL(String url) throws SQLException +987 { +988 return target.acceptsURL(url); +989 } +990 +991 @Override +992 public java.sql.Connection connect( +993 String url, +994 java.util.Properties info +995 ) +996 throws +997 SQLException +998 { +999 return target.connect(url, info); +1000 } +1001 +1002 @Override +1003 public int getMajorVersion() +1004 { +1005 return target.getMajorVersion(); +1006 } +1007 +1008 @Override +1009 public int getMinorVersion() +1010 { +1011 return target.getMinorVersion(); +1012 } +1013 +1014 @Override +1015 public DriverPropertyInfo[] getPropertyInfo( +1016 String url, +1017 Properties info +1018 ) +1019 throws +1020 SQLException +1021 { +1022 return target.getPropertyInfo(url, info); +1023 } +1024 +1025 @Override +1026 public boolean jdbcCompliant() +1027 { +1028 return target.jdbcCompliant(); +1029 } +1030 +1031 /** +1032 * This Method cannot be annotated with @Override, becaus the plugin +1033 * will not compile then under Java 1.6! +1034 */ +1035 public Logger getParentLogger() throws SQLFeatureNotSupportedException +1036 { +1037 throw new SQLFeatureNotSupportedException("Not supported, for backward-compatibility with Java 1.6"); +1038 } +1039 +1040 @Override +1041 public String toString() +1042 { +1043 return "Proxy: " + target; +1044 } +1045 +1046 @Override +1047 public int hashCode() +1048 { +1049 return target.hashCode(); +1050 } +1051 +1052 @Override +1053 public boolean equals(Object obj) +1054 { +1055 if (!(obj instanceof DriverProxy)) +1056 return false; +1057 DriverProxy other = (DriverProxy) obj; +1058 return this.target.equals(other.target); +1059 } +1060 } +1061 } ++