/* * #%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-visualization, 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.input; 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.input.device.Device; import de.hshannover.f4.trust.visitmeta.input.gui.MotionControllerHandler; 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 external input {@link Device}. * {@link Device}s will be searched in a subfolder named <i>devices</i>. * * @author Bastian Hellmann * */ public class DeviceToGuiConnector { private static Logger logger = Logger.getLogger(DeviceToGuiConnector.class); private static final String DEVICE_FOLDER = "devices"; private static final String LIB_FOLDER = "lib"; private static final String NATIVE_LIBS_FOLDER = "native-libs"; /** * Initializes external input devices. Tries to load them from subfolders of * <i>devices</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 device called <i>device-1</i> with native libraries for * Linux 64bit would have the path * <i>devices/device-1/native-libs/linux/x64/</i> * * @param motionControllerHandler * a {@link MotionControllerHandler} instance to be used by all * {@link Device}s * @return a {@link List} of {@link Device} that were loaded, initialized * and started */ public static List<Device> initializeDevices( MotionControllerHandler motionControllerHandler) { List<File> deviceDirectories = FileUtils .listSubDirectories(DEVICE_FOLDER); List<Device> devices = new ArrayList<>(); List<Device> startedDevices = new ArrayList<>(); if (deviceDirectories.size() > 0) { logger.info("Device directories found: " + deviceDirectories); 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 : deviceDirectories) { Device device = null; List<URL> jarFilesForDevice = 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 device '" + subDirectory + "'"); } subdirectoryJarFiles = FileUtils.listJarFiles(subDirectory); jarFilesForDevice.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 device '" + subDirectory + "'"); } if (jarFilesForDevice.size() > 0) { ClassLoader loader = URLClassLoader .newInstance(jarFilesForClassloader .toArray(new URL[] {})); for (URL jarFile : jarFilesForDevice) { device = loadDeviceFromJarFile(loader, jarFile); if (device != null) { devices.add(device); logger.debug("Device '" + device.getName() + "' was loaded from jar-file '" + jarFile + "'"); } else { logger.warn("Could not instantiate device '" + subDirectory + "' from jar-file '" + jarFile + "'"); } } } else { logger.warn("Did not found any jar-files for device '" + subDirectory + "'"); } } if (devices.size() > 0) { startedDevices = initAndStartDevices(devices, motionControllerHandler); } else { logger.warn("Did not found any device inside '" + DEVICE_FOLDER); } } return startedDevices; } /** * Tries to initialize and start the already loaded {@link Device}s, by * calling their corresponding interface methods. * * @param devices * a {@link List} of {@link Device} that were loaded and * instantiated via Java Reflection * @param motionControllerHandler * a {@link MotionControllerHandler} instance to be used by all * {@link Device}s * @return a {@link List} of {@link Device} that were successfully * initialized and started */ private static List<Device> initAndStartDevices(List<Device> devices, MotionControllerHandler motionControllerHandler) { boolean initializationResult = false; int i = 1; int num = devices.size(); List<Device> startedDevices = new ArrayList<>(); for (Device device : devices) { device.setMotionControllerHandler(motionControllerHandler); if (device != null) { initializationResult = device.init(); if (initializationResult) { device.start(); startedDevices.add(device); } else { logger.error("Could not initialize device (" + i + "/" + num + ") '" + device.getName() + "'"); } } i++; } return startedDevices; } /** * Try to load a {@link Device} 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 Device} * from the JAR file * @param jarFile * JAR file to load the {@link Device} from * @return a instance of {@link Device}, or null if loading fails */ private static Device loadDeviceFromJarFile(ClassLoader classLoader, URL jarFile) { try { List<String> classNames = ReflectionUtils.getClassNames(jarFile); return ReflectionUtils.loadClass(classLoader, classNames, Device.class); } catch (IOException | SecurityException | IllegalArgumentException e) { logger.warn("Could not load device from " + jarFile + ": " + e); } return null; } }