blob: b7670d0c5a31a499e687ed229738c35fab32c8d7 [file] [log] [blame]
package org.apache.wicket.osgi;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.wicket.util.string.Strings;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* A test that verifies that there are no non-empty packages with the same name in two or more
* wicket modules.
*
* Based on https://gist.github.com/1977817, contributed by Andreas Pieber
*/
public class OsgiClashingPackagesTest
{
@Test
public void collectProjectPackages() throws IOException
{
char pathSeparator = System.getProperty("path.separator").charAt(0);
String classpath = System.getProperty("java.class.path");
String[] dependencies = Strings.split(classpath, pathSeparator);
// packageName -> projects containing a package with this name
Map<String, List<Project>> projectBuckets = new HashMap<>();
for (String dependency : dependencies)
{
// process only wicket-xyz.jar
if (dependency.contains("wicket-") && dependency.endsWith(".jar"))
{
JarFile jarFile = new JarFile(dependency);
try
{
String projectName = Strings.afterLast(dependency, '/');
Project project = new Project(projectName, jarFile);
project.addTo(projectBuckets);
}
finally
{
jarFile.close();
}
}
}
Set<Entry<String, List<Project>>> entrySet = projectBuckets.entrySet();
for (Entry<String, List<Project>> entry : entrySet)
{
List<Project> projects = entry.getValue();
if (projects.size() > 1)
{
fail(entry);
}
}
}
private void fail(Entry<String, List<Project>> entry)
{
StringBuilder builder = new StringBuilder();
String packageName = entry.getKey();
builder.append("Package '").append(packageName).append(
"' has files in two or more modules: ");
for (Project conflict : entry.getValue())
{
builder.append(conflict.getName()).append(", ");
}
try
{
builder.append("\nResources:\n");
Enumeration<URL> resources = getClass().getClassLoader().getResources(packageName);
while (resources.hasMoreElements())
{
URL resource = resources.nextElement();
builder.append("\n\t").append(resource.toExternalForm());
}
}
catch (IOException e)
{
e.printStackTrace();
}
Assertions.fail(builder.toString());
}
private static class Project
{
// a set with all package names in a dependency
private final Set<String> packagesWithContent = new TreeSet<>();
// the name of the dependency
private final String name;
public Project(String name, JarFile jarFile)
{
this.name = name;
collectPackageNames(jarFile);
}
/**
* Adds this project to as a value in the global map that contains 'packageName ->
* List[Project]'
*
* @param projectBuckets
* the global map
*/
public void addTo(Map<String, List<Project>> projectBuckets)
{
for (String packageWithContent : packagesWithContent)
{
if (!projectBuckets.containsKey(packageWithContent))
{
projectBuckets.put(packageWithContent, new ArrayList<>());
}
projectBuckets.get(packageWithContent).add(this);
}
}
/**
* Collects the names of all packages in this JarFile
*
* @param jarFile
* the jar file to analyze
*/
private void collectPackageNames(final JarFile jarFile)
{
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements())
{
JarEntry jarEntry = entries.nextElement();
String entryName = jarEntry.getName();
if (shouldCollect(entryName))
{
String packageName = Strings.beforeLast(entryName, '/');
packagesWithContent.add(packageName);
}
}
}
public String getName()
{
return name;
}
@Override
public String toString()
{
return "Project{" + "name='" + name + '\'' + '}';
}
private boolean shouldCollect(final String entryName)
{
return !entryName.endsWith("/") &&
// all modules have META-INF {MANIFEST.MF, Maven stuff, ..}
!entryName.startsWith("META-INF/") &&
// ignore Wicket's IInitializer conf files
(!entryName.startsWith("META-INF/wicket") || !entryName.endsWith(".properties"));
}
}
}