1 package com.atlassian.maven.plugins.amps.product.studio;
2
3 import com.atlassian.maven.plugins.amps.MavenContext;
4 import com.atlassian.maven.plugins.amps.MavenGoals;
5 import com.atlassian.maven.plugins.amps.Product;
6 import com.atlassian.maven.plugins.amps.ProductArtifact;
7 import com.atlassian.maven.plugins.amps.product.AmpsProductHandler;
8 import com.atlassian.maven.plugins.amps.product.ProductHandler;
9 import com.atlassian.maven.plugins.amps.product.ProductHandlerFactory;
10 import com.atlassian.maven.plugins.amps.util.ConfigFileUtils;
11 import com.atlassian.maven.plugins.amps.util.ConfigFileUtils.Replacement;
12 import com.atlassian.maven.plugins.amps.util.ProjectUtils;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.Lists;
16 import com.google.common.collect.Maps;
17 import org.apache.commons.io.FileUtils;
18 import org.apache.commons.io.IOUtils;
19 import org.apache.maven.model.Model;
20 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
21 import org.apache.maven.plugin.MojoExecutionException;
22 import org.apache.maven.plugin.logging.Log;
23 import org.apache.maven.surefire.shade.org.apache.commons.lang.StringUtils;
24
25 import java.io.BufferedReader;
26 import java.io.File;
27 import java.io.FileReader;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.Reader;
31 import java.io.UnsupportedEncodingException;
32 import java.net.URLEncoder;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Map;
39 import java.util.Properties;
40
41 import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO;
42 import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO_BAMBOO;
43 import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO_CONFLUENCE;
44 import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO_CROWD;
45 import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO_FECRU;
46 import static com.atlassian.maven.plugins.amps.product.ProductHandlerFactory.STUDIO_JIRA;
47 import static com.atlassian.maven.plugins.amps.util.ZipUtils.unzip;
48 import static org.apache.commons.io.FileUtils.copyDirectory;
49
50
51
52
53
54
55 public class StudioProductHandler extends AmpsProductHandler
56 {
57 private static final String STUDIO_PROPERTIES = "home/studio.properties";
58 private static final String STUDIO_TEST_PROPERTIES = "studiotest.properties";
59 private static final String STUDIO_INITIAL_DATA_PROPERTIES = "home/studio-initial-data.properties";
60 private static final String DEVMODE_HAL_LICENSES_XML = "home/devmode-hal-licenses.xml";
61 private static final String STUDIO_INITIAL_DATA_XML = "home/studio-initial-data.xml";
62
63
64 private static final String ONDEMAND_VERSION_TOKEN = "STUDIO-VERSION";
65 private static final String ONDEMAND_GROUP_ID = "com.atlassian.studio";
66
67
68 private static final String JIRA_VERSION_KEY = "jira.version";
69 private static final String CONFLUENCE_VERSION_KEY = "confluence.version";
70
71 private static final Map<String, String> defaultContextPaths = new HashMap<String, String>()
72 {
73 {
74 put(STUDIO_BAMBOO, "/builds");
75 put(STUDIO_CONFLUENCE, "/wiki");
76 put(STUDIO_CROWD, "/crowd");
77 put(STUDIO_FECRU, "/");
78 put(STUDIO_JIRA, "/jira");
79 }
80 };
81
82 private static final Map<String, Integer> defaultDebugPorts = new HashMap<String, Integer>()
83 {
84 {
85 put(STUDIO_BAMBOO, 5011);
86 put(STUDIO_CONFLUENCE, 5007);
87 put(STUDIO_CROWD, 5003);
88 put(STUDIO_FECRU, 5005);
89 put(STUDIO_JIRA, 5009);
90 }
91 };
92
93 public StudioProductHandler(MavenContext context, MavenGoals goals)
94 {
95 super(context, goals);
96 }
97
98
99
100 @Override
101 protected ProductArtifact getTestResourcesArtifact()
102 {
103 return new ProductArtifact("com.atlassian.studio", "studio-test-resources");
104 }
105
106
107
108 @Override
109 public String getId()
110 {
111 return STUDIO;
112 }
113
114
115
116
117
118
119
120
121
122
123 public static List<String> getDependantInstances(Product studioContext) throws MojoExecutionException
124 {
125 if (!STUDIO.equals(studioContext.getId()))
126 {
127 return Collections.emptyList();
128 }
129 StudioProperties studioProperties = getStudioProperties(studioContext);
130 studioProperties.setStudioProduct(studioContext);
131 List<String> instanceIds = studioContext.getInstanceIds();
132 if (instanceIds.isEmpty())
133 {
134 instanceIds.add(STUDIO_CROWD);
135 instanceIds.add(STUDIO_JIRA);
136 instanceIds.add(STUDIO_CONFLUENCE);
137 instanceIds.add(STUDIO_BAMBOO);
138 instanceIds.add(STUDIO_FECRU);
139 }
140 return instanceIds;
141 }
142
143
144
145
146
147 @Override
148 public int start(Product ctx) throws MojoExecutionException
149 {
150
151 sanityCheck(ctx);
152
153
154 createStudioHome(ctx);
155
156
157
158
159 File symlink = new File(context.getProject().getBuild().getDirectory(), "svn");
160 if (!symlink.exists())
161 {
162
163 createSymlink(ctx.getStudioProperties().getSvnRoot(), symlink);
164 }
165
166 return 0;
167 }
168
169
170
171
172 private void sanityCheck(Product studioProduct) throws MojoExecutionException
173 {
174 StudioProperties properties = studioProduct.getStudioProperties();
175 if (properties == null)
176 {
177 throw new MojoExecutionException(String.format("Something went wrong when starting %s. The 'studio' handler was not initialised propertly.",
178 studioProduct.getInstanceId()));
179 }
180 if (properties.getCrowd() == null || properties.getCrowd().getStudioProperties() == null)
181 {
182 log.error(String.format(
183 "You won't be able to run %s, Studio-Crowd was not configured properly.", studioProduct.getInstanceId()));
184 }
185 if (properties.isJiraEnabled() && (properties.getJira() == null || properties.getJira().getStudioProperties() == null))
186 {
187 log.error(String.format(
188 "You won't be able to run %s, Studio-JIRA was not configured properly.", studioProduct.getInstanceId()));
189 }
190 if (properties.isConfluenceEnabled() && (properties.getConfluence() == null || properties.getConfluence().getStudioProperties() == null))
191 {
192 log.error(String.format(
193 "You won't be able to run %s, Studio-Confluence was not configured properly.", studioProduct.getInstanceId()));
194 }
195 if (properties.isFisheyeEnabled() && (properties.getFisheye() == null || properties.getFisheye().getStudioProperties() == null))
196 {
197 log.error(String.format(
198 "You won't be able to run %s, Studio-Fisheye was not configured properly.", studioProduct.getInstanceId()));
199 }
200 if (properties.isBambooEnabled() && (properties.getBamboo() == null || properties.getBamboo().getStudioProperties() == null))
201 {
202 log.error(String.format(
203 "You won't be able to run %s, Studio-Bamboo was not configured properly.", studioProduct.getInstanceId()));
204 }
205 }
206
207 @Override
208 public void stop(Product ctx) throws MojoExecutionException
209 {
210
211 File symlink = new File(context.getProject().getBuild().getDirectory(), "svn");
212 symlink.deleteOnExit();
213
214
215 }
216
217 @Override
218 public int getDefaultHttpPort()
219 {
220
221 return 0;
222 }
223
224
225
226
227
228
229
230
231
232
233 public static boolean setDefaultValues(MavenContext context, Product product)
234 {
235
236
237 String defaultContextPath = defaultContextPaths.get(product.getId());
238 if (defaultContextPath != null)
239 {
240
241 if (product.getOutput() == null)
242 {
243 product.setOutput(new File(context.getProject().getBuild().getDirectory(), product.getInstanceId() + ".log").getAbsolutePath());
244 }
245 if (product.getContextPath() == null)
246 {
247 product.setContextPath(defaultContextPath);
248 }
249 if (product.getVersion() == null)
250 {
251
252
253 product.setVersion(ONDEMAND_VERSION_TOKEN);
254 }
255
256
257 if (product.getJvmDebugPort() == 0)
258 {
259 product.setJvmDebugPort(defaultDebugPorts.get(product.getId()));
260 }
261
262
263 if (product.getShutdownEnabled() == null)
264 {
265 product.setShutdownEnabled(Boolean.TRUE);
266 }
267
268 return true;
269 }
270 return false;
271 }
272
273
274
275
276
277
278
279
280
281
282 public void configureStudioProducts(Map<String, Product> productMap) throws MojoExecutionException
283 {
284
285 for (Product studioContext : productMap.values())
286 {
287 if (STUDIO.equals(studioContext.getId()))
288 {
289 StudioProperties studioProperties = getStudioProperties(studioContext);
290
291 boolean confluenceStandalone = true;
292 final OnDemandProductVersions versions = new OnDemandProductVersions(studioContext, studioProperties);
293
294
295 for (String instanceId : getDependantInstances(studioContext))
296 {
297 Product product = productMap.get(instanceId);
298
299
300
301
302
303
304
305 if (STUDIO_CROWD.equals(product.getId()))
306 {
307 studioProperties.setCrowd(product);
308 if (product.getSynchronousStartup() == null)
309 {
310 product.setSynchronousStartup(Boolean.TRUE);
311 }
312 }
313 else if (STUDIO_CONFLUENCE.equals(product.getId()))
314 {
315 studioProperties.setConfluence(product);
316 if (product.getSynchronousStartup() == null)
317 {
318 product.setSynchronousStartup(studioContext.getSynchronousStartup());
319 }
320 }
321 else if (STUDIO_JIRA.equals(product.getId()))
322 {
323 studioProperties.setJira(product);
324 confluenceStandalone = false;
325 if (product.getSynchronousStartup() == null)
326 {
327 product.setSynchronousStartup(studioContext.getSynchronousStartup());
328 }
329 }
330 else if (STUDIO_FECRU.equals(product.getId()))
331 {
332 studioProperties.setFisheye(product);
333 confluenceStandalone = false;
334 }
335 else if (STUDIO_BAMBOO.equals(product.getId()))
336 {
337 studioProperties.setBamboo(product);
338 confluenceStandalone = false;
339 if (product.getSynchronousStartup() == null)
340 {
341 product.setSynchronousStartup(studioContext.getSynchronousStartup());
342 }
343 }
344 else
345 {
346 throw new MojoExecutionException("A non-studio product was listed in a Studio instance: " + product.getInstanceId());
347 }
348
349 studioProperties.setModeConfluenceStandalone(confluenceStandalone);
350
351
352 product.setStudioProperties(studioProperties);
353
354 if (ONDEMAND_VERSION_TOKEN.equals(product.getVersion()))
355 {
356 product.setVersion(versions.getVersion(product));
357 }
358 }
359
360
361 File studioHomeDir = getHomeDirectory(studioContext);
362 File studioCommonsDir = studioHomeDir.getParentFile();
363 File svnHomeDir = new File(studioCommonsDir, "svn-home");
364 File webDavDir = new File(studioCommonsDir, "webdav-home");
365 String svnPublicUrl;
366 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"))
367 {
368 svnPublicUrl = "file:///" + svnHomeDir.getAbsolutePath();
369 log.warn("Studio is only designed to run on Linux systems.");
370 }
371 else
372 {
373 svnPublicUrl = "file://" + svnHomeDir.getAbsolutePath();
374 }
375
376 studioProperties.setStudioHome(studioHomeDir.getAbsolutePath());
377 studioProperties.setSvnRoot(svnHomeDir.getAbsolutePath());
378 studioProperties.setSvnPublicUrl(svnPublicUrl);
379 studioProperties.setWebDavHome(webDavDir.getAbsolutePath());
380 }
381 }
382 }
383
384
385
386
387
388
389
390
391
392 private static StudioProperties getStudioProperties(Product studioContext)
393 {
394 StudioProperties properties = studioContext.getStudioProperties();
395 if (properties == null)
396 {
397 properties = new StudioProperties(studioContext);
398 studioContext.setStudioProperties(properties);
399 }
400 return properties;
401 }
402
403
404
405
406
407
408
409
410
411
412
413 @Override
414 public File getSnapshotDirectory(Product studio)
415 {
416 return getHomeDirectory(studio).getParentFile();
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456 public void createStudioHome(Product studio) throws MojoExecutionException
457 {
458 StudioProperties properties = getStudioProperties(studio);
459
460
461 File studioHomeDir = new File(properties.getStudioHome());
462 File studioCommonsDir = studioHomeDir.getParentFile();
463
464
465 if (!studioHomeDir.exists())
466 {
467 extractHome(studioCommonsDir, studio);
468 if (!studioHomeDir.exists())
469 {
470 throw new MojoExecutionException("The Studio home zip must contain a '*/*/home' folder");
471 }
472 }
473
474 File svnHomeDir = new File(properties.getSvnRoot());
475 if (!svnHomeDir.exists())
476 {
477 throw new MojoExecutionException("The Studio home zip must contain a '*/*/svn-home' folder");
478 }
479
480 File webDavDir = new File(properties.getWebDavHome());
481 if (!webDavDir.exists())
482 {
483 throw new MojoExecutionException("The Studio home zip must contain a '*/*/webdav-home' folder");
484 }
485
486
487 parameteriseFiles(studioCommonsDir, studio);
488
489
490 properties.overrideSystemProperty("studio.home", studioHomeDir.getAbsolutePath());
491 properties.overrideSystemProperty("studio.initial.data.xml", new File(studioCommonsDir, STUDIO_INITIAL_DATA_XML).getAbsolutePath());
492 properties.overrideSystemProperty("studio.initial.data.properties", new File(studioCommonsDir, STUDIO_INITIAL_DATA_PROPERTIES).getAbsolutePath());
493 properties.overrideSystemProperty("studio.hal.instance.uri", new File(studioCommonsDir, DEVMODE_HAL_LICENSES_XML).getAbsolutePath());
494
495
496
497 Product crowd = properties.getCrowd();
498 if (crowd.getDataPath() == null)
499 {
500 crowd.setDataPath(new File(studioCommonsDir, "crowd-home").getAbsolutePath());
501 }
502
503 Product confluence = properties.getConfluence();
504 if (confluence != null && confluence.getDataPath() == null)
505 {
506 confluence.setDataPath(new File(studioCommonsDir, "confluence-home").getAbsolutePath());
507 }
508
509 Product jira = properties.getJira();
510 if (jira != null && jira.getDataPath() == null)
511 {
512 jira.setDataPath(new File(studioCommonsDir, "jira-home").getAbsolutePath());
513 }
514
515 Product bamboo = properties.getBamboo();
516 if (bamboo != null && bamboo.getDataPath() == null)
517 {
518 bamboo.setDataPath(new File(studioCommonsDir, "bamboo-home").getAbsolutePath());
519 }
520
521 Product fecru = properties.getFisheye();
522 if (fecru != null && fecru.getDataPath() == null)
523 {
524 fecru.setDataPath(new File(studioCommonsDir, "fecru-home").getAbsolutePath());
525 }
526
527
528 overrideAndPatchHomeDir(studioCommonsDir, studio);
529 }
530
531 private void parameteriseFiles(File studioSnapshotCopyDir, Product studio) throws MojoExecutionException
532 {
533 ConfigFileUtils.replace(getConfigFiles(studio, studioSnapshotCopyDir), getReplacements(studio), false, log);
534 }
535
536 @Override
537 public List<File> getConfigFiles(Product studio, File studioSnapshotDir)
538 {
539 List<File> list = Lists.newArrayList();
540 list.add(new File(studioSnapshotDir, STUDIO_PROPERTIES));
541 list.add(new File(studioSnapshotDir, STUDIO_INITIAL_DATA_PROPERTIES));
542 list.add(new File(studioSnapshotDir, STUDIO_INITIAL_DATA_XML));
543 list.add(new File(studioSnapshotDir, DEVMODE_HAL_LICENSES_XML));
544 list.add(new File(project.getBuild().getTestOutputDirectory(), STUDIO_TEST_PROPERTIES));
545
546 list.add(new File(studioSnapshotDir, "fecru-home/config.xml"));
547 list.add(new File(studioSnapshotDir, "confluence-home/database/confluencedb.log"));
548 list.add(new File(studioSnapshotDir, "confluence-home/database/confluencedb.script"));
549 list.add(new File(studioSnapshotDir, "jira-home/database.log"));
550 list.add(new File(studioSnapshotDir, "jira-home/database.script"));
551 list.add(new File(studioSnapshotDir, "bamboo-home/database.log"));
552 list.add(new File(studioSnapshotDir, "bamboo-home/database.script"));
553 return list;
554 }
555
556
557
558
559 @Override
560 public List<Replacement> getReplacements(final Product studio)
561 {
562 List<Replacement> replacements = super.getReplacements(studio);
563 replacements.addAll(new ArrayList<Replacement>()
564 {
565 private void putIfNotNull(String key, String value)
566 {
567 putIfNotNull(key, value, true);
568 }
569
570 private void putIfNotNull(String key, String value, boolean reversible)
571 {
572 if (reversible && StringUtils.isNotBlank(value))
573 {
574 add(new Replacement(key, value));
575 }
576 else if (value != null)
577 {
578 add(new Replacement(key, value, false));
579 }
580 }
581
582
583 {
584 StudioProperties properties = studio.getStudioProperties();
585 putIfNotNull("%GREENHOPPER-LICENSE%", "test-classes/greenhopper.license");
586
587 if (properties.isJiraEnabled())
588 {
589 File attachmentsFolder = new File(getHomeDirectory(properties.getJira()), "attachments");
590 putIfNotNull("%JIRA-ATTACHMENTS%", attachmentsFolder.getAbsolutePath());
591 putIfNotNull("%JIRA-BASE-URL%", properties.getJiraUrl());
592 putIfNotNull("%JIRA-HOST-URL%", properties.getJiraHostUrl());
593 putIfNotNull("%JIRA-CONTEXT%", properties.getJiraContextPath(), false);
594 }
595
596 if (properties.isConfluenceEnabled())
597 {
598 putIfNotNull("%CONFLUENCE-BASE-URL%", properties.getConfluenceUrl());
599 putIfNotNull("%CONFLUENCE-HOST-URL%", properties.getConfluenceHostUrl());
600 putIfNotNull("%CONFLUENCE-CONTEXT%", properties.getConfluenceContextPath(), false);
601 }
602
603 if (properties.isFisheyeEnabled())
604 {
605 putIfNotNull("%FISHEYE-BASE-URL%", properties.getFisheyeUrl());
606 putIfNotNull("%FISHEYE-HOST-URL%", properties.getFisheyeHostUrl());
607 putIfNotNull("%FISHEYE-CONTROL-PORT%", properties.getFisheyeControlPort());
608 putIfNotNull("%FISHEYE-CONTEXT%", properties.getFisheyeContextPath(), false);
609 putIfNotNull("%FISHEYE-SHUTDOWN-ENABLED%", String.valueOf(firstNotNull(properties.getFisheyeShutdownEnabled(), Boolean.TRUE)));
610 }
611
612 if (properties.isBambooEnabled())
613 {
614 putIfNotNull("%BAMBOO-BASE-URL%", properties.getBambooUrl());
615 putIfNotNull("%BAMBOO-HOST-URL%", properties.getBambooHostUrl());
616 putIfNotNull("%BAMBOO-CONTEXT%", properties.getBambooContextPath(), false);
617 putIfNotNull("%BAMBOO-ENABLED%", "true", false);
618 }
619 else
620 {
621 putIfNotNull("%BAMBOO-ENABLED%", "false", false);
622 }
623
624 putIfNotNull("%CROWD-BASE-URL%", properties.getCrowdUrl());
625 putIfNotNull("%CROWD-HOST-URL%", properties.getCrowdHostUrl());
626 putIfNotNull("%CROWD-CONTEXT%", properties.getCrowdContextPath(), false);
627
628 putIfNotNull("%SVN-BASE-URL%", properties.getSvnRoot());
629 putIfNotNull("%SVN-PUBLIC-URL%", properties.getSvnPublicUrl());
630 putIfNotNull("%SVN-HOOKS%", properties.getSvnHooks());
631
632 putIfNotNull("%STUDIO-DATA-LOCATION%", "", false);
633 putIfNotNull("%STUDIO-HOME%", properties.getStudioHome());
634 putIfNotNull("%GAPPS-ENABLED%", Boolean.toString(properties.isGappsEnabled()), false);
635 if (properties.isGappsEnabled())
636 {
637 putIfNotNull("%GAPPS-ENABLED%", Boolean.toString(true), false);
638 putIfNotNull("%STUDIO-GAPPS-DOMAIN%", properties.getGappsDomain());
639 }
640 else
641 {
642 putIfNotNull("%GAPPS-ENABLED%", Boolean.toString(false), false);
643 }
644 putIfNotNull("%STUDIO-WEBDAV-DIRECTORY%", properties.getWebDavHome());
645 putIfNotNull("%STUDIO-SVN-IMPORT-STAGING-DIRECTORY%", properties.getSvnImportStagingDirectory());
646
647 try
648 {
649 putIfNotNull("%SVN_HOME_URL_ENCODED%", URLEncoder.encode(propertiesEncode(properties.getSvnRoot()), "UTF-8"));
650 }
651 catch (UnsupportedEncodingException badJvm)
652 {
653 throw new RuntimeException("UTF-8 should be supported on any JVM", badJvm);
654 }
655
656 }
657 });
658 return replacements;
659 }
660
661 private void createSymlink(String source, File target) throws MojoExecutionException
662 {
663
664 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"))
665 {
666 log.error("Studio is designed to run on Linux systems. As you can't create a symbolic link for SVN, you " +
667 "will have problems using SVN, FishEye and Bamboo, and possibly the other products.");
668 return;
669 }
670
671
672 String[] systemCommand = {
673 "ln",
674 "-s",
675 source,
676 target.getAbsolutePath()
677 };
678 try
679 {
680
681 Process symlinkCreation = Runtime.getRuntime().exec(systemCommand);
682
683
684 BufferedReader errorStream = new BufferedReader(
685 new InputStreamReader(symlinkCreation.getErrorStream()));
686 String errorLine = null;
687 boolean hasErrors = false;
688 while ((errorLine = errorStream.readLine()) != null)
689 {
690 if (!hasErrors)
691 {
692 System.err.println("Error while executing " + systemCommand + ": ");
693 hasErrors = true;
694 }
695 System.err.println(errorLine);
696 }
697 if (hasErrors)
698 {
699 System.out.println("Please execute this command in your command line and press a key to continue");
700 System.in.read();
701 }
702 }
703 catch (IOException e)
704 {
705 throw new MojoExecutionException("Could not create the symlink: " + systemCommand, e);
706 }
707 }
708
709
710
711
712
713 private void extractHome(File target, Product studio) throws MojoExecutionException
714 {
715
716 File testResourcesZip = getProductHomeData(studio);
717
718 try
719 {
720 if (!testResourcesZip.exists())
721 {
722 throw new MojoExecutionException(String.format("This source doesn't exist: %s", testResourcesZip.getAbsoluteFile()));
723 }
724 if (testResourcesZip.isDirectory())
725 {
726 copyDirectory(testResourcesZip, target);
727 }
728 else
729 {
730 unzip(testResourcesZip, target.getAbsolutePath(), 2);
731 }
732 }
733 catch (IOException ioe)
734 {
735 throw new MojoExecutionException(String.format("Unable to copy/unzip the studio home from %s to %s", testResourcesZip.getAbsolutePath(),
736 target.getAbsolutePath()), ioe);
737 }
738 }
739
740
741
742
743 static void processProductsHomeDirectory(Log log, Product ctx, File homeDir) throws MojoExecutionException
744 {
745
746
747 if (ctx.getStudioProperties() == null)
748 {
749 throw new MojoExecutionException(String.format("%s product is dependant on Studio. You must include the Studio product in your execution.",
750 ctx.getInstanceId()));
751 }
752 }
753
754
755
756
757
758
759
760
761
762
763
764
765 static void addProductHandlerOverrides(Log log, Product ctx, File homeDir, File explodedWarDir) throws MojoExecutionException
766 {
767 addProductHandlerOverrides(log, ctx, homeDir, explodedWarDir, "WEB-INF/classes/crowd.properties");
768 }
769
770
771
772
773
774
775
776
777
778
779
780
781 static void addProductHandlerOverrides(Log log, Product ctx, File homeDir, File explodedWarDir, String crowdPropertiesPath) throws MojoExecutionException
782 {
783 File crowdProperties = new File(explodedWarDir, crowdPropertiesPath);
784 if (checkFileExists(crowdProperties, log))
785 {
786 parametriseCrowdFile(crowdProperties, ctx.getStudioProperties().getCrowdUrl(), log);
787 }
788 }
789
790
791
792
793
794
795
796
797
798
799
800 private static void parametriseCrowdFile(File crowdProperties, String crowdUrl, Log log) throws MojoExecutionException
801 {
802 List<Replacement> replacements = Lists.newArrayList();
803 replacements.add(new Replacement("%CROWD-INTERNAL-URL%", crowdUrl));
804 replacements.add(new Replacement("%CROWD-URL%", crowdUrl));
805
806 ConfigFileUtils.replace(crowdProperties, replacements, false, log);
807 }
808
809
810 public void cleanupProductHomeForZip(Product studioProduct, File studioHome) throws MojoExecutionException
811 {
812 try
813 {
814
815 StudioProperties studioProperties = studioProduct.getStudioProperties();
816
817
818
819 Map<String, Product> products = Maps.newHashMap();
820 products.put("crowd-home", studioProperties.getCrowd());
821 products.put("confluence-home", studioProperties.getConfluence());
822 products.put("jira-home", studioProperties.getJira());
823 products.put("fecru-home", studioProperties.getFisheye());
824 products.put("bamboo-home", studioProperties.getBamboo());
825
826
827 for (String productHomeName : products.keySet())
828 {
829 Product product = products.get(productHomeName);
830 if (product != null)
831 {
832 File productDestinationDirectory = new File(studioHome, productHomeName);
833 File productHomeDirectory = getHomeDirectory(product);
834
835
836 if (productDestinationDirectory.exists())
837 {
838 FileUtils.deleteDirectory(productDestinationDirectory);
839 }
840 ProjectUtils.createDirectory(productDestinationDirectory);
841 copyDirectory(productHomeDirectory, productDestinationDirectory);
842
843 }
844 }
845
846
847 super.cleanupProductHomeForZip(studioProduct, studioHome);
848
849
850
851
852 for (String productHomeName : products.keySet())
853 {
854 Product product = products.get(productHomeName);
855 if (product != null)
856 {
857 File productDestinationDirectory = new File(studioHome, productHomeName);
858 ProductHandler handler = ProductHandlerFactory.create(product.getId(), context, goals);
859 handler.cleanupProductHomeForZip(product, productDestinationDirectory);
860 }
861 }
862 }
863 catch (IOException ioe)
864 {
865 throw new MojoExecutionException("Could not copy a product home directory.", ioe);
866 }
867 }
868
869 static boolean checkFileExists(File file, Log log)
870 {
871 if (!file.exists())
872 {
873 log.warn(String.format("%s does not exist. Will skip customisation", file.getAbsolutePath()));
874 return false;
875 }
876 return true;
877 }
878
879
880
881
882
883
884
885 public static <T> T firstNotNull(T... values)
886 {
887 for (T t : values)
888 {
889 if (t != null)
890 {
891 return t;
892 }
893 }
894 return null;
895 }
896
897 @VisibleForTesting
898 protected Model getOnDemandPomModel(Product ondemand, StudioProperties properties) throws MojoExecutionException
899 {
900 File pomFile = getOnDemandPom(ondemand, properties);
901 return readModel(pomFile);
902 }
903
904 private Model readModel(File pomFile) throws MojoExecutionException
905 {
906 Reader filePomReader = null;
907 try
908 {
909 filePomReader = new BufferedReader(new FileReader(pomFile));
910 MavenXpp3Reader pomReader = new MavenXpp3Reader();
911 return pomReader.read(filePomReader);
912 }
913 catch (Exception e)
914 {
915 throw new MojoExecutionException("Failed to read ondemand-fireball pom", e);
916 }
917 finally
918 {
919 IOUtils.closeQuietly(filePomReader);
920 }
921 }
922
923 private File getOnDemandPom(Product ondemand, StudioProperties properties) throws MojoExecutionException
924 {
925 final File baseDir = getBaseDirectory(ondemand);
926 File ondemandPom = new File(baseDir, "ondemand.pom");
927 if (!ondemandPom.exists())
928 {
929 ondemandPom = goals.copyArtifact("ondemand.pom", baseDir, getPomArtifact(properties), "pom");
930 }
931 return ondemandPom;
932 }
933
934 private ProductArtifact getPomArtifact(StudioProperties properties)
935 {
936 final String version = properties.getVersion();
937 if (isPostOnDemandSplit(version))
938 {
939 return new ProductArtifact(ONDEMAND_GROUP_ID, "ondemand-fireball", version);
940 }
941 else
942 {
943 return new ProductArtifact(ONDEMAND_GROUP_ID, "studio-parent", version);
944 }
945
946 }
947
948 private boolean isPostOnDemandSplit(String version)
949 {
950
951 return getVersionNumber(version) >= 133 && !version.equals("133-partial-1");
952 }
953
954 private int getVersionNumber(String version)
955 {
956 final int digitsCount = getDigitsCount(version);
957 if (digitsCount == 0)
958 {
959 return 0;
960 }
961 else
962 {
963 return Integer.parseInt(version.substring(0, digitsCount));
964 }
965 }
966
967 private int getDigitsCount(String version)
968 {
969 for (int i=0; i<version.length(); i++)
970 {
971 if (!Character.isDigit(version.charAt(i)))
972 {
973 return i;
974 }
975 }
976 return version.length();
977 }
978
979
980
981
982
983 private class OnDemandProductVersions
984 {
985 private final Map<String,String> productVersionMappings;
986 private final StudioProperties properties;
987
988 OnDemandProductVersions(Product ondemand, StudioProperties properties) throws MojoExecutionException
989 {
990 this.properties = properties;
991 this.productVersionMappings = initMappings(ondemand);
992 }
993
994 String getVersion(Product product)
995 {
996 if (productVersionMappings.containsKey(product.getId()))
997 {
998 return productVersionMappings.get(product.getId());
999 }
1000 else
1001 {
1002
1003 return properties.getVersion();
1004 }
1005 }
1006
1007 private Map<String, String> initMappings(Product ondemand) throws MojoExecutionException
1008 {
1009
1010
1011
1012
1013 ImmutableMap.Builder<String,String> builder = ImmutableMap.builder();
1014
1015
1016 return builder.build();
1017 }
1018
1019 private void validate(Model model) throws MojoExecutionException
1020 {
1021 Properties props = model.getProperties();
1022 validatePropertyExists(JIRA_VERSION_KEY, props);
1023 validatePropertyExists(CONFLUENCE_VERSION_KEY, props);
1024 }
1025
1026 private String getVersionProperty(String key, Properties props)
1027 {
1028 if (props.containsKey(key))
1029 {
1030 return props.getProperty(key);
1031 }
1032 else
1033 {
1034 return properties.getVersion();
1035 }
1036 }
1037
1038 private void validatePropertyExists(String key, Properties props)
1039 {
1040 if (!props.containsKey(key))
1041 {
1042 context.getLog().warn("Expected property '" + key + "' in the OnDemand fireball POM (version "
1043 + properties.getVersion() + ") not found. OnDemand version will be used instead");
1044 }
1045 }
1046
1047
1048 }
1049
1050 }