/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapid_i.deployment.update.client; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.jar.JarFile; import java.util.logging.Level; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import com.rapid_i.Launcher; import com.rapidminer.RapidMiner; import com.rapidminer.io.process.XMLTools; import com.rapidminer.tools.FileSystemService; import com.rapidminer.tools.LogService; import com.rapidminer.tools.ParameterService; import com.rapidminer.tools.plugin.Plugin; /** * * @author Simon Fischer * */ public class ManagedExtension { /** Maps {@link ManagedExtension#getPackageId()} to the ManagedExtension itself. */ private static final Map<String,ManagedExtension> MANAGED_EXTENSIONS = new HashMap<String,ManagedExtension>(); private final SortedSet<String> installedVersions = new TreeSet<String>(new Comparator<String>() { @Override public int compare(String o1, String o2) { return normalizeVersion(o1).compareTo(normalizeVersion(o2)); } }); private final String packageID; private final String name; private String selectedVersion; private boolean active; private boolean installedInHomeDir; private final String license; private ManagedExtension(Element element, boolean homeDir) { this.installedInHomeDir = homeDir; this.packageID = XMLTools.getTagContents(element, "id"); this.name = XMLTools.getTagContents(element, "name"); this.license = XMLTools.getTagContents(element, "license"); this.active = Boolean.parseBoolean(XMLTools.getTagContents(element, "active")); this.selectedVersion = XMLTools.getTagContents(element, "selected-version"); NodeList versions = element.getElementsByTagName("installed-version"); for (int i = 0; i < versions.getLength(); i++) { installedVersions.add(((Element)versions.item(i)).getTextContent()); } } private ManagedExtension(String id, String name, String license) { super(); this.packageID = id; this.name = name; this.license = license; this.selectedVersion = null; //installedVersions.add(version); this.setActive(true); } public void setActive(boolean active) { this.active = active; } public boolean isActive() { return active; } public String getName() { return name; } private File findFile() { return findFile(selectedVersion); } private File findFile(String version) { for (File dir : getManagedExtensionsDirectories()) { File file = new File(dir, packageID+"-"+version+".jar"); if (file.exists()) { return file; } } return null; } /** * This method returns the jar file of the extension or throws an {@link FileNotFoundException} * exception. */ public JarFile findArchive() throws IOException { File findFile = findFile(); if (findFile != null) return new JarFile(findFile); throw new FileNotFoundException("Could not access file of installed extension."); } public JarFile findArchive(String version) throws IOException { File findFile = findFile(version); if (findFile == null) { throw new IOException("Failed to find extension jar file (extension "+getName()+", version "+version+")."); } else { try { return new JarFile(findFile); } catch (IOException e) { throw new IOException("Failed to open jar file "+findFile+": "+e, e); } } } public String getSelectedVersion() { return selectedVersion; } private static File[] getManagedExtensionsDirectories() { File local = getUserExtensionsDir(); try { File global = getGlobalExtensionsDir(); return new File[] { global, local }; } catch (IOException e) { LogService.getRoot().warning("None of the properties "+RapidMiner.PROPERTY_RAPIDMINER_INIT_PLUGINS+" and "+Launcher.PROPERTY_RAPIDMINER_HOME+" is set. No globally installed extensions will be loaded."); return new File[] { local }; } } private static File getGlobalExtensionsDir() throws IOException { return new File(Plugin.getPluginLocation(), "managed"); } public static File getUserExtensionsDir() { return FileSystemService.getUserConfigFile("managed"); } private Element toXML(Document doc) { Element result = doc.createElement("extension"); XMLTools.setTagContents(result, "id", packageID); XMLTools.setTagContents(result, "name", name); XMLTools.setTagContents(result, "active", ""+active); XMLTools.setTagContents(result, "license", license); XMLTools.setTagContents(result, "selected-version", getSelectedVersion()); for (String v : installedVersions) { Element elem = doc.createElement("installed-version"); result.appendChild(elem); elem.appendChild(doc.createTextNode(v)); } return result; } private static Document toXML(boolean inHomeDir) throws ParserConfigurationException { Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element root = doc.createElement("extensions"); doc.appendChild(root); for (ManagedExtension ext : MANAGED_EXTENSIONS.values()) { if (ext.installedInHomeDir == inHomeDir) { root.appendChild(ext.toXML(doc)); } } return doc; } public static void saveConfiguration() { try { File localDir = getUserExtensionsDir(); if (!localDir.exists()) { localDir.mkdirs(); } XMLTools.stream(toXML(true), new File(localDir, "extensions.xml"), Charset.forName("UTF-8")); } catch (Exception e) { LogService.getRoot().log(Level.WARNING, "Cannot save local user extensions: "+e, e); } try { File globalDir = getGlobalExtensionsDir(); if (!globalDir.exists()) { globalDir.mkdirs(); } XMLTools.stream(toXML(false), new File(globalDir, "extensions.xml"), Charset.forName("UTF-8")); } catch (Exception e) { LogService.getRoot().log(Level.WARNING, "Cannot save global extensions: "+e, e); } LogService.getRoot().config("Saved extension state."); } /** Reads configuration files. */ private static void readConfiguration() { MANAGED_EXTENSIONS.clear(); try { File file = new File(getUserExtensionsDir(), "extensions.xml"); if (file.exists()) { parse(XMLTools.parse(file), true); } } catch (Exception e) { LogService.getRoot().log(Level.WARNING, "Failed to read local extension state: "+e, e); } try { File file = new File(getGlobalExtensionsDir(), "extensions.xml"); if (file.exists()) { parse(XMLTools.parse(file), false); } } catch (Exception e) { LogService.getRoot().log(Level.WARNING, "Failed to read global extension state: "+e, e); } LogService.getRoot().config("Read extension state."); } private static void parse(Document parse, boolean inHomeDir) { NodeList extensions = parse.getDocumentElement().getElementsByTagName("extension"); for (int i = 0; i < extensions.getLength(); i++) { register(new ManagedExtension((Element)extensions.item(i), inHomeDir)); } } private static void register(ManagedExtension ext) { MANAGED_EXTENSIONS.put(ext.packageID, ext); } public static List<File> getActivePluginJars() { List<File> result = new LinkedList<File>(); for (ManagedExtension ext : MANAGED_EXTENSIONS.values()) { if (ext.isActive()) { File file = ext.findFile(); if (file != null){ result.add(file); } } } return result; } public static ManagedExtension get(String packageId) { return MANAGED_EXTENSIONS.get(packageId); } public static ManagedExtension getOrCreate(String packageId, String packageName, String license) { ManagedExtension ext = MANAGED_EXTENSIONS.get(packageId); if (ext == null) { ext = new ManagedExtension(packageId, packageName, license); ext.installedInHomeDir = isInstallToHome(); MANAGED_EXTENSIONS.put(packageId, ext); saveConfiguration(); } return ext; } public String getPackageId() { return packageID; } public void addAndSelectVersion(String version) { this.selectedVersion = version; installedVersions.add(version); saveConfiguration(); } public File getDestinationFile(String version) throws IOException { if (installedInHomeDir) { return new File(getUserExtensionsDir(), packageID+"-"+version+".jar"); } else { makeGlobalManagedExtensionsDir(); return new File(getGlobalExtensionsDir(), packageID+"-"+version+".jar"); } } private static void makeGlobalManagedExtensionsDir() throws IOException { File managedDir = getGlobalExtensionsDir(); if (!managedDir.exists()) { if (!managedDir.mkdirs()) { throw new IOException("Cannot create directory "+managedDir+". Make sure you have administrator privileges or check property "+UpdateManager.PARAMETER_INSTALL_TO_HOME+" in the preferences."); } } } private static boolean isInstallToHome() { return !"false".equals(ParameterService.getParameterValue(UpdateManager.PARAMETER_INSTALL_TO_HOME)); } public static void init() { readConfiguration(); } public static Collection<ManagedExtension> getAll() { return MANAGED_EXTENSIONS.values(); } public Set<String> getInstalledVersions() { return installedVersions; } public void setSelectedVersion(String version) { this.selectedVersion = version; } public String getLatestInstalledVersionBefore(String version) { SortedSet<String> head = installedVersions.headSet(version); return head.isEmpty() ? null : head.last(); } public String getLatestInstalledVersion() { return installedVersions.isEmpty() ? null : installedVersions.last(); } /** Adds leading zeroes until the version is of the form * "XX.XX.XXX". */ public static String normalizeVersion(String version) { if (version == null) { return null; } String[] split = version.split("\\."); if (split.length < 3) { String[] newSplit = new String[3]; System.arraycopy(split, 0, newSplit, 0, split.length); for (int i = split.length; i < newSplit.length; i++) { newSplit[i] = "0"; } split = newSplit; } StringBuilder result = new StringBuilder(); for (int i = 0; i < split.length; i++) { int lastDigit; for (lastDigit = split[i].length()-1; lastDigit >= 0; lastDigit--) { if (Character.isDigit(split[i].charAt(lastDigit))) { break; } } String letters = split[i].substring(lastDigit+1); String digits = split[i].substring(0, lastDigit+1); int desiredLength = i == split.length-1 ? 3 : 2; while (digits.length() < desiredLength) { digits = "0"+digits; } if (i != 0) { result.append('.'); } result.append(digits).append(letters); } return result.toString(); } private static Collection<ManagedExtension> getActiveByLicense(String license) { List<ManagedExtension> result = new LinkedList<ManagedExtension>(); for (ManagedExtension ext : MANAGED_EXTENSIONS.values()) { if (ext.isActive() && ext.license != null && ext.license.equals(license)) { result.add(ext); } } return result; } static void checkForLicenseConflicts() { Collection<ManagedExtension> lgpl = getActiveByLicense("GPL"); Collection<ManagedExtension> comm = getActiveByLicense(UpdateManager.COMMERCIAL_LICENSE_NAME); if (!lgpl.isEmpty() && !comm.isEmpty()) { new LicenseConflictDialog(lgpl, comm).setVisible(true); } } /** Returns true if uninstall was successful. */ public boolean uninstallActiveVersion() { File file = findFile(selectedVersion); // we only mark as uninstalled if // (1) File does not exist, probably was removed manually // (2) We were able to remove it (requires administrator permissions if installed globally). if (file != null && file.exists()) file.delete(); installedVersions.remove(selectedVersion); selectedVersion = null; active = false; if (installedVersions.isEmpty()) { MANAGED_EXTENSIONS.remove(this.getPackageId()); } saveConfiguration(); return true; } }