/** * Copyright 2012 Google Inc. All Rights Reserved. */ package com.google.appengine; import static com.google.common.collect.Iterables.find; import com.google.common.io.ByteStreams; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.io.Files; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.ArtifactResult; import org.eclipse.aether.resolution.VersionRangeRequest; import org.eclipse.aether.resolution.VersionRangeResolutionException; import org.eclipse.aether.resolution.VersionRangeResult; import org.eclipse.aether.version.Version; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * Resolves the sdk in the .m2 repository, retrieving the appropriate version for this version of * the plugin if necessary. * * @author Matt Stephenson <[email protected]> */ public class SdkResolver { private static final String SDK_GROUP_ID = "com.google.appengine"; private static final String SDK_ARTIFACT_ID = "appengine-java-sdk"; private static final String SDK_EXTENSION = "zip"; public static File getSdk(MavenProject project, RepositorySystem repoSystem, RepositorySystemSession repoSession, List<RemoteRepository>... repos) throws MojoExecutionException { Artifact artifact = (Artifact) find(project.getPluginArtifacts(), new Predicate<Artifact>() { @Override public boolean apply(Artifact artifact1) { return (artifact1.getArtifactId().equals("appengine-maven-plugin") && artifact1.getGroupId().equals("com.google.appengine")); } }); String version = artifact.getVersion(); if(version.endsWith("-SNAPSHOT")) { String newestVersion = determineNewestVersion(repoSystem, repoSession, repos); return getSdk(newestVersion, repoSystem, repoSession, repos); } return getSdk(version, repoSystem, repoSession, repos); } private static String determineNewestVersion(RepositorySystem repoSystem, RepositorySystemSession repoSession, List<RemoteRepository>[] repos) throws MojoExecutionException { String version;VersionRangeRequest rangeRequest = new VersionRangeRequest(); rangeRequest.setArtifact(new DefaultArtifact(SDK_GROUP_ID + ":" + SDK_ARTIFACT_ID + ":[0,)")); for(List<RemoteRepository> repoList : repos) { for(RemoteRepository repo : repoList) { rangeRequest.addRepository(repo); } } VersionRangeResult rangeResult; try { rangeResult = repoSystem.resolveVersionRange(repoSession, rangeRequest); } catch (VersionRangeResolutionException e) { throw new MojoExecutionException("Could not resolve latest version of the App Engine Java SDK", e); } List<Version> versions = rangeResult.getVersions(); Collections.sort(versions); Version newest = Iterables.getLast(versions); version = newest.toString(); return version; } public static File getSdk(String version, RepositorySystem repoSystem, RepositorySystemSession repoSession, List<RemoteRepository>... repos) throws MojoExecutionException { List<RemoteRepository> allRepos = ImmutableList.copyOf(Iterables.concat(repos)); ArtifactRequest request = new ArtifactRequest(new DefaultArtifact(SDK_GROUP_ID, SDK_ARTIFACT_ID, SDK_EXTENSION, version), allRepos, null); ArtifactResult result; try { result = repoSystem.resolveArtifact(repoSession, request); } catch (ArtifactResolutionException e) { throw new MojoExecutionException("Could not resolve SDK artifact in Maven.", e); } File sdkArchive = result.getArtifact().getFile(); File sdkRepoDir = sdkArchive.getParentFile(); File sdkBaseDir = new File(sdkRepoDir, SDK_ARTIFACT_ID); if (sdkBaseDir.exists() && !sdkBaseDir.isDirectory()) { throw new MojoExecutionException("Could not unpack the SDK because there is an unexpected file at " + sdkBaseDir + " which conflicts with where we plan to unpack the SDK."); } if (!sdkBaseDir.exists()) { sdkBaseDir.mkdirs(); } // While processing the zip archive, if we find an initial entry that is a directory, and all entries are a child // of this directory, then we append this to the sdkBaseDir we return. String sdkBaseDirSuffix = null; try { ZipFile sdkZipArchive = new ZipFile(sdkArchive); Enumeration<? extends ZipEntry> zipEntries = sdkZipArchive.entries(); if (!zipEntries.hasMoreElements()) { throw new MojoExecutionException("The SDK zip archive appears corrupted. There are no entries in the zip index."); } ZipEntry firstEntry = zipEntries.nextElement(); if (firstEntry.isDirectory()) { sdkBaseDirSuffix = firstEntry.getName(); } else { //Reinitialize entries zipEntries = sdkZipArchive.entries(); } while (zipEntries.hasMoreElements()) { ZipEntry zipEntry = zipEntries.nextElement(); if (!zipEntry.isDirectory()) { File zipEntryDestination = new File(sdkBaseDir, zipEntry.getName()); if (!zipEntry.getName().startsWith(sdkBaseDirSuffix)) { //We found an entry that doesn't use this initial base directory, oh well, just set it to null. sdkBaseDirSuffix = null; } if (!zipEntryDestination.exists()) { Files.createParentDirs(zipEntryDestination); Files.write(ByteStreams.toByteArray(sdkZipArchive.getInputStream(zipEntry)), zipEntryDestination); } } } } catch (IOException e) { throw new MojoExecutionException("Could not open SDK zip archive.", e); } if (sdkBaseDirSuffix == null) { return sdkBaseDir; } return new File(sdkBaseDir, sdkBaseDirSuffix); } }