Package org.joget.plugin.base

Source Code of org.joget.plugin.base.PluginManager

package org.joget.plugin.base;

import org.joget.commons.util.LogUtil;
import org.joget.commons.util.SetupManager;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.StringMap;
import org.joget.commons.util.HostManager;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import freemarker.cache.URLTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import java.io.StringWriter;
import java.io.Writer;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringEscapeUtils;
import org.joget.commons.util.ResourceBundleUtil;
import org.joget.commons.util.StringUtil;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.LocaleResolver;

public class PluginManager implements ApplicationContextAware {

    private Felix felix = null;
    private String baseDirectory = SetupManager.getBaseSharedDirectory() + File.separator + "app_plugins";
    private ApplicationContext applicationContext;
    private Map<Class, Map<String, Plugin>> pluginCache = new HashMap<Class, Map<String, Plugin>>();
    private Set<String> blackList;
    private Set<String> scanPackageList;
   
    public final static String ESCAPE_JAVASCRIPT = "javascript";
   
    public PluginManager() {
        init();
    }

    public PluginManager(String baseDirectory) {
        if (baseDirectory != null) {
            this.baseDirectory = baseDirectory;
        }
        init();
    }

    public Set<String> getBlackList() {
        return blackList;
    }

    public void setBlackList(Set<String> blackList) {
        this.blackList = blackList;
    }

    public Set<String> getScanPackageList() {
        return scanPackageList;
    }

    public void setScanPackageList(Set<String> scanPackageList) {
        this.scanPackageList = scanPackageList;
    }

    /**
     * Retrieves plugin base directory from system setup
     */
    public String getBaseDirectory() {
        try {
            SetupManager setupManager = (SetupManager) applicationContext.getBean("setupManager");
            String dataFileBasePath = setupManager.getSettingValue("dataFileBasePath");
            if (dataFileBasePath != null && dataFileBasePath.length() > 0) {
                return dataFileBasePath + File.separator + "plugins";
            } else {
                return baseDirectory;
            }
        } catch (Exception ex) {
            return baseDirectory;
        }
    }

    /**
     * Initializes the plugin manager
     */
    protected void init() {
        Properties config = new Properties();
        try {
            config.load(getClass().getClassLoader().getResourceAsStream("config.properties"));
        } catch (IOException ex) {
            LogUtil.error(PluginManager.class.getName(), ex, "");
        }

        // workaround for log4j classloading issues
        System.setProperty("log4j.ignoreTCL", "true");
       
        // Create a case-insensitive configuration property map.
        Map configMap = new StringMap(false);
        configMap.putAll(config);
        // Configure the Felix instance to be embedded.

        // Explicitly specify the directory to use for caching bundles.
        String targetCache = "target/felix-cache/";
        String tempDir = System.getProperty("java.io.tmpdir");
        File targetDir = new File(tempDir, targetCache);
        File targetCacheDir = new File(targetDir, "cache");

        if (HostManager.isVirtualHostEnabled()) {
            // locate empty cache directory to use
            boolean proceed = false;
            int count = 0;
            while (!proceed) {
                // check for existing cache
                String dirName = "cache" + count;
                targetCacheDir = new File(targetDir, dirName);
                File[] bundles = targetCacheDir.listFiles();
                proceed = bundles == null || bundles.length <= 1;
                count++;
            }
        }
        // set configuration
        configMap.put("org.osgi.framework.storage", targetCacheDir.getAbsolutePath());
        configMap.put("felix.log.level", "0");
        configMap.put("org.osgi.framework.storage.clean", "onFirstInit");
        configMap.put("felix.cache.locking", "false");

        try {
            if (felix == null) {
                felix = new Felix(configMap);
                felix.start();
            }
            //refresh();
            LogUtil.info(PluginManager.class.getName(), "PluginManager initialized");
        } catch (Exception ex) {
            LogUtil.error(PluginManager.class.getName(), ex, "Could not create framework");
        }

    }

    /**
     * Find and install plugins from the baseDirectory
     */
    public void refresh() {
        uninstallAll(false);
        installBundles();
    }

