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 public static void setDefaultValues(MavenContext context, Product product)
233 {
234
235
236 String defaultContextPath = defaultContextPaths.get(product.getId());
237 if (defaultContextPath != null)
238 {
239
240 if (product.getOutput() == null)
241 {
242 product.setOutput(new File(context.getProject().getBuild().getDirectory(), product.getInstanceId() + ".log").getAbsolutePath());
243 }
244 if (product.getContextPath() == null)
245 {
246 product.setContextPath(defaultContextPath);
247 }
248 if (product.getVersion() == null)
249 {
250
251
252 product.setVersion(ONDEMAND_VERSION_TOKEN);
253 }
254
255
256 if (product.getJvmDebugPort() == 0)
257 {
258 product.setJvmDebugPort(defaultDebugPorts.get(product.getId()));
259 }
260
261
262 if (product.getShutdownEnabled() == null)
263 {
264 product.setShutdownEnabled(Boolean.TRUE);
265 }
266 }
267 }
268
269
270
271
272
273
274
275
276
277
278 public void configureStudioProducts(Map<String, Product> productMap) throws MojoExecutionException
279 {
280
281 for (Product studioContext : productMap.values())
282 {
283 if (STUDIO.equals(studioContext.getId()))
284 {
285 StudioProperties studioProperties = getStudioProperties(studioContext);
286
287 boolean confluenceStandalone = true;
288 final OnDemandProductVersions versions = new OnDemandProductVersions(studioContext, studioProperties);
289
290
291 for (String instanceId : getDependantInstances(studioContext))
292 {
293 Product product = productMap.get(instanceId);
294
295
296
297
298
299
300
301 if (STUDIO_CROWD.equals(product.getId()))
302 {
303 studioProperties.setCrowd(product);
304 if (product.getSynchronousStartup() == null)
305 {
306 product.setSynchronousStartup(Boolean.TRUE);
307 }
308 }
309 else if (STUDIO_CONFLUENCE.equals(product.getId()))
310 {
311 studioProperties.setConfluence(product);
312 if (product.getSynchronousStartup() == null)
313 {
314 product.setSynchronousStartup(studioContext.getSynchronousStartup());
315 }
316 }
317 else if (STUDIO_JIRA.equals(product.getId()))
318 {
319 studioProperties.setJira(product);
320 confluenceStandalone = false;
321 if (product.getSynchronousStartup() == null)
322 {
323 product.setSynchronousStartup(studioContext.getSynchronousStartup());
324 }
325 }
326 else if (STUDIO_FECRU.equals(product.getId()))
327 {
328 studioProperties.setFisheye(product);
329 confluenceStandalone = false;
330 }
331 else if (STUDIO_BAMBOO.equals(product.getId()))
332 {
333 studioProperties.setBamboo(product);
334 confluenceStandalone = false;
335 if (product.getSynchronousStartup() == null)
336 {
337 product.setSynchronousStartup(studioContext.getSynchronousStartup());
338 }
339 }
340 else
341 {
342 throw new MojoExecutionException("A non-studio product was listed in a Studio instance: " + product.getInstanceId());
343 }
344
345 studioProperties.setModeConfluenceStandalone(confluenceStandalone);
346
347
348 product.setStudioProperties(studioProperties);
349
350 if (ONDEMAND_VERSION_TOKEN.equals(product.getVersion()))
351 {
352 product.setVersion(versions.getVersion(product));
353 }
354 }
355
356
357 File studioHomeDir = getHomeDirectory(studioContext);
358 File studioCommonsDir = studioHomeDir.getParentFile();
359 File svnHomeDir = new File(studioCommonsDir, "svn-home");
360 File webDavDir = new File(studioCommonsDir, "webdav-home");
361 String svnPublicUrl;
362 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"))
363 {
364 svnPublicUrl = "file:///" + svnHomeDir.getAbsolutePath();
365 log.warn("Studio is only designed to run on Linux systems.");
366 }
367 else
368 {
369 svnPublicUrl = "file://" + svnHomeDir.getAbsolutePath();
370 }
371
372 studioProperties.setStudioHome(studioHomeDir.getAbsolutePath());
373 studioProperties.setSvnRoot(svnHomeDir.getAbsolutePath());
374 studioProperties.setSvnPublicUrl(svnPublicUrl);
375 studioProperties.setWebDavHome(webDavDir.getAbsolutePath());
376 }
377 }
378 }
379
380
381
382
383
384
385
386
387
388 private static StudioProperties getStudioProperties(Product studioContext)
389 {
390 StudioProperties properties = studioContext.getStudioProperties();
391 if (properties == null)
392 {
393 properties = new StudioProperties(studioContext);
394 studioContext.setStudioProperties(properties);
395 }
396 return properties;
397 }
398
399
400
401
402
403
404
405
406
407
408
409 @Override
410 public File getSnapshotDirectory(Product studio)
411 {
412 return getHomeDirectory(studio).getParentFile();
413 }
414
415
416
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 public void createStudioHome(Product studio) throws MojoExecutionException
453 {
454 StudioProperties properties = getStudioProperties(studio);
455
456
457 File studioHomeDir = new File(properties.getStudioHome());
458 File studioCommonsDir = studioHomeDir.getParentFile();
459
460
461 if (!studioHomeDir.exists())
462 {
463 extractHome(studioCommonsDir, studio);
464 if (!studioHomeDir.exists())
465 {
466 throw new MojoExecutionException("The Studio home zip must contain a '*/*/home' folder");
467 }
468 }
469
470 File svnHomeDir = new File(properties.getSvnRoot());
471 if (!svnHomeDir.exists())
472 {
473 throw new MojoExecutionException("The Studio home zip must contain a '*/*/svn-home' folder");
474 }
475
476 File webDavDir = new File(properties.getWebDavHome());
477 if (!webDavDir.exists())
478 {
479 throw new MojoExecutionException("The Studio home zip must contain a '*/*/webdav-home' folder");
480 }
481
482
483 parameteriseFiles(studioCommonsDir, studio);
484
485
486 properties.overrideSystemProperty("studio.home", studioHomeDir.getAbsolutePath());
487 properties.overrideSystemProperty("studio.initial.data.xml", new File(studioCommonsDir, STUDIO_INITIAL_DATA_XML).getAbsolutePath());
488 properties.overrideSystemProperty("studio.initial.data.properties", new File(studioCommonsDir, STUDIO_INITIAL_DATA_PROPERTIES).getAbsolutePath());
489 properties.overrideSystemProperty("studio.hal.instance.uri", new File(studioCommonsDir, DEVMODE_HAL_LICENSES_XML).getAbsolutePath());
490
491
492
493 Product crowd = properties.getCrowd();
494 if (crowd.getDataPath() == null)
495 {
496 crowd.setDataPath(new File(studioCommonsDir, "crowd-home").getAbsolutePath());
497 }
498
499 Product confluence = properties.getConfluence();
500 if (confluence != null && confluence.getDataPath() == null)
501 {
502 confluence.setDataPath(new File(studioCommonsDir, "confluence-home").getAbsolutePath());
503 }
504
505 Product jira = properties.getJira();
506 if (jira != null && jira.getDataPath() == null)
507 {
508 jira.setDataPath(new File(studioCommonsDir, "jira-home").getAbsolutePath());
509 }
510
511 Product bamboo = properties.getBamboo();
512 if (bamboo != null && bamboo.getDataPath() == null)
513 {
514 bamboo.setDataPath(new File(studioCommonsDir, "bamboo-home").getAbsolutePath());
515 }
516
517 Product fecru = properties.getFisheye();
518 if (fecru != null && fecru.getDataPath() == null)
519 {
520 fecru.setDataPath(new File(studioCommonsDir, "fecru-home").getAbsolutePath());
521 }
522
523
524 overrideAndPatchHomeDir(studioCommonsDir, studio);
525 }
526
527 private void parameteriseFiles(File studioSnapshotCopyDir, Product studio) throws MojoExecutionException
528 {
529 ConfigFileUtils.replace(getConfigFiles(studio, studioSnapshotCopyDir), getReplacements(studio), false, log);
530 }
531
532 @Override
533 public List<File> getConfigFiles(Product studio, File studioSnapshotDir)
534 {
535 List<File> list = Lists.newArrayList();
536 list.add(new File(studioSnapshotDir, STUDIO_PROPERTIES));
537 list.add(new File(studioSnapshotDir, STUDIO_INITIAL_DATA_PROPERTIES));
538 list.add(new File(studioSnapshotDir, STUDIO_INITIAL_DATA_XML));
539 list.add(new File(studioSnapshotDir, DEVMODE_HAL_LICENSES_XML));
540 list.add(new File(project.getBuild().getTestOutputDirectory(), STUDIO_TEST_PROPERTIES));
541
542 list.add(new File(studioSnapshotDir, "fecru-home/config.xml"));
543 list.add(new File(studioSnapshotDir, "confluence-home/database/confluencedb.log"));
544 list.add(new File(studioSnapshotDir, "confluence-home/database/confluencedb.script"));
545 list.add(new File(studioSnapshotDir, "jira-home/database.log"));
546 list.add(new File(studioSnapshotDir, "jira-home/database.script"));
547 list.add(new File(studioSnapshotDir, "bamboo-home/database.log"));
548 list.add(new File(studioSnapshotDir, "bamboo-home/database.script"));
549 return list;
550 }
551
552
553
554
555 @Override
556 public List<Replacement> getReplacements(final Product studio)
557 {
558 List<Replacement> replacements = super.getReplacements(studio);
559 replacements.addAll(new ArrayList<Replacement>()
560 {
561 private void putIfNotNull(String key, String value)
562 {
563 putIfNotNull(key, value, true);
564 }
565
566 private void putIfNotNull(String key, String value, boolean reversible)
567 {
568 if (reversible && StringUtils.isNotBlank(value))
569 {
570 add(new Replacement(key, value));
571 }
572 else if (value != null)
573 {
574 add(new Replacement(key, value, false));
575 }
576 }
577
578
579 {
580 StudioProperties properties = studio.getStudioProperties();
581 putIfNotNull("%GREENHOPPER-LICENSE%", "test-classes/greenhopper.license");
582
583 if (properties.isJiraEnabled())
584 {
585 File attachmentsFolder = new File(getHomeDirectory(properties.getJira()), "attachments");
586 putIfNotNull("%JIRA-ATTACHMENTS%", attachmentsFolder.getAbsolutePath());
587 putIfNotNull("%JIRA-BASE-URL%", properties.getJiraUrl());
588 putIfNotNull("%JIRA-HOST-URL%", properties.getJiraHostUrl());
589 putIfNotNull("%JIRA-CONTEXT%", properties.getJiraContextPath(), false);
590 }
591
592 if (properties.isConfluenceEnabled())
593 {
594 putIfNotNull("%CONFLUENCE-BASE-URL%", properties.getConfluenceUrl());
595 putIfNotNull("%CONFLUENCE-HOST-URL%", properties.getConfluenceHostUrl());
596 putIfNotNull("%CONFLUENCE-CONTEXT%", properties.getConfluenceContextPath(), false);
597 }
598
599 if (properties.isFisheyeEnabled())
600 {
601 putIfNotNull("%FISHEYE-BASE-URL%", properties.getFisheyeUrl());
602 putIfNotNull("%FISHEYE-HOST-URL%", properties.getFisheyeHostUrl());
603 putIfNotNull("%FISHEYE-CONTROL-PORT%", properties.getFisheyeControlPort());
604 putIfNotNull("%FISHEYE-CONTEXT%", properties.getFisheyeContextPath(), false);
605 putIfNotNull("%FISHEYE-SHUTDOWN-ENABLED%", String.valueOf(firstNotNull(properties.getFisheyeShutdownEnabled(), Boolean.TRUE)));
606 }
607
608 if (properties.isBambooEnabled())
609 {
610 putIfNotNull("%BAMBOO-BASE-URL%", properties.getBambooUrl());
611 putIfNotNull("%BAMBOO-HOST-URL%", properties.getBambooHostUrl());
612 putIfNotNull("%BAMBOO-CONTEXT%", properties.getBambooContextPath(), false);
613 putIfNotNull("%BAMBOO-ENABLED%", "true", false);
614 }
615 else
616 {
617 putIfNotNull("%BAMBOO-ENABLED%", "false", false);
618 }
619
620 putIfNotNull("%CROWD-BASE-URL%", properties.getCrowdUrl());
621 putIfNotNull("%CROWD-HOST-URL%", properties.getCrowdHostUrl());
622 putIfNotNull("%CROWD-CONTEXT%", properties.getCrowdContextPath(), false);
623
624 putIfNotNull("%SVN-BASE-URL%", properties.getSvnRoot());
625 putIfNotNull("%SVN-PUBLIC-URL%", properties.getSvnPublicUrl());
626 putIfNotNull("%SVN-HOOKS%", properties.getSvnHooks());
627
628 putIfNotNull("%STUDIO-DATA-LOCATION%", "", false);
629 putIfNotNull("%STUDIO-HOME%", properties.getStudioHome());
630 putIfNotNull("%GAPPS-ENABLED%", Boolean.toString(properties.isGappsEnabled()), false);
631 if (properties.isGappsEnabled())
632 {
633 putIfNotNull("%GAPPS-ENABLED%", Boolean.toString(true), false);
634 putIfNotNull("%STUDIO-GAPPS-DOMAIN%", properties.getGappsDomain());
635 }
636 else
637 {
638 putIfNotNull("%GAPPS-ENABLED%", Boolean.toString(false), false);
639 }
640 putIfNotNull("%STUDIO-WEBDAV-DIRECTORY%", properties.getWebDavHome());
641 putIfNotNull("%STUDIO-SVN-IMPORT-STAGING-DIRECTORY%", properties.getSvnImportStagingDirectory());
642
643 try
644 {
645 putIfNotNull("%SVN_HOME_URL_ENCODED%", URLEncoder.encode(propertiesEncode(properties.getSvnRoot()), "UTF-8"));
646 }
647 catch (UnsupportedEncodingException badJvm)
648 {
649 throw new RuntimeException("UTF-8 should be supported on any JVM", badJvm);
650 }
651
652 }
653 });
654 return replacements;
655 }
656
657 private void createSymlink(String source, File target) throws MojoExecutionException
658 {
659
660 if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows"))
661 {
662 log.error("Studio is designed to run on Linux systems. As you can't create a symbolic link for SVN, you " +
663 "will have problems using SVN, FishEye and Bamboo, and possibly the other products.");
664 return;
665 }
666
667
668 String[] systemCommand = {
669 "ln",
670 "-s",
671 source,
672 target.getAbsolutePath()
673 };
674 try
675 {
676
677 Process symlinkCreation = Runtime.getRuntime().exec(systemCommand);
678
679
680 BufferedReader errorStream = new BufferedReader(
681 new InputStreamReader(symlinkCreation.getErrorStream()));
682 String errorLine = null;
683 boolean hasErrors = false;
684 while ((errorLine = errorStream.readLine()) != null)
685 {
686 if (!hasErrors)
687 {
688 System.err.println("Error while executing " + systemCommand + ": ");
689 hasErrors = true;
690 }
691 System.err.println(errorLine);
692 }
693 if (hasErrors)
694 {
695 System.out.println("Please execute this command in your command line and press a key to continue");
696 System.in.read();
697 }
698 }
699 catch (IOException e)
700 {
701 throw new MojoExecutionException("Could not create the symlink: " + systemCommand, e);
702 }
703 }
704
705
706
707
708
709 private void extractHome(File target, Product studio) throws MojoExecutionException
710 {
711
712 File testResourcesZip = getProductHomeData(studio);
713
714 try
715 {
716 if (!testResourcesZip.exists())
717 {
718 throw new MojoExecutionException(String.format("This source doesn't exist: %s", testResourcesZip.getAbsoluteFile()));
719 }
720 if (testResourcesZip.isDirectory())
721 {
722 copyDirectory(testResourcesZip, target);
723 }
724 else
725 {
726 unzip(testResourcesZip, target.getAbsolutePath(), 2);
727 }
728 }
729 catch (IOException ioe)
730 {
731 throw new MojoExecutionException(String.format("Unable to copy/unzip the studio home from %s to %s", testResourcesZip.getAbsolutePath(),
732 target.getAbsolutePath()), ioe);
733 }
734 }
735
736
737
738
739 static void processProductsHomeDirectory(Log log, Product ctx, File homeDir) throws MojoExecutionException
740 {
741
742
743 if (ctx.getStudioProperties() == null)
744 {
745 throw new MojoExecutionException(String.format("%s product is dependant on Studio. You must include the Studio product in your execution.",
746 ctx.getInstanceId()));
747 }
748 }
749
750
751
752
753
754
755
756
757
758
759
760
761 static void addProductHandlerOverrides(Log log, Product ctx, File homeDir, File explodedWarDir) throws MojoExecutionException
762 {
763 addProductHandlerOverrides(log, ctx, homeDir, explodedWarDir, "WEB-INF/classes/crowd.properties");
764 }
765
766
767
768
769
770
771
772
773
774
775
776
777 static void addProductHandlerOverrides(Log log, Product ctx, File homeDir, File explodedWarDir, String crowdPropertiesPath) throws MojoExecutionException
778 {
779 File crowdProperties = new File(explodedWarDir, crowdPropertiesPath);
780 if (checkFileExists(crowdProperties, log))
781 {
782 parametriseCrowdFile(crowdProperties, ctx.getStudioProperties().getCrowdUrl(), log);
783 }
784 }
785
786
787
788
789
790
791
792
793
794
795
796 private static void parametriseCrowdFile(File crowdProperties, String crowdUrl, Log log) throws MojoExecutionException
797 {
798 List<Replacement> replacements = Lists.newArrayList();
799 replacements.add(new Replacement("%CROWD-INTERNAL-URL%", crowdUrl));
800 replacements.add(new Replacement("%CROWD-URL%", crowdUrl));
801
802 ConfigFileUtils.replace(crowdProperties, replacements, false, log);
803 }
804
805
806 public void cleanupProductHomeForZip(Product studioProduct, File studioHome) throws MojoExecutionException
807 {
808 try
809 {
810
811 StudioProperties studioProperties = studioProduct.getStudioProperties();
812
813
814
815 Map<String, Product> products = Maps.newHashMap();
816 products.put("crowd-home", studioProperties.getCrowd());
817 products.put("confluence-home", studioProperties.getConfluence());
818 products.put("jira-home", studioProperties.getJira());
819 products.put("fecru-home", studioProperties.getFisheye());
820 products.put("bamboo-home", studioProperties.getBamboo());
821
822
823 for (String productHomeName : products.keySet())
824 {
825 Product product = products.get(productHomeName);
826 if (product != null)
827 {
828 File productDestinationDirectory = new File(studioHome, productHomeName);
829 File productHomeDirectory = getHomeDirectory(product);
830
831
832 if (productDestinationDirectory.exists())
833 {
834 FileUtils.deleteDirectory(productDestinationDirectory);
835 }
836 ProjectUtils.createDirectory(productDestinationDirectory);
837 copyDirectory(productHomeDirectory, productDestinationDirectory);
838
839 }
840 }
841
842
843 super.cleanupProductHomeForZip(studioProduct, studioHome);
844
845
846
847
848 for (String productHomeName : products.keySet())
849 {
850 Product product = products.get(productHomeName);
851 if (product != null)
852 {
853 File productDestinationDirectory = new File(studioHome, productHomeName);
854 ProductHandler handler = ProductHandlerFactory.create(product.getId(), context, goals);
855 handler.cleanupProductHomeForZip(product, productDestinationDirectory);
856 }
857 }
858 }
859 catch (IOException ioe)
860 {
861 throw new MojoExecutionException("Could not copy a product home directory.", ioe);
862 }
863 }
864
865 static boolean checkFileExists(File file, Log log)
866 {
867 if (!file.exists())
868 {
869 log.warn(String.format("%s does not exist. Will skip customisation", file.getAbsolutePath()));
870 return false;
871 }
872 return true;
873 }
874
875
876
877
878
879
880
881 public static <T> T firstNotNull(T... values)
882 {
883 for (T t : values)
884 {
885 if (t != null)
886 {
887 return t;
888 }
889 }
890 return null;
891 }
892
893 @VisibleForTesting
894 protected Model getOnDemandPomModel(Product ondemand, StudioProperties properties) throws MojoExecutionException
895 {
896 File pomFile = getOnDemandPom(ondemand, properties);
897 return readModel(pomFile);
898 }
899
900 private Model readModel(File pomFile) throws MojoExecutionException
901 {
902 Reader filePomReader = null;
903 try
904 {
905 filePomReader = new BufferedReader(new FileReader(pomFile));
906 MavenXpp3Reader pomReader = new MavenXpp3Reader();
907 return pomReader.read(filePomReader);
908 }
909 catch (Exception e)
910 {
911 throw new MojoExecutionException("Failed to read ondemand-fireball pom", e);
912 }
913 finally
914 {
915 IOUtils.closeQuietly(filePomReader);
916 }
917 }
918
919 private File getOnDemandPom(Product ondemand, StudioProperties properties) throws MojoExecutionException
920 {
921 final File baseDir = getBaseDirectory(ondemand);
922 File ondemandPom = new File(baseDir, "ondemand.pom");
923 if (!ondemandPom.exists())
924 {
925 ondemandPom = goals.copyArtifact("ondemand.pom", baseDir, getPomArtifact(properties), "pom");
926 }
927 return ondemandPom;
928 }
929
930 private ProductArtifact getPomArtifact(StudioProperties properties)
931 {
932 final String version = properties.getVersion();
933 if (isPostOnDemandSplit(version))
934 {
935 return new ProductArtifact(ONDEMAND_GROUP_ID, "ondemand-fireball", version);
936 }
937 else
938 {
939 return new ProductArtifact(ONDEMAND_GROUP_ID, "studio-parent", version);
940 }
941
942 }
943
944 private boolean isPostOnDemandSplit(String version)
945 {
946
947 return getVersionNumber(version) >= 133 && !version.equals("133-partial-1");
948 }
949
950 private int getVersionNumber(String version)
951 {
952 final int digitsCount = getDigitsCount(version);
953 if (digitsCount == 0)
954 {
955 return 0;
956 }
957 else
958 {
959 return Integer.parseInt(version.substring(0, digitsCount));
960 }
961 }
962
963 private int getDigitsCount(String version)
964 {
965 for (int i=0; i<version.length(); i++)
966 {
967 if (!Character.isDigit(version.charAt(i)))
968 {
969 return i;
970 }
971 }
972 return version.length();
973 }
974
975
976
977
978
979 private class OnDemandProductVersions
980 {
981 private final Map<String,String> productVersionMappings;
982 private final StudioProperties properties;
983
984 OnDemandProductVersions(Product ondemand, StudioProperties properties) throws MojoExecutionException
985 {
986 this.properties = properties;
987 this.productVersionMappings = initMappings(ondemand);
988 }
989
990 String getVersion(Product product)
991 {
992 if (productVersionMappings.containsKey(product.getId()))
993 {
994 return productVersionMappings.get(product.getId());
995 }
996 else
997 {
998
999 return properties.getVersion();
1000 }
1001 }
1002
1003 private Map<String, String> initMappings(Product ondemand) throws MojoExecutionException
1004 {
1005 Model model = getOnDemandPomModel(ondemand, properties);
1006 validate(model);
1007 ImmutableMap.Builder<String,String> builder = ImmutableMap.builder();
1008 builder.put(STUDIO_JIRA, getVersionProperty(JIRA_VERSION_KEY, model.getProperties()));
1009 builder.put(STUDIO_CONFLUENCE, getVersionProperty(CONFLUENCE_VERSION_KEY, model.getProperties()));
1010 return builder.build();
1011 }
1012
1013
1014
1015 private void validate(Model model) throws MojoExecutionException
1016 {
1017 Properties props = model.getProperties();
1018 validatePropertyExists(JIRA_VERSION_KEY, props);
1019 validatePropertyExists(CONFLUENCE_VERSION_KEY, props);
1020 }
1021
1022 private String getVersionProperty(String key, Properties props)
1023 {
1024 if (props.containsKey(key))
1025 {
1026 return props.getProperty(key);
1027 }
1028 else
1029 {
1030 return properties.getVersion();
1031 }
1032 }
1033
1034 private void validatePropertyExists(String key, Properties props)
1035 {
1036 if (!props.containsKey(key))
1037 {
1038 context.getLog().warn("Expected property '" + key + "' in the OnDemand fireball POM (version "
1039 + properties.getVersion() + ") not found. OnDemand version will be used instead");
1040 }
1041 }
1042
1043
1044 }
1045
1046 }