/** * ***************************************************************************** * * Copyright (c) 2012 Oracle Corporation. * * All rights reserved. This program and the accompanying materials are made * available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Winston Prakash * ***************************************************************************** */ package org.eclipse.hudson.plugins; import hudson.PluginManager.FailedPlugin; import hudson.Util; import hudson.model.Hudson; import hudson.util.VersionNumber; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility that provides information about the plugins that are already * installed * * @author Winston Prakash */ public final class InstalledPluginManager { private final Logger logger = LoggerFactory.getLogger(InstalledPluginManager.class); private final Map<String, InstalledPluginInfo> installedPluginInfos = new HashMap<String, InstalledPluginInfo>(); private final File pluginsDir; public InstalledPluginManager(File dir) { pluginsDir = dir; loadInstalledPlugins(); } public File getPluginsDir() { return pluginsDir; } public Set<String> getInstalledPluginNames() { return installedPluginInfos.keySet(); } public InstalledPluginInfo getInstalledPlugin(String name) { return installedPluginInfos.get(name); } public boolean isInstalled(String name) { return installedPluginInfos.keySet().contains(name); } public void loadInstalledPlugins() { File[] hpiArchives = pluginsDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith("hpi") || name.endsWith("hpl"); } }); if ((hpiArchives != null) && (hpiArchives.length > 0)) { for (File archive : hpiArchives) { try { InstalledPluginInfo installedPluginInfo = new InstalledPluginInfo(archive); installedPluginInfos.put(installedPluginInfo.getShortName(), installedPluginInfo); } catch (IOException exc) { logger.warn("Failed to load plugin ", exc); } } } } static final class Dependency { private final String shortName; private final String version; private final boolean optional; public Dependency(String s) { int idx = s.indexOf(':'); if (idx == -1) { throw new IllegalArgumentException("Illegal dependency specifier " + s); } this.shortName = s.substring(0, idx); this.version = s.substring(idx + 1); boolean isOptional = false; String[] osgiProperties = s.split(";"); for (int i = 1; i < osgiProperties.length; i++) { String osgiProperty = osgiProperties[i].trim(); if (osgiProperty.equalsIgnoreCase("resolution:=optional")) { isOptional = true; } } this.optional = isOptional; } public String getShortName() { return shortName; } public String getVersion() { return version; } public boolean isOptional() { return optional; } @Override public String toString() { return shortName + " (" + version + ")"; } } public final static class InstalledPluginInfo { private final List<Dependency> dependencies = new ArrayList<Dependency>(); private File hpiArchive; private String shortName; private String wikiUrl; private String longName; private String version; public InstalledPluginInfo(File archive) throws IOException { hpiArchive = archive; parseManifest(); } void parseManifest() throws IOException { Manifest manifest; JarFile jarfile = null; boolean isLinked = hpiArchive.getName().endsWith(".hpl"); if (isLinked) { // resolve the .hpl file to the location of the manifest file BufferedReader br = new BufferedReader(new FileReader(hpiArchive)); String firstLine = br.readLine(); if (firstLine.startsWith("Manifest-Version:")) { // this is the manifest already } else { // indirection hpiArchive = resolve(hpiArchive, firstLine); } // then parse manifest FileInputStream in = new FileInputStream(hpiArchive); try { manifest = new Manifest(in); } catch (IOException e) { throw new IOException("Failed to load " + hpiArchive, e); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(br); } } else { jarfile = new JarFile(hpiArchive); manifest = jarfile.getManifest(); } final Attributes attributes = manifest.getMainAttributes(); shortName = attributes.getValue("Short-Name"); if (shortName == null) { attributes.getValue("Extension-Name"); } if (shortName == null) { shortName = hpiArchive.getName(); int idx = shortName.lastIndexOf('.'); if (idx >= 0) { shortName = shortName.substring(0, idx); } } wikiUrl = attributes.getValue("Url"); longName = attributes.getValue("Long-Name"); if (longName == null) { longName = shortName; } version = attributes.getValue("Plugin-Version"); if (version == null) { version = attributes.getValue("Implementation-Version"); } String deps = attributes.getValue("Plugin-Dependencies"); if (deps != null) { for (String dep : deps.split(",")) { dependencies.add(new Dependency(dep)); } } if (jarfile != null) { jarfile.close(); } } public boolean isFailedToLoad() { for (FailedPlugin p : Hudson.getInstance().getPluginManager().getFailedPlugins()) { if (p.name.equals(shortName)) { return true; } } return false; } public boolean isEnabled() { File disabledMarker = new File(hpiArchive.getPath() + ".disabled"); return !disabledMarker.exists() && !isFailedToLoad(); } public void setEnable(boolean enable) throws IOException { File disabledMarker = new File(hpiArchive.getPath() + ".disabled"); if (enable && disabledMarker.exists()) { FileUtils.deleteQuietly(disabledMarker); } if (!enable && !disabledMarker.exists()) { FileUtils.touch(disabledMarker); } } public boolean isDowngrdable() throws IOException { File backupFile = Util.changeExtension(hpiArchive, ".bak"); if (backupFile.exists()) { InstalledPluginInfo bakPluginInfo = new InstalledPluginInfo(backupFile); return !bakPluginInfo.version.trim().equals(version); } return false; } public String getBackupVersion() throws IOException { File backupFile = Util.changeExtension(hpiArchive, ".bak"); if (backupFile.exists()) { InstalledPluginInfo bakPluginInfo = new InstalledPluginInfo(backupFile); return bakPluginInfo.version; } return ""; } public boolean isPinned() { File pinnedMarker = new File(hpiArchive.getPath() + ".pinned"); return pinnedMarker.exists(); } public void unpin() { File pinnedMarker = new File(hpiArchive.getPath() + ".pinned"); if (pinnedMarker.exists()) { FileUtils.deleteQuietly(pinnedMarker); } } public void downgade() throws IOException { File backupFile = Util.changeExtension(hpiArchive, ".bak"); if (backupFile.exists()) { hpiArchive.delete(); } if (!backupFile.renameTo(hpiArchive)) { throw new IOException("Failed to rename " + backupFile + " to " + hpiArchive); } } public VersionNumber getVersionNumber() { return new VersionNumber(version); } public boolean isOlderThan(VersionNumber v) { try { return getVersionNumber().compareTo(v) < 0; } catch (IllegalArgumentException e) { // Assume older return true; } } public String getLongName() { return longName; } public String getShortName() { return shortName; } public String getVersion() { return version; } public String getWikiUrl() { return wikiUrl; } public List<Dependency> getDependencies() { return dependencies; } @Override public String toString() { return "[Plugin Name:" + shortName + " Long Name:" + longName + " Version:" + version + " Wiki:" + wikiUrl + "]"; } private File resolve(File base, String relative) { File rel = new File(relative); if (rel.isAbsolute()) { return rel; } else { return new File(base.getParentFile(), relative); } } } }