    protected void installBundles() {

        Collection<URL> urlList = new ArrayList<URL>();
        File baseDirFile = new File(getBaseDirectory());
        recurseDirectory(urlList, baseDirFile);

        Collection<Bundle> bundleList = new ArrayList<Bundle>();
        for (URL url : urlList) {
            // install the JAR file as a bundle
            String location = url.toExternalForm();
            Bundle bundle = installBundle(location);
            if (bundle != null) {
                bundleList.add(bundle);
            }

        }

        for (Bundle bundle : bundleList) {
            startBundle(bundle);
        }


    }

    protected void recurseDirectory(Collection<URL> urlList, File baseDirFile) {
        File[] files = baseDirFile.listFiles();
        if (files != null) {
            for (File file : files) {
                //LogUtil.info(getClass().getName(), " -" + file.getName());
                if (file.isFile() && file.getName().toLowerCase().endsWith(".jar")) {
                    try {
                        urlList.add(file.toURI().toURL());
                        LogUtil.debug(PluginManager.class.getName(), " found jar " + file.toURI().toURL());
                    } catch (MalformedURLException ex) {
                        LogUtil.error(PluginManager.class.getName(), ex, "");
                    }
                } else if (file.isDirectory()) {
                    recurseDirectory(urlList, file);
                }
            }
        }
    }

    protected Bundle installBundle(String location) {
        try {
            BundleContext context = felix.getBundleContext();
            Bundle newBundle = context.installBundle(location);
            if (newBundle.getSymbolicName() == null) {
                newBundle.uninstall();
                newBundle = null;
            } else {
                newBundle.update();
            }
            // clear cache
            pluginCache.clear();
            return newBundle;
        } catch (Exception be) {
            LogUtil.error(PluginManager.class.getName(), be, "Failed bundle installation from " + location + ": " + be.toString());
            return null;
        }
    }

    protected boolean startBundle(Bundle bundle) {
        try {
            //bundle.update();
            bundle.start();
            LogUtil.info(PluginManager.class.getName(), "Bundle " + bundle.getSymbolicName() + " started");
        } catch (Exception be) {
            LogUtil.error(PluginManager.class.getName(), be, "Failed bundle start for " + bundle + ": " + be.toString());
            return true;
        }
        return false;
    }

    /**
     * List registered plugins
     * @return
     */
    public Collection<Plugin> list() {
        return list(null);
    }

    /**
     * Returns a list of plugins, both from the OSGI container and the classpath.
     * Plugins from the OSGI container will take priority if there are conflicting classes.
     * @param clazz Optional filter for type of plugins to return, null will return all.
     * @return
     */
    public Collection<Plugin> list(Class clazz) {
        // lookup in cache
        Class classFilter = (clazz != null) ? clazz : Plugin.class;
        Map<String, Plugin> pluginMap = pluginCache.get(classFilter);
        if (pluginMap == null) {
            // load plugins
            pluginMap = internalLoadPluginMap(clazz);

            // store in cache
            pluginCache.put(classFilter, pluginMap);
        }
        Collection<Plugin> pluginList = new ArrayList<Plugin>();
        pluginList.addAll(pluginMap.values());
        return pluginList;
    }

    /**
     * Returns a list of plugins from the OSGI container only.
     * Plugins from the OSGI container will take priority if there are conflicting classes.
     * @param clazz Optional filter for type of plugins to return, null will return all.
     * @return
     */
    public Collection<Plugin> listOsgiPlugin(Class clazz) {
        Map<String, Plugin> pluginMap = new TreeMap<String, Plugin>();

        // find OSGI plugins
        Collection<Plugin> pluginList = loadOsgiPlugins();
        for (Plugin plugin : pluginList) {
            if (clazz == null || clazz.isAssignableFrom(plugin.getClass())) {
                pluginMap.put(plugin.getName(), plugin);
            }
        }

        return pluginMap.values();
    }

    /**
     * Returns a map of plugins with class name as key, both from the OSGI container and the classpath.
     * Plugins from the OSGI container will take priority if there are conflicting classes.
     * @param clazz Optional filter for type of plugins to return, null will return all.
     * @return
     */
    public Map<String, Plugin> loadPluginMap(Class clazz) {
        Map<String, Plugin> pluginMap = new HashMap<String, Plugin>();
        for (Plugin plugin : list(clazz)) {
            pluginMap.put(plugin.getClass().getName(), plugin);
        }
        return pluginMap;
    }

