# Musson Industrial's Embr-Periscope Module

**URL:** <https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047>\
**Category:** 3rd Party Modules\
**Tags:** perspective\
**Created:** [November 24, 2024, 10:26pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047 "2024-11-24T22:26:31Z")\
**Posts on this page:** 1\
**Showing post:** 43

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [April 7, 2025, 11:22pm UTC](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047/43 "2025-04-07T23:22:06Z")

</div>

> [@paul-griffith](#):
>
> set the thread context classloader to Perspective's

🧙‍♂️

More testing required, but this seems to make the gateway happy (no more errors in the logs).

```kotlin
    override fun startup(activationState: LicenseState) {
        ...

        logger.debug("Registering components...")
        val originalClassLoader = Thread.currentThread().contextClassLoader
        Thread.currentThread().contextClassLoader = context.perspectiveContext.javaClass.classLoader

        ...
        componentRegistry.registerComponent(...)
        modelDelegateRegistry.register(...)
        ...

        Thread.currentThread().contextClassLoader = originalClassLoader
    }

```

Edit: The current hack. This has the benefit of also resolving my own reusable schemas.

```kotlin
class DelegatedClassLoader(parent: ClassLoader, private vararg val delegates: ClassLoader) :
    ClassLoader(parent) {

    override fun findClass(name: String): Class<*> {
        for (delegate in delegates) {
            try {
                return delegate.loadClass(name)
            } catch (_: ClassNotFoundException) {}
        }
        throw ClassNotFoundException(name)
    }

    override fun findResource(name: String): URL? =
        delegates.firstNotNullOfOrNull { it.getResource(name) }

    override fun findResources(name: String): Enumeration<URL> =
        Collections.enumeration(delegates.flatMap { it.getResources(name).toList() })
}

fun withContextClassLoaders(vararg delegates: ClassLoader, block: () -> Unit) {
    val originalContextClassLoader = Thread.currentThread().contextClassLoader
    Thread.currentThread().contextClassLoader =
        DelegatedClassLoader(originalContextClassLoader, *delegates)

    block()

    Thread.currentThread().contextClassLoader = originalContextClassLoader
}

```

```kotlin
    // GatewayHook
    override fun startup(activationState: LicenseState) {
        ...

        logger.debug("Registering components...")
        withContextClassLoaders(
            this.javaClass.classLoader,
            context.perspectiveContext.javaClass.classLoader,
        ) {
            componentRegistry.registerComponent(...)
            modelDelegateRegistry.register(...)
        }

        ...
    }

```

This fix is now included in `0.7.2`.

> **[Release @embr-modules/periscope@0.7.2 · mussonindustrial/embr](https://github.com/mussonindustrial/embr/releases/tag/%40embr-modules%2Fperiscope%400.7.2)**
>
> Patch Changes
> 
> 
> 40ad4a7: Fix Perspective component schema validation errors on startup. #265
> 
> This patch uses DelegatedClassLoader to simultaneously resolve schema definitions from both Perspective...

---

_[View the full topic](https://forum.inductiveautomation.com/t/musson-industrials-embr-periscope-module/99047)._
