You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2015/10/13 22:46:26 UTC

svn commit: r1708509 - in /sling/trunk/tooling/release/launchpad-comparator: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/sling/ src/main/java/org/apache/sling/tooling/ src/main/java/org/apache/...

Author: rombert
Date: Tue Oct 13 20:46:25 2015
New Revision: 1708509

URL: http://svn.apache.org/viewvc?rev=1708509&view=rev
Log:
SLING-5099 - Create 'New and Noteworthy' page for Sling 8

Added a tooling/release/launchpad-comparator helper module which
presents differences between two launchpad versions.

Added:
    sling/trunk/tooling/release/launchpad-comparator/
    sling/trunk/tooling/release/launchpad-comparator/pom.xml
    sling/trunk/tooling/release/launchpad-comparator/src/
    sling/trunk/tooling/release/launchpad-comparator/src/main/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/AetherSetup.java
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/ArtifactKey.java
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Artifacts.java
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Main.java
    sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/VersionChange.java
    sling/trunk/tooling/release/launchpad-comparator/src/test/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/lc/
    sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/lc/ArtifactsTest.java

Added: sling/trunk/tooling/release/launchpad-comparator/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/pom.xml?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/pom.xml (added)
+++ sling/trunk/tooling/release/launchpad-comparator/pom.xml Tue Oct 13 20:46:25 2015
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>25</version>
+    </parent>
+    <groupId>org.apache.sling.tooling</groupId>
+    <artifactId>launchpad-comparator</artifactId>
+    <version>0.9.0-SNAPSHOT</version>
+    <name>Apache Sling Launchpad Comparator</name>
+    <description>Command line utility which helps with comparing the artifacts contained by two launchpad instances.</description>
+
+    <dependencies>
+
+        <!-- Aether dependencies start -->
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-api</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-util</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-impl</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-connector-basic</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-transport-file</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-transport-http</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.aether</groupId>
+            <artifactId>aether-transport-wagon</artifactId>
+            <version>${aetherVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-aether-provider</artifactId>
+            <version>${mavenVersion}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.wagon</groupId>
+            <artifactId>wagon-ssh</artifactId>
+            <version>${wagonVersion}</version>
+        </dependency>
+        <!-- Aether dependencies start -->
+
+        <!-- Sl4fj -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- Provisioning model -->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.provisioning.model</artifactId>
+            <version>1.3.0</version>
+        </dependency>
+
+        <!-- Launchpad plugin, used to read bundle lists -->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>maven-launchpad-plugin</artifactId>
+            <version>2.3.2</version>
+        </dependency>
+
+        <!-- Force usable version of plexus-utils, needed by launchpad plugin -->
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+            <version>3.0.15</version>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.4.1</version>
+                <configuration>
+                    <transformers>
+                        <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                            <mainClass>org.apache.sling.tooling.lc.Main</mainClass>
+                        </transformer>
+                    </transformers>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <sling.java.version>8</sling.java.version>
+        <aetherVersion>1.0.0.v20140518</aetherVersion>
+        <mavenVersion>3.1.0</mavenVersion>
+        <wagonVersion>1.0</wagonVersion>
+    </properties>
+</project>
\ No newline at end of file

Added: sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/AetherSetup.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/AetherSetup.java?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/AetherSetup.java (added)
+++ sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/AetherSetup.java Tue Oct 13 20:46:25 2015
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package org.apache.sling.tooling.lc;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
+import org.eclipse.aether.impl.DefaultServiceLocator;
+import org.eclipse.aether.repository.LocalRepository;
+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.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.transport.TransporterFactory;
+import org.eclipse.aether.transport.file.FileTransporterFactory;
+import org.eclipse.aether.transport.http.HttpTransporterFactory;
+
+class AetherSetup {
+    
+    private final List<RemoteRepository> repos = Arrays.asList(
+        new RemoteRepository.Builder("central", "default", "http://central.maven.org/maven2/").build(),
+        new RemoteRepository.Builder("apache", "default", "https://repository.apache.org/content/groups/snapshots/").build()
+    );
+    
+    private final RepositorySystem system;
+    private final DefaultRepositorySystemSession session;
+            
+    
+    public AetherSetup() {
+        
+        DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+        locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
+        locator.addService(TransporterFactory.class, FileTransporterFactory.class);
+        locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+
+        system = locator.getService(RepositorySystem.class);
+        
+        session = MavenRepositorySystemUtils.newSession();
+
+        LocalRepository localRepo = new LocalRepository("target/local-repo");
+        session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
+    }
+    
+
+    public File download(String coordinates) throws ArtifactResolutionException {
+        ArtifactResult fromResult = system.resolveArtifact(session, new ArtifactRequest(new DefaultArtifact(coordinates), repos, null));
+
+        return fromResult.getArtifact().getFile();
+    }
+}

Added: sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/ArtifactKey.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/ArtifactKey.java?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/ArtifactKey.java (added)
+++ sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/ArtifactKey.java Tue Oct 13 20:46:25 2015
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+package org.apache.sling.tooling.lc;
+
+import java.util.Objects;
+
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
+import org.apache.sling.provisioning.model.Artifact;
+
+class ArtifactKey implements Comparable<ArtifactKey> {
+    
+    private String groupId;
+    private String artifactId;
+    private String classifier;
+    private String type;
+    
+    public ArtifactKey(Bundle bundle) {
+        
+        this(bundle.getGroupId(), bundle.getArtifactId(), bundle.getClassifier(), bundle.getType());
+    }
+
+    public ArtifactKey(Artifact artifact) {
+        
+        this(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getType());
+    }
+    
+    private ArtifactKey(String groupId, String artifactId, String classifier, String type) {
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+        this.classifier = classifier != null ? classifier : "";
+        this.type = type;
+    }
+
+    @Override
+    public int hashCode() {
+        
+        return Objects.hash(artifactId, classifier, groupId, type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        
+        if ( !(obj instanceof ArtifactKey) ) {
+            return false;
+        }
+        
+        ArtifactKey other = (ArtifactKey) obj;
+        
+        return Objects.equals(artifactId, other.artifactId)
+                && Objects.equals(groupId, other.groupId)
+                && Objects.equals(classifier, other.classifier)
+                && Objects.equals(type, other.type);
+    }
+    
+    public String getArtifactId() {
+        return artifactId;
+    }
+    
+    public String getGroupId() {
+        return groupId;
+    }
+    
+    public String getClassifier() {
+        return classifier;
+    }
+    
+    public String getType() {
+        return type;
+    }
+    
+    @Override
+    public String toString() {
+        
+        return "Artifact [groupId=" + groupId + ", artifactId=" + artifactId + ", classifier=" + classifier + ", type=" + type + "]";
+    }
+
+    @Override
+    public int compareTo(ArtifactKey o) {
+        
+        Artifact us = new Artifact(groupId, artifactId, "0.0.0", classifier, type);
+        Artifact them = new Artifact(o.groupId, o.artifactId, "0.0.0", o.classifier, o.type);
+        
+        return us.compareTo(them);
+    }
+}
\ No newline at end of file

Added: sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Artifacts.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Artifacts.java?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Artifacts.java (added)
+++ sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Artifacts.java Tue Oct 13 20:46:25 2015
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+package org.apache.sling.tooling.lc;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class Artifacts {
+
+    private static final Pattern VERSION_NUMBER = Pattern.compile("^(\\d+)(-SNAPSHOT)?");
+    
+    public static final String launchpadCoordinates(String version) {
+        
+        Matcher versionMatcher = VERSION_NUMBER.matcher(version);
+        
+        if ( !versionMatcher.matches()) {
+            throw new IllegalArgumentException("Invalid version " + version);
+        }
+        
+        int versionNumber = Integer.parseInt(versionMatcher.group(1));
+
+        
+        // versions 6 and 7 used an XML bundle list
+        if ( versionNumber < 8 ) {
+            return "org.apache.sling:org.apache.sling.launchpad:xml:bundlelist:" + version;
+        }
+        
+        // versions 8 and newer use the provisioning model
+        return "org.apache.sling:org.apache.sling.launchpad:txt:slingfeature:" + version;
+    }
+}

Added: sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Main.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Main.java?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Main.java (added)
+++ sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/Main.java Tue Oct 13 20:46:25 2015
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+package org.apache.sling.tooling.lc;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.sling.maven.projectsupport.BundleListUtils;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
+import org.apache.sling.provisioning.model.Artifact;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.ModelUtility;
+import org.apache.sling.provisioning.model.io.ModelReader;
+
+import com.google.common.collect.Sets;
+
+public class Main {
+
+    public static void main(String[] args) throws Exception {
+        
+        // 0. read CLI arguments
+        String firstVersion = "7";
+        String secondVersion = "8";
+        if ( args.length == 2) {
+            firstVersion = args[0];
+            secondVersion = args[1];
+        }
+        
+        System.out.format("Computing differences between Launchpad versions %s and %s...%n", 
+                firstVersion, secondVersion);
+        
+        // 1. download artifacts
+        AetherSetup aether = new AetherSetup();
+
+        File fromFile = aether.download(Artifacts.launchpadCoordinates(firstVersion));
+        File toFile = aether.download(Artifacts.launchpadCoordinates(secondVersion));
+
+        // 2. parse artifact definitions
+        Model model;
+        try (BufferedReader reader = Files.newBufferedReader(toFile.toPath())) {
+            model = ModelUtility.getEffectiveModel(ModelReader.read(reader, null));
+        }
+        
+        // TODO - versions are not interpolated
+        Map<ArtifactKey, Artifact> to = model.getFeatures().stream()
+            .flatMap( f -> f.getRunModes().stream())
+            .flatMap( r -> r.getArtifactGroups().stream())
+            .flatMap( g -> StreamSupport.stream(g.spliterator(), false))
+            .collect(Collectors.toMap( a -> new ArtifactKey(a), Function.identity()));
+        
+        BundleList readBundleList = BundleListUtils.readBundleList(fromFile);
+        
+        // 3. generate added / removed / changed
+        Map<ArtifactKey, Artifact> from = readBundleList.getStartLevels().stream()
+            .flatMap( sl -> sl.getBundles().stream() )
+            .collect(Collectors.toMap( b -> new ArtifactKey(b), Main::newArtifact));
+        
+        Set<Artifact> removed = Sets.difference(from.keySet(), to.keySet()).stream()
+            .map( k -> from.get(k))
+            .collect(Collectors.toSet());
+
+        Set<Artifact> added = Sets.difference(to.keySet(), from.keySet()).stream()
+                .map( k -> to.get(k))
+                .collect(Collectors.toSet());
+
+        Map<ArtifactKey, VersionChange> changed = to.values().stream()
+                .filter( k -> !added.contains(k) && !removed.contains(k))
+                .map( k -> new ArtifactKey(k))
+                .filter( k -> !Objects.equals(to.get(k).getVersion(), from.get(k).getVersion()))
+                .collect(Collectors.toMap( Function.identity(), k -> new VersionChange(from.get(k).getVersion(), to.get(k).getVersion())));
+
+        // 4. output changes
+        
+        System.out.println("Added ");
+        added.stream().sorted().forEach(Main::outputFormatted);
+        
+        System.out.println("Removed ");
+        removed.stream().sorted().forEach(Main::outputFormatted);
+        
+        System.out.println("Changed");
+        changed.entrySet().stream()
+            .sorted( (a, b) -> a.getKey().compareTo(b.getKey()) )
+            .forEach(Main::outputFormatted);
+    }
+
+    private static Artifact newArtifact(Bundle bundle) {
+        
+        return new Artifact(bundle.getGroupId(), bundle.getArtifactId(), bundle.getVersion(), bundle.getClassifier(), bundle.getType());
+    }
+    
+    private static void outputFormatted(Artifact a) {
+        
+        System.out.format("    %-30s : %-55s : %s%n", a.getGroupId(), a.getArtifactId(), a.getVersion());
+        
+    }
+
+    private static void outputFormatted(Map.Entry<ArtifactKey, VersionChange> e) {
+        
+        System.out.format("    %-30s : %-55s : %s -> %s%n", e.getKey().getGroupId(), e.getKey().getArtifactId(), e.getValue().getFrom(), e.getValue().getTo());
+        
+    }
+}

Added: sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/VersionChange.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/VersionChange.java?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/VersionChange.java (added)
+++ sling/trunk/tooling/release/launchpad-comparator/src/main/java/org/apache/sling/tooling/lc/VersionChange.java Tue Oct 13 20:46:25 2015
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package org.apache.sling.tooling.lc;
+
+class VersionChange {
+    
+    private String from;
+    private String to;
+
+    public VersionChange(String from, String to) {
+        this.from = from;
+        this.to = to;
+    }
+    
+    public String getFrom() {
+        return from;
+    }
+    
+    public String getTo() {
+        return to;
+    }
+    
+    @Override
+    public String toString() {
+        return "VersionChange [" +from + " -> " + to +"]";
+    }
+}
\ No newline at end of file

Added: sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/lc/ArtifactsTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/lc/ArtifactsTest.java?rev=1708509&view=auto
==============================================================================
--- sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/lc/ArtifactsTest.java (added)
+++ sling/trunk/tooling/release/launchpad-comparator/src/test/java/org/apache/sling/tooling/lc/ArtifactsTest.java Tue Oct 13 20:46:25 2015
@@ -0,0 +1,35 @@
+package org.apache.sling.tooling.lc;
+
+import static org.apache.sling.tooling.lc.Artifacts.launchpadCoordinates;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class ArtifactsTest {
+    
+    @Test
+    public void launchpadV7() {
+        
+        assertThat(launchpadCoordinates("7"), equalTo("org.apache.sling:org.apache.sling.launchpad:xml:bundlelist:7"));
+    }
+    
+    @Test
+    public void launchpadV7Snapshot() {
+        
+        assertThat(launchpadCoordinates("7-SNAPSHOT"), equalTo("org.apache.sling:org.apache.sling.launchpad:xml:bundlelist:7-SNAPSHOT"));
+    }
+
+    @Test
+    public void launchpadV8() {
+        
+        assertThat(launchpadCoordinates("8"), equalTo("org.apache.sling:org.apache.sling.launchpad:txt:slingfeature:8"));
+    }
+    
+    @Test
+    public void launchpadV8Snapshot() {
+        
+        assertThat(launchpadCoordinates("8-SNAPSHOT"), equalTo("org.apache.sling:org.apache.sling.launchpad:txt:slingfeature:8-SNAPSHOT"));
+    }
+
+}