    /**
     * Returns a list of plugins, both from the OSGI container and the classpath.
     * Plugins from the OSGI container will take priority if there are conflicting classes.
     * @param clazz Optional filter for type of plugins to return, null will return all.
     * @return A Map of name=pluginObject
     */
    protected Map<String, Plugin> internalLoadPluginMap(Class clazz) {
        Map<String, Plugin> pluginMap = new TreeMap<String, Plugin>();

        Class classFilter = (clazz != null) ? clazz : Plugin.class;

        // find plugins in classpath
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        provider.addIncludeFilter(new AssignableTypeFilter(classFilter));
        Set<BeanDefinition> components = provider.findCandidateComponents("org.joget");
        if (scanPackageList != null) {
            for (String scanPackage: scanPackageList) {
                components.addAll(provider.findCandidateComponents(scanPackage));
            }
        }
        for (BeanDefinition component : components) {
            String beanClassName = component.getBeanClassName();
            if (blackList == null || !blackList.contains(beanClassName)) {
                try {
                    Class<? extends Plugin> beanClass = Class.forName(beanClassName).asSubclass(Plugin.class);
                    Plugin plugin = beanClass.newInstance();
                    pluginMap.put(plugin.getName(), plugin);
                } catch (Exception ex) {
                    LogUtil.warn(PluginManager.class.getName(), " Error loading plugin class  " + beanClassName);
                }
            }
        }

        // find OSGI plugins
        Collection<Plugin> pluginList = loadOsgiPlugins();
        for (Plugin plugin : pluginList) {
            if (clazz == null || clazz.isAssignableFrom(plugin.getClass())) {
                if (blackList == null || !blackList.contains(plugin.getClass().getName())) {
                    pluginMap.put(plugin.getName(), plugin);
                }
            }
        }

        LogUtil.debug(PluginManager.class.getName(), " Loaded plugins from classpath and OSGI container");
        return pluginMap;
    }

    /**
     * Load all plugins from the OSGI container
     * @return
     */
    protected Collection<Plugin> loadOsgiPlugins() {
        Collection<Plugin> list = new ArrayList<Plugin>();
        BundleContext context = felix.getBundleContext();
        Bundle[] bundles = context.getBundles();
        for (Bundle b : bundles) {
            ServiceReference[] refs = b.getRegisteredServices();
            if (refs != null) {
                for (ServiceReference sr : refs) {
                    LogUtil.debug(PluginManager.class.getName(), " bundle service: " + sr);
                    Object obj = context.getService(sr);
                    if (obj instanceof Plugin) {
                        list.add((Plugin) obj);
                    }
                    context.ungetService(sr);
                }
            }
        }
        return list;
    }

    /**
     * Disable plugin
     * @param name
     */
    public boolean disable(String name) {
        boolean result = false;
        BundleContext context = felix.getBundleContext();
        ServiceReference sr = context.getServiceReference(name);
        if (sr != null) {
            try {
                sr.getBundle().stop();
                context.ungetService(sr);
                result = true;
            } catch (Exception ex) {
                LogUtil.error(PluginManager.class.getName(), ex, "");
            }
        }
        return result;
    }

    /**
     * Install a new plugin
     * @return
     */
    public boolean upload(String filename, InputStream in) {
        String location = null;
        File outputFile = null;
        try {
            // check filename
            if (filename == null || filename.trim().length() == 0) {
                throw new PluginException("Invalid plugin name");
            }
            if (!filename.endsWith(".jar")) {
                filename += ".jar";
            }

            // write file
            FileOutputStream out = null;
            try {
                outputFile = new File(getBaseDirectory(), filename);
                File outputDir = outputFile.getParentFile();
                if (!outputDir.exists()) {
                    outputDir.mkdirs();
                }
                out = new FileOutputStream(outputFile);
                BufferedInputStream bin = new BufferedInputStream(in);
                int len = 0;
                byte[] buffer = new byte[4096];
                while ((len = bin.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                }
                out.flush();
                location = outputFile.toURI().toURL().toExternalForm();
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    LogUtil.error(PluginManager.class.getName(), ex, "");
                }
            }

            // validate jar file
            boolean isValid = false;
            try {
                JarFile jarFile = new JarFile(outputFile);
                isValid = true;
            } catch (IOException ex) {
                //delete invalid file
                try {
                    outputFile.delete();
                } catch (Exception e) {
                    LogUtil.error(PluginManager.class.getName(), ex, "");
                }

                LogUtil.error(PluginManager.class.getName(), ex, "");
                throw new PluginException("Invalid jar file");
            } catch (Exception ex) {
                LogUtil.error(PluginManager.class.getName(), ex, "");
                throw new PluginException("Invalid jar file");
            }

            // install
            if (location != null && isValid) {
                Bundle newBundle = installBundle(location);
                if (newBundle != null) {
                    startBundle(newBundle);
                }
            }

            return true;
        } catch (Exception ex) {
            LogUtil.error(PluginManager.class.getName(), ex, "");
            throw new PluginException("Unable to write plugin file", ex);
        }
    }

