| /******************************************************************************* |
| * Copyright (c) 2000, 2010 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.update.internal.core; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.net.*; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.update.configuration.IConfiguredSite; |
| import org.eclipse.update.configurator.ConfiguratorUtils; |
| import org.eclipse.update.configurator.IPlatformConfiguration; |
| import org.eclipse.update.core.IFeature; |
| import org.eclipse.update.core.IFeatureReference; |
| import org.eclipse.update.core.IImport; |
| import org.eclipse.update.core.IPluginEntry; |
| import org.eclipse.update.core.ISite; |
| import org.eclipse.update.core.VersionedIdentifier; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.Constants; |
| import org.osgi.service.packageadmin.PackageAdmin; |
| |
| /** |
| * This class manages the configurations. |
| */ |
| |
| public class SiteStatusAnalyzer { |
| |
| private static final String SOURCE_BUNDLES_PATH = "org.eclipse.equinox.source/source.info"; //$NON-NLS-1$ |
| private static final String ID = "org.eclipse.update.core"; //$NON-NLS-1$ |
| private static List allConfiguredFeatures; /*VersionedIdentifier */ |
| private LocalSite siteLocal; |
| |
| // A list of versionedIdentifiers for source bundles; initialized on demand. |
| private List sourceBundles = null; |
| |
| /** |
| * |
| */ |
| public SiteStatusAnalyzer(LocalSite siteLocal) { |
| this.siteLocal = siteLocal; |
| } |
| |
| /* |
| * check if the Plugins of the feature are on the plugin path |
| * If all the plugins are on the plugin path, and the version match and there is no other version -> HAPPY |
| * If all the plugins are on the plugin path, and the version match and there is other version -> AMBIGUOUS |
| * If some of the plugins are on the plugin path, but not all -> UNHAPPY |
| * Check on all ConfiguredSites |
| */ |
| private IStatus getStatus(IFeature feature) { |
| |
| // validate site |
| ISite featureSite = feature.getSite(); |
| if (featureSite == null) { |
| if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION) |
| UpdateCore.debug("Cannot determine status of feature:" + feature.getLabel() + ". Site is NULL."); //$NON-NLS-1$ //$NON-NLS-2$ |
| String msg = NLS.bind(Messages.SiteLocal_UnableToDetermineFeatureStatusSiteNull, (new Object[] {feature.getURL()})); |
| return createStatus(IStatus.ERROR, IFeature.STATUS_AMBIGUOUS, msg, null); |
| } |
| |
| // validate configured site |
| ConfiguredSite cSite = (ConfiguredSite) featureSite.getCurrentConfiguredSite(); |
| if (cSite == null) { |
| if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION) |
| UpdateCore.warn("Cannot determine status of feature: " + feature.getLabel() + ". Configured Site is NULL."); //$NON-NLS-1$ //$NON-NLS-2$ |
| String msg = NLS.bind(Messages.SiteLocal_UnableToDetermineFeatureStatusConfiguredSiteNull, (new Object[] {feature.getURL()})); |
| return createStatus(IStatus.ERROR, IFeature.STATUS_AMBIGUOUS, msg, null); |
| } |
| |
| // check if disable, if so return |
| IFeatureReference ref = cSite.getSite().getFeatureReference(feature); |
| if (ref != null) { |
| if (!cSite.getConfigurationPolicy().isConfigured(ref)) |
| return createStatus(IStatus.OK, IFeature.STATUS_DISABLED, "", null); //$NON-NLS-1$ |
| } else { |
| if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION) |
| UpdateCore.warn("Unable to find reference for feature " + feature + " in site " + cSite.getSite().getURL()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| // check if broken |
| IStatus status = cSite.getBrokenStatus(feature); |
| if (status.getSeverity() != IStatus.OK) { |
| if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_CONFIGURATION) |
| UpdateCore.debug("Feature broken:" + feature.getLabel() + ".Site:" + cSite.toString()); //$NON-NLS-1$ //$NON-NLS-2$ |
| return status; |
| } |
| |
| // check ambiguous against registry [17015] |
| IPluginEntry[] featuresEntries = feature.getPluginEntries(); |
| return status(feature, featuresEntries); |
| } |
| |
| /* |
| * check if the Plugins of the feature are on the plugin path |
| * If all the plugins are on the plugin path, and the version match and there is no other version -> HAPPY |
| * If all the plugins are on the plugin path, and the version match and there is other version -> AMBIGUOUS |
| * If some of the plugins are on the plugin path, but not all -> UNHAPPY |
| * Check on all ConfiguredSites |
| */ |
| public IStatus getFeatureStatus(IFeature feature) throws CoreException { |
| |
| IFeature childFeature = null; |
| IStatus childStatus; |
| |
| IFeatureReference[] children = feature.getIncludedFeatureReferences(); |
| |
| // consider disable |
| // check the current feature |
| String msg = Messages.SiteLocal_FeatureDisable; |
| int code = IFeature.STATUS_DISABLED; |
| IStatus featureStatus = getStatus(feature); |
| MultiStatus multiTemp = new MultiStatus(featureStatus.getPlugin(), code, msg, null); |
| if (featureStatus.getSeverity() == IStatus.ERROR) { |
| if (featureStatus.isMultiStatus()) { |
| multiTemp.addAll(featureStatus); |
| } else { |
| multiTemp.add(featureStatus); |
| } |
| } |
| // preserve the worse code through the method (self assesment + children assessment) |
| if (featureStatus.getCode() > code) |
| code = featureStatus.getCode(); |
| |
| // do not check children if feature is disable |
| if (!(code == IFeature.STATUS_DISABLED)) { |
| for (int i = 0; i < children.length; i++) { |
| if (!UpdateManagerUtils.isOptional(children[i])) { |
| try { |
| childFeature = children[i].getFeature(null); |
| } catch (CoreException e) { |
| childFeature = null; |
| if (!UpdateManagerUtils.isOptional(children[i])) |
| UpdateCore.warn("Error retrieving feature:" + children[i]); //$NON-NLS-1$ |
| } |
| |
| if (childFeature == null) { |
| UpdateCore.warn("getFeatureStatus: Feature is null for:" + children[i]); //$NON-NLS-1$ |
| // Unable to find children feature, broken |
| Object featureAsPrintableObject = children[i].getURL(); |
| featureAsPrintableObject = children[i].getVersionedIdentifier(); |
| String msg1 = NLS.bind(Messages.SiteLocal_NestedFeatureUnavailable, (new Object[] {featureAsPrintableObject})); |
| multiTemp.add(createStatus(IStatus.ERROR, IFeature.STATUS_UNHAPPY, msg1, null)); |
| if (IFeature.STATUS_UNHAPPY > code) |
| code = IFeature.STATUS_UNHAPPY; |
| } else { |
| childStatus = getFeatureStatus(childFeature); |
| // do not add the status, add the children status as getFeatureStatus |
| // returns a multiStatus |
| if (childStatus.getCode() == IFeature.STATUS_DISABLED) { |
| VersionedIdentifier versionID = childFeature.getVersionedIdentifier(); |
| String featureVer = (versionID == null) ? "" : versionID.getVersion().toString(); //$NON-NLS-1$ |
| String msg1 = NLS.bind(Messages.SiteLocal_NestedFeatureDisable, (new String[] {childFeature.getLabel(), featureVer})); |
| multiTemp.add(createStatus(IStatus.ERROR, childStatus.getCode(), msg1, null)); |
| if (IFeature.STATUS_UNHAPPY > code) |
| code = IFeature.STATUS_UNHAPPY; |
| } |
| if (childStatus.getSeverity() != IStatus.OK) { |
| VersionedIdentifier versionID = childFeature.getVersionedIdentifier(); |
| String featureVer = (versionID == null) ? "" : versionID.getVersion().toString(); //$NON-NLS-1$ |
| String msg1 = NLS.bind(Messages.SiteLocal_NestedFeatureUnHappy, (new String[] {childFeature.getLabel(), featureVer})); |
| multiTemp.add(createStatus(IStatus.ERROR, childStatus.getCode(), msg1, null)); |
| if (childStatus.getCode() > code) |
| code = childStatus.getCode(); |
| } |
| } |
| } |
| } |
| } |
| |
| // set message |
| switch (code) { |
| case IFeature.STATUS_HAPPY : |
| msg = Messages.SiteLocal_FeatureHappy; |
| break; |
| case IFeature.STATUS_UNHAPPY : |
| msg = Messages.SiteLocal_FeatureUnHappy; |
| break; |
| case IFeature.STATUS_AMBIGUOUS : |
| msg = Messages.SiteLocal_FeatureAmbiguous; |
| break; |
| case IFeature.STATUS_DISABLED : |
| msg = Messages.SiteLocal_FeatureDisable; |
| break; |
| default : |
| msg = Messages.SiteLocal_FeatureStatusUnknown; |
| break; |
| } |
| MultiStatus multi = new MultiStatus(featureStatus.getPlugin(), code, msg, null); |
| multi.addAll(multiTemp); |
| return multi; |
| } |
| |
| /* |
| * compute the status based on getStatus() rules |
| */ |
| private IStatus status(IFeature pluginsOriginatorFeature, IPluginEntry[] featurePlugins) { |
| VersionedIdentifier featurePluginID; |
| |
| String happyMSG = Messages.SiteLocal_FeatureHappy; |
| String ambiguousMSG = Messages.SiteLocal_FeatureAmbiguous; |
| IStatus featureStatus = createStatus(IStatus.OK, IFeature.STATUS_HAPPY, "", null); //$NON-NLS-1$ |
| MultiStatus multi = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_AMBIGUOUS, ambiguousMSG, null); |
| PackageAdmin pkgAdmin = UpdateCore.getPlugin().getPackageAdmin(); |
| |
| // is Ambigous if we find a plugin from the feature |
| // with a different version and not the one we are looking |
| for (int i = 0; i < featurePlugins.length; i++) { |
| MultiStatus tempmulti = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_AMBIGUOUS, ambiguousMSG, null); |
| featurePluginID = featurePlugins[i].getVersionedIdentifier(); |
| boolean found = false; |
| |
| String singleVersionRange = '[' + featurePluginID.getVersion().toString() + ',' + featurePluginID.getVersion().toString() + ']'; |
| Bundle[] bundles = pkgAdmin.getBundles(featurePluginID.getIdentifier(), singleVersionRange); |
| if (bundles != null && bundles.length == 1) { |
| found = true; |
| continue; |
| } |
| |
| // Check if there is another feature with this plugin (but different version) |
| // log it |
| bundles = pkgAdmin.getBundles(featurePluginID.getIdentifier(), null); |
| for (int j = 0; bundles != null && j < bundles.length && !found; j++) { |
| String bundleVersion = (String) bundles[j].getHeaders().get(Constants.BUNDLE_VERSION); |
| IFeature feature = getFeatureForId(new VersionedIdentifier(bundles[j].getSymbolicName(), bundleVersion)); |
| if ((feature != null) && (!isFeaturePatchOfThisFeature(pluginsOriginatorFeature, feature))) { |
| String msg = null; |
| String label = feature.getLabel(); |
| String featureVersion = feature.getVersionedIdentifier().getVersion().toString(); |
| Object[] values = new Object[] {bundles[j].getSymbolicName(), featurePluginID.getVersion(), bundleVersion, label, featureVersion}; |
| msg = NLS.bind(Messages.SiteLocal_TwoVersionSamePlugin2, values); |
| UpdateCore.warn("Found another version of the same plugin on the path:" + bundles[j].getSymbolicName() + " " + bundleVersion); //$NON-NLS-1$ //$NON-NLS-2$ |
| tempmulti.add(createStatus(IStatus.ERROR, IFeature.STATUS_AMBIGUOUS, msg, null)); |
| } else { |
| found = true; |
| } |
| |
| } |
| |
| // check whether the plugin is a source bundle |
| // that has not been configured into the runtime |
| if (!found) { |
| loadSourceBundlesList(); |
| for (Iterator iter = sourceBundles.iterator(); iter.hasNext();) { |
| VersionedIdentifier nextId = (VersionedIdentifier) iter.next(); |
| if (featurePluginID.equals(nextId)) { |
| found = true; |
| break; |
| } |
| } |
| } |
| |
| // if we haven't found the exact plugin, add the children |
| // of tempMulti (i,e the other we found) |
| // if we have no children, we have a problem as a required plugin is not there at all |
| if (!found) { |
| if (tempmulti.getChildren().length > 0) { |
| multi.addAll(tempmulti); |
| } else { |
| if (multi.getCode() != IFeature.STATUS_UNHAPPY) { |
| String unhappyMSG = Messages.SiteLocal_FeatureUnHappy; |
| MultiStatus newMulti = new MultiStatus(featureStatus.getPlugin(), IFeature.STATUS_UNHAPPY, unhappyMSG, null); |
| newMulti.addAll(multi); |
| multi = newMulti; |
| } |
| String msg = NLS.bind(Messages.SiteLocal_NoPluginVersion, (new String[] {featurePluginID.getIdentifier()})); |
| multi.add(createStatus(IStatus.ERROR, IFeature.STATUS_UNHAPPY, msg, null)); |
| } |
| } |
| } |
| |
| if (!multi.isOK()) |
| return multi; |
| |
| // we return happy as we consider the isBroken verification has been done |
| return createStatus(IStatus.OK, IFeature.STATUS_HAPPY, happyMSG, null); |
| } |
| |
| public static File toFile(URL url) { |
| try { |
| if (!"file".equalsIgnoreCase(url.getProtocol())) //$NON-NLS-1$ |
| return null; |
| //assume all illegal characters have been properly encoded, so use URI class to unencode |
| return new File(new URI(url.toExternalForm())); |
| } catch (Exception e) { |
| //URL contains unencoded characters |
| return new File(url.getFile()); |
| } |
| } |
| |
| /** |
| * Get the contents of the source bundles text file. |
| */ |
| private void loadSourceBundlesList() { |
| if (sourceBundles != null) |
| return; |
| |
| sourceBundles = new ArrayList(32); |
| IPlatformConfiguration config = ConfiguratorUtils.getCurrentPlatformConfiguration(); |
| URL configLocation = config.getConfigurationLocation(); |
| if (configLocation == null) |
| return; |
| // Drop off /org.eclipse.update/platform.xml |
| File configDir = toFile(configLocation); |
| configDir = configDir.getParentFile(); |
| if (configDir == null) |
| return; |
| configDir = configDir.getParentFile(); |
| if (configDir == null) |
| return; |
| File sourceBundlesFile = new File(configDir, SOURCE_BUNDLES_PATH); |
| |
| try { |
| BufferedReader reader = new BufferedReader(new FileReader(sourceBundlesFile)); |
| String line; |
| try { |
| while ((line = reader.readLine()) != null) { |
| if (line.startsWith("#")) |
| continue; |
| line = line.trim();// symbolicName,version,other ignored stuff |
| if (line.length() == 0) |
| continue; |
| |
| StringTokenizer tok = new StringTokenizer(line, ",", true); |
| String symbolicName = tok.nextToken(); |
| if (symbolicName.equals(",")) |
| continue; |
| else |
| tok.nextToken(); // , |
| |
| String version = tok.nextToken(); |
| if (version.equals(",")) |
| continue; |
| else |
| tok.nextToken(); // , |
| |
| VersionedIdentifier sourceId = new VersionedIdentifier(symbolicName, version); |
| sourceBundles.add(sourceId); |
| } |
| } finally { |
| try { |
| reader.close(); |
| } catch (IOException ex) { |
| // ignore |
| } |
| } |
| } catch (MalformedURLException e) { |
| UpdateCore.log(new Status(IStatus.ERROR, ID, "Error occurred while reading source bundle list.", e)); //$NON-NLS-1$ |
| } catch (IOException e) { |
| UpdateCore.log(new Status(IStatus.ERROR, ID, "Error occurred while reading source bundle list.", e)); //$NON-NLS-1$ |
| } |
| } |
| |
| private boolean isFeaturePatchOfThisFeature(IFeature pluginsOriginatorFeature, IFeature feature) { |
| |
| if (!feature.isPatch()) |
| return false; |
| |
| IImport[] featureImports = feature.getImports(); |
| |
| if (featureImports == null) { |
| return false; |
| } |
| |
| for (int i = 0; i < featureImports.length; i++) { |
| if (featureImports[i].isPatch() && featureImports[i].getVersionedIdentifier().equals(pluginsOriginatorFeature.getVersionedIdentifier())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * creates a Status |
| */ |
| private IStatus createStatus(int statusSeverity, int statusCode, String msg, Exception e) { |
| String id = UpdateCore.getPlugin().getBundle().getSymbolicName(); |
| |
| StringBuffer completeString = new StringBuffer(""); //$NON-NLS-1$ |
| if (msg != null) |
| completeString.append(msg); |
| if (e != null) { |
| completeString.append("\r\n["); //$NON-NLS-1$ |
| completeString.append(e.toString()); |
| completeString.append("]\r\n"); //$NON-NLS-1$ |
| } |
| return new Status(statusSeverity, id, statusCode, completeString.toString(), e); |
| } |
| |
| /* |
| * returns all the configured fetaures |
| */ |
| private IFeature[] getAllConfiguredFeatures() { |
| if (allConfiguredFeatures == null) { |
| |
| allConfiguredFeatures = new ArrayList(); |
| IConfiguredSite[] allConfiguredSites = siteLocal.getCurrentConfiguration().getConfiguredSites(); |
| |
| for (int i = 0; i < allConfiguredSites.length; i++) { |
| IFeatureReference[] refs = allConfiguredSites[i].getConfiguredFeatures(); |
| IFeature feature = null; |
| for (int j = 0; j < refs.length; j++) { |
| feature = null; |
| try { |
| feature = refs[j].getFeature(null); |
| } catch (CoreException e) { |
| } |
| if (feature != null) { |
| allConfiguredFeatures.add(feature); |
| } |
| } |
| } |
| } |
| |
| IFeature[] features = new IFeature[allConfiguredFeatures.size()]; |
| if (allConfiguredFeatures.size() > 0) { |
| allConfiguredFeatures.toArray(features); |
| } |
| return features; |
| } |
| |
| /* |
| * returns the Feature that declares this versionedIdentifier or null if none found |
| */ |
| private IFeature getFeatureForId(VersionedIdentifier id) { |
| |
| if (id == null) |
| return null; |
| |
| IFeature[] allFeatures = getAllConfiguredFeatures(); |
| IFeature currentFeature = null; |
| IPluginEntry[] allPlugins = null; |
| IPluginEntry currentPlugin = null; |
| for (int i = 0; i < allFeatures.length; i++) { |
| currentFeature = allFeatures[i]; |
| allPlugins = currentFeature.getPluginEntries(); |
| for (int j = 0; j < allPlugins.length; j++) { |
| currentPlugin = allPlugins[j]; |
| if (id.equals(currentPlugin.getVersionedIdentifier())) |
| return currentFeature; |
| } |
| } |
| return null; |
| } |
| } |