Package play.plugins

Source Code of play.plugins.PluginCollection

package play.plugins;

import play.Logger;
import play.Play;
import play.PlayPlugin;
import play.classloading.ApplicationClasses;
import play.classloading.ApplicationClassloader;
import play.db.Model;
import play.exceptions.UnexpectedException;
import play.mvc.Http;
import play.mvc.Router;
import play.mvc.results.Result;
import play.templates.BaseTemplate;
import play.templates.Template;
import play.test.BaseTest;
import play.test.TestEngine;
import play.vfs.VirtualFile;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.*;

/**
* Class handling all plugins used by Play.
*
* Loading/reloading/enabling/disabling is handled here.
*
* This class also exposes many PlayPlugin-methods which
* when called, the method is executed on all enabled plugins.
*
* Since all the enabled-plugins-iteration is done here,
* the code elsewhere is cleaner.
*/
public class PluginCollection {

    /**
     * Property holding the name of the play.plugins-resource-name.
     * Can be modified in unittest to supply modifies plugin-list
     */
    protected String play_plugins_resourceName = "play.plugins";

    protected Object lock = new Object();
    /**
     * List that holds all loaded plugins, enabled or disabled
     */
    protected List<PlayPlugin> allPlugins = new ArrayList<PlayPlugin>();

    /**
     * Readonly copy of allPlugins - updated each time allPlugins is updated.
     * Using this cached copy so we don't have to create it all the time..
     */
    protected List<PlayPlugin> allPlugins_readOnlyCopy = createReadonlyCopy(allPlugins);

    /**
     * List of all enabled plugins
     */
    protected List<PlayPlugin> enabledPlugins = new ArrayList<PlayPlugin>();

    /**
     * Readonly copy of enabledPlugins - updated each time enabledPlugins is updated.
     * Using this cached copy so we don't have to create it all the time
     */
    protected List<PlayPlugin> enabledPlugins_readOnlyCopy = createReadonlyCopy(enabledPlugins);


    /**
     * Using readonly list to crash if someone tries to modify the copy.
     * @param list
     * @return
     */
    protected List<PlayPlugin> createReadonlyCopy( List<PlayPlugin> list ){
        return Collections.unmodifiableList( new ArrayList<PlayPlugin>( list ));
    }

    /**
     * Enable found plugins
     */
    public void loadPlugins() {
        Logger.trace("Loading plugins");
        // Play! plugings
        Enumeration<URL> urls = null;
        try {
            urls = Play.classloader.getResources( play_plugins_resourceName);
        } catch (Exception e) {
            Logger.error("Error loading play.plugins", e);
        }
        while (urls != null && urls.hasMoreElements()) {
            URL url = urls.nextElement();
            Logger.trace("Found one plugins descriptor, %s", url);
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                String line = null;
                while ((line = reader.readLine()) != null) {
                    String[] infos = line.split(":");
                    PlayPlugin plugin = (PlayPlugin) Play.classloader.loadClass(infos[1].trim()).newInstance();

                    plugin.index = Integer.parseInt(infos[0]);
                    if( addPlugin(plugin) ){
                        Logger.trace("Loaded plugin %s", plugin);
                    }else{
                        Logger.warn("Did not load plugin %s. Already loaded", plugin);
                    }
                }
            } catch (Exception ex) {
                Logger.error(ex, "Cannot load %s", url);
            }
        }

        //now we must call onLoad for all plugins - and we must detect if a plugin
        //disables another plugin the old way, by removing it from Play.plugins.
        for( PlayPlugin plugin : getEnabledPlugins()){

            //is this plugin still enabled?
            if( isEnabled(plugin)){
                initializePlugin(plugin);
            }
        }

