1   package com.atlassian.maven.plugins.amps.product;
2   
3   import static com.atlassian.maven.plugins.amps.util.FileUtils.doesFileNameMatchArtifact;
4   import static com.atlassian.maven.plugins.amps.util.ZipUtils.unzip;
5   import static org.apache.commons.io.FileUtils.copyDirectory;
6   import static org.apache.commons.io.FileUtils.copyFile;
7   import static org.apache.commons.io.FileUtils.iterateFiles;
8   import static org.apache.commons.io.FileUtils.moveDirectory;
9   import static org.apache.commons.io.FileUtils.readFileToString;
10  import static org.apache.commons.lang.StringUtils.isNotBlank;
11  
12  import java.io.File;
13  import java.io.IOException;
14  import java.util.ArrayList;
15  import java.util.Collection;
16  import java.util.HashMap;
17  import java.util.Iterator;
18  import java.util.List;
19  import java.util.Map;
20  
21  import org.apache.maven.plugin.MojoExecutionException;
22  import org.apache.maven.project.MavenProject;
23  
24  import com.atlassian.maven.plugins.amps.MavenGoals;
25  import com.atlassian.maven.plugins.amps.Product;
26  import com.atlassian.maven.plugins.amps.ProductArtifact;
27  
28  public abstract class AbstractProductHandler implements ProductHandler
29  {
30      protected final MavenGoals goals;
31      protected final MavenProject project;
32      private final PluginProvider pluginProvider;
33  
34      protected AbstractProductHandler(MavenProject project, MavenGoals goals, PluginProvider pluginProvider)
35      {
36          this.project = project;
37          this.goals = goals;
38          this.pluginProvider = pluginProvider;
39      }
40      
41      public final int start(final Product ctx) throws MojoExecutionException
42      {
43          final File homeDir = extractAndProcessHomeDirectory(ctx);
44          final File extractedApp = extractApplication(ctx, homeDir);
45          final File finalApp = addArtifactsAndOverrides(ctx, homeDir, extractedApp);
46          return startApplication(ctx, finalApp, homeDir, mergeSystemProperties(ctx));
47      }
48  
49      protected final File extractAndProcessHomeDirectory(final Product ctx) throws MojoExecutionException
50      {
51          final File productHomeData = getProductHomeData(ctx);
52          if (productHomeData != null)
53          {
54              final File homeDir = getHomeDirectory(ctx);
55  
56              // Only create the home dir if it doesn't exist
57              if (!homeDir.exists())
58              {
59                  extractProductHomeData(productHomeData, homeDir, ctx);
60  
61                  // just in case
62                  homeDir.mkdir();
63                  processHomeDirectory(ctx, homeDir);
64              }
65  
66              // Always override files regardless of home directory existing or not
67              try
68              {
69                  overrideAndPatchHomeDir(homeDir, ctx);
70              }
71              catch (IOException e)
72              {
73                  throw new MojoExecutionException("Unable to override files using src/test/resources", e);
74              }
75  
76              return homeDir;
77          }
78          else
79          {
80              return getHomeDirectory(ctx);
81          }
82      }
83  
84      private File getProductHomeData(final Product ctx) throws MojoExecutionException
85      {
86          File productHomeZip = null;
87          String dpath = ctx.getDataPath();
88  
89          //use custom zip if supplied
90          if (isNotBlank(dpath))
91          {
92              File customHomeZip = new File(dpath);
93  
94              if (customHomeZip.exists())
95              {
96                  productHomeZip = customHomeZip;
97              }
98              else
99              {
100                 throw new MojoExecutionException("Unable to use custom test resources set by <productDataPath>. File '" +
101                         customHomeZip.getAbsolutePath() + "' does not exist");
102             }
103         }
104 
105         //if we didn't find a custom zip, use the default
106         if (productHomeZip == null && getTestResourcesArtifact() != null)
107         {
108             ProductArtifact artifact = new ProductArtifact(
109                 getTestResourcesArtifact().getGroupId(), getTestResourcesArtifact().getArtifactId(), ctx.getDataVersion());
110             if (artifact != null)
111             {
112                 productHomeZip = goals.copyHome(getBaseDirectory(ctx), artifact);
113             }
114         }
115 
116         return productHomeZip;
117     }
118 
119     protected void extractProductHomeData(File productHomeData, File homeDir, Product ctx)
120             throws MojoExecutionException
121     {
122         final File tmpDir = new File(getBaseDirectory(ctx), "tmp-resources");
123         tmpDir.mkdir();
124 
125         try
126         {
127             if (productHomeData.isFile())
128             {
129                 File tmp = new File(getBaseDirectory(ctx), ctx.getId() + "-home");
130                 
131                 unzip(productHomeData, tmpDir.getPath());
132                 copyDirectory(tmpDir.listFiles()[0], getBaseDirectory(ctx), true);
133                 moveDirectory(tmp, homeDir);
134             }
135             else if (productHomeData.isDirectory())
136             {
137                 copyDirectory(productHomeData, homeDir);
138             }
139         }
140         catch (final IOException ex)
141         {
142             throw new MojoExecutionException("Unable to copy home directory", ex);
143         }
144     }
145 
146     private void overrideAndPatchHomeDir(File homeDir, final Product ctx) throws IOException
147     {
148         final File srcDir = new File(project.getBasedir(), "src/test/resources/" + ctx.getInstanceId() + "-home");
149         if (srcDir.exists() && homeDir.exists())
150         {
151             copyDirectory(srcDir, homeDir);
152         }
153     }
154 
155     private final File addArtifactsAndOverrides(final Product ctx, final File homeDir, final File app) throws MojoExecutionException
156     {
157         try
158         {
159             final File appDir;
160             if (app.isFile())
161             {
162                 appDir = new File(getBaseDirectory(ctx), "webapp");
163                 if (!appDir.exists())
164                 {
165                     unzip(app, appDir.getAbsolutePath());
166                 }
167             }
168             else
169             {
170                 appDir = app;
171             }
172 
173             addArtifacts(ctx, homeDir, appDir);
174 
175             // override war files
176             try
177             {
178                 addOverrides(appDir, ctx);
179             }
180             catch (IOException e)
181             {
182                 throw new MojoExecutionException("Unable to override WAR files using src/test/resources/" + ctx.getInstanceId() + "-app", e);
183             }
184 
185             if (app.isFile())
186             {
187                 final File warFile = new File(app.getParentFile(), getId() + ".war");
188                 com.atlassian.core.util.FileUtils.createZipFile(appDir, warFile);
189                 return warFile;
190             }
191             else
192             {
193                 return appDir;
194             }
195 
196         }
197         catch (final Exception e)
198         {
199             e.printStackTrace();
200             throw new MojoExecutionException(e.getMessage());
201         }
202     }
203 
204     private void addArtifacts(final Product ctx, final File homeDir, final File appDir)
205             throws IOException, MojoExecutionException, Exception
206     {
207         File pluginsDir = getUserInstalledPluginsDirectory(appDir, homeDir);
208         final File bundledPluginsDir = new File(getBaseDirectory(ctx), "bundled-plugins");
209         
210         bundledPluginsDir.mkdir();
211         // add bundled plugins
212         final File bundledPluginsZip = new File(appDir, getBundledPluginPath(ctx));
213         if (bundledPluginsZip.exists())
214         {
215             unzip(bundledPluginsZip, bundledPluginsDir.getPath());
216         }
217         
218         if (isStaticPlugin())
219         {
220             if (!supportsStaticPlugins())
221             {
222                   throw new MojoExecutionException("According to your atlassian-plugin.xml file, this plugin is not " +
223                           "atlassian-plugins version 2. This app currently only supports atlassian-plugins " +
224                           "version 2.");
225             }
226             pluginsDir = new File(appDir, "WEB-INF/lib");
227         }
228         
229         if (pluginsDir == null)
230         {
231             pluginsDir = bundledPluginsDir;
232         }
233         
234         createDirectory(pluginsDir);
235         
236         // add this plugin itself if enabled
237         if (ctx.isInstallPlugin())
238         {
239             addThisPluginToDirectory(pluginsDir);
240             addTestPluginToDirectory(pluginsDir);
241         }
242         
243         // add plugins2 plugins if necessary
244         if (!isStaticPlugin())
245         {
246             addArtifactsToDirectory(pluginProvider.provide(ctx), pluginsDir);
247         }
248         
249         // add plugins1 plugins
250         List<ProductArtifact> artifacts = new ArrayList<ProductArtifact>();
251         artifacts.addAll(getDefaultLibPlugins());
252         artifacts.addAll(ctx.getLibArtifacts());
253         addArtifactsToDirectory(artifacts, new File(appDir, "WEB-INF/lib"));
254         
255         artifacts = new ArrayList<ProductArtifact>();
256         artifacts.addAll(getDefaultBundledPlugins());
257         artifacts.addAll(ctx.getBundledArtifacts());
258         
259         addArtifactsToDirectory(artifacts, bundledPluginsDir);
260         
261         if (bundledPluginsDir.list().length > 0)
262         {
263             com.atlassian.core.util.FileUtils.createZipFile(bundledPluginsDir, bundledPluginsZip);
264         }
265         
266         if (ctx.getLog4jProperties() != null && getLog4jPropertiesPath() != null)
267         {
268             copyFile(ctx.getLog4jProperties(), new File(appDir, getLog4jPropertiesPath()));
269         }
270     }
271     
272     abstract protected void processHomeDirectory(Product ctx, File homeDir) throws MojoExecutionException;
273     abstract protected ProductArtifact getTestResourcesArtifact();
274     abstract protected File extractApplication(Product ctx, File homeDir) throws MojoExecutionException;
275     abstract protected int startApplication(Product ctx, File app, File homeDir, Map<String, String> properties) throws MojoExecutionException;
276     abstract protected boolean supportsStaticPlugins();
277     abstract protected Collection<? extends ProductArtifact> getDefaultBundledPlugins();
278     abstract protected Collection<? extends ProductArtifact> getDefaultLibPlugins();
279     abstract protected String getBundledPluginPath(Product ctx);
280     abstract protected File getUserInstalledPluginsDirectory(File webappDir, File homeDir);
281     
282     protected String getLog4jPropertiesPath()
283     {
284         return null;
285     }
286 
287     protected boolean isStaticPlugin() throws IOException
288     {
289         final File atlassianPluginXml = new File(project.getBasedir(), "src/main/resources/atlassian-plugin.xml");
290         if (atlassianPluginXml.exists())
291         {
292             String text = readFileToString(atlassianPluginXml);
293             return !text.contains("pluginsVersion=\"2\"") && !text.contains("plugins-version=\"2\"");
294         }
295         else
296         {
297             // probably an osgi bundle
298             return false;
299         }
300     }
301 
302     protected final void addThisPluginToDirectory(final File targetDir) throws IOException
303     {
304         final File thisPlugin = getPluginFile();
305 
306         // remove any existing version
307         for (final Iterator<?> iterateFiles = iterateFiles(targetDir, null, false); iterateFiles.hasNext();)
308         {
309             final File file = (File) iterateFiles.next();
310             if (doesFileNameMatchArtifact(file.getName(), project.getArtifactId()))
311             {
312                 file.delete();
313             }
314         }
315 
316         // add the plugin jar to the directory
317         copyFile(thisPlugin, new File(targetDir, thisPlugin.getName()));
318     }
319 
320     protected void addTestPluginToDirectory(final File targetDir) throws IOException
321     {
322         final File testPluginFile = getTestPluginFile();
323         if (testPluginFile.exists())
324         {
325             // add the test plugin jar to the directory
326             copyFile(testPluginFile, new File(targetDir, testPluginFile.getName()));
327         }
328 
329     }
330 
331     protected final File getPluginFile()
332     {
333         return new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + ".jar");
334     }
335 
336     protected File getTestPluginFile()
337     {
338         return new File(project.getBuild().getDirectory(), project.getBuild().getFinalName() + "-tests.jar");
339     }
340 
341     protected final void addArtifactsToDirectory(final List<ProductArtifact> artifacts, final File pluginsDir) throws MojoExecutionException
342     {
343         // first remove plugins from the webapp that we want to update
344         if (pluginsDir.isDirectory() && pluginsDir.exists())
345         {
346             for (final Iterator<?> iterateFiles = iterateFiles(pluginsDir, null, false); iterateFiles.hasNext();)
347             {
348                 final File file = (File) iterateFiles.next();
349                 for (final ProductArtifact webappArtifact : artifacts)
350                 {
351                     if (!file.isDirectory() && doesFileNameMatchArtifact(file.getName(), webappArtifact.getArtifactId()))
352                     {
353                         file.delete();
354                     }
355                 }
356             }
357         }
358         // copy the all the plugins we want in the webapp
359         if (!artifacts.isEmpty())
360         {
361             goals.copyPlugins(pluginsDir, artifacts);
362         }
363     }
364 
365     protected final void addOverrides(File appDir, final Product ctx) throws IOException
366     {
367         final File srcDir = new File(project.getBasedir(), "src/test/resources/" + ctx.getInstanceId() + "-app");
368         if (srcDir.exists() && appDir.exists())
369         {
370             copyDirectory(srcDir, appDir);
371         }
372     }
373     
374     public final File getBaseDirectory(Product ctx)
375     {
376         return createDirectory(new File(project.getBuild().getDirectory(), ctx.getInstanceId()));
377     }
378 
379     public final File getHomeDirectory(Product ctx)
380     {
381         return new File(getBaseDirectory(ctx), "home");
382     }
383     
384     protected final File createHomeDirectory(Product ctx)
385     {
386         return createDirectory(getHomeDirectory(ctx));
387     }
388 
389     protected final File createDirectory(File dir)
390     {
391         if (!dir.exists() && !dir.mkdirs())
392         {
393             throw new RuntimeException("Failed to create directory " + dir.getAbsolutePath());
394         }
395         return dir;
396     }
397 
398     protected final Map<String, String> mergeSystemProperties(Product ctx)
399     {
400         final Map<String, String> properties = new HashMap<String, String>();
401 
402         properties.putAll(getSystemProperties(ctx));
403         for (Map.Entry<String, Object> entry : ctx.getSystemPropertyVariables().entrySet())
404         {
405             properties.put(entry.getKey(), (String) entry.getValue());
406         }
407         return properties;
408     }
409 
410     protected abstract Map<String, String> getSystemProperties(Product ctx);
411 
412 }