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