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