Revize 5bd5baa5
Přidáno uživatelem Tomáš Šimandl před asi 6 roky(ů)
sources/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleLoader.java | ||
---|---|---|
7 | 7 |
import java.io.File; |
8 | 8 |
import java.io.FileInputStream; |
9 | 9 |
import java.io.FilenameFilter; |
10 |
import java.io.IOException; |
|
10 | 11 |
import java.net.URISyntaxException; |
11 | 12 |
import java.net.URL; |
12 | 13 |
import java.net.URLClassLoader; |
... | ... | |
18 | 19 |
import java.util.jar.Manifest; |
19 | 20 |
import java.util.stream.Collectors; |
20 | 21 |
|
21 |
public class ModuleLoader { |
|
22 |
/** |
|
23 |
* @author Tomáš Šimandl |
|
24 |
*/ |
|
25 |
class ModuleLoader { |
|
22 | 26 |
|
23 | 27 |
private static final Logger logger = LogManager.getLogger(); |
28 |
/** Filter to get only jar files from modules folder. */ |
|
24 | 29 |
private static final FilenameFilter MODULE_FILTER = (file, name) -> name.contains(".jar"); |
30 |
/** Identification of class name in modules manifest. */ |
|
25 | 31 |
private static final String MODULE_CLASS_IDENTIFIER = "Module-Class"; |
32 |
/** Identification of modules visible name in modules manifest. */ |
|
26 | 33 |
private static final String MODULE_NAME_IDENTIFIER = "Module-Name"; |
27 | 34 |
|
35 |
/** Path to folder where are modules. Path is relative to resources. */ |
|
28 | 36 |
private final String modulesPath; |
37 |
/** Name of method which must contains every module. */ |
|
29 | 38 |
private final String methodName; |
39 |
/** Class type of parameter to method which must contains every module. */ |
|
30 | 40 |
private final Class methodParamClass; |
31 | 41 |
|
32 |
public ModuleLoader(String modulesPath, String methodName, Class methodParamClass) { |
|
42 |
|
|
43 |
/** |
|
44 |
* Only story input parameters |
|
45 |
* @param modulesPath Path to folder where are modules. Path is relative to resources. |
|
46 |
* @param methodName Name of method which must contains every module. |
|
47 |
* @param methodParamClass Class type of parameter to method which must contains every module. |
|
48 |
*/ |
|
49 |
ModuleLoader(String modulesPath, String methodName, Class methodParamClass) { |
|
33 | 50 |
this.modulesPath = modulesPath; |
34 | 51 |
this.methodName = methodName; |
35 | 52 |
this.methodParamClass = methodParamClass; |
36 | 53 |
logger.info("Initializing new ModuleLoader with folder path: " + modulesPath); |
37 | 54 |
} |
38 | 55 |
|
39 |
public Set<Pair<String, Class>> loadModules() { |
|
56 |
/** |
|
57 |
* Method starts process of loading all modules from modules folder (defined in constructor). |
|
58 |
* @return set of loaded modules in Pair where key is: 'Visible name' and value: 'Class to be called'. |
|
59 |
*/ |
|
60 |
Set<Pair<String, Class>> loadModules() { |
|
40 | 61 |
logger.info("Loading all modules from file."); |
41 | 62 |
final File[] modules = loadJarFiles(); |
42 | 63 |
return Arrays.stream(modules) |
43 |
.map(this::loadModule) |
|
44 |
.filter(Optional::isPresent) |
|
45 |
.map(Optional::get) |
|
46 |
.collect(Collectors.toSet()); |
|
64 |
.map(this::loadModule) // call method loadModule for every module
|
|
65 |
.filter(Optional::isPresent) // remove modules which were not loaded
|
|
66 |
.map(Optional::get) // extract modules from Optional class
|
|
67 |
.collect(Collectors.toSet()); // return set of modules
|
|
47 | 68 |
} |
48 | 69 |
|
49 |
public Optional<File> getModulesFolder() { |
|
70 |
/** |
|
71 |
* Method open file given by modules path in constructor, check if folder exists and if it is a directory. |
|
72 |
* On success return Optional of opened folder otherwise returns empty optional. |
|
73 |
* @return Optional of opened folder otherwise returns empty optional. |
|
74 |
*/ |
|
75 |
Optional<File> getModulesFolder() { |
|
50 | 76 |
|
51 | 77 |
final URL fileURL = getClass().getClassLoader().getResource(modulesPath); |
52 | 78 |
if (fileURL == null) { |
... | ... | |
69 | 95 |
} |
70 | 96 |
} |
71 | 97 |
|
98 |
/** |
|
99 |
* Load all jar files from folder given by modules path in constructor. |
|
100 |
* |
|
101 |
* @return array of founded jars or empty array |
|
102 |
*/ |
|
72 | 103 |
private File[] loadJarFiles() { |
73 | 104 |
|
74 | 105 |
Optional<File> folderOptional = getModulesFolder(); |
75 | 106 |
if (folderOptional.isPresent()) { |
76 | 107 |
File[] files = folderOptional.get().listFiles(MODULE_FILTER); |
77 |
logger.info(files == null ? 0 : files.length + " modules were read from file"); |
|
108 |
if (files == null) files = new File[0]; |
|
109 |
logger.info(files.length + " modules were read from file"); |
|
78 | 110 |
return files; |
79 | 111 |
} |
80 | 112 |
return new File[0]; |
81 | 113 |
} |
82 | 114 |
|
115 |
/** |
|
116 |
* Load one particular module given by input File containing opened jar file. |
|
117 |
* Method returns module in Optional in Pair where key is visible modules name and value is access Class from module. |
|
118 |
* |
|
119 |
* @param moduleFile opened jar file with module |
|
120 |
* @return opened module or empty Optional where some error occurs. |
|
121 |
*/ |
|
83 | 122 |
private Optional<Pair<String, Class>> loadModule(File moduleFile) { |
84 | 123 |
JarInputStream jis = null; |
85 | 124 |
try { |
sources/src/main/java/cz/zcu/kiv/offscreen/modularization/ModuleProvider.java | ||
---|---|---|
13 | 13 |
import java.util.Set; |
14 | 14 |
import java.util.concurrent.*; |
15 | 15 |
|
16 |
/** |
|
17 |
* @author Tomáš Šimandl |
|
18 |
*/ |
|
16 | 19 |
public class ModuleProvider { |
17 | 20 |
|
21 |
/** Name of accessed method in every module. */ |
|
18 | 22 |
public static final String METHOD_NAME = "getRawJson"; |
23 |
/** Class of input parameter to accessed method in every module. */ |
|
19 | 24 |
public static final Class METHOD_PARAMETER_CLASS = String.class; |
20 | 25 |
|
21 | 26 |
private static final Logger logger = LogManager.getLogger(); |
27 |
/** Path to folder with modules relative to resources */ |
|
22 | 28 |
private static final String MODULES_PATH = "modules"; |
29 |
/** Instance of this class used for singleton pattern. */ |
|
23 | 30 |
private static ModuleProvider instance = null; |
24 | 31 |
|
32 |
/** |
|
33 |
* Map containing actual loaded modules. Key is hash of visible name and value is pair of |
|
34 |
* visible name na accessed method. |
|
35 |
*/ |
|
25 | 36 |
private ConcurrentMap<String, Pair<String, Class>> modules = new ConcurrentHashMap<>(); |
37 |
/** Instance of class ModuleLoader. */ |
|
26 | 38 |
private final ModuleLoader loader; |
27 |
private boolean watch = true; |
|
28 | 39 |
|
40 |
/** Instance of ScheduledExecutorService used for scheduling of module folder watcher. */ |
|
29 | 41 |
private ScheduledExecutorService executor; |
42 |
/** Instance of ScheduledFuture used for scheduling of module folder watcher. */ |
|
30 | 43 |
private ScheduledFuture scheduledFuture; |
44 |
/** Instance of WatchService used for watching of folder with modules. */ |
|
31 | 45 |
private WatchService watcher = null; |
32 | 46 |
|
33 |
|
|
47 |
/** |
|
48 |
* Static method for creating only one instance of this class. |
|
49 |
* Singleton pattern. |
|
50 |
* |
|
51 |
* @return one instance of this class for every call. |
|
52 |
*/ |
|
34 | 53 |
public static ModuleProvider getInstance() { |
35 | 54 |
if (instance == null) { |
36 | 55 |
instance = new ModuleProvider(); |
... | ... | |
38 | 57 |
return instance; |
39 | 58 |
} |
40 | 59 |
|
60 |
/** |
|
61 |
* Private constructor is used for singleton pattern. Constructor loads (method moduleLoader.loadModules) |
|
62 |
* and store (method processModules) all modules from folder and start watcher on modules folder. |
|
63 |
* When watcher fails than is automatically starts new watcher after 5 minutes timeout. |
|
64 |
*/ |
|
41 | 65 |
private ModuleProvider() { |
42 | 66 |
this.loader = new ModuleLoader(MODULES_PATH, METHOD_NAME, METHOD_PARAMETER_CLASS); |
43 | 67 |
|
... | ... | |
48 | 72 |
logger.debug("Scheduling Modules Watcher thread."); |
49 | 73 |
// task will be scheduled after 1 minute |
50 | 74 |
// When task ends (on failure) after one minute will be planed again |
51 |
scheduledFuture = executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);
|
|
75 |
scheduledFuture = executor.scheduleWithFixedDelay(task, 0, 5, TimeUnit.MINUTES);
|
|
52 | 76 |
} |
53 | 77 |
|
78 |
/** |
|
79 |
* Method is used for stopping watcher on modules folder and thread associate with it. |
|
80 |
*/ |
|
54 | 81 |
public void stopWatcher() { |
55 | 82 |
logger.debug("Stopping WatcherProvider"); |
56 |
watch = false; |
|
57 | 83 |
|
58 | 84 |
try { |
59 | 85 |
logger.info("Closing WatcherService"); |
... | ... | |
69 | 95 |
if (executor != null) executor.shutdown(); |
70 | 96 |
} |
71 | 97 |
|
98 |
/** |
|
99 |
* Method open folder with modules and starts watcher on folder. Watcher is activated when any file is created, |
|
100 |
* deleted of modified in modules folder. Activation of watcher run loading (method moduleLoader.loadModules) |
|
101 |
* and storing (method processModules) of all modules. |
|
102 |
*/ |
|
72 | 103 |
private void initModulesWatcher() { |
73 | 104 |
try { |
74 | 105 |
logger.debug("Initializing new WatcherService for modules directory"); |
... | ... | |
101 | 132 |
break; // watching only one folder and loading all files every loop => Only one iteration is needed. |
102 | 133 |
} |
103 | 134 |
|
104 |
if (!key.reset() || !watch) {
|
|
135 |
if (!key.reset()) { |
|
105 | 136 |
logger.warn("Stopping modules directory watcher"); |
106 | 137 |
break; |
107 | 138 |
} |
... | ... | |
112 | 143 |
} |
113 | 144 |
} |
114 | 145 |
|
146 |
/** |
|
147 |
* Method is used for prepare loaded modules to be used in application. Result is stored in modules variable. |
|
148 |
* First of all is created new map where keys are hash of first arguments of input pairs and values are input pairs. |
|
149 |
* Than variable modules is cleared and all values from created map is insert to it. |
|
150 |
* |
|
151 |
* @param unprocessedModules Set of loaded modules where first argument is visible name and second is accessible method. |
|
152 |
*/ |
|
115 | 153 |
private void processModules(Set<Pair<String, Class>> unprocessedModules) { |
116 | 154 |
long startTime = System.nanoTime(); |
117 | 155 |
Map<String, Pair<String, Class>> localModules = new HashMap<>(); |
... | ... | |
127 | 165 |
logger.debug("Modules were loaded and processed in " + (System.nanoTime() - startTime) / 1000000d + " milliseconds"); |
128 | 166 |
} |
129 | 167 |
|
168 |
/** |
|
169 |
* Return all loaded modules in map where value is Pair of visible name and module accessible method and key is |
|
170 |
* hash code of visible name. |
|
171 |
* |
|
172 |
* @return all loaded modules. |
|
173 |
*/ |
|
130 | 174 |
public Map<String, Pair<String, Class>> getModules() { |
131 | 175 |
return modules; |
132 | 176 |
} |
Také k dispozici: Unified diff
Add documentation to modularization classes