package org.apache.maven.plugin.nar;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
/**
* @author Mark Donszelmann (Mark.Donszelmann@gmail.com)
*/
public class NarManager
{
private Log log;
private MavenProject project;
private ArtifactRepository repository;
private AOL defaultAOL;
private String linkerName;
private String[] narTypes =
{ NarConstants.NAR_NO_ARCH, Library.STATIC, Library.SHARED, Library.JNI, Library.PLUGIN };
public NarManager( Log log, ArtifactRepository repository, MavenProject project, String architecture, String os,
Linker linker )
throws MojoFailureException, MojoExecutionException
{
this.log = log;
this.repository = repository;
this.project = project;
this.defaultAOL = NarUtil.getAOL(project, architecture, os, linker, null);
this.linkerName = NarUtil.getLinkerName(project, architecture, os, linker);
}
/**
* Returns dependencies which are dependent on NAR files (i.e. contain NarInfo)
*/
public final List/* <NarArtifact> */getNarDependencies(String scope)
throws MojoExecutionException
{
List narDependencies = new LinkedList();
for ( Iterator i = getDependencies( scope ).iterator(); i.hasNext(); )
{
Artifact dependency = (Artifact) i.next();
log.debug("Examining artifact for NarInfo: " + dependency);
NarInfo narInfo = getNarInfo(dependency);
if ( narInfo != null )
{
log.debug(" - added as NarDependency");
narDependencies.add(new NarArtifact(dependency, narInfo));
}
}
return narDependencies;
}
/**
* Returns all NAR dependencies by type: noarch, static, dynamic, jni, plugin.
*
* @throws MojoFailureException
*/
public final Map/* <String, List<AttachedNarArtifact>> */getAttachedNarDependencyMap( String scope )
throws MojoExecutionException, MojoFailureException
{
Map attachedNarDependencies = new HashMap();
for ( Iterator i = getNarDependencies( scope ).iterator(); i.hasNext(); )
{
Artifact dependency = (Artifact) i.next();
for ( int j = 0; j < narTypes.length; j++ )
{
List artifactList = getAttachedNarDependencies( dependency, defaultAOL, narTypes[j] );
if ( artifactList != null )
{
attachedNarDependencies.put(narTypes[j], artifactList);
}
}
}
return attachedNarDependencies;
}
public final List/* <AttachedNarArtifact> */getAttachedNarDependencies( List/* <NarArtifacts> */narArtifacts )
throws MojoExecutionException, MojoFailureException
{
return getAttachedNarDependencies(narArtifacts, (String) null);
}
public final List/* <AttachedNarArtifact> */getAttachedNarDependencies( List/* <NarArtifacts> */narArtifacts,
String classifier )
throws MojoExecutionException, MojoFailureException
{
AOL aol = null;
String type = null;
if ( classifier != null )
{
int dash = classifier.lastIndexOf('-');
if ( dash < 0 )
{
aol = new AOL(classifier);
type = null;
}
else
{
aol = new AOL(classifier.substring(0, dash));
type = classifier.substring(dash + 1);
}
}
return getAttachedNarDependencies(narArtifacts, aol, type);
}
public final List/* <AttachedNarArtifact> */getAttachedNarDependencies(
List/* <NarArtifacts> */narArtifacts, String[] classifiers)
throws MojoExecutionException, MojoFailureException
{
List artifactList = new ArrayList();
if( classifiers != null && classifiers.length > 0 )
{
for ( int j = 0; j < classifiers.length; j++ )
{
if ( artifactList != null )
{
artifactList.addAll( getAttachedNarDependencies( narArtifacts, classifiers[j] ));
}
}
}
else
{
artifactList.addAll( getAttachedNarDependencies( narArtifacts, ( String )null ));
}
return artifactList;
}
/**
* Returns a list of all attached nar dependencies for a specific binding and "noarch", but not where "local" is
* specified
*
* @param scope compile, test, runtime, ....
* @param aol either a valid aol, noarch or null. In case of null both the default getAOL() and noarch dependencies
* are returned.
* @param type noarch, static, shared, jni, or null. In case of null the default binding found in narInfo is used.
* @return
* @throws MojoExecutionException
* @throws MojoFailureException
*/
public final List/* <AttachedNarArtifact> */getAttachedNarDependencies( List/* <NarArtifacts> */narArtifacts,
AOL archOsLinker, String type )
throws MojoExecutionException, MojoFailureException
{
boolean noarch = false;
AOL aol = archOsLinker;
if ( aol == null )
{
noarch = true;
aol = defaultAOL;
}
List artifactList = new ArrayList();
for ( Iterator i = narArtifacts.iterator(); i.hasNext(); )
{
Artifact dependency = (Artifact) i.next();
NarInfo narInfo = getNarInfo(dependency);
if ( noarch )
{
artifactList.addAll( getAttachedNarDependencies( dependency, null, NarConstants.NAR_NO_ARCH ) );
}
// use preferred binding, unless non existing.
String binding = narInfo.getBinding( aol, type != null ? type : Library.STATIC );
// FIXME kludge, but does not work anymore since AOL is now a class
if ( aol.equals( NarConstants.NAR_NO_ARCH ) )
{
// FIXME no handling of local
artifactList.addAll( getAttachedNarDependencies( dependency, null, NarConstants.NAR_NO_ARCH ) );
}
else
{
artifactList.addAll( getAttachedNarDependencies( dependency, aol, binding ) );
}
}
return artifactList;
}
private List/* <AttachedNarArtifact> */getAttachedNarDependencies( Artifact dependency, AOL archOsLinker,
String type )
throws MojoExecutionException, MojoFailureException
{
AOL aol = archOsLinker;
log.debug( "GetNarDependencies for " + dependency + ", aol: " + aol + ", type: " + type );
List artifactList = new ArrayList();
NarInfo narInfo = getNarInfo(dependency);
String[] nars = narInfo.getAttachedNars(aol, type);
// FIXME Move this to NarInfo....
if ( nars != null )
{
for ( int j = 0; j < nars.length; j++ )
{
log.debug(" Checking: " + nars[j]);
if ( nars[j].equals( "" ) )
{
continue;
}
String[] nar = nars[j].split(":", 5);
if ( nar.length >= 4 )
{
try
{
String groupId = nar[0].trim();
String artifactId = nar[1].trim();
String ext = nar[2].trim();
String classifier = nar[3].trim();
// translate for instance g++ to gcc...
aol = narInfo.getAOL(aol);
if ( aol != null )
{
classifier = NarUtil.replace( "${aol}", aol.toString(), classifier );
}
String version = nar.length >= 5 ? nar[4].trim() : dependency.getVersion();
artifactList.add( new AttachedNarArtifact( groupId, artifactId, version, dependency.getScope(),
ext, classifier, dependency.isOptional(), dependency.getFile() ));
}
catch ( InvalidVersionSpecificationException e )
{
throw new MojoExecutionException( "Error while reading nar file for dependency " + dependency,
e );
}
}
else
{
log.warn( "nars property in " + dependency.getArtifactId() + " contains invalid field: '" + nars[j]
+ "' for type: " + type);
}
}
}
return artifactList;
}
public final NarInfo getNarInfo(Artifact dependency)
throws MojoExecutionException
{
// FIXME reported to maven developer list, isSnapshot changes behaviour
// of getBaseVersion, called in pathOf.
dependency.isSnapshot();
File file = new File( repository.getBasedir(), repository.pathOf( dependency ) );
if ( !file.exists() )
{
return null;
}
JarFile jar = null;
try
{
jar = new JarFile(file);
NarInfo info =
new NarInfo( dependency.getGroupId(), dependency.getArtifactId(), dependency.getBaseVersion(), log );
if ( !info.exists( jar ) )
{
return null;
}
info.read(jar);
return info;
}
catch ( IOException e )
{
throw new MojoExecutionException("Error while reading " + file, e);
}
finally
{
if ( jar != null )
{
try
{
jar.close();
}
catch ( IOException e )
{
// ignore
}
}
}
}
public final File getNarFile(Artifact dependency)
throws MojoFailureException
{
// FIXME reported to maven developer list, isSnapshot changes behaviour
// of getBaseVersion, called in pathOf.
dependency.isSnapshot();
return new File( repository.getBasedir(), NarUtil.replace( "${aol}", defaultAOL.toString(),
repository.pathOf( dependency ) ) );
}
private List getDependencies( String scope )
{
if ( scope.equals( Artifact.SCOPE_TEST ) )
{
return project.getTestArtifacts();
}
else if ( scope.equals( Artifact.SCOPE_RUNTIME ) )
{
return project.getRuntimeArtifacts();
}
return project.getCompileArtifacts();
}
public final void downloadAttachedNars( List/* <NarArtifacts> */narArtifacts, List remoteRepositories,
ArtifactResolver resolver, String classifier)
throws MojoExecutionException, MojoFailureException
{
// FIXME this may not be the right way to do this.... -U ignored and
// also SNAPSHOT not used
List dependencies = getAttachedNarDependencies(narArtifacts, classifier);
log.debug( "Download called with classifier: " + classifier + " for NarDependencies {" );
for ( Iterator i = dependencies.iterator(); i.hasNext(); )
{
log.debug(" - " + (i.next()));
}
log.debug("}");
for ( Iterator i = dependencies.iterator(); i.hasNext(); )
{
Artifact dependency = (Artifact) i.next();
try
{
log.debug("Resolving " + dependency);
resolver.resolve(dependency, remoteRepositories, repository);
}
catch ( ArtifactNotFoundException e )
{
String message = "nar not found " + dependency.getId();
throw new MojoExecutionException(message, e);
}
catch ( ArtifactResolutionException e )
{
String message = "nar cannot resolve " + dependency.getId();
throw new MojoExecutionException(message, e);
}
}
}
public final void unpackAttachedNars( List/* <NarArtifacts> */narArtifacts, ArchiverManager archiverManager,
String classifier, String os, NarLayout layout, File unpackDir )
throws MojoExecutionException, MojoFailureException
{
log.debug( "Unpack called for OS: " + os + ", classifier: " + classifier + " for NarArtifacts {" );
for ( Iterator i = narArtifacts.iterator(); i.hasNext(); )
{
log.debug(" - " + (i.next()));
}
log.debug("}");
// FIXME, kludge to get to download the -noarch, based on classifier
List dependencies = getAttachedNarDependencies(narArtifacts, classifier);
for ( Iterator i = dependencies.iterator(); i.hasNext(); )
{
Artifact dependency = (Artifact) i.next();
log.debug("Unpack " + dependency + " to " + unpackDir);
File file = getNarFile(dependency);
layout.unpackNar(unpackDir, archiverManager, file, os, linkerName, defaultAOL);
}
}
}