001package org.intellimate.izou.system.context;
002
003import org.intellimate.izou.activator.ActivatorModel;
004import org.intellimate.izou.addon.AddOnModel;
005import org.intellimate.izou.events.*;
006import org.intellimate.izou.identification.Identifiable;
007import org.intellimate.izou.identification.Identification;
008import org.intellimate.izou.identification.IdentificationManager;
009import org.intellimate.izou.identification.IllegalIDException;
010import org.intellimate.izou.main.Main;
011import org.intellimate.izou.output.OutputExtensionModel;
012import org.intellimate.izou.output.OutputPluginModel;
013import org.intellimate.izou.resource.ResourceModel;
014import org.intellimate.izou.resource.ResourceBuilderModel;
015import org.intellimate.izou.system.Context;
016import org.intellimate.izou.system.file.FileSubscriber;
017import org.intellimate.izou.system.file.ReloadableFile;
018import org.intellimate.izou.system.logger.IzouLogger;
019import org.intellimate.izou.threadpool.TrackingExecutorService;
020import org.apache.logging.log4j.LogManager;
021import org.apache.logging.log4j.spi.ExtendedLogger;
022
023import java.io.File;
024import java.io.IOException;
025import java.nio.file.Path;
026import java.util.List;
027import java.util.Optional;
028import java.util.concurrent.CompletableFuture;
029import java.util.concurrent.ExecutorService;
030import java.util.function.Consumer;
031
032/**
033 * This class provides much of the general Communication with Izou.
034 */
035public class ContextImplementation implements Context {
036    private final AddOnModel addOn;
037    private final Main main;
038    private final Events events = new EventsImpl();
039    private final Resources resources = new ResourcesImpl();
040    private final Files files;
041    private final ExtendedLogger logger;
042    private final ThreadPool threadPool;
043    private final Activators activators = new ActivatorsImpl();
044    private final Output output = new OutputImpl();
045    private final System system = new SystemImpl();
046
047    /**
048     * creates a new context for the addOn
049     *
050     * A context contains all the "global" or generally necessary information an addOn might need that it otherwise does
051     * not have access too
052     *
053     * @param addOn the addOn for which to create a new context
054     * @param main instance of main
055     * @param logLevel the logLevel to initialize the IzouLogger with
056     */
057    public ContextImplementation(AddOnModel addOn, Main main, String logLevel) {
058        this.addOn = addOn;
059        this.main = main;
060        this.files = new FilesImpl();
061        this.threadPool = new ThreadPoolImpl();
062
063        IzouLogger izouLogger = main.getIzouLogger();
064        ExtendedLogger logger = null;
065        if (izouLogger != null)
066            this.logger = izouLogger.createFileLogger(addOn.getID(), logLevel);
067        else {
068            this.logger = null;
069            org.apache.logging.log4j.Logger fileLogger = LogManager.getLogger(this.getClass());
070            fileLogger.error("IzouLogger has not been initialized");
071            throw new NullPointerException("IzouLogger has not been initialized");
072        }
073    }
074
075    /**
076     * returns the API used for interaction with Events
077     * @return Events
078     */
079    @Override
080    public Events getEvents() {
081        return events;
082    }
083
084    /**
085     * returns the API used for interaction with Resource
086     * @return Resource
087     */
088    @Override
089    public Resources getResources() {
090        return resources;
091    }
092
093    /**
094     * returns the API used for interaction with Files
095     * @return Files
096     */
097    @Override
098    public Files getFiles() {
099        return files;
100    }
101
102    /**
103     * gets logger for addOn
104     *
105     * @return the logger
106     */
107    @Override
108    public ExtendedLogger getLogger() {
109        return logger;
110    }
111
112    /**
113     * returns the API used to manage the ThreadPool
114     * @return ThreadPool
115     */
116    @Override
117    public ThreadPool getThreadPool() {
118        return threadPool;
119    }
120
121    /**
122     * returns the API to manage the Activators
123     * @return Activator
124     */
125    @Override
126    public Activators getActivators() {
127        return activators;
128    }
129
130    /**
131     * returns the API used to manage the OutputPlugins and OutputExtensions
132     * @return Output
133     */
134    @Override
135    public Output getOutput() {
136        return output;
137    }
138
139    /**
140     * retruns the API used to interact with Izou.
141     *
142     * @return System.
143     */
144    @Override
145    public System getSystem() {
146        return system;
147    }
148
149    /**
150     * gets addOn
151     *
152     * @return the addOn
153     */
154    @Override
155    public AddOnModel getAddOn() {
156        return addOn;
157    }
158
159    private class FilesImpl implements Files {
160        /**
161         * Use this method to register a file with the watcherService
162         *
163         * @param dir directory of file
164         * @param fileType the name/extension of the file
165         *                 IMPORTANT: Please try to always enter the full name with extension of the file (Ex: "test.txt"),
166         *                 it would be best if the fileType is the full file name, and that the file name is clearly
167         *                 distinguishable from other files.
168         *                 For example, the property files are stored with the ID of the addon they belong too. That way
169         *                 every property file is easily distinguishable.
170         * @param reloadableFile object of interface that file belongs to
171         * @throws IOException exception thrown by watcher service
172         */
173        @Override
174        public void registerFileDir(Path dir, String fileType, ReloadableFile reloadableFile) throws IOException {
175            main.getFileManager().registerFileDir(dir, fileType, reloadableFile);
176        }
177
178        /**
179         * Writes default file to real file
180         * The default file would be a file that can be packaged along with the code, from which a real file (say a
181         * properties file for example) can be loaded. This is useful because there are files (like property files0 that
182         * cannot be shipped with the package and have to be created at runtime. To still be able to fill these files, you
183         * can create a default file (usually txt) from which the content, as mentioned above, can then be loaded into the
184         * real file.
185         *
186         * @param defaultFilePath path to default file (or where it should be created)
187         * @param realFilePath path to real file (that should be filled with content of default file)
188         * @return true if operation has succeeded, else false
189         */
190        @Override
191        public boolean writeToFile(String defaultFilePath, String realFilePath) {
192            return main.getFileManager().writeToFile(defaultFilePath, realFilePath);
193        }
194
195        /**
196         * Creates a default File in case it does not exist yet. Default files can be used to load other files that are
197         * created at runtime (like properties file)
198         *
199         * @param defaultFilePath path to default file.txt (or where it should be created)
200         * @param initMessage the string to write in default file
201         * @throws java.io.IOException is thrown by bufferedWriter
202         */
203        @Override
204        public void createDefaultFile(String defaultFilePath, String initMessage) throws IOException {
205            main.getFileManager().createDefaultFile(defaultFilePath, initMessage);
206        }
207
208        /**
209         * Registers a {@link FileSubscriber} with a {@link ReloadableFile}. So when the {@code reloadableFile} is
210         * reloaded, the fileSubscriber will be notified. Multiple file subscribers can be registered with the same
211         * reloadable file.
212         *
213         * @param reloadableFile the reloadable file that should be observed
214         * @param fileSubscriber the fileSubscriber that should be notified when the reloadable file is reloaded
215         * @param identification the Identification of the requesting instance
216         * @throws IllegalIDException not yet implemented
217         */
218        @Override
219        public void register(ReloadableFile reloadableFile, FileSubscriber fileSubscriber,
220                             Identification identification) throws IllegalIDException {
221            main.getFilePublisher().register(reloadableFile, fileSubscriber, identification);
222        }
223
224        /**
225         * Registers a {@link FileSubscriber} so that whenever any file is reloaded, the fileSubscriber is notified.
226         *
227         * @param fileSubscriber the fileSubscriber that should be notified when the reloadable file is reloaded
228         * @param identification the Identification of the requesting instance
229         * @throws IllegalIDException not yet implemented
230         */
231        @Override
232        public void register(FileSubscriber fileSubscriber, Identification identification) throws IllegalIDException {
233           main.getFilePublisher().register(fileSubscriber, identification);
234        }
235
236        /**
237         * Unregisters all instances of fileSubscriber found.
238         *
239         * @param fileSubscriber the fileSubscriber to unregister
240         */
241        @Override
242        public void unregister(FileSubscriber fileSubscriber) {
243           main.getFilePublisher().unregister(fileSubscriber);
244        }
245
246        /**
247         * gets the File pointing towards the location of the lib-folder
248         *
249         * @return the File
250         */
251        @Override
252        public File getLibLocation() {
253            return main.getFileSystemManager().getLibLocation();
254        }
255
256        /**
257         * gets the File pointing towards the location of the resource-folder
258         *
259         * @return the File
260         */
261        @Override
262        public File getResourceLocation() {
263            return main.getFileSystemManager().getResourceLocation();
264        }
265
266        /**
267         * gets the File pointing towards the location of the properties-folder
268         *
269         * @return the File
270         */
271        @Override
272        public File getPropertiesLocation() {
273            return main.getFileSystemManager().getPropertiesLocation();
274        }
275
276        /**
277         * gets the File pointing towards the location of the logs-folder
278         *
279         * @return the File
280         */
281        @Override
282        public File getLogsLocation() {
283            return main.getFileSystemManager().getLogsLocation();
284        }
285    }
286
287    private class EventsImpl implements Events {
288        public EventsDistributor eventsDistributor = new DistributorImpl();
289        /**
290         * Adds an listener for events.
291         * <p>
292         * Be careful with this method, it will register the listener for ALL the informations found in the Event. If your
293         * event-type is a common event type, it will fire EACH time!.
294         * It will also register for all Descriptors individually!
295         * It will also ignore if this listener is already listening to an Event.
296         * Method is thread-safe.
297         * </p>
298         * @param event the Event to listen to (it will listen to all descriptors individually!)
299         * @param eventListener the ActivatorEventListener-interface for receiving activator events
300         * @throws IllegalIDException not yet implemented
301         */
302        @SuppressWarnings("JavaDoc")
303        @Override
304        public void registerEventListener(EventModel event, EventListenerModel eventListener) throws IllegalIDException {
305            main.getEventDistributor().registerEventListener(event, eventListener);
306        }
307
308        /**
309         * Adds an listener for events.
310         * <p>
311         * It will register for all ids individually!
312         * This method will ignore if this listener is already listening to an Event.
313         * Method is thread-safe.
314         * </p>
315         * @param ids this can be type, or descriptors etc.
316         * @param eventListener the ActivatorEventListener-interface for receiving activator events
317         */
318        @Override
319        public void registerEventListener(List<String> ids, EventListenerModel eventListener) {
320            main.getEventDistributor().registerEventListener(ids, eventListener);
321        }
322        /**
323         * unregister an EventListener
324         *<p>
325         * It will unregister for all Descriptors individually!
326         * It will also ignore if this listener is not listening to an Event.
327         * Method is thread-safe.
328         *
329         * @param event the Event to stop listen to
330         * @param eventListener the ActivatorEventListener used to listen for events
331         * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed
332         */
333        @Override
334        public void unregisterEventListener(EventModel event, EventListenerModel eventListener) {
335            main.getEventDistributor().unregisterEventListener(event, eventListener);
336        }
337
338        /**
339         * unregister an EventListener that gets called before the generation of the resources and the outputPlugins.
340         * <p>
341         * It will unregister for all Descriptors individually!
342         * It will also ignore if this listener is not listening to an Event.
343         * Method is thread-safe.
344         *
345         * @param eventListener the ActivatorEventListener used to listen for events
346         * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed
347         */
348        @Override
349        public void unregisterEventListener(EventListenerModel eventListener) {
350            main.getEventDistributor().unregisterEventListener(eventListener);
351        }
352
353        /**
354         * Adds an listener for events that gets called when the event finished processing.
355         * <p>
356         * Be careful with this method, it will register the listener for ALL the informations found in the Event. If your
357         * event-type is a common event type, it will fire EACH time!.
358         * It will also register for all Descriptors individually!
359         * It will also ignore if this listener is already listening to an Event.
360         * Method is thread-safe.
361         * </p>
362         *
363         * @param event         the Event to listen to (it will listen to all descriptors individually!)
364         * @param eventListener the ActivatorEventListener-interface for receiving activator events
365         * @throws IllegalIDException not yet implemented
366         */
367        @Override
368        public void registerEventFinishedListener(EventModel event, EventListenerModel eventListener) throws IllegalIDException {
369            main.getEventDistributor().registerEventFinishedListener(event, eventListener);
370        }
371
372        /**
373         * Adds an listener for events that gets called when the event finished processing.
374         * <p>
375         * It will register for all ids individually!
376         * This method will ignore if this listener is already listening to an Event.
377         * Method is thread-safe.
378         * </p>
379         *
380         * @param ids           this can be type, or descriptors etc.
381         * @param eventListener the ActivatorEventListener-interface for receiving activator events
382         */
383        @Override
384        public void registerEventFinishedListener(List<String> ids, EventListenerModel eventListener) {
385            main.getEventDistributor().registerEventFinishedListener(ids, eventListener);
386        }
387
388        /**
389         * unregister an EventListener that got called when the event finished processing.
390         * <p>
391         * It will unregister for all Descriptors individually!
392         * It will also ignore if this listener is not listening to an Event.
393         * Method is thread-safe.
394         *
395         * @param event         the Event to stop listen to
396         * @param eventListener the ActivatorEventListener used to listen for events
397         * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed
398         */
399        @Override
400        public void unregisterEventFinishedListener(EventModel event, EventListenerModel eventListener) {
401            main.getEventDistributor().unregisterEventFinishedListener(event, eventListener);
402        }
403
404        /**
405         * unregister an EventListener that got called when the event finished processing.
406         * <p>
407         * It will unregister for all Descriptors individually!
408         * It will also ignore if this listener is not listening to an Event.
409         * Method is thread-safe.
410         *
411         * @param eventListener the ActivatorEventListener used to listen for events
412         * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed
413         */
414        @Override
415        public void unregisterEventFinishedListener(EventListenerModel eventListener) {
416            main.getEventDistributor().unregisterEventFinishedListener(eventListener);
417        }
418
419        /**
420         * Registers with the LocalEventManager to fire an event.
421         * <p>
422         * Note: the same Event can be fired from multiple sources.
423         * Method is thread-safe.
424         * @param identification the Identification of the the instance
425         * @return an Optional, empty if already registered
426         * @throws IllegalIDException not yet implemented
427         */
428        @Override
429        public Optional<EventCallable> registerEventCaller(Identification identification) throws IllegalIDException {
430            return main.getLocalEventManager().registerCaller(identification);
431        }
432
433        /**
434         * Unregister with the LocalEventManager.
435         * <p>
436         * Method is thread-safe.
437         * @param identification the Identification of the the instance
438         */
439        @Override
440        public void unregisterEventCaller(Identification identification) {
441            main.getLocalEventManager().unregisterCaller(identification);
442        }
443
444        /**
445         * This method fires an Event
446         *
447         * @param event the fired Event
448         * @throws IllegalIDException not yet implemented
449         */
450        @Override
451        public void fireEvent(EventModel event) throws IllegalIDException, MultipleEventsException {
452            main.getLocalEventManager().fireEvent(event);
453        }
454
455        /**
456         * returns the API for the EventsDistributor
457         * @return Distributor
458         */
459        @Override
460        public EventsDistributor distributor() {
461            return eventsDistributor;
462        }
463
464        /**
465         * returns the ID of the Manager (LocalEventManager)
466         */
467        @Override
468        public Identification getManagerIdentification() {
469            Optional<Identification> identification = IdentificationManager.getInstance()
470                    .getIdentification(main.getLocalEventManager());
471            if (!identification.isPresent()) {
472                //should not happen
473                throw new RuntimeException("unable to obtain ID for LocalEventManager");
474            }
475            return identification.get();
476        }
477
478        private class DistributorImpl implements EventsDistributor {
479            /**
480             * with this method you can register EventPublisher add a Source of Events to the System.
481             * <p>
482             * This method represents a higher level of abstraction! Use the EventManager to fire Events!
483             * This method is intended for use cases where you have an entire new source of events (e.g. network)
484             * @param identification the Identification of the Source
485             * @return An Optional Object which may or may not contains an EventPublisher
486             * @throws IllegalIDException not yet implemented
487             */
488            @Override
489            public Optional<EventCallable> registerEventPublisher(Identification identification) throws IllegalIDException {
490                return main.getEventDistributor().registerEventPublisher(identification);
491            }
492
493            /**
494             * with this method you can unregister EventPublisher add a Source of Events to the System.
495             * <p>
496             * This method represents a higher level of abstraction! Use the EventManager to fire Events!
497             * This method is intended for use cases where you have an entire new source of events (e.g. network)
498             * @param identification the Identification of the Source
499             */
500            @Override
501            public void unregisterEventPublisher(Identification identification) {
502                main.getEventDistributor().unregisterEventPublisher(identification);
503            }
504
505            /**
506             * Registers an EventController to control EventDispatching-Behaviour
507             * <p>
508             * Method is thread-safe.
509             * It is expected that this method executes quickly.
510             *
511             * @param eventsController the EventController Interface to control event-dispatching
512             * @throws IllegalIDException not yet implemented
513             */
514            @Override
515            public void registerEventsController(EventsControllerModel eventsController) throws IllegalIDException {
516                main.getEventDistributor().registerEventsController(eventsController);
517            }
518
519            /**
520             * Unregisters an EventController
521             * <p>
522             * Method is thread-safe.
523             *
524             * @param eventsController the EventController Interface to remove
525             */
526            @Override
527            public void unregisterEventsController(EventsControllerModel eventsController) {
528                main.getEventDistributor().unregisterEventsController(eventsController);
529            }
530
531            /**
532             * fires the event concurrently, this is generally discouraged.
533             * <p>
534             * This method should not be used for normal Events, for for events which obey the following laws:<br>
535             * 1. they are time critical.<br>
536             * 2. addons are not expected to react in any way beside a small update<br>
537             * 3. they are few.<br>
538             * if your event matches the above laws, you may consider firing it concurrently.
539             * </p>
540             *
541             * @param eventModel the EventModel
542             */
543            @Override
544            public void fireEventConcurrently(EventModel<?> eventModel) {
545                main.getEventDistributor().fireEventConcurrently(eventModel);
546            }
547
548            /**
549             * returns the ID of the Manager (EventsDistributor)
550             */
551            @Override
552            public Identification getManagerIdentification() {
553                Optional<Identification> identification = IdentificationManager.getInstance()
554                        .getIdentification(main.getEventDistributor());
555                if (!identification.isPresent()) {
556                    //should not happen
557                    throw new RuntimeException("unable to obtain ID for EventsDistributor");
558                }
559                return identification.get();
560            }
561        }
562    }
563
564    private class ResourcesImpl implements Resources {
565        /**
566         * registers a ResourceBuilder.
567         * <p>
568         *  this method registers all the events, resourcesID etc.
569         * </p>
570         * @param resourceBuilder an instance of the ResourceBuilder
571         * @throws IllegalIDException not yet implemented
572         */
573        @Override
574        public void registerResourceBuilder(ResourceBuilderModel resourceBuilder) throws IllegalIDException {
575            main.getResourceManager().registerResourceBuilder(resourceBuilder);
576        }
577
578        /**
579         * unregister a ResourceBuilder.
580         * <p>
581         * this method unregisters all the events, resourcesID etc.
582         * @param resourceBuilder an instance of the ResourceBuilder
583         */
584        @Override
585        public void unregisterResourceBuilder(ResourceBuilderModel resourceBuilder) {
586            main.getResourceManager().unregisterResourceBuilder(resourceBuilder);
587        }
588
589        /**
590         * generates a resources
591         * <p>
592         * @param resource the resource to request
593         * @param consumer the callback when the ResourceBuilder finishes
594         * @throws IllegalIDException not yet implemented
595         */
596        @Override
597        @Deprecated
598        public void generateResource(ResourceModel resource, Consumer<List<ResourceModel>> consumer) throws IllegalIDException {
599            main.getResourceManager().generatedResource(resource, consumer);
600        }
601
602        /**
603         * generates a resources
604         * <p>
605         * It will use the first matching resource! So if you really want to be sure, set the provider
606         * Identification
607         * </p>
608         * @param resource the resource to request
609         * @return an optional of an CompletableFuture
610         * @throws IllegalIDException not yet implemented
611         */
612        @Override
613        public Optional<CompletableFuture<List<ResourceModel>>> generateResource(ResourceModel resource) throws IllegalIDException {
614            return main.getResourceManager().generateResource(resource);
615        }
616
617        /**
618         * returns the ID of the Manager
619         */
620        @Override
621        public Identification getManagerIdentification() {
622            Optional<Identification> identification = IdentificationManager.getInstance()
623                    .getIdentification(main.getResourceManager());
624            if (!identification.isPresent()) {
625                //should not happen
626                throw new RuntimeException("unable to obtain ID for ResourceManager");
627            }
628            return identification.get();
629        }
630    }
631
632    private class ThreadPoolImpl implements ThreadPool {
633        /**
634         * returns an ThreadPool where all the IzouPlugins are running
635         * @param identifiable the Identifiable to set each created Task as the Source
636         * @return an instance of ExecutorService
637         * @throws IllegalIDException not implemented yet
638         */
639        @Override
640        public ExecutorService getThreadPool(Identifiable identifiable) throws IllegalIDException {
641            return TrackingExecutorService.createTrackingExecutorService(
642                    main.getThreadPoolManager().getAddOnsThreadPool(), identifiable);
643        }
644
645        /**
646         * tries everything to log the exception
647         *
648         * @param throwable the Throwable
649         * @param target    an instance of the thing which has thrown the Exception
650         */
651        @Override
652        public void handleThrowable(Throwable throwable, Object target) {
653            main.getThreadPoolManager().handleThrowable(throwable, target);
654        }
655
656        /**
657         * returns the ID of the Manager
658         */
659        @Override
660        public Identification getManagerIdentification() {
661            Optional<Identification> identification = IdentificationManager.getInstance()
662                    .getIdentification(main.getEventDistributor());
663            if (!identification.isPresent()) {
664                //should not happen
665                throw new RuntimeException("unable to obtain ID for ThreadPoolManager");
666            }
667            return identification.get();
668        }
669    }
670
671    private class ActivatorsImpl implements Activators {
672        /**
673         * adds an activator and automatically submits it to the Thread-Pool
674         * @param activatorModel the activator to add
675         * @throws IllegalIDException not yet implemented
676         */
677        @Override
678        public void addActivator(ActivatorModel activatorModel) throws IllegalIDException {
679            main.getActivatorManager().addActivator(activatorModel);
680        }
681
682        /**
683         * removes the activator and stops the Thread
684         * @param activatorModel the activator to remove
685         */
686        @Override
687        public void removeActivator(ActivatorModel activatorModel) {
688            main.getActivatorManager().removeActivator(activatorModel);
689        }
690
691        /**
692         * returns the ID of the Manager
693         */
694        @Override
695        public Identification getManagerIdentification() {
696            Optional<Identification> identification = IdentificationManager.getInstance()
697                    .getIdentification(main.getActivatorManager());
698            if (!identification.isPresent()) {
699                //should not happen
700                throw new RuntimeException("unable to obtain ID for ActivatorManager");
701            }
702            return identification.get();
703        }
704    }
705
706    public class OutputImpl implements Output {
707        /**
708         * adds output extension to desired outputPlugin
709         *
710         * adds output extension to desired outputPlugin, so that the output-plugin can start and stop the outputExtension
711         * task as needed. The outputExtension is specific to the output-plugin
712         *
713         * @param outputExtension the outputExtension to be added
714         * @throws IllegalIDException not yet implemented
715         */
716        @Override
717        public void addOutputExtension(OutputExtensionModel outputExtension) throws IllegalIDException {
718            main.getOutputManager().addOutputExtension(outputExtension);
719        }
720
721        /**
722         * removes the output-extension of id: extensionId from outputPluginList
723         *
724         * @param outputExtension the OutputExtension to remove
725         */
726        @Override
727        public void removeOutputExtension(OutputExtensionModel outputExtension) {
728            main.getOutputManager().removeOutputExtension(outputExtension);
729        }
730
731        /**
732         * adds outputPlugin to outputPluginList, starts a new thread for the outputPlugin, and stores the future object in a HashMap
733         * @param outputPlugin OutputPlugin to add
734         * @throws IllegalIDException not yet implemented
735         */
736        @Override
737        public void addOutputPlugin(OutputPluginModel outputPlugin) throws IllegalIDException {
738            main.getOutputManager().addOutputPlugin(outputPlugin);
739        }
740
741        /**
742         * removes the OutputPlugin and stops the thread
743         * @param outputPlugin the outputPlugin to remove
744         */
745        @Override
746        public void removeOutputPlugin(OutputPluginModel outputPlugin) {
747            main.getOutputManager().removeOutputPlugin(outputPlugin);
748        }
749
750        /**
751         * returns all the associated OutputExtensions
752         *
753         * @param outputPlugin the OutputPlugin to search for
754         * @return a List of Identifications
755         */
756        @Override
757        public List<Identification> getAssociatedOutputExtension(OutputPluginModel<?, ?> outputPlugin) {
758            return main.getOutputManager().getAssociatedOutputExtension(outputPlugin);
759        }
760
761        /**
762         * starts every associated OutputExtension
763         *
764         * @param outputPlugin the OutputPlugin to generate the Data for
765         * @param t            the argument or null
766         * @param event        the Event to generate for  @return a List of Future-Objects
767         * @param <T>          the type of the argument
768         * @param <X>          the return type
769         * @return             a List of Future-Objects
770         */
771        @Override
772        public <T, X> List<CompletableFuture<X>> generateAllOutputExtensions(OutputPluginModel<T, X> outputPlugin,
773                                                                                       T t, EventModel event) {
774            return main.getOutputManager().generateAllOutputExtensions(outputPlugin, t, event);
775        }
776
777        /**
778         * returns the ID of the Manager
779         */
780        @Override
781        public Identification getManagerIdentification() {
782            Optional<Identification> identification = IdentificationManager.getInstance()
783                    .getIdentification(main.getOutputManager());
784            if (!identification.isPresent()) {
785                //should not happen
786                throw new RuntimeException("unable to obtain ID for OutputManager");
787            }
788            return identification.get();
789        }
790    }
791
792    private class SystemImpl implements System {
793        /**
794         * this method registers an listener which will be fired when all the addons finished registering.
795         *
796         * @param runnable the runnable to register.
797         */
798        @Override
799        public void registerInitializedListener(Runnable runnable) {
800            main.getAddOnManager().addInitializedListener(runnable);
801        }
802    }
803}