mirror of
https://github.com/corda/corda.git
synced 2025-01-29 15:43:55 +00:00
AttachmentsClassLoader fails when calling setURLStreamHandlerFactory (#4512)
This commit is contained in:
parent
8e61d11a49
commit
2acb3d37cb
@ -19,6 +19,8 @@ import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.net.*
|
||||
import java.lang.reflect.AccessibleObject.setAccessible
|
||||
import java.net.URLStreamHandlerFactory
|
||||
|
||||
/**
|
||||
* A custom ClassLoader that knows how to load classes from a set of attachments. The attachments themselves only
|
||||
@ -41,10 +43,11 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
|
||||
private val log = contextLogger()
|
||||
|
||||
init {
|
||||
// This is required to register the AttachmentURLStreamHandlerFactory.
|
||||
URL.setURLStreamHandlerFactory(AttachmentURLStreamHandlerFactory)
|
||||
// Apply our own URLStreamHandlerFactory to resolve attachments
|
||||
setOrDecorateURLStreamHandlerFactory()
|
||||
}
|
||||
|
||||
|
||||
// Jolokia and Json-simple are dependencies that were bundled by mistake within contract jars.
|
||||
// In the AttachmentsClassLoader we just ignore any class in those 2 packages.
|
||||
private val ignoreDirectories = listOf("org/jolokia/", "org/json/simple/")
|
||||
@ -115,6 +118,50 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
|
||||
return it.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply our custom factory either directly, if `URL.setURLStreamHandlerFactory` has not been called yet,
|
||||
* or use a decorator and reflection to bypass the single-call-per-JVM restriction otherwise.
|
||||
*/
|
||||
private fun setOrDecorateURLStreamHandlerFactory() {
|
||||
// Retrieve the `URL.factory` field
|
||||
val factoryField = URL::class.java.getDeclaredField("factory")
|
||||
// Make it accessible
|
||||
factoryField.isAccessible = true
|
||||
|
||||
// Check for preset factory, set directly if missing
|
||||
val existingFactory: URLStreamHandlerFactory? = factoryField.get(null) as URLStreamHandlerFactory?
|
||||
if (existingFactory == null) {
|
||||
URL.setURLStreamHandlerFactory(AttachmentURLStreamHandlerFactory)
|
||||
}
|
||||
// Otherwise, decorate the existing and replace via reflection
|
||||
// as calling `URL.setURLStreamHandlerFactory` again will throw an error
|
||||
else {
|
||||
log.warn("The URLStreamHandlerFactory was already set in the JVM. Please be aware that this is not recommended.")
|
||||
// Retrieve the field "streamHandlerLock" of the class URL that
|
||||
// is the lock used to synchronize access to the protocol handlers
|
||||
val lockField = URL::class.java.getDeclaredField("streamHandlerLock")
|
||||
// It is a private field so we need to make it accessible
|
||||
// Note: this will only work as-is in JDK8.
|
||||
lockField.isAccessible = true
|
||||
// Use the same lock to reset the factory
|
||||
synchronized(lockField.get(null)) {
|
||||
// Reset the value to prevent Error due to a factory already defined
|
||||
factoryField.set(null, null)
|
||||
// Set our custom factory and wrap the current one into it
|
||||
URL.setURLStreamHandlerFactory(
|
||||
// Set the factory to a decorator
|
||||
object : URLStreamHandlerFactory {
|
||||
// route between our own and the pre-existing factory
|
||||
override fun createURLStreamHandler(protocol: String): URLStreamHandler? {
|
||||
return AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
||||
?: existingFactory.createURLStreamHandler(protocol)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,3 +247,4 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user