class PluginManagerInvoker {

    /**
     * Initialize plugin for a classloader.
     *
     * @param pluginClass    identify plugin instance
     * @param appClassLoader classloader in which the plugin should reside
     */
    public static <T> T callInitializePlugin(Class<T> pluginClass, ClassLoader appClassLoader) {
        // noinspection unchecked
        return (T) PluginManager.getInstance().getPluginRegistry().initializePlugin(
                pluginClass.getName(), appClassLoader
        );
    }


    public static String buildInitializePlugin(Class pluginClass) {
        return buildInitializePlugin(pluginClass, "getClass().getClassLoader()");
    }

    public static String buildInitializePlugin(Class pluginClass, String classLoaderVar) {
        return "org.hotswap.agent.config.PluginManager.getInstance().getPluginRegistry().initializePlugin(" +
                "\"" + pluginClass.getName() + "\", " + classLoaderVar +
                ");";
    }


    /**
     * Free all classloader references and close any associated plugin instance.
     * Typical use is after webapp undeploy.
     *
     * @param appClassLoader clasloade to free
     */
    public static void callCloseClassLoader(ClassLoader appClassLoader) {
        PluginManager.getInstance().closeClassLoader(appClassLoader);
    }

    public static String buildCallCloseClassLoader(String classLoaderVar) {
        return "org.hotswap.agent.config.PluginManager.getInstance().closeClassLoader(" + classLoaderVar + ");";
    }

    /**
     * Methods on plugin should be called via reflection, because the real plugin object is in parent classloader,
     * but plugin class may be defined in app classloader as well introducing ClassCastException on same class name.
     *
     * @param pluginClass    class name of the plugin - it is used to resolve plugin instance from plugin manager
     * @param appClassLoader application classloader (to resolve plugin instance)
     * @param method         method name
     * @param paramTypes     param types (as required by reflection)
     * @param params         actual param values
     * @return method return value
     */
    public static Object callPluginMethod(Class pluginClass, ClassLoader appClassLoader, String method, Class[] paramTypes, Object[] params) {
        Object pluginInstance = PluginManager.getInstance().getPlugin(pluginClass.getName(), appClassLoader);

        try {
            Method m = pluginInstance.getClass().getDeclaredMethod(method, paramTypes);
            return m.invoke(pluginInstance, params);
        } catch (Exception e) {
            throw new Error(String.format("Exception calling method %s on plugin class %s", method, pluginClass), e);
        }
    }

    /**
     * Equivalent to callPluginMethod for insertion into source code.
     * <p/>
     * PluginManagerInvoker.buildCallPluginMethod(this, "hibernateInitialized",
     * "getClass().getClassLoader()", "java.lang.ClassLoader")
     *
     * @param pluginClass       plugin to use
     * @param method            method name
     * @param paramValueAndType for each param its value AND type must be provided
     * @return method source code
     */
    public static String buildCallPluginMethod(Class pluginClass, String method, String... paramValueAndType) {
        return buildCallPluginMethod("getClass().getClassLoader()", pluginClass, method, paramValueAndType);
    }

    /**
     * Same as {@link PluginManagerInvoker#buildCallPluginMethod(Class, String, String...)}, but with explicit
     * appClassLoader variable. Use this method if appClassLoader is different from getClass().getClassLoader().
     */
    public static String buildCallPluginMethod(String appClassLoaderVar, Class pluginClass,
                                               String method, String... paramValueAndType) {<FILL_FUNCTION_BODY>}
}


        String managerClass = PluginManager.class.getName();
        int paramCount = paramValueAndType.length / 2;

        StringBuilder b = new StringBuilder();

        // block to hide variables and catch checked exceptions
        b.append("try {");

        b.append("ClassLoader __pluginClassLoader = ");
        b.append(managerClass);
        b.append(".class.getClassLoader();");

        // Object __pluginInstance = org.hotswap.agent.config.PluginManager.getInstance().getPlugin(org.hotswap.agent.plugin.TestPlugin.class.getName(), __pluginClassLoader);
        b.append("Object __pluginInstance = ");
        b.append(managerClass);
        b.append(".getInstance().getPlugin(");
        b.append(pluginClass.getName());
        b.append(".class.getName(), " + appClassLoaderVar + ");");

        // Class __pluginClass = __pluginClassLoader.loadClass("org.hotswap.agent.plugin.TestPlugin");
        b.append("Class __pluginClass = ");
        b.append("__pluginClassLoader.loadClass(\"");
        b.append(pluginClass.getName());
        b.append("\");");

        // param types
        b.append("Class[] paramTypes = new Class[" + paramCount + "];");
        for (int i = 0; i < paramCount; i++) {
            // paramTypes[i] = = __pluginClassLoader.loadClass("my.test.TestClass").getClass();
            b.append("paramTypes[" + i + "] = __pluginClassLoader.loadClass(\"" + paramValueAndType[(i * 2) + 1] + "\");");
        }

        //   java.lang.reflect.Method __pluginMethod = __pluginClass.getDeclaredMethod("method", paramType1, paramType2);
        b.append("java.lang.reflect.Method __callPlugin = __pluginClass.getDeclaredMethod(\"");
        b.append(method);
        b.append("\", paramTypes");
        b.append(");");

        b.append("Object[] params = new Object[" + paramCount + "];");
        for (int i = 0; i < paramCount; i = i + 1) {
            b.append("params[" + i + "] = " + paramValueAndType[i * 2] + ";");
        }

        // __pluginMethod.invoke(__pluginInstance, param1, param2);
        b.append("__callPlugin.invoke(__pluginInstance, params);");

        // catch (Exception e) {throw new Error(e);}
        b.append("} catch (Exception e) {throw new Error(e);}");

        return b.toString();
    
<no_super_class>