Skip to content

Commit 6de0f88

Browse files
committed
Try to use gradle transformer, not working
1 parent 08f59d6 commit 6de0f88

File tree

9 files changed

+210
-110
lines changed

9 files changed

+210
-110
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.gradle.internal.dependencies.patches.hdfs;
11+
12+
import org.gradle.api.artifacts.transform.CacheableTransform;
13+
import org.gradle.api.artifacts.transform.InputArtifact;
14+
import org.gradle.api.artifacts.transform.TransformAction;
15+
import org.gradle.api.artifacts.transform.TransformOutputs;
16+
import org.gradle.api.artifacts.transform.TransformParameters;
17+
import org.gradle.api.file.FileSystemLocation;
18+
import org.gradle.api.provider.Provider;
19+
import org.gradle.api.tasks.Classpath;
20+
import org.gradle.api.tasks.Input;
21+
import org.gradle.api.tasks.Optional;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.objectweb.asm.ClassReader;
24+
import org.objectweb.asm.ClassVisitor;
25+
import org.objectweb.asm.ClassWriter;
26+
import java.io.File;
27+
import java.io.FileOutputStream;
28+
import java.io.IOException;
29+
import java.io.InputStream;
30+
import java.util.Enumeration;
31+
import java.util.HashMap;
32+
import java.util.List;
33+
import java.util.Locale;
34+
import java.util.Map;
35+
import java.util.function.Function;
36+
import java.util.jar.JarEntry;
37+
import java.util.jar.JarFile;
38+
import java.util.jar.JarOutputStream;
39+
import java.util.stream.Stream;
40+
41+
import static java.util.Map.entry;
42+
43+
44+
@CacheableTransform
45+
public abstract class HdfsClassPatcher implements TransformAction<HdfsClassPatcher.Parameters> {
46+
47+
record JarPatchers(String artifactName, Map<String, Function<ClassWriter, ClassVisitor>> jarPatchers) {}
48+
49+
static final List<JarPatchers> allPatchers = List.of(
50+
new JarPatchers(
51+
"hadoop-common",
52+
Map.ofEntries(
53+
entry("org/apache/hadoop/util/ShutdownHookManager.class", ShutdownHookManagerPatcher::new),
54+
entry("org/apache/hadoop/util/Shell.class", ShellPatcher::new),
55+
entry("org/apache/hadoop/security/UserGroupInformation.class", SubjectGetSubjectPatcher::new)
56+
)
57+
),
58+
new JarPatchers(
59+
"hadoop-auth",
60+
Map.of(
61+
"org/apache/hadoop/security/authentication/client/KerberosAuthenticator.class",
62+
SubjectGetSubjectPatcher::new
63+
)
64+
)
65+
);
66+
67+
interface Parameters extends TransformParameters {
68+
@Input
69+
@Optional
70+
List<String> getMatchingArtifacts();
71+
72+
void setMatchingArtifacts(List<String> matchingArtifacts);
73+
}
74+
75+
@Classpath
76+
@InputArtifact
77+
public abstract Provider<FileSystemLocation> getInputArtifact();
78+
79+
@Override
80+
public void transform(@NotNull TransformOutputs outputs) {
81+
File inputFile = getInputArtifact().get().getAsFile();
82+
83+
List<String> matchingArtifacts = getParameters().getMatchingArtifacts();
84+
if (matchingArtifacts.isEmpty() == false &&
85+
matchingArtifacts.stream().noneMatch(supported -> inputFile.getName().contains(supported))
86+
) {
87+
outputs.file(getInputArtifact());
88+
} else {
89+
Stream<JarPatchers> patchersToApply = allPatchers.stream().filter(jp -> matchingArtifacts.contains(jp.artifactName()));
90+
patchersToApply.forEach(patchers -> {
91+
Map<String, Function<ClassWriter, ClassVisitor>> jarPatchers = new HashMap<>(patchers.jarPatchers());
92+
File outputFile = outputs.file(inputFile.getName().replace(".jar", "-patched.jar"));
93+
94+
patchJar(inputFile, outputFile, jarPatchers);
95+
96+
if (jarPatchers.isEmpty() == false) {
97+
throw new IllegalArgumentException(
98+
String.format(
99+
Locale.ROOT,
100+
"error patching [%s] with [%s]: the jar does not contain [%s]",
101+
inputFile.getName(),
102+
patchers.artifactName(),
103+
String.join(", ", jarPatchers.keySet())
104+
)
105+
);
106+
}
107+
});
108+
}
109+
}
110+
111+
private static void patchJar(File inputFile, File outputFile, Map<String, Function<ClassWriter, ClassVisitor>> jarPatchers) {
112+
try (JarFile jarFile = new JarFile(inputFile); JarOutputStream jos = new JarOutputStream(new FileOutputStream(outputFile))) {
113+
Enumeration<JarEntry> entries = jarFile.entries();
114+
while (entries.hasMoreElements()) {
115+
JarEntry entry = entries.nextElement();
116+
String entryName = entry.getName();
117+
// Add the entry to the new JAR file
118+
jos.putNextEntry(new JarEntry(entryName));
119+
System.out.println("EntryName = " + entryName);
120+
121+
Function<ClassWriter, ClassVisitor> classPatcher = jarPatchers.remove(entryName);
122+
if (classPatcher != null) {
123+
System.out.println("Patching " + entryName);
124+
byte[] classToPatch = jarFile.getInputStream(entry).readAllBytes();
125+
126+
ClassReader classReader = new ClassReader(classToPatch);
127+
ClassWriter classWriter = new ClassWriter(classReader, 0);
128+
classReader.accept(classPatcher.apply(classWriter), 0);
129+
130+
jos.write(classWriter.toByteArray());
131+
} else {
132+
// Read the entry's data and write it to the new JAR
133+
try (InputStream is = jarFile.getInputStream(entry)) {
134+
byte[] buffer = new byte[1024];
135+
int bytesRead;
136+
while ((bytesRead = is.read(buffer)) != -1) {
137+
jos.write(buffer, 0, bytesRead);
138+
}
139+
}
140+
}
141+
jos.closeEntry();
142+
}
143+
} catch (IOException ex) {
144+
throw new RuntimeException(ex);
145+
}
146+
}
147+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
package org.elasticsearch.hdfs.patch;
10+
package org.elasticsearch.gradle.internal.dependencies.patches.hdfs;
1111

1212
import org.objectweb.asm.MethodVisitor;
1313
import org.objectweb.asm.Opcodes;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
package org.elasticsearch.hdfs.patch;
10+
package org.elasticsearch.gradle.internal.dependencies.patches.hdfs;
1111

1212
import org.objectweb.asm.ClassVisitor;
1313
import org.objectweb.asm.ClassWriter;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
package org.elasticsearch.hdfs.patch;
10+
package org.elasticsearch.gradle.internal.dependencies.patches.hdfs;
1111

1212
import org.objectweb.asm.ClassVisitor;
1313
import org.objectweb.asm.ClassWriter;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
package org.elasticsearch.hdfs.patch;
10+
package org.elasticsearch.gradle.internal.dependencies.patches.hdfs;
1111

1212
import org.objectweb.asm.ClassVisitor;
1313
import org.objectweb.asm.ClassWriter;

plugins/repository-hdfs/build.gradle

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,20 @@ versions << [
2222
'hadoop': '3.4.1'
2323
]
2424

25+
def patched = Attribute.of('patched', Boolean)
26+
2527
configurations {
2628
hdfsFixture2
2729
hdfsFixture3
30+
hdfsClientApi {
31+
attributes {
32+
attribute(patched, true)
33+
}
34+
}
2835
}
2936

3037
dependencies {
38+
hdfsClientApi "org.apache.hadoop:hadoop-client-api:${versions.hadoop}"
3139
api project(path: 'hadoop-client-api', configuration: 'default')
3240
if (isEclipse) {
3341
/*
@@ -69,6 +77,20 @@ dependencies {
6977

7078
hdfsFixture2 project(path: ':test:fixtures:hdfs-fixture', configuration: 'shadowedHdfs2')
7179
hdfsFixture3 project(path: ':test:fixtures:hdfs-fixture', configuration: 'shadow')
80+
81+
attributesSchema {
82+
attribute(patched)
83+
}
84+
artifactTypes.getByName("jar") {
85+
attributes.attribute(patched, false)
86+
}
87+
registerTransform(org.elasticsearch.gradle.internal.dependencies.patches.hdfs.HdfsClassPatcher) {
88+
from.attribute(patched, false)
89+
to.attribute(patched, true)
90+
parameters {
91+
matchingArtifacts = ["hadoop-common", "hadoop-auth"]
92+
}
93+
}
7294
}
7395

7496
restResources {

plugins/repository-hdfs/hadoop-client-api/build.gradle

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,32 @@ import org.gradle.api.file.ArchiveOperations
22

33
apply plugin: 'elasticsearch.java'
44

5+
def patched = Attribute.of('patched', Boolean)
6+
57
configurations {
68
thejar {
79
canBeResolved = true
10+
attributes {
11+
attribute(patched, true)
12+
}
813
}
9-
patcher
1014
}
1115

1216
dependencies {
1317
thejar("org.apache.hadoop:hadoop-client-api:${project.parent.versions.hadoop}") {
1418
transitive = false
1519
}
16-
patcher(project(':plugins:repository-hdfs:hadoop-common-patcher'))
17-
}
18-
19-
def outputDir = layout.buildDirectory.dir("patched-classes")
20-
21-
def patchTask = tasks.register("patchClasses", JavaExec) {
22-
inputs.files(configurations.thejar).withPathSensitivity(PathSensitivity.RELATIVE)
23-
outputs.dir(outputDir)
24-
classpath = configurations.patcher
25-
mainClass = 'org.elasticsearch.hdfs.patch.HdfsClassPatcher'
26-
def thejar = configurations.thejar
27-
doFirst {
28-
args(thejar.singleFile, outputDir.get().asFile)
20+
attributesSchema {
21+
attribute(patched)
2922
}
30-
}
31-
32-
interface InjectedArchiveOps {
33-
@Inject ArchiveOperations getArchiveOperations()
34-
}
35-
36-
tasks.named('jar').configure {
37-
dependsOn(configurations.thejar)
38-
def injected = project.objects.newInstance(InjectedArchiveOps)
39-
def thejar = configurations.thejar
40-
from(patchTask)
41-
from({ injected.getArchiveOperations().zipTree(thejar.singleFile) }) {
42-
eachFile {
43-
if (outputDir.get().file(it.relativePath.pathString).asFile.exists()) {
44-
it.exclude()
45-
}
23+
artifactTypes.getByName("jar") {
24+
attributes.attribute(patched, false)
25+
}
26+
registerTransform(org.elasticsearch.gradle.internal.dependencies.patches.hdfs.HdfsClassPatcher) {
27+
from.attribute(patched, false)
28+
to.attribute(patched, true)
29+
parameters {
30+
matchingArtifacts = ["hadoop-common", "hadoop-auth"]
4631
}
4732
}
4833
}

plugins/repository-hdfs/hadoop-common-patcher/src/main/java/org/elasticsearch/hdfs/patch/HdfsClassPatcher.java

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)