/** * */ package de.saumya.mojo.ruby.gems; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata; import org.apache.maven.artifact.repository.metadata.RepositoryMetadata; import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager; import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.model.Dependency; import org.apache.maven.model.building.ModelBuildingRequest; import org.apache.maven.project.DefaultProjectBuildingRequest; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuilder; import org.apache.maven.project.ProjectBuildingException; import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.repository.RepositorySystem; import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest; import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.util.IOUtil; @Component(role = GemManager.class) public class DefaultGemManager implements GemManager { public static final String DEFAULT_GEMS_REPOSITORY_BASE_URL = "http://rubygems-proxy.torquebox.org/"; @Requirement private RepositorySystem repositorySystem; @Requirement private RepositoryMetadataManager repositoryMetadataManager; @Requirement private ProjectBuilder builder; private Artifact setLatestVersionIfMissing(final Artifact artifact, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException { if (artifact.getVersion() == null) { final List<String> versions = availableVersions(artifact, localRepository, remoteRepositories); artifact.setVersionRange(null); artifact.setVersion(versions.get(versions.size() - 1)); } return artifact; } public Artifact createGemArtifact(final String gemname) throws GemException { return createGemArtifact(gemname, null); } public Artifact createGemArtifact(final String gemname, final String version) throws GemException { return createArtifact("rubygems", gemname, version, "gem"); } public Artifact createGemArtifactWithLatestVersion(final String gemname, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException { final Artifact gem = createGemArtifact(gemname, null); setLatestVersionIfMissing(gem, localRepository, remoteRepositories); return gem; } // gem repositories public ArtifactRepository defaultGemArtifactRepository() { return defaultGemArtifactRepositoryForVersion("0.0.0"); } public ArtifactRepository defaultGemArtifactRepositoryForVersion( final String artifactVersion) { final String preRelease = artifactVersion != null && artifactVersion.matches(".*[a-zA-Z].*") ? "pre" : ""; return this.repositorySystem.createArtifactRepository("rubygems-" + preRelease + "releases", DEFAULT_GEMS_REPOSITORY_BASE_URL + preRelease + "releases", new DefaultRepositoryLayout(), new ArtifactRepositoryPolicy(), new ArtifactRepositoryPolicy()); } public void addDefaultGemRepository(final List<ArtifactRepository> repos) { addDefaultGemRepositories(repos); } public void addDefaultGemRepositories(final List<ArtifactRepository> repos) { ArtifactRepositoryPolicy enabled = new ArtifactRepositoryPolicy(true, "never", "strict"); ArtifactRepositoryPolicy disabled = new ArtifactRepositoryPolicy(false, "never", "strict"); ArtifactRepository repo = this.repositorySystem.createArtifactRepository("rubygems-releases", DEFAULT_GEMS_REPOSITORY_BASE_URL + "releases", new DefaultRepositoryLayout(), enabled, disabled); repos.add(repo); // repo = this.repositorySystem.createArtifactRepository("rubygems-prereleases", // DEFAULT_GEMS_REPOSITORY_BASE_URL + "prereleases", // new DefaultRepositoryLayout(), // disabled, enabled); //repos.add(repo); } public void addDefaultGemRepositoryForVersion(final String artifactVersion, final List<ArtifactRepository> repos) { final ArtifactRepository repo = defaultGemArtifactRepositoryForVersion(artifactVersion); for (final ArtifactRepository ar : repos) { if (ar.getUrl().equals(repo.getUrl())) { return; } } repos.add(repo); } // maven-gem artifacts public Artifact createJarArtifactForGemname(final String gemName) throws GemException { return createJarArtifactForGemname(gemName, null); } public Artifact createPomArtifactForGemname(final String gemName) throws GemException { final int index = gemName.lastIndexOf(GROUP_ID_ARTIFACT_ID_SEPARATOR); final String groupId = gemName.substring(0, index); final String artifactId = gemName.substring(index + 1); return createArtifact(groupId, artifactId, null, "pom"); } public Artifact createJarArtifactForGemname(final String gemName, final String version) throws GemException { final int index = gemName.lastIndexOf(GROUP_ID_ARTIFACT_ID_SEPARATOR); final String groupId = gemName.substring(0, index); final String artifactId = gemName.substring(index + 1); return createArtifact(groupId, artifactId, version, "jar"); } public Artifact createJarArtifactForGemnameWithLatestVersion( final String gemName, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException { final Artifact artifact = createJarArtifactForGemname(gemName, null); setLatestVersionIfMissing(artifact, localRepository, remoteRepositories); return artifact; } // convenience methods public Artifact createArtifact(final String groupId, final String artifactId, final String version, final String type) { return createArtifact(groupId, artifactId, version, null, type); } public Artifact createArtifact(final String groupId, final String artifactId, final String version, final String classifier, final String type) { final Dependency dep = new Dependency(); dep.setGroupId(groupId); dep.setArtifactId(artifactId); dep.setType(type); if(classifier != null){ dep.setClassifier(classifier); } dep.setVersion(version == null ? "[0,)" : version); return this.repositorySystem.createDependencyArtifact(dep); } public Set<Artifact> resolve(final Artifact artifact, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException{ return resolve(artifact, localRepository, remoteRepositories, false); } public Set<Artifact> resolve(final Artifact artifact, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories, boolean transitively) throws GemException { if (artifact.getFile() == null || !artifact.getFile().exists()) { ArtifactResolutionRequest req = new ArtifactResolutionRequest() .setArtifact(artifact) .setResolveTransitively(transitively) .setLocalRepository(localRepository) .setRemoteRepositories(remoteRepositories); final Set<Artifact> artifacts = this.repositorySystem.resolve(req).getArtifacts(); if (artifacts.size() == 0) { throw new GemException("could not resolve artifact: " + artifact); } artifact.setFile(artifacts.iterator().next().getFile()); return artifacts; } else { return Collections.emptySet(); } } public MavenProject buildModel(final Artifact artifact, final Object repositorySystemSession, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories, boolean resolve) throws GemException { // build a POM and resolve all artifacts final ProjectBuildingRequest pomRequest = new DefaultProjectBuildingRequest() .setLocalRepository(localRepository) .setRemoteRepositories(remoteRepositories) .setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_STRICT) .setResolveDependencies(resolve); setRepositorySession(pomRequest, repositorySystemSession); try { return this.builder.build(artifact, pomRequest).getProject(); } catch (final ProjectBuildingException e) { throw new GemException("error building POM", e); } } public void setRepositorySession( ProjectBuildingRequest pomRequest, Object repositorySystemSession ) throws GemException { Class<?> clazz; try { try { clazz = Thread.currentThread().getContextClassLoader().loadClass( "org.sonatype.aether.RepositorySystemSession" ); } catch (ClassNotFoundException e1) { // TODO use eclipse aether here clazz = Thread.currentThread().getContextClassLoader().loadClass( "org.eclipse.aether.RepositorySystemSession" ); } Method m = pomRequest.getClass().getMethod("setRepositorySession", clazz ); m.invoke( pomRequest, repositorySystemSession ); } catch ( Exception e ) { throw new GemException("error building POM", e); } } public MavenProject buildPom(final Artifact artifact, final Object repositorySystemSession, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException { MavenProject pom = buildModel(artifact, repositorySystemSession, localRepository, remoteRepositories, true); resolve(pom.getArtifact(), localRepository, remoteRepositories); return pom; } // versions public String latestVersion(final Artifact artifact, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException { final List<String> versions = availableVersions(artifact, localRepository, remoteRepositories); return versions.get(versions.size() - 1); } public List<String> availableVersions(final Artifact artifact, final ArtifactRepository localRepository, final List<ArtifactRepository> remoteRepositories) throws GemException { final MetadataResolutionRequest request = new DefaultMetadataResolutionRequest(); request.setArtifact(artifact); request.setLocalRepository(localRepository); request.setRemoteRepositories(remoteRepositories); final RepositoryMetadata metadata = new ArtifactRepositoryMetadata(request.getArtifact()); try { this.repositoryMetadataManager.resolve(metadata, request); } catch (final RepositoryMetadataResolutionException e) { throw new GemException("error updateding versions of artifact: " + artifact, e); } final List<String> versions; if (metadata.getMetadata().getVersioning() == null) { if(remoteRepositories.size() == 0){ throw new GemException("no version found - maybe system is offline or wrong <groupId:artifactId>: " + artifact.getGroupId() + GROUP_ID_ARTIFACT_ID_SEPARATOR + artifact.getArtifactId()); } versions = new ArrayList<String>(); } else { versions= metadata.getMetadata() .getVersioning() .getVersions(); } for(ArtifactRepository repo : remoteRepositories){ BufferedReader reader = null; try { URL url = new URL(repo.getUrl() + "/" + artifact.getGroupId().replace(".", "/") + "/" + artifact.getArtifactId() + "/"); reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8")); String line = reader.readLine(); while (line != null) { //TODO maybe try to be more relax on how the version is embedded if (line.contains("<a href=")) { // first cut the end and then the beginning - allow greedy .* String version = line.replaceFirst("</a>.*", "") .replaceFirst(".*<a href=\".*\">", ""); if(version.endsWith("/")){ version = version.substring(0, version.length() - 1); if (!versions.contains(version)) { versions.add(version); } } } line = reader.readLine(); } } catch (MalformedURLException e) { // should never happen throw new RuntimeException("error scraping versions from html page", e); } catch (IOException e) { // TODO // System.err.println("error scraping versions from html index page: " + e.getMessage()); // e.printStackTrace(); } finally { IOUtil.close(reader); } } Collections.sort(versions); return versions; } }