Skip to content

Commit

Permalink
gradle: Use a proxy to wrap the existing manifest
Browse files Browse the repository at this point in the history
We use a proxy over all the interfaces of the existing manifest object
so that if someone has set it to a custom type with custom interfaces,
the custom interfaces will be preserved.

See #5275

Signed-off-by: BJ Hargrave <bj@hargrave.dev>
  • Loading branch information
bjhargrave committed Jun 3, 2022
1 parent ea2ebe0 commit 3be4064
Showing 1 changed file with 62 additions and 58 deletions.
Expand Up @@ -17,10 +17,15 @@
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -37,9 +42,6 @@
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.java.archives.Attributes;
import org.gradle.api.java.archives.ManifestException;
import org.gradle.api.java.archives.ManifestMergeSpec;
import org.gradle.api.java.archives.internal.DefaultManifest;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
Expand All @@ -66,7 +68,6 @@
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import aQute.lib.utf8properties.UTF8Properties;
import groovy.lang.Closure;

/**
* BundleTaskExtension for Gradle.
Expand Down Expand Up @@ -100,7 +101,7 @@ public class BundleTaskExtension {
private final ConfigurableFileCollection classpath;
private final Provider<String> bnd;
private final MapProperty<String, Object> properties;
private final EffectiveManifest effectiveManifest;
private final Optional<EffectiveManifest> effectiveManifest;

/**
* The bndfile property.
Expand Down Expand Up @@ -217,10 +218,15 @@ public BundleTaskExtension(org.gradle.api.tasks.bundling.Jar task) {
properties = objects.mapProperty(String.class, Object.class)
.convention(Maps.of("project", "__convention__"));
// Wrap manifest
org.gradle.api.java.archives.Manifest manifest = task.getManifest();
effectiveManifest = new EffectiveManifest(manifest);
if (manifest != null) {
task.setManifest(effectiveManifest);
org.gradle.api.java.archives.Manifest delegate = task.getManifest();
if (delegate != null) {
EffectiveManifest em = new EffectiveManifest(delegate);
effectiveManifest = Optional.of(em);
org.gradle.api.java.archives.Manifest manifest = (org.gradle.api.java.archives.Manifest) Proxy
.newProxyInstance(em.classLoader(), em.interfaces(), em);
task.setManifest(manifest);
} else {
effectiveManifest = Optional.empty();
}
// need to programmatically add to inputs since @InputFiles in a
// extension is not processed
Expand Down Expand Up @@ -515,7 +521,8 @@ public void execute(Task t) {
long now = System.currentTimeMillis();
archiveFile.setLastModified(now);
// Set effective manifest to generated manifest
effectiveManifest.setEffectiveManifest(builtJar.getManifest());
Manifest manifest = builtJar.getManifest();
effectiveManifest.ifPresent(em -> em.setEffectiveManifest(manifest));
logReport(builder, getTask().getLogger());
if (!builder.isOk()) {
failTask("Bundle " + archiveFileName + " has errors", archiveFile);
Expand All @@ -537,77 +544,74 @@ private boolean isEmpty(String header) {
}
}

static final class EffectiveManifest implements org.gradle.api.java.archives.Manifest {
static final class EffectiveManifest implements InvocationHandler {
final org.gradle.api.java.archives.Manifest delegate;
final Class<?> delegateClass;
Manifest effectiveManifest;

EffectiveManifest(org.gradle.api.java.archives.Manifest delegate) {
this.delegate = delegate;
this.delegateClass = delegate.getClass();
this.effectiveManifest = null;
}

void setEffectiveManifest(Manifest effectiveManifest) {
this.effectiveManifest = effectiveManifest;
}

@Override
public org.gradle.api.java.archives.Manifest attributes(Map<String, ?> attributes) throws ManifestException {
delegate.attributes(attributes);
return this;
ClassLoader classLoader() {
return delegateClass.getClassLoader();
}

@Override
public org.gradle.api.java.archives.Manifest attributes(Map<String, ?> attributes, String sectionName)
throws ManifestException {
delegate.attributes(attributes, sectionName);
return this;
Class<?>[] interfaces() {
return collectInterfaces(delegateClass, new LinkedHashSet<>()).toArray(new Class<?>[0]);
}

@Override
public org.gradle.api.java.archives.Manifest from(Object... mergePath) {
delegate.from(mergePath);
return this;
}

@Override
public org.gradle.api.java.archives.Manifest from(Object mergePath, Closure<?> closure) {
delegate.from(mergePath, closure);
return this;
}

@Override
public org.gradle.api.java.archives.Manifest from(Object mergePath, Action<ManifestMergeSpec> action) {
delegate.from(mergePath, action);
return this;
private static Set<Class<?>> collectInterfaces(Class<?> c, Set<Class<?>> found) {
while (c != null) {
for (Class<?> i : c.getInterfaces()) {
if (found.add(i)) {
collectInterfaces(i, found);
}
}
c = c.getSuperclass();
}
return found;
}

@Override
public Attributes getAttributes() {
return delegate.getAttributes();
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();

@Override
public org.gradle.api.java.archives.Manifest getEffectiveManifest() {
Manifest effectiveManifest = this.effectiveManifest;
if (effectiveManifest == null) {
return delegate.getEffectiveManifest();
if ((effectiveManifest != null) && name.equals("getEffectiveManifest") && (parameterTypes.length == 0)) {
org.gradle.api.java.archives.Manifest result = new DefaultManifest(null);
result.attributes(new AttributesMap(effectiveManifest.getMainAttributes()));
effectiveManifest.getEntries()
.forEach((section, attrs) -> result.attributes(new AttributesMap(attrs), section));
return result;
}
org.gradle.api.java.archives.Manifest result = new DefaultManifest(null);
result.attributes(new AttributesMap(effectiveManifest.getMainAttributes()));
effectiveManifest.getEntries()
.forEach((section, attrs) -> result.attributes(new AttributesMap(attrs), section));
return result;
}

@Override
public Map<String, Attributes> getSections() {
return delegate.getSections();
}

@Override
public org.gradle.api.java.archives.Manifest writeTo(Object path) {
getEffectiveManifest().writeTo(path);
return this;
Method delegateMethod;
try {
delegateMethod = delegateClass.getMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
throw new UnsupportedOperationException(e);
}
try {
Object result = delegateMethod.invoke(delegate, args);
if (result == delegate) { // returning "this"
return proxy;
}
return result;
} catch (InvocationTargetException e) {
Throwable t = e;
for (Throwable cause; (t instanceof InvocationTargetException) && ((cause = t.getCause()) != null);) {
t = cause;
}
throw t;
}
}
}

Expand Down

0 comments on commit 3be4064

Please sign in to comment.