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