2222import java .net .URL ;
2323import java .net .URLClassLoader ;
2424import java .util .Enumeration ;
25+ import java .util .jar .JarFile ;
26+ import java .util .jar .Manifest ;
27+ import lombok .extern .slf4j .Slf4j ;
2528import net .bytebuddy .jar .asm .ClassReader ;
2629import net .bytebuddy .jar .asm .ClassWriter ;
2730import net .bytebuddy .jar .asm .commons .ClassRemapper ;
2831
32+ @ Slf4j
2933public class ExporterClassLoader extends URLClassLoader {
3034 // We need to prefix the names to prevent the gradle shadowJar relocation rules from touching
3135 // them. It's possible to do this by excluding this class from shading, but it may cause issue
3236 // with transitive dependencies down the line.
33- private final ShadingRemapper remapper =
37+ private static final ShadingRemapper remapper =
3438 new ShadingRemapper (
3539 rule (
3640 "#io.opentelemetry.OpenTelemetry" ,
@@ -52,8 +56,11 @@ public class ExporterClassLoader extends URLClassLoader {
5256 rule ("#java.util.logging.Logger" , "#io.opentelemetry.auto.bootstrap.PatchLogger" ),
5357 rule ("#org.slf4j" , "#io.opentelemetry.auto.slf4j" ));
5458
55- public ExporterClassLoader (final URL [] urls , final ClassLoader parent ) {
56- super (urls , parent );
59+ private final Manifest manifest ;
60+
61+ public ExporterClassLoader (final URL url , final ClassLoader parent ) {
62+ super (new URL [] {url }, parent );
63+ this .manifest = getManifest (url );
5764 }
5865
5966 @ Override
@@ -69,16 +76,79 @@ public Enumeration<URL> getResources(final String name) throws IOException {
6976
7077 @ Override
7178 protected Class <?> findClass (final String name ) throws ClassNotFoundException {
72-
7379 // Use resource loading to get the class as a stream of bytes, then use ASM to transform it.
74- try (final InputStream in = getResourceAsStream (name .replace ('.' , '/' ) + ".class" )) {
75- final ClassWriter cw = new ClassWriter (0 );
76- final ClassReader cr = new ClassReader (in );
77- cr .accept (new ClassRemapper (cw , remapper ), ClassReader .EXPAND_FRAMES );
78- final byte [] bytes = cw .toByteArray ();
80+ InputStream in = getResourceAsStream (name .replace ('.' , '/' ) + ".class" );
81+ if (in == null ) {
82+ throw new ClassNotFoundException (name );
83+ }
84+ try {
85+ final byte [] bytes = remapClassBytes (in );
86+ definePackageIfNeeded (name );
7987 return defineClass (name , bytes , 0 , bytes .length );
8088 } catch (final IOException e ) {
81- throw new ClassNotFoundException (name );
89+ throw new ClassNotFoundException (name , e );
90+ } finally {
91+ try {
92+ in .close ();
93+ } catch (IOException e ) {
94+ log .debug (e .getMessage (), e );
95+ }
96+ }
97+ }
98+
99+ private void definePackageIfNeeded (String className ) {
100+ String packageName = getPackageName (className );
101+ if (packageName == null ) {
102+ // default package
103+ return ;
104+ }
105+ if (isPackageDefined (packageName )) {
106+ // package has already been defined
107+ return ;
108+ }
109+ try {
110+ definePackage (packageName );
111+ } catch (IllegalArgumentException e ) {
112+ // this exception is thrown when the package has already been defined, which is possible due
113+ // to race condition with the check above
114+ if (!isPackageDefined (packageName )) {
115+ // this shouldn't happen however
116+ log .error (e .getMessage (), e );
117+ }
118+ }
119+ }
120+
121+ private boolean isPackageDefined (String packageName ) {
122+ return getPackage (packageName ) != null ;
123+ }
124+
125+ private void definePackage (String packageName ) {
126+ if (manifest == null ) {
127+ definePackage (packageName , null , null , null , null , null , null , null );
128+ } else {
129+ definePackage (packageName , manifest , null );
130+ }
131+ }
132+
133+ private static byte [] remapClassBytes (InputStream in ) throws IOException {
134+ final ClassWriter cw = new ClassWriter (0 );
135+ final ClassReader cr = new ClassReader (in );
136+ cr .accept (new ClassRemapper (cw , remapper ), ClassReader .EXPAND_FRAMES );
137+ return cw .toByteArray ();
138+ }
139+
140+ private static String getPackageName (String className ) {
141+ int index = className .lastIndexOf ('.' );
142+ return index == -1 ? null : className .substring (0 , index );
143+ }
144+
145+ private static Manifest getManifest (URL url ) {
146+ try {
147+ JarFile jarFile = new JarFile (url .getFile ());
148+ return jarFile .getManifest ();
149+ } catch (IOException e ) {
150+ log .warn (e .getMessage (), e );
82151 }
152+ return null ;
83153 }
84154}
0 commit comments