    /**
     * Uninstall/remove all plugin, without deleting the plugin file
     * @param name
     * @return
     */
    public void uninstallAll(boolean deleteFiles) {
        Collection<Plugin> pluginList = this.list();
        for (Plugin plugin : pluginList) {
            uninstall(plugin.getClass().getName(), deleteFiles);
        }
    }

    /**
     * Uninstall/remove a plugin, and delete the plugin file
     * @param name
     * @return
     */
    public boolean uninstall(String name) {
        return uninstall(name, true);
    }

    /**
     * Uninstall/remove a plugin
     * @param name
     * @return
     */
    public boolean uninstall(String name, boolean deleteFile) {
        boolean result = false;
        BundleContext context = felix.getBundleContext();
        ServiceReference sr = context.getServiceReference(name);
        if (sr != null) {
            try {
                Bundle bundle = sr.getBundle();
                bundle.stop();
                bundle.uninstall();
                String location = bundle.getLocation();
                context.ungetService(sr);

                // delete location
                if (deleteFile) {
                    File file = new File(new URI(location));
                    boolean deleted = file.delete();
                }
                result = true;

                // clear cache
                pluginCache.clear();
            } catch (Exception ex) {
                LogUtil.error(PluginManager.class.getName(), ex, "");
            }
        }
        return result;
    }

    /**
     * Returns a plugin, from either the OSGI container and the classpath.
     * Plugins from the OSGI container will take priority if there are conflicting classes.
     * @param name Class name of the required plugin
     * @return
     */
    public Plugin getPlugin(String name) {
        if (blackList != null && blackList.contains(name)) {
            return null;
        }
       
        if (name != null && name.trim().length() > 0 && !"null".equalsIgnoreCase(name)) {
            Plugin plugin = loadOsgiPlugin(name);
            if (plugin == null) {
                plugin = loadClassPathPlugin(name);
            }
            return plugin;
        } else {
            return null;
        }
    }

    /**
     * Retrieve a plugin from the OSGI container
     * @param name Fully qualified class name for the required plugin
     * @return
     */
    protected Plugin loadOsgiPlugin(String name) {
        Plugin plugin = null;
        try {
            BundleContext context = felix.getBundleContext();

            ServiceReference sr = context.getServiceReference(name);
            if (sr != null) {
                Class clazz = sr.getBundle().loadClass(name);
                Object obj = clazz.newInstance();
                boolean isPlugin = obj instanceof Plugin;
                LogUtil.debug(PluginManager.class.getName(), " plugin obj " + obj + " class: " + obj.getClass().getName() + " " + isPlugin);
                LogUtil.debug(PluginManager.class.getName(), " plugin classloader: " + obj.getClass().getClassLoader());
                LogUtil.debug(PluginManager.class.getName(), " current classloader: " + Plugin.class.getClassLoader());
                if (isPlugin) {
                    plugin = (Plugin) obj;
                }
                context.ungetService(sr);
            }
        } catch (Exception ex) {
            LogUtil.error(PluginManager.class.getName(), ex, "");
            throw new PluginException("Plugin " + name + " could not be retrieved", ex);
        }
        return plugin;
    }

