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