commit 19f6f5c545fd8ca2453d439a56799c01702088d1
Author: Pavel Fidranský <fidransky@outlook.com>
Date:   Fri Feb 8 22:44:01 2019 +0100

    simplified ModuleLoader to load modules directly from classpath instead of special directory, modules are added to classpath as normal maven dependencies

diff --git a/sources/imiger-core/pom.xml b/sources/imiger-core/pom.xml
index eebd94e..fccd905 100644
--- a/sources/imiger-core/pom.xml
+++ b/sources/imiger-core/pom.xml
@@ -58,6 +58,12 @@
     </build>
 
     <dependencies>
+        <dependency>
+            <groupId>cz.zcu.kiv</groupId>
+            <artifactId>imiger-spade-converter</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-core</artifactId>
diff --git a/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleLoader.java b/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleLoader.java
index 141a36b..9ba3103 100644
--- a/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleLoader.java
+++ b/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleLoader.java
@@ -1,14 +1,13 @@
 package cz.zcu.kiv.offscreen.modularization;
 
 import javafx.util.Pair;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.io.File;
 import java.io.FileInputStream;
-import java.io.FilenameFilter;
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.Arrays;
@@ -25,15 +24,15 @@ import java.util.stream.Collectors;
 class ModuleLoader {
 
     private static final Logger logger = LogManager.getLogger();
-    /** Filter to get only jar files from modules folder. */
-    private static final FilenameFilter MODULE_FILTER = (file, name) -> name.contains(".jar");
+    /** Valid file extension of Java module. */
+    private static final String VALID_MODULE_EXTENSION = "jar";
+    /** Identification of section in manifest containing other information about the module. */
+    private static final String MODULE_SECTION_IDENTIFIER = "cz.zcu.kiv.imiger.plugin";
     /** Identification of class name in modules manifest. */
     private static final String MODULE_CLASS_IDENTIFIER = "Module-Class";
     /** Identification of modules visible name in modules manifest. */
     private static final String MODULE_NAME_IDENTIFIER = "Module-Name";
 
-    /** Path to folder where are modules. Path is relative to resources. */
-    private final String modulesPath;
     /** Name of method which must contains every module. */
     private final String methodName;
     /** Class type of parameter to method which must contains every module. */
@@ -42,15 +41,13 @@ class ModuleLoader {
 
     /**
      * Only story  input parameters
-     * @param modulesPath Path to folder where are modules. Path is relative to resources.
      * @param methodName Name of method which must contains every module.
      * @param methodParamClass Class type of parameter to method which must contains every module.
      */
-    ModuleLoader(String modulesPath, String methodName, Class methodParamClass) {
-        this.modulesPath = modulesPath;
+    ModuleLoader(String methodName, Class methodParamClass) {
         this.methodName = methodName;
         this.methodParamClass = methodParamClass;
-        logger.info("Initializing new ModuleLoader with folder path: " + modulesPath);
+        logger.info("Initializing new ModuleLoader");
     }
 
     /**
@@ -59,8 +56,9 @@ class ModuleLoader {
      */
     Set<Pair<String, Class>> loadModules() {
         logger.info("Loading all modules from file.");
-        final File[] modules = loadJarFiles();
-        return Arrays.stream(modules)
+        final URL[] resources = ((URLClassLoader) getClass().getClassLoader()).getURLs();
+        return Arrays.stream(resources)
+                .filter(this::isJarFile) // remove resources which are not jar files
                 .map(this::loadModule) // call method loadModule for every module
                 .filter(Optional::isPresent) // remove modules which were not loaded
                 .map(Optional::get) // extract modules from Optional class
@@ -68,73 +66,43 @@ class ModuleLoader {
     }
 
     /**
-     * Method open file given by modules path in constructor, check if folder exists and if it is a directory.
-     * On success return Optional of opened folder otherwise returns empty optional.
-     * @return Optional of opened folder otherwise returns empty optional.
-     */
-    Optional<File> getModulesFolder() {
-
-        final URL fileURL = getClass().getClassLoader().getResource(modulesPath);
-        if (fileURL == null) {
-            logger.warn("Can not open modules directory.");
-            return Optional.empty();
-        }
-
-        try {
-            final File modulesFolder = new File(fileURL.toURI());
-
-            if (!modulesFolder.exists() || !modulesFolder.isDirectory()) {
-                logger.warn("Modules folder is not exists or it is not a directory");
-                return Optional.empty();
-            }
-            return Optional.of(modulesFolder);
-
-        } catch (URISyntaxException e) {
-            logger.warn("Can not open modules directory", e);
-            return Optional.empty();
-        }
-    }
-
-    /**
-     * Load all jar files from folder given by modules path in constructor.
+     * Checks if resource is a jar file by its extension.
      *
-     * @return array of founded jars or empty array
+     * @param resourceUrl URL of the resource in local filesystem
+     * @return true if the resource is a jar file, otherwise false
      */
-    private File[] loadJarFiles() {
-
-        Optional<File> folderOptional = getModulesFolder();
-        if (folderOptional.isPresent()) {
-            File[] files = folderOptional.get().listFiles(MODULE_FILTER);
-            if (files == null) files = new File[0];
-            logger.info(files.length + " modules were read from file");
-            return files;
-        }
-        return new File[0];
+    private boolean isJarFile(URL resourceUrl) {
+        return FilenameUtils.getExtension(resourceUrl.getFile()).equals(VALID_MODULE_EXTENSION);
     }
 
     /**
      * Load one particular module given by input File containing opened jar file.
      * Method returns module in Optional in Pair where key is visible modules name and value is access Class from module.
      *
-     * @param moduleFile opened jar file with module
+     * @param moduleUrl URL of the module in local filesystem
      * @return opened module or empty Optional where some error occurs.
      */
-    private Optional<Pair<String, Class>> loadModule(File moduleFile) {
+    private Optional<Pair<String, Class>> loadModule(URL moduleUrl) {
         JarInputStream jis = null;
         try {
-            jis = new JarInputStream(new FileInputStream(moduleFile));
+            jis = new JarInputStream(new FileInputStream(FileUtils.toFile(moduleUrl)));
             final Manifest mf = jis.getManifest();
-            final Attributes attributes = mf.getMainAttributes();
+            final Attributes attributes = mf.getAttributes(MODULE_SECTION_IDENTIFIER);
+            if (attributes == null) {
+                throw new NotModuleException();
+            }
+
             final String moduleClassName = attributes.getValue(MODULE_CLASS_IDENTIFIER);
             final String moduleVisibleName = attributes.getValue(MODULE_NAME_IDENTIFIER);
 
-            final ClassLoader loader = URLClassLoader.newInstance(new URL[]{moduleFile.toURI().toURL()});
-            final Class<?> clazz = Class.forName(moduleClassName, true, loader);
+            final Class<?> clazz = Class.forName(moduleClassName, true, getClass().getClassLoader());
             // checking if method exists, if not throw exception
             clazz.getMethod(methodName, methodParamClass);
 
             return Optional.of(new Pair<>(moduleVisibleName, clazz));
 
+        } catch (NotModuleException e) {
+            return Optional.empty();
         } catch (Exception e) {
             logger.debug("Invalid module throw exception: ", e);
             return Optional.empty();
diff --git a/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleProvider.java b/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleProvider.java
index eab6937..da8c668 100644
--- a/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleProvider.java
+++ b/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleProvider.java
@@ -4,12 +4,11 @@ import javafx.util.Pair;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
-import java.io.File;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.nio.file.*;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.*;
 
@@ -24,8 +23,6 @@ public class ModuleProvider {
     public static final Class METHOD_PARAMETER_CLASS = String.class;
 
     private static final Logger logger = LogManager.getLogger();
-    /** Path to folder with modules relative to resources */
-    private static final String MODULES_PATH = "modules";
     /** Instance of this class used for singleton pattern. */
     private static ModuleProvider instance = null;
 
@@ -63,7 +60,7 @@ public class ModuleProvider {
      * When watcher fails than is automatically starts new watcher after 5 minutes timeout.
      */
     private ModuleProvider() {
-        this.loader = new ModuleLoader(MODULES_PATH, METHOD_NAME, METHOD_PARAMETER_CLASS);
+        this.loader = new ModuleLoader(METHOD_NAME, METHOD_PARAMETER_CLASS);
 
         processModules(loader.loadModules());
 
@@ -104,12 +101,7 @@ public class ModuleProvider {
         try {
             logger.debug("Initializing new WatcherService for modules directory");
 
-            Optional<File> folderFileOptional = loader.getModulesFolder();
-            if (!folderFileOptional.isPresent()) {
-                return;
-            }
-
-            Path path = folderFileOptional.get().toPath();
+            Path path = Paths.get(getClass().getClassLoader().getResource("/../lib/").toURI());
             watcher = FileSystems.getDefault().newWatchService();
             path.register(watcher,
                     StandardWatchEventKinds.ENTRY_CREATE,
@@ -138,7 +130,7 @@ public class ModuleProvider {
                 }
             }
 
-        } catch (IOException | InterruptedException e) {
+        } catch (URISyntaxException | IOException | InterruptedException e) {
             logger.error("Modules directory watcher throw an exception: ", e);
         }
     }
diff --git a/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/NotModuleException.java b/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/NotModuleException.java
new file mode 100644
index 0000000..53b8375
--- /dev/null
+++ b/sources/imiger-core/src/main/java/cz/zcu/kiv/offscreen/modularization/NotModuleException.java
@@ -0,0 +1,10 @@
+package cz.zcu.kiv.offscreen.modularization;
+
+/**
+ * Exception thrown when a jar file which is not a valid IMiGEr plugin is loaded.
+ */
+class NotModuleException extends Exception {
+    NotModuleException() {
+        super("Jar file is not a valid IMiGEr plugin!");
+    }
+}
diff --git a/sources/imiger-core/src/main/resources/modules/imiger-spade-converter-1.0-SNAPSHOT-jar-with-dependencies.jar b/sources/imiger-core/src/main/resources/modules/imiger-spade-converter-1.0-SNAPSHOT-jar-with-dependencies.jar
deleted file mode 100644
index 384152a..0000000
Binary files a/sources/imiger-core/src/main/resources/modules/imiger-spade-converter-1.0-SNAPSHOT-jar-with-dependencies.jar and /dev/null differ
diff --git a/sources/imiger-spade-converter/pom.xml b/sources/imiger-spade-converter/pom.xml
index b21d25c..f20462e 100644
--- a/sources/imiger-spade-converter/pom.xml
+++ b/sources/imiger-spade-converter/pom.xml
@@ -19,24 +19,21 @@
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-assembly-plugin</artifactId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.1</version>
                 <configuration>
                     <archive>
-                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                        <manifestSections>
+                            <manifestSection>
+                                <name>cz.zcu.kiv.imiger.plugin</name>
+                                <manifestEntries>
+                                    <Module-Class>cz.zcu.kiv.imiger.plugin.spade.Spade</Module-Class>
+                                    <Module-Name>Spade JSON</Module-Name>
+                                </manifestEntries>
+                            </manifestSection>
+                        </manifestSections>
                     </archive>
-                    <descriptorRefs>
-                        <descriptorRef>jar-with-dependencies</descriptorRef>
-                    </descriptorRefs>
                 </configuration>
-                <executions>
-                    <execution>
-                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
-                        <phase>package</phase> <!-- bind to the packaging phase -->
-                        <goals>
-                            <goal>single</goal>
-                        </goals>
-                    </execution>
-                </executions>
             </plugin>
         </plugins>
     </build>
diff --git a/sources/imiger-spade-converter/src/main/resources/META-INF/MANIFEST.MF b/sources/imiger-spade-converter/src/main/resources/META-INF/MANIFEST.MF
deleted file mode 100644
index 5602d20..0000000
--- a/sources/imiger-spade-converter/src/main/resources/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,6 +0,0 @@
-Manifest-Version: 1.0
-Created-By: Apache Maven ${maven.version}
-Built-By: ${user.name}
-Build-Jdk: ${java.version}
-Module-Class: cz.zcu.kiv.imiger.plugin.spade.Spade
-Module-Name: Spade JSON