        //must update Play.plugins-list one last time
        updatePlayPluginsList();

    }

    /**
     * Reloads all loaded plugins that is application-supplied.
     */
    public void reloadApplicationPlugins() throws Exception{
        Set<PlayPlugin> reloadedPlugins = new HashSet<PlayPlugin>();

        for (PlayPlugin plugin : getAllPlugins()) {

            //Is this plugin an application-supplied-plugin?
            if (isLoadedByApplicationClassloader(plugin)) {
                //This plugin is application-supplied - Must reload it
                String pluginClassName = plugin.getClass().getName();
                Class pluginClazz = Play.classloader.loadClass( pluginClassName);

                //first looking for constructors the old way
                Constructor<?>[] constructors = pluginClazz.getConstructors();

                if( constructors.length == 0){
                    //no constructors in plugin
                    //using getDeclaredConstructors() instead of getConstructors() to make it work for plugins without constructor
                    constructors = pluginClazz.getDeclaredConstructors();
                }

                PlayPlugin newPlugin = (PlayPlugin) constructors[0].newInstance();
                newPlugin.index = plugin.index;
                //replace this plugin
                replacePlugin(plugin, newPlugin);
                reloadedPlugins.add(newPlugin);
            }
        }

        //now we must call onLoad for all reloaded plugins
        for( PlayPlugin plugin : reloadedPlugins ){
            initializePlugin( plugin );
        }

        updatePlayPluginsList();

    }

    protected boolean isLoadedByApplicationClassloader(PlayPlugin plugin) {
        return plugin.getClass().getClassLoader().getClass().equals(ApplicationClassloader.class);
    }


    /**
     * Calls plugin.onLoad but detects if plugin removes other plugins from Play.plugins-list to detect
     * if plugins disables a plugin the old hacked way..
     * @param plugin
     */
    @SuppressWarnings({"deprecation"})
    protected void initializePlugin(PlayPlugin plugin) {
        Logger.trace("Initializing plugin " + plugin);
        //we're ready to call onLoad for this plugin.
        //must create a unique Play.plugins-list for this onLoad-method-call so
        //we can detect if some plugins are removed/disabled
        Play.plugins = new ArrayList<PlayPlugin>( getEnabledPlugins() );
        plugin.onLoad();
        //check for missing/removed plugins
        for( PlayPlugin enabledPlugin : getEnabledPlugins()){
            if( !Play.plugins.contains( enabledPlugin)) {
                Logger.info("Detected that plugin '" + plugin + "' disabled the plugin '" + enabledPlugin + "' the old way - should use Play.disablePlugin()");
                //this enabled plugin was disabled.
                //must disable it in pluginCollection
                disablePlugin( enabledPlugin);
            }
        }
    }


    /**
     * Adds one plugin and enables it
     * @param plugin
     * @return true if plugin was new and was added
     */
    protected boolean addPlugin( PlayPlugin plugin ){
        synchronized( lock ){
            if( !allPlugins.contains(plugin) ){
                allPlugins.add( plugin );
                Collections.sort(allPlugins);
                allPlugins_readOnlyCopy = createReadonlyCopy( allPlugins);
                enablePlugin(plugin);
                return true;
            }
        }
        return false;
    }

    protected void replacePlugin( PlayPlugin oldPlugin, PlayPlugin newPlugin){
        synchronized( lock ){
            if( allPlugins.remove( oldPlugin )){
                allPlugins.add( newPlugin);
                Collections.sort( allPlugins);
                allPlugins_readOnlyCopy = createReadonlyCopy( allPlugins);
            }

            if( enabledPlugins.remove( oldPlugin )){
                enabledPlugins.add(newPlugin);
                Collections.sort( enabledPlugins);
                enabledPlugins_readOnlyCopy = createReadonlyCopy( enabledPlugins);
            }

        }
    }

    /**
     * Enable plugin.
     *
     * @param plugin
     * @return true if plugin exists and was enabled now
     */
    public boolean enablePlugin( PlayPlugin plugin ){
        synchronized( lock ){
            if( allPlugins.contains( plugin )){
                //the plugin exists
                if( !enabledPlugins.contains( plugin )){
                    //plugin not currently enabled
                    enabledPlugins.add( plugin );
                    Collections.sort( enabledPlugins);
                    enabledPlugins_readOnlyCopy = createReadonlyCopy( enabledPlugins);
                    updatePlayPluginsList();
                    Logger.trace("Plugin " + plugin + " enabled");
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * enable plugin of specified type
     * @return true if plugin was enabled
     */
    public boolean enablePlugin( Class<? extends PlayPlugin> pluginClazz ){
        return enablePlugin(getPluginInstance(pluginClazz));
    }


    /**
     * Returns the first instance of a loaded plugin of specified type
     * @param pluginClazz
     * @return
     */
    public PlayPlugin getPluginInstance( Class<? extends PlayPlugin> pluginClazz){
        synchronized( lock ){
            for( PlayPlugin p : getAllPlugins()){
                if (pluginClazz.isInstance(p)) {
                    return p;
                }
            }
        }
        return null;
    }


    /**
     * disable plugin
     * @param plugin
     * @return true if plugin was enabled and now is disabled
     */
    public boolean disablePlugin( PlayPlugin plugin ){
        synchronized( lock ){
            //try to disable it?
            if( enabledPlugins.remove( plugin ) ){
                //plugin was removed
                enabledPlugins_readOnlyCopy = createReadonlyCopy( enabledPlugins);
                updatePlayPluginsList();
                Logger.trace("Plugin " + plugin + " disabled");
                return true;
            }
        }
        return false;
    }

    /**
     * disable plugin of specified type
     * @return true if plugin was enabled and now is disabled
     */
    public boolean disablePlugin( Class<? extends PlayPlugin> pluginClazz ){
        return disablePlugin( getPluginInstance( pluginClazz));
    }



    /**
     * Must update Play.plugins-list to be backward compatible
     */
    @SuppressWarnings({"deprecation"})
    public void updatePlayPluginsList(){
        Play.plugins = Collections.unmodifiableList( getEnabledPlugins() );
    }

    /**
     * Returns new readonly list of all enabled plugins
     * @return
     */
    public List<PlayPlugin> getEnabledPlugins(){
        return enabledPlugins_readOnlyCopy;
    }

    /**
     * Returns new readonly list of all plugins
     * @return
     */
    public List<PlayPlugin> getAllPlugins(){
        return allPlugins_readOnlyCopy;
    }


    /**
     *
     * @param plugin
     * @return true if plugin is enabled
     */
    public boolean isEnabled( PlayPlugin plugin){
        return getEnabledPlugins().contains( plugin );
    }

    public boolean compileSources() {
        for( PlayPlugin plugin : getEnabledPlugins() ){
            if(plugin.compileSources()) {
                return true;
            }
        }
        return false;
    }

    public boolean detectClassesChange() {
        for(PlayPlugin plugin : getEnabledPlugins()){
            if(plugin.detectClassesChange()) {
                return true;
            }
        }
        return false;
    }

    public void invocationFinally(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.invocationFinally();
        }
    }

    public void beforeInvocation(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.beforeInvocation();
        }
    }

    public void afterInvocation(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.afterInvocation();
        }
    }

    public void onInvocationSuccess(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.onInvocationSuccess();
        }
    }

    public void onInvocationException(Throwable e) {
        for (PlayPlugin plugin : getEnabledPlugins()) {
            try {
                plugin.onInvocationException(e);
            } catch (Throwable ex) {
                //nop
            }
        }
    }

    public void beforeDetectingChanges(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.beforeDetectingChanges();
        }
    }

    public void detectChange(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.detectChange();
        }
    }

    public void onApplicationReady(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.onApplicationReady();
        }
    }

    public void onConfigurationRead(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.onConfigurationRead();
        }
    }

    public void onApplicationStart(){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.onApplicationStart();
        }
    }

    public void afterApplicationStart(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.afterApplicationStart();
        }
    }

    public void onApplicationStop(){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.onApplicationStop();
        }
    }

    public void onEvent(String message, Object context){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.onEvent(message, context);
        }
    }

    public void enhance(ApplicationClasses.ApplicationClass applicationClass){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            try {
                long start = System.currentTimeMillis();
                plugin.enhance(applicationClass);
                Logger.trace("%sms to apply %s to %s", System.currentTimeMillis() - start, plugin, applicationClass.name);
            } catch (Exception e) {
                throw new UnexpectedException("While applying " + plugin + " on " + applicationClass.name, e);
            }
        }
    }

    @Deprecated
    public List<ApplicationClasses.ApplicationClass> onClassesChange(List<ApplicationClasses.ApplicationClass> modified){
        List<ApplicationClasses.ApplicationClass> modifiedWithDependencies = new ArrayList<ApplicationClasses.ApplicationClass>();
        for( PlayPlugin plugin : getEnabledPlugins() ){
            modifiedWithDependencies.addAll( plugin.onClassesChange(modified) );
        }
        return modifiedWithDependencies;
    }

    @Deprecated
    public void compileAll(List<ApplicationClasses.ApplicationClass> classes){
        for( PlayPlugin plugin : getEnabledPlugins() ){
            plugin.compileAll(classes);
        }
    }

    public Object bind(String name, Object o, Map<String, String[]> params){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            Object result = plugin.bind(name, o, params);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    public Object bind(String name, Class clazz, Type type, Annotation[] annotations, Map<String, String[]> params){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            Object result = plugin.bind(name, clazz, type, annotations, params);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    public Map<String, Object> unBind(Object src, String name){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            Map<String, Object> r = plugin.unBind(src, name);
            if (r != null) {
                return r;
            }
        }
        return null;
    }

    public Object willBeValidated(Object value){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            Object newValue = plugin.willBeValidated(value);
            if (newValue != null) {
                return newValue;
            }
        }
        return value;
    }

    public Model.Factory modelFactory(Class<? extends Model> modelClass){
        for(PlayPlugin plugin : getEnabledPlugins()) {
            Model.Factory factory = plugin.modelFactory(modelClass);
            if(factory != null) {
                return factory;
            }
        }
        return null;
    }

    public String getMessage(String locale, Object key, Object... args){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            String message = plugin.getMessage(locale, key, args);
            if(message != null) {
                return message;
            }
        }
        return null;
    }

    public void beforeActionInvocation(Method actionMethod){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.beforeActionInvocation(actionMethod);
        }
    }

    public void onActionInvocationResult(Result result){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.onActionInvocationResult(result);
        }
    }

    public void afterActionInvocation(){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.afterActionInvocation();
        }
    }

    public void routeRequest(Http.Request request){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.routeRequest(request);
        }
    }

    public void onRequestRouting(Router.Route route){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.onRequestRouting(route);
        }
    }

    public void onRoutesLoaded(){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.onRoutesLoaded();
        }
    }

    public boolean rawInvocation(Http.Request request, Http.Response response)throws Exception{
        for (PlayPlugin plugin : getEnabledPlugins()) {
            if (plugin.rawInvocation(request, response)) {
                //raw = true;
                return true;
            }
        }
        return false;
    }


    public boolean serveStatic(VirtualFile file, Http.Request request, Http.Response response){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            if (plugin.serveStatic(file, request, response)) {
                //raw = true;
                return true;
            }
        }
        return false;
    }

    public List<String> addTemplateExtensions(){
        List<String> list = new ArrayList<String>();
        for (PlayPlugin plugin : getEnabledPlugins()) {
            list.addAll(plugin.addTemplateExtensions());
        }
        return list;
    }

    public String overrideTemplateSource(BaseTemplate template, String source){
        for(PlayPlugin plugin : getEnabledPlugins()) {
            String newSource = plugin.overrideTemplateSource(template, source);
            if(newSource != null) {
                source = newSource;
            }
        }
        return source;
    }

    public Template loadTemplate(VirtualFile file){
        for(PlayPlugin plugin : getEnabledPlugins() ) {
            Template pluginProvided = plugin.loadTemplate(file);
            if(pluginProvided != null) {
                return pluginProvided;
            }
        }
        return null;
    }

    public void afterFixtureLoad(){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            plugin.afterFixtureLoad();
        }
    }

    public TestEngine.TestResults runTest(Class<BaseTest> clazz){
        for (PlayPlugin plugin : getEnabledPlugins()) {
            TestEngine.TestResults pluginTestResults = plugin.runTest(clazz);
            if (pluginTestResults != null) {
                return pluginTestResults;
            }
        }
        return null;
    }















}
TOP

Related Classes of play.plugins.PluginCollection

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.