/** * *************************************************************************** * Copyright (c) 2010 Qcadoo Limited * Project: Qcadoo Framework * Version: 1.4 * * This file is part of Qcadoo. * * Qcadoo is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************** */ package com.qcadoo.plugin.internal.accessor; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Service; import org.springframework.web.context.support.XmlWebApplicationContext; import com.qcadoo.model.beans.qcadooPlugin.QcadooPluginPlugin; import com.qcadoo.plugin.api.Plugin; import com.qcadoo.plugin.api.PluginState; import com.qcadoo.plugin.api.Version; import com.qcadoo.plugin.internal.PluginUtilsService; import com.qcadoo.plugin.internal.api.InternalPlugin; import com.qcadoo.plugin.internal.api.InternalPluginAccessor; import com.qcadoo.plugin.internal.api.ModuleFactoryAccessor; import com.qcadoo.plugin.internal.api.PluginDao; import com.qcadoo.plugin.internal.api.PluginDependencyManager; import com.qcadoo.plugin.internal.api.PluginDescriptorParser; import com.qcadoo.plugin.internal.stateresolver.InternalPluginStateResolver; import com.qcadoo.tenant.api.MultiTenantUtil; @Service public class DefaultPluginAccessor implements InternalPluginAccessor, ApplicationListener<ContextRefreshedEvent> { private static final Logger LOG = LoggerFactory.getLogger(DefaultPluginAccessor.class); @Autowired private PluginDescriptorParser pluginDescriptorParser; @Autowired private PluginDao pluginDao; @Autowired private PluginDependencyManager pluginDependencyManager; @Autowired private ModuleFactoryAccessor moduleFactoryAccessor; @Autowired private InternalPluginStateResolver pluginStateResolver; @Autowired @SuppressWarnings("unused") private PluginUtilsService pluginUtilsService; @Autowired @SuppressWarnings("unused") private MultiTenantUtil multiTenantUtil; private final Map<String, Plugin> plugins = new HashMap<String, Plugin>(); private volatile boolean alreadyInitialized; @Override public Plugin getEnabledPlugin(final String identifier) { Plugin plugin = plugins.get(identifier); if (plugin == null) { return null; } if (plugin.hasState(PluginState.ENABLED)) { return plugin; } else { return null; } } @Override public Collection<Plugin> getSystemPlugins() { Set<Plugin> systemPlugins = new HashSet<Plugin>(); for (Plugin plugin : plugins.values()) { if (plugin.isSystemPlugin()) { systemPlugins.add(plugin); } } return systemPlugins; } @Override public Collection<Plugin> getEnabledPlugins() { Set<Plugin> enabledPlugins = new HashSet<Plugin>(); for (Plugin plugin : plugins.values()) { if (plugin.hasState(PluginState.ENABLED)) { enabledPlugins.add(plugin); } } return enabledPlugins; } @Override public Plugin getPlugin(final String identifier) { return plugins.get(identifier); } @Override public Collection<Plugin> getPlugins() { return plugins.values(); } public void init() { LOG.info("Plugin Framework initialization"); long time = System.currentTimeMillis(); pluginStateResolver.setPluginAccessor(this); Set<InternalPlugin> enabledPluginsFromDescriptor = pluginDescriptorParser.loadPlugins(); Set<QcadooPluginPlugin> pluginsFromDatabase = pluginDao.list(); for (InternalPlugin plugin : enabledPluginsFromDescriptor) { QcadooPluginPlugin existingPlugin = null; for (QcadooPluginPlugin databasePlugin : pluginsFromDatabase) { if (plugin.getIdentifier().equals(databasePlugin.getIdentifier())) { existingPlugin = databasePlugin; break; } } if (existingPlugin == null) { plugin.changeStateTo(PluginState.ENABLING); } else { plugin.changeStateTo(PluginState.valueOf(existingPlugin.getState())); } if (existingPlugin == null || plugin.compareVersion(new Version(existingPlugin.getVersion())) > 0) { pluginDao.save(plugin); } else if (plugin.compareVersion(new Version(existingPlugin.getVersion())) < 0) { throw new IllegalStateException("Unsupported operation: downgrade, for plugin: " + plugin.getIdentifier()); } LOG.info("Registering plugin " + plugin); plugins.put(plugin.getIdentifier(), plugin); } for (QcadooPluginPlugin databasePlugin : pluginsFromDatabase) { if (databasePlugin.getState().equals(PluginState.TEMPORARY.toString())) { continue; } Plugin existingPlugin = null; for (Plugin plugin : enabledPluginsFromDescriptor) { if (databasePlugin.getIdentifier().equals(plugin.getIdentifier())) { existingPlugin = plugin; break; } } if (existingPlugin == null) { pluginDao.delete(databasePlugin); } } Set<InternalPlugin> temporaryPlugins = pluginDescriptorParser.getTemporaryPlugins(); for (InternalPlugin plugin : temporaryPlugins) { plugins.put(plugin.getIdentifier(), plugin); } LOG.info("Plugin Framework initialized in " + (System.currentTimeMillis() - time) + "ms"); } @Override public void onApplicationEvent(final ContextRefreshedEvent event) { if (!alreadyInitialized) { synchronized (this) { if (!alreadyInitialized) { init(); alreadyInitialized = true; } } } if (event != null && event.getSource() instanceof XmlWebApplicationContext) { XmlWebApplicationContext eventSource = (XmlWebApplicationContext) event.getSource(); if (eventSource.getParent() != null) { return; } } long time = System.currentTimeMillis(); List<Plugin> sortedPlugins = pluginDependencyManager.sortPluginsInDependencyOrder(plugins.values(), plugins); moduleFactoryAccessor.init(sortedPlugins); for (Plugin plugin : sortedPlugins) { if (plugin.hasState(PluginState.ENABLING)) { ((InternalPlugin) plugin).changeStateTo(PluginState.ENABLED); pluginDao.save(plugin); } } LOG.info("Plugin Framework started in " + (System.currentTimeMillis() - time) + "ms"); } void setPluginDescriptorParser(final PluginDescriptorParser pluginDescriptorParser) { this.pluginDescriptorParser = pluginDescriptorParser; } void setPluginDao(final PluginDao pluginDao) { this.pluginDao = pluginDao; } void setPluginDependencyManager(final PluginDependencyManager pluginDependencyManager) { this.pluginDependencyManager = pluginDependencyManager; } void setModuleFactoryAccessor(final ModuleFactoryAccessor moduleFactoryAccessor) { this.moduleFactoryAccessor = moduleFactoryAccessor; } void setInternalPluginStateResolver(final InternalPluginStateResolver pluginStateResolver) { this.pluginStateResolver = pluginStateResolver; } @Override public void savePlugin(final Plugin plugin) { plugins.put(plugin.getIdentifier(), plugin); } @Override public void removePlugin(final Plugin plugin) { plugins.remove(plugin.getIdentifier()); } }