View Javadoc

1   package org.appfuse.mojo.installer;
2   
3   import java.io.File;
4   import java.io.FileWriter;
5   import java.io.IOException;
6   import java.io.StringWriter;
7   import java.net.MalformedURLException;
8   import java.net.URL;
9   import java.util.ArrayList;
10  import java.util.Collections;
11  import java.util.LinkedHashMap;
12  import java.util.LinkedHashSet;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.Properties;
16  import java.util.Set;
17  import java.util.TreeSet;
18  
19  import org.apache.commons.beanutils.BeanComparator;
20  import org.apache.commons.io.FileUtils;
21  import org.apache.maven.artifact.Artifact;
22  import org.apache.maven.embedder.MavenEmbedder;
23  import org.apache.maven.embedder.MavenEmbedderConsoleLogger;
24  import org.apache.maven.model.Dependency;
25  import org.apache.maven.plugin.AbstractMojo;
26  import org.apache.maven.plugin.MojoExecutionException;
27  import org.apache.maven.plugin.MojoFailureException;
28  import org.apache.maven.project.MavenProject;
29  import org.apache.tools.ant.Project;
30  import org.apache.tools.ant.taskdefs.Get;
31  import org.apache.tools.ant.taskdefs.LoadFile;
32  import org.apache.tools.ant.taskdefs.Move;
33  import org.apache.tools.ant.types.FileSet;
34  import org.appfuse.tool.RenamePackages;
35  import org.appfuse.tool.SubversionUtils;
36  import org.tmatesoft.svn.core.SVNErrorMessage;
37  import org.tmatesoft.svn.core.SVNException;
38  
39  
40  /**
41   * This mojo is used to "install" source artifacts from Subversion into an AppFuse project.
42   * If you get an OutOfMemoryError when running this plugin, you should be able to fix it
43   * by setting your MAVEN_OPTS environment variable to "-Xms128M -Xmx256M".
44   *
45   * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
46   * @goal full-source
47   */
48  public class InstallSourceMojo extends AbstractMojo {
49      private static final String APPFUSE_GROUP_ID = "org.appfuse";
50      private static final String FILE_SEP = System.getProperty("file.separator");
51      private static final String LINE_SEP = System.getProperty("line.separator");
52  
53      Project antProject = AntUtils.createProject();
54      Properties appfuseProperties;
55  
56      // ThreadLocale to hold properties as they're built when traversing through a modular project
57      private static final ThreadLocal<Map> propertiesContextHolder = new ThreadLocal<Map>();
58  
59  
60      /**
61       * The path where the files from SVN will be placed. This is intentionally set to "src" since that's the default
62       * src directory used for exporting AppFuse artifacts.
63       *
64       * @parameter expression="${appfuse.destinationDirectory}" default-value="${basedir}/src"
65       */
66      private String destinationDirectory;
67  
68      /**
69       * The directory containing the source code.
70       *
71       * @parameter expression="${appfuse.trunk}" default-value="https://svn.java.net/svn/appfuse~svn/"
72       */
73      private String trunk;
74  
75      /**
76       * The tag containing the source code - defaults to '/trunk', but you may want to set it to '/tags/TAGNAME'
77       *
78       * @parameter expression="${appfuse.tag}" default-value="trunk/"
79       */
80      private String tag;
81  
82      /**
83       * <i>Maven Internal</i>: Project to interact with.
84       *
85       * @parameter expression="${project}"
86       * @required
87       * @readonly
88       * @noinspection UnusedDeclaration
89       */
90      private MavenProject project;
91  
92      public void execute() throws MojoExecutionException, MojoFailureException {
93          // http://issues.appfuse.org/browse/APF-1025
94          System.setProperty("file.encoding", "UTF-8");
95  
96          // If appfuse.version is specified as a property, and not a SNAPSHOT, use it for the tag
97          String appfuseVersion = project.getProperties().getProperty("appfuse.version");
98  
99          if ((appfuseVersion != null) && !appfuseVersion.endsWith("SNAPSHOT") && tag.equals("trunk/")) {
100             tag = "tags/APPFUSE_" + appfuseVersion.toUpperCase().replaceAll("-", "_") + "/";
101             // APF-1168: Allow milestone and release candidates
102             if (tag.contains("_M")) {
103                 tag = tag.replace("_M", "-M");
104             } else if (tag.contains("_R")) {
105                 tag = tag.replace("_R", "-R");
106             }
107         }
108 
109         String daoFramework = project.getProperties().getProperty("dao.framework");
110 
111         if (daoFramework == null) {
112             log("No dao.framework property specified, defaulting to 'hibernate'");
113         }
114 
115         String webFramework = project.getProperties().getProperty("web.framework");
116 
117         boolean modular = (project.getPackaging().equals("pom") && !project.hasParent());
118         if (project.getPackaging().equals("jar") || (project.getPackaging().equals("war") && !project.hasParent())) {
119             // export data-common
120             log("Installing source from data-common module...");
121             export("data/common/src", (modular) ? "core/src" : destinationDirectory);
122 
123             // export persistence framework
124             log("Installing source from " + daoFramework + " module...");
125             export("data/" + daoFramework + "/src", (modular) ? "core/src" : destinationDirectory);
126 
127             // export service module
128             log("Installing source from service module...");
129             export("service/src", (modular) ? "core/src" : destinationDirectory);
130 
131             // move Base*TestCase to test directory
132             moveFiles((modular) ? "core/src/main" : destinationDirectory + "/main",
133                     (modular) ? "core/src/test" : destinationDirectory + "/test", "**/Base*TestCase.java");
134 
135             // delete dao.framework related files from test directory
136             deleteFile("test/resources/hibernate.cfg.xml");
137             deleteFile("test/resources/META-INF");
138             deleteFile("test/resources/sql-map-config.xml");
139 
140             // If JPA, delete hibernate.cfg.xml b/c it will cause issues when
141             // using jpaconfiguration with the hibernate3-maven-plugin
142             if ("jpa".equalsIgnoreCase(daoFramework)) {
143                 deleteFile("main/resources/hibernate.cfg.xml");
144             }
145         }
146 
147         // it's OK if a project created with appfuse-ws doesn't have a web framework defined
148         // currently, a project with appfuse-ws can be identified by enunciate
149         boolean isWebServicesProject = false;
150         for (Object pluginArtifact : project.getPluginArtifacts()) {
151             if (((Artifact) pluginArtifact).getArtifactId().contains("enunciate")) {
152                 isWebServicesProject = true;
153                 break;
154             }
155         }
156 
157         if (project.getPackaging().equalsIgnoreCase("war")) {
158 
159             if (webFramework == null && !isWebServicesProject) {
160                 getLog().error("The web.framework property is not specified - please modify your pom.xml to add " +
161                         " this property. For example: <web.framework>struts</web.framework>.");
162                 throw new MojoExecutionException("No web.framework property specified, please modify pom.xml to add it.");
163             }
164 
165             if (project.hasParent()) {
166                 // copy jdbc.properties to core/src/test/resources
167                 //FileUtils.copyFileToDirectory(new File("src/main/resources/jdbc.properties"), new File("../core/src/test/resources"));
168 
169                 // delete hibernate, ibatis and jpa files from web project
170                 deleteFile("main/resources/hibernate.cfg.xml");
171                 deleteFile("main/resources/META-INF");
172                 deleteFile("main/resources/sql-map-config.xml");
173 
174                 // there's a jdbc.properties in test/resources that shouldn't be there
175                 deleteFile("test/resources/jdbc.properties");
176             } else if (!isAppFuse()) {
177                 // there's a jdbc.properties in test/resources that shouldn't be there
178                 deleteFile("test/resources/jdbc.properties");
179             }
180         }
181 
182         log("Source successfully exported, modifying pom.xml...");
183 
184         List dependencies = project.getOriginalModel().getDependencies();
185         List<Dependency> newDependencies = new ArrayList<Dependency>();
186 
187         // remove all appfuse dependencies
188         for (Object dependency : dependencies) {
189             Dependency dep = (Dependency) dependency;
190 
191             if (!dep.getGroupId().equals(APPFUSE_GROUP_ID)) {
192                 newDependencies.add(dep);
193             }
194         }
195 
196         if (!project.getPackaging().equals("pom") && !project.hasParent()) {
197 
198             // add dependencies from root appfuse pom
199             newDependencies = addModuleDependencies(newDependencies, "root", "");
200 
201             // Add dependencies from appfuse-data
202             newDependencies = addModuleDependencies(newDependencies, "data", "data");
203 
204             // Add dependencies from appfuse-data-common
205             newDependencies = addModuleDependencies(newDependencies, "data-common", "data/common");
206 
207             // Add dependencies from appfuse-${dao.framework}
208             newDependencies = addModuleDependencies(newDependencies, daoFramework, "data/" + daoFramework);
209 
210             // Add dependencies from appfuse-service
211             newDependencies = addModuleDependencies(newDependencies, "service", "service");
212 
213             if (!isWebServicesProject && project.getPackaging().equals("war")) {
214                 newDependencies = addWebDependencies(appfuseVersion, newDependencies, webFramework);
215             }
216 
217             createFullSourcePom(newDependencies);
218         } else {
219             if (project.getPackaging().equals("pom")) {
220                 // add dependencies from root appfuse pom
221                 newDependencies = addModuleDependencies(newDependencies, "root", "");
222 
223                 createFullSourcePom(newDependencies);
224             }
225 
226             if (project.getPackaging().equals("jar")) {
227                 newDependencies.clear();
228 
229                 // Add dependencies from appfuse-data
230                 newDependencies = addModuleDependencies(newDependencies, "data", "data");
231 
232                 // Add dependencies from appfuse-data-common
233                 newDependencies = addModuleDependencies(newDependencies, "data-common", "data/common");
234 
235                 // Add dependencies from appfuse-${dao.framework}
236                 newDependencies = addModuleDependencies(newDependencies, daoFramework, "data/" + daoFramework);
237 
238                 // Add dependencies from appfuse-service
239                 newDependencies = addModuleDependencies(newDependencies, "service", "service");
240 
241                 createFullSourcePom(newDependencies);
242             }
243 
244             if (project.getPackaging().equals("war")) {
245                 newDependencies.clear();
246 
247                 newDependencies = addWebDependencies(appfuseVersion, newDependencies, webFramework);
248 
249                 createFullSourcePom(newDependencies);
250             }
251         }
252     }
253 
254     private List<Dependency> addWebDependencies(String appfuseVersion, List<Dependency> newDependencies, String webFramework) {
255         // Add dependencies from appfuse-common-web
256         newDependencies = addModuleDependencies(newDependencies, "web", "web");
257 
258         Double appfuseVersionAsDouble = new Double(appfuseVersion.substring(0, appfuseVersion.lastIndexOf(".")));
259 
260         getLog().debug("Detected AppFuse version: " + appfuseVersionAsDouble);
261 
262         if (isAppFuse() && appfuseVersionAsDouble < 2.1) {
263 
264             // Add dependencies from appfuse-common-web
265             newDependencies = addModuleDependencies(newDependencies, "web-common", "web/common");
266 
267             //newDependencies = addModuleDependencies(newDependencies, webFramework, "web/" + webFramework);
268         }
269 
270         // modular archetypes still seem to need these - todo: figure out why
271         if (isAppFuse() && project.getPackaging().equals("war") && project.hasParent()) {
272             newDependencies = addModuleDependencies(newDependencies, "web-common", "web/common");
273 
274             newDependencies = addModuleDependencies(newDependencies, webFramework, "web/" + webFramework);
275         }
276         return newDependencies;
277     }
278 
279     private boolean isAppFuse() {
280         return (project.getProperties().getProperty("copyright.year") != null);
281     }
282 
283     private void deleteFile(String filePath) {
284         if (!filePath.startsWith("/")) {
285             filePath = "/" + filePath;
286         }
287         File duplicateFile = new File(getFilePath(destinationDirectory + filePath));
288         try {
289             getLog().debug("Looking for duplicate file at '" + duplicateFile.getCanonicalPath());
290             if (duplicateFile.exists()) {
291                 getLog().debug("Deleting duplicate file at '" + duplicateFile.getCanonicalPath());
292                 if (duplicateFile.isDirectory()) {
293                     FileUtils.deleteDirectory(duplicateFile);
294                 } else {
295                     FileUtils.forceDeleteOnExit(duplicateFile);
296                 }
297             }
298         } catch (IOException io) {
299             getLog().error("Failed to delete '" + filePath + "', please delete manually.");
300         }
301     }
302 
303     private void createFullSourcePom(List<Dependency> newDependencies) throws MojoFailureException {
304         // Change spring-test and jmock dependencies to use <optional>true</option> instead of <scope>test</scope>.
305         // This is necessary because Base*TestCase classes are in src/main/java. If we move these classes to their
306         // own test module, this will no longer be necessary. For the first version of this mojo, it seems easier
307         // to follow the convention used in AppFuse rather than creating a test module and changing all modules to
308         // depend on it.
309 
310         // create properties based on dependencies while we're at it
311         Set<String> projectProperties = new TreeSet<String>();
312 
313         for (Dependency dep : newDependencies) {
314             if (dep.getArtifactId().equals("spring-test") || dep.getArtifactId().contains("jmock") ||
315                     dep.getArtifactId().equals("junit") || dep.getArtifactId().equals("shale-test")) {
316                 dep.setOptional(true);
317                 dep.setScope(null);
318             }
319             String version = dep.getVersion();
320             // trim off ${}
321             if (version.startsWith("${")) {
322                 version = version.substring(2);
323             }
324 
325             if (version.endsWith("}")) {
326                 version = version.substring(0, version.length() - 1);
327             }
328             projectProperties.add(version);
329         }
330 
331         // add core as a dependency for modular wars
332         if (project.getPackaging().equals("war") && project.hasParent()) {
333             Dependency core = new Dependency();
334             core.setGroupId("${project.parent.groupId}");
335             core.setArtifactId("core");
336             core.setVersion("${project.parent.version}");
337             newDependencies.add(core);
338 
339             // workaround for JSF requiring JSP 2.1 - this is a true hack
340             if (project.getProperties().getProperty("web.framework").equals("jsf")) {
341                 Dependency jsp21 = new Dependency();
342                 jsp21.setGroupId("javax.servlet.jsp");
343                 jsp21.setArtifactId("jsp-api");
344                 jsp21.setVersion("${jsp.version}");
345                 jsp21.setScope("provided");
346                 newDependencies.add(jsp21);
347 
348                 // replace jsp.version property as well
349                 project.getOriginalModel().getProperties().setProperty("jsp.version", "2.1");
350             }
351         }
352 
353         Collections.sort(newDependencies, new BeanComparator("groupId"));
354 
355         project.getOriginalModel().setDependencies(newDependencies);
356 
357         Properties currentProperties = project.getOriginalModel().getProperties();
358 
359         Set<String> currentKeys = new LinkedHashSet<String>();
360         for (Object key : currentProperties.keySet()) {
361             currentKeys.add((String) key);
362         }
363 
364         StringBuffer sortedProperties = new StringBuffer();
365 
366         Properties appfuseProperties = getAppFuseProperties();
367 
368         // holder for properties - stored in ThreadLocale
369         Map<String, String> propertiesForPom = new LinkedHashMap<String, String>();
370 
371         for (String key : projectProperties) {
372             // don't add property if it already exists in project
373             if (!currentKeys.contains(key)) {
374                 String value = appfuseProperties.getProperty(key);
375 
376                 // this happens when the version number is hard-coded
377                 if (value == null) {
378                     continue;
379                 }
380 
381                 // hack for Tapestry depending on commons-pool (a.k.a. commons-dbcp 1.2.2)
382                 if ("tapestry".equals(project.getProperties().getProperty("web.framework")) && key.equals("commons.dbcp.version")) {
383                     value = "1.2.2";
384                 }
385 
386                 if (value.contains("&amp;")) {
387                     value = "<![CDATA[" + value + "]]>";
388                 }
389 
390                 sortedProperties.append("        <").append(key).append(">")
391                         .append(value).append("</").append(key).append(">" + "\n");
392                 propertiesForPom.put(key, value);
393             }
394         }
395 
396         if (project.getPackaging().equals("pom") || project.hasParent()) {
397             // store sorted properties in a thread local for later retrieval
398             Map<String, String> properties = new LinkedHashMap<String, String>();
399             if (propertiesContextHolder.get() != null) {
400                 properties = (LinkedHashMap) propertiesContextHolder.get();
401             }
402 
403             for (String key : propertiesForPom.keySet()) {
404                 if (!properties.containsKey(key)) {
405                     properties.put(key, propertiesForPom.get(key));
406                 }
407             }
408 
409             propertiesContextHolder.set(properties);
410         }
411 
412         StringWriter writer = new StringWriter();
413 
414         try {
415             project.writeOriginalModel(writer);
416 
417             File pom = new File("pom-fullsource.xml");
418 
419             if (pom.exists()) {
420                 pom.delete();
421             }
422 
423             FileWriter fw = new FileWriter(pom);
424             fw.write(writer.toString());
425             fw.flush();
426             fw.close();
427         } catch (IOException ex) {
428             getLog().error("Unable to create pom-fullsource.xml: " + ex.getMessage(), ex);
429             throw new MojoFailureException(ex.getMessage());
430         }
431 
432         log("Updated dependencies in pom.xml...");
433 
434         // I tried to use regex here, but couldn't get it to work - going with the old fashioned way instead
435         String pomXml = writer.toString();
436         int startTag = pomXml.indexOf("\n  <dependencies>");
437 
438         String dependencyXml = pomXml.substring(startTag, pomXml.indexOf("</dependencies>", startTag));
439         // change 2 spaces to 4
440         dependencyXml = dependencyXml.replaceAll("  ", "    ");
441         dependencyXml = "\n    <!-- Dependencies calculated by AppFuse when running full-source plugin -->" + dependencyXml;
442 
443         try {
444             String packaging = project.getPackaging();
445             String pathToPom = "pom.xml";
446             if (project.hasParent()) {
447                 if (packaging.equals("jar")) {
448                     pathToPom = "core/" + pathToPom;
449                 } else if (packaging.equals("war")) {
450                     pathToPom = "web/" + pathToPom;
451                 }
452             }
453 
454             String originalPom = FileUtils.readFileToString(new File(pathToPom), "UTF-8");
455             // replace tabs with spaces (in case user has changed their pom.xml
456             originalPom = originalPom.replace("\t", "    ");
457             startTag = originalPom.indexOf("\n    <dependencies>");
458 
459             StringBuffer sb = new StringBuffer();
460             sb.append(originalPom.substring(0, startTag));
461             sb.append(dependencyXml);
462             sb.append(originalPom.substring(originalPom.indexOf("</dependencies>", startTag)));
463 
464             String adjustedPom = sb.toString();
465 
466             // Calculate properties and add them to pom if not a modular project - otherwise properties are added
467             // near the end of this method from a threadlocal
468             if (!project.getPackaging().equals("pom") && !project.hasParent()) {
469                 adjustedPom = addPropertiesToPom(adjustedPom, sortedProperties);
470             }
471 
472             adjustedPom = adjustLineEndingsForOS(adjustedPom);
473 
474             FileUtils.writeStringToFile(new File(pathToPom), adjustedPom, "UTF-8"); // was pomWithProperties
475         } catch (IOException ex) {
476             getLog().error("Unable to write to pom.xml: " + ex.getMessage(), ex);
477             throw new MojoFailureException(ex.getMessage());
478         }
479 
480         boolean renamePackages = true;
481         if (System.getProperty("renamePackages") != null) {
482             renamePackages = Boolean.valueOf(System.getProperty("renamePackages"));
483         }
484 
485         if (renamePackages && !project.getPackaging().equals("pom")) {
486             log("Renaming packages to '" + project.getGroupId() + "'...");
487             RenamePackages renamePackagesTool = new RenamePackages(project.getGroupId());
488             if (project.hasParent()) {
489                 if (project.getPackaging().equals("jar")) {
490                     renamePackagesTool.setBaseDir("core/src");
491                 } else {
492                     renamePackagesTool.setBaseDir("web/src");
493                 }
494             }
495 
496             renamePackagesTool.execute();
497         }
498 
499         // when performing full-source on a modular project, add the properties to the root pom.xml at the end
500         if (project.getPackaging().equals("war") && project.hasParent()) {
501             // store sorted properties in a thread local for later retrieval
502             Map properties = propertiesContextHolder.get();
503             // alphabetize the properties by key
504             Set<String> propertiesToAdd = new TreeSet<String>(properties.keySet());
505 
506             StringBuffer calculatedProperties = new StringBuffer();
507 
508             for (String key : propertiesToAdd) {
509                 // don't add property if it already exists in project
510                 Set<Object> keysInProject = project.getParent().getOriginalModel().getProperties().keySet();
511                 if (!keysInProject.contains(key)) {
512                     String value = getAppFuseProperties().getProperty(key);
513 
514                     if (value.contains("&amp;")) {
515                         value = "<![CDATA[" + value + "]]>";
516                     }
517 
518                     calculatedProperties.append("        <");
519                     calculatedProperties.append(key);
520                     calculatedProperties.append(">");
521                     calculatedProperties.append(value);
522                     calculatedProperties.append("</");
523                     calculatedProperties.append(key);
524                     calculatedProperties.append(">");
525                     calculatedProperties.append("\n");
526                 }
527             }
528 
529             try {
530                 String originalPom = FileUtils.readFileToString(new File("pom.xml"), "UTF-8");
531 
532                 // Move modules to build section.
533                 originalPom = originalPom.replaceAll("  <modules>", "");
534                 // Because I hate fucking regex.
535                 originalPom = originalPom.replaceAll("    <module>.*?</module>", "");
536                 originalPom = originalPom.replaceAll("  </modules>", "");
537 
538                 originalPom = originalPom.replace("<repositories>", "<modules>\n" +
539                         "        <module>core</module>\n" +
540                         "        <module>web</module>\n" +
541                         "    </modules>\n\n    <repositories>");
542 
543                 String pomWithProperties = addPropertiesToPom(originalPom, calculatedProperties);
544 
545                 FileUtils.writeStringToFile(new File("pom.xml"), pomWithProperties, "UTF-8");
546             } catch (IOException ex) {
547                 getLog().error("Unable to read root pom.xml: " + ex.getMessage(), ex);
548                 throw new MojoFailureException(ex.getMessage());
549             }
550 
551         }
552 
553         // cleanup so user isn't aware that files were created
554         File pom = new File("pom-fullsource.xml");
555 
556         if (pom.exists()) {
557             pom.delete();
558         }
559     }
560 
561     private static String addPropertiesToPom(String existingPomXmlAsString, StringBuffer sortedProperties) {
562         String adjustedPom = existingPomXmlAsString;
563 
564         // add new properties
565         adjustedPom = adjustedPom.replace("<jdbc.password/>", "<jdbc.password/>" + LINE_SEP + LINE_SEP +
566                 "        <!-- Properties calculated by AppFuse when running full-source plugin -->\n" +
567                 sortedProperties);
568 
569         // also look for empty jdbc.password tag since the archetype plugin sometimes expands empty elements
570         adjustedPom = adjustedPom.replace("<jdbc.password></jdbc.password>", "<jdbc.password/>" + LINE_SEP + LINE_SEP +
571                 "        <!-- Properties calculated by AppFuse when running full-source plugin -->\n" +
572                 sortedProperties);
573 
574         adjustedPom = adjustedPom.replaceAll("<amp.fullSource>false</amp.fullSource>", "<amp.fullSource>true</amp.fullSource>");
575 
576         return adjustLineEndingsForOS(adjustedPom);
577     }
578 
579     private static String adjustLineEndingsForOS(String adjustedPom) {
580         String os = System.getProperty("os.name");
581 
582         if (os.startsWith("Linux") || os.startsWith("Mac")) {
583             // remove the \r returns
584             adjustedPom = adjustedPom.replaceAll("\r", "");
585         } else if (os.startsWith("Windows")) {
586             // use windows line endings
587             adjustedPom = adjustedPom.replaceAll(">\n", ">\r\n");
588         }
589 
590         return adjustedPom;
591     }
592 
593     private Properties getAppFuseProperties() {
594         // should only happen when executing full-source on modular modules (b/c they don't export root).
595         if (appfuseProperties == null) {
596             File pom = new File("target/appfuse-root/pom.xml");
597             appfuseProperties = createProjectFromPom(pom).getOriginalModel().getProperties();
598         }
599         return appfuseProperties;
600     }
601 
602     private String getFilePath(String s) {
603         s = s.replace("/", FILE_SEP);
604         //log("returning: " + s);
605         return s;
606     }
607 
608     private void export(String url, String destinationDirectory) throws MojoExecutionException {
609         SubversionUtils svn = new SubversionUtils(trunk + tag + url, destinationDirectory);
610 
611         try {
612             svn.export();
613         } catch (SVNException e) {
614             SVNErrorMessage err = e.getErrorMessage();
615 
616             /*
617              * Display all tree of error messages.
618              * Utility method SVNErrorMessage.getFullMessage() may be used instead of the loop.
619              */
620             while (err != null) {
621                 getLog()
622                         .error(err.getErrorCode().getCode() + " : " +
623                                 err.getMessage());
624                 err = err.getChildErrorMessage();
625             }
626 
627             throw new MojoExecutionException(e.getMessage());
628         }
629     }
630 
631     private void log(String msg) {
632         getLog().info("[AppFuse] " + msg);
633     }
634 
635     private List<Dependency> addModuleDependencies(List<Dependency> dependencies, String moduleName, String moduleLocation) {
636         log("Adding dependencies from " + moduleName + " module...");
637 
638         // Read dependencies from module's pom.xml
639         URL pomLocation = null;
640         File newDir = new File("target", "appfuse-" + moduleName);
641 
642         if (!newDir.exists()) {
643             newDir.mkdirs();
644         }
645 
646         File pom = new File("target/appfuse-" + moduleName + "/pom.xml");
647 
648         try {
649             pomLocation = new URL(trunk + tag + moduleLocation + "/pom.xml");
650         } catch (MalformedURLException e) {
651             e.printStackTrace();
652         }
653 
654         Get get = (Get) AntUtils.createProject().createTask("get");
655         get.setSrc(pomLocation);
656         get.setDest(pom);
657         get.setUsername("guest");
658         get.setPassword("");
659         get.execute();
660 
661         MavenProject p = createProjectFromPom(pom);
662 
663         List moduleDependencies = p.getOriginalModel().getDependencies();
664 
665         // set the properties for appfuse if root module
666         if (moduleName.equalsIgnoreCase("root")) {
667             appfuseProperties = p.getOriginalModel().getProperties();
668         }
669 
670         // create a list of artifactIds to check for duplicates (there's no equals() on Dependency)
671         Set<String> artifactIds = new LinkedHashSet<String>();
672 
673         for (Dependency dep : dependencies) {
674             artifactIds.add(dep.getArtifactId());
675         }
676 
677         // add all non-appfuse dependencies
678         for (Object moduleDependency : moduleDependencies) {
679             Dependency dep = (Dependency) moduleDependency;
680 
681             if (dep.getGroupId().equals("javax.servlet") && dep.getArtifactId().equals("jsp-api")
682                     && "jsf".equals(project.getProperties().getProperty("web.framework"))) {
683                 // skip adding dependency for old group id of jsp-api
684                 continue;
685             }
686 
687             if (!artifactIds.contains(dep.getArtifactId()) &&
688                     !dep.getArtifactId().contains("appfuse")) {
689                 dependencies.add(dep);
690             }
691         }
692 
693         return dependencies;
694     }
695 
696     private MavenProject createProjectFromPom(File pom) {
697         MavenEmbedder maven = new MavenEmbedder();
698         maven.setOffline(true);
699         maven.setClassLoader(Thread.currentThread().getContextClassLoader());
700         maven.setLogger(new MavenEmbedderConsoleLogger());
701 
702         MavenProject p = null;
703 
704         try {
705             maven.setAlignWithUserInstallation(true);
706             maven.start();
707             p = maven.readProjectWithDependencies(pom);
708             maven.stop();
709         } catch (Exception e) {
710             e.printStackTrace();
711         }
712 
713         return p;
714     }
715 
716     /**
717      * This method will create an ANT based LoadFile task based on an infile and a property name.
718      * The property will be loaded with the infile for use later by the Replace task.
719      *
720      * @param inFile   The file to process
721      * @param propName the name to assign it to
722      * @return The ANT LoadFile task that loads a property with a file
723      */
724     protected LoadFile createLoadFileTask(String inFile, String propName) {
725         LoadFile loadFileTask = (LoadFile) antProject.createTask("loadfile");
726         loadFileTask.init();
727         loadFileTask.setProperty(propName);
728         loadFileTask.setSrcFile(new File(inFile));
729 
730         return loadFileTask;
731     }
732 
733     /**
734      * This method will movie files from the source directory to the destination directory based on
735      * the pattern.
736      *
737      * @param inSourceDirectory      The source directory to copy from.
738      * @param inDestinationDirectory The destination directory to copy to.
739      * @param inPattern              The file pattern to match to locate files to copy.
740      */
741     protected void moveFiles(final String inSourceDirectory, final String inDestinationDirectory,
742                              final String inPattern) {
743         Move moveTask = (Move) antProject.createTask("move");
744 
745         FileSet fileSet = AntUtils.createFileset(inSourceDirectory, inPattern, new ArrayList());
746         moveTask.setTodir(new File(inDestinationDirectory));
747         moveTask.addFileset(fileSet);
748         moveTask.execute();
749     }
750 }