1   package com.atlassian.maven.plugins.amps.product;
2   
3   
4   import static org.apache.commons.lang.StringUtils.isNotBlank;
5   
6   import java.io.File;
7   import java.io.IOException;
8   import java.util.Collections;
9   import java.util.Comparator;
10  import java.util.List;
11  
12  import org.apache.commons.io.FileUtils;
13  import org.apache.maven.plugin.MojoExecutionException;
14  import org.apache.maven.plugin.logging.Log;
15  import org.apache.maven.project.MavenProject;
16  
17  import com.atlassian.maven.plugins.amps.MavenContext;
18  import com.atlassian.maven.plugins.amps.MavenGoals;
19  import com.atlassian.maven.plugins.amps.Product;
20  import com.atlassian.maven.plugins.amps.ProductArtifact;
21  import com.atlassian.maven.plugins.amps.util.ConfigFileUtils;
22  import com.atlassian.maven.plugins.amps.util.ConfigFileUtils.Replacement;
23  import com.atlassian.maven.plugins.amps.util.ProjectUtils;
24  import com.atlassian.maven.plugins.amps.util.ZipUtils;
25  import com.google.common.collect.Lists;
26  
27  /**
28   * This abstract class is common to real applications (which inherit from AbstractProductHandler, like JIRA or Confluence)
29   * and the fake application Studio.
30   *
31   * This class handles common operations
32   *
33   * @since 3.6
34   */
35  public abstract class AmpsProductHandler implements ProductHandler
36  {
37  
38      protected final MavenGoals goals;
39      protected final MavenProject project;
40      protected final MavenContext context;
41      protected final Log log;
42  
43      protected AmpsProductHandler(MavenContext context, MavenGoals goals)
44      {
45          this.project = context.getProject();
46          this.context = context;
47          this.goals = goals;
48          this.log = context.getLog();
49      }
50  
51      /**
52       * Copies and creates a zip file of the previous run's home directory minus any installed plugins.
53       *
54       * @param homeDirectory
55       *            The path to the previous run's home directory.
56       * @param targetZip
57       *            The path to the final zip file.
58       * @param product
59       *            The product
60       *
61       * @since 3.1-m3
62       */
63      public void createHomeZip(final File homeDirectory, final File targetZip, final Product product) throws MojoExecutionException
64      {
65          if (homeDirectory == null || !homeDirectory.exists())
66          {
67              String homePath = "null";
68              if (homeDirectory != null)
69              {
70                  homePath = homeDirectory.getAbsolutePath();
71              }
72              context.getLog().info("home directory doesn't exist, skipping. [" + homePath + "]");
73              return;
74          }
75  
76  
77          try
78              {
79              /*
80               * The zip has /someRootFolder/{productId}-home/
81               */
82              final File appDir = getBaseDirectory(product);
83              final File tmpDir = new File(appDir, "tmp-resources");
84              final File homeSnapshot = new File(tmpDir, "generated-home");
85              final String entryBase = "generated-resources/" + product.getId() + "-home";
86  
87              if (homeSnapshot.exists())
88              {
89                  FileUtils.deleteDirectory(homeSnapshot);
90              }
91  
92              homeSnapshot.mkdirs();
93              FileUtils.copyDirectory(homeDirectory, homeSnapshot, true);
94  
95              cleanupProductHomeForZip(product, homeSnapshot);
96              ZipUtils.zipDir(targetZip, homeSnapshot, entryBase);
97          }
98          catch (IOException e)
99          {
100             throw new RuntimeException("Error zipping home directory", e);
101         }
102     }
103 
104     /**
105      * Prepares the home directory to snapshot:
106      * <ul>
107      * <li>Removes all unnecessary files</li>
108      * <li>Perform product-specific clean-up</li>
109      * <ul>
110      * This is a reference implementation. It is probable that each application has a different set of directories to delete.
111      * @param product the product details
112      * @param homeDirectory 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.
113      * @throws IOException
114      */
115     public void cleanupProductHomeForZip(Product product, File snapshotDir) throws MojoExecutionException, IOException
116     {
117         try {
118             // we want to get rid of the plugins folders.
119             FileUtils.deleteDirectory(new File(snapshotDir, "plugins")); // Not used by: fisheye, confluence, studio - Used by: crowd, bamboo, jira
120             FileUtils.deleteDirectory(new File(snapshotDir, "bundled-plugins")); // Not used by: fisheye, jira - Used by: confluence, crowd, bamboo
121 
122             // Proceed to replacements
123             List<Replacement> replacements = getReplacements(product);
124             // Sort by longer values first, so that the right keys are used.
125             Collections.sort(replacements, new Comparator<Replacement>(){
126                 @Override
127                 public int compare(Replacement replacement1, Replacement replacement2)
128                 {
129                     // longest value < shortest value
130                     int length1 = replacement1.getValue().length();
131                     int length2 = replacement2.getValue().length();
132                     return length2 - length1;
133                 }
134             });
135             List<File> files = getConfigFiles(product, snapshotDir);
136 
137             ConfigFileUtils.replace(files, replacements, true, log);
138         }
139         catch (IOException ioe)
140         {
141             throw new MojoExecutionException("Could not delete home/plugins/ and /home/bundled-plugins/", ioe);
142         }
143     }
144 
145     abstract protected ProductArtifact getTestResourcesArtifact();
146 
147     protected File getProductHomeData(final Product ctx) throws MojoExecutionException
148     {
149         File productHomeZip = null;
150         String dpath = ctx.getDataPath();
151 
152         //use custom zip if supplied
153         if (isNotBlank(dpath))
154         {
155             File customHomeZip = new File(dpath);
156 
157             if (customHomeZip.exists())
158             {
159                 productHomeZip = customHomeZip;
160             }
161             else
162             {
163                 throw new MojoExecutionException("Unable to use custom test resources set by <productDataPath>. File '" +
164                         customHomeZip.getAbsolutePath() + "' does not exist");
165             }
166         }
167 
168         //if we didn't find a custom zip, use the default
169         ProductArtifact testResourcesArtifact = getTestResourcesArtifact();
170         if (productHomeZip == null && testResourcesArtifact != null)
171         {
172             ProductArtifact artifact = new ProductArtifact(
173                 testResourcesArtifact.getGroupId(), testResourcesArtifact.getArtifactId(), ctx.getDataVersion());
174             productHomeZip = goals.copyHome(getBaseDirectory(ctx), artifact);
175         }
176 
177         return productHomeZip;
178     }
179 
180 
181     /**
182      * Lists parameters which must be replaced in the configuration files of the home directory.
183      * <p/>
184      * Used reversely when reading / when creating a home zip.
185      */
186     public List<ConfigFileUtils.Replacement> getReplacements(Product product)
187     {
188         // Standard replacements:
189         List<Replacement> replacements = Lists.newArrayList();
190         replacements.add(new Replacement("%PROJECT_BUILD_DIR%", project.getBuild().getDirectory()));
191         replacements.add(new Replacement("%PRODUCT_BASE_DIR%", getBaseDirectory(product).getAbsolutePath()));
192         replacements.add(new Replacement("%PRODUCT_HOME_DIR%", getHomeDirectory(product).getAbsolutePath()));
193         return replacements;
194     }
195 
196     @Override
197     public List<File> getConfigFiles(Product product, File snapshotDir)
198     {
199         return Lists.newArrayList();
200     }
201 
202     public File getBaseDirectory(Product ctx)
203     {
204         return ProjectUtils.createDirectory(new File(project.getBuild().getDirectory(), ctx.getInstanceId()));
205     }
206 
207     public File getHomeDirectory(Product ctx)
208     {
209         return new File(getBaseDirectory(ctx), "home");
210     }
211 
212     public File getSnapshotDirectory(Product product)
213     {
214         return getHomeDirectory(product);
215     }
216 
217     protected File createHomeDirectory(Product ctx)
218     {
219         return ProjectUtils.createDirectory(getHomeDirectory(ctx));
220     }
221 }