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
42
43
44
45
46
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
57 private static final ThreadLocal<Map> propertiesContextHolder = new ThreadLocal<Map>();
58
59
60
61
62
63
64
65
66 private String destinationDirectory;
67
68
69
70
71
72
73 private String trunk;
74
75
76
77
78
79
80 private String tag;
81
82
83
84
85
86
87
88
89
90 private MavenProject project;
91
92 public void execute() throws MojoExecutionException, MojoFailureException {
93
94 System.setProperty("file.encoding", "UTF-8");
95
96
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
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
120 log("Installing source from data-common module...");
121 export("data/common/src", (modular) ? "core/src" : destinationDirectory);
122
123
124 log("Installing source from " + daoFramework + " module...");
125 export("data/" + daoFramework + "/src", (modular) ? "core/src" : destinationDirectory);
126
127
128 log("Installing source from service module...");
129 export("service/src", (modular) ? "core/src" : destinationDirectory);
130
131
132 moveFiles((modular) ? "core/src/main" : destinationDirectory + "/main",
133 (modular) ? "core/src/test" : destinationDirectory + "/test", "**/Base*TestCase.java");
134
135
136 deleteFile("test/resources/hibernate.cfg.xml");
137 deleteFile("test/resources/META-INF");
138 deleteFile("test/resources/sql-map-config.xml");
139
140
141
142 if ("jpa".equalsIgnoreCase(daoFramework)) {
143 deleteFile("main/resources/hibernate.cfg.xml");
144 }
145 }
146
147
148
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
167
168
169
170 deleteFile("main/resources/hibernate.cfg.xml");
171 deleteFile("main/resources/META-INF");
172 deleteFile("main/resources/sql-map-config.xml");
173
174
175 deleteFile("test/resources/jdbc.properties");
176 } else if (!isAppFuse()) {
177
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
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
199 newDependencies = addModuleDependencies(newDependencies, "root", "");
200
201
202 newDependencies = addModuleDependencies(newDependencies, "data", "data");
203
204
205 newDependencies = addModuleDependencies(newDependencies, "data-common", "data/common");
206
207
208 newDependencies = addModuleDependencies(newDependencies, daoFramework, "data/" + daoFramework);
209
210
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
221 newDependencies = addModuleDependencies(newDependencies, "root", "");
222
223 createFullSourcePom(newDependencies);
224 }
225
226 if (project.getPackaging().equals("jar")) {
227 newDependencies.clear();
228
229
230 newDependencies = addModuleDependencies(newDependencies, "data", "data");
231
232
233 newDependencies = addModuleDependencies(newDependencies, "data-common", "data/common");
234
235
236 newDependencies = addModuleDependencies(newDependencies, daoFramework, "data/" + daoFramework);
237
238
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
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
265 newDependencies = addModuleDependencies(newDependencies, "web-common", "web/common");
266
267
268 }
269
270
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
305
306
307
308
309
310
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
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
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
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
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
369 Map<String, String> propertiesForPom = new LinkedHashMap<String, String>();
370
371 for (String key : projectProperties) {
372
373 if (!currentKeys.contains(key)) {
374 String value = appfuseProperties.getProperty(key);
375
376
377 if (value == null) {
378 continue;
379 }
380
381
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("&")) {
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
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
435 String pomXml = writer.toString();
436 int startTag = pomXml.indexOf("\n <dependencies>");
437
438 String dependencyXml = pomXml.substring(startTag, pomXml.indexOf("</dependencies>", startTag));
439
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
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
467
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");
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
500 if (project.getPackaging().equals("war") && project.hasParent()) {
501
502 Map properties = propertiesContextHolder.get();
503
504 Set<String> propertiesToAdd = new TreeSet<String>(properties.keySet());
505
506 StringBuffer calculatedProperties = new StringBuffer();
507
508 for (String key : propertiesToAdd) {
509
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("&")) {
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
533 originalPom = originalPom.replaceAll(" <modules>", "");
534
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
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
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
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
584 adjustedPom = adjustedPom.replaceAll("\r", "");
585 } else if (os.startsWith("Windows")) {
586
587 adjustedPom = adjustedPom.replaceAll(">\n", ">\r\n");
588 }
589
590 return adjustedPom;
591 }
592
593 private Properties getAppFuseProperties() {
594
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
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
618
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
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
666 if (moduleName.equalsIgnoreCase("root")) {
667 appfuseProperties = p.getOriginalModel().getProperties();
668 }
669
670
671 Set<String> artifactIds = new LinkedHashSet<String>();
672
673 for (Dependency dep : dependencies) {
674 artifactIds.add(dep.getArtifactId());
675 }
676
677
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
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
718
719
720
721
722
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
735
736
737
738
739
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 }