    /**
     * Retrieve a plugin using the system classloader
     * @param name Fully qualified class name for the required plugin
     * @return
     */
    protected Plugin loadClassPathPlugin(String name) {
        Plugin plugin = null;
        try {
            Class clazz = Class.forName(name);
            Object obj = clazz.newInstance();
            plugin = (Plugin) obj;
        } catch (Exception ex) {
            LogUtil.debug(PluginManager.class.getName(), "plugin " + name + " not found in classpath");
        }
        return plugin;
    }

    /**
     * Retrieves an InputStream to a resource from a plugin. The plugin may either be from OSGI container or system classpath.
     * @param pluginName
     * @param resourceUrl
     * @return
     * @throws IOException
     */
    public InputStream getPluginResource(String pluginName, String resourceUrl) throws IOException {
        InputStream result = null;

        URL url = getPluginResourceURL(pluginName, resourceUrl);
        if (url != null) {
            // get inputstream from url
            if (url != null) {
                result = url.openConnection().getInputStream();
            }
        }

        return result;
    }

    /**
     * Reads a resource from a plugin. java.util.Formatter text patterns supported.
     * @param pluginName
     * @param resourceUrl
     * @param arguments
     * @param removeNewLines
     * @param translationPath
     * @return null if the resource is not found or in the case of an exception
     * @see java.util.Formatter
     */
    public String readPluginResourceAsString(String pluginName, String resourceUrl, Object[] arguments, boolean removeNewLines, String translationPath) {
        String output = null;
        InputStream input = null;
        ByteArrayOutputStream stream = null;
        if (pluginName != null && resourceUrl != null) {
            try {
                input = getPluginResource(pluginName, resourceUrl);
                if (input != null) {
                    // write output
                    stream = new ByteArrayOutputStream();
                    byte[] bbuf = new byte[65536];
                    int length = 0;
                    while ((input != null) && ((length = input.read(bbuf)) != -1)) {
                        stream.write(bbuf, 0, length);
                    }
                }
            } catch (Exception e) {
                LogUtil.error(PluginManager.class.getName(), e, "Error reading resource ");
            } finally {
                try {
                    if (stream != null) {
                        stream.flush();
                        stream.close();
                    }
                    if (input != null) {
                        input.close();
                    }
                } catch (Exception e) {
                    LogUtil.error(PluginManager.class.getName(), e, "Error closing IO");
                }
            }
           
            // set and return output
            if (stream != null) {
                output = new String(stream.toByteArray());

                if (arguments != null && arguments.length > 0) {
                    // format arguments
                    output = String.format(output, arguments);
                }

                if (removeNewLines) {
                    // compress by removing new lines
                    output = output.replace('\n', ' ');
                    output = output.replace('\r', ' ');
                    output = output.trim();
                }
            }

            String escapeType = null;

            if (resourceUrl.endsWith(".json") || resourceUrl.endsWith(".js")) {
                escapeType = ESCAPE_JAVASCRIPT;
            }

            output = processPluginTranslation(output, pluginName, translationPath, escapeType);
        }

        return output;
    }

    /**
     * Reads a message bundle from a plugin.
     * @param pluginName
     * @param translationPath
     * @return null if the resource bundle is not found or in the case of an exception
     */
    public ResourceBundle getPluginMessageBundle(String pluginName, String translationPath) {
        ResourceBundle bundle = null;

        // get plugin
        Plugin plugin = getPlugin(pluginName);
        if (plugin != null) {
           
            LocaleResolver localeResolver = (LocaleResolver) getBean("localeResolver")
            Locale locale = localeResolver.resolveLocale(getHttpServletRequest());

            try {
                bundle = ResourceBundle.getBundle(translationPath, locale, plugin.getClass().getClassLoader());
            } catch (Exception e) {
                LogUtil.debug(PluginManager.class.getName(), translationPath + " translation file not found");
            }
        }
        return bundle;
    }
   
    public String processPluginTranslation(String content, String pluginName, String translationPath) {
        return processPluginTranslation(content, pluginName, translationPath, null);
    }

