Java tutorial
/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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 Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.services.pluginmgr; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.action.IAction; import org.pentaho.platform.api.engine.IContentGenerator; import org.pentaho.platform.api.engine.IContentGeneratorInfo; import org.pentaho.platform.api.engine.IContentInfo; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IPlatformPlugin; import org.pentaho.platform.api.engine.IPluginLifecycleListener; import org.pentaho.platform.api.engine.IPluginManager; import org.pentaho.platform.api.engine.IPluginManagerListener; import org.pentaho.platform.api.engine.IPluginProvider; import org.pentaho.platform.api.engine.IPluginResourceLoader; import org.pentaho.platform.api.engine.IServiceManager; import org.pentaho.platform.api.engine.ISolutionFileMetaProvider; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.api.engine.PlatformPluginRegistrationException; import org.pentaho.platform.api.engine.PluginBeanDefinition; import org.pentaho.platform.api.engine.PluginBeanException; import org.pentaho.platform.api.engine.PluginLifecycleException; import org.pentaho.platform.api.engine.PluginServiceDefinition; import org.pentaho.platform.api.engine.ServiceException; import org.pentaho.platform.api.engine.ServiceInitializationException; import org.pentaho.platform.api.engine.perspective.IPluginPerspectiveManager; import org.pentaho.platform.api.engine.perspective.pojo.IPluginPerspective; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.core.system.objfac.StandaloneSpringPentahoObjectFactory; import org.pentaho.platform.plugin.services.messages.Messages; import org.pentaho.platform.plugin.services.pluginmgr.servicemgr.ServiceConfig; import org.pentaho.platform.util.logging.Logger; import org.pentaho.ui.xul.XulOverlay; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.FileSystemResource; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; public class DefaultPluginManager implements IPluginManager { private static final Log logger = LogFactory.getLog(DefaultPluginManager.class); private static final String DEFAULT_PERSPECTIVE = "generatedContent"; // A namespacing prefix is added when registering meta provider objects in the object factory private static final String METAPROVIDER_KEY_PREFIX = "METAPROVIDER-"; //$NON-NLS-1$ protected Map<String, ClassLoader> classLoaderMap = Collections .synchronizedMap(new HashMap<String, ClassLoader>()); protected Map<String, GenericApplicationContext> beanFactoryMap = Collections .synchronizedMap(new HashMap<String, GenericApplicationContext>()); protected Map<String, IPlatformPlugin> registeredPlugins = new Hashtable<String, IPlatformPlugin>(); protected Map<String, IContentInfo> contentTypeByExtension = Collections .synchronizedMap(new HashMap<String, IContentInfo>()); protected List<XulOverlay> overlaysCache = Collections.synchronizedList(new ArrayList<XulOverlay>()); @Override public Set<String> getContentTypes() { // map.keySet returns a set backed by the map, so we cannot allow modification of the set return Collections.unmodifiableSet(contentTypeByExtension.keySet()); } @Override public List<XulOverlay> getOverlays() { return Collections.unmodifiableList(overlaysCache); } @Override public IContentInfo getContentTypeInfo(String type) { return contentTypeByExtension.get(type); } /** * Clears all the lists and maps in preparation for reloading the state from the plugin provider. Fires the plugin * unloaded event for each known plugin. */ private void unloadPlugins() { overlaysCache.clear(); classLoaderMap.clear(); // TODO: can we reset/reload the spring bean factory here? contentTypeByExtension.clear(); // we do not need to synchronize here since unloadPlugins // is called within the synchronized block in reload for (IPlatformPlugin plugin : registeredPlugins.values()) { try { plugin.unLoaded(); } catch (Throwable t) { // we do not want any type of exception to leak out and cause a problem here // A plugin unload should not adversely affect anything downstream, it should // log an error and otherwise fail silently String msg = Messages.getInstance().getErrorString( "PluginManager.ERROR_0014_PLUGIN_FAILED_TO_PROPERLY_UNLOAD", plugin.getId()); //$NON-NLS-1$ Logger.error(getClass().toString(), msg, t); PluginMessageLogger.add(msg); } } registeredPlugins.clear(); } @Override public List<String> getRegisteredPlugins() { List<String> pluginIds = new ArrayList<String>(registeredPlugins.size()); for (IPlatformPlugin plugin : registeredPlugins.values()) { pluginIds.add(plugin.getId()); } return pluginIds; } @Deprecated public final boolean reload(IPentahoSession session) { return reload(); } @Override public final boolean reload() { IPentahoSession session = PentahoSessionHolder.getSession(); boolean anyErrors = false; IPluginProvider pluginProvider = PentahoSystem.get(IPluginProvider.class, "IPluginProvider", session); List<IPlatformPlugin> providedPlugins = null; try { synchronized (registeredPlugins) { this.unloadPlugins(); } // the plugin may fail to load during getPlugins without an exception thrown if the provider // is capable of discovering the plugin fine but there are structural problems with the plugin // itself. In this case a warning should be logged by the provider, but, again, no exception // is expected. providedPlugins = pluginProvider.getPlugins(session); } catch (PlatformPluginRegistrationException e1) { String msg = Messages.getInstance().getErrorString("PluginManager.ERROR_0012_PLUGIN_DISCOVERY_FAILED"); //$NON-NLS-1$ Logger.error(getClass().toString(), msg, e1); PluginMessageLogger.add(msg); anyErrors = true; } // TODO: refresh appc context here? synchronized (providedPlugins) { for (IPlatformPlugin plugin : providedPlugins) { try { registeredPlugins.put(plugin.getId(), plugin); ClassLoader loader = setPluginClassLoader(plugin); initializeBeanFactory(plugin, loader); } catch (Throwable t) { // this has been logged already anyErrors = true; String msg = Messages.getInstance() .getErrorString("PluginManager.ERROR_0011_FAILED_TO_REGISTER_PLUGIN", plugin.getId()); //$NON-NLS-1$ Logger.error(getClass().toString(), msg, t); PluginMessageLogger.add(msg); } } registeredPlugins.clear(); for (IPlatformPlugin plugin : providedPlugins) { try { GenericApplicationContext beanFactory = beanFactoryMap.get(plugin.getId()); if (beanFactory != null) { beanFactory.refresh(); } registerPlugin(plugin); registeredPlugins.put(plugin.getId(), plugin); } catch (Throwable t) { // this has been logged already anyErrors = true; String msg = Messages.getInstance() .getErrorString("PluginManager.ERROR_0011_FAILED_TO_REGISTER_PLUGIN", plugin.getId()); //$NON-NLS-1$ Logger.error(getClass().toString(), msg, t); PluginMessageLogger.add(msg); } } } IServiceManager svcManager = PentahoSystem.get(IServiceManager.class, null); if (svcManager != null) { try { svcManager.initServices(); } catch (ServiceInitializationException e) { String msg = Messages.getInstance() .getErrorString("PluginManager.ERROR_0022_SERVICE_INITIALIZATION_FAILED"); //$NON-NLS-1$ Logger.error(getClass().toString(), msg, e); PluginMessageLogger.add(msg); } } return !anyErrors; } /** * Gets the plugin ready to handle lifecycle events. */ private static void bootStrapPlugin(IPlatformPlugin plugin, ClassLoader loader) throws PlatformPluginRegistrationException { Object listener = null; try { if (!StringUtils.isEmpty(plugin.getLifecycleListenerClassname())) { listener = loader.loadClass(plugin.getLifecycleListenerClassname()).newInstance(); } } catch (Throwable t) { throw new PlatformPluginRegistrationException(Messages.getInstance().getErrorString( "PluginManager.ERROR_0017_COULD_NOT_LOAD_PLUGIN_LIFECYCLE_LISTENER", plugin.getId(), plugin //$NON-NLS-1$ .getLifecycleListenerClassname()), t); } if (listener != null) { if (!IPluginLifecycleListener.class.isAssignableFrom(listener.getClass())) { throw new PlatformPluginRegistrationException(Messages.getInstance().getErrorString( "PluginManager.ERROR_0016_PLUGIN_LIFECYCLE_LISTENER_WRONG_TYPE", plugin.getId(), plugin.getLifecycleListenerClassname())); //$NON-NLS-1$ } plugin.addLifecycleListener((IPluginLifecycleListener) listener); } } @SuppressWarnings("unchecked") private void registerPlugin(final IPlatformPlugin plugin) throws PlatformPluginRegistrationException, PluginLifecycleException { // TODO: we should treat the registration of a plugin as an atomic operation // with rollback if something is broken if (StringUtils.isEmpty(plugin.getId())) { throw new PlatformPluginRegistrationException(Messages.getInstance() .getErrorString("PluginManager.ERROR_0026_PLUGIN_INVALID", plugin.getSourceDescription())); //$NON-NLS-1$ } if (registeredPlugins.containsKey(plugin.getId())) { throw new PlatformPluginRegistrationException(Messages.getInstance() .getErrorString("PluginManager.ERROR_0024_PLUGIN_ALREADY_LOADED_BY_SAME_NAME", plugin.getId())); //$NON-NLS-1$ } ClassLoader loader = setPluginClassLoader(plugin); bootStrapPlugin(plugin, loader); plugin.init(); registerContentTypes(plugin, loader); registerContentGenerators(plugin, loader); registerPerspectives(plugin, loader); // cache overlays overlaysCache.addAll(plugin.getOverlays()); // service registry must take place after bean registry since // a service class may be configured as a plugin bean registerServices(plugin, loader); PluginMessageLogger .add(Messages.getInstance().getString("PluginManager.PLUGIN_REGISTERED", plugin.getId())); //$NON-NLS-1$ try { plugin.loaded(); } catch (Throwable t) { // The plugin has already been loaded, so there is really no logical response to any type // of failure here except to log an error and otherwise fail silently String msg = Messages.getInstance() .getErrorString("PluginManager.ERROR_0015_PLUGIN_LOADED_HANDLING_FAILED", plugin.getId()); //$NON-NLS-1$ Logger.error(getClass().toString(), msg, t); PluginMessageLogger.add(msg); } } private void registerPerspectives(IPlatformPlugin plugin, ClassLoader loader) { for (IPluginPerspective pluginPerspective : plugin.getPluginPerspectives()) { PentahoSystem.get(IPluginPerspectiveManager.class).addPluginPerspective(pluginPerspective); } } protected void registerContentTypes(IPlatformPlugin plugin, ClassLoader loader) throws PlatformPluginRegistrationException { // index content types and define any file meta providers for (IContentInfo info : plugin.getContentInfos()) { contentTypeByExtension.put(info.getExtension(), info); String metaProviderClass = plugin.getMetaProviderMap().get(info.getExtension()); // if a meta-provider is defined for this content type, then register it... if (!StringUtils.isEmpty(metaProviderClass)) { Class<?> clazz = null; String defaultErrMsg = Messages.getInstance().getErrorString( "PluginManager.ERROR_0013_FAILED_TO_SET_CONTENT_TYPE_META_PROVIDER", metaProviderClass, info.getExtension()); //$NON-NLS-1$ try { // do a test load to fail early if class not found clazz = loader.loadClass(metaProviderClass); } catch (Exception e) { throw new PlatformPluginRegistrationException(defaultErrMsg, e); } // check that the class is an accepted type if (!(ISolutionFileMetaProvider.class.isAssignableFrom(clazz))) { throw new PlatformPluginRegistrationException(Messages.getInstance().getErrorString( "PluginManager.ERROR_0019_WRONG_TYPE_FOR_CONTENT_TYPE_META_PROVIDER", metaProviderClass, info.getExtension())); //$NON-NLS-1$ } // the class is ok, so register it with the factory assertUnique(plugin.getId(), METAPROVIDER_KEY_PREFIX + info.getExtension()); BeanDefinition beanDef = BeanDefinitionBuilder.rootBeanDefinition(metaProviderClass) .setScope(BeanDefinition.SCOPE_PROTOTYPE).getBeanDefinition(); beanFactoryMap.get(plugin.getId()) .registerBeanDefinition(METAPROVIDER_KEY_PREFIX + info.getExtension(), beanDef); } } } /** * The native bean factory is the bean factory that has had all of its bean definitions loaded natively. In other * words, the plugin manager will not add any further bean definitions (i.e. from a plugin.xml file) into this * factory. This factory represents the one responsible for holding bean definitions for plugin.spring.xml or, if in a * unit test environment, the unit test pre-loaded bean factory. * * @return a bean factory will preconfigured bean definitions or <code>null</code> if no bean definition source is * available */ protected BeanFactory getNativeBeanFactory(final IPlatformPlugin plugin, final ClassLoader loader) { BeanFactory nativeFactory = null; if (plugin.getBeanFactory() != null) { // then we are probably in a unit test so just use the preconfigured one BeanFactory testFactory = plugin.getBeanFactory(); if (testFactory instanceof ConfigurableBeanFactory) { ((ConfigurableBeanFactory) testFactory).setBeanClassLoader(loader); } else { logger.warn(Messages.getInstance().getString("PluginManager.WARN_WRONG_BEAN_FACTORY_TYPE")); //$NON-NLS-1$ } nativeFactory = testFactory; } else { File f = new File(((PluginClassLoader) loader).getPluginDir(), "plugin.spring.xml"); //$NON-NLS-1$ if (f.exists()) { logger.debug("Found plugin spring file @ " + f.getAbsolutePath()); //$NON-NLS-1$ FileSystemResource fsr = new FileSystemResource(f); GenericApplicationContext appCtx = new GenericApplicationContext() { @Override protected void prepareBeanFactory(ConfigurableListableBeanFactory clBeanFactory) { super.prepareBeanFactory(clBeanFactory); clBeanFactory.setBeanClassLoader(loader); } @Override public ClassLoader getClassLoader() { return loader; } }; XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(appCtx); xmlReader.setBeanClassLoader(loader); xmlReader.loadBeanDefinitions(fsr); nativeFactory = appCtx; } } return nativeFactory; } /** * Initializes a bean factory for serving up instance of plugin classes. * * @return an instance of the factory that allows callers to continue to define more beans on it programmatically */ protected void initializeBeanFactory(final IPlatformPlugin plugin, final ClassLoader loader) throws PlatformPluginRegistrationException { if (!(loader instanceof PluginClassLoader)) { logger.warn( "Can't determine plugin dir to load spring file because classloader is not of type PluginClassLoader. " //$NON-NLS-1$ + "This is since we are probably in a unit test"); //$NON-NLS-1$ return; } // // Get the native factory (the factory that comes preconfigured via either Spring bean files or via JUnit test // BeanFactory nativeBeanFactory = getNativeBeanFactory(plugin, loader); // // Now create the definable factory for accepting old style bean definitions from IPluginProvider // GenericApplicationContext beanFactory = null; if (nativeBeanFactory != null && nativeBeanFactory instanceof GenericApplicationContext) { beanFactory = (GenericApplicationContext) nativeBeanFactory; } else { beanFactory = new GenericApplicationContext(); beanFactory.setClassLoader(loader); beanFactory.getBeanFactory().setBeanClassLoader(loader); if (nativeBeanFactory != null) { beanFactory.getBeanFactory().setParentBeanFactory(nativeBeanFactory); } } beanFactoryMap.put(plugin.getId(), beanFactory); // // Register any beans defined via the pluginProvider // // we do not have to synchronize on the bean set here because the // map that backs the set is never modified after the plugin has // been made available to the plugin manager for (PluginBeanDefinition def : plugin.getBeans()) { // register by classname if id is null def.setBeanId((def.getBeanId() == null) ? def.getClassname() : def.getBeanId()); assertUnique(plugin.getId(), def.getBeanId()); // defining plugin beans the old way through the plugin provider ifc supports only prototype scope BeanDefinition beanDef = BeanDefinitionBuilder.rootBeanDefinition(def.getClassname()) .setScope(BeanDefinition.SCOPE_PROTOTYPE).getBeanDefinition(); beanFactory.registerBeanDefinition(def.getBeanId(), beanDef); } StandaloneSpringPentahoObjectFactory pentahoFactory = new StandaloneSpringPentahoObjectFactory( "Plugin Factory ( " + plugin.getId() + " )"); pentahoFactory.init(null, beanFactory); } /** * A utility method that throws an exception if a bean with the id is already defined for this plugin */ protected void assertUnique(String pluginId, String beanId) throws PlatformPluginRegistrationException { if (beanFactoryMap.get(pluginId).containsBean(beanId)) { throw new PlatformPluginRegistrationException(Messages.getInstance() .getErrorString("PluginManager.ERROR_0018_BEAN_ALREADY_REGISTERED", beanId, pluginId)); //$NON-NLS-1$ } } private void registerServices(IPlatformPlugin plugin, ClassLoader loader) throws PlatformPluginRegistrationException { IServiceManager svcManager = PentahoSystem.get(IServiceManager.class, null); for (PluginServiceDefinition pws : plugin.getServices()) { for (ServiceConfig ws : createServiceConfigs(pws, plugin, loader)) { try { svcManager.registerService(ws); } catch (ServiceException e) { throw new PlatformPluginRegistrationException(Messages.getInstance().getErrorString( "PluginManager.ERROR_0025_SERVICE_REGISTRATION_FAILED", ws.getId(), plugin.getId()), e); //$NON-NLS-1$ } } } } /* * A utility method to convert plugin version of webservice definition to the official engine version consumable by an * IServiceManager */ private Collection<ServiceConfig> createServiceConfigs(PluginServiceDefinition pws, IPlatformPlugin plugin, ClassLoader loader) throws PlatformPluginRegistrationException { Collection<ServiceConfig> services = new ArrayList<ServiceConfig>(); // Set the service type (one service config instance created per service type) // if (pws.getTypes() == null || pws.getTypes().length < 1) { throw new PlatformPluginRegistrationException(Messages.getInstance() .getErrorString("PluginManager.ERROR_0023_SERVICE_TYPE_UNSPECIFIED", pws.getId())); //$NON-NLS-1$ } for (String type : pws.getTypes()) { ServiceConfig ws = new ServiceConfig(); ws.setServiceType(type); ws.setTitle(pws.getTitle()); ws.setDescription(pws.getDescription()); String serviceClassName = (StringUtils.isEmpty(pws.getServiceClass())) ? pws.getServiceBeanId() : pws.getServiceClass(); String serviceId; if (!StringUtils.isEmpty(pws.getId())) { serviceId = pws.getId(); } else { serviceId = serviceClassName; if (serviceClassName.indexOf('.') > 0) { serviceId = serviceClassName.substring(serviceClassName.lastIndexOf('.') + 1); } } ws.setId(serviceId); // Register the service class // final String serviceClassKey = ws.getServiceType() + "-" + ws.getId() + "/" + serviceClassName; //$NON-NLS-1$ //$NON-NLS-2$ assertUnique(plugin.getId(), serviceClassKey); // defining plugin beans the old way through the plugin provider ifc supports only prototype scope BeanDefinition beanDef = BeanDefinitionBuilder.rootBeanDefinition(serviceClassName) .setScope(BeanDefinition.SCOPE_PROTOTYPE).getBeanDefinition(); beanFactoryMap.get(plugin.getId()).registerBeanDefinition(serviceClassKey, beanDef); if (!this.isBeanRegistered(serviceClassKey)) { throw new PlatformPluginRegistrationException(Messages.getInstance() .getErrorString("PluginManager.ERROR_0020_NO_SERVICE_CLASS_REGISTERED", serviceClassKey)); //$NON-NLS-1$ } // Load/set the service class and supporting types // try { ws.setServiceClass(loadClass(serviceClassKey)); ArrayList<Class<?>> classes = new ArrayList<Class<?>>(); if (pws.getExtraClasses() != null) { for (String extraClass : pws.getExtraClasses()) { classes.add(loadClass(extraClass)); } } ws.setExtraClasses(classes); } catch (PluginBeanException e) { throw new PlatformPluginRegistrationException(Messages.getInstance() .getErrorString("PluginManager.ERROR_0021_SERVICE_CLASS_LOAD_FAILED", serviceClassKey), e); //$NON-NLS-1$ } services.add(ws); } return services; } private ClassLoader setPluginClassLoader(IPlatformPlugin plugin) throws PlatformPluginRegistrationException { ClassLoader loader = classLoaderMap.get(plugin.getId()); if (loader == null) { String pluginDirPath = PentahoSystem.getApplicationContext() .getSolutionPath("system/" + plugin.getSourceDescription()); //$NON-NLS-1$ // need to scrub out duplicate file delimeters otherwise we will // not be able to locate resources in jars. This classloader ultimately // needs to be made less fragile pluginDirPath = pluginDirPath.replace("//", "/"); //$NON-NLS-1$ //$NON-NLS-2$ Logger.debug(this, "plugin dir for " + plugin.getId() + " is [" + pluginDirPath + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ File pluginDir = new File(pluginDirPath); if (!pluginDir.exists() || !pluginDir.isDirectory() || !pluginDir.canRead()) { throw new PlatformPluginRegistrationException(Messages.getInstance().getErrorString( "PluginManager.ERROR_0027_PLUGIN_DIR_UNAVAILABLE", pluginDir.getAbsolutePath())); //$NON-NLS-1$ } loader = new PluginClassLoader(pluginDir, this.getClass().getClassLoader()); if (plugin.getLoaderType() == IPlatformPlugin.ClassLoaderType.OVERRIDING) { ((PluginClassLoader) loader).setOverrideLoad(true); } classLoaderMap.put(plugin.getId(), loader); } return loader; } public ClassLoader getClassLoader(IPlatformPlugin plugin) { return getClassLoader(plugin.getId()); } @Override public ClassLoader getClassLoader(String pluginId) { return classLoaderMap.get(pluginId); } // public ListableBeanFactory asBeanFactory() { // return beanFactoryMap.get(pluginId); // } @Override public ListableBeanFactory getBeanFactory(String pluginId) { return beanFactoryMap.get(pluginId).getBeanFactory(); } private void registerContentGenerators(IPlatformPlugin plugin, ClassLoader loader) throws PlatformPluginRegistrationException { // register the content generators for (IContentGeneratorInfo cgInfo : plugin.getContentGenerators()) { // define the bean in the factory BeanDefinition beanDef = BeanDefinitionBuilder.rootBeanDefinition(cgInfo.getClassname()) .setScope(BeanDefinition.SCOPE_PROTOTYPE).getBeanDefinition(); GenericApplicationContext factory = beanFactoryMap.get(plugin.getId()); // register bean with alias of content generator id (old way) factory.registerBeanDefinition(cgInfo.getId(), beanDef); // register bean with alias of type (with default perspective) as well (new way) factory.registerAlias(cgInfo.getId(), cgInfo.getType()); PluginMessageLogger.add(Messages.getInstance() .getString("PluginManager.USER_CONTENT_GENERATOR_REGISTERED", cgInfo.getId(), plugin.getId())); //$NON-NLS-1$ } } public Object getBean(String beanId, Class<?> requiredType) { if (beanId == null) { throw new IllegalArgumentException("beanId cannot be null"); //$NON-NLS-1$ } Object bean = null; for (GenericApplicationContext beanFactory : beanFactoryMap.values()) { if (beanFactory.containsBean(beanId)) { if (requiredType == null) { bean = beanFactory.getBean(beanId); } else { bean = beanFactory.getBean(beanId, requiredType); } } } if (bean == null) { throw new NoSuchBeanDefinitionException("Could not find bean with id " + beanId); } return bean; } @Override public Object getBean(String beanId) throws PluginBeanException { if (beanId == null) { throw new IllegalArgumentException("beanId cannot be null"); //$NON-NLS-1$ } Object bean = null; for (GenericApplicationContext beanFactory : beanFactoryMap.values()) { if (beanFactory.containsBean(beanId)) { try { bean = beanFactory.getBean(beanId); } catch (Throwable ex) { // Catching throwable on purpose throw new PluginBeanException(ex); } } } if (bean == null) { throw new PluginBeanException( Messages.getInstance().getString("PluginManager.WARN_CLASS_NOT_REGISTERED", beanId)); //$NON-NLS-1$ } return bean; } @Override public IContentGenerator getContentGenerator(String type, String perspectiveName) { IContentGenerator cg = null; if (perspectiveName == null || perspectiveName.equals(DEFAULT_PERSPECTIVE)) { cg = (IContentGenerator) getBean(type, IContentGenerator.class); } else { String beanId = (perspectiveName == null) ? type : type + "." + perspectiveName; //$NON-NLS-1$ try { cg = (IContentGenerator) getBean(beanId, IContentGenerator.class); } catch (NoSuchBeanDefinitionException e) { // fallback condition, look for a type agnostic content generator try { cg = (IContentGenerator) getBean(perspectiveName, IContentGenerator.class); } catch (NoSuchBeanDefinitionException e2) { throw new NoSuchBeanDefinitionException( "Failed to find bean: " + e.getMessage() + " : " + e2.getMessage()); } } } return cg; } public IAction getAction(String type, String perspectiveName) { IAction action = null; String beanId = (perspectiveName == null) ? type : type + "." + perspectiveName; //$NON-NLS-1$ try { action = (IAction) getBean(beanId, IAction.class); } catch (NoSuchBeanDefinitionException e) { // fallback condition, look for a type agnostic content generator try { action = (IAction) getBean(perspectiveName, IAction.class); } catch (NoSuchBeanDefinitionException e2) { throw new NoSuchBeanDefinitionException( "Failed to find bean: " + e.getMessage() + " : " + e2.getMessage()); } } return action; } @Override public Class<?> loadClass(String beanId) throws PluginBeanException { if (beanId == null) { throw new IllegalArgumentException("beanId cannot be null"); //$NON-NLS-1$ } Class<?> type = null; for (GenericApplicationContext beanFactory : beanFactoryMap.values()) { if (beanFactory.containsBean(beanId)) { try { type = beanFactory.getType(beanId); break; } catch (Throwable ex) { // Catching throwable on purpose throw new PluginBeanException(ex); } } } if (type == null) { throw new PluginBeanException( Messages.getInstance().getString("PluginManager.WARN_CLASS_NOT_REGISTERED", beanId)); //$NON-NLS-1$ } return type; } @Override public boolean isBeanRegistered(String beanId) { if (beanId == null) { throw new IllegalArgumentException("beanId cannot be null"); //$NON-NLS-1$ } boolean registered = false; for (GenericApplicationContext beanFactory : beanFactoryMap.values()) { if (beanFactory.containsBean(beanId)) { registered = true; } } return registered; } @Override public void unloadAllPlugins() { synchronized (registeredPlugins) { this.unloadPlugins(); } } public Object getPluginSetting(IPlatformPlugin plugin, String key, String defaultValue) { return getPluginSetting(plugin.getId(), key, defaultValue); } @Override public Object getPluginSetting(String pluginId, String key, String defaultValue) { IPluginResourceLoader resLoader = PentahoSystem.get(IPluginResourceLoader.class, null); ClassLoader classLoader = classLoaderMap.get(pluginId); return resLoader.getPluginSetting(classLoader, key, defaultValue); } private Collection<String> getBeanIdsForType(String pluginId, Class<?> clazz) { ArrayList<String> ids = new ArrayList<String>(); ListableBeanFactory fac = beanFactoryMap.get(pluginId).getBeanFactory(); String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(fac, clazz); for (String beanName : names) { ids.add(beanName); for (String beanAlias : fac.getAliases(beanName)) { ids.add(beanAlias); } } return ids; } @Override public String getPluginIdForType(String contentType) { for (String pluginId : getRegisteredPlugins()) { for (String beanId : getBeanIdsForType(pluginId, IContentGenerator.class)) { String serviceContentType = beanId; if (beanId.contains(".")) { //$NON-NLS-1$ serviceContentType = beanId.substring(0, beanId.indexOf('.')); } if (contentType.equals(serviceContentType)) { return pluginId; } } } // if no content generator was found in any of the plugins that can service contentType, return null return null; } @Override @Deprecated public IContentGenerator getContentGeneratorForType(String type, IPentahoSession session) throws ObjectFactoryException { try { return getContentGenerator(type, (String) null); } catch (NoSuchBeanDefinitionException e) { throw new ObjectFactoryException(e); } } @Override public String getPluginIdForClassLoader(ClassLoader classLoader) { if (classLoader == null) { return null; } for (String pluginId : classLoaderMap.keySet()) { ClassLoader maybeClassLoader = classLoaderMap.get(pluginId); if (maybeClassLoader.equals(classLoader)) { return pluginId; } } return null; } private String trimLeadingSlash(String path) { return (path.startsWith("/")) ? path.substring(1) : path; //$NON-NLS-1$ } /** * Return <code>true</code> if the servicePath is being addressed by the requestPath. The request path is said to * request the service if it contains at least ALL of the elements of the servicePath, in order. It may include more * than these elements but it must contain at least the servicePath. * * @param servicePath * @param requestPath * @return <code>true</code> if the servicePath is being addressed by the requestPath */ protected boolean isRequested(String servicePath, String requestPath) { String[] requestPathElements = trimLeadingSlash(requestPath).split("/"); //$NON-NLS-1$ String[] servicePathElements = trimLeadingSlash(servicePath).split("/"); //$NON-NLS-1$ if (requestPathElements.length < servicePathElements.length) { return false; } for (int i = 0; i < servicePathElements.length; i++) { if (!requestPathElements[i].equals(servicePathElements[i])) { return false; } } return true; } @Deprecated public String getServicePlugin(String path) { for (IPlatformPlugin plugin : registeredPlugins.values()) { String pluginId = getStaticResourcePluginId(plugin, path); if (pluginId != null) { return pluginId; } for (IContentGeneratorInfo contentGenerator : plugin.getContentGenerators()) { String cgId = contentGenerator.getId(); if (isRequested(cgId, path)) { return plugin.getId(); } } } return null; } private String getStaticResourcePluginId(IPlatformPlugin plugin, String path) { Map<String, String> resourceMap = plugin.getStaticResourceMap(); for (String url : resourceMap.keySet()) { if (isRequested(url, path)) { return plugin.getId(); } } return null; } @Deprecated public boolean isStaticResource(String path) { for (IPlatformPlugin plugin : registeredPlugins.values()) { String pluginId = getStaticResourcePluginId(plugin, path); if (pluginId != null) { return true; } } return false; } public boolean isPublic(String pluginId, String path) { IPlatformPlugin plugin = registeredPlugins.get(pluginId); if (plugin == null) { return false; } Map<String, String> resourceMap = plugin.getStaticResourceMap(); if (path.startsWith("/")) { //$NON-NLS-1$ path = path.substring(1); } for (String pluginRelativeDir : resourceMap.values()) { if (path.startsWith(pluginRelativeDir)) { return true; } } return false; } @Deprecated public InputStream getStaticResource(String path) { for (IPlatformPlugin plugin : registeredPlugins.values()) { Map<String, String> resourceMap = plugin.getStaticResourceMap(); for (String url : resourceMap.keySet()) { if (isRequested(url, path)) { IPluginResourceLoader resLoader = PentahoSystem.get(IPluginResourceLoader.class, null); ClassLoader classLoader = classLoaderMap.get(plugin.getId()); String resourcePath = path.replace(url, resourceMap.get(url)); return resLoader.getResourceAsStream(classLoader, resourcePath); } } } return null; } public List<String> getExternalResourcesForContext(String context) { List<String> resources = new ArrayList<String>(); for (IPlatformPlugin plugin : registeredPlugins.values()) { List<String> pluginRes = plugin.getExternalResourcesForContext(context); if (pluginRes != null) { resources.addAll(pluginRes); } } return resources; } @Override public List<String> getPluginRESTPerspectivesForType(String contentType) { List<String> pluginPerspectives = new ArrayList<String>(); for (String pluginId : getRegisteredPlugins()) { for (String beanId : getBeanIdsForType(pluginId, IContentGenerator.class)) { String serviceContentType = beanId; if (beanId.contains(".")) { //$NON-NLS-1$ serviceContentType = beanId.substring(0, beanId.indexOf('.')); } if (serviceContentType != null && serviceContentType.equals(contentType)) { if (beanId.contains(".")) { //$NON-NLS-1$ pluginPerspectives.add(beanId.substring(beanId.lastIndexOf('.'), beanId.length())); } } } } return pluginPerspectives; } @Override public List<String> getPluginRESTPerspectivesForId(String id) { List<String> pluginPerspectives = new ArrayList<String>(); for (String pluginId : getRegisteredPlugins()) { if (id.equals(pluginId)) { for (String beanId : getBeanIdsForType(pluginId, IContentGenerator.class)) { if (beanId.contains(".")) { //$NON-NLS-1$ pluginPerspectives.add(beanId.substring(beanId.lastIndexOf('.') + 1, beanId.length())); } } } } return pluginPerspectives; } @Override public void addPluginManagerListener(IPluginManagerListener listener) { } }