package net.sourceforge.javautil.classloader.resolver.impl.maven;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
import javax.xml.bind.JAXBException;
import net.sourceforge.javautil.classloader.resolver.IClassArtifactReference;
import net.sourceforge.javautil.classloader.resolver.IClassPackageDescriptor;
import net.sourceforge.javautil.classloader.resolver.ClassPackageException;
import net.sourceforge.javautil.classloader.resolver.IClassPackageReference;
import net.sourceforge.javautil.classloader.resolver.IClassPackageRepositoryLocalImportable;
import net.sourceforge.javautil.classloader.resolver.IClassPackageRepositoryRemote;
import net.sourceforge.javautil.classloader.resolver.IClassPackageRepositoryRemoteAttempt;
import net.sourceforge.javautil.classloader.resolver.IClassPackage.IVersion;
import net.sourceforge.javautil.classloader.resolver.IClassPackageRepositoryLocal.RepositoryType;
import net.sourceforge.javautil.classloader.resolver.IClassPackageResolver;
import net.sourceforge.javautil.classloader.resolver.impl.ClassPackageImpl;
import net.sourceforge.javautil.classloader.resolver.impl.ClassPackageRepositoryLocalAbstract;
import net.sourceforge.javautil.classloader.resolver.impl.ClassPackageRepositoryRemoteAttemptImpl;
import net.sourceforge.javautil.classloader.source.ClassSource;
import net.sourceforge.javautil.classloader.source.VirtualDirectoryClassSource;
import net.sourceforge.javautil.common.ChecksumUtil;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.common.FileUtil;
import net.sourceforge.javautil.common.IOUtil;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.IVirtualArtifact;
import net.sourceforge.javautil.common.io.VirtualArtifactNotFoundException;
import net.sourceforge.javautil.common.io.IVirtualDirectory;
import net.sourceforge.javautil.common.io.IVirtualFile;
import net.sourceforge.javautil.common.io.IVirtualPath;
import net.sourceforge.javautil.common.io.impl.SimplePath;
import net.sourceforge.javautil.common.io.impl.SystemDirectory;
import net.sourceforge.javautil.common.xml.XMLDocument;
/**
* This represents a standard maven repository with the standard Maven2 layout.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class MavenRepositoryLocalPackage extends ClassPackageRepositoryLocalAbstract implements IClassPackageRepositoryLocalImportable {
public static final String LOCAL_DEFAULT_OVERRIDE = "net.sf.javautil.maven.repository.default";
/**
* This will return the default local maven repository. It will check the
* {@value #LOCAL_DEFAULT_OVERRIDE} system property, and if set use it, otherwise
* it will use the 'user.home' system property and append .m2/repository
* to point to the local repository.
*
* @return A directory pointing to the local maven repository default
*/
public static IVirtualDirectory getDefaultLocalPackageRepository () {
String location = System.getProperty(LOCAL_DEFAULT_OVERRIDE);
if (location == null) {
location = MavenLocalSettings.getDefaultInstance().getLocalRepository();
if (location == null) location = System.getProperty("user.home") + File.separator + ".m2/repository";
}
return new SystemDirectory(location);
}
protected Logger log = Logger.getLogger(MavenRepositoryLocalPackage.class.getName());
protected final IVirtualDirectory directory;
protected long lastModified;
public MavenRepositoryLocalPackage() { this(getDefaultLocalPackageRepository()); }
public MavenRepositoryLocalPackage(IVirtualDirectory directory) {
this.directory = directory;
this.lastModified = directory.getLastModified();
}
public RepositoryType getRepositoryType() {
return RepositoryType.Packaged;
}
@Override protected boolean refreshRoot() {
if (this.directory.getLastModified() > this.lastModified) {
this.lastModified = this.directory.getLastModified();
return true;
}
return false;
}
@Override public void cacheRemoteDescriptor(IClassPackageRepositoryRemote remote, IClassPackageDescriptor descriptor) {
try {
IVirtualDirectory packageDir = directory.getDirectory(this.createPackagePath(descriptor), true);
String prefix = this.createFilenamePrefix(descriptor);
IVirtualFile imported = packageDir.getFile(remote.getName().replaceAll("[:/]+", "-") + "remote-" + prefix + ".pom", true);
((ProjectObjectModel) descriptor).write(imported.getOutputStream());
} catch (VirtualArtifactNotFoundException e) {
throw new ClassPackageException(descriptor, "Could not import remote descriptor", e);
} catch (IOException e) {
throw new ClassPackageException(descriptor, "Could not import remote descriptor", e);
} catch (JAXBException e) {
throw new ClassPackageException(descriptor, "Could not import remote descriptor", e);
}
}
@Override public IClassPackageDescriptor getCachedRemotedDescriptor(IClassPackageResolver resolver, IClassPackageRepositoryRemote remote, IClassPackageReference reference) {
IVirtualDirectory packageDir = directory.getDirectory(this.createPackagePath(reference), true);
String prefix = this.createFilenamePrefix(reference);
IVirtualFile cache = packageDir.getFile( remote.getName().replaceAll("[:/ ]+", "-") + "remote-" + prefix + ".pom" );
ProjectObjectModel pom = cache == null ? null : ProjectObjectModel.parse(resolver, cache);
return pom;
}
public void cacheRemoteAttempt(List<IClassPackageRepositoryRemote> remote, IClassPackageReference reference) {
IVirtualDirectory packageDir = directory.getDirectory(this.createPackagePath(reference), true);
String filename = this.createFilenamePrefix(reference) + ".pom.lastUpdated";
Properties properties = new Properties();
if (packageDir.getFile(filename) != null) {
try {
properties.load(packageDir.getFile(filename).getInputStream());
} catch (IOException e) {
log.warning("Could not load previous remote attempt cache for: " + reference + ": " + e.getMessage());
ThrowableManagerRegistry.caught(e);
}
}
IVirtualFile cache = packageDir.getFile(filename, true);
for (IClassPackageRepositoryRemote rr : remote) {
properties.setProperty(rr.getRepositoryURL().toExternalForm(), String.valueOf(System.currentTimeMillis()));
}
OutputStream out = null;
try {
out = cache.getOutputStream();
properties.store(out, "#Last modified on: " + new Date());
} catch (IOException e) {
throw ThrowableManagerRegistry.caught(e);
} finally {
if (out != null) try { out.close(); } catch (IOException e) { ThrowableManagerRegistry.caught(e); }
}
}
public List<IClassPackageRepositoryRemoteAttempt> getLastRemoteAttempts(IClassPackageReference reference) {
List<IClassPackageRepositoryRemoteAttempt> attempts = new ArrayList<IClassPackageRepositoryRemoteAttempt>();
IVirtualDirectory packageDir = this.getDirectory(reference);
if (packageDir != null) {
IVirtualFile cache = packageDir.getFile(this.createFilenamePrefix(reference) + ".pom.lastUpdated");
if (cache != null) {
try {
Properties properties = new Properties();
properties.load( cache.getInputStream() );
Enumeration names = properties.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
attempts.add(new ClassPackageRepositoryRemoteAttemptImpl(Long.parseLong(properties.getProperty(name)), name));
}
} catch (NumberFormatException e) {
throw ThrowableManagerRegistry.caught(e);
} catch (IOException e) {
throw ThrowableManagerRegistry.caught(e);
}
}
}
return attempts;
}
public void importPackage(IVirtualFile file, String packaging, IClassPackageReference reference) {
IVirtualDirectory packageDir = directory.getDirectory(this.createPackagePath(reference), true);
String jarPrefix = this.createJarPrefix(reference);
String prefix = this.createFilenamePrefix(reference);
IVirtualFile target = packageDir.getFile(jarPrefix + "." + packaging);
if (target == null) {
ProjectObjectModel pom = new ProjectObjectModel();
pom.setModelVersion("4.0");
pom.setGroupId(reference.getGroupId());
pom.setArtifactId(reference.getArtifactId());
pom.setVersionString(reference.getVersion().toVersionString());
pom.setPackaging(packaging);
try {
pom.write(packageDir.createFile(prefix + ".pom").getOutputStream());
file.copy( packageDir.createFile(jarPrefix + "." + packaging) );
this.clearCachedInformation(reference);
} catch (JAXBException e) {
throw ThrowableManagerRegistry.caught(e);
} catch (IOException e) {
throw ThrowableManagerRegistry.caught(e);
}
} else {
throw new IllegalArgumentException("This package already exists");
}
}
public void importPackage(IClassPackageRepositoryRemote remote, IClassPackageResolver resolver, IClassPackageReference reference) {
IVirtualDirectory packageDir = directory.getDirectory(this.createPackagePath(reference), true);
String jarPrefix = this.createJarPrefix(reference);
String prefix = this.createFilenamePrefix(reference);
URL source = null;
URL pom = remote.getResource(reference, prefix + ".pom");
if (pom != null) {
boolean validated = this.download(pom, packageDir.getFile(prefix + ".pom", true));
if (!validated) this.handleInvalidChecksum(packageDir, reference, pom);
ProjectObjectModel pomXml = this.resolveDescriptor(resolver, reference);
if ("pom".equals( pomXml.getPackaging() )) return;
source = remote.getResource(reference, jarPrefix + ".jar");
if (source == null) return;
} else {
return;
}
boolean validated = this.download(source, packageDir.getFile(jarPrefix + ".jar", true));
if (!validated) this.handleInvalidChecksum(packageDir, reference, pom);
}
@Override protected ClassSource resolve(String type, IClassPackageResolver resolver, IClassPackageReference reference) {
IClassPackageDescriptor descriptor = this.getDescriptor(resolver, reference);
if (descriptor != null) {
if (!"main".equalsIgnoreCase(type)) return null;
try {
IVirtualFile jar = this.getDirectory(reference).getFile(this.createJarPrefix(reference) + ".jar");
return jar == null ? null : this.getClassSource(reference, jar);
} catch (VirtualArtifactNotFoundException e) {
throw ThrowableManagerRegistry.caught(e);
}
}
return null;
}
@Override public ProjectObjectModel resolveDescriptor(IClassPackageResolver resolver, IClassPackageReference reference) {
IVirtualDirectory directory = this.getDirectory(reference);
if (directory != null) {
IVirtualFile pd = directory.getFile( this.createFilenamePrefix(reference) + ".pom" );
try {
ProjectObjectModel model = pd != null ? ProjectObjectModel.parse(resolver, pd) : null;
// if (model != null && model.getVersion() == null)
// model.setVersionString( reference.getVersion().toVersionString() );
//
// if (model != null && model.getParent() != null) {
// model.setParentPom( (ProjectObjectModel) resolver.getDescriptor(model.getParent()) );
// }
return model;
} catch (Exception e) {
e.printStackTrace();
log.warning("Could not get descriptor: (" + reference + "): " + e.getMessage());
throw ThrowableManagerRegistry.caught(e);
}
}
return null;
}
public List<IVersion> getVersionsAvailable(IClassArtifactReference reference) {
try {
IVirtualDirectory artifactDir = this.directory.getDirectory( this.createArtifactPath(reference) );
List<IVersion> version = new ArrayList<IVersion>();
for (IVirtualDirectory versionDir : artifactDir.getDirectories()) {
version.add( ClassPackageImpl.decode(versionDir.getName()) );
}
return version;
} catch (VirtualArtifactNotFoundException e) {
return Collections.EMPTY_LIST;
}
}
public boolean contains(IClassPackageResolver resolver, IClassArtifactReference reference) { return this.getDirectory(reference) != null; }
public boolean contains(IClassPackageResolver resolver, IClassPackageReference reference) { return this.getDirectory(reference) != null; }
public IVirtualDirectory getDirectory (IClassPackageReference reference) {
try {
return this.directory.getDirectory( this.createPackagePath(reference) );
} catch (VirtualArtifactNotFoundException e) {
return null;
}
}
public IVirtualDirectory getDirectory (IClassArtifactReference reference) {
try {
return this.directory.getDirectory( this.createArtifactPath(reference) );
} catch (VirtualArtifactNotFoundException e) {
return null;
}
}
public String createJarPrefix(IClassPackageReference reference) {
StringBuffer sb = new StringBuffer()
.append( reference.getArtifactId() )
.append( "-" )
.append( reference.getVersion().toVersionString() );
if (reference.getClassifier() != null) {
sb.append("-").append(reference.getClassifier());
}
return sb.toString();
}
public String createFilenamePrefix(IClassPackageReference reference) {
return new StringBuffer()
.append( reference.getArtifactId() )
.append( "-" )
.append( reference.getVersion().toVersionString() )
.toString();
}
public IVirtualPath createPackagePath(IClassPackageReference reference) {
return new SimplePath( CollectionUtil.push(reference.getGroupId().split("\\."),
reference.getArtifactId(), reference.getVersion().toVersionString()) );
}
public IVirtualPath createArtifactPath(IClassArtifactReference reference) {
return new SimplePath( CollectionUtil.push(reference.getGroupId().split("\\."), reference.getArtifactId() ));
}
/**
* @param versionDirectory The version directory related to the checksum failure
* @param project The project related to the failure
* @param url The URL that failed checksum validation
*/
protected void handleInvalidChecksum (IVirtualDirectory versionDirectory, IClassPackageReference reference, URL url) {
log.warning(reference + " download did not pass checksum validation: " + url);
versionDirectory.remove();
throw new ClassPackageException(reference, "Checksum invalid");
}
/**
* Download and possibly validate (check sum) a package from one repository to this local repository.
*
* @param resolver The resolver to use in the download.
*/
protected boolean download (URL source, IVirtualFile targetFile) {
try {
log.warning("Downloading to maven repository (" + directory + "): " + source);
ByteArrayOutputStream checksumOut = new ByteArrayOutputStream();
String type = ChecksumUtil.downloadWithChecksum(source, checksumOut, targetFile.getOutputStream(), false);
if (type != null) {
IVirtualFile checksumFile = targetFile.getOwner().getFile(targetFile.getName() + ("md5".equals(type) ? ".md5" : ".sha1"), true);
IOUtil.transfer(new ByteArrayInputStream(checksumOut.toByteArray()), checksumFile.getOutputStream());
}
return true;
} catch (NoSuchAlgorithmException e) {
ThrowableManagerRegistry.caught(e);
return false;
} catch (IOException e) {
ThrowableManagerRegistry.caught(e);
return false;
} catch (RuntimeException e) {
ThrowableManagerRegistry.caught(e);
return false;
}
}
}