    public String processPluginTranslation(String content, String pluginName, String translationPath, String escapeType) {
        if (!(content != null && content.indexOf("@@") >= 0)) {
            return content;
        }

        Pattern pattern = Pattern.compile("\\@@([^@@^\"^ ])*\\.([^@@^\"])*\\@@");
        Matcher matcher = pattern.matcher(content);

        List<String> keyList = new ArrayList<String>();
        while (matcher.find()) {
            keyList.add(matcher.group());
        }

        if (!keyList.isEmpty()) {
            ResourceBundle bundle = null;
           
            if (translationPath != null && !translationPath.isEmpty()) {
                bundle = getPluginMessageBundle(pluginName, translationPath);
            }

            for (String key : keyList) {
                String tempKey = key.replaceAll("@@", "");
                String label = null;

                if (bundle != null && bundle.containsKey(tempKey)) {
                    label = bundle.getString(tempKey);
                } else if (ResourceBundleUtil.getMessage(tempKey) != null) {
                    label = ResourceBundleUtil.getMessage(tempKey);
                }

                if (label != null) {
                    if (ESCAPE_JAVASCRIPT.equals(escapeType)) {
                        label = StringEscapeUtils.escapeJavaScript(label);
                    }
                    content = content.replaceAll(StringUtil.escapeRegex(key), StringUtil.escapeRegex(label));
                }
            }
        }

        return content;
    }

    public String getMessage(String key, String pluginName, String translationPath) {
        return processPluginTranslation("@@" + key + "@@", pluginName, translationPath);
    }

    public String getPluginFreeMarkerTemplate(Map data, final String pluginName, final String templatePath, String translationPath) {
        String result = "";
        try {
            // init configuration
            Configuration configuration = new Configuration();

            configuration.setObjectWrapper(new DefaultObjectWrapper() {
                // override object wrapper for Maps to support list-ordered maps

                @Override
                public TemplateModel wrap(Object obj) throws TemplateModelException {
                    if (obj instanceof Map) {
                        return new ListOrderedHash((Map) obj, this);
                    } else {
                        return super.wrap(obj);
                    }
                }
            });

            // set template loader
            configuration.setTemplateLoader(new URLTemplateLoader() {

                @Override
                protected URL getURL(String string) {
                    URL url = getPluginResourceURL(pluginName, templatePath);
                    return url;
                }

                @Override
                public long getLastModified(Object templateSource) {
                    return 0;
                }
               
            });

            // Get or create a template
            Template temp = configuration.getTemplate(templatePath);

            // Merge data-model with template
            Writer out = new StringWriter();
            temp.process(data, out);
            out.flush();
            result = out.toString();
           
            result = processPluginTranslation(result, pluginName, translationPath);

        } catch (Exception ex) {
            LogUtil.error(PluginManager.class.getName(), ex, "");
            result = ex.toString();
        }
        return result;
    }

    /**
     * Retrieves a URL to a resource from a plugin. The plugin may either be from OSGI container or system classpath.
     * @param pluginName
     * @param pluginName
     * @param resourceUrl
     * @return
     */
    public URL getPluginResourceURL(String pluginName, String resourceUrl) {
        URL url = null;

        // get plugin
        Plugin plugin = getPlugin(pluginName);

        if (plugin != null) {
            // get class loader for plugin
            ClassLoader loader = plugin.getClass().getClassLoader();

            // get resource url, remove first /
            if (resourceUrl.startsWith("/")) {
                resourceUrl = resourceUrl.substring(1);
            }
            url = loader.getResource(resourceUrl);
        }

        return url;
    }

    /**
     * Execute a plugin
     * @param name The fully qualified class name of the plugin
     * @param properties
     * @return
     */
    public Object execute(String name, Map properties) {
        Object result = null;
        Plugin plugin = getPlugin(name);
        if (plugin != null) {
            result = plugin.execute(properties);
            LogUtil.info(PluginManager.class.getName(), " Executed plugin " + plugin + ": " + result);
        } else {
            LogUtil.info(PluginManager.class.getName(), " Plugin " + name + " not found");
        }
        return result;
    }

    /**
     * Stop the plugin manager
     */
    public synchronized void shutdown() {
        if (felix != null) {
            try {
                uninstallAll(false);
                felix.stop();
            } catch (Exception ex) {
                LogUtil.error(PluginManager.class.getName(), ex, "Could not stop Felix");
            }
            felix = null;
        }
    }

    @Override
    public void finalize() {
        shutdown();
    }

