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