1 package ejava.utils.jpa;
2
3
4 import java.io.File;
5 import java.io.IOException;
6 import java.net.MalformedURLException;
7 import java.net.URL;
8 import java.net.URLClassLoader;
9 import java.util.Arrays;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Map;
14
15 import javax.persistence.Persistence;
16
17 import org.apache.maven.artifact.DependencyResolutionRequiredException;
18 import org.apache.maven.plugin.AbstractMojo;
19 import org.apache.maven.plugin.MojoFailureException;
20 import org.apache.maven.plugins.annotations.Execute;
21 import org.apache.maven.plugins.annotations.LifecyclePhase;
22 import org.apache.maven.plugins.annotations.Mojo;
23 import org.apache.maven.plugins.annotations.Parameter;
24 import org.apache.maven.plugins.annotations.ResolutionScope;
25 import org.apache.maven.project.MavenProject;
26 import org.hibernate.cfg.AvailableSettings;
27 import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
28 import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
29
30
31
32
33
34
35
36 @Mojo( name = "generate", defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES , requiresDependencyResolution = ResolutionScope.TEST, threadSafe=true )
37 @Execute(phase=LifecyclePhase.TEST_COMPILE)
38 public class JPASchemaGenMojo extends AbstractMojo {
39
40
41
42
43
44
45 @Parameter( property = "persistenceUnit", required=false)
46 private String persistenceUnit;
47
48
49
50
51 @Parameter( property = "createPath", required=false, defaultValue="target/classes/ddl/${persistenceUnit}-create.ddl")
52 private String createPath;
53
54
55
56
57 @Parameter( property = "dropPath", required=false, defaultValue="target/classes/ddl/${persistenceUnit}-drop.ddl")
58 private String dropPath;
59
60
61
62
63 @Parameter( property = "delimiter", required=false, defaultValue=";")
64 private String delimiter;
65
66 @Parameter( property = "format", required=false, defaultValue="true")
67 private boolean format;
68
69 @Parameter( property = "scriptsAction", required=false, defaultValue="drop-and-create")
70 private String scriptsAction;
71
72
73
74
75
76
77
78
79
80 @Parameter( property = "schemagenUrl", required=false, defaultValue="jdbc:h2:mem:")
81 private String schemagenUrl;
82
83
84
85
86 @Parameter( property = "schemagenUser", required=false, defaultValue="")
87 private String schemagenUser;
88
89
90
91
92 @Parameter( property = "schemagenPassword", required=false, defaultValue="")
93 private String schemagenPassword;
94
95
96
97
98
99 @Parameter( property = "schemagenDriver", required=false, defaultValue="org.h2.Driver")
100 private String schemagenDriver;
101
102
103
104
105 @Parameter( property = "project", required=true, defaultValue = "${project}" )
106 private MavenProject project;
107
108 protected URLClassLoader getClassLoader() throws DependencyResolutionRequiredException, MalformedURLException {
109 List<String> elements = project.getTestClasspathElements();
110 URL[] urls = new URL[elements.size()];
111 for (int i=0; i<elements.size(); i++) {
112 String path = elements.get(i);
113 URL url = new File(path).toURI().toURL();
114 urls[i] = url;
115 }
116 URLClassLoader classLoader = new URLClassLoader(urls, getClass().getClassLoader());
117 for (URL url: classLoader.getURLs()) {
118 getLog().debug("url=" + url.toString());
119 }
120 return classLoader;
121 }
122
123 protected String resolvePath(String path) {
124 path = path.replace("${persistenceUnit}", persistenceUnit);
125 File f = new File(path);
126
127 return !f.isAbsolute() ? project.getBasedir() + File.separator + path : path;
128 }
129
130 protected Map<String, Object> configure() {
131 Map<String, Object> properties = new HashMap<>();
132 properties.put(AvailableSettings.HBM2DDL_SCRIPTS_ACTION, scriptsAction);
133 properties.put(AvailableSettings.HBM2DDL_SCRIPTS_CREATE_TARGET, resolvePath(createPath));
134 properties.put(AvailableSettings.HBM2DDL_SCRIPTS_DROP_TARGET, resolvePath(dropPath));
135 properties.put(AvailableSettings.HBM2DDL_DELIMITER, delimiter);
136 properties.put(AvailableSettings.FORMAT_SQL, Boolean.valueOf(format).toString());
137 if (schemagenUrl!=null && !schemagenUrl.trim().isEmpty()) {
138 properties.put(AvailableSettings.JPA_JDBC_URL, schemagenUrl);
139 properties.put(AvailableSettings.JPA_JDBC_USER, schemagenUser);
140 properties.put(AvailableSettings.JPA_JDBC_PASSWORD, schemagenPassword);
141 properties.put(AvailableSettings.JPA_JDBC_DRIVER, schemagenDriver);
142 }
143 return properties;
144 }
145
146 public void execute() throws MojoFailureException {
147 URLClassLoader classLoader = null;
148 try {
149 classLoader = getClassLoader();
150 this.persistenceUnit = findPersistenceUnit(classLoader);
151 getLog().info("Generating database schema for: " + persistenceUnit);
152
153 Thread.currentThread().setContextClassLoader(classLoader);
154 URL pxml = classLoader.getResource("META-INF/persistence.xml");
155 URL hprops = classLoader.getResource("hibernate.properties");
156 getLog().debug("META-INF/persistence.xml found= " + (pxml!=null ? pxml : "false"));
157 getLog().debug("hibernate.properties found= " + (hprops!=null ? hprops : "false"));
158
159 Map<String, Object> properties = configure();
160 properties.forEach((k,v) -> getLog().debug(k + "=" + v));
161
162
163 for (String prop: Arrays.asList(AvailableSettings.HBM2DDL_SCRIPTS_DROP_TARGET, AvailableSettings.HBM2DDL_SCRIPTS_CREATE_TARGET)) {
164 String path = (String)properties.get(prop);
165 if (path!=null && path.toLowerCase().contains("target")) {
166 File f = new File(path);
167 if (f.exists()) {
168 getLog().info("removing existing target file:" + f.getPath());
169 f.delete();
170 }
171
172 boolean created = f.getParentFile().mkdirs();
173 if (created) {
174 getLog().info("created missing schema target directory: " + f.getParent());
175 }
176 }
177 }
178
179 Persistence.generateSchema(persistenceUnit, properties);
180 loadBeforeClosing();
181 } catch (DependencyResolutionRequiredException | MalformedURLException e) {
182 throw new MojoFailureException(e.toString());
183 } finally {
184 if (classLoader!=null) {
185 try { classLoader.close(); } catch (IOException e) {}
186 }
187 }
188 }
189
190 protected String findPersistenceUnit(ClassLoader clsLoader) throws MojoFailureException {
191 if (persistenceUnit!=null) {
192 return persistenceUnit;
193 }
194 Map<String, Object> properties = new HashMap<>();
195 properties.put(AvailableSettings.CLASSLOADERS, Collections.singletonList(clsLoader));
196 List<ParsedPersistenceXmlDescriptor> units = PersistenceXmlParser.locatePersistenceUnits(properties);
197 if (units.size()==1) {
198 return units.get(0).getName();
199 } else if (units.isEmpty()) {
200 throw new MojoFailureException("no persistenceUnit name specified and none found");
201 } else {
202 StringBuilder names = new StringBuilder();
203 units.forEach(n -> {
204 if (names.length()>0) { names.append(", "); }
205 names.append(n.getName());
206 });
207 throw new MojoFailureException(String.format("too many persistence units found[%s], specify persistenceUnit name to use", names));
208 }
209 }
210
211
212
213
214
215
216
217 protected void loadBeforeClosing() throws MojoFailureException {
218 for (String cls : new String[] {
219 "org.h2.mvstore.WriteBuffer",
220 "org.h2.mvstore.MVMap$2",
221 "org.h2.mvstore.MVMap$2$1",
222 "org.h2.mvstore.DataUtils$MapEntry",
223 "org.h2.mvstore.Chunk"
224 }) {
225 try {
226 Thread.currentThread().getContextClassLoader().loadClass(cls);
227 } catch (ClassNotFoundException ex) {
228 getLog().info("error pre-loading class[" + cls + "]: "+ ex.toString());
229
230 }
231 }
232
233 }
234
235 public void setPersistenceUnit(String puName) {
236 this.persistenceUnit = puName;
237 }
238 public void setProject(MavenProject project) {
239 this.project = project;
240 }
241
242 public void setCreatePath(String createPath) {
243 this.createPath = createPath;
244 }
245
246 public void setDropPath(String dropPath) {
247 this.dropPath = dropPath;
248 }
249
250 public void setDelimiter(String delimiter) {
251 this.delimiter = delimiter;
252 }
253
254 public void setFormat(boolean format) {
255 this.format = format;
256 }
257
258 public void setScriptsAction(String scriptsAction) {
259 this.scriptsAction = scriptsAction;
260 }
261
262 }