    public Object testPlugin(String name, String location, Map properties, boolean override) {
        LogUtil.info(PluginManager.class.getName(), "====testPlugin====");
        // check for existing plugin
        Plugin plugin = getPlugin(name);
        boolean existing = (plugin != null);
        boolean install = (location != null && location.trim().length() > 0);

        // install plugin
        if (install && (!existing || override)) {
            InputStream in = null;
            try {
                LogUtil.info(PluginManager.class.getName(), " ===install=== ");
                File file = new File(location);
                if (file.exists()) {
                    in = new FileInputStream(file);
                    upload(file.getName(), in);
                }
            } catch (Exception ex) {
                LogUtil.error(PluginManager.class.getName(), ex, "");
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    LogUtil.error(PluginManager.class.getName(), ex, "");
                }
            }
        }

        // execute plugin
        LogUtil.info(PluginManager.class.getName(), " ===execute=== ");
        Object result = execute(name, properties);
        LogUtil.info(PluginManager.class.getName(), "  result: " + result);

        // uninstall plugin
        if (install && (!existing || override)) {
            LogUtil.info(PluginManager.class.getName(), " ===uninstall=== ");
            uninstall(name);
        }
        LogUtil.info(PluginManager.class.getName(), "====testPlugin end====");

        return result;
    }

    public static void main(String[] args) {
//        String pluginDirectory = "target/wflow-bundles";
        PluginManager pm = new PluginManager();

        FileInputStream in = null;
        try {
            LogUtil.info(PluginManager.class.getName(), " ===Plugin List=== ");
            for (Plugin p : pm.list()) {
                LogUtil.info(PluginManager.class.getName(), " plugin: " + p.getName() + "; " + p.getClass().getName());
            }
            String samplePluginFile = "../wflow-plugins/wflow-plugin-sample/target/wflow-plugin-sample.jar";
            String samplePlugin = "org.joget.plugin.sample.SamplePlugin";

            try {
                LogUtil.info(PluginManager.class.getName(), " ===Install SamplePlugin=== ");
                File file = new File(samplePluginFile);
                in = new FileInputStream(file);
                pm.upload(file.getName(), in);
            } catch (Exception ex) {
                LogUtil.error(PluginManager.class.getName(), ex, "");
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (IOException ex) {
                    LogUtil.error(PluginManager.class.getName(), ex, "");
                }
            }

            LogUtil.info(PluginManager.class.getName(), " ===Plugin List after install=== ");
            for (Plugin p : pm.list()) {
                LogUtil.info(PluginManager.class.getName(), " plugin: " + p.getName() + "; " + p.getClass().getName());
            }

            LogUtil.info(PluginManager.class.getName(), " ===Execute SamplePlugin=== ");
            pm.execute(samplePlugin, null);

            LogUtil.info(PluginManager.class.getName(), " ===Uninstall SamplePlugin=== ");
            pm.uninstall(samplePlugin);
            LogUtil.info(PluginManager.class.getName(), " ===New Plugin List after removal=== ");
            for (Plugin p : pm.list()) {
                LogUtil.info(PluginManager.class.getName(), " plugin: " + p.getName() + "; " + p.getClass().getName());
            }
            pm.refresh();
            LogUtil.info(PluginManager.class.getName(), " ===New Plugin List after refresh=== ");
            for (Plugin p : pm.list()) {
                LogUtil.info(PluginManager.class.getName(), " plugin: " + p.getName() + "; " + p.getClass().getName());
            }

            pm.testPlugin(samplePlugin, samplePluginFile, null, true);
        } finally {
            pm.shutdown();
        }
    }
   
    public HttpServletRequest getHttpServletRequest() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            return request;
        } catch (NoClassDefFoundError e) {
            // ignore if servlet request class is not available
            return null;
        } catch (IllegalStateException e) {
            // ignore if servlet request is not available, e.g. when triggered from a deadline
            return null;
        }
    }

    public Object getBean(String beanName) {
        Object bean = null;
        if (applicationContext != null) {
            bean = applicationContext.getBean(beanName);
        }
        return bean;
    }

    public void setApplicationContext(ApplicationContext appContext) throws BeansException {
        this.applicationContext = appContext;
        refresh();
    }
}
TOP

Related Classes of org.joget.plugin.base.PluginManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.