blob: e0a665033c51289a7b68ff92f2ee0cfc09efb11b [file] [log] [blame]
package org.eclipse.update.core;
/*
* (c) Copyright IBM Corp. 2000, 2002.
* All Rights Reserved.
*/
import java.io.IOException;
import java.net.*;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.update.core.model.*;
import org.eclipse.update.internal.core.*;
/**
* Convenience implementation of a feature.
* <p>
* This class may be instantiated or subclassed by clients.
* </p>
* @see org.eclipse.update.core.IFeature
* @see org.eclipse.update.core.model.FeatureModel
* @since 2.0
*/
public class Feature extends FeatureModel implements IFeature {
/**
* Simple file name of the default feature manifest file
* @since 2.0
*/
public static final String FEATURE_FILE = "feature"; //$NON-NLS-1$
/**
* File extension of the default feature manifest file
* @since 2.0
*/
public static final String FEATURE_XML = FEATURE_FILE + ".xml"; //$NON-NLS-1$
private ISite site; // feature site
private IFeatureContentProvider featureContentProvider; // content provider
private List /*of IFeatureReference*/
includedFeatureReferences;
/**
* Feature default constructor
*
* @since 2.0
*/
public Feature() {
}
/**
* Compares two features for equality
*
* @param object feature object to compare with
* @return <code>true</code> if the two features are equal,
* <code>false</code> otherwise
* @since 2.0
*/
public boolean equals(Object object) {
if (!(object instanceof IFeature))
return false;
IFeature f = (IFeature) object;
return getVersionedIdentifier().equals(f.getVersionedIdentifier());
}
/**
* Returns the feature identifier.
*
* @see IFeature#getVersionedIdentifier()
* @since 2.0
*/
public VersionedIdentifier getVersionedIdentifier() {
return new VersionedIdentifier(
getFeatureIdentifier(),
getFeatureVersion());
}
/**
* Returns the site this feature is associated with.
*
* @see IFeature#getSite()
* @since 2.0
*/
public ISite getSite() {
return site;
}
/**
* Returns the feature URL.
*
* @see IFeature#getURL()
* @since 2.0
*/
public URL getURL() {
IFeatureContentProvider contentProvider = null;
try {
contentProvider = getFeatureContentProvider();
} catch (CoreException e) {
UpdateManagerPlugin.warn("No content Provider",e);
}
return (contentProvider != null) ? contentProvider.getURL() : null;
}
/**
* Returns an information entry referencing the location of the
* feature update site.
*
* @see IFeature#getUpdateSiteEntry()
* @since 2.0
*/
public IURLEntry getUpdateSiteEntry() {
return (IURLEntry) getUpdateSiteEntryModel();
}
/**
* Return an array of information entries referencing locations of other
* update sites.
*
* @see IFeature#getDiscoverySiteEntries()
* @since 2.0
*/
public IURLEntry[] getDiscoverySiteEntries() {
URLEntryModel[] result = getDiscoverySiteEntryModels();
if (result.length == 0)
return new IURLEntry[0];
else
return (IURLEntry[]) result;
}
/**
* Returns and optional custom install handler entry.
*
* @see IFeature#getInstallHandlerEntry()
* @since 2.0
*/
public IInstallHandlerEntry getInstallHandlerEntry() {
return (IInstallHandlerEntry) getInstallHandlerModel();
}
/**
* Returns the feature description.
*
* @see IFeature#getDescription()
* @since 2.0
*/
public IURLEntry getDescription() {
return (IURLEntry) getDescriptionModel();
}
/**
* Returns the copyright information for the feature.
*
* @see IFeature#getCopyright()
* @since 2.0
*/
public IURLEntry getCopyright() {
return (IURLEntry) getCopyrightModel();
}
/**
* Returns the license information for the feature.
*
* @see IFeature#getLicense()
* @since 2.0
*/
public IURLEntry getLicense() {
return (IURLEntry) getLicenseModel();
}
/**
* Return optional image for the feature.
*
* @see IFeature#getImage()
* @since 2.0
*/
public URL getImage() {
return getImageURL();
}
/**
* Return a list of plug-in dependencies for this feature.
*
* @see IFeature#getImports()
* @since 2.0
*/
public IImport[] getImports() {
ImportModel[] result = getImportModels();
if (result.length == 0)
return new IImport[0];
else
return (IImport[]) result;
}
/**
* Install the contents of this feature into the specified target feature.
* This method is a reference implementation of the feature installation
* protocol. Other concrete feature implementation that override this
* method need to implement this protocol.
*
* @see IFeature#install(IFeature, IVerificationListener, IProgressMonitor)
* @since 2.0
*/
public IFeatureReference install(
IFeature targetFeature,
IVerificationListener verificationListener,
IProgressMonitor progress)
throws CoreException {
//DEBUG
debug("Installing...:" + getURL().toExternalForm());
ErrorRecoveryLog recoveryLog=ErrorRecoveryLog.getLog();
// make sure we have an InstallMonitor
InstallMonitor monitor;
if (progress == null)
monitor = null;
else if (progress instanceof InstallMonitor)
monitor = (InstallMonitor) progress;
else
monitor = new InstallMonitor(progress);
// Setup optional install handler
InstallHandlerProxy handler =
new InstallHandlerProxy(
IInstallHandler.HANDLER_ACTION_INSTALL,
this,
this.getInstallHandlerEntry(),
monitor);
boolean success = false;
Throwable originalException = null;
InstallAbortedException abortedException = null;
// Get source feature provider and verifier.
// Initialize target variables.
IFeatureContentProvider provider = getFeatureContentProvider();
IVerifier verifier = provider.getVerifier();
IFeatureReference result = null;
IFeatureContentConsumer consumer = null;
IPluginEntry[] targetSitePluginEntries = null;
try {
// determine list of plugins to install
// find the intersection between the plugin entries already contained
// on the target site, and plugin entries packaged in source feature
IPluginEntry[] sourceFeaturePluginEntries = getPluginEntries();
ISite targetSite = targetFeature.getSite();
if (targetSite==null){
debug("The site to install in is null");
targetSitePluginEntries = new IPluginEntry[0];
} else {
targetSitePluginEntries = targetSite.getPluginEntries();
}
IPluginEntry[] pluginsToInstall =
UpdateManagerUtils.diff(
sourceFeaturePluginEntries,
targetSitePluginEntries);
INonPluginEntry[] nonPluginsToInstall = getNonPluginEntries();
IFeatureReference[] children = getIncludedFeatureReferences();
// determine number of monitor tasks
// 2 tasks for the feature jar (download/verify + install)
// + 2*n tasks for plugin entries (download/verify + install for each)
// + 1*m tasks per non-plugin data entry (download for each)
// + 1 task for custom non-plugin entry handling (1 for all combined)
// + 5*x tasks for children features (5 subtasks per install)
int taskCount = 2
+ 2 * pluginsToInstall.length
+ nonPluginsToInstall.length
+ 1
+ 5*children.length;
if (monitor != null)
monitor.beginTask("", taskCount);
SubProgressMonitor subMonitor=null;
// start log
recoveryLog.open(recoveryLog.START_INSTALL_LOG);
// Start the installation tasks
handler.installInitiated();
// Download and verify feature archive(s)
ContentReference[] references =
provider.getFeatureEntryArchiveReferences(monitor);
verifyReferences(verifier,references,monitor,verificationListener,true);
monitorWork(monitor,1);
// Download and verify plugin archives
for (int i = 0; i < pluginsToInstall.length; i++) {
references =
provider.getPluginEntryArchiveReferences(
pluginsToInstall[i],
monitor);
verifyReferences(verifier,references,monitor,verificationListener,false);
monitorWork(monitor,1);
}
handler.pluginsDownloaded(pluginsToInstall);
// Download non-plugin archives. Verification handled by optional install handler
for (int i = 0; i < nonPluginsToInstall.length; i++) {
references =
provider.getNonPluginEntryArchiveReferences(
nonPluginsToInstall[i],
monitor);
monitorWork(monitor,1);
}
handler.nonPluginDataDownloaded(
nonPluginsToInstall,
verificationListener);
// All archives are downloaded and verified. Get ready to install
consumer = targetFeature.getFeatureContentConsumer();
// install the children feature
for (int i = 0; i < children.length; i++) {
IFeature childFeature = null;
try {
childFeature = children[i].getFeature();
} catch (CoreException e) {
UpdateManagerPlugin.warn(null,e);
}
if (childFeature != null){
if (monitor!=null)
subMonitor = new SubProgressMonitor(monitor,5);
((Site) targetSite).install(// need to cast
childFeature,
consumer,
verifier,
verificationListener,
subMonitor);
}
}
// Install plugin files
for (int i = 0; i < pluginsToInstall.length; i++) {
references =
provider.getPluginEntryContentReferences(
pluginsToInstall[i],
monitor);
IContentConsumer pluginConsumer =
consumer.open(pluginsToInstall[i]);
String msg = "";
if (monitor != null){
subMonitor = new SubProgressMonitor(monitor,1);
VersionedIdentifier pluginVerId = pluginsToInstall[i].getVersionedIdentifier();
String pluginID = (pluginVerId==null)?"":pluginVerId.getIdentifier();
msg = Policy.bind("Feature.TaskInstallPluginFiles",pluginID); //$NON-NLS-1$
}
for (int j = 0; j < references.length; j++) {
setMonitorTaskName(subMonitor,msg+references[j].getIdentifier());
pluginConsumer.store(references[j], subMonitor);
}
if (monitor != null) {
if (monitor.isCanceled())
abort();
}
}
// check if we need to install feature files [16718]
// store will throw CoreException if another feature is already
// installed in the same place
if (!featureAlreadyInstalled(targetSite)){
//Install feature files
references = provider.getFeatureEntryContentReferences(monitor);
String msg = "";
if (monitor != null){
subMonitor = new SubProgressMonitor(monitor,1);
msg = Policy.bind("Feature.TaskInstallFeatureFiles");//$NON-NLS-1$
}
for (int i = 0; i < references.length; i++) {
setMonitorTaskName(subMonitor,msg+" "+references[i].getIdentifier());
consumer.store(references[i], subMonitor);
}
} else {
if (monitor!=null)
monitor.worked(1);
}
if (monitor != null) {
if (monitor.isCanceled())
abort();
}
// call handler to complete installation (eg. handle non-plugin entries)
handler.completeInstall(consumer);
monitorWork(monitor,1);
// indicate install success
success = true;
} catch (InstallAbortedException e){
abortedException = e;
}catch (CoreException e) {
originalException = e;
} finally {
Exception newException = null;
try {
if (consumer != null) {
if (success) {
result = consumer.close();
// close the log
recoveryLog.close(recoveryLog.END_INSTALL_LOG);
} else {
consumer.abort();
}
}
handler.installCompleted(success);
// if abort is done, no need for the log to stay
recoveryLog.delete();
} catch (Exception e) {
newException = e;
}
// original exception wins unless it is InstallAbortedException
// and an error occured during abort
if (originalException != null){
throw Utilities.newCoreException(
Policy.bind("InstallHandler.error", this.getLabel()),
originalException);
}
if (newException != null)
throw Utilities.newCoreException(
Policy.bind("InstallHandler.error", this.getLabel()),
newException);
if (abortedException != null){
throw abortedException;
}
}
return result;
}
/**
* Returns an array of plug-in entries referenced by this feature
*
* @see IFeature#getPluginEntries()
* @since 2.0
*/
public IPluginEntry[] getPluginEntries() {
PluginEntryModel[] result = getPluginEntryModels();
if (result.length == 0)
return new IPluginEntry[0];
else
return (IPluginEntry[]) result;
}
/**
* Returns the count of referenced plug-in entries.
*
* @see IFeature#getPluginEntryCount()
* @since 2.0
*/
public int getPluginEntryCount() {
return getPluginEntryModels().length;
}
/**
* Returns an array of non-plug-in entries referenced by this feature
*
* @see IFeature#getNonPluginEntries()
* @since 2.0
*/
public INonPluginEntry[] getNonPluginEntries() {
NonPluginEntryModel[] result = getNonPluginEntryModels();
if (result.length == 0)
return new INonPluginEntry[0];
else
return (INonPluginEntry[]) result;
}
/**
* Returns the count of referenced non-plug-in entries.
*
* @see IFeature#getNonPluginEntryCount()
* @since 2.0
*/
public int getNonPluginEntryCount() {
return getNonPluginEntryModels().length;
}
/**
* Returns an array of feature references included by this feature
*
* @return an erray of feature references, or an empty array.
* @since 2.0
*/
public IFeatureReference[] getIncludedFeatureReferences()
throws CoreException {
if (includedFeatureReferences == null)
initializeIncludedReferences();
if (includedFeatureReferences.size() == 0)
return new FeatureReference[0];
return (IFeatureReference[]) includedFeatureReferences.toArray(
arrayTypeFor(includedFeatureReferences));
}
/**
* Returns the download size of the feature, if it can be determined.
*
* @see IFeature#getDownloadSize()
* @since 2.0
*/
public long getDownloadSize() {
try {
Set allPluginEntries = new HashSet();
Set allNonPluginEntries = new HashSet();
IPluginEntry[] plugins = getPluginEntries();
allPluginEntries.addAll(Arrays.asList(plugins));
INonPluginEntry[] nonPlugins = getNonPluginEntries();
allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
IFeatureReference[] children = getIncludedFeatureReferences();
for (int i = 0; i < children.length; i++) {
plugins = children[i].getFeature().getPluginEntries();
allPluginEntries.addAll(Arrays.asList(plugins));
nonPlugins = children[i].getFeature().getNonPluginEntries();
allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
}
IPluginEntry[] totalPlugins =
new IPluginEntry[allPluginEntries.size()];
INonPluginEntry[] totalNonPlugins =
new INonPluginEntry[allNonPluginEntries.size()];
if (allPluginEntries.size() != 0) {
allPluginEntries.toArray(totalPlugins);
}
if (allNonPluginEntries.size() != 0) {
allNonPluginEntries.toArray(totalNonPlugins);
}
return getFeatureContentProvider().getDownloadSizeFor(
totalPlugins,
totalNonPlugins);
} catch (CoreException e) {
UpdateManagerPlugin.warn(null,e);
return ContentEntryModel.UNKNOWN_SIZE;
}
}
/**
* Returns the install size of the feature, if it can be determined.
*
* @see IFeature#getInstallSize()
* @since 2.0
*/
public long getInstallSize() {
try {
Set allPluginEntries = new HashSet();
Set allNonPluginEntries = new HashSet();
IPluginEntry[] plugins = getPluginEntries();
allPluginEntries.addAll(Arrays.asList(plugins));
INonPluginEntry[] nonPlugins = getNonPluginEntries();
allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
IFeatureReference[] children = getIncludedFeatureReferences();
for (int i = 0; i < children.length; i++) {
plugins = children[i].getFeature().getPluginEntries();
allPluginEntries.addAll(Arrays.asList(plugins));
nonPlugins = children[i].getFeature().getNonPluginEntries();
allNonPluginEntries.addAll(Arrays.asList(nonPlugins));
}
IPluginEntry[] totalPlugins =
new IPluginEntry[allPluginEntries.size()];
INonPluginEntry[] totalNonPlugins =
new INonPluginEntry[allNonPluginEntries.size()];
if (allPluginEntries.size() != 0) {
allPluginEntries.toArray(totalPlugins);
}
if (allNonPluginEntries.size() != 0) {
allNonPluginEntries.toArray(totalNonPlugins);
}
return getFeatureContentProvider().getInstallSizeFor(
totalPlugins,
totalNonPlugins);
} catch (CoreException e) {
UpdateManagerPlugin.warn(null,e);
return ContentEntryModel.UNKNOWN_SIZE;
}
}
/**
* Returns the content provider for this feature.
*
* @see IFeature#getFeatureContentProvider()
* @since 2.0
*/
public IFeatureContentProvider getFeatureContentProvider()
throws CoreException {
if (featureContentProvider == null) {
throw Utilities.newCoreException(
Policy.bind(
"Feature.NoContentProvider",
getVersionedIdentifier().toString()),
null);
//$NON-NLS-1$
}
return this.featureContentProvider;
}
/**
* Returns the content consumer for this feature.
*
* @see IFeature#getFeatureContentConsumer()
* @since 2.0
*/
public IFeatureContentConsumer getFeatureContentConsumer()
throws CoreException {
throw new UnsupportedOperationException();
}
/**
* Sets the site for this feature.
*
* @see IFeature#setSite(ISite)
* @since 2.0
*/
public void setSite(ISite site) throws CoreException {
if (this.site != null) {
String featureURLString =
(getURL() != null) ? getURL().toExternalForm() : "";
throw Utilities.newCoreException(
Policy.bind("Feature.SiteAlreadySet", featureURLString),
null);
//$NON-NLS-1$
}
this.site = site;
}
/**
* Sets the content provider for this feature.
*
* @see IFeature#setFeatureContentProvider(IFeatureContentProvider)
* @since 2.0
*/
public void setFeatureContentProvider(IFeatureContentProvider featureContentProvider) {
this.featureContentProvider = featureContentProvider;
featureContentProvider.setFeature(this);
}
/**
* Return the string representation of this fetaure
*
* @return feature as string
* @since 2.0
*/
public String toString() {
String URLString =
(getURL() == null)
? Policy.bind("Feature.NoURL")
: getURL().toExternalForm();
//$NON-NLS-1$
return Policy.bind(
"Feature.FeatureVersionToString",
URLString,
getVersionedIdentifier().toString());
//$NON-NLS-1$
}
/*
* Installation has been cancelled, abort and revert
*/
private void abort() throws CoreException {
// throws an exception that will be caught by the install
// the install will abort the consumer.
CoreException exp = Utilities.newCoreException(Policy.bind("Feature.InstallationCancelled"), null); //$NON-NLS-1$
throw new InstallAbortedException(exp);
}
/*
* Initializes includes feature references
* If the included feature reference is found on the site, add it to the List
* Otherwise attempt to instanciate it using the same type as this feature and
* using the default location on the site
*/
private void initializeIncludedReferences() throws CoreException {
includedFeatureReferences = new ArrayList();
VersionedIdentifier[] identifiers =
getFeatureIncludeVersionedIdentifier();
ISite site = getSite();
IFeatureReference[] refs =
(site == null) ? null : site.getFeatureReferences();
for (int i = 0; i < identifiers.length; i++) {
VersionedIdentifier identifier = identifiers[i];
boolean found = false;
// too long to compute if not a file system
// other solution would be to parse feature.xml
// when parsing file system to create archive features/FeatureId_Ver.jar
if("file".equals(site.getURL().getProtocol())){
// check if declared on the Site
if (refs != null) {
for (int ref = 0; ref < refs.length && !found; ref++) {
if (refs[ref]!=null){
IFeature feature = null;
try {
feature = refs[ref].getFeature();
} catch (CoreException e) {
UpdateManagerPlugin.warn(null,e);
};
if (feature != null) {
if (identifier
.equals(feature.getVersionedIdentifier())) {
includedFeatureReferences.add(refs[ref]);
found = true;
}
}
}
}
}
}
// instanciate by mapping it based on the site.xml
// in future we may ask for a factory to create the feature ref
if (!found) {
FeatureReference newRef = new FeatureReference();
newRef.setSite(getSite());
IFeatureReference parentRef =
getSite().getFeatureReference(this);
if (parentRef instanceof FeatureReference) {
newRef.setType(((FeatureReference) parentRef).getType());
}
String featureID =
Site.DEFAULT_FEATURE_PATH + identifier.toString() + ".jar";
URL featureURL =
getSite().getSiteContentProvider().getArchiveReference(
featureID);
newRef.setURL(featureURL);
try {
newRef.resolve(
getSite().getURL(),
null); // no need to get the bundle
includedFeatureReferences.add(newRef);
} catch (Exception e) {
throw Utilities.newCoreException(
Policy.bind(
"Feature.UnableToInitializeFeatureReference",
identifier.toString()),
e);
}
}
}
}
/**
* Helper method to access resouce bundle for feature. The default
* implementation attempts to load the appropriately localized
* feature.properties file.
*
* @param url base URL used to load the resource bundle.
* @return resource bundle, or <code>null</code>.
* @since 2.0
*/
private ResourceBundle getResourceBundle(URL url)
throws IOException, CoreException {
if (url == null)
return null;
ResourceBundle bundle = null;
try {
url = UpdateManagerUtils.asDirectoryURL(url);
ClassLoader l = new URLClassLoader(new URL[] { url }, null);
bundle =
ResourceBundle.getBundle(
Site.SITE_FILE,
Locale.getDefault(),
l);
} catch (MissingResourceException e) {
UpdateManagerPlugin.warn(e.getLocalizedMessage() + ":" + url.toExternalForm()); //$NON-NLS-1$
} catch (MalformedURLException e) {
UpdateManagerPlugin.warn(e.getLocalizedMessage()); //$NON-NLS-1$
}
return bundle;
}
/*
*
*/
private void debug(String trace){
//DEBUG
if (UpdateManagerPlugin.DEBUG
&& UpdateManagerPlugin.DEBUG_SHOW_INSTALL) {
UpdateManagerPlugin.debug(trace);
}
}
/*
*
*/
private void setMonitorTaskName(IProgressMonitor monitor,String taskName){
if (monitor!=null)
monitor.setTaskName(taskName);
}
/*
*
*/
private void monitorWork(IProgressMonitor monitor, int tick) throws CoreException {
if (monitor!=null){
monitor.worked(tick);
if(monitor.isCanceled()){
abort();
}
}
}
/*
*
*/
private void verifyReferences(IVerifier verifier,ContentReference[] references,InstallMonitor monitor,IVerificationListener verificationListener,boolean isFeature) throws CoreException {
IVerificationResult vr=null;
if (verifier != null) {
for (int j = 0; j < references.length; j++) {
vr = verifier.verify(this,references[j],isFeature,monitor);
if (vr != null) {
if (verificationListener == null)
return;
int result = verificationListener.prompt(vr);
if (result == IVerificationListener.CHOICE_ABORT) {
CoreException exp = Utilities
.newCoreException(
Policy.bind("JarVerificationService.CancelInstall"),
//$NON-NLS-1$
vr.getVerificationException());
throw new InstallAbortedException(exp);
}
if (result == IVerificationListener.CHOICE_ERROR) {
throw Utilities
.newCoreException(
Policy.bind(
"JarVerificationService.UnsucessfulVerification"),
//$NON-NLS-1$
vr.getVerificationException());
}
}
}
}
}
/*
* returns true f the same feature is installed on the site
*/
private boolean featureAlreadyInstalled(ISite targetSite){
IFeatureReference ref = targetSite.getFeatureReference(this);
return (ref!=null);
}
}