/*
* Copyright 2015 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.util.Varargs;
import org.terasology.util.io.FileTypesFilter;
import org.terasology.util.io.FilesUtil;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Set;
/**
* A scanner for reading modules off of the filesystem. These modules may either be archives (zip or jar) or directories. To qualify as a module they must contain a
* json module metadata file (by default named "module.txt").
*
* @author Immortius
*/
public class ModulePathScanner {
private static final Logger logger = LoggerFactory.getLogger(ModulePathScanner.class);
private final ModuleLoader moduleLoader;
public ModulePathScanner() {
this.moduleLoader = new ModuleLoader();
}
public ModulePathScanner(ModuleLoader loader) {
this.moduleLoader = loader;
}
public ModuleLoader getModuleLoader() {
return moduleLoader;
}
/**
* Scans one or more paths for modules. Paths are scanned in order, with directories scanned before files. If a module is discovered multiple times (same id and version),
* the first copy of the module found is used.
*
* @param registry The registry to populate with discovered modules
* @param path The first path to scan
* @param additionalPaths Additional paths to scan
*/
public void scan(ModuleRegistry registry, Path path, Path... additionalPaths) {
Set<Path> discoveryPaths = Varargs.combineToSet(path, additionalPaths);
scan(registry, discoveryPaths);
}
/**
* Scans a collection of paths for modules.
* Paths are scanned in order, with directories scanned before files. If a module is discovered multiple times (same id and version), the first copy of the module
* found is used.
*
* @param registry The registry to populate with discovered modules
* @param paths The paths to scan
*/
public void scan(ModuleRegistry registry, Collection<Path> paths) {
for (Path discoveryPath : paths) {
try {
scanModuleDirectories(discoveryPath, registry);
scanModuleArchives(discoveryPath, registry);
} catch (IOException e) {
logger.error("Failed to scan path {}", discoveryPath, e);
}
}
}
/**
* Scans a directory for module archives (jar or zip)
*
* @param discoveryPath The directory to scan
* @param registry The registry to populate with discovered modules
* @throws IOException If an error occurs scanning the directory - but not an individual module.
*/
private void scanModuleArchives(Path discoveryPath, ModuleRegistry registry) throws IOException {
for (Path modulePath : Files.newDirectoryStream(discoveryPath, new FileTypesFilter("jar", "zip"))) {
loadModule(registry, modulePath);
}
}
/**
* Scans a directory for module directories
*
* @param discoveryPath The directory to scan
* @param registry The registry to populate with discovered modules
* @throws IOException If an error occurs scanning the directory - but not an individual module.
*/
private void scanModuleDirectories(Path discoveryPath, ModuleRegistry registry) throws IOException {
for (Path modulePath : Files.newDirectoryStream(discoveryPath, FilesUtil.DIRECTORY_FILTER)) {
loadModule(registry, modulePath);
}
}
private void loadModule(ModuleRegistry registry, Path modulePath) {
try {
Module module = moduleLoader.load(modulePath);
if (module != null) {
if (registry.add(module)) {
logger.info("Discovered module: {}", module);
} else {
logger.info("Discovered duplicate module: {}-{}, skipping", module.getId(), module.getVersion());
}
} else {
logger.warn("Not a module: {}", modulePath);
}
} catch (IOException e) {
logger.warn("Failed to load module at '{}'", modulePath, e);
}
}
}