/*
* #%L
* =====================================================
* _____ _ ____ _ _ _ _
* |_ _|_ __ _ _ ___| |_ / __ \| | | | ___ | | | |
* | | | '__| | | / __| __|/ / _` | |_| |/ __|| |_| |
* | | | | | |_| \__ \ |_| | (_| | _ |\__ \| _ |
* |_| |_| \__,_|___/\__|\ \__,_|_| |_||___/|_| |_|
* \____/
*
* =====================================================
*
* Hochschule Hannover
* (University of Applied Sciences and Arts, Hannover)
* Faculty IV, Dept. of Computer Science
* Ricklinger Stadtweg 118, 30459 Hannover, Germany
*
* Email: trust@f4-i.fh-hannover.de
* Website: http://trust.f4.hs-hannover.de/
*
* This file is part of visitmeta-dataservice, version 0.6.0,
* implemented by the Trust@HsH research group at the Hochschule Hannover.
* %%
* Copyright (C) 2012 - 2016 Trust@HsH
* %%
* 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.
* #L%
*/
package de.hshannover.f4.trust.visitmeta.dataservice;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import de.hshannover.f4.trust.visitmeta.interfaces.DataserviceModule;
import de.hshannover.f4.trust.visitmeta.interfaces.ifmap.ConnectionManager;
import de.hshannover.f4.trust.visitmeta.util.FileUtils;
import de.hshannover.f4.trust.visitmeta.util.OperatingSystemUtils;
import de.hshannover.f4.trust.visitmeta.util.ReflectionUtils;
/**
* Class that loads and initializes {@link DataserviceModule}.
* {@link DataserviceModule}s will be searched in a subfolder named
* <i>dataservice-modules</i>.
*
* @author Bastian Hellmann
*
*/
public class DataserviceModuleConnector {
private static Logger logger = Logger
.getLogger(DataserviceModuleConnector.class);
private static final String DATASERVICE_MODULES_FOLDER = "dataservice-modules";
private static final String LIB_FOLDER = "lib";
private static final String NATIVE_LIBS_FOLDER = "native-libs";
/**
* Initializes external input {@link DataserviceModule}s. Tries to load them
* from subfolders of <i>dataservice-modules</i> inside the projects root
* directory.
*
* Inside each subdirectory,
* <ul>
* <li>native libraries are searched in <i>native-libs</i>, with
* subdirectories for the operating system
* <ul>
* <li>windows
* <li>osx
* <li>linux
* </ul>
* and inside them for the systems architecture
* <ul>
* <li>x64
* <li>x86
* </ul>
* <li>external dependancies in <i>lib</i>
* </ul>
* For example, a {@link DataserviceModule} called <i>module-1</i> with
* native libraries for Linux 64bit would have the path
* <i>dataservice-modules/module-1/native-libs/linux/x64/</i>
*
* @param manager
* a {@link ConnectionManager} instance to be used by all
* {@link DataserviceModule}s
* @return a {@link List} of {@link DataserviceModule} that were loaded,
* initialized and started
*/
public static List<DataserviceModule> initializeModules(
ConnectionManager manager) {
List<File> moduleDirectories = FileUtils
.listSubDirectories(DATASERVICE_MODULES_FOLDER);
List<DataserviceModule> modules = new ArrayList<>();
List<DataserviceModule> startedModules = new ArrayList<>();
if (moduleDirectories.size() > 0) {
logger.info("Dataservice module directories found: "
+ moduleDirectories);
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
String osNameFolder = OperatingSystemUtils
.getOperatingSystemNameFolder(osName);
String osArchFolder = OperatingSystemUtils
.getSystemArchitectureFolder(osArch);
logger.info("Operating system information: " + osName + " v"
+ osVersion + " (" + osArch + ")");
for (File subDirectory : moduleDirectories) {
DataserviceModule module = null;
List<URL> jarFilesForDataserviceModule = new ArrayList<>();
List<URL> jarFilesForClassloader = new ArrayList<>();
List<URL> subdirectoryJarFiles = new ArrayList<>();
String nativeLibsPath = NATIVE_LIBS_FOLDER + File.separator
+ osNameFolder + File.separator + osArchFolder;
File nativeLibsFolder = FileUtils.findDirectory(subDirectory,
nativeLibsPath);
if (nativeLibsFolder != null) {
FileUtils.appendToLibraryPath(nativeLibsFolder
.getAbsolutePath());
} else {
logger.warn("Did not found native-library folder for dataservice module '"
+ subDirectory + "'");
}
subdirectoryJarFiles = FileUtils.listJarFiles(subDirectory);
jarFilesForDataserviceModule.addAll(subdirectoryJarFiles);
jarFilesForClassloader.addAll(subdirectoryJarFiles);
File libFolder = FileUtils.findDirectory(subDirectory,
LIB_FOLDER);
if (libFolder != null) {
jarFilesForClassloader.addAll(FileUtils
.listJarFiles(libFolder));
} else {
logger.warn("Did not found dependency-library folder for dataservice module '"
+ subDirectory + "'");
}
if (jarFilesForDataserviceModule.size() > 0) {
ClassLoader loader = URLClassLoader
.newInstance(jarFilesForClassloader
.toArray(new URL[] {}));
for (URL jarFile : jarFilesForDataserviceModule) {
module = loadModuleFromJarFile(loader, jarFile);
if (module != null) {
modules.add(module);
logger.debug("Dataservice module '"
+ module.getName()
+ "' was loaded from jar-file '" + jarFile
+ "'");
} else {
logger.warn("Could not instantiate dataservice module '"
+ subDirectory
+ "' from jar-file '"
+ jarFile + "'");
}
}
} else {
logger.warn("Did not found any jar-files for dataservice module '"
+ subDirectory + "'");
}
}
if (modules.size() > 0) {
startedModules = initAndStartModules(modules, manager);
} else {
logger.warn("Did not found any dataservice modules inside '"
+ DATASERVICE_MODULES_FOLDER);
}
}
return startedModules;
}
/**
* Tries to initialize and start the already loaded
* {@link DataserviceModule}s, by calling their corresponding interface
* methods.
*
* @param modules
* a {@link List} of {@link DataserviceModule} that were loaded
* and instantiated via Java Reflection
* @param manager
* a {@link ConnectionManager} instance to be used by all
* {@link DataserviceModule}s
* @return a {@link List} of {@link DataserviceModule} that were
* successfully initialized and started
*/
private static List<DataserviceModule> initAndStartModules(
List<DataserviceModule> modules, ConnectionManager manager) {
boolean initializationResult = false;
int i = 1;
int num = modules.size();
List<DataserviceModule> startedModules = new ArrayList<>();
for (DataserviceModule module : modules) {
if (module != null) {
module.setConnectionManager(manager);
initializationResult = module.init();
if (initializationResult) {
module.start();
startedModules.add(module);
} else {
logger.error("Could not initialize dataservice module ("
+ i + "/" + num + ") '" + module.getName() + "'");
}
}
i++;
}
return startedModules;
}
/**
* Try to load a {@link DataserviceModule} from a Jar-File with a given
* {@link ClassLoader} and returns a fresh instance of that class. If
* loading fails, <code>null</code> is returned.
*
* @param classLoader
* a {@link ClassLoader} that contains all needed native
* libraries and Java dependencies for loading a
* {@link DataserviceModule} from the JAR file
* @param jarFile
* JAR file to load the {@link DataserviceModule} from
* @return a instance of {@link DataserviceModule}, or null if loading fails
*/
private static DataserviceModule loadModuleFromJarFile(
ClassLoader classLoader, URL jarFile) {
try {
List<String> classNames = ReflectionUtils.getClassNames(jarFile);
return ReflectionUtils.loadClass(classLoader, classNames,
DataserviceModule.class);
} catch (IOException | SecurityException | IllegalArgumentException e) {
logger.warn("Could not load dataservice module from " + jarFile
+ ": " + e);
}
return null;
}
}