package org.gcube.data.spd.plugin; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.ObjectExistsException; import net.sf.ehcache.config.CacheConfiguration; import net.sf.ehcache.store.MemoryStoreEvictionPolicy; import org.gcube.common.resources.gcore.GCoreEndpoint; import org.gcube.common.resources.gcore.GCoreEndpoint.Profile.Endpoint; import org.gcube.common.resources.gcore.HostingNode; import org.gcube.common.resources.gcore.ServiceEndpoint; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.data.spd.Constants; import org.gcube.data.spd.caching.MyCacheEventListener; import org.gcube.data.spd.model.PluginDescription; import org.gcube.data.spd.model.service.types.PluginDescriptions; import org.gcube.data.spd.model.util.Capabilities; import org.gcube.data.spd.plugin.fwk.AbstractPlugin; import org.gcube.data.spd.remoteplugin.RemotePlugin; import org.gcube.data.spd.utils.Utils; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.gcube.smartgears.context.application.ApplicationContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PluginManager{ private static Logger log = LoggerFactory.getLogger(PluginManager.class); private static final int CACHE_ENTRIES_PER_PLUGIN =500; private static final String RESOURCE_CATEGORY ="BiodiversityRepository"; private ServiceLoader loader; private Map plugins = new HashMap(); private ApplicationContext ctx; private EnumMap> pluginsPerCapability= new EnumMap>(Capabilities.class); public Set getPluginsPerCapability(Capabilities capability, Collection plugins){ Set returnSet = new HashSet(); if (pluginsPerCapability.containsKey(capability)){ for (AbstractPlugin plugin : plugins) if (pluginsPerCapability.get(capability).contains(plugin)) returnSet.add(plugin); return Collections.unmodifiableSet(returnSet); }else return Collections.emptySet(); } public Set getPluginsPerCapability(Capabilities capability){ if (pluginsPerCapability.containsKey(capability)) return Collections.unmodifiableSet(pluginsPerCapability.get(capability)); else return Collections.emptySet(); } /** * Creates a new instance, installing all the plugins found on the classpath. */ public PluginManager(ApplicationContext context) { log.debug("creating the plugin manager"); this.ctx = context; initializePlugins(); } //update the pluginManager with a new plugin when a runtimeresource is added in its scope public void addRemotePlugins(List remotePluginDescriptions, String gCoreEndpointId){ for (PluginDescription description : remotePluginDescriptions ) try{ if (!plugins.containsKey(description.getName()) && !description.isRemote()){ RemotePlugin plugin = new RemotePlugin(); plugin.remoteIntitializer(description, gCoreEndpointId); log.debug("found remote plugin for "+plugin.getRepositoryName()); checkPlugin(plugin); //initializing cache per plugin if (plugin.isUseCache()) createCache(plugin.getRepositoryName()); log.trace("created remote plugin "+plugin.getRepositoryName()+" with endpoints id "+plugin.getRemoteUris()); }else { AbstractPlugin plugin = plugins.get(description.getName()); if(plugin.isRemote()){ ((RemotePlugin) plugin).addUrl(gCoreEndpointId); log.trace("added remote Plugin "+plugin.getRepositoryName()+" from endpoint id "+gCoreEndpointId); } } }catch (Exception e) { log.error("initialization failed for remote plugin "+description.getName(),e); } } /*public void update(ServiceEndpoint resource){ try { if (!resource.scopes().contains(this.scope.toString())) this.removePlugin(resource.profile().name()); else if (!plugins.containsKey(resource.profile().name())) { add(resource); }else plugins.get(resource.profile().name()).initialize(resource); } catch (Exception e) { log.error("error updateting plugin "+resource.profile().name(),e); } }*/ /** * Returns the installed plugins, indexed by name. * @return the plugins */ public Map plugins() { return plugins; } private void retrievePlugins(Map runtimeResourcePerPlugin){ for (AbstractPlugin plugin : loader) { ServiceEndpoint resource=null; if ((resource=runtimeResourcePerPlugin.get(plugin.getRepositoryName()))==null) continue; log.debug("found a repo plugin for "+plugin.getRepositoryName()); if (plugin.getRepositoryName()==null) { log.error("plugin "+plugin.getClass().getSimpleName()+" has a null repository name"); continue; } if (plugin.getRepositoryName().contains(":")) { log.error("plugin "+plugin.getClass().getSimpleName()+" contains an invalid character"); continue; } if (plugin.getDescription()==null) { log.warn("plugin "+plugin.getClass().getSimpleName()+" has a null description"); continue; } try{ if(!plugin.isInitialized()){ plugin.initialize(resource); log.debug("initialization finished for plugin "+plugin.getRepositoryName()); } checkPlugin(plugin); //initializing cache per plugin if (plugin.isUseCache()) createCache(plugin.getRepositoryName()); }catch (Exception e) { log.error("initialization failed for plugin "+plugin.getRepositoryName(),e); } } } public void retrieveRemotePlugins(){ List descriptions = new ArrayList(plugins.size()); for (AbstractPlugin plugin : plugins.values()) if (!plugin.isRemote()) descriptions.add(Utils.getPluginDescription(plugin)); PluginDescriptions myDescriptions = new PluginDescriptions(descriptions); for (GCoreEndpoint address: retrieveTwinServicesAddresses()) { String endpointId = ctx.profile(GCoreEndpoint.class).id(); List pluginDescriptions =null; URI uri = null; try { for (Endpoint endpoint : address.profile().endpoints()) if (endpoint.name().equals("remote-dispatcher")){ uri = endpoint.uri(); break; } if (uri!=null){ //TODO : call remote rest service //RemoteDispatcher remoteDispatcher = org.gcube.data.spd.client.Constants.getRemoteDispatcherService(uri.toString()); //pluginDescriptions = remoteDispatcher.exchangePlugins(myDescriptions, endpointId).getDescriptions(); } }catch (Throwable e) { log.warn("error contacting remote plugin hosted on a Whn id "+address.profile().ghnId()); continue; } if (pluginDescriptions==null) continue; log.trace("plugins in Pluginmanager are "+plugins.keySet()); addRemotePlugins(pluginDescriptions, endpointId); } } private List retrieveTwinServicesAddresses(){ List addresses = Collections.emptyList(); log.info("retreiving twin services in context {} ",ScopeProvider.instance.get()); try{ SimpleQuery query = queryFor(GCoreEndpoint.class); query.addCondition("$resource/Profile/ServiceName/text() eq '"+Constants.SERVICE_NAME+"'") .addCondition("$resource/Profile/ServiceClass/text() eq '"+Constants.SERVICE_CLASS+"'") .addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'") .addCondition("not($resource/Profile/GHN[@UniqueID='"+ctx.container().profile(HostingNode.class).id()+"'])"); //gcube/data/speciesproductsdiscovery/manager DiscoveryClient client = clientFor(GCoreEndpoint.class); addresses = client.submit(query); }catch(Exception e){ log.warn("error discoverying twin services",e); } log.trace("retieved "+addresses.size()+" gcore endpoints"); return addresses; } private void checkPlugin(AbstractPlugin plugin){ plugins.put(plugin.getRepositoryName(),plugin); for (Capabilities capability :plugin.getSupportedCapabilities()){ if (pluginsPerCapability.containsKey(capability)) pluginsPerCapability.get(capability).add(plugin); else { HashSet pluginsSet = new HashSet(); pluginsSet.add(plugin); pluginsPerCapability.put(capability, pluginsSet); } } } private void createCache(String pluginName){ try{ Cache pluginCache = new Cache( new CacheConfiguration(pluginName, CACHE_ENTRIES_PER_PLUGIN) .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU) .overflowToDisk(false) .eternal(false) .timeToLiveSeconds(60*60*24*7) .timeToIdleSeconds(0) .diskPersistent(true) .diskExpiryThreadIntervalSeconds(0) .diskStorePath(ctx.persistence().location())); pluginCache.getCacheEventNotificationService().registerListener(new MyCacheEventListener()); CacheManager.getInstance().addCache(pluginCache); log.trace("cache created for plugin "+ pluginName); }catch (ObjectExistsException e) { log.warn("the cache for plugin "+pluginName+" already exists"); log.trace("the size is "+ CacheManager.getInstance().getCache(pluginName).getSize()); } } private void initializePlugins(){ log.trace("initializing plugins"); if (loader==null){ log.warn("ServiceLoader is null intializing plugins"); loader=ServiceLoader.load(AbstractPlugin.class); } Map runtimeResourcePerPlugin = new HashMap(); try{ SimpleQuery query = queryFor(ServiceEndpoint.class); query.addCondition("$resource/Profile/Category/text() eq '"+RESOURCE_CATEGORY+"'"); DiscoveryClient client = clientFor(ServiceEndpoint.class); List resources = client.submit(query); for (ServiceEndpoint resource: resources) runtimeResourcePerPlugin.put(resource.profile().name(), resource); }catch(Exception e){ log.warn("error discoverying runtime resources",e); } retrievePlugins(runtimeResourcePerPlugin); retrieveRemotePlugins(); } public void removePlugin(String pluginName){ AbstractPlugin plugin = this.plugins.get(pluginName); for (Capabilities capability :plugin.getSupportedCapabilities()){ if (pluginsPerCapability.containsKey(capability)){ pluginsPerCapability.get(capability).remove(plugin); if (pluginsPerCapability.get(capability).size()==0) pluginsPerCapability.remove(capability); } } this.plugins.remove(pluginName); } public void removePlugins() { initializePlugins(); } public void removeRemotePlugin(String gCoreEndpointId) { List pluginToRemove = new ArrayList(); for (AbstractPlugin plugin : plugins.values()) if (plugin.isRemote()){ RemotePlugin rPlugin =(RemotePlugin) plugin; rPlugin.getRemoteUris().remove(gCoreEndpointId); if (rPlugin.getRemoteUris().isEmpty()) pluginToRemove.add(rPlugin.getRepositoryName()); } for (String pluginName: pluginToRemove){ log.info("removing remote plugin {}", pluginName); this.removePlugin(pluginName); } } public void shutdown(){ notifyRemoteServicesOnShutdown(); } private void notifyRemoteServicesOnShutdown(){ for (GCoreEndpoint address: retrieveTwinServicesAddresses()) { String endpointId = ctx.profile(GCoreEndpoint.class).id(); URI uri = null; try { for (Endpoint endpoint : address.profile().endpoints()) if (endpoint.name().equals("remote-dispatcher")){ uri = endpoint.uri(); break; } if (uri!=null){ //TODO : call remote rest service //RemoteDispatcher remoteDispatcher = org.gcube.data.spd.client.Constants.getRemoteDispatcherService(uri.toString()); //remoteDispatcher.removeAll(endpointId); } }catch (Throwable e) { log.warn("error contacting remote plugin hosted on a Whn id "+address.profile().ghnId()); continue; } } } }