blob: fcdbb1dfe1aec916dfdc21c1e0ee630d3ec8230f [file] [log] [blame]
/*******************************************************************************
* 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;
}
}