1   package com.atlassian.maven.plugins.amps.osgi;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.List;
7   import java.util.Set;
8   
9   import com.atlassian.maven.plugins.amps.AbstractAmpsMojo;
10  
11  import org.apache.commons.io.FileUtils;
12  import org.apache.maven.archiver.MavenArchiveConfiguration;
13  import org.apache.maven.archiver.MavenArchiver;
14  import org.apache.maven.artifact.Artifact;
15  import org.apache.maven.artifact.DependencyResolutionRequiredException;
16  import org.apache.maven.model.Build;
17  import org.apache.maven.plugin.MojoExecutionException;
18  import org.apache.maven.plugin.MojoFailureException;
19  import org.apache.maven.project.MavenProject;
20  import org.apache.maven.project.MavenProjectHelper;
21  import org.codehaus.plexus.archiver.ArchiverException;
22  import org.codehaus.plexus.archiver.jar.JarArchiver;
23  import org.codehaus.plexus.archiver.jar.ManifestException;
24  import org.jfrog.maven.annomojo.annotations.MojoComponent;
25  import org.jfrog.maven.annomojo.annotations.MojoGoal;
26  import org.jfrog.maven.annomojo.annotations.MojoParameter;
27  
28  /**
29   * Generates the obr artifact, containing the plugin, its dependencies, and the obr XML file.  The OBR file looks like
30   * this:
31   * <p/>
32   * <pre>
33   * this-plugin.jar
34   * obr.xml
35   * dependencies/required-plugin.jar
36   * </pre>
37   * <p/>
38   * All plugins in the root directory will be installed, while the ones in the "dependencies" directory will be installed
39   * only if they are needed.
40   */
41  @MojoGoal ("generate-obr-artifact")
42  public class GenerateObrArtifactMojo extends AbstractAmpsMojo
43  {
44      @MojoParameter
45      private List<PluginDependency> pluginDependencies = new ArrayList<PluginDependency>();
46  
47      /**
48       * The Jar archiver.
49       */
50      @MojoComponent (role = "org.codehaus.plexus.archiver.Archiver", roleHint = "jar")
51      private JarArchiver jarArchiver;
52  
53      /**
54       * The archive configuration to use. See <a href="http://maven.apache.org/shared/maven-archiver/index.html">Maven
55       * Archiver Reference</a>.
56       */
57      @MojoParameter
58      private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
59  
60      /**
61       * Specifies whether or not to attach the artifact to the project
62       */
63      @MojoParameter (expression = "${attach}", defaultValue = "true")
64      private boolean attach;
65  
66      @MojoComponent
67      private MavenProjectHelper projectHelper;
68  
69      /**
70       * The directory where the generated archive file will be put.
71       */
72      @MojoParameter (defaultValue = "${project.build.directory}")
73      protected File outputDirectory;
74  
75      /**
76       * The filename to be used for the generated archive file.  The "-obr" suffix will be appended.
77       */
78      @MojoParameter (defaultValue = "${project.build.finalName}")
79      protected String finalName;
80  
81      /**
82       * Contains the full list of projects in the reactor.
83       */
84      @MojoParameter (expression = "${reactorProjects}", readonly = true)
85      protected List<MavenProject> reactorProjects;
86  
87      public void execute() throws MojoExecutionException, MojoFailureException
88      {
89          try
90          {
91              Build build = getMavenContext().getProject().getBuild();
92              List<File> deps = resolvePluginDependencies();
93              // todo: we use build.getFinalName() here, but finalName (mojo parameter) in generateObrZip().  this seems weird.
94              File obrDir = layoutObr(deps, new File(build.getDirectory(), build.getFinalName() + ".jar"));
95              generateObrZip(obrDir);
96          }
97          catch (IOException e)
98          {
99              throw new MojoExecutionException(e.getMessage(), e);
100         }
101     }
102 
103     /**
104      * @param obrDir Directory containing the files to go into the obr zip
105      * @throws MojoExecutionException If something goes wrong
106      */
107     private void generateObrZip(File obrDir) throws MojoExecutionException
108     {
109         MavenArchiver archiver = new MavenArchiver();
110         archiver.setArchiver(jarArchiver);
111         File outputFile = new File(outputDirectory, finalName + ".obr");
112         final MavenProject mavenProject = getMavenContext().getProject();
113         try
114         {
115             archiver.getArchiver().addDirectory(obrDir, "");
116             archiver.setOutputFile(outputFile);
117 
118             archive.setAddMavenDescriptor(false);
119 
120             // todo: be smarter about when this is updated
121             archive.setForced(true);
122 
123             archiver.createArchive(mavenProject, archive);
124         }
125         catch (IOException e)
126         {
127             throw new MojoExecutionException("Error creating obr archive: " + e.getMessage(), e);
128         }
129         catch (ArchiverException e)
130         {
131             throw new MojoExecutionException("Error creating obr archive: " + e.getMessage(), e);
132         }
133         catch (DependencyResolutionRequiredException e)
134         {
135             throw new MojoExecutionException("Error creating obr archive: " + e.getMessage(), e);
136         }
137         catch (ManifestException e)
138         {
139             throw new MojoExecutionException("Error creating obr archive: " + e.getMessage(), e);
140         }
141 
142         if (attach)
143         {
144             projectHelper.attachArtifact(mavenProject, getType(), outputFile);
145         }
146         else
147         {
148             getLog().info("NOT adding " + getType() + " to attached artifacts list, so it won't be installed or deployed.");
149         }
150     }
151 
152     /**
153      * Creates a directory containing the files that will be in the obr artifact.
154      *
155      * @param deps The dependencies for this artifact
156      * @param mainArtifact The main artifact file
157      * @return The directory containing the future obr zip contents
158      * @throws IOException If the files cannot be copied
159      * @throws MojoExecutionException If the dependencies cannot be retrieved
160      */
161     private File layoutObr(List<File> deps, File mainArtifact) throws MojoExecutionException, IOException
162     {
163         // create directories
164         File obrDir = new File(getMavenContext().getProject().getBuild().getDirectory(), "obr");
165         obrDir.mkdir();
166         File depDir = new File(obrDir, "dependencies");
167         depDir.mkdir();
168 
169         // Copy in the dependency plugins for the obr generation
170         for (File dep : deps)
171         {
172             FileUtils.copyFileToDirectory(dep, depDir, true);
173         }
174 
175         // Generate the obr xml
176         File obrXml = new File(obrDir, "obr.xml");
177         for (File dep : depDir.listFiles())
178         {
179             getMavenGoals().generateObrXml(dep, obrXml);
180         }
181 
182         // Copy the main artifact over
183         File mainArtifactCopy = new File(obrDir, mainArtifact.getName());
184         FileUtils.copyFile(mainArtifact, mainArtifactCopy);
185 
186         // Generate the obr xml for the main artifact
187         // The File must be the one copied into the obrDir (see AMPS-300)
188         getMavenGoals().generateObrXml(mainArtifactCopy, obrXml);
189 
190         return obrDir;
191     }
192 
193     private List<File> resolvePluginDependencies()
194     {
195         List<File> deps = new ArrayList<File>();
196         for (Artifact artifact : (Set<Artifact>) getMavenContext().getProject().getDependencyArtifacts())
197         {
198             if (pluginDependencies.contains(new PluginDependency(artifact.getGroupId(), artifact.getArtifactId())))
199             {
200                 deps.add(artifact.getFile());
201             }
202         }
203         return deps;
204     }
205 
206     protected String getType()
207     {
208         return "obr";
209     }
210 }