View Javadoc

1   package org.appfuse.mojo.installer;
2   
3   import org.apache.commons.beanutils.BeanComparator;
4   import org.apache.commons.io.FileUtils;
5   import org.apache.commons.lang.StringUtils;
6   import org.apache.maven.artifact.Artifact;
7   import org.apache.maven.artifact.repository.ArtifactRepository;
8   import org.apache.maven.model.Dependency;
9   import org.apache.maven.model.DependencyManagement;
10  import org.apache.maven.plugin.AbstractMojo;
11  import org.apache.maven.plugin.MojoExecutionException;
12  import org.apache.maven.plugin.MojoFailureException;
13  import org.apache.maven.project.MavenProject;
14  import org.apache.maven.project.MavenProjectBuilder;
15  import org.apache.maven.settings.Settings;
16  import org.apache.tools.ant.Project;
17  import org.apache.tools.ant.taskdefs.Get;
18  import org.apache.tools.ant.taskdefs.Move;
19  import org.apache.tools.ant.types.FileSet;
20  import org.appfuse.tool.RenamePackages;
21  import org.appfuse.tool.SubversionUtils;
22  import org.tmatesoft.svn.core.SVNErrorMessage;
23  import org.tmatesoft.svn.core.SVNException;
24  
25  import java.io.File;
26  import java.io.FileWriter;
27  import java.io.IOException;
28  import java.io.StringWriter;
29  import java.net.MalformedURLException;
30  import java.net.URL;
31  import java.util.*;
32  
33  import static java.lang.String.format;
34  
35  
36  /**
37   * This mojo is used to "install" source artifacts from Subversion into an AppFuse project.
38   * If you get an OutOfMemoryError when running this plugin, you should be able to fix it
39   * by setting your MAVEN_OPTS environment variable to "-Xms128M -Xmx256M".
40   *
41   * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
42   * @goal full-source
43   */
44  public class InstallSourceMojo extends AbstractMojo {
45      private static final String APPFUSE_GROUP_ID = "org.appfuse";
46      private static final String FILE_SEP = System.getProperty("file.separator");
47      private static final String LINE_SEP = System.getProperty("line.separator");
48  
49      Project antProject = AntUtils.createProject();
50      Properties appfuseProperties;
51  
52      // ThreadLocale to hold properties as they're built when traversing through a modular project
53      private static final ThreadLocal<Map> propertiesContextHolder = new ThreadLocal<Map>();
54  
55      /**
56       * The path where the files from SVN will be placed. This is intentionally set to "src" since that's the default
57       * src directory used for exporting AppFuse artifacts.
58       *
59       * @parameter expression="${appfuse.destinationDirectory}" default-value="${basedir}/src"
60       */
61      private String destinationDirectory;
62  
63      /**
64       * The branch containing the source code
65       *
66       * @parameter expression="${appfuse.branch}"
67       */
68      private String branch;
69  
70      /**
71       * The directory containing the source code.
72       *
73       * @parameter expression="${appfuse.trunk}" default-value="https://github.com/appfuse/appfuse/"
74       */
75      private String trunk;
76  
77      /**
78       * The tag containing the source code - defaults to '/trunk', but you may want to set it to '/tags/TAGNAME'
79       *
80       * @parameter expression="${appfuse.tag}" default-value="trunk/"
81       */
82      private String tag;
83  
84      /**
85       * <i>Maven Internal</i>: Project to interact with.
86       *
87       * @parameter expression="${project}"
88       * @required
89       * @readonly
90       * @noinspection UnusedDeclaration
91       */
92      private MavenProject project;
93  
94      /**
95       * @parameter expression="${settings}"
96       * @required
97       * @readonly
98       */
99      private Settings settings;
100 
101     /**
102      * @component
103      */
104     private MavenProjectBuilder mavenProjectBuilder;
105 
106     /**
107      * @parameter default-value= "${localRepository}"
108      */
109     private ArtifactRepository local;
110 
111     public void execute() throws MojoExecutionException, MojoFailureException {
112         // http://issues.appfuse.org/browse/APF-1025
113         System.setProperty("file.encoding", "UTF-8");
114 
115         // If appfuse.version is specified as a property, and not a SNAPSHOT, use it for the tag
116         String appfuseVersion = project.getProperties().getProperty("appfuse.version");
117 
118         if ((appfuseVersion != null) && !appfuseVersion.endsWith("SNAPSHOT") && tag.equals("trunk/")) {
119             tag = "tags/APPFUSE_" + appfuseVersion.toUpperCase().replaceAll("-", "_") + "/";
120             // APF-1168: Allow milestone and release candidates
121             if (tag.contains("_M")) {
122                 tag = tag.replace("_M", "-M");
123             } else if (tag.contains("_R")) {
124                 tag = tag.replace("_R", "-R");
125             }
126         }
127 
128         if (branch != null && !"".equals(branch) && !"master".equals(branch) && !"HEAD".equals(branch)) {
129             log("Using branch: " + branch);
130             tag = "branches/" + branch + "/";
131         }
132 
133         String daoFramework = project.getProperties().getProperty("dao.framework");
134 
135         if (daoFramework == null) {
136             log("No dao.framework property specified, defaulting to 'hibernate'");
137         }
138 
139         String webFramework = project.getProperties().getProperty("web.framework");
140 
141         boolean modular = project.getPackaging().equals("pom");
142         // if core project or modular web
143         if (project.getPackaging().equals("jar") ||
144             (project.getPackaging().equals("war") && project.getParentArtifact().getGroupId().contains("appfuse"))) {
145             // export data-common
146             log("Importing source from data-common module...");
147             String coreSource = project.getBuild().getSourceDirectory();
148             export("data/common/src", (modular) ? coreSource : destinationDirectory);
149 
150             // Keep web project original testing hibernate.properties instead of overwriting it: rename
151             File orig = new File((modular ? coreSource : destinationDirectory) + "/test/resources/hibernate.properties");
152             File dest = new File((modular ? coreSource : destinationDirectory) + "/test/resources/hibernate.properties.orig");
153             if (orig.exists() && webFramework != null && !webFramework.isEmpty()) {
154                 renameFile(orig, dest);
155             }
156 
157             // export persistence framework
158             log("Importing source from " + daoFramework + " module...");
159             export("data/" + daoFramework + "/src", (modular) ? coreSource : destinationDirectory);
160 
161             // export service module
162             log("Importing source from service module...");
163             export("service/src", (modular) ? coreSource : destinationDirectory);
164 
165             // move Base*TestCase to test directory
166             moveFiles((modular) ? coreSource + "/main" : destinationDirectory + "/main",
167                 (modular) ? coreSource + "/test" : destinationDirectory + "/test", "**/Base*TestCase.java");
168 
169             // delete dao.framework related files from test directory
170             deleteFile("test/resources/hibernate.cfg.xml");
171             deleteFile("test/resources/META-INF");
172             deleteFile("test/resources/sql-map-config.xml");
173 
174             // If JPA, delete hibernate.cfg.xml b/c it will cause issues when
175             // using jpaconfiguration with the hibernate3-maven-plugin
176             if ("jpa".equalsIgnoreCase(daoFramework)) {
177                 deleteFile("main/resources/hibernate.cfg.xml");
178             }
179 
180             // Keep web project original testing hibernate.properties instead of overwriting it: delete copied and rename back
181             if (dest.exists() && webFramework != null && !webFramework.isEmpty()) {
182                 deleteFile(orig.getPath());
183                 renameFile(dest, orig);
184             }
185 
186             log("Source successfully imported!");
187         }
188 
189         if (modular) {
190             try {
191                 String pom = FileUtils.readFileToString(new File("pom.xml"), "UTF-8");
192 
193                 // Move modules to build section.
194                 pom = pom.replaceAll("  <modules>\n", "");
195                 pom = pom.replaceAll("    <module>.*?</module>\n", "");
196                 pom = pom.replaceAll("  </modules>\n", "");
197 
198                 pom = pom.replace("<build>", "<modules>\n" +
199                     "        <module>core</module>\n" +
200                     "        <module>web</module>\n" +
201                     "    </modules>\n\n    <build>");
202 
203                 FileUtils.writeStringToFile(new File("pom.xml"), pom, "UTF-8");
204             } catch (IOException ex) {
205                 // no big deal if this fails, everything will still work
206             }
207 
208             try {
209                 String pom = FileUtils.readFileToString(new File("core/pom.xml"), "UTF-8");
210 
211                 // remove appfuse-data-common as a dependency
212                 pom = pom.replaceAll("<dependencies>\n" +
213                     "        <dependency>\n" +
214                     "            <groupId>org.appfuse</groupId>\n" +
215                     "            <artifactId>appfuse-data-common</artifactId>\n" +
216                     "            <version>(.*?)appfuse.version}</version>\n" +
217                     "        </dependency>", "<dependencies>");
218 
219                 // modify the appfuse dependencies to be type pom
220                 pom = pom.replaceAll("<version>(.*?)appfuse.version}</version>",
221                     "<version>$1appfuse.version}</version>\n            <type>pom</type>");
222 
223                 // modify hibernate4-plugin to exclude dependency scan
224                 pom = pom.replaceAll("<artifactId>hibernate4-maven-plugin</artifactId>\n" +
225                     "                <configuration>", "<artifactId>hibernate4-maven-plugin</artifactId>\n" +
226                     "                <configuration>\n" +
227                     "                    <scanDependencies>none</scanDependencies>");
228 
229                 pom = adjustLineEndingsForOS(pom);
230                 FileUtils.writeStringToFile(new File("core/pom.xml"), pom, "UTF-8");
231             } catch (IOException io) {
232                 getLog().error("Failed to change core module's dependencies to use <type>pom</type>.\n" +
233                     "Please make this change manually.");
234             }
235 
236             // exclude all appfuse dependencies in web module
237             try {
238                 String pom = FileUtils.readFileToString(new File("web/pom.xml"), "UTF-8");
239                 pom = pom.replaceAll("appfuse-hibernate", "*");
240 
241                 // modify hibernate4-plugin to exclude dependency scan
242                 pom = pom.replaceAll("<artifactId>hibernate4-maven-plugin</artifactId>",
243                     "<artifactId>hibernate4-maven-plugin</artifactId>\n" +
244                         "                <configuration>\n                    " +
245                         "                    <scanDependencies>none</scanDependencies>\n" +
246                         "                </configuration>");
247                 pom = adjustLineEndingsForOS(pom);
248                 FileUtils.writeStringToFile(new File("web/pom.xml"), pom, "UTF-8");
249             } catch (IOException io) {
250                 getLog().error("Failed to change web module to exclude AppFuse dependencies.\n" +
251                     "Please make this change manually: %s/appfuse-hibernate/*");
252             }
253         } else {
254             try {
255                 String pom = FileUtils.readFileToString(new File("pom.xml"), "UTF-8");
256                 // modify hibernate4-plugin to exclude dependency scan
257                 if (project.getPackaging().equals("jar")) {
258                     pom = pom.replaceAll("<artifactId>hibernate4-maven-plugin</artifactId>\n" +
259                         "                <configuration>", "<artifactId>hibernate4-maven-plugin</artifactId>\n" +
260                         "                <configuration>\n" +
261                         "                    <scanDependencies>none</scanDependencies>");
262                 } else {
263                     pom = pom.replaceAll("<artifactId>hibernate4-maven-plugin</artifactId>",
264                         "<artifactId>hibernate4-maven-plugin</artifactId>\n" +
265                         "                <configuration>\n" +
266                         "                    <scanDependencies>none</scanDependencies>\n" +
267                         "                </configuration>");
268                 }
269 
270                 // remove appfuse-data-common as a dependency
271                 pom = pom.replaceAll("<dependencies>\n" +
272                     "        <dependency>\n" +
273                     "            <groupId>org.appfuse</groupId>\n" +
274                     "            <artifactId>appfuse-data-common</artifactId>\n" +
275                     "            <version>(.*?)appfuse.version}</version>\n" +
276                     "        </dependency>", "<dependencies>");
277 
278                 pom = adjustLineEndingsForOS(pom);
279                 FileUtils.writeStringToFile(new File("pom.xml"), pom, "UTF-8");
280             } catch (IOException io) {
281                 getLog().error("Failed to add <scanDependencies>none</scanDependencies> to hibernate4-maven-plugin. " +
282                     "Please make this change manually.");
283             }
284         }
285 
286         // it's OK if a project created with appfuse-ws doesn't have a web framework defined
287         // currently, a project with appfuse-ws can be identified by enunciate
288         boolean isWebServicesProject = false;
289         for (Object pluginArtifact : project.getPluginArtifacts()) {
290             if (((Artifact) pluginArtifact).getArtifactId().contains("enunciate")) {
291                 isWebServicesProject = true;
292                 try {
293                     String enunciate = FileUtils.readFileToString(new File("enunciate.xml"), "UTF-8");
294                     enunciate = enunciate.replaceAll("org.appfuse", project.getGroupId());
295                     enunciate = adjustLineEndingsForOS(enunciate);
296                     FileUtils.writeStringToFile(new File("enunciate.xml"), enunciate, "UTF-8");
297                 } catch (IOException e) {
298                     getLog().error("Failed to rename 'org.appfuse' to '" + project.getGroupId() + 
299                             " in enunciate.xml'. Please make this change manually.");
300                 }
301                 break;
302             }
303         }
304 
305         if (project.getPackaging().equalsIgnoreCase("war")) {
306             if (webFramework == null && !isWebServicesProject) {
307                 getLog().error("The web.framework property is not specified - please modify your pom.xml to add " +
308                     " this property. For example: <web.framework>struts</web.framework>.");
309                 throw new MojoExecutionException("No web.framework property specified, please modify pom.xml to add it.");
310             }
311 
312             if (project.getArtifactId().contains("core")) {
313                 // delete hibernate, ibatis and jpa files from web project
314                 deleteFile("main/resources/hibernate.cfg.xml");
315                 deleteFile("main/resources/META-INF");
316                 deleteFile("main/resources/sql-map-config.xml");
317 
318                 // there's a jdbc.properties in test/resources that shouldn't be there
319                 deleteFile("test/resources/jdbc.properties");
320 
321             } else if (!isAppFuse()) {
322                 // there's a jdbc.properties in test/resources that shouldn't be there
323                 deleteFile("test/resources/jdbc.properties");
324                 deleteFile("test/resources/log4j2.xml");
325             }
326         }
327 
328         // As of AppFuse 3.5, pom files no longer need to be adjusted when running full-source
329         //calculateDependencies(appfuseVersion, daoFramework, webFramework, isWebServicesProject);
330         renamePackages();
331     }
332 
333     private void calculateDependencies(String appfuseVersion, String daoFramework, String webFramework, boolean isWebServicesProject) throws MojoFailureException {
334         List dependencies = project.getOriginalModel().getDependencies();
335         List<Dependency> newDependencies = new ArrayList<>();
336 
337         // remove all appfuse dependencies
338         for (Object dependency : dependencies) {
339             Dependency dep = (Dependency) dependency;
340 
341             if (!dep.getGroupId().equals(APPFUSE_GROUP_ID)) {
342                 newDependencies.add(dep);
343             }
344         }
345 
346         if (!project.getPackaging().equals("pom")) {
347 
348             // add dependencies from root appfuse pom
349             newDependencies = addModuleDependencies(newDependencies, "root", "", "");
350 
351             // Add dependencies from appfuse-data
352             newDependencies = addModuleDependencies(newDependencies, "data", "data", "appfuse-root");
353 
354             // Add dependencies from appfuse-data-common
355             newDependencies = addModuleDependencies(newDependencies, "data-common", "data/common", "appfuse-root/appfuse-data");
356 
357             // Add dependencies from appfuse-${dao.framework}
358             newDependencies = addModuleDependencies(newDependencies, daoFramework, "data/" + daoFramework, "appfuse-root/appfuse-data");
359 
360             // Add dependencies from appfuse-service
361             newDependencies = addModuleDependencies(newDependencies, "service", "service", "appfuse-root");
362 
363             if (!isWebServicesProject && project.getPackaging().equals("war")) {
364                 newDependencies = addWebDependencies(appfuseVersion, newDependencies, webFramework);
365             }
366 
367             createFullSourcePom(newDependencies);
368         } else {
369             if (project.getPackaging().equals("pom")) {
370                 // add dependencies from root appfuse pom
371                 newDependencies = addModuleDependencies(newDependencies, "root", "", "");
372 
373                 createFullSourcePom(newDependencies);
374             }
375 
376             if (project.getPackaging().equals("jar")) {
377                 newDependencies.clear();
378 
379                 // add dependencies from root appfuse pom
380                 newDependencies = addModuleDependencies(newDependencies, "root", "", "");
381 
382                 // Add dependencies from appfuse-data
383                 newDependencies = addModuleDependencies(newDependencies, "data", "data", "appfuse-root");
384 
385                 // Add dependencies from appfuse-data-common
386                 newDependencies = addModuleDependencies(newDependencies, "data-common", "data/common", "appfuse-root/appfuse-data");
387 
388                 // Add dependencies from appfuse-${dao.framework}
389                 newDependencies = addModuleDependencies(newDependencies, daoFramework, "data/" + daoFramework, "appfuse-root/appfuse-data");
390 
391                 // Add dependencies from appfuse-service
392                 newDependencies = addModuleDependencies(newDependencies, "service", "service", "appfuse-root");
393 
394                 createFullSourcePom(newDependencies);
395             }
396 
397             if (project.getPackaging().equals("war")) {
398                 newDependencies.clear();
399 
400                 // add dependencies from root appfuse pom
401                 newDependencies = addModuleDependencies(newDependencies, "root", "", "");
402 
403                 newDependencies = addWebDependencies(appfuseVersion, newDependencies, webFramework);
404 
405                 createFullSourcePom(newDependencies);
406             }
407         }
408     }
409 
410     private List<Dependency> addWebDependencies(String appfuseVersion, List<Dependency> newDependencies, String webFramework) {
411         // Add dependencies from appfuse-common-web
412         newDependencies = addModuleDependencies(newDependencies, "web", "web", "appfuse-root");
413 
414         Double appfuseVersionAsDouble = new Double(appfuseVersion.substring(0, appfuseVersion.lastIndexOf(".")));
415         if (StringUtils.countMatches(".", appfuseVersion) == 1) {
416             appfuseVersionAsDouble = new Double(appfuseVersion);
417         }
418 
419         getLog().debug("Detected AppFuse version: " + appfuseVersionAsDouble);
420 
421         if (isAppFuse() && appfuseVersionAsDouble < 2.1) {
422             // Add dependencies from appfuse-common-web
423             newDependencies = addModuleDependencies(newDependencies, "web-common", "web/common", "appfuse-root/appfuse-web");
424         }
425 
426         if (isAppFuse() && project.getPackaging().equals("war") && project.hasParent()) {
427             newDependencies = addModuleDependencies(newDependencies, "web-common", "web/common", "appfuse-root/appfuse-web");
428             newDependencies = addModuleDependencies(newDependencies, webFramework, "web/" + webFramework, "appfuse-root/appfuse-web");
429         }
430 
431         return newDependencies;
432     }
433 
434     // TODO: Improve logic or remove need for calculation
435     private boolean isAppFuse() {
436         return (project.getParent().getArtifactId().contains("appfuse-web"));
437     }
438 
439     private void deleteFile(String filePath) {
440         if (!filePath.startsWith("/")) {
441             filePath = "/" + filePath;
442         }
443         File duplicateFile = new File(getFilePath(destinationDirectory + filePath));
444         try {
445             getLog().debug("Looking for duplicate file at '" + duplicateFile.getCanonicalPath());
446             if (duplicateFile.exists()) {
447                 getLog().debug("Deleting duplicate file at '" + duplicateFile.getCanonicalPath());
448                 if (duplicateFile.isDirectory()) {
449                     FileUtils.deleteDirectory(duplicateFile);
450                 } else {
451                     FileUtils.forceDeleteOnExit(duplicateFile);
452                 }
453             }
454         } catch (IOException io) {
455             getLog().error("Failed to delete '" + filePath + "', please delete manually.");
456         }
457     }
458 
459     private void createFullSourcePom(List<Dependency> newDependencies) throws MojoFailureException {
460         // create properties based on dependencies
461         Set<String> projectProperties = new TreeSet<>();
462 
463         for (Dependency dep : newDependencies) {
464             projectProperties.add(getDependencyVersionOrThrowExceptionIfNotAvailable(dep));
465         }
466 
467         // add core as a dependency for modular wars
468         if (project.getPackaging().equals("war") && !project.getParentArtifact().getGroupId().contains("appfuse")) {
469             Dependency core = new Dependency();
470             core.setGroupId("${project.parent.groupId}");
471             // This assumes you're following conventions of ${project.artifactId}-core
472             core.setArtifactId("${project.parent.artifactId}-core");
473             core.setVersion("${project.parent.version}");
474             newDependencies.add(core);
475         }
476 
477         Collections.sort(newDependencies, new BeanComparator("groupId"));
478 
479         project.getOriginalModel().setDependencies(newDependencies);
480 
481         Properties currentProperties = project.getOriginalModel().getProperties();
482 
483         Set<String> currentKeys = new LinkedHashSet<String>();
484         for (Object key : currentProperties.keySet()) {
485             currentKeys.add((String) key);
486         }
487 
488         StringBuffer sortedProperties = new StringBuffer();
489 
490         Properties appfuseProperties = getAppFuseProperties();
491 
492         // holder for properties - stored in ThreadLocale
493         Map<String, String> propertiesForPom = new LinkedHashMap<String, String>();
494 
495         for (String key : projectProperties) {
496             // don't add property if it already exists in project
497             if (!currentKeys.contains(key)) {
498                 String value = appfuseProperties.getProperty(key);
499 
500                 // this happens when the version number is hard-coded
501                 if (value == null) {
502                     continue;
503                 }
504 
505                 if (value.contains("&amp;")) {
506                     value = "<![CDATA[" + value + "]]>";
507                 }
508 
509                 sortedProperties.append("        <").append(key).append(">")
510                     .append(value).append("</").append(key).append(">" + "\n");
511                 propertiesForPom.put(key, value);
512             }
513         }
514 
515         if (project.getPackaging().equals("pom") || project.hasParent()) {
516             // store sorted properties in a thread local for later retrieval
517             Map<String, String> properties = new LinkedHashMap<String, String>();
518             if (propertiesContextHolder.get() != null) {
519                 properties = (LinkedHashMap) propertiesContextHolder.get();
520             }
521 
522             for (String key : propertiesForPom.keySet()) {
523                 if (!properties.containsKey(key)) {
524                     properties.put(key, propertiesForPom.get(key));
525                 }
526             }
527 
528             propertiesContextHolder.set(properties);
529         }
530 
531         StringWriter writer = new StringWriter();
532 
533         try {
534             project.writeOriginalModel(writer);
535 
536             File pom = new File("pom-fullsource.xml");
537 
538             if (pom.exists()) {
539                 pom.delete();
540             }
541 
542             FileWriter fw = new FileWriter(pom);
543             fw.write(writer.toString());
544             fw.flush();
545             fw.close();
546         } catch (IOException ex) {
547             getLog().error("Unable to create pom-fullsource.xml: " + ex.getMessage(), ex);
548             throw new MojoFailureException(ex.getMessage());
549         }
550 
551         //log("Updated dependencies in pom.xml...");
552 
553         // I tried to use regex here, but couldn't get it to work - going with the old fashioned way instead
554         String pomXml = writer.toString();
555         int startTag = pomXml.indexOf("\n  <dependencies>");
556 
557         String dependencyXml = pomXml.substring(startTag, pomXml.indexOf("</dependencies>", startTag));
558         // change 2 spaces to 4
559         dependencyXml = dependencyXml.replaceAll("  ", "    ");
560         dependencyXml = "\n    <!-- Dependencies calculated by AppFuse when running full-source plugin -->" + dependencyXml;
561 
562         try {
563             String packaging = project.getPackaging();
564             String pathToPom = "pom.xml";
565             if (project.hasParent() && !project.getParentArtifact().getGroupId().contains("appfuse")) {
566                 if (packaging.equals("jar")) {
567                     pathToPom = "core/" + pathToPom;
568                 } else if (packaging.equals("war")) {
569                     pathToPom = "web/" + pathToPom;
570                 }
571             }
572 
573             String originalPom = FileUtils.readFileToString(new File(pathToPom), "UTF-8");
574             // replace tabs with spaces (in case user has changed their pom.xml
575             originalPom = originalPom.replace("\t", "    ");
576             startTag = originalPom.indexOf("\n    <dependencies>");
577 
578             StringBuffer sb = new StringBuffer();
579             sb.append(originalPom.substring(0, startTag));
580             sb.append(dependencyXml);
581             sb.append(originalPom.substring(originalPom.indexOf("</dependencies>", startTag)));
582 
583             String adjustedPom = sb.toString();
584 
585             // Calculate properties and add them to pom if not a modular project - otherwise properties are added
586             // near the end of this method from a threadlocal
587             if (!project.getPackaging().equals("pom") && project.getParentArtifact().getGroupId().contains("appfuse")) {
588                 adjustedPom = addPropertiesToPom(adjustedPom, sortedProperties);
589             }
590 
591             adjustedPom = adjustLineEndingsForOS(adjustedPom);
592 
593             FileUtils.writeStringToFile(new File(pathToPom), adjustedPom, "UTF-8");
594         } catch (IOException ex) {
595             getLog().error("Unable to write to pom.xml: " + ex.getMessage(), ex);
596             throw new MojoFailureException(ex.getMessage());
597         }
598 
599         // cleanup so user isn't aware that files were created
600         File pom = new File("pom-fullsource.xml");
601 
602         if (pom.exists()) {
603             pom.delete();
604         }
605 
606         // when performing full-source on a modular project, add the properties to the root pom.xml at the end
607         if (project.getPackaging().equals("war") &&
608             (project.hasParent() && !project.getParentArtifact().getGroupId().contains("appfuse"))) {
609             // store sorted properties in a thread local for later retrieval
610             Map properties = propertiesContextHolder.get();
611             // alphabetize the properties by key
612             Set<String> propertiesToAdd = new TreeSet<>(properties.keySet());
613 
614             StringBuffer calculatedProperties = new StringBuffer();
615 
616             for (String key : propertiesToAdd) {
617                 // don't add property if it already exists in project
618                 Set<Object> keysInProject = project.getParent().getOriginalModel().getProperties().keySet();
619                 if (!keysInProject.contains(key)) {
620                     String value = getAppFuseProperties().getProperty(key);
621 
622                     if (value.contains("&amp;")) {
623                         value = "<![CDATA[" + value + "]]>";
624                     }
625 
626                     calculatedProperties.append("        <");
627                     calculatedProperties.append(key);
628                     calculatedProperties.append(">");
629                     calculatedProperties.append(value);
630                     calculatedProperties.append("</");
631                     calculatedProperties.append(key);
632                     calculatedProperties.append(">");
633                     calculatedProperties.append("\n");
634                 }
635             }
636 
637             try {
638                 String originalPom = FileUtils.readFileToString(new File("pom.xml"), "UTF-8");
639 
640                 // Move modules to build section.
641                 originalPom = originalPom.replaceAll("  <modules>", "");
642                 originalPom = originalPom.replaceAll("    <module>.*?</module>", "");
643                 originalPom = originalPom.replaceAll("  </modules>", "");
644 
645                 originalPom = originalPom.replace("<repositories>", "<modules>\n" +
646                     "        <module>core</module>\n" +
647                     "        <module>web</module>\n" +
648                     "    </modules>\n\n    <repositories>");
649 
650                 String pomWithProperties = addPropertiesToPom(originalPom, calculatedProperties);
651 
652                 FileUtils.writeStringToFile(new File("pom.xml"), pomWithProperties, "UTF-8");
653             } catch (IOException ex) {
654                 getLog().error("Unable to modify root pom.xml: " + ex.getMessage(), ex);
655                 throw new MojoFailureException(ex.getMessage());
656             }
657         }
658     }
659 
660     private void renamePackages() {
661         boolean renamePackages = true;
662         if (System.getProperty("renamePackages") != null) {
663             renamePackages = Boolean.valueOf(System.getProperty("renamePackages"));
664         }
665 
666         if (renamePackages && !project.getPackaging().equals("pom")) {
667             log("Renaming packages to '" + project.getGroupId() + "'...");
668             RenamePackages renamePackagesTool = new RenamePackages(project.getGroupId());
669             if (project.hasParent() && !project.getParentArtifact().getGroupId().contains("appfuse")) {
670                 renamePackagesTool.setBaseDir(project.getBasedir() + "/src");
671             }
672 
673             renamePackagesTool.execute();
674         }
675     }
676 
677     private String getDependencyVersionOrThrowExceptionIfNotAvailable(Dependency dep) {
678         String version = dep.getVersion();
679 
680         if (version == null) {
681             version = getDependencyVersionFromDependencyManagementOrThrowExceptionIfNotAvailable(dep);
682         }
683 
684         // trim off ${}
685         if (version.startsWith("${")) {
686             version = version.substring(2);
687         }
688 
689         if (version.endsWith("}")) {
690             version = version.substring(0, version.length() - 1);
691         }
692         return version;
693     }
694 
695     @SuppressWarnings("unchecked")
696     private String getDependencyVersionFromDependencyManagementOrThrowExceptionIfNotAvailable(Dependency dep) {
697         DependencyManagement dependencyManagement = project.getDependencyManagement();
698         if (dependencyManagement != null) {
699             List<Dependency> managedDeps = dependencyManagement.getDependencies();
700             for (Dependency managedDep : managedDeps) {
701                 if (managedDep.getArtifactId().equals(dep.getArtifactId()) &&
702                     managedDep.getGroupId().equals(dep.getGroupId())) {
703                     return managedDep.getVersion();
704                 }
705             }
706             throw new IllegalArgumentException(format(
707                 "Unable to determine version for dependency: %s:%s", dep.getGroupId(), dep.getArtifactId()));
708         } else {
709             throw new IllegalArgumentException(format(
710                 "Unable to determine version for dependency: %s:%s. DependencyManagement is null",
711                 dep.getGroupId(), dep.getArtifactId()));
712         }
713     }
714 
715     private static String addPropertiesToPom(String existingPomXmlAsString, StringBuffer sortedProperties) {
716         String adjustedPom = existingPomXmlAsString;
717 
718         if (!"".equals(sortedProperties)) {
719             // add new properties
720             adjustedPom = adjustedPom.replace("<jdbc.password/>", "<jdbc.password/>" + LINE_SEP + LINE_SEP +
721                 "        <!-- Properties calculated by AppFuse when running full-source plugin -->\n" +
722                 sortedProperties);
723 
724             // also look for empty jdbc.password tag since the archetype plugin sometimes expands empty elements
725             adjustedPom = adjustedPom.replace("<jdbc.password></jdbc.password>", "<jdbc.password/>" + LINE_SEP + LINE_SEP +
726                 "        <!-- Properties calculated by AppFuse when running full-source plugin -->\n" +
727                 sortedProperties);
728         }
729 
730         adjustedPom = adjustedPom.replaceAll("<amp.fullSource>false</amp.fullSource>", "<amp.fullSource>true</amp.fullSource>");
731 
732         return adjustLineEndingsForOS(adjustedPom);
733     }
734 
735     private static String adjustLineEndingsForOS(String adjustedPom) {
736         String os = System.getProperty("os.name");
737 
738         if (os.startsWith("Linux") || os.startsWith("Mac")) {
739             // remove the \r returns
740             adjustedPom = adjustedPom.replaceAll("\r", "");
741         } else if (os.startsWith("Windows")) {
742             // use windows line endings
743             adjustedPom = adjustedPom.replaceAll(">\n", ">\r\n");
744         }
745 
746         return adjustedPom;
747     }
748 
749     private Properties getAppFuseProperties() {
750         // should only happen when executing full-source on modular modules (b/c they don't export root).
751         if (appfuseProperties == null) {
752             File pom = new File("target/appfuse-root/pom.xml");
753             appfuseProperties = createProjectFromPom(pom).getOriginalModel().getProperties();
754         }
755         return appfuseProperties;
756     }
757 
758     private String getFilePath(String s) {
759         s = s.replace("/", FILE_SEP);
760         //log("returning: " + s);
761         return s;
762     }
763 
764     private void export(String url, String destinationDirectory) throws MojoExecutionException {
765         SubversionUtils svn = new SubversionUtils(trunk + tag + url, destinationDirectory);
766 
767         try {
768             svn.export();
769         } catch (SVNException e) {
770             SVNErrorMessage err = e.getErrorMessage();
771 
772             /*
773              * Display all tree of error messages.
774              * Utility method SVNErrorMessage.getFullMessage() may be used instead of the loop.
775              */
776             while (err != null) {
777                 getLog()
778                     .error(err.getErrorCode().getCode() + " : " +
779                         err.getMessage());
780                 err = err.getChildErrorMessage();
781             }
782 
783             throw new MojoExecutionException(e.getMessage());
784         }
785     }
786 
787     private void log(String msg) {
788         getLog().info("[AppFuse] " + msg);
789     }
790 
791     private List<Dependency> addModuleDependencies(List<Dependency> dependencies, String moduleName, String moduleLocation, String parentModule) {
792         log("Adding dependencies from " + moduleName + " module...");
793 
794         // Read dependencies from module's pom.xml
795         URL pomLocation = null;
796         File newDir = new File(project.getFile().getParent(), "target/" + parentModule + "/appfuse-" + moduleName);
797 
798         if (!newDir.exists()) {
799             newDir.mkdirs();
800         }
801 
802         File pom = new File(project.getFile().getParent(), "target/" + parentModule + "/appfuse-" + moduleName + "/pom.xml");
803 
804         try {
805             // replace github.com with raw.github.com and trunk with master
806             trunk = trunk.replace("https://github.com", "https://raw.githubusercontent.com");
807             tag = tag.replace("trunk", "master");
808             // replace tag/branch with nothing
809             if (tag.contains("tags/")) {
810                 tag = tag.replace("tags/", "");
811             }
812             if (tag.contains("branches/")) {
813                 tag = tag.replace("branches/", "");
814             }
815 
816             // add separator if moduleLocation is populated
817             if (!"".equals(moduleLocation)) {
818                 moduleLocation += "/";
819             }
820             pomLocation = new URL(trunk + tag + moduleLocation + "pom.xml");
821         } catch (MalformedURLException e) {
822             e.printStackTrace();
823         }
824 
825         Get get = (Get) AntUtils.createProject().createTask("get");
826         get.setSrc(pomLocation);
827         get.setDest(pom);
828         get.execute();
829 
830         MavenProject p = createProjectFromPom(pom);
831 
832         List moduleDependencies = p.getOriginalModel().getDependencies();
833 
834         // set the properties for appfuse if root module
835         if (moduleName.equalsIgnoreCase("root")) {
836             appfuseProperties = p.getOriginalModel().getProperties();
837         }
838 
839         // create a list of artifactIds to check for duplicates (there's no equals() on Dependency)
840         Set<String> artifactIds = new LinkedHashSet<String>();
841 
842         for (Dependency dep : dependencies) {
843             artifactIds.add(dep.getArtifactId());
844         }
845 
846         // add all non-appfuse dependencies
847         for (Object moduleDependency : moduleDependencies) {
848             Dependency dep = (Dependency) moduleDependency;
849 
850             if (dep.getGroupId().equals("javax.servlet.jsp") && dep.getArtifactId().equals("jsp-api")
851                 && "jsf".equals(project.getProperties().getProperty("web.framework"))) {
852                 // skip adding dependency for old group id of jsp-api
853                 continue;
854             }
855 
856             if (!artifactIds.contains(dep.getArtifactId()) &&
857                 !dep.getArtifactId().contains("appfuse")) {
858                 dependencies.add(dep);
859             }
860         }
861 
862         return dependencies;
863     }
864 
865     private MavenProject createProjectFromPom(File pom) {
866         try {
867             return mavenProjectBuilder.buildWithDependencies(pom, local, null);
868         } catch (Exception e) {
869             getLog().warn("skip error reading maven project: " + e.getMessage(), e);
870         }
871         return null;
872     }
873 
874     /**
875      * This method will movie files from the source directory to the destination directory based on
876      * the pattern.
877      *
878      * @param inSourceDirectory      The source directory to copy from.
879      * @param inDestinationDirectory The destination directory to copy to.
880      * @param inPattern              The file pattern to match to locate files to copy.
881      */
882     protected void moveFiles(final String inSourceDirectory, final String inDestinationDirectory,
883                              final String inPattern) {
884         Move moveTask = (Move) antProject.createTask("move");
885 
886         FileSet fileSet = AntUtils.createFileset(inSourceDirectory, inPattern, new ArrayList());
887         moveTask.setTodir(new File(inDestinationDirectory));
888         moveTask.addFileset(fileSet);
889         moveTask.execute();
890     }
891 
892     /**
893      * This method will movie files from the source directory to the destination directory based on
894      * the pattern.
895      *
896      * @param from The source file to rename.
897      * @param to   The file to rename to.
898      */
899     protected void renameFile(final File from, final File to) {
900         Move moveTask = (Move) antProject.createTask("move");
901 
902         moveTask.setFile(from);
903         moveTask.setTofile(to);
904         moveTask.execute();
905     }
906 }