View Javadoc

1   /*
2    * #%L
3    * Native ARchive plugin for Maven
4    * %%
5    * Copyright (C) 2002 - 2014 NAR Maven Plugin developers.
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   * 
11   * http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package com.github.maven_nar;
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.HashSet;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Set;
30  import java.util.jar.JarFile;
31  import java.util.zip.ZipInputStream;
32  import org.apache.commons.io.IOUtils;
33  
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.repository.ArtifactRepository;
36  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
37  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
38  import org.apache.maven.artifact.resolver.ArtifactResolver;
39  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
40  import org.apache.maven.plugin.MojoExecutionException;
41  import org.apache.maven.plugin.MojoFailureException;
42  import org.apache.maven.plugins.annotations.Component;
43  import org.apache.maven.plugins.annotations.Parameter;
44  import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
45  import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
46  import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
47  import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter;
48  import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
49  import org.codehaus.plexus.archiver.manager.ArchiverManager;
50  import org.codehaus.plexus.util.StringUtils;
51  
52  /**
53   * @author Mark Donszelmann
54   */
55  public abstract class AbstractDependencyMojo extends AbstractNarMojo {
56  
57    @Parameter(defaultValue = "${localRepository}", required = true, readonly = true)
58    private ArtifactRepository localRepository;
59  
60    /**
61     * Artifact resolver, needed to download the attached nar files.
62     */
63    @Component(role = org.apache.maven.artifact.resolver.ArtifactResolver.class)
64    protected ArtifactResolver artifactResolver;
65  
66    /**
67     * Remote repositories which will be searched for nar attachments.
68     */
69    @Parameter(defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true)
70    protected List remoteArtifactRepositories;
71  
72    /**
73     * Comma separated list of Artifact names to exclude.
74     * 
75     * @since 2.0
76     */
77    @Parameter(property = "excludeArtifactIds", defaultValue = "")
78    protected String excludeArtifactIds;
79  
80    /**
81     * Comma separated list of Artifact names to include.
82     * 
83     * @since 2.0
84     */
85    @Parameter(property = "includeArtifactIds", defaultValue = "")
86    protected String includeArtifactIds;
87  
88    /**
89     * Comma separated list of GroupId Names to exclude.
90     * 
91     * @since 2.0
92     */
93    @Parameter(property = "excludeGroupIds", defaultValue = "")
94    protected String excludeGroupIds;
95  
96    /**
97     * Comma separated list of GroupIds to include.
98     * 
99     * @since 2.0
100    */
101   @Parameter(property = "includeGroupIds", defaultValue = "")
102   protected String includeGroupIds;
103 
104   /**
105    * To look up Archiver/UnArchiver implementations
106    */
107   @Component(role = org.codehaus.plexus.archiver.manager.ArchiverManager.class)
108   protected ArchiverManager archiverManager;
109 
110   public final void downloadAttachedNars(final List<AttachedNarArtifact> dependencies)
111       throws MojoExecutionException, MojoFailureException {
112     getLog().debug("Download for NarDependencies {");
113     for (final AttachedNarArtifact attachedNarArtifact : dependencies) {
114       getLog().debug("  - " + attachedNarArtifact);
115     }
116     getLog().debug("}");
117 
118     for (final AttachedNarArtifact attachedNarArtifact : dependencies) {
119       try {
120         getLog().debug("Resolving " + attachedNarArtifact);
121         this.artifactResolver.resolve(attachedNarArtifact, this.remoteArtifactRepositories, getLocalRepository());
122       } catch (final ArtifactNotFoundException e) {
123         final String message = "nar not found " + attachedNarArtifact.getId();
124         throw new MojoExecutionException(message, e);
125       } catch (final ArtifactResolutionException e) {
126         final String message = "nar cannot resolve " + attachedNarArtifact.getId();
127         throw new MojoExecutionException(message, e);
128       }
129     }
130   }
131 
132   public final List<AttachedNarArtifact> getAllAttachedNarArtifacts(final List<NarArtifact> narArtifacts,
133       List<? extends Executable> libraries) throws MojoExecutionException, MojoFailureException {
134     final List<AttachedNarArtifact> artifactList = new ArrayList<>();
135     for (NarArtifact dependency : narArtifacts) {
136       if ("NAR".equalsIgnoreCase(getMavenProject().getPackaging())) {
137         final String bindings[] = getBindings(libraries, dependency);
138 
139         // TODO: dependency.getFile(); find out what the stored pom says
140         // about this - what nars should exist, what layout are they
141         // using...
142         for (final String binding : bindings) {
143           artifactList.addAll(getAttachedNarArtifacts(dependency, /* library. */
144               getAOL(), binding));
145         }
146       } else {
147         artifactList.addAll(getAttachedNarArtifacts(dependency, getAOL(), Library.EXECUTABLE));
148         artifactList.addAll(getAttachedNarArtifacts(dependency, getAOL(), Library.SHARED));
149         artifactList.addAll(getAttachedNarArtifacts(dependency, getAOL(), Library.JNI));
150         artifactList.addAll(getAttachedNarArtifacts(dependency, getAOL(), Library.STATIC));
151       }
152       artifactList.addAll(getAttachedNarArtifacts(dependency, null, NarConstants.NAR_NO_ARCH));
153     }
154     return artifactList;
155   }
156 
157   protected final ArchiverManager getArchiverManager() {
158     return this.archiverManager;
159   }
160 
161   /**
162    * Returns the artifacts which must be taken in account for the Mojo.
163    * 
164    * @return Artifacts
165    */
166   protected abstract ScopeFilter getArtifactScopeFilter();
167 
168   /**
169    * Returns the attached NAR Artifacts (AOL and noarch artifacts) from the NAR
170    * dependencies artifacts of the project.
171    * The artifacts which will be processed are those returned by the method
172    * getArtifacts() which must be implemented
173    * in each class which extends AbstractDependencyMojo.
174    * 
175    * @return Attached NAR Artifacts
176    * @throws MojoFailureException
177    * @throws MojoExecutionException
178    * 
179    * @see getArtifacts
180    */
181   protected List<AttachedNarArtifact> getAttachedNarArtifacts(List<? extends Executable> libraries)
182       throws MojoFailureException, MojoExecutionException {
183     getLog().info("Getting Nar dependencies");
184     final List<NarArtifact> narArtifacts = getNarArtifacts();
185     final List<AttachedNarArtifact> attachedNarArtifacts = getAllAttachedNarArtifacts(narArtifacts, libraries);
186     return attachedNarArtifacts;
187   }
188 
189   private List<AttachedNarArtifact> getAttachedNarArtifacts(final NarArtifact dependency, final AOL aol,
190       final String type) throws MojoExecutionException, MojoFailureException {
191     getLog().debug("GetNarDependencies for " + dependency + ", aol: " + aol + ", type: " + type);
192     final List<AttachedNarArtifact> artifactList = new ArrayList<>();
193     final NarInfo narInfo = dependency.getNarInfo();
194     final String[] nars = narInfo.getAttachedNars(aol, type);
195     // FIXME Move this to NarInfo....
196     if (nars != null) {
197       for (final String nar2 : nars) {
198         getLog().debug("    Checking: " + nar2);
199         if (nar2.equals("")) {
200           continue;
201         }
202         final String[] nar = nar2.split(":", 5);
203         if (nar.length >= 4) {
204           try {
205             final String groupId = nar[0].trim();
206             final String artifactId = nar[1].trim();
207             final String ext = nar[2].trim();
208             String classifier = nar[3].trim();
209             // translate for instance g++ to gcc...
210             final AOL aolString = narInfo.getAOL(aol);
211             if (aolString != null) {
212               classifier = NarUtil.replace("${aol}", aolString.toString(), classifier);
213             }
214             final String version = nar.length >= 5 ? nar[4].trim() : dependency.getBaseVersion();
215             artifactList.add(new AttachedNarArtifact(groupId, artifactId, version, dependency.getScope(), ext,
216                 classifier, dependency.isOptional(), dependency.getFile()));
217           } catch (final InvalidVersionSpecificationException e) {
218             throw new MojoExecutionException("Error while reading nar file for dependency " + dependency, e);
219           }
220         } else {
221           getLog().warn("nars property in " + dependency.getArtifactId() + " contains invalid field: '" + nar2);
222         }
223       }
224     }
225     return artifactList;
226   }
227 
228   protected String[] getBindings(List<? extends Executable> libraries, NarArtifact dependency)
229       throws MojoFailureException, MojoExecutionException {
230 
231     Set<String> bindings = new HashSet<>();
232     if (libraries != null){
233       for (Object library : libraries) {
234         Executable exec = (Executable) library;
235         // how does this project specify the dependency is used
236         String binding = exec.getBinding(dependency);
237         if( null != binding )
238           bindings.add(binding);
239       }
240     }
241 
242     // - if it is specified but the atrifact is not available should fail.
243     // otherwise
244     // how does the artifact specify it should be used by default
245     // -
246     // whats the preference for this type of library to use (shared - shared,
247     // static - static...)
248 
249     // library.getType()
250     if (bindings.isEmpty())
251       bindings.add(dependency.getNarInfo().getBinding(getAOL(), Library.STATIC));
252 
253     return bindings.toArray(new String[1]);
254   }
255 
256   protected String getBinding(Executable exec, NarArtifact dependency)
257       throws MojoFailureException, MojoExecutionException {
258 
259     // how does this project specify the dependency is used
260     String binding = exec.getBinding(dependency);
261 
262     // - if it is specified but the atrifact is not available should fail.
263     // otherwise
264     // how does the artifact specify it should be used by default
265     // -
266     // whats the preference for this type of library to use (shared - shared,
267     // static - static...)
268 
269     // library.getType()
270     if (binding == null)
271       binding = dependency.getNarInfo().getBinding(getAOL(), Library.STATIC);
272 
273     return binding;
274   }
275 
276   /**
277    * The plugin remote repositories declared in the pom.
278    * 
279    * @since 2.2
280    */
281   // @Parameter(defaultValue = "${project.pluginArtifactRepositories}")
282   // private List remotePluginRepositories;
283 
284   protected final ArtifactRepository getLocalRepository() {
285     return this.localRepository;
286   }
287 
288   /**
289    * Returns dependencies which are dependent on NAR files (i.e. contain
290    * NarInfo)
291    */
292   public final List<NarArtifact> getNarArtifacts() throws MojoExecutionException {
293     final List<NarArtifact> narDependencies = new LinkedList<>();
294 
295     FilterArtifacts filter = new FilterArtifacts();
296 
297     filter.addFilter(new GroupIdFilter(cleanToBeTokenizedString(this.includeGroupIds),
298         cleanToBeTokenizedString(this.excludeGroupIds)));
299 
300     filter.addFilter(new ArtifactIdFilter(cleanToBeTokenizedString(this.includeArtifactIds),
301         cleanToBeTokenizedString(this.excludeArtifactIds)));
302 
303     filter.addFilter(getArtifactScopeFilter());
304 
305     @SuppressWarnings("unchecked")
306     Set<Artifact> artifacts = getMavenProject().getArtifacts();
307 
308     // perform filtering
309     try {
310       artifacts = filter.filter(artifacts);
311     } catch (ArtifactFilterException e) {
312       throw new MojoExecutionException(e.getMessage(), e);
313     }
314 
315     for (final Object element : artifacts) {
316       final Artifact dependency = (Artifact) element;
317 
318       if ("nar".equalsIgnoreCase(dependency.getType())) {
319         getLog().debug("Examining artifact for NarInfo: " + dependency);
320 
321         final NarInfo narInfo = getNarInfo(dependency);
322         if (narInfo != null) {
323           getLog().debug("    - added as NarDependency");
324           narDependencies.add(new NarArtifact(dependency, narInfo));
325         }
326       }
327     }
328     getLog().debug("Dependencies contained " + narDependencies.size() + " NAR artifacts.");
329     return narDependencies;
330   }
331 
332   public final NarInfo getNarInfo(final Artifact dependency) throws MojoExecutionException {
333     // FIXME reported to maven developer list, isSnapshot changes behaviour
334     // of getBaseVersion, called in pathOf.
335     dependency.isSnapshot();
336 
337     if (dependency.getFile().isDirectory()) {
338       getLog().debug("Dependency is not packaged: " + dependency.getFile());
339 
340       return new NarInfo(dependency.getGroupId(), dependency.getArtifactId(), dependency.getBaseVersion(), getLog(),
341           dependency.getFile());
342     }
343 
344     final File file = new File(getLocalRepository().getBasedir(), getLocalRepository().pathOf(dependency));
345     if (!file.exists()) {
346       getLog().debug("Dependency nar file does not exist: " + file);
347       return null;
348     }
349 
350     ZipInputStream zipStream = null;
351     try {
352       zipStream = new ZipInputStream(new FileInputStream(file));
353       if (zipStream.getNextEntry() == null) {
354         getLog().debug("Skipping unreadable artifact: " + file);
355         return null;
356       }
357     } catch (IOException e) {
358       throw new MojoExecutionException("Error while testing for zip file " + file, e);
359     } finally {
360       IOUtils.closeQuietly(zipStream);
361     }
362 
363     JarFile jar = null;
364     try {
365       jar = new JarFile(file);
366       final NarInfo info = new NarInfo(dependency.getGroupId(), dependency.getArtifactId(),
367           dependency.getBaseVersion(), getLog());
368       if (!info.exists(jar)) {
369         getLog().debug("Dependency nar file does not contain this artifact: " + file);
370         return null;
371       }
372       info.read(jar);
373       return info;
374     } catch (final IOException e) {
375       throw new MojoExecutionException("Error while reading " + file, e);
376     } finally {
377       IOUtils.closeQuietly(jar);
378     }
379   }
380 
381   protected final NarManager getNarManager() throws MojoFailureException, MojoExecutionException {
382     return new NarManager(getLog(), getLocalRepository(), getMavenProject(), getArchitecture(), getOS(), getLinker());
383   }
384 
385   protected final List/* <ArtifactRepository> */getRemoteRepositories() {
386     return this.remoteArtifactRepositories;
387   }
388 
389   public final void unpackAttachedNars(final List<AttachedNarArtifact> dependencies)
390       throws MojoExecutionException, MojoFailureException {
391     final File unpackDir = getUnpackDirectory();
392 
393     getLog().info(String.format("Unpacking %1$d dependencies to %2$s", dependencies.size(), unpackDir));
394 
395     for (final Object element : dependencies) {
396       final AttachedNarArtifact dependency = (AttachedNarArtifact) element;
397       final File file = getNarManager().getNarFile(dependency); // dependency.getNarFile();
398       getLog().debug(String.format("Unpack %1$s (%2$s) to %3$s", dependency, file, unpackDir));
399 
400       // TODO: each dependency may have it's own (earlier) version of layout -
401       // if it is unknown then we should report an error to update the nar
402       // package
403       // NarLayout layout = AbstractNarLayout.getLayout( "NarLayout21"/* TODO:
404       // dependency.getLayout() */, getLog() );
405       // we should then target the layout to match the layout for this nar which
406       // is the workspace we are in.
407       final NarLayout layout = getLayout();
408       // TODO: the dependency may be specified against a different linker
409       // (version)?
410       // AOL aol = dependency.getClassifier(); Trim
411       layout.unpackNar(unpackDir, this.archiverManager, file, getOS(), getLinker().getName(), getAOL());
412     }
413   }
414 
415   //
416   // clean up configuration string before it can be tokenized
417   //
418   private static String cleanToBeTokenizedString(String str) {
419     String ret = "";
420     if (!StringUtils.isEmpty(str)) {
421       // remove initial and ending spaces, plus all spaces next to commas
422       ret = str.trim().replaceAll("[\\s]*,[\\s]*", ",");
423     }
424 
425     return ret;
426   }
427 }