X-Git-Url: https://juplo.de/gitweb/?p=hibernate4-maven-plugin;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fde%2Fjuplo%2Fplugins%2Fhibernate%2FAbstractSchemaMojo.java;fp=src%2Fmain%2Fjava%2Fde%2Fjuplo%2Fplugins%2Fhibernate%2FAbstractSchemaMojo.java;h=6c8a28189a0156500c568903328ab2c4ef19920f;hp=0000000000000000000000000000000000000000;hb=4940080670944a15916c68fb294e18a6bfef12d5;hpb=fdda82a6f76deefd10f83da89d7e82054e3c3ecd diff --git a/src/main/java/de/juplo/plugins/hibernate/AbstractSchemaMojo.java b/src/main/java/de/juplo/plugins/hibernate/AbstractSchemaMojo.java new file mode 100644 index 00000000..6c8a2818 --- /dev/null +++ b/src/main/java/de/juplo/plugins/hibernate/AbstractSchemaMojo.java @@ -0,0 +1,949 @@ +package de.juplo.plugins.hibernate; + + +import com.pyx4j.log4j.MavenLogAppender; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.MappedSuperclass; +import javax.persistence.spi.PersistenceUnitTransactionType; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Resource; +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.boot.MetadataBuilder; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.cfgxml.internal.ConfigLoader; +import org.hibernate.boot.model.naming.ImplicitNamingStrategy; +import org.hibernate.boot.model.naming.PhysicalNamingStrategy; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.boot.spi.MetadataImplementor; +import static org.hibernate.cfg.AvailableSettings.DIALECT; +import static org.hibernate.cfg.AvailableSettings.DRIVER; +import static org.hibernate.cfg.AvailableSettings.IMPLICIT_NAMING_STRATEGY; +import static org.hibernate.cfg.AvailableSettings.PASS; +import static org.hibernate.cfg.AvailableSettings.PHYSICAL_NAMING_STRATEGY; +import static org.hibernate.cfg.AvailableSettings.USER; +import static org.hibernate.cfg.AvailableSettings.URL; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.internal.util.config.ConfigurationException; +import static org.hibernate.jpa.AvailableSettings.JDBC_DRIVER; +import static org.hibernate.jpa.AvailableSettings.JDBC_PASSWORD; +import static org.hibernate.jpa.AvailableSettings.JDBC_URL; +import static org.hibernate.jpa.AvailableSettings.JDBC_USER; +import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor; +import org.hibernate.jpa.boot.internal.PersistenceXmlParser; +import org.hibernate.jpa.boot.spi.ProviderChecker; +import org.scannotation.AnnotationDB; + + +/** + * Baseclass with common attributes and methods. + * + * @phase process-classes + * @threadSafe + * @requiresDependencyResolution runtime + */ +public abstract class AbstractSchemaMojo extends AbstractMojo +{ + public final static String EXPORT_SKIPPED_PROPERTY = "hibernate.export.skipped"; + + private final static Pattern SPLIT = Pattern.compile("[^,\\s]+"); + + + /** + * The maven project. + *
+ * Only needed internally. + * + * @parameter property="project" + * @required + * @readonly + */ + private MavenProject project; + + /** + * Build-directory. + *
+ * Only needed internally. + * + * @parameter property="project.build.directory" + * @required + * @readonly + */ + String buildDirectory; + + /** + * Classes-Directory to scan. + *
+ * This parameter defaults to the maven build-output-directory for classes. + * Additionally, all dependencies are scanned for annotated classes. + * + * @parameter property="project.build.outputDirectory" + * @since 1.0 + */ + private String outputDirectory; + + /** + * Whether to scan test-classes too, or not. + *
+ * If this parameter is set to true
the test-classes of the
+ * artifact will be scanned for hibernate-annotated classes additionally.
+ *
+ * @parameter property="hibernate.export.scan_testclasses" default-value="false"
+ * @since 1.0.1
+ */
+ private boolean scanTestClasses;
+
+ /**
+ * Dependency-Scopes, that should be scanned for annotated classes.
+ *
+ * By default, only dependencies in the scope compile
are
+ * scanned for annotated classes. Multiple scopes can be seperated by
+ * white space or commas.
+ *
md5s
+ * If you do not want any dependencies to be scanned for annotated
+ * classes, set this parameter to none
.
+ *
+ * The plugin does not scan for annotated classes in transitive + * dependencies. If some of your annotated classes are hidden in a + * transitive dependency, you can simply add that dependency explicitly. + * + * @parameter property="hibernate.export.scan_dependencies" default-value="compile" + * @since 1.0.3 + */ + private String scanDependencies; + + /** + * Test-Classes-Directory to scan. + *
+ * This parameter defaults to the maven build-output-directory for + * test-classes. + *
+ * This parameter is only used, when scanTestClasses
is set
+ * to true
!
+ *
+ * @parameter property="project.build.testOutputDirectory"
+ * @since 1.0.2
+ */
+ private String testOutputDirectory;
+
+ /**
+ * Skip execution
+ *
+ * If set to true
, the execution is skipped.
+ *
+ * A skipped execution is signaled via the maven-property
+ * ${hibernate.export.skipped}
.
+ *
+ * The execution is skipped automatically, if no modified or newly added + * annotated classes are found and the dialect was not changed. + * + * @parameter property="hibernate.skip" default-value="${maven.test.skip}" + * @since 1.0 + */ + private boolean skip; + + /** + * Force execution + *
+ * Force execution, even if no modified or newly added annotated classes + * where found and the dialect was not changed. + *
+ * skip
takes precedence over force
.
+ *
+ * @parameter property="hibernate.export.force" default-value="false"
+ * @since 1.0
+ */
+ private boolean force;
+
+ /**
+ * SQL-Driver name.
+ *
+ * @parameter property="hibernate.connection.driver_class"
+ * @since 1.0
+ */
+ private String driver;
+
+ /**
+ * Database URL.
+ *
+ * @parameter property="hibernate.connection.url"
+ * @since 1.0
+ */
+ private String url;
+
+ /**
+ * Database username
+ *
+ * @parameter property="hibernate.connection.username"
+ * @since 1.0
+ */
+ private String username;
+
+ /**
+ * Database password
+ *
+ * @parameter property="hibernate.connection.password"
+ * @since 1.0
+ */
+ private String password;
+
+ /**
+ * Hibernate dialect.
+ *
+ * @parameter property="hibernate.dialect"
+ * @since 1.0
+ */
+ private String dialect;
+
+ /**
+ * Implicit naming strategy
+ *
+ * @parameter property=IMPLICIT_NAMING_STRATEGY
+ * @since 2.0
+ */
+ private String implicitNamingStrategy;
+
+ /**
+ * Physical naming strategy
+ *
+ * @parameter property=PHYSICAL_NAMING_STRATEGY
+ * @since 2.0
+ */
+ private String physicalNamingStrategy;
+
+ /**
+ * Path to a file or name of a ressource with hibernate properties.
+ * If this parameter is specified, the plugin will try to load configuration
+ * values from a file with the given path or a ressource on the classpath with
+ * the given name. If both fails, the execution of the plugin will fail.
+ *
+ * If this parameter is not set the plugin will load configuration values
+ * from a ressource named hibernate.properties
on the classpath,
+ * if it is present, but will not fail if there is no such ressource.
+ *
+ * During ressource-lookup, the test-classpath takes precedence. + * + * @parameter + * @since 1.0 + */ + private String hibernateProperties; + + /** + * Path to Hibernate configuration file (.cfg.xml). + * If this parameter is specified, the plugin will try to load configuration + * values from a file with the given path or a ressource on the classpath with + * the given name. If both fails, the execution of the plugin will fail. + *
+ * If this parameter is not set the plugin will load configuration values
+ * from a ressource named hibernate.cfg.xml
on the classpath,
+ * if it is present, but will not fail if there is no such ressource.
+ *
+ * During ressource-lookup, the test-classpath takes precedence. + *
+ * Settings in this file will overwrite settings in the properties file. + * + * @parameter + * @since 1.1.0 + */ + private String hibernateConfig; + + /** + * Name of the persistence-unit. + * If this parameter is specified, the plugin will try to load configuration + * values from a persistence-unit with the specified name. If no such + * persistence-unit can be found, the plugin will throw an exception. + *
+ * If this parameter is not set and there is only one persistence-unit + * available, that unit will be used automatically. But if this parameter is + * not set and there are multiple persistence-units available on, + * the class-path, the execution of the plugin will fail. + *
+ * Settings in this file will overwrite settings in the properties or the
+ * configuration file.
+ *
+ * @parameter
+ * @since 1.1.0
+ */
+ private String persistenceUnit;
+
+ /**
+ * List of Hibernate-Mapping-Files (XML).
+ * Multiple files can be separated with white-spaces and/or commas.
+ *
+ * @parameter property="hibernate.mapping"
+ * @since 1.0.2
+ */
+ private String mappings;
+
+
+ @Override
+ public final void execute()
+ throws
+ MojoFailureException,
+ MojoExecutionException
+ {
+ if (skip)
+ {
+ getLog().info("Execution of hibernate-maven-plugin was skipped!");
+ project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
+ return;
+ }
+
+ ModificationTracker tracker;
+ try
+ {
+ tracker = new ModificationTracker(buildDirectory, getLog());
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ throw new MojoFailureException("Digest-Algorithm MD5 is missing!", e);
+ }
+
+ SimpleConnectionProvider connectionProvider =
+ new SimpleConnectionProvider(getLog());
+
+ try
+ {
+ /** Start extended logging */
+ MavenLogAppender.startPluginLog(this);
+
+ /** Load checksums for old mapping and configuration */
+ tracker.load();
+
+ /** Create a BootstrapServiceRegistry with special ClassLoader */
+ BootstrapServiceRegistry bootstrapServiceRegitry =
+ new BootstrapServiceRegistryBuilder()
+ .applyClassLoader(createClassLoader())
+ .build();
+ ClassLoaderService classLoaderService =
+ bootstrapServiceRegitry.getService(ClassLoaderService.class);
+
+ Properties properties = new Properties();
+ ConfigLoader configLoader = new ConfigLoader(bootstrapServiceRegitry);
+
+ /** Loading and merging configuration */
+ properties.putAll(loadProperties(configLoader));
+ properties.putAll(loadConfig(configLoader));
+ properties.putAll(loadPersistenceUnit(classLoaderService, properties));
+
+ /** Overwriting/Completing configuration */
+ configure(properties);
+
+ /** Check configuration for modifications */
+ if(tracker.check(properties))
+ getLog().debug("Configuration has changed.");
+ else
+ getLog().debug("Configuration unchanged.");
+
+ /** Configure Hibernate */
+ StandardServiceRegistry serviceRegistry =
+ new StandardServiceRegistryBuilder(bootstrapServiceRegitry)
+ .applySettings(properties)
+ .addService(ConnectionProvider.class, connectionProvider)
+ .build();
+
+ /** Load Mappings */
+ MetadataSources sources = new MetadataSources(serviceRegistry);
+ addAnnotatedClasses(sources, classLoaderService, tracker);
+ addMappings(sources, tracker);
+
+ /** Skip execution, if mapping and configuration is unchanged */
+ if (!tracker.modified())
+ {
+ getLog().info(
+ "Mapping and configuration unchanged."
+ );
+ if (force)
+ getLog().info("Schema generation is forced!");
+ else
+ {
+ getLog().info("Skipping schema generation!");
+ project.getProperties().setProperty(EXPORT_SKIPPED_PROPERTY, "true");
+ return;
+ }
+ }
+
+
+ /** Create a connection, if sufficient configuration infromation is available */
+ connectionProvider.open(classLoaderService, properties);
+
+ MetadataBuilder metadataBuilder = sources.getMetadataBuilder();
+
+ StrategySelector strategySelector =
+ serviceRegistry.getService(StrategySelector.class);
+
+ if (properties.containsKey(IMPLICIT_NAMING_STRATEGY))
+ {
+ metadataBuilder.applyImplicitNamingStrategy(
+ strategySelector.resolveStrategy(
+ ImplicitNamingStrategy.class,
+ properties.getProperty(IMPLICIT_NAMING_STRATEGY)
+ )
+ );
+ }
+
+ if (properties.containsKey(PHYSICAL_NAMING_STRATEGY))
+ {
+ metadataBuilder.applyPhysicalNamingStrategy(
+ strategySelector.resolveStrategy(
+ PhysicalNamingStrategy.class,
+ properties.getProperty(PHYSICAL_NAMING_STRATEGY)
+ )
+ );
+ }
+
+ build((MetadataImplementor)metadataBuilder.build());
+ }
+ finally
+ {
+ /** Remember mappings and configuration */
+ tracker.save();
+
+ /** Close the connection - if one was opened */
+ connectionProvider.close();
+
+ /** Stop Log-Capturing */
+ MavenLogAppender.endPluginLog(this);
+ }
+ }
+
+
+ abstract void build(MetadataImplementor metadata)
+ throws
+ MojoFailureException,
+ MojoExecutionException;
+
+
+ private URLClassLoader createClassLoader() throws MojoExecutionException
+ {
+ try
+ {
+ getLog().debug("Creating ClassLoader for project-dependencies...");
+ List