package org.intellimate.izou.system.context; import org.intellimate.izou.activator.ActivatorModel; import org.intellimate.izou.identification.AddOnInformation; import org.intellimate.izou.addon.AddOnModel; import org.intellimate.izou.events.*; import org.intellimate.izou.identification.Identifiable; import org.intellimate.izou.identification.Identification; import org.intellimate.izou.identification.IdentificationManager; import org.intellimate.izou.identification.IllegalIDException; import org.intellimate.izou.main.Main; import org.intellimate.izou.output.OutputControllerModel; import org.intellimate.izou.output.OutputExtensionModel; import org.intellimate.izou.output.OutputPluginModel; import org.intellimate.izou.resource.ResourceModel; import org.intellimate.izou.resource.ResourceBuilderModel; import org.intellimate.izou.system.Context; import org.intellimate.izou.system.file.FileSubscriber; import org.intellimate.izou.system.file.ReloadableFile; import org.intellimate.izou.system.logger.IzouLogger; import org.intellimate.izou.threadpool.TrackingExecutorService; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.spi.ExtendedLogger; import org.intellimate.izou.util.IdentifiableSet; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.function.Consumer; /** * This class provides much of the general Communication with Izou. */ public class ContextImplementation implements Context { private final AddOnModel addOn; private final Main main; private final Events events = new EventsImpl(); private final Resources resources = new ResourcesImpl(); private final Files files; private final ExtendedLogger logger; private final ThreadPool threadPool; private final Activators activators = new ActivatorsImpl(); private final Output output = new OutputImpl(); private final System system = new SystemImpl(); private final AddOns addOns = new AddOnsImpl(); /** * creates a new context for the addOn * * A context contains all the "global" or generally necessary information an addOn might need that it otherwise does * not have access too * * @param addOn the addOn for which to create a new context * @param main instance of main * @param logLevel the logLevel to initialize the IzouLogger with */ public ContextImplementation(AddOnModel addOn, Main main, String logLevel) { this.addOn = addOn; this.main = main; this.files = new FilesImpl(); this.threadPool = new ThreadPoolImpl(); IzouLogger izouLogger = main.getIzouLogger(); if (izouLogger != null) this.logger = izouLogger.createFileLogger(addOn.getID(), logLevel); else { this.logger = null; org.apache.logging.log4j.Logger fileLogger = LogManager.getLogger(this.getClass()); fileLogger.error("IzouLogger has not been initialized"); throw new NullPointerException("IzouLogger has not been initialized"); } } /** * returns the API used for interaction with Events * @return Events */ @Override public Events getEvents() { return events; } /** * returns the API used for interaction with Resource * @return Resource */ @Override public Resources getResources() { return resources; } /** * returns the API used for interaction with Files * @return Files */ @Override public Files getFiles() { return files; } /** * gets logger for addOn * * @return the logger */ @Override public ExtendedLogger getLogger() { return logger; } /** * returns the API used to manage the ThreadPool * @return ThreadPool */ @Override public ThreadPool getThreadPool() { return threadPool; } /** * returns the API to manage the Activators * @return Activator */ @Override public Activators getActivators() { return activators; } /** * Returns the API used to manage the OutputPlugins and OutputExtensions. * * @return Output */ @Override public Output getOutput() { return output; } /** * Returns the API used to interact with Izou. * * @return System. */ @Override public System getSystem() { return system; } /** * Gets the API to manage the addOns. * * @return AddOns. */ @Override public AddOns getAddOns() { return addOns; } private class FilesImpl implements Files { /** * Use this method to register a file with the watcherService * * @param dir directory of file * @param fileType the name/extension of the file * IMPORTANT: Please try to always enter the full name with extension of the file (Ex: "test.txt"), * it would be best if the fileType is the full file name, and that the file name is clearly * distinguishable from other files. * For example, the property files are stored with the ID of the addon they belong too. That way * every property file is easily distinguishable. * @param reloadableFile object of interface that file belongs to * @throws IOException exception thrown by watcher service */ @Override public void registerFileDir(Path dir, String fileType, ReloadableFile reloadableFile) throws IOException { main.getFileManager().registerFileDir(dir, fileType, reloadableFile); } /** * Writes default file to real file * The default file would be a file that can be packaged along with the code, from which a real file (say a * properties file for example) can be loaded. This is useful because there are files (like property files0 that * cannot be shipped with the package and have to be created at runtime. To still be able to fill these files, you * can create a default file (usually txt) from which the content, as mentioned above, can then be loaded into the * real file. * * @param defaultFilePath path to default file (or where it should be created) * @param realFilePath path to real file (that should be filled with content of default file) * @return true if operation has succeeded, else false */ @Override public boolean writeToFile(String defaultFilePath, String realFilePath) { return main.getFileManager().writeToFile(defaultFilePath, realFilePath); } /** * Creates a default File in case it does not exist yet. Default files can be used to load other files that are * created at runtime (like properties file) * * @param defaultFilePath path to default file.txt (or where it should be created) * @param initMessage the string to write in default file * @throws java.io.IOException is thrown by bufferedWriter */ @Override public void createDefaultFile(String defaultFilePath, String initMessage) throws IOException { main.getFileManager().createDefaultFile(defaultFilePath, initMessage); } /** * Registers a {@link FileSubscriber} with a {@link ReloadableFile}. So when the {@code reloadableFile} is * reloaded, the fileSubscriber will be notified. Multiple file subscribers can be registered with the same * reloadable file. * * @param reloadableFile the reloadable file that should be observed * @param fileSubscriber the fileSubscriber that should be notified when the reloadable file is reloaded * @param identification the Identification of the requesting instance * @throws IllegalIDException not yet implemented */ @Override public void register(ReloadableFile reloadableFile, FileSubscriber fileSubscriber, Identification identification) throws IllegalIDException { main.getFilePublisher().register(reloadableFile, fileSubscriber, identification); } /** * Registers a {@link FileSubscriber} so that whenever any file is reloaded, the fileSubscriber is notified. * * @param fileSubscriber the fileSubscriber that should be notified when the reloadable file is reloaded * @param identification the Identification of the requesting instance * @throws IllegalIDException not yet implemented */ @Override public void register(FileSubscriber fileSubscriber, Identification identification) throws IllegalIDException { main.getFilePublisher().register(fileSubscriber, identification); } /** * Unregisters all instances of fileSubscriber found. * * @param fileSubscriber the fileSubscriber to unregister */ @Override public void unregister(FileSubscriber fileSubscriber) { main.getFilePublisher().unregister(fileSubscriber); } /** * gets the File pointing towards the location of the lib-folder * * @return the File */ @Override public File getLibLocation() { return main.getFileSystemManager().getLibLocation(); } /** * gets the File pointing towards the location of the resource-folder * * @return the File */ @Override public File getResourceLocation() { return main.getFileSystemManager().getResourceLocation(); } /** * gets the File pointing towards the location of the properties-folder * * @return the File */ @Override public File getPropertiesLocation() { return main.getFileSystemManager().getPropertiesLocation(); } /** * gets the File pointing towards the location of the logs-folder * * @return the File */ @Override public File getLogsLocation() { return main.getFileSystemManager().getLogsLocation(); } } private class EventsImpl implements Events { public EventsDistributor eventsDistributor = new DistributorImpl(); /** * Adds an listener for events. * <p> * Be careful with this method, it will register the listener for ALL the informations found in the Event. If your * event-type is a common event type, it will fire EACH time!. * It will also register for all Descriptors individually! * It will also ignore if this listener is already listening to an Event. * Method is thread-safe. * </p> * @param event the Event to listen to (it will listen to all descriptors individually!) * @param eventListener the ActivatorEventListener-interface for receiving activator events * @throws IllegalIDException not yet implemented */ @SuppressWarnings("JavaDoc") @Override public void registerEventListener(EventModel event, EventListenerModel eventListener) throws IllegalIDException { main.getEventDistributor().registerEventListener(event, eventListener); } /** * Adds an listener for events. * <p> * It will register for all ids individually! * This method will ignore if this listener is already listening to an Event. * Method is thread-safe. * </p> * @param ids this can be type, or descriptors etc. * @param eventListener the ActivatorEventListener-interface for receiving activator events */ @Override public void registerEventListener(List<String> ids, EventListenerModel eventListener) { main.getEventDistributor().registerEventListener(ids, eventListener); } /** * unregister an EventListener *<p> * It will unregister for all Descriptors individually! * It will also ignore if this listener is not listening to an Event. * Method is thread-safe. * * @param event the Event to stop listen to * @param eventListener the ActivatorEventListener used to listen for events * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed */ @Override public void unregisterEventListener(EventModel event, EventListenerModel eventListener) { main.getEventDistributor().unregisterEventListener(event, eventListener); } /** * unregister an EventListener that gets called before the generation of the resources and the outputPlugins. * <p> * It will unregister for all Descriptors individually! * It will also ignore if this listener is not listening to an Event. * Method is thread-safe. * * @param eventListener the ActivatorEventListener used to listen for events * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed */ @Override public void unregisterEventListener(EventListenerModel eventListener) { main.getEventDistributor().unregisterEventListener(eventListener); } /** * Adds an listener for events that gets called when the event finished processing. * <p> * Be careful with this method, it will register the listener for ALL the informations found in the Event. If your * event-type is a common event type, it will fire EACH time!. * It will also register for all Descriptors individually! * It will also ignore if this listener is already listening to an Event. * Method is thread-safe. * </p> * * @param event the Event to listen to (it will listen to all descriptors individually!) * @param eventListener the ActivatorEventListener-interface for receiving activator events * @throws IllegalIDException not yet implemented */ @Override public void registerEventFinishedListener(EventModel event, EventListenerModel eventListener) throws IllegalIDException { main.getEventDistributor().registerEventFinishedListener(event, eventListener); } /** * Adds an listener for events that gets called when the event finished processing. * <p> * It will register for all ids individually! * This method will ignore if this listener is already listening to an Event. * Method is thread-safe. * </p> * * @param ids this can be type, or descriptors etc. * @param eventListener the ActivatorEventListener-interface for receiving activator events */ @Override public void registerEventFinishedListener(List<String> ids, EventListenerModel eventListener) { main.getEventDistributor().registerEventFinishedListener(ids, eventListener); } /** * unregister an EventListener that got called when the event finished processing. * <p> * It will unregister for all Descriptors individually! * It will also ignore if this listener is not listening to an Event. * Method is thread-safe. * * @param event the Event to stop listen to * @param eventListener the ActivatorEventListener used to listen for events * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed */ @Override public void unregisterEventFinishedListener(EventModel event, EventListenerModel eventListener) { main.getEventDistributor().unregisterEventFinishedListener(event, eventListener); } /** * unregister an EventListener that got called when the event finished processing. * <p> * It will unregister for all Descriptors individually! * It will also ignore if this listener is not listening to an Event. * Method is thread-safe. * * @param eventListener the ActivatorEventListener used to listen for events * @throws IllegalArgumentException if Listener is already listening to the Event or the id is not allowed */ @Override public void unregisterEventFinishedListener(EventListenerModel eventListener) { main.getEventDistributor().unregisterEventFinishedListener(eventListener); } /** * Registers with the LocalEventManager to fire an event. * <p> * Note: the same Event can be fired from multiple sources. * Method is thread-safe. * @param identification the Identification of the the instance * @return an Optional, empty if already registered * @throws IllegalIDException not yet implemented */ @Override public Optional<EventCallable> registerEventCaller(Identification identification) throws IllegalIDException { return main.getLocalEventManager().registerCaller(identification); } /** * Unregister with the LocalEventManager. * <p> * Method is thread-safe. * @param identification the Identification of the the instance */ @Override public void unregisterEventCaller(Identification identification) { main.getLocalEventManager().unregisterCaller(identification); } /** * This method fires an Event * * @param event the fired Event * @throws IllegalIDException not yet implemented */ @Override public void fireEvent(EventModel event) throws IllegalIDException, MultipleEventsException { main.getLocalEventManager().fireEvent(event); } /** * returns the API for the EventsDistributor * @return Distributor */ @Override public EventsDistributor distributor() { return eventsDistributor; } /** * returns the ID of the Manager (LocalEventManager) */ @Override public Identification getManagerIdentification() { Optional<Identification> identification = IdentificationManager.getInstance() .getIdentification(main.getLocalEventManager()); if (!identification.isPresent()) { //should not happen throw new RuntimeException("unable to obtain ID for LocalEventManager"); } return identification.get(); } private class DistributorImpl implements EventsDistributor { /** * with this method you can register EventPublisher add a Source of Events to the System. * <p> * This method represents a higher level of abstraction! Use the EventManager to fire Events! * This method is intended for use cases where you have an entire new source of events (e.g. network) * @param identification the Identification of the Source * @return An Optional Object which may or may not contains an EventPublisher * @throws IllegalIDException not yet implemented */ @Override public Optional<EventCallable> registerEventPublisher(Identification identification) throws IllegalIDException { return main.getEventDistributor().registerEventPublisher(identification); } /** * with this method you can unregister EventPublisher add a Source of Events to the System. * <p> * This method represents a higher level of abstraction! Use the EventManager to fire Events! * This method is intended for use cases where you have an entire new source of events (e.g. network) * @param identification the Identification of the Source */ @Override public void unregisterEventPublisher(Identification identification) { main.getEventDistributor().unregisterEventPublisher(identification); } /** * Registers an EventController to control EventDispatching-Behaviour * <p> * Method is thread-safe. * It is expected that this method executes quickly. * * @param eventsController the EventController Interface to control event-dispatching * @throws IllegalIDException not yet implemented */ @Override public void registerEventsController(EventsControllerModel eventsController) throws IllegalIDException { main.getEventDistributor().registerEventsController(eventsController); } /** * Unregisters an EventController * <p> * Method is thread-safe. * * @param eventsController the EventController Interface to remove */ @Override public void unregisterEventsController(EventsControllerModel eventsController) { main.getEventDistributor().unregisterEventsController(eventsController); } /** * fires the event concurrently, this is generally discouraged. * <p> * This method should not be used for normal Events, for for events which obey the following laws:<br> * 1. they are time critical.<br> * 2. addons are not expected to react in any way beside a small update<br> * 3. they are few.<br> * if your event matches the above laws, you may consider firing it concurrently. * </p> * * @param eventModel the EventModel */ @Override public void fireEventConcurrently(EventModel<?> eventModel) { main.getEventDistributor().fireEventConcurrently(eventModel); } /** * returns the ID of the Manager (EventsDistributor) */ @Override public Identification getManagerIdentification() { Optional<Identification> identification = IdentificationManager.getInstance() .getIdentification(main.getEventDistributor()); if (!identification.isPresent()) { //should not happen throw new RuntimeException("unable to obtain ID for EventsDistributor"); } return identification.get(); } } } private class ResourcesImpl implements Resources { /** * registers a ResourceBuilder. * <p> * this method registers all the events, resourcesID etc. * </p> * @param resourceBuilder an instance of the ResourceBuilder * @throws IllegalIDException not yet implemented */ @Override public void registerResourceBuilder(ResourceBuilderModel resourceBuilder) throws IllegalIDException { main.getResourceManager().registerResourceBuilder(resourceBuilder); } /** * unregister a ResourceBuilder. * <p> * this method unregisters all the events, resourcesID etc. * @param resourceBuilder an instance of the ResourceBuilder */ @Override public void unregisterResourceBuilder(ResourceBuilderModel resourceBuilder) { main.getResourceManager().unregisterResourceBuilder(resourceBuilder); } /** * generates a resources * <p> * @param resource the resource to request * @param consumer the callback when the ResourceBuilder finishes * @throws IllegalIDException not yet implemented */ @Override @Deprecated public void generateResource(ResourceModel resource, Consumer<List<ResourceModel>> consumer) throws IllegalIDException { main.getResourceManager().generatedResource(resource, consumer); } /** * generates a resources * <p> * It will use the first matching resource! So if you really want to be sure, set the provider * Identification * </p> * @param resource the resource to request * @return an optional of an CompletableFuture * @throws IllegalIDException not yet implemented */ @Override public Optional<CompletableFuture<List<ResourceModel>>> generateResource(ResourceModel resource) throws IllegalIDException { return main.getResourceManager().generateResource(resource); } /** * returns the ID of the Manager */ @Override public Identification getManagerIdentification() { Optional<Identification> identification = IdentificationManager.getInstance() .getIdentification(main.getResourceManager()); if (!identification.isPresent()) { //should not happen throw new RuntimeException("unable to obtain ID for ResourceManager"); } return identification.get(); } } private class ThreadPoolImpl implements ThreadPool { /** * returns an ThreadPool where all the IzouPlugins are running * @param identifiable the Identifiable to set each created Task as the Source * @return an instance of ExecutorService * @throws IllegalIDException not implemented yet */ @Override public ExecutorService getThreadPool(Identifiable identifiable) throws IllegalIDException { return TrackingExecutorService.createTrackingExecutorService( main.getThreadPoolManager().getAddOnsThreadPool(), identifiable); } /** * tries everything to log the exception * * @param throwable the Throwable * @param target an instance of the thing which has thrown the Exception */ @Override public void handleThrowable(Throwable throwable, Object target) { main.getThreadPoolManager().handleThrowable(throwable, target); } /** * returns the ID of the Manager */ @Override public Identification getManagerIdentification() { Optional<Identification> identification = IdentificationManager.getInstance() .getIdentification(main.getEventDistributor()); if (!identification.isPresent()) { //should not happen throw new RuntimeException("unable to obtain ID for ThreadPoolManager"); } return identification.get(); } } private class ActivatorsImpl implements Activators { /** * adds an activator and automatically submits it to the Thread-Pool * @param activatorModel the activator to add * @throws IllegalIDException not yet implemented */ @Override public void addActivator(ActivatorModel activatorModel) throws IllegalIDException { main.getActivatorManager().addActivator(activatorModel); } /** * removes the activator and stops the Thread * @param activatorModel the activator to remove */ @Override public void removeActivator(ActivatorModel activatorModel) { main.getActivatorManager().removeActivator(activatorModel); } /** * returns the ID of the Manager */ @Override public Identification getManagerIdentification() { Optional<Identification> identification = IdentificationManager.getInstance() .getIdentification(main.getActivatorManager()); if (!identification.isPresent()) { //should not happen throw new RuntimeException("unable to obtain ID for ActivatorManager"); } return identification.get(); } } private class AddOnsImpl implements AddOns { @Override public AddOnModel getAddOn() { return addOn; } @Override public Optional<AddOnInformation> getAddOnInformation(String id) { return main.getAddOnInformationManager().getAddOnInformation(id); } @Override public Optional<AddOnInformation> getAddOnInformation(int serverID) { return main.getAddOnInformationManager().getAddOnInformation(serverID); } @Override public IdentifiableSet<AddOnInformation> getAllAddOnInformations() { return main.getAddOnInformationManager().getAllAddOnInformations(); } } private class OutputImpl implements Output { /** * adds output extension to desired outputPlugin * * adds output extension to desired outputPlugin, so that the output-plugin can start and stop the outputExtension * task as needed. The outputExtension is specific to the output-plugin * * @param outputExtension the outputExtension to be added * @throws IllegalIDException not yet implemented */ @Override public void addOutputExtension(OutputExtensionModel outputExtension) throws IllegalIDException { main.getOutputManager().addOutputExtension(outputExtension); } /** * removes the output-extension of id: extensionId from outputPluginList * * @param outputExtension the OutputExtension to remove */ @Override public void removeOutputExtension(OutputExtensionModel outputExtension) { main.getOutputManager().removeOutputExtension(outputExtension); } /** * adds outputPlugin to outputPluginList, starts a new thread for the outputPlugin, and stores the future object in a HashMap * @param outputPlugin OutputPlugin to add * @throws IllegalIDException not yet implemented */ @Override public void addOutputPlugin(OutputPluginModel outputPlugin) throws IllegalIDException { main.getOutputManager().addOutputPlugin(outputPlugin); } /** * removes the OutputPlugin and stops the thread * @param outputPlugin the outputPlugin to remove */ @Override public void removeOutputPlugin(OutputPluginModel outputPlugin) { main.getOutputManager().removeOutputPlugin(outputPlugin); } @Override public void addOutputController(OutputControllerModel outputController) { main.getOutputControllerManager().addOutputController(outputController); } @Override public void removeOutputController(OutputControllerModel outputController) { main.getOutputControllerManager().removeOutputController(outputController); } @Override public Optional<OutputControllerModel> getOutputController(Identifiable identifiable) { return main.getOutputControllerManager().getOutputController(identifiable); } /** * returns all the associated OutputExtensions * * @param outputPlugin the OutputPlugin to search for * @return a List of Identifications */ @Override public List<Identification> getAssociatedOutputExtension(OutputPluginModel<?, ?> outputPlugin) { return main.getOutputManager().getAssociatedOutputExtension(outputPlugin); } /** * starts every associated OutputExtension * * @param outputPlugin the OutputPlugin to generate the Data for * @param t the argument or null * @param event the Event to generate for @return a List of Future-Objects * @param <T> the type of the argument * @param <X> the return type * @return a List of Future-Objects */ @Override public <T, X> List<CompletableFuture<X>> generateAllOutputExtensions(OutputPluginModel<T, X> outputPlugin, T t, EventModel event) { return main.getOutputManager().generateAllOutputExtensions(outputPlugin, t, event); } /** * returns the ID of the Manager */ @Override public Identification getManagerIdentification() { Optional<Identification> identification = IdentificationManager.getInstance() .getIdentification(main.getOutputManager()); if (!identification.isPresent()) { //should not happen throw new RuntimeException("unable to obtain ID for OutputManager"); } return identification.get(); } } private class SystemImpl implements System { /** * this method registers an listener which will be fired when all the addons finished registering. * * @param runnable the runnable to register. */ @Override public void registerInitializedListener(Runnable runnable) { main.getAddOnManager().addInitializedListener(runnable); } } }