/maven-amps-plugin/src/main/java/com/atlassian/maven/plugins/amps/product/AmpsProductHandler.java

https://bitbucket.org/mmeinhold/amps · Java · 304 lines · 210 code · 40 blank · 54 comment · 19 complexity · f5e6500594e518a5ec07da9fa38a1f69 MD5 · raw file

  1. package com.atlassian.maven.plugins.amps.product;
  2. import static com.atlassian.maven.plugins.amps.util.FileUtils.copyDirectory;
  3. import static org.apache.commons.lang.StringUtils.isNotBlank;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.io.UnsupportedEncodingException;
  7. import java.net.InetAddress;
  8. import java.net.URLEncoder;
  9. import java.net.UnknownHostException;
  10. import java.util.Collections;
  11. import java.util.Comparator;
  12. import java.util.List;
  13. import com.atlassian.maven.plugins.amps.*;
  14. import org.apache.commons.io.FileUtils;
  15. import org.apache.commons.lang.StringUtils;
  16. import org.apache.maven.plugin.MojoExecutionException;
  17. import org.apache.maven.plugin.logging.Log;
  18. import org.apache.maven.project.MavenProject;
  19. import com.atlassian.maven.plugins.amps.util.ConfigFileUtils;
  20. import com.atlassian.maven.plugins.amps.util.ConfigFileUtils.Replacement;
  21. import com.atlassian.maven.plugins.amps.util.ProjectUtils;
  22. import com.atlassian.maven.plugins.amps.util.ZipUtils;
  23. import com.google.common.collect.Lists;
  24. /**
  25. * This abstract class is common to real applications (which inherit from AbstractProductHandler, like JIRA or Confluence)
  26. * and the fake application Studio.
  27. *
  28. * This class handles common operations
  29. *
  30. * @since 3.6
  31. */
  32. public abstract class AmpsProductHandler implements ProductHandler
  33. {
  34. protected final MavenGoals goals;
  35. protected final MavenProject project;
  36. protected final MavenContext context;
  37. protected final Log log;
  38. protected AmpsProductHandler(MavenContext context, MavenGoals goals)
  39. {
  40. this.project = context.getProject();
  41. this.context = context;
  42. this.goals = goals;
  43. this.log = context.getLog();
  44. }
  45. /**
  46. * Copies and creates a zip file of the previous run's home directory minus any installed plugins.
  47. *
  48. * @param homeDirectory
  49. * The path to the previous run's home directory.
  50. * @param targetZip
  51. * The path to the final zip file.
  52. * @param product
  53. * The product
  54. *
  55. * @since 3.1-m3
  56. */
  57. public void createHomeZip(final File homeDirectory, final File targetZip, final Product product) throws MojoExecutionException
  58. {
  59. if (homeDirectory == null || !homeDirectory.exists())
  60. {
  61. String homePath = "null";
  62. if (homeDirectory != null)
  63. {
  64. homePath = homeDirectory.getAbsolutePath();
  65. }
  66. context.getLog().info("home directory doesn't exist, skipping. [" + homePath + "]");
  67. return;
  68. }
  69. try
  70. {
  71. /*
  72. * The zip has /someRootFolder/{productId}-home/
  73. */
  74. final File appDir = getBaseDirectory(product);
  75. final File tmpDir = new File(appDir, "tmp-resources");
  76. final File homeSnapshot = new File(tmpDir, "generated-home");
  77. final String entryBase = "generated-resources/" + product.getId() + "-home";
  78. if (homeSnapshot.exists())
  79. {
  80. FileUtils.deleteDirectory(homeSnapshot);
  81. }
  82. homeSnapshot.mkdirs();
  83. copyDirectory(homeDirectory, homeSnapshot, true);
  84. cleanupProductHomeForZip(product, homeSnapshot);
  85. ZipUtils.zipDir(targetZip, homeSnapshot, entryBase);
  86. }
  87. catch (IOException e)
  88. {
  89. throw new RuntimeException("Error zipping home directory", e);
  90. }
  91. }
  92. /**
  93. * Prepares the home directory to snapshot:
  94. * <ul>
  95. * <li>Removes all unnecessary files</li>
  96. * <li>Perform product-specific clean-up</li>
  97. * <ul>
  98. * This is a reference implementation. It is probable that each application has a different set of directories to delete.
  99. * @param product the product details
  100. * @param snapshotDir an image of the home which will be zipped. This is not the working home, so you're free to remove files and parametrise them.
  101. * @throws IOException
  102. */
  103. public void cleanupProductHomeForZip(Product product, File snapshotDir) throws MojoExecutionException, IOException
  104. {
  105. try {
  106. // we want to get rid of the plugins folders.
  107. FileUtils.deleteDirectory(new File(snapshotDir, "plugins")); // Not used by: fisheye, confluence, studio - Used by: crowd, bamboo, jira
  108. FileUtils.deleteDirectory(new File(snapshotDir, "bundled-plugins")); // Not used by: fisheye, jira - Used by: confluence, crowd, bamboo
  109. // Get rid of "studio-test-resources.zip", which is the homeZip that was used
  110. // when we started Amps.
  111. if (this.getTestResourcesArtifact() != null)
  112. {
  113. String originalHomeZip = this.getTestResourcesArtifact().getArtifactId() + ".zip";
  114. FileUtils.deleteQuietly(new File(snapshotDir, originalHomeZip));
  115. }
  116. // Proceed to replacements
  117. List<Replacement> replacements = getReplacements(product);
  118. // Sort by longer values first, so that the right keys are used.
  119. Collections.sort(replacements, new Comparator<Replacement>(){
  120. @Override
  121. public int compare(Replacement replacement1, Replacement replacement2)
  122. {
  123. // longest value < shortest value
  124. int length1 = replacement1.getValue().length();
  125. int length2 = replacement2.getValue().length();
  126. return length2 - length1;
  127. }
  128. });
  129. List<File> files = getConfigFiles(product, snapshotDir);
  130. ConfigFileUtils.replace(files, replacements, true, log);
  131. }
  132. catch (IOException ioe)
  133. {
  134. throw new MojoExecutionException("Could not delete home/plugins/ and /home/bundled-plugins/", ioe);
  135. }
  136. }
  137. abstract protected ProductArtifact getTestResourcesArtifact();
  138. protected File getProductHomeData(final Product ctx) throws MojoExecutionException
  139. {
  140. File productHomeZip = null;
  141. String dataPath = ctx.getDataPath();
  142. //use custom zip if supplied
  143. if (isNotBlank(dataPath))
  144. {
  145. File customHomeZip = new File(dataPath);
  146. if (customHomeZip.exists())
  147. {
  148. return customHomeZip;
  149. }
  150. throw new MojoExecutionException("Unable to use custom test resources set by <dataPath>. File '" +
  151. customHomeZip.getAbsolutePath() + "' does not exist");
  152. }
  153. //if we didn't find a custom zip, use the default
  154. ProductArtifact testResourcesArtifact = getTestResourcesArtifact();
  155. if (productHomeZip == null && testResourcesArtifact != null)
  156. {
  157. ProductArtifact artifact = new ProductArtifact(
  158. testResourcesArtifact.getGroupId(), testResourcesArtifact.getArtifactId(), ctx.getDataVersion());
  159. productHomeZip = goals.copyHome(getBaseDirectory(ctx), artifact);
  160. }
  161. return productHomeZip;
  162. }
  163. protected void overrideAndPatchHomeDir(File homeDir, final Product ctx) throws MojoExecutionException
  164. {
  165. try
  166. {
  167. final File srcDir = new File(project.getBasedir(), "src/test/resources/" + ctx.getInstanceId() + "-home");
  168. if (srcDir.exists() && homeDir.exists())
  169. {
  170. copyDirectory(srcDir, homeDir, false);
  171. }
  172. }
  173. catch (IOException e)
  174. {
  175. throw new MojoExecutionException("Unable to override files using src/test/resources", e);
  176. }
  177. }
  178. /**
  179. * Lists parameters which must be replaced in the configuration files of the home directory.
  180. * <p/>
  181. * Used reversely when reading / when creating a home zip.
  182. */
  183. public List<ConfigFileUtils.Replacement> getReplacements(Product product)
  184. {
  185. // Standard replacements:
  186. List<Replacement> replacements = Lists.newArrayList();
  187. String buildDirectory = project.getBuild().getDirectory();
  188. String baseDirectory = getBaseDirectory(product).getAbsolutePath();
  189. String homeDirectory = getHomeDirectory(product).getAbsolutePath();
  190. replacements.add(new Replacement("%PROJECT_BUILD_DIR%", buildDirectory));
  191. replacements.add(new Replacement("%PRODUCT_BASE_DIR%", baseDirectory));
  192. replacements.add(new Replacement("%PRODUCT_HOME_DIR%", homeDirectory));
  193. // These replacements are especially for Fecru, but there's no reason not to find them in other config files
  194. try {
  195. replacements.add(new Replacement("%PROJECT_BUILD_DIR_URL_ENCODED%", URLEncoder.encode(propertiesEncode(buildDirectory), "UTF-8")));
  196. replacements.add(new Replacement("%PRODUCT_BASE_DIR_URL_ENCODED%", URLEncoder.encode(propertiesEncode(baseDirectory), "UTF-8")));
  197. replacements.add(new Replacement("%PRODUCT_HOME_DIR_URL_ENCODED%", URLEncoder.encode(propertiesEncode(homeDirectory), "UTF-8")));
  198. }
  199. catch (UnsupportedEncodingException badJvm)
  200. {
  201. throw new RuntimeException("UTF-8 should be supported on any JVM", badJvm);
  202. }
  203. replacements.add(Replacement.onlyWhenCreatingSnapshot("localhost", product.getServer()));
  204. try
  205. {
  206. String localHostName = InetAddress.getLocalHost().getHostName();
  207. replacements.add(new Replacement("%LOCAL_HOST_NAME%", localHostName));
  208. }
  209. catch (UnknownHostException e)
  210. {
  211. // If we can't get the local computer's hostname, it's probable no product could,
  212. // so we don't need to search-replace the value.
  213. }
  214. return replacements;
  215. }
  216. /**
  217. * Encodes a String for a Properties file. Escapes : and =.
  218. */
  219. protected String propertiesEncode(String decoded)
  220. {
  221. if (decoded == null)
  222. {
  223. return null;
  224. }
  225. String replacement1 = decoded.replaceAll(":", "\\:");
  226. String replacement2 = replacement1.replaceAll("=", "\\=");
  227. return replacement2;
  228. }
  229. @Override
  230. public List<File> getConfigFiles(Product product, File snapshotDir)
  231. {
  232. return Lists.newArrayList();
  233. }
  234. public File getBaseDirectory(Product ctx)
  235. {
  236. return ProjectUtils.createDirectory(new File(project.getBuild().getDirectory(), ctx.getInstanceId()));
  237. }
  238. public File getHomeDirectory(Product ctx)
  239. {
  240. if (StringUtils.isNotBlank(ctx.getDataHome()))
  241. {
  242. return new File(ctx.getDataHome());
  243. }
  244. return new File(getBaseDirectory(ctx), "home");
  245. }
  246. public File getSnapshotDirectory(Product product)
  247. {
  248. return getHomeDirectory(product);
  249. }
  250. @Override
  251. public String getDefaultContextPath()
  252. {
  253. return "/" + getId();
  254. }
  255. @Override
  256. public String getDefaultContainerId()
  257. {
  258. return AmpsDefaults.DEFAULT_CONTAINER;
  259. }
  260. protected File createHomeDirectory(Product ctx)
  261. {
  262. return ProjectUtils.createDirectory(getHomeDirectory(ctx));
  263. }
  264. }