View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.maven.hibernate;
17  
18  import java.io.ByteArrayOutputStream;
19  import java.io.File;
20  import java.io.PrintStream;
21  import java.net.URL;
22  import java.net.URLClassLoader;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.maven.plugin.AbstractMojo;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.plugin.MojoFailureException;
29  import org.apache.maven.project.MavenProject;
30  import org.hibernate.cfg.Configuration;
31  import org.hibernate.cfg.NamingStrategy;
32  import org.hibernate.ejb.Ejb3Configuration;
33  import org.hibernate.tool.hbm2ddl.SchemaExport;
34  
35  /**
36   * Plugin for schema export to DDL.
37   *
38   * @goal hbm2ddl
39   * @phase process-classes
40   * @threadSafe
41   * @requiresProject
42   * @requiresDependencyResolution compile+runtime
43   * @since 1.0
44   * @description Runs Hibernate 4' schema export to DDL.
45   */
46  @SuppressWarnings("deprecation")
47  public class SchemaExportMojo extends AbstractMojo
48  {
49    // ********************************* Fields *********************************
50  
51    // --- constants ------------------------------------------------------------
52  
53    // --- members --------------------------------------------------------------
54  
55    /**
56     * The Maven project.
57     *
58     * @parameter expression="${project}"
59     * @required
60     * @readonly
61     * @since 1.0
62     */
63    private MavenProject project;
64  
65    /**
66     * The name of the persistence unit to export.
67     *
68     * @parameter
69     * @since 1.0
70     */
71    private String unitName;
72  
73    /**
74     * The SQL statement delimiter to use.
75     *
76     * @parameter default-value=";"
77     * @since 1.0
78     */
79    private String delimiter;
80  
81    /**
82     * The file to write the drop schema script to.
83     *
84     * @parameter default-value="${project.build.directory}/hibernate/drop.sql"
85     * @since 1.0
86     */
87    private String dropOutputFile;
88  
89    /**
90     * The file to write the create schema script to.
91     *
92     * @parameter default-value="${project.build.directory}/hibernate/create.sql"
93     * @since 1.0
94     */
95    private String createOutputFile;
96  
97    /**
98     * The naming strategy to be used when generating physical column and table
99     * names. This must be a complete class name that implements
100    * <code>org.hibernate.cfg.NamingStrategy</code> interface (like for example
101    * <code>org.hibernate.cfg.ImprovedNamingStrategy</code>).
102    *
103    * @parameter
104    * @since 1.0
105    */
106   private String namingStrategy;
107 
108   // ... command ..............................................................
109 
110   // ... fixes ................................................................
111 
112   // ****************************** Initializer *******************************
113 
114   // ****************************** Constructors ******************************
115 
116   // ****************************** Inner Classes *****************************
117 
118   // ********************************* Methods ********************************
119 
120   // --- init -----------------------------------------------------------------
121 
122   // --- get&set --------------------------------------------------------------
123 
124   // --- business -------------------------------------------------------------
125 
126   // CHECKSTYLE:OFF
127   /**
128    * {@inheritDoc}
129    *
130    * @see org.apache.maven.plugin.Mojo#execute()
131    */
132   @Override
133   public void execute() throws MojoExecutionException, MojoFailureException
134   // CHECKSTYLE:ON
135   {
136     getLog().info("Exporting '" + unitName + "'.");
137 
138     ensureOutputFolder(dropOutputFile);
139     ensureOutputFolder(createOutputFile);
140 
141     final Thread currentThread = Thread.currentThread();
142     final PrintStream oldOut = System.out;
143     final ClassLoader oldClassLoader = currentThread.getContextClassLoader();
144     final ArrayList<String> errorMessages = new ArrayList<String>();
145 
146     try
147     {
148       // Can't log from here because we are setting a different output stream.
149       System.setOut(new PrintStream(new ByteArrayOutputStream()));
150 
151       currentThread.setContextClassLoader(getClassLoader(oldClassLoader));
152 
153       final Ejb3Configuration jpaConfiguration =
154           new Ejb3Configuration().configure(unitName, null);
155 
156       final Configuration cfg = jpaConfiguration.getHibernateConfiguration();
157       checkNamingStrategy(cfg, errorMessages);
158 
159       final SchemaExport export = new SchemaExport(cfg);
160       export.setDelimiter(delimiter);
161       export.setOutputFile(dropOutputFile);
162       export.execute(true, false, true, false);
163       export.setOutputFile(createOutputFile);
164       export.execute(true, false, false, true);
165     }
166     finally
167     {
168       System.setOut(oldOut);
169       // Now we can log again.
170 
171       logErrors(errorMessages);
172 
173       currentThread.setContextClassLoader(oldClassLoader);
174     }
175   }
176 
177   private void ensureOutputFolder(final String fileName)
178   {
179     final File file = new File(fileName);
180     final File dir = file.getParentFile();
181     if (!dir.exists())
182     {
183       dir.mkdirs();
184     }
185   }
186 
187   @SuppressWarnings("unchecked")
188   private ClassLoader getClassLoader(final ClassLoader delegate)
189   {
190     try
191     {
192       final List<String> classpathElements = new ArrayList<String>();
193       classpathElements.addAll(project.getCompileClasspathElements());
194       classpathElements.addAll(project.getRuntimeClasspathElements());
195       classpathElements.add(project.getBuild().getOutputDirectory());
196 
197       final URL urls[] = new URL[classpathElements.size()];
198       for (int i = 0; i < classpathElements.size(); ++i)
199       {
200         urls[i] = new File(classpathElements.get(i)).toURI().toURL();
201       }
202 
203       return new URLClassLoader(urls, delegate);
204     }
205     catch (final Exception e)
206     {
207       getLog().debug("Couldn't get the classloader.");
208       return this.getClass().getClassLoader();
209     }
210   }
211 
212   /**
213    * Checks whether the user wants to use a Hibernate naming strategy and sets
214    * it if yes.
215    *
216    * @param cfg The Hibernate configuration object.
217    * @param errorMessages The list where error messages will be stored.
218    */
219   private void checkNamingStrategy(final Configuration cfg,
220       final ArrayList<String> errorMessages)
221   {
222     try
223     {
224       if (namingStrategy != null && !namingStrategy.isEmpty())
225       {
226         cfg.setNamingStrategy((NamingStrategy) Class.forName(namingStrategy)
227             .newInstance());
228       }
229     }
230     catch (final InstantiationException e)
231     {
232       errorMessages
233           .add("hibernate4-maven-plugin cannot find the informed Hibernate naming strategy. Be sure that you informed the correct class name (InstantiationException).");
234       e.printStackTrace();
235     }
236     catch (final IllegalAccessException e)
237     {
238       errorMessages
239           .add("hibernate4-maven-plugin cannot find the informed Hibernate naming strategy. Be sure that you informed the correct class name (IllegalAccessException).");
240       e.printStackTrace();
241     }
242     catch (final ClassNotFoundException e)
243     {
244       errorMessages
245           .add("hibernate4-maven-plugin cannot find the informed Hibernate naming strategy. Be sure that you informed the correct class name (ClassNotFoundException).");
246       e.printStackTrace();
247     }
248   }
249 
250   /**
251    * Iterates and logs an <code>java.util.ArrayList&lt;String&gt;</code> of
252    * error messages using
253    * <code>"The following errors were found while executing hibernate4-maven-plugin:"</code>
254    * as introduction.
255    *
256    * @param errorMessages The error messages list.
257    */
258   private final void logErrors(final ArrayList<String> errorMessages)
259   {
260     if (errorMessages != null && !errorMessages.isEmpty())
261     {
262       getLog()
263           .info(
264               "### The following errors were found while executing hibernate4-maven-plugin:");
265 
266       for (final String errorMessage : errorMessages)
267       {
268         getLog().info(" - " + errorMessage);
269       }
270     }
271   }
272 
273   // --- object basics --------------------------------------------------------
274 
275 }