001/**
002 * Copyright (C) 2014  Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/
003 *
004 * This file is part of Dicoogle/dicoogle.
005 *
006 * Dicoogle/dicoogle is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * Dicoogle/dicoogle is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with Dicoogle.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package pt.ua.dicoogle.plugins;
020
021import org.apache.commons.configuration.ConfigurationException;
022import org.apache.commons.io.FileUtils;
023import org.restlet.resource.ServerResource;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026import pt.ua.dicoogle.core.ServerSettings;
027import pt.ua.dicoogle.plugins.webui.WebUIPlugin;
028import pt.ua.dicoogle.plugins.webui.WebUIPluginManager;
029import pt.ua.dicoogle.sdk.*;
030import pt.ua.dicoogle.sdk.Utils.TaskQueue;
031import pt.ua.dicoogle.sdk.Utils.TaskRequest;
032import pt.ua.dicoogle.sdk.core.PlatformCommunicatorInterface;
033import pt.ua.dicoogle.sdk.datastructs.Report;
034import pt.ua.dicoogle.sdk.datastructs.SearchResult;
035import pt.ua.dicoogle.sdk.settings.ConfigurationHolder;
036import pt.ua.dicoogle.sdk.task.JointQueryTask;
037import pt.ua.dicoogle.sdk.task.Task;
038import pt.ua.dicoogle.server.ControlServices;
039import pt.ua.dicoogle.server.PluginRestletApplication;
040import pt.ua.dicoogle.server.web.DicoogleWeb;
041import pt.ua.dicoogle.taskManager.RunningIndexTasks;
042import pt.ua.dicoogle.taskManager.TaskManager;
043
044import javax.swing.*;
045import java.io.File;
046import java.io.IOException;
047import java.net.URI;
048import java.util.*;
049import java.util.concurrent.Callable;
050import java.util.concurrent.ExecutionException;
051import java.util.zip.ZipFile;
052
053/**
054 *
055 * PluginController is the core of the Plugins architecture.
056 *
057 * <p>
058 * It loads the plugins, takes care of the list of active plugins and control
059 * the tasks that are exchanged between plugins and core plugins
060 *
061 * @author Carlos Ferreira
062 * @author Frederico Valente
063 * @author Luís A. Bastião Silva <bastiao@ua.pt>
064 * @author Tiago Marques Godinho
065 * @author Eduardo Pinho
066 */
067public class PluginController{
068
069    private static final Logger logger = LoggerFactory.getLogger(PluginController.class);
070    private static PluginController instance;
071
072    public synchronized static PluginController getInstance() {
073        if (instance == null) {
074            instance = new PluginController(new File("Plugins"));
075        }
076        return instance;
077    }
078    private final Collection<PluginSet> pluginSets;
079    private File pluginFolder;
080    private TaskQueue tasks = null;
081
082        private PluginSet remoteQueryPlugins = null;
083    private final WebUIPluginManager webUI;
084    private final DicooglePlatformProxy proxy;
085    private TaskManager taskManager = new TaskManager(Integer.parseInt(System.getProperty("dicoogle.taskManager.nThreads", "4")));
086    
087    public PluginController(File pathToPluginDirectory) {
088        logger.info("Creating PluginController Instance");
089        pluginFolder = pathToPluginDirectory;
090
091        tasks = new TaskQueue();
092
093        //the plugin directory does not exist. lets create it
094        if (!pathToPluginDirectory.exists()) {
095                logger.info("Creating new Plugin Folder");
096            pathToPluginDirectory.mkdirs();
097        }
098
099        //loads the plugins
100        pluginSets = PluginFactory.getPlugins(pathToPluginDirectory);
101        //load web UI plugins (they are not Java, so the process is delegated to another entity)
102        this.webUI = new WebUIPluginManager();
103        // loadByPluginName all at "WebPlugins"
104        this.webUI.loadAll(new File("WebPlugins"));
105        
106        // go through each jar'd plugin and fetch their WebPlugins
107        for (File j : FileUtils.listFiles(pluginFolder, new String[]{"jar", "zip"}, false)) {
108            try {
109                this.webUI.loadAllFromZip(new ZipFile(j));
110            } catch (IOException ex) {
111                // ignore
112                logger.warn("Failed to load web UI plugins from {}: {}", j.getName(), ex.getMessage());
113            }
114        }
115        
116        logger.info("Loaded Local Plugins");
117
118        //loads plugins' settings and passes them to the plugin
119        File settingsFolder = new File(pluginFolder.getPath() + "/settings/");
120        if (!settingsFolder.exists()) {
121                logger.info("Creating Local Settings Folder");
122            settingsFolder.mkdir();
123        }
124
125        for (Iterator<PluginSet> it = pluginSets.iterator(); it.hasNext();) {
126            PluginSet plugin = it.next();
127            logger.info("Loading plugin: " + plugin.getName());
128            File pluginSettingsFile = new File(settingsFolder + "/" + plugin.getName().replace('/', '-') + ".xml");
129            try {
130                ConfigurationHolder holder = new ConfigurationHolder(pluginSettingsFile);
131                if(plugin.getName().equals("RemotePluginSet")) {
132                        this.remoteQueryPlugins = plugin;
133                        holder.getConfiguration().setProperty("NodeName", ServerSettings.getInstance().getNodeName());
134                        holder.getConfiguration().setProperty("TemporaryPath", ServerSettings.getInstance().getPath());
135                        
136                        logger.info("Started Remote Communications Manager");
137                }
138                applySettings(plugin, holder);
139            }
140            catch (ConfigurationException e){
141                logger.error("Failed to create configuration holder", e);
142                        }
143            catch (RuntimeException e) {
144                logger.error("Unexpected error while loading plugin set {}. Plugin disabled.", plugin.getName(), e);
145                it.remove();
146            }
147        }
148        logger.info("Settings pushed to plugins");
149        webUI.loadSettings(settingsFolder);
150        logger.info("Settings pushed to web UI plugins");
151        
152        pluginSets.add(new DefaultFileStoragePlugin());
153        logger.info("Added default storage plugin");
154        
155        this.proxy = new DicooglePlatformProxy(this);
156        
157        initializePlugins(pluginSets);
158        initRestInterface(pluginSets);
159        initJettyInterface(pluginSets);
160        logger.info("Initialized plugins");
161    }
162    
163    private void initializePlugins(Collection<PluginSet> plugins) {
164        for (PluginSet set : plugins) {
165            logger.debug("SetPlugins: {}", set);
166            
167            // provide platform to each plugin interface
168            final Collection<Collection<?>> all = Arrays.asList(
169                    set.getStoragePlugins(),
170                    set.getIndexPlugins(),
171                    set.getQueryPlugins(),
172                    set.getJettyPlugins(),
173                    set.getRestPlugins()
174            );
175            for (Collection interfaces : all) {
176                if (interfaces == null) {
177                    logger.debug("Plugin set {} provided a null collection!");
178                    continue;
179                }
180                for (Object o : interfaces) {
181                    if (o instanceof PlatformCommunicatorInterface) {
182                        ((PlatformCommunicatorInterface)o).setPlatformProxy(proxy);
183                    }
184                }
185            }
186
187            // and to the set itself
188            if (set instanceof PlatformCommunicatorInterface) {
189                ((PlatformCommunicatorInterface) set).setPlatformProxy(proxy);
190            }
191        }
192    }
193    
194    private void applySettings(PluginSet set, ConfigurationHolder holder) {
195        // provide platform to each plugin interface
196        final Collection<Collection<? extends DicooglePlugin>> all = Arrays.asList(
197                set.getStoragePlugins(),
198                set.getIndexPlugins(),
199                set.getQueryPlugins(),
200                set.getJettyPlugins()
201        );
202        for (Collection<? extends DicooglePlugin> interfaces : all) {
203            if (interfaces == null) continue;
204            for (DicooglePlugin p : interfaces) {
205                p.setSettings(holder);
206            }
207        }
208        set.setSettings(holder);
209
210    }
211    
212    /**
213     * Each pluginSet provides a collection of barebone rest interfaces Here we
214     * check which interfaces are present and create a restlet component to
215     * handle them. also we export them using common settings and security
216     * profiles
217     */
218    private void initRestInterface(Collection<PluginSet> plugins) {
219        logger.info("Initializing plugin rest interfaces");
220
221        ArrayList<ServerResource> restInterfaces = new ArrayList<>();
222        for (PluginSet set : plugins) {
223            Collection<ServerResource> restInterface = set.getRestPlugins();
224            if (restInterface == null) {
225                continue;
226            }
227            restInterfaces.addAll(restInterface);
228        }
229
230        for (ServerResource resource : restInterfaces) {
231            PluginRestletApplication.attachRestPlugin(resource);
232        }
233        logger.info("Finished initializing rest interfaces");
234    }
235
236    private void initJettyInterface(Collection<PluginSet> plugins) {
237        logger.info("Initializing jetty interface");
238                
239         ArrayList<JettyPluginInterface> jettyInterfaces = new ArrayList<>();
240         for(PluginSet set : plugins){
241                 Collection<JettyPluginInterface> jettyInterface = set.getJettyPlugins();
242                 if(jettyInterface == null) continue;
243                 jettyInterfaces.addAll(jettyInterface);
244         }
245         
246         DicoogleWeb jettyServer = ControlServices.getInstance().getWebServicePlatform();
247         for(JettyPluginInterface resource : jettyInterfaces){
248                 jettyServer.addContextHandlers( resource.getJettyHandlers() );
249         }
250    }
251
252    /**
253     * Stops the plugins and saves the settings
254     */
255    public void shutdown() throws IOException {
256        for (PluginSet plugin : pluginSets) {
257            //TODO: I Think it is better to enable auto-save settings
258            /*Settings settings = plugin.getSettings();
259            if (settings != null) {
260                settings.save();
261            }
262        */
263            //lets the plugin know we are shutting down
264            plugin.shutdown();
265        }
266    }
267
268    /**
269     * stops a pluginset. this could be more efficient, however this is hardly a
270     * bottleneck TODO: needs more granularity, we should be able to stop only
271     * the indexers or the queryers
272     *
273     * @param pluginName
274     */
275    public void stopPlugin(String pluginName) {
276        for (PluginSet pluginSet : pluginSets) {
277            if (pluginSet.getName().compareTo(pluginName) == 0) {
278                //pluginSet.stop();
279                return;
280            }
281        }
282    }
283
284    public void startPlugin(String pluginName) {
285        for (PluginSet pluginSet : pluginSets) {
286            if (pluginSet.getName().compareTo(pluginName) == 0) {
287                //pluginSet.stop();
288                return;
289            }
290        }
291    }
292
293    public Collection<IndexerInterface> getIndexingPlugins(boolean onlyEnabled) {
294        ArrayList<IndexerInterface> indexers = new ArrayList<>();
295        for (PluginSet pSet : pluginSets) {
296            for (IndexerInterface index : pSet.getIndexPlugins()) {
297                if (!index.isEnabled() && onlyEnabled) {
298                    continue;
299                }
300                indexers.add(index);
301            }
302        }
303        return indexers;
304    }
305
306    public Collection<StorageInterface> getStoragePlugins(boolean onlyEnabled) {
307        ArrayList<StorageInterface> storagePlugins = new ArrayList<>();
308        for (PluginSet pSet : pluginSets) {
309            for (StorageInterface store : pSet.getStoragePlugins()) {
310                if (!store.isEnabled() && onlyEnabled) {
311                    continue;
312                }
313                storagePlugins.add(store);
314            }
315        }
316        return storagePlugins;
317    }
318
319    /**
320     * Resolve a URI to a DicomInputStream
321     * @param location
322     * @param args
323     * @return 
324     */
325    public Iterable<StorageInputStream> resolveURI(URI location, Object ...args)
326    {
327        Collection<StorageInterface> storages = getStoragePlugins(true);
328        
329        for (StorageInterface store : storages) {
330            if (store.handles(location)) 
331            {
332                logger.debug("Resolving URI: {} Storage: {}", location, store.getName());
333                return store.at(location, args);
334            }
335        }
336
337        logger.error("Could not resolve uri: {}", location);
338        return Collections.emptyList();    
339    }
340    
341    /** Retrieve a storage interface capable of handling files on a given location.
342     * 
343     * TODO: this can be heavily improved if we keep a map of scheme->indexer
344     * However we are not supposed to call this every other cycle.
345     *
346     * TODO: we should return a proxy storage that always returns error
347     * 
348     * @todo "schema" is a typo, should read "scheme"
349     * 
350     * @param location a URI of the location, only the scheme matters
351     * @return a storage interface capable of handling the location, null if no suitable plugin is found
352     */
353    public StorageInterface getStorageForSchema(URI location) {
354        if(location == null){
355            logger.warn("URI for retrieving storage interface is null, ignoring");
356            return null;
357        }
358        Collection<StorageInterface> storages = getStoragePlugins(false);
359        
360        for (StorageInterface store : storages) {
361            try {
362                if (store.handles(location)) {
363                    logger.debug("Retrieved storage for scheme: {}", location);
364                    return store;
365                }
366            } catch (RuntimeException ex) {
367                logger.warn("Storage plugin {} failed unexpectedly", store.getName(), ex);
368            }
369        }
370        logger.warn("Could not get storage for scheme: {}", location);
371        return null;
372    }
373    
374    /** Retrieve a storage interface capable of handling files with the given scheme.
375     * 
376     * TODO: this can be heavily improved if we keep a map of scheme->indexer
377     * However we are not supposed to call this every other cycle.
378     *
379     * TODO: we should return a proxy storage that always returns error
380     * 
381     * @param scheme a URI of the location, only the scheme matters
382     * @return a storage interface capable of handling the location, null if no suitable plugin is found
383     */
384    public StorageInterface getStorageForSchema(String scheme) {
385        URI uri = URI.create(scheme + ":/");
386        return getStorageForSchema(uri);
387    }
388
389    public Collection<QueryInterface> getQueryPlugins(boolean onlyEnabled) {
390        ArrayList<QueryInterface> queriers = new ArrayList<>();
391        for (PluginSet pSet : pluginSets) {
392            for (QueryInterface querier : pSet.getQueryPlugins()) {
393                if (!querier.isEnabled() && onlyEnabled) {
394                    continue;
395                }
396                queriers.add(querier);
397            }
398        }
399        return queriers;
400    }
401
402    public void addTask(TaskRequest task) {
403        this.tasks.addTask(task);
404    }
405   
406
407    
408    public List<String> getQueryProvidersName(boolean enabled){
409        Collection<QueryInterface> plugins = getQueryPlugins(enabled);
410        List<String> names = new ArrayList<>(plugins.size());
411        for(QueryInterface p : plugins){
412                names.add(p.getName());
413        }
414        //logger.info("Query Providers: "+Arrays.toString(names.toArray()) );
415        return names;
416    }
417    
418    public QueryInterface getQueryProviderByName(String name, boolean onlyEnabled){
419        Collection<QueryInterface> plugins = getQueryPlugins(onlyEnabled);
420        for(QueryInterface p : plugins){
421                if(p.getName().equalsIgnoreCase(name)){
422                        //logger.info("Retrived Query Provider: "+name);
423                        return p;
424                }
425        }
426        logger.error("Could not retrieve query provider {} for onlyEnabled = {}", name, onlyEnabled);
427        return null;
428    }
429    
430    //TODO: CONVENIENCE METHOD
431    public IndexerInterface getIndexerByName(String name, boolean onlyEnabled){
432        Collection<IndexerInterface> plugins = getIndexingPlugins(onlyEnabled);
433        for(IndexerInterface p : plugins){
434                if(p.getName().equalsIgnoreCase(name)){
435                        //logger.info("Retrived Query Provider: "+name);
436                        return p;
437                }
438        }
439        logger.error("No indexer matching name {} for onlyEnabled = {}", name, onlyEnabled);
440        return null;
441    }
442    
443    public JointQueryTask queryAll(JointQueryTask holder, final String query, final Object ... parameters)
444    {
445        //logger.info("Querying all providers");
446        List<String> providers = this.getQueryProvidersName(true);
447        
448        return query(holder, providers, query, parameters);        
449    }
450    
451    public Task<Iterable<SearchResult>> query(String querySource, final String query, final Object ... parameters){
452        Task<Iterable<SearchResult>> t = getTaskForQuery(querySource, query, parameters);       
453        taskManager.dispatch(t);
454        //logger.info("Fired Query Task: "+querySource +" QueryString:"+query);
455        
456        return t;//returns the handler to obtain the computation results
457    }
458    
459    public JointQueryTask query(JointQueryTask holder, List<String> querySources, final String query, final Object ... parameters){
460        if(holder == null)
461                return null;
462        
463        List<Task<Iterable<SearchResult>>> tasks = new ArrayList<>();
464        for(String p : querySources){
465                Task<Iterable<SearchResult>> task = getTaskForQuery(p, query, parameters);
466                tasks.add(task);
467                holder.addTask(task);
468        }
469
470        //and executes said task asynchronously
471        for(Task<?> t : tasks)
472                taskManager.dispatch(t);
473
474        //logger.info("Fired Query Tasks: "+Arrays.toString(querySources.toArray()) +" QueryString:"+query);
475        return holder;//returns the handler to obtain the computation results
476    }
477    
478    private Task<Iterable<SearchResult>> getTaskForQuery(final String querySource, final String query, final Object ... parameters){
479        final QueryInterface queryEngine = getQueryProviderByName(querySource, true);
480        //returns a tasks that runs the query from the selected query engine
481        Task<Iterable<SearchResult>> queryTask = new Task<>(querySource,
482            new Callable<Iterable<SearchResult>>(){
483            @Override public Iterable<SearchResult> call() throws Exception {
484                if(queryEngine == null) return Collections.emptyList();
485                try {
486                    return queryEngine.query(query, parameters);
487                } catch (RuntimeException ex) {
488                    logger.warn("Query plugin {} failed unexpectedly", querySource, ex);
489                    return Collections.EMPTY_LIST;
490                }
491            }
492        });
493        //logger.info("Prepared Query Task: QueryString");
494        return queryTask;
495    }        
496 
497    /*
498     * Given an URI (which may be a path to a dir or file, a web resource or whatever)
499     * this method creates a task that
500     * calls the appropriate indexers and instructs them to index the data pointed to by the URI
501     * it is up to the caller to run the task asynchronously by feeding it to an executor
502     * or in a blocking way by calling the get() method of the task
503     */
504    public List<Task<Report>> index(URI path) {
505        logger.info("Starting Indexing procedure for {}", path.toString());
506        StorageInterface store = getStorageForSchema(path);
507
508        if(store==null){ 
509            logger.error("No storage plugin detected, ignoring index request");
510            return Collections.emptyList(); 
511        }
512        
513        Collection<IndexerInterface> indexers= getIndexingPlugins(true);
514        //Collection<IndexerInterface> indexers = getIndexingPluginsByMimeType(path);
515        ArrayList<Task<Report>> rettasks = new ArrayList<>();
516        final  String pathF = path.toString();
517        for(IndexerInterface indexer : indexers){
518            try {
519                Task<Report> task = indexer.index(store.at(path));
520                if(task == null) continue;
521                final String taskUniqueID = UUID.randomUUID().toString();
522                task.setName(String.format("[%s]index %s", indexer.getName(), path));
523                task.onCompletion(new Runnable() {
524                    @Override
525                    public void run() {
526                        logger.info("Task [{}] complete: {} is indexed", taskUniqueID, pathF);
527                    }
528                });
529
530                taskManager.dispatch(task);
531                rettasks.add(task);
532                RunningIndexTasks.getInstance().addTask(taskUniqueID, task);
533            } catch (RuntimeException ex) {
534                logger.warn("Indexer {} failed unexpectedly", indexer.getName(), ex);
535            }
536        }
537        logger.info("Finished firing all indexing plugins for {}", path);
538        
539        return rettasks;        
540    }
541    
542    //
543    public List<Task<Report>> index(String pluginName, URI path) {
544        logger.info("Starting Indexing procedure for {}", path);
545        StorageInterface store = getStorageForSchema(path);
546
547        if(store==null){ 
548                logger.error("No storage plugin detected, ignoring index request");
549            return Collections.emptyList(); 
550        }
551        
552        final String taskUniqueID = UUID.randomUUID().toString();
553        
554        IndexerInterface indexer = getIndexerByName(pluginName, true);
555        ArrayList<Task<Report>> rettasks = new ArrayList<>();
556        final  String pathF = path.toString();
557        try {
558            Task<Report> task = indexer.index(store.at(path));
559            if (task != null) {
560                task.setName(String.format("[%s]index %s", pluginName, path));
561                task.onCompletion(new Runnable() {
562
563                    @Override
564                    public void run() {
565                        logger.info("Task [{}] complete: {} is indexed", taskUniqueID, pathF);
566
567                        //RunningIndexTasks.getInstance().removeTask(taskUniqueID);
568                    }
569                });
570
571                taskManager.dispatch(task);
572
573                rettasks.add(task);
574                logger.info("Fired indexer {} for URI {}", pluginName, path.toString());
575                RunningIndexTasks.getInstance().addTask(taskUniqueID, task);
576            }
577        } catch (RuntimeException ex) {
578            logger.warn("Indexer {} failed unexpectedly", indexer.getName(), ex);
579        }
580
581        return rettasks;        
582    }
583
584    public void unindex(URI path) {
585        logger.info("Starting unindexing procedure for {}", path.toString());
586        this.doUnindex(path, this.getIndexingPlugins(true));
587    }
588
589    /** Issue the removal of indexed entries in a path from the given indexers.
590     * 
591     * @param path the URI of the directory or file to unindex
592     * @param indexProviders a collection of providers
593     */
594    public void unindex(URI path, Collection<String> indexProviders) {
595        logger.info("Starting unindexing procedure for {}", path);
596        
597        if (indexProviders != null) {
598            List<IndexerInterface> indexers = new ArrayList<>();
599            for (String provider : indexProviders) {
600                indexers.add(this.getIndexerByName(provider, true));
601            }
602            this.doUnindex(path, indexers);
603        } else {
604            this.doUnindex(path, this.getIndexingPlugins(true));
605        }
606    }
607    
608    /** Issue an unindexation procedure to the given indexers.
609     * 
610     * @param path the URI of the directory or file to unindex
611     * @param indexers a collection of providers
612     */
613    private void doUnindex(URI path, Collection<IndexerInterface> indexers) {
614        for (IndexerInterface indexer : indexers) {
615            try {
616                indexer.unindex(path);
617            } catch (RuntimeException ex) {
618                logger.warn("Indexer {} failed unexpectedly", indexer.getName(), ex);
619            }
620        }
621        logger.info("Finished unindexing {}", path);
622    }
623    
624    public void remove(URI uri){
625      StorageInterface si = getStorageForSchema(uri);
626      if(si != null)
627        doRemove(uri, si);
628      else
629        logger.error("Could not find storage plugin to handle URI: {}", uri);      
630    }
631    
632    public void doRemove(URI uri, StorageInterface si) {
633        try {
634            if (si.handles(uri)) {
635                si.remove(uri);
636            } else {
637                logger.warn("Storage Plugin does not handle URI: {},{}", uri, si);
638            }
639            logger.info("Finished removing {}", uri);
640        } catch (RuntimeException ex) {
641            logger.warn("Storage {} failed unexpectedly", si.getName(), ex);
642        }
643    }
644
645    /*
646     * Convenience method that calls index(URI) and runs the returned
647     * tasks on the executing thread 
648     */
649    public List<Report> indexBlocking(URI path) {
650        logger.info("Starting indexing blocking procedure for {}", path);
651        List<Task<Report>> ret = index(path);
652        
653        ArrayList<Report> reports = new ArrayList<>(ret.size());
654        for(Task<Report> t : ret){
655                try {
656                                reports.add(t.get());
657                        }
658            catch (InterruptedException | ExecutionException e) {
659                logger.error(e.getMessage(), e);
660                        } catch (RuntimeException e) {
661                logger.warn("Indexer task failed unexpectedly", e);
662            }
663        }
664        logger.info("Finished indexing {}", path);
665        
666        return reports;
667    }
668
669    //METHODs FOR PluginController4Users
670    //TODO:this method is a workaround! we do get rightmenu items, but only for the search window
671    //which should be moved to plugins and hence we are assuming too much in here!
672 
673    @Deprecated
674        public List<JMenuItem> getRightButtonItems() {
675        logger.info("getRightButtonItems()");
676        ArrayList<JMenuItem> rightMenuItems = new ArrayList<>();
677        
678        for (PluginSet set : pluginSets) {
679            logger.info("Set plugins: {}", set.getGraphicalPlugins());
680            Collection<GraphicalInterface> graphicalPlugins = set.getGraphicalPlugins();
681            if (graphicalPlugins == null) {
682                continue;
683            }
684            logger.info("Looking for plugin");
685            for (GraphicalInterface gpi : graphicalPlugins) {
686                logger.info("GPI: {}", gpi);
687                ArrayList<JMenuItem> rbPanels = gpi.getRightButtonItems();
688                if (rbPanels == null) {
689                    continue;
690                }
691                rightMenuItems.addAll(rbPanels);
692            }
693        }
694        return rightMenuItems;
695    }
696
697    //returns a list of tabs from all plugins
698    @Deprecated
699    public List<JPanel> getTabItems() {
700        logger.info("getTabItems");
701        ArrayList<JPanel> panels = new ArrayList<>();
702
703        for (PluginSet set : pluginSets) {
704            Collection<GraphicalInterface> graphicalPlugins = set.getGraphicalPlugins();
705            if (graphicalPlugins == null) {
706                continue;
707            }
708            for (GraphicalInterface gpi : graphicalPlugins) {
709                ArrayList<JPanel> tPanels = gpi.getTabPanels();
710                if (tPanels == null) {
711                    continue;
712                }
713                panels.addAll(tPanels);
714            }
715        }
716        return panels;
717    }
718
719    @Deprecated
720    public List<JMenuItem> getMenuItems() {
721        logger.info("getMenuItems");
722        ArrayList<JMenuItem> items = new ArrayList<>();
723
724        for (PluginSet set : pluginSets) {
725            Collection<GraphicalInterface> graphicalPlugins = set.getGraphicalPlugins();
726            if (graphicalPlugins == null) {
727                continue;
728            }
729
730            for (GraphicalInterface gpi : graphicalPlugins) {
731                Collection<JMenuItem> setItems = gpi.getMenuItems();
732                if (setItems == null) {
733                    continue;
734                }
735                items.addAll(setItems);
736            }
737        }
738        return items;
739    }
740    
741    // Methods for Web UI 
742
743    /** Retrieve all web UI plugin descriptors for the given slot id.
744     * 
745     * @param ids the slot id's for the plugin ("query", "result", "menu", ...), empty or null for any slot
746     * @return a collection of web UI plugins.
747     */
748    public Collection<WebUIPlugin> getWebUIPlugins(String... ids) {
749        logger.debug("getWebUIPlugins(slot ids: {})", ids != null ? Arrays.asList(ids) : "<any>");
750        List<WebUIPlugin> plugins = new ArrayList<>();
751        Set<String> idSet = Collections.EMPTY_SET;
752        if (ids != null) {
753            idSet = new HashSet<>(Arrays.asList(ids));
754        }
755        for (WebUIPlugin plugin : webUI.pluginSet()) {
756            if (!plugin.isEnabled()) {
757                continue;
758            }
759            if (idSet.isEmpty() || idSet.contains(plugin.getSlotId())) {
760                plugins.add(plugin);
761            }
762        }
763        return plugins;
764    }
765    
766    /** Retrieve the web UI plugin descriptor of the plugin with the given name.
767     * 
768     * @param name the unique name of the plugin
769     * @return a web UI plugin descriptor object, or null if no such plugin exists or is inactive
770     */
771    public WebUIPlugin getWebUIPlugin(String name) {
772        logger.debug("getWebUIPlugin(name: {})", name);
773        WebUIPlugin plugin = webUI.get(name);
774        return plugin == null ? null
775                : plugin.isEnabled() ? plugin : null;
776    }
777
778    /** Retrieve the web UI plugin descriptor package.json.
779     * 
780     * @param name the unique name of the plugin
781     * @return the full contents of the package.json, null if the plugin is not available
782     */
783    public String getWebUIPackageJSON(String name) {
784        logger.debug("getWebUIPackageJSON(name: {})", name);
785        try {
786            Object o = webUI.retrieveJSON(name);
787            return (o != null)
788                    ? o.toString()
789                    : null;
790        } catch (IOException ex) {
791            logger.error("Failed to retrieve package JSON", ex);
792            return null;
793        }
794    }
795
796    /** Retrieve the web UI plugin module code.
797     * 
798     * @param name the unique name of the plugin
799     * @return the full contents of the module file, null if the plugin is not available
800     */
801    public String getWebUIModuleJS(String name) {
802        logger.debug("getWebUIModuleJS(name: {})", name);
803        try {
804            return webUI.retrieveModuleJS(name);
805        } catch (IOException ex) {
806            logger.error("Failed to retrieve module", ex);
807            return null;
808        }
809    }
810
811    //METHODS FOR SERVICE:JAVA
812    /**
813     *
814     * TODO: REVIEW! BELOW
815     *
816     * Checks if the plugin exists and has advanced/internal settings.
817     *
818     * @param pluginName the name of the plugin.
819     * @return true if the plugin exists and has at least one advance/internal
820     * settings, false otherwise.
821     */
822    public boolean hasAdvancedSettings(String pluginName) {
823        return false;
824    }
825
826    public HashMap<String, String> getAdvancedSettingsHelp(String pluginName) {
827        return null;
828    }
829}