public void patch(final ClassLoader classLoaderFrom, final String pluginPath,
                      final ClassLoader classLoaderTo, final ProtectionDomain protectionDomain) {

        List<byte[]> cache = getPluginCache(classLoaderFrom, pluginPath);

        if (cache != null) {

            final ClassPool cp = new ClassPool();
            cp.appendClassPath(new LoaderClassPath(getClass().getClassLoader()));
            Set<String> loadedClasses = new HashSet<>();
            String packagePrefix = pluginPath.replace('/', '.');

            for (byte[] pluginBytes: cache) {
                CtClass pluginClass = null;
                try {
                    // force to load class in classLoaderFrom (it may not yet be loaded) and if the classLoaderTo
                    // is parent of classLoaderFrom, after definition in classLoaderTo will classLoaderFrom return
                    // class from parent classloader instead own definition (hence change of behaviour).
                    InputStream is = new ByteArrayInputStream(pluginBytes);
                    pluginClass = cp.makeClass(is);
                    try {
                        classLoaderFrom.loadClass(pluginClass.getName());
                    } catch (NoClassDefFoundError e) {
                        LOGGER.trace("Skipping class loading {} in classloader {} - " +
                                "class has probably unresolvable dependency.", pluginClass.getName(), classLoaderTo);
                    }
                    // and load the class in classLoaderTo as well. Now the class is defined in BOTH classloaders.
                    transferTo(pluginClass, packagePrefix, classLoaderTo, protectionDomain, loadedClasses);
                } catch (CannotCompileException e) {
                    LOGGER.trace("Skipping class definition {} in app classloader {} - " +
                            "class is probably already defined.", pluginClass.getName(), classLoaderTo);
                } catch (NoClassDefFoundError e) {
                    LOGGER.trace("Skipping class definition {} in app classloader {} - " +
                            "class has probably unresolvable dependency.", pluginClass.getName(), classLoaderTo);
                } catch (Throwable e) {
                    LOGGER.trace("Skipping class definition app classloader {} - " +
                            "unknown error.", e, classLoaderTo);
                }
            }
        }

        LOGGER.debug("Classloader {} patched with plugin classes from agent classloader {}.", classLoaderTo, classLoaderFrom);

    }
@Test
    public void testPatch() throws Exception {
        ClassLoader appClassLoader = new URLClassLoader(new URL[]{}, getClass().getClassLoader());

        assertEquals("Class created in parent classloader", getClass().getClassLoader(),
                appClassLoader.loadClass(AnonymousClassInfo.class.getName()).getClassLoader());

        new ClassLoaderDefineClassPatcher().patch(getClass().getClassLoader(), PluginManager.PLUGIN_PACKAGE.replace(".", "/"),
                appClassLoader, null);

        assertEquals("Class created in app classloader", appClassLoader,
                appClassLoader.loadClass(AnonymousClassInfo.class.getName()).getClassLoader());
        ;
    }