/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package com.bc.ceres.core.runtime.internal; import com.bc.ceres.core.Assert; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.core.CoreException; import com.bc.ceres.core.SubProgressMonitor; import com.bc.ceres.core.CanceledException; import static com.bc.ceres.core.runtime.Constants.MODULE_MANIFEST_NAME; import com.bc.ceres.core.runtime.ModuleState; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.BufferedWriter; import java.io.FileWriter; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * A loader for modules. */ public class ModuleLoader { private static final String MODULE_LOCATION_REJECTED_0 = "Module location rejected: [{0}]"; private Logger logger; private HashSet<URL> visitedLocations; public ModuleLoader(Logger logger) { Assert.notNull(logger, "logger"); this.logger = logger; this.visitedLocations = new HashSet<>(32); } public ModuleImpl[] loadModules(ClassLoader classLoader, ProgressMonitor pm) throws IOException { Assert.notNull(classLoader, "classLoader"); Assert.notNull(pm, "pm"); Enumeration<URL> resources = classLoader.getResources(MODULE_MANIFEST_NAME); ArrayList<URL> resourceList = new ArrayList<>(32); while (resources.hasMoreElements()) { resourceList.add(resources.nextElement()); } pm.beginTask("Scanning classpath for modules", resourceList.size()); try { ArrayList<ModuleImpl> moduleList = new ArrayList<>(32); for (URL manifestUrl : resourceList) { URL locationUrl = UrlHelper.manifestToLocationUrl(manifestUrl); if (locationUrl != null) { if (!visitedLocations.contains(locationUrl)) { try { ModuleImpl module = new ModuleReader(logger).readFromLocation(locationUrl); module.setState(ModuleState.INSTALLED); moduleList.add(module); visitedLocations.add(locationUrl); } catch (CoreException e) { logger.log(Level.WARNING, MessageFormat.format(MODULE_LOCATION_REJECTED_0, locationUrl), e); } } } else { logger.log(Level.WARNING, MessageFormat.format(MODULE_LOCATION_REJECTED_0, manifestUrl)); } pm.worked(1); } return moduleList.toArray(new ModuleImpl[moduleList.size()]); } finally { pm.done(); } } public ModuleImpl[] loadModules(File modulesDir, ProgressMonitor pm) throws IOException { Assert.notNull(modulesDir, "modulesDir"); Assert.notNull(pm, "pm"); if (!modulesDir.isDirectory()) { throw new IOException(MessageFormat.format("Directory not found: [{0}]", modulesDir)); } File[] moduleFiles = modulesDir.listFiles(new FileFilter() { public boolean accept(File file) { return JarFilenameFilter.isJarName(file.getPath()) || file.isDirectory(); } }); if (moduleFiles == null) { return ModuleImpl.EMPTY_ARRAY; } pm.beginTask("Scanning directory for modules", moduleFiles.length); try { ArrayList<ModuleImpl> moduleList = new ArrayList<>(32); for (File moduleFile : moduleFiles) { File uninstallMarker = new File(moduleFile.getPath() + RuntimeImpl.UNINSTALL_FILE_SUFFIX); int toWork = 1; if (uninstallMarker.exists()) { logger.warning(MessageFormat.format("Skipping uninstalled (but not yet deleted) module file [{0}].", moduleFile)); } else { URL locationUrl = UrlHelper.fileToUrl(moduleFile); if (!visitedLocations.contains(locationUrl)) { try { ModuleImpl module = loadModule(moduleFile, SubProgressMonitor.create(pm, 1)); moduleList.add(module); visitedLocations.add(locationUrl); toWork = 0; } catch (CoreException e) { logger.log(Level.WARNING, MessageFormat.format(MODULE_LOCATION_REJECTED_0, locationUrl), e); } } } pm.worked(toWork); } return moduleList.toArray(new ModuleImpl[moduleList.size()]); } finally { pm.done(); } } public ModuleImpl loadModule(File moduleFile, ProgressMonitor pm) throws CoreException { pm.beginTask("Loading module", 2); try { ModuleImpl module = new ModuleReader(logger).readFromLocation(moduleFile); pm.worked(1); if ("dir".equalsIgnoreCase(module.getPackaging()) && !moduleFile.isDirectory()) { logger.info(MessageFormat.format("Unpacking [{0}]...", moduleFile.getName())); File archiveFile = moduleFile; try { moduleFile = unpack(archiveFile, module.isNative(), SubProgressMonitor.create(pm, 1)); } catch (IOException e) { throw new CoreException("Failed to install module [" + moduleFile + "]", e); } finally { if (!archiveFile.delete()) { logger.warning(MessageFormat.format("Failed to delete file [{0}], reason unknown.", archiveFile)); } } module = new ModuleReader(logger).readFromLocation(moduleFile); } else { pm.worked(1); } module.setState(ModuleState.INSTALLED); return module; } finally { pm.done(); } } private File unpack(File archiveFile, boolean isNative, ProgressMonitor pm) throws IOException, CanceledException { String dirName = IOHelper.getBaseName(archiveFile); File moduleDir = new File(archiveFile.getParent(), dirName); List<String> installedFiles = IOHelper.unpack(archiveFile, moduleDir, isNative, pm); try { writeInstallInfo(moduleDir, installedFiles); } catch (IOException e) { logger.log(Level.SEVERE, "Failed to write install info file.", e); } return moduleDir; } private void writeInstallInfo(File moduleDir, List<String> installedFiles) throws IOException { File installInfoFile = new File(moduleDir, ModuleInstaller.INSTALL_INFO_XML); try (BufferedWriter writer = new BufferedWriter(new FileWriter(installInfoFile))) { InstallInfo installInfo = new InstallInfo(installedFiles.toArray(new String[installedFiles.size()])); installInfo.write(writer); } } }