1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
package com.javaforge.bobber.plugin.archetype; |
18 | |
|
19 | |
import com.javaforge.bobber.archetype.model.Template; |
20 | |
import com.javaforge.bobber.archetype.model.Variable; |
21 | |
import com.javaforge.bobber.archetype.model.io.xpp3.BobberArchetypeXpp3Reader; |
22 | |
|
23 | |
import java.io.File; |
24 | |
import java.io.FileOutputStream; |
25 | |
import java.io.FileWriter; |
26 | |
import java.io.IOException; |
27 | |
import java.io.InputStream; |
28 | |
import java.io.InputStreamReader; |
29 | |
import java.io.StringWriter; |
30 | |
import java.io.Writer; |
31 | |
import java.net.MalformedURLException; |
32 | |
import java.net.URL; |
33 | |
import java.net.URLClassLoader; |
34 | |
import java.util.ArrayList; |
35 | |
import java.util.Enumeration; |
36 | |
import java.util.Iterator; |
37 | |
import java.util.List; |
38 | |
import java.util.Map; |
39 | |
import java.util.StringTokenizer; |
40 | |
import java.util.jar.JarEntry; |
41 | |
import java.util.jar.JarFile; |
42 | |
import java.util.zip.ZipEntry; |
43 | |
|
44 | |
import org.apache.maven.archetype.Archetype; |
45 | |
import org.apache.maven.archetype.ArchetypeDescriptorException; |
46 | |
import org.apache.maven.archetype.ArchetypeNotFoundException; |
47 | |
import org.apache.maven.archetype.ArchetypeTemplateProcessingException; |
48 | |
import org.apache.maven.artifact.Artifact; |
49 | |
import org.apache.maven.artifact.factory.ArtifactFactory; |
50 | |
import org.apache.maven.artifact.repository.ArtifactRepository; |
51 | |
import org.apache.maven.artifact.resolver.ArtifactResolutionException; |
52 | |
import org.apache.maven.artifact.resolver.ArtifactResolver; |
53 | |
import org.apache.maven.settings.MavenSettingsBuilder; |
54 | |
import org.apache.velocity.VelocityContext; |
55 | |
import org.codehaus.plexus.components.interactivity.InputHandler; |
56 | |
import org.codehaus.plexus.logging.AbstractLogEnabled; |
57 | |
import org.codehaus.plexus.util.FileUtils; |
58 | |
import org.codehaus.plexus.util.IOUtil; |
59 | |
import org.codehaus.plexus.util.StringUtils; |
60 | |
import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
61 | |
import org.codehaus.plexus.velocity.VelocityComponent; |
62 | |
|
63 | |
|
64 | |
|
65 | |
|
66 | 0 | public class BobberArchetype |
67 | |
extends AbstractLogEnabled |
68 | |
implements Archetype |
69 | |
{ |
70 | |
|
71 | 0 | private static final String NEW_LINE = System.getProperty("line.separator"); |
72 | |
|
73 | |
|
74 | |
|
75 | |
|
76 | |
|
77 | |
|
78 | |
private VelocityComponent velocity; |
79 | |
|
80 | |
|
81 | |
private ArtifactResolver artifactResolver; |
82 | |
|
83 | |
|
84 | |
private InputHandler inputHandler; |
85 | |
|
86 | |
|
87 | |
private ArtifactFactory artifactFactory; |
88 | |
|
89 | |
|
90 | |
|
91 | |
private MavenSettingsBuilder settingsBuilder; |
92 | |
private static final int MESSAGE_LINE_LENGTH = 80; |
93 | |
|
94 | |
|
95 | |
public void createArchetype(String archetypeGroupId, String archetypeArtifactId, String archetypeVersion, |
96 | |
ArtifactRepository localRepository, List remoteRepositories, Map parameters) |
97 | |
throws ArchetypeNotFoundException, ArchetypeDescriptorException, ArchetypeTemplateProcessingException |
98 | |
{ |
99 | |
|
100 | |
|
101 | |
|
102 | |
|
103 | 0 | Artifact archetypeArtifact = artifactFactory.createArtifact(archetypeGroupId, archetypeArtifactId, |
104 | |
archetypeVersion, Artifact.SCOPE_RUNTIME, "jar"); |
105 | |
|
106 | |
try |
107 | |
{ |
108 | 0 | artifactResolver.resolve(archetypeArtifact, remoteRepositories, localRepository); |
109 | |
} |
110 | 0 | catch (ArtifactResolutionException e) |
111 | |
{ |
112 | 0 | throw new ArchetypeDescriptorException("Error attempting to download archetype: " + e.getMessage(), e); |
113 | 0 | } |
114 | |
|
115 | |
|
116 | |
|
117 | |
|
118 | |
|
119 | 0 | BobberArchetypeXpp3Reader builder = new BobberArchetypeXpp3Reader(); |
120 | |
|
121 | |
com.javaforge.bobber.archetype.model.BobberArchetype archetype; |
122 | |
|
123 | |
JarFile archetypeJarFile; |
124 | |
|
125 | |
try |
126 | |
{ |
127 | |
|
128 | 0 | archetypeJarFile = new JarFile(archetypeArtifact.getFile()); |
129 | |
|
130 | 0 | final ZipEntry zipEntry = archetypeJarFile.getEntry(ARCHETYPE_DESCRIPTOR); |
131 | 0 | InputStream is = archetypeJarFile.getInputStream(zipEntry); |
132 | |
|
133 | 0 | if (is == null) |
134 | |
{ |
135 | 0 | throw new ArchetypeDescriptorException( |
136 | |
"The " + ARCHETYPE_DESCRIPTOR + " descriptor cannot be found."); |
137 | |
} |
138 | |
|
139 | 0 | archetype = builder.read(new InputStreamReader(is)); |
140 | |
|
141 | 0 | archetypeJarFile.close(); |
142 | |
} |
143 | 0 | catch (IOException e) |
144 | |
{ |
145 | 0 | throw new ArchetypeDescriptorException("Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e); |
146 | |
} |
147 | 0 | catch (XmlPullParserException e) |
148 | |
{ |
149 | 0 | throw new ArchetypeDescriptorException("Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e); |
150 | 0 | } |
151 | |
|
152 | |
|
153 | |
|
154 | |
|
155 | |
|
156 | 0 | String basedir = (String) parameters.get("basedir"); |
157 | |
|
158 | 0 | String artifactId = (String) parameters.get("artifactId"); |
159 | |
|
160 | 0 | File pomFile = new File(basedir, ARCHETYPE_POM); |
161 | |
|
162 | |
File outputDirectoryFile; |
163 | |
|
164 | 0 | if (pomFile.exists() && archetype.isAllowPartial()) |
165 | |
{ |
166 | 0 | outputDirectoryFile = new File(basedir); |
167 | |
} |
168 | |
else |
169 | |
{ |
170 | 0 | outputDirectoryFile = new File(basedir, artifactId); |
171 | |
|
172 | |
|
173 | 0 | if (!archetype.isAllowPartial() && |
174 | |
outputDirectoryFile.exists() && |
175 | |
outputDirectoryFile.listFiles().length > 0) |
176 | |
{ |
177 | 0 | throw new ArchetypeTemplateProcessingException( |
178 | |
outputDirectoryFile.getName() + " already exists - please run from a clean directory"); |
179 | |
} |
180 | |
|
181 | 0 | outputDirectoryFile.mkdir(); |
182 | |
|
183 | |
} |
184 | |
|
185 | 0 | String outputDirectory = outputDirectoryFile.getAbsolutePath(); |
186 | |
|
187 | |
|
188 | |
|
189 | |
|
190 | |
|
191 | 0 | VelocityContext context = new VelocityContext(); |
192 | |
|
193 | 0 | String packageName = (String) parameters.get("package"); |
194 | |
|
195 | 0 | addParamToContext("package", packageName, context); |
196 | 0 | addParamToContext("packagePath", StringUtils.replace(packageName, ".", "/"), context); |
197 | |
|
198 | 0 | for (Iterator iterator = parameters.keySet().iterator(); iterator.hasNext();) |
199 | |
{ |
200 | 0 | String key = (String) iterator.next(); |
201 | |
|
202 | 0 | Object value = parameters.get(key); |
203 | 0 | addParamToContext(key, value, context); |
204 | 0 | } |
205 | |
|
206 | |
|
207 | |
|
208 | |
|
209 | |
|
210 | 0 | boolean inInteractiveMode = false; |
211 | |
try |
212 | |
{ |
213 | 0 | inInteractiveMode = settingsBuilder.buildSettings().getInteractiveMode().booleanValue(); |
214 | |
|
215 | 0 | String temp = System.getProperty("interactive", null); |
216 | 0 | if (temp != null) |
217 | |
{ |
218 | 0 | inInteractiveMode = Boolean.valueOf(temp).booleanValue(); |
219 | |
} |
220 | 0 | getLogger().info("Interactive is: " + inInteractiveMode); |
221 | |
} |
222 | 0 | catch (Exception ie) |
223 | |
{ |
224 | 0 | throw new ArchetypeTemplateProcessingException("unable to read settings ", ie); |
225 | 0 | } |
226 | 0 | if (inInteractiveMode) |
227 | |
{ |
228 | 0 | getLogger().info("Please enter the values for the following archetype variables:"); |
229 | |
} |
230 | |
|
231 | |
|
232 | 0 | final List variables = archetype.getVariables(); |
233 | 0 | processVariables(variables.iterator(), context, inInteractiveMode); |
234 | |
|
235 | |
|
236 | |
|
237 | |
|
238 | 0 | if (getLogger().isInfoEnabled()) |
239 | |
{ |
240 | 0 | Object[] keys = context.getKeys(); |
241 | 0 | if (keys.length > 0) |
242 | |
{ |
243 | 0 | getLogger().info("----------------------------------------------------------------------------"); |
244 | |
|
245 | 0 | getLogger().info("Using following parameters for creating Archetype: " + archetypeArtifactId + ":" + |
246 | |
archetypeVersion); |
247 | |
|
248 | 0 | getLogger().info("----------------------------------------------------------------------------"); |
249 | |
|
250 | 0 | for (int i = 0; i < keys.length; i++) |
251 | |
{ |
252 | |
|
253 | 0 | String parameterName = (String) keys[i]; |
254 | |
|
255 | 0 | Object parameterValue = context.get(parameterName); |
256 | |
|
257 | 0 | getLogger().info("Parameter: " + parameterName + " = " + parameterValue); |
258 | |
} |
259 | |
} |
260 | |
else |
261 | |
{ |
262 | 0 | getLogger().info("No Parameters found for creating Archetype"); |
263 | |
} |
264 | |
} |
265 | |
|
266 | |
|
267 | |
|
268 | |
|
269 | |
|
270 | |
try |
271 | |
{ |
272 | 0 | archetypeJarFile = new JarFile(archetypeArtifact.getFile()); |
273 | 0 | Enumeration entries = archetypeJarFile.entries(); |
274 | |
|
275 | 0 | while (entries.hasMoreElements()) |
276 | |
{ |
277 | 0 | JarEntry entry = (JarEntry) entries.nextElement(); |
278 | 0 | String path = entry.getName(); |
279 | 0 | if (!path.startsWith(ARCHETYPE_RESOURCES) || path.endsWith(".vm")) |
280 | |
{ |
281 | 0 | continue; |
282 | |
} |
283 | |
|
284 | 0 | File t = new File(outputDirectory, path.substring(19)); |
285 | 0 | if (entry.isDirectory()) |
286 | |
{ |
287 | |
|
288 | 0 | getLogger().debug("Extracting directory: " + entry.getName() + " to " + t.getAbsolutePath()); |
289 | 0 | t.mkdir(); |
290 | 0 | continue; |
291 | |
} |
292 | |
|
293 | 0 | getLogger().debug("Extracting file: " + entry.getName() + " to " + t.getAbsolutePath()); |
294 | 0 | t.createNewFile(); |
295 | 0 | IOUtil.copy(archetypeJarFile.getInputStream(entry), new FileOutputStream(t)); |
296 | 0 | } |
297 | |
|
298 | 0 | archetypeJarFile.close(); |
299 | |
|
300 | |
|
301 | 0 | File t = new File(outputDirectory, ARCHETYPE_DESCRIPTOR); |
302 | 0 | t.delete(); |
303 | |
} |
304 | 0 | catch (IOException ioe) |
305 | |
{ |
306 | 0 | throw new ArchetypeTemplateProcessingException("Error extracting archetype", ioe); |
307 | 0 | } |
308 | |
|
309 | |
|
310 | |
|
311 | |
|
312 | |
|
313 | |
|
314 | |
|
315 | 0 | ClassLoader old = Thread.currentThread().getContextClassLoader(); |
316 | |
try |
317 | |
{ |
318 | 0 | URL[] urls = new URL[1]; |
319 | 0 | urls[0] = archetypeArtifact.getFile().toURI().toURL(); |
320 | 0 | URLClassLoader archetypeJarLoader = new URLClassLoader(urls); |
321 | |
|
322 | 0 | Thread.currentThread().setContextClassLoader(archetypeJarLoader); |
323 | 0 | for (Iterator i = archetype.getTemplates().iterator(); i.hasNext();) |
324 | |
{ |
325 | 0 | final Template template = (Template) i.next(); |
326 | |
|
327 | |
|
328 | |
|
329 | |
|
330 | |
|
331 | |
|
332 | 0 | boolean shouldProcess = true; |
333 | 0 | String condition = template.getDependsOnVar(); |
334 | 0 | String requiredValue = null; |
335 | 0 | List options = new ArrayList(); |
336 | 0 | if (StringUtils.isNotEmpty(condition)) |
337 | |
{ |
338 | |
|
339 | 0 | boolean not = false; |
340 | |
|
341 | 0 | int x = condition.indexOf("!="); |
342 | 0 | getLogger().debug("Processing Condition : " + condition); |
343 | 0 | if (x > -1) |
344 | |
{ |
345 | 0 | not = true; |
346 | 0 | requiredValue = condition.substring(x + 2).trim(); |
347 | 0 | options = getListOfValues(requiredValue); |
348 | 0 | condition = condition.substring(0, x).trim(); |
349 | |
} |
350 | |
else |
351 | |
{ |
352 | 0 | x = condition.indexOf("="); |
353 | 0 | if (x > -1) |
354 | |
{ |
355 | 0 | requiredValue = condition.substring(x + 1); |
356 | 0 | options = getListOfValues(requiredValue); |
357 | 0 | condition = condition.substring(0, x); |
358 | |
} |
359 | |
} |
360 | 0 | getLogger().debug("Not Expr: " + not); |
361 | 0 | getLogger().debug("Condition Value: '" + condition + "'"); |
362 | 0 | getLogger().debug("Required Value: '" + requiredValue + "'"); |
363 | 0 | final Variable var = (Variable) findVariable(condition, variables); |
364 | 0 | if (var != null) |
365 | |
{ |
366 | 0 | final String strValue = (String) context.get(var.getName()); |
367 | 0 | getLogger().debug("Variable Value is: '" + strValue + "'"); |
368 | 0 | if (requiredValue == null) |
369 | |
{ |
370 | 0 | if (!Boolean.valueOf(strValue).booleanValue()) |
371 | |
{ |
372 | 0 | shouldProcess = false; |
373 | |
} |
374 | |
} |
375 | |
else |
376 | |
{ |
377 | 0 | if (!options.contains(strValue)) |
378 | |
{ |
379 | 0 | shouldProcess = false; |
380 | |
} |
381 | |
} |
382 | |
|
383 | 0 | } |
384 | |
else |
385 | |
{ |
386 | 0 | getLogger().debug("Variable Value is: null"); |
387 | 0 | shouldProcess = false; |
388 | |
} |
389 | 0 | if (not) |
390 | |
{ |
391 | 0 | shouldProcess = !shouldProcess; |
392 | |
} |
393 | |
} |
394 | |
|
395 | 0 | if (shouldProcess) |
396 | |
{ |
397 | 0 | processTemplate(template, outputDirectory, context); |
398 | |
} |
399 | |
else |
400 | |
{ |
401 | 0 | getLogger().debug("Condition not met, skipping " + template.getOutput()); |
402 | |
} |
403 | 0 | } |
404 | |
|
405 | |
} |
406 | 0 | catch (MalformedURLException mfe) |
407 | |
{ |
408 | 0 | throw new ArchetypeTemplateProcessingException("Error loading archetype resources into the classpath", mfe); |
409 | |
} |
410 | |
finally |
411 | |
{ |
412 | 0 | Thread.currentThread().setContextClassLoader(old); |
413 | 0 | } |
414 | |
|
415 | |
|
416 | |
|
417 | |
|
418 | 0 | if (getLogger().isInfoEnabled()) |
419 | |
{ |
420 | 0 | getLogger().info("Archetype created in dir: " + outputDirectory); |
421 | |
} |
422 | |
|
423 | 0 | } |
424 | |
|
425 | |
protected void addParamToContext(String key, Object value, VelocityContext context) |
426 | |
{ |
427 | 0 | getLogger().info("Adding Parameter to template Context: " + key + "=" + value); |
428 | 0 | context.put(key, value); |
429 | |
|
430 | 0 | } |
431 | |
|
432 | |
protected void processVariables(Iterator variables, VelocityContext context, final boolean interactiveMode) throws ArchetypeTemplateProcessingException |
433 | |
{ |
434 | 0 | while (variables.hasNext()) |
435 | |
{ |
436 | 0 | Variable var = (Variable) variables.next(); |
437 | 0 | String val = System.getProperty(var.getName(), var.getDefvalue()); |
438 | |
|
439 | 0 | if (interactiveMode) |
440 | |
{ |
441 | |
|
442 | 0 | StringBuffer message = new StringBuffer(); |
443 | 0 | message.append(var.getName()).append(": ") |
444 | |
.append(NEW_LINE) |
445 | |
.append(StringUtils.repeat("*", MESSAGE_LINE_LENGTH)) |
446 | |
.append(NEW_LINE) |
447 | |
.append(NEW_LINE) |
448 | |
.append(StringUtils.center(var.getDescription(), MESSAGE_LINE_LENGTH)) |
449 | |
.append(NEW_LINE) |
450 | |
.append(StringUtils.leftPad("[default: " + val + "]", MESSAGE_LINE_LENGTH)) |
451 | |
.append(NEW_LINE) |
452 | |
.append(StringUtils.repeat("*", MESSAGE_LINE_LENGTH)); |
453 | 0 | getLogger().info(message.toString()); |
454 | |
try |
455 | |
{ |
456 | 0 | String answer = inputHandler.readLine(); |
457 | 0 | if (!StringUtils.isEmpty(answer)) |
458 | |
{ |
459 | 0 | val = answer; |
460 | |
} |
461 | |
} |
462 | 0 | catch (IOException ie) |
463 | |
{ |
464 | 0 | throw new ArchetypeTemplateProcessingException(ie); |
465 | 0 | } |
466 | 0 | context.put(var.getName(), val); |
467 | 0 | } |
468 | |
else |
469 | |
{ |
470 | 0 | context.put(var.getName(), val); |
471 | |
} |
472 | |
|
473 | 0 | if (val.toLowerCase().equals("false") || val.toLowerCase().equals("n")) |
474 | |
{ |
475 | 0 | if (var.getVariables() != null) |
476 | |
{ |
477 | |
|
478 | 0 | processVariables(var.getVariables().iterator(), context, false); |
479 | |
|
480 | |
} |
481 | |
} |
482 | 0 | else if (var.getVariables() != null) |
483 | |
{ |
484 | |
|
485 | 0 | processVariables(var.getVariables().iterator(), context, interactiveMode); |
486 | |
|
487 | |
} |
488 | 0 | } |
489 | |
|
490 | 0 | } |
491 | |
|
492 | |
protected List getListOfValues(String s) |
493 | |
{ |
494 | 0 | List options = new ArrayList(); |
495 | 0 | for (StringTokenizer stringTokenizer = new StringTokenizer(s, "|"); stringTokenizer.hasMoreTokens();) |
496 | |
{ |
497 | 0 | options.add(stringTokenizer.nextToken()); |
498 | |
} |
499 | 0 | return options; |
500 | |
} |
501 | |
|
502 | |
protected void processTemplate(Template template, String outputDirectory, VelocityContext context) |
503 | |
throws ArchetypeTemplateProcessingException |
504 | |
{ |
505 | |
File outFile; |
506 | |
|
507 | |
|
508 | |
try |
509 | |
{ |
510 | 0 | StringWriter wout = new StringWriter(); |
511 | |
|
512 | 0 | velocity.getEngine().evaluate(context, wout, "output value", template.getOutput()); |
513 | 0 | outFile = new File(outputDirectory, wout.toString()); |
514 | 0 | getLogger().debug(outFile.getAbsolutePath()); |
515 | 0 | FileUtils.forceMkdir(outFile.getParentFile()); |
516 | 0 | getLogger().debug("Created directory: " + outFile.getParentFile() + ", Dir exists = " + outFile.getParentFile().exists()); |
517 | |
|
518 | |
} |
519 | 0 | catch (Exception e) |
520 | |
{ |
521 | 0 | e.printStackTrace(); |
522 | 0 | throw new ArchetypeTemplateProcessingException("error evaluating output file name " + template.getOutput(), e); |
523 | 0 | } |
524 | |
|
525 | |
|
526 | 0 | Writer writer = null; |
527 | |
try |
528 | |
{ |
529 | 0 | getLogger().info("Processing Template: " + template.getFile()); |
530 | 0 | String templateLocation = ARCHETYPE_RESOURCES + "/" + template.getFile(); |
531 | |
|
532 | 0 | writer = new FileWriter(outFile); |
533 | 0 | velocity.getEngine().mergeTemplate(templateLocation, context, writer); |
534 | 0 | writer.flush(); |
535 | |
|
536 | |
} |
537 | 0 | catch (Exception e) |
538 | |
{ |
539 | 0 | throw new ArchetypeTemplateProcessingException("Error merging velocity templates", e); |
540 | |
} |
541 | |
finally |
542 | |
{ |
543 | 0 | IOUtil.close(writer); |
544 | 0 | getLogger().info("Written Template to: " + outFile + ", file exists = " + outFile.exists()); |
545 | 0 | } |
546 | |
|
547 | |
|
548 | |
|
549 | |
try |
550 | |
{ |
551 | 0 | final File templateFile = new File(outputDirectory, template.getFile()); |
552 | 0 | final String templateDir = FileUtils.dirname(templateFile.getCanonicalPath()); |
553 | 0 | final String outputDir = FileUtils.dirname(outFile.getCanonicalPath()); |
554 | 0 | if (getLogger().isDebugEnabled()) |
555 | |
{ |
556 | 0 | getLogger().debug("TemplateDir=" + templateDir); |
557 | 0 | getLogger().debug("OutputDir=" + outputDir); |
558 | |
} |
559 | 0 | if (!outputDir.startsWith(templateDir)) |
560 | |
{ |
561 | 0 | getLogger().debug("Deleting Template Dir:" + templateDir); |
562 | 0 | FileUtils.forceDelete(templateDir); |
563 | |
} |
564 | |
} |
565 | 0 | catch (IOException e) |
566 | |
{ |
567 | 0 | throw new ArchetypeTemplateProcessingException("Failed to cleanup the working dir.", e); |
568 | 0 | } |
569 | 0 | } |
570 | |
|
571 | |
|
572 | |
|
573 | |
|
574 | |
|
575 | |
|
576 | |
|
577 | |
|
578 | |
|
579 | |
protected Object findVariable(String variableName, List variables) |
580 | |
{ |
581 | |
|
582 | 0 | for (int i = 0; i < variables.size(); i++) |
583 | |
{ |
584 | 0 | Variable var = (Variable) variables.get(i); |
585 | 0 | if (variableName.equals(var.getName())) |
586 | |
{ |
587 | 0 | return var; |
588 | |
} |
589 | 0 | else if (var.getVariables() != null) |
590 | |
{ |
591 | 0 | Object o = findVariable(variableName, var.getVariables()); |
592 | 0 | if (o != null) |
593 | |
{ |
594 | 0 | return o; |
595 | |
} |
596 | |
} |
597 | |
} |
598 | |
|
599 | 0 | return null; |
600 | |
} |
601 | |
|
602 | |
} |
603 | |
|
604 | |
|
605 | |
|