package org.eclipse.update.internal.core; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import java.io.*; | |
import java.net.*; | |
import java.util.*; | |
import org.eclipse.core.runtime.*; | |
import org.eclipse.core.runtime.CoreException; | |
import org.eclipse.update.core.*; | |
import org.eclipse.update.core.IPluginEntry; | |
import org.xml.sax.SAXException; | |
/** | |
* Abstract Class that implements most of the behavior of a feature | |
* A feature ALWAYS belongs to an ISite | |
*/ | |
// VK: we need to rework this as an API base class. Also need to rework which methods | |
// VK: need to be exposed on IFeature (same comment for Site, ISite) | |
// VK: Also, need to be able to poof-up a feature model from XML without having a site, | |
// VK: or passing null site (needed in PDE, build code, etc). Need a constructor | |
// VK: that takes a stream | |
public abstract class Feature implements IFeature { | |
/** | |
* | |
*/ | |
private static CoreException CANCEL_EXCEPTION; | |
/** | |
* | |
*/ | |
public static final String FEATURE_FILE = "feature"; | |
/** | |
* | |
*/ | |
public static final String FEATURE_XML = FEATURE_FILE + ".xml"; | |
/** | |
* Identifier of the Feature | |
*/ | |
private VersionedIdentifier versionIdentifier; | |
/** | |
* Site in which teh feature resides | |
*/ | |
private ISite site; | |
/** | |
* User label fo the Feature | |
*/ | |
private String label; | |
/** | |
* reference to the feature inside the site. | |
* This URL can be a Jar file, a directory or any URL that is understood by the | |
* Subclass of AbstractFeature. | |
*/ | |
private URL url; | |
/** | |
* Url and label of site where update of this feature can ve found | |
*/ | |
private IInfo updateInfo; | |
/** | |
* Url and label of site where other informations related to this feature can be found | |
*/ | |
private List discoveryInfos; | |
/** | |
* The primary application | |
*/ | |
private String application; | |
/** | |
* provider of the Feature | |
*/ | |
private String provider; | |
/** | |
* Short description and url for long description of this feature | |
*/ | |
private IInfo description; | |
/** | |
* Short copyright and url for long copyright of this feature | |
*/ | |
private IInfo copyright; | |
/** | |
* Short license and url for long license of this feature | |
*/ | |
private IInfo license; | |
/** | |
* Image (shoudl be either GIF or JPG) | |
*/ | |
private URL image; | |
private String nl; | |
private String os; | |
private String ws; | |
/** | |
* List of plugin entries teh feature contains | |
* read from teh xml file | |
*/ | |
private List pluginEntries; | |
/** | |
* List of data entries the feature contains | |
* read from the xml file | |
*/ | |
private List dataEntries; | |
/** | |
* List of plugin the feature require | |
* to be installed in the site before this feature | |
* can be installed | |
*/ | |
private List requires; | |
/** | |
* private internal | |
* used for lazy instantiation and | |
* hydration with the XML file | |
*/ | |
private boolean isInitialized = false; | |
/** | |
* Static block to initialize the possible CANCEL ERROR | |
* thrown when the USER cancels teh operation | |
*/ | |
static { | |
// in case we throw a cancel exception | |
String pluginId = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus cancelStatus = new Status(IStatus.ERROR, pluginId, IStatus.OK, "Install has been Cancelled", null); | |
CANCEL_EXCEPTION = new CoreException(cancelStatus); | |
} | |
/** | |
* Copy constructor | |
*/ | |
public Feature(IFeature sourceFeature, ISite targetSite) throws CoreException { | |
// do not call other ctr as we do not want t parse XML file | |
this.site = targetSite; | |
this.url = sourceFeature.getURL(); | |
this.versionIdentifier = sourceFeature.getIdentifier(); | |
this.label = sourceFeature.getLabel(); | |
this.updateInfo = sourceFeature.getUpdateInfo(); | |
this.setDiscoveryInfos(sourceFeature.getDiscoveryInfos()); | |
this.provider = sourceFeature.getProvider(); | |
this.description = sourceFeature.getDescription(); | |
this.copyright = sourceFeature.getCopyright(); | |
this.license = sourceFeature.getLicense(); | |
this.setPluginEntries(sourceFeature.getPluginEntries()); | |
this.isInitialized = true; | |
} | |
/** | |
* Constructor | |
*/ | |
public Feature(URL url, ISite targetSite) throws CoreException { | |
this.site = targetSite; | |
this.url = url; | |
initializeFeature(); | |
} | |
/** | |
* @see IFeature#getIdentifier() | |
*/ | |
public VersionedIdentifier getIdentifier() { | |
if (versionIdentifier == null && !isInitialized) | |
logNotInitialized(); | |
return versionIdentifier; | |
} | |
/** | |
* @see IFeature#getSite() | |
* Do not hydrate, value set ins constructor | |
*/ | |
public ISite getSite() { | |
return site; | |
} | |
/** | |
* @see IFeature#getLabel() | |
*/ | |
public String getLabel() { | |
if (label == null && !isInitialized) | |
logNotInitialized(); | |
return label; | |
} | |
/** | |
* @see IFeature#getURL() | |
* Do not hydrate. Initialization will not populate the url. | |
* If the URL is null, then the creation hasn't set the URL, so return null. | |
* It has to be set at creation time or using the set method | |
* Usually done from the site when creating the Feature. | |
* | |
* The DefaultSiteParser is setting it at creation time | |
*/ | |
public URL getURL() { | |
return url; | |
} | |
/** | |
* @see IFeature#getRootURL() | |
* In general, the Root URL is the URL of teh Feature | |
* | |
* The RootURL is used to calculate relative URL for teh feature | |
* In case of a file feature, you can just append teh relative path | |
* to the URL of teh feature | |
* | |
* In case of a JAR file, you cannot *just* append the file | |
* You have to transfrom the URL | |
* | |
* Can be overriden | |
*/ | |
public URL getRootURL() throws MalformedURLException, IOException, CoreException { | |
return url; | |
} | |
/** | |
* @see IFeature#getUpdateInfo() | |
*/ | |
public IInfo getUpdateInfo() { | |
if (updateInfo == null && !isInitialized) | |
logNotInitialized(); | |
return updateInfo; | |
} | |
/** | |
* @see IFeature#getDiscoveryInfos() | |
*/ | |
//VK: method name: getDiscoveryInfo (plural==singular) | |
public IInfo[] getDiscoveryInfos() { | |
IInfo[] result = new IInfo[0]; | |
if (discoveryInfos == null && !isInitialized) | |
logNotInitialized(); | |
if (!(discoveryInfos == null || discoveryInfos.isEmpty())) { | |
result = new IInfo[discoveryInfos.size()]; | |
discoveryInfos.toArray(result); | |
} | |
return result; | |
} | |
/* | |
* @see IFeature#getApplication() | |
*/ | |
public String getApplication() { | |
return application; | |
} | |
/** | |
* @see IFeature#getProvider() | |
*/ | |
public String getProvider() { | |
if (provider == null && !isInitialized) | |
logNotInitialized(); | |
return provider; | |
} | |
/** | |
* @see IFeature#getDescription() | |
*/ | |
public IInfo getDescription() { | |
if (description == null && !isInitialized) | |
logNotInitialized(); | |
return description; | |
} | |
/** | |
* @see IFeature#getCopyright() | |
*/ | |
public IInfo getCopyright() { | |
if (copyright == null && !isInitialized) | |
logNotInitialized(); | |
return copyright; | |
} | |
/** | |
* @see IFeature#getLicense() | |
*/ | |
public IInfo getLicense() { | |
if (license == null && !isInitialized) | |
logNotInitialized(); | |
return license; | |
} | |
/** | |
* @see IFeature#getImage() | |
*/ | |
public URL getImage() { | |
if (image == null && !isInitialized) | |
logNotInitialized(); | |
return image; | |
} | |
/** | |
* @see IFeature#getNL() | |
*/ | |
public String getNL() { | |
if (nl == null && !isInitialized) | |
logNotInitialized(); | |
return nl; | |
} | |
/** | |
* @see IFeature#getOS() | |
*/ | |
public String getOS() { | |
if (os == null && !isInitialized) | |
logNotInitialized(); | |
return os; | |
} | |
/** | |
* @see IFeature#getWS() | |
*/ | |
public String getWS() { | |
if (ws == null && !isInitialized) | |
logNotInitialized(); | |
return ws; | |
} | |
/** | |
* Sets the site | |
* @param site The site to set | |
*/ | |
public void setSite(ISite site) { | |
this.site = site; | |
} | |
/** | |
* Sets the identifier | |
* @param identifier The identifier to set | |
*/ | |
public void setIdentifier(VersionedIdentifier identifier) { | |
this.versionIdentifier = identifier; | |
} | |
/** | |
* Sets the label | |
* @param label The label to set | |
*/ | |
public void setLabel(String label) { | |
this.label = label; | |
} | |
/** | |
* Sets the url | |
* @param url The url to set | |
*/ | |
public void setURL(URL url) { | |
this.url = url; | |
} | |
/** | |
* Sets the updateInfo | |
* @param updateInfo The updateInfo to set | |
*/ | |
public void setUpdateInfo(IInfo updateInfo) { | |
this.updateInfo = updateInfo; | |
} | |
/** | |
* Sets the discoveryInfos | |
* @param discoveryInfos The discoveryInfos to set | |
*/ | |
//VK: method name: getDiscoveryInfo (plural==singular) | |
public void setDiscoveryInfos(IInfo[] discoveryInfos) { | |
if (discoveryInfos != null) { | |
this.discoveryInfos = (new ArrayList()); | |
for (int i = 0; i < discoveryInfos.length; i++) { | |
this.discoveryInfos.add(discoveryInfos[i]); | |
} | |
} | |
} | |
/** | |
* Adds a discoveryInfo | |
* @param discoveryInfo The discoveryInfo to add | |
*/ | |
public void addDiscoveryInfo(IInfo discoveryInfo) { | |
if (discoveryInfos == null) | |
discoveryInfos = new ArrayList(0); | |
discoveryInfos.add(discoveryInfo); | |
} | |
/** | |
* Sets the application | |
* @param application the application to set | |
*/ | |
public void setApplication(String application) { | |
this.application = application; | |
} | |
/** | |
* Sets the provider | |
* @param provider The provider to set | |
*/ | |
public void setProvider(String provider) { | |
this.provider = provider; | |
} | |
/** | |
* Sets the description | |
* @param description The description to set | |
*/ | |
public void setDescription(IInfo description) { | |
this.description = description; | |
} | |
/** | |
* Sets the copyright | |
* @param copyright The copyright to set | |
*/ | |
public void setCopyright(IInfo copyright) { | |
this.copyright = copyright; | |
} | |
/** | |
* Sets the license | |
* @param license The license to set | |
*/ | |
public void setLicense(IInfo license) { | |
this.license = license; | |
} | |
/** | |
* Sets the image | |
* @param image The image to set | |
*/ | |
public void setImage(URL image) { | |
this.image = image; | |
} | |
/** | |
* Sets the nl | |
* @param nl The nl to set | |
*/ | |
public void setNL(String nl) { | |
this.nl = nl; | |
} | |
/** | |
* Sets the os | |
* @param os The os to set | |
*/ | |
public void setOS(String os) { | |
this.os = os; | |
} | |
/** | |
* Sets the ws | |
* @param ws The ws to set | |
*/ | |
public void setWS(String ws) { | |
this.ws = ws; | |
} | |
/** | |
* @see IPluginContainer#getDownloadSize(IPluginEntry) | |
*/ | |
public int getDownloadSize(IPluginEntry entry) { | |
Assert.isTrue(entry instanceof PluginEntry); | |
return ((PluginEntry) entry).getDownloadSize(); | |
} | |
/** | |
* @see IPluginContainer#getInstallSize(IPluginEntry) | |
*/ | |
public int getInstallSize(IPluginEntry entry) { | |
Assert.isTrue(entry instanceof PluginEntry); | |
return ((PluginEntry) entry).getInstallSize(); | |
} | |
/** | |
* returns the download size | |
* of the feature to be installed on the site. | |
* If the site is <code>null</code> returns the maximum size | |
* | |
* If one plug-in entry has an unknown size. | |
* then the download size is unknown. | |
* | |
* @see IFeature#getDownloadSize(ISite) | |
* | |
*/ | |
public long getDownloadSize(ISite site) throws CoreException { | |
int result = 0; | |
IPluginEntry[] entriesToInstall = this.getPluginEntries(); | |
if (site != null) { | |
IPluginEntry[] siteEntries = site.getPluginEntries(); | |
entriesToInstall = intersection(entriesToInstall, siteEntries); | |
} | |
if (entriesToInstall == null || entriesToInstall.length == 0) { | |
result = -1; | |
} else { | |
int pluginSize = 0; | |
int i = 0; | |
while (i < entriesToInstall.length && pluginSize != -1) { | |
pluginSize = getDownloadSize(entriesToInstall[i]); | |
result = pluginSize == -1 ? -1 : result + pluginSize; | |
i++; | |
} | |
} | |
return result; | |
} | |
/** | |
* returns the install size | |
* of the feature to be installed on the site. | |
* If the site is <code>null</code> returns the maximum size | |
* | |
* If one plug-in entry has an unknown size. | |
* then the install size is unknown. | |
* | |
* @see IFeature#getInstallSize(ISite) | |
*/ | |
public long getInstallSize(ISite site) throws CoreException { | |
int result = 0; | |
IPluginEntry[] entriesToInstall = this.getPluginEntries(); | |
if (site != null) { | |
IPluginEntry[] siteEntries = site.getPluginEntries(); | |
entriesToInstall = intersection(entriesToInstall, siteEntries); | |
} | |
if (entriesToInstall == null || entriesToInstall.length == 0) { | |
result = -1; | |
} else { | |
int pluginSize = 0; | |
int i = 0; | |
while (i < entriesToInstall.length && pluginSize != -1) { | |
pluginSize = getInstallSize(entriesToInstall[i]); | |
result = pluginSize == -1 ? -1 : result + pluginSize; | |
i++; | |
} | |
} | |
return result; | |
} | |
/** | |
* @see IFeature#isExecutable() | |
*/ | |
public boolean isExecutable() { | |
return false; | |
} | |
/** | |
* @see IFeature#isInstallable() | |
*/ | |
public boolean isInstallable() { | |
return false; | |
} | |
/** | |
* Method install. | |
* @param targetFeature | |
* @param monitor | |
* @throws CoreException | |
*/ | |
public void install(IFeature targetFeature, IProgressMonitor monitor) throws CoreException { | |
IPluginEntry[] sourceFeaturePluginEntries = getPluginEntries(); | |
IPluginEntry[] targetSitePluginEntries = targetFeature.getSite().getPluginEntries(); | |
Site tempSite = (Site) SiteManager.getTempSite(); | |
// VK: why are we creating a temp site (vs. simple temp directory) | |
// VK: why are we handling feature jar, plugin jar and data files differently wrt download | |
// determine list of plugins to install | |
// find the intersection between the two arrays of IPluginEntry... | |
// The one teh site contains and teh one the feature contains | |
IPluginEntry[] pluginsToInstall = intersection(sourceFeaturePluginEntries, targetSitePluginEntries); | |
// private abstract - Determine list of content references id /archives id /bundles id that | |
// map the list of plugins to install | |
String[] archiveIDToInstall = getContentReferenceToInstall(pluginsToInstall); | |
try { | |
// download and install data bundles | |
// before we set the site of teh feature to the TEMP site | |
IDataEntry[] dataEntries = getDataEntries(); | |
if (dataEntries.length > 0) { | |
downloadDataLocally(targetFeature, dataEntries, monitor); | |
} | |
// optmization, may be private to implementation | |
// copy *blobs/content references/archives/bundles* in TEMP space | |
if (((Site) getSite()).optimize()) { | |
if (archiveIDToInstall != null) { | |
downloadArchivesLocally(tempSite, archiveIDToInstall, monitor); | |
} | |
} | |
// obtain the list of *Streamable Storage Unit* | |
// from the archive | |
if (monitor != null) { | |
int total = pluginsToInstall == null ? 1 : pluginsToInstall.length + 1; | |
monitor.beginTask("Install feature " + getLabel(), total); | |
} | |
if (pluginsToInstall != null) { | |
InputStream inStream = null; | |
for (int i = 0; i < pluginsToInstall.length; i++) { | |
if (monitor != null) { | |
monitor.subTask("Installing plug-in: " + pluginsToInstall[i]); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
open(pluginsToInstall[i]); | |
String[] names = getStorageUnitNames(pluginsToInstall[i]); | |
if (names != null) { | |
for (int j = 0; j < names.length; j++) { | |
if ((inStream = getInputStreamFor(pluginsToInstall[i], names[j])) != null) | |
targetFeature.store(pluginsToInstall[i], names[j], inStream); | |
} | |
} | |
close(pluginsToInstall[i]); | |
if (monitor != null) { | |
monitor.worked(1); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
} | |
} | |
// install the Feature info | |
InputStream inStream = null; | |
String[] names = getStorageUnitNames(); | |
if (names != null) { | |
openFeature(); | |
if (monitor != null) { | |
monitor.subTask("Installing Feature information"); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
for (int j = 0; j < names.length; j++) { | |
if ((inStream = getInputStreamFor(names[j])) != null) | |
((Site) targetFeature.getSite()).storeFeatureInfo(getIdentifier(), names[j], inStream); | |
} | |
closeFeature(); | |
if (monitor != null) { | |
monitor.worked(1); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
} | |
} catch (IOException e) { | |
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus status = new Status(IStatus.ERROR, id, IStatus.OK, "Error during Install", e); | |
throw new CoreException(status); | |
} finally { | |
//do not clean up TEMP drive | |
// as other feature may be there... clean up when exiting the plugin | |
} | |
} | |
/** | |
* remove myself... | |
*/ | |
public void remove(IProgressMonitor monitor) throws CoreException { | |
// remove the feature and the plugins if they are not used and not activated | |
// get the plugins from the feature | |
IPluginEntry[] pluginsToRemove = ((SiteLocal) SiteManager.getLocalSite()).getDeltaPluginEntries(this); | |
try { | |
// obtain the list of *Streamable Storage Unit* | |
// from the archive | |
if (monitor != null) { | |
int total = pluginsToRemove == null ? 1 : pluginsToRemove.length + 1; | |
monitor.beginTask("Uninstall feature " + getLabel(), total); | |
} | |
if (pluginsToRemove != null) { | |
for (int i = 0; i < pluginsToRemove.length; i++) { | |
if (monitor != null) { | |
monitor.subTask("Removing plug-in: " + pluginsToRemove[i]); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
remove(pluginsToRemove[i]); | |
if (monitor != null) { | |
monitor.worked(1); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
} | |
} | |
// install the Feature info | |
String[] names = getStorageUnitNames(); | |
if (names != null) { | |
if (monitor != null) { | |
monitor.subTask("Removing Feature information"); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
((Site)this.getSite()).removeFeatureInfo(getIdentifier()); | |
closeFeature(); | |
if (monitor != null) { | |
monitor.worked(1); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
} | |
} catch (IOException e) { | |
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus status = new Status(IStatus.ERROR, id, IStatus.OK, "Error during Uninstall", e); | |
throw new CoreException(status); | |
} | |
} | |
/* | |
* @see IPluginContainer#remove(IPluginEntry) | |
*/ | |
public void remove(IPluginEntry entry) throws CoreException { | |
((Site)getSite()).remove(entry); | |
} | |
/** | |
* initialize teh feature by reading the feature.xml if it exists | |
*/ | |
// VK: why is this public ??? | |
public void initializeFeature() throws CoreException { | |
if (!isInitialized) { | |
isInitialized = true; | |
InputStream featureStream = null; | |
try { | |
featureStream = getInputStreamFor(FEATURE_XML); | |
// VK: this forces everyone to implement feature.xml handling | |
// VK: regardless of the packaging scheme used for the feature. WHY ??? | |
new FeatureParser(featureStream, this); | |
} catch (IOException e) { | |
// if we cannot find the feature and or the feature.xml... | |
// We should not stop the execution | |
// but we must Log it all the time, not only when debugging... | |
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus status = new Status(IStatus.WARNING, id, IStatus.OK, "Error opening feature.xml in the feature archive:" + url.toExternalForm(), e); | |
UpdateManagerPlugin.getPlugin().getLog().log(status); | |
} catch (SAXException e) { | |
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus status = new Status(IStatus.WARNING, id, IStatus.OK, "Error parsing feature.xml in the feature archive:" + url.toExternalForm(), e); | |
throw new CoreException(status); | |
} finally { | |
try { | |
featureStream.close(); | |
} catch (Exception e) { | |
} | |
try { | |
closeFeature(); | |
} catch (Exception e) { | |
} | |
} | |
} | |
} | |
/** | |
*/ | |
// VK: method name ... downloadArchives(...) | |
private void downloadArchivesLocally(ISite tempSite, String[] archiveIDToInstall, IProgressMonitor monitor) throws CoreException, IOException { | |
URL sourceURL; | |
String newFile; | |
URL newURL; | |
if (monitor != null) { | |
monitor.beginTask("Download archives bundles to Temporary Space", archiveIDToInstall.length); | |
} | |
for (int i = 0; i < archiveIDToInstall.length; i++) { | |
// transform the id by asking the site to map them to real URL inside the SITE | |
sourceURL = ((Site) getSite()).getURL(archiveIDToInstall[i]); | |
if (monitor != null) { | |
monitor.subTask("..." + archiveIDToInstall[i]); | |
} | |
// the name of the file in the temp directory | |
// should be the regular plugins/pluginID_ver as the Temp site is OUR site | |
newFile = Site.DEFAULT_PLUGIN_PATH + archiveIDToInstall[i]; | |
newURL = UpdateManagerUtils.resolveAsLocal(sourceURL, newFile, monitor); | |
// VK: this is a very confusing way to do a download | |
// transfer the possible mapping to the temp site | |
((Site) tempSite).addArchive(new Info(archiveIDToInstall[i], newURL)); | |
if (monitor != null) { | |
monitor.worked(1); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
} | |
// the site of this feature now becomes the TEMP directory | |
// FIXME: make sure there is no other issue | |
// like asking for stuff that hasn't been copied | |
// or reusing this feature | |
// of having an un-manageable temp site | |
this.setSite(tempSite); | |
} | |
/** | |
*/ | |
// VK: downloadData ... why are we handling the download of plugin and non-plugin | |
// VK: file differently??? Why not just have one download method that takes a source | |
// VK: and target??? Would have one download code handling progress, recovery, etc | |
private void downloadDataLocally(IFeature targetFeature, IDataEntry[] dataToInstall, IProgressMonitor monitor) throws CoreException, IOException { | |
URL sourceURL; | |
// any other data | |
IDataEntry[] entries = getDataEntries(); | |
// VK: why getDataEntries() when this is passed as dataToInstall ??? It is not used anywhere else. | |
if (entries != null) { | |
if (monitor != null) { | |
monitor.beginTask("Installing Other Data information", dataToInstall.length); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
for (int j = 0; j < entries.length; j++) { | |
// VK: see above ... use "entries.length" but iterate over dataToInstall[j] !!!! | |
String name = dataToInstall[j].getIdentifier(); | |
if (monitor != null) { | |
monitor.subTask("..." + name); | |
} | |
// the id is URL format with "/" | |
String dataEntryId = Site.DEFAULT_FEATURE_PATH + getIdentifier().toString() + "/" + name; | |
// transform the id by asking the site to map them to real URL inside the SITE | |
sourceURL = ((Site) getSite()).getURL(dataEntryId); | |
((Site) targetFeature.getSite()).storeFeatureInfo(getIdentifier(), name, sourceURL.openStream()); | |
if (monitor != null) { | |
monitor.worked(1); | |
if (monitor.isCanceled()) { | |
throw CANCEL_EXCEPTION; | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Logs that an attempt to read a non initialize variable has been made | |
*/ | |
private void logNotInitialized() { | |
Exception trace = new Exception("Attempt to read uninitialized variable"); | |
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus status = new Status(IStatus.WARNING, id, IStatus.OK, "the program is reading a variable of Feature before loading it", trace); | |
UpdateManagerPlugin.getPlugin().getLog().log(status); | |
} | |
/** | |
* Returns the intersection between two array of PluginEntries. | |
*/ | |
private IPluginEntry[] intersection(IPluginEntry[] array1, IPluginEntry[] array2) { | |
if (array1 == null || array1.length == 0) { | |
return array2; | |
} | |
if (array2 == null || array2.length == 0) { | |
return array1; | |
} | |
List list1 = Arrays.asList(array1); | |
List result = new ArrayList(0); | |
for (int i = 0; i < array2.length; i++) { | |
if (!list1.contains(array2[i])) | |
result.add(array2[i]); | |
} | |
return (IPluginEntry[]) result.toArray(); | |
} | |
/** | |
* @see IPluginContainer#getPluginEntries() | |
*/ | |
public IPluginEntry[] getPluginEntries() { | |
IPluginEntry[] result = new IPluginEntry[0]; | |
if (pluginEntries == null && !isInitialized) | |
logNotInitialized(); | |
if (!(pluginEntries == null || pluginEntries.isEmpty())) { | |
result = new IPluginEntry[pluginEntries.size()]; | |
pluginEntries.toArray(result); | |
} | |
return result; | |
} | |
/** | |
* @see IFeature#getDataEntries() | |
*/ | |
public IDataEntry[] getDataEntries() { | |
IDataEntry[] result = new IDataEntry[0]; | |
if (dataEntries == null && !isInitialized) | |
logNotInitialized(); | |
if (!(dataEntries == null || dataEntries.isEmpty())) { | |
result = new IDataEntry[dataEntries.size()]; | |
dataEntries.toArray(result); | |
} | |
return result; | |
} | |
/** | |
* @see IPluginContainer#getPluginEntryCount() | |
*/ | |
public int getPluginEntryCount() { | |
return getPluginEntries().length; | |
} | |
/** | |
* @see IFeature#getImports() | |
*/ | |
public IImport[] getImports() { | |
IImport[] result = new IImport[0]; | |
if (!(requires == null || requires.isEmpty())) { | |
result = new IImport[requires.size()]; | |
requires.toArray(result); | |
} | |
return result; | |
} | |
/** | |
* Sets the pluginEntries | |
* @param pluginEntries The pluginEntries to set | |
*/ | |
public void setPluginEntries(IPluginEntry[] pluginEntries) { | |
if (pluginEntries != null) { | |
for (int i = 0; i < pluginEntries.length; i++) { | |
addPluginEntry(pluginEntries[i]); | |
} | |
} | |
} | |
/** | |
* Sets the import | |
* @param imports The imports to set | |
*/ | |
public void setImports(IImport[] imports) { | |
if (imports != null) { | |
for (int i = 0; i < imports.length; i++) { | |
addImport(imports[i]); | |
} | |
} | |
} | |
/** | |
* @see IPluginContainer#addPluginEntry(IPluginEntry) | |
*/ | |
public void addPluginEntry(IPluginEntry pluginEntry) { | |
if (pluginEntries == null) | |
pluginEntries = new ArrayList(0); | |
pluginEntries.add(pluginEntry); | |
} | |
/** | |
* @see IFeature#addDataEntry(IDataEntry) | |
*/ | |
public void addDataEntry(IDataEntry dataEntry) { | |
if (dataEntries == null) | |
dataEntries = new ArrayList(0); | |
dataEntries.add(dataEntry); | |
} | |
/** | |
* Adds an import | |
* @param anImport The import to add | |
*/ | |
public void addImport(IImport anImport) { | |
if (this.requires == null) | |
this.requires = new ArrayList(0); | |
this.requires.add(anImport); | |
} | |
/** | |
* @see IPluginContainer#store(IPluginEntry, String, InputStream) | |
*/ | |
public void store(IPluginEntry pluginEntry, String contentKey, InputStream inStream) throws CoreException { | |
// check if pluginEntry already exists before passing to the site | |
// anything else ? | |
boolean found = false; | |
int i = 0; | |
IPluginEntry[] entries = getPluginEntries(); | |
while (i < entries.length && !found) { | |
if (entries[i].equals(pluginEntry)) { | |
found = true; | |
} | |
i++; | |
} | |
if (!found) { | |
String id = UpdateManagerPlugin.getPlugin().getDescriptor().getUniqueIdentifier(); | |
IStatus status = new Status(IStatus.ERROR, id, IStatus.OK, "The plugin:" + pluginEntry.getIdentifier().toString() + " is not part of the plugins of the feature:" + this.getIdentifier().toString(), null); | |
throw new CoreException(status); | |
} | |
getSite().store(pluginEntry, contentKey, inStream); | |
} | |
/** | |
* perform pre processing before opening a plugin archive | |
* @param entry the plugin about to be opened | |
*/ | |
protected void open(IPluginEntry entry) { | |
}; | |
/** | |
* perform post processing to close a plugin archive | |
* @param entry the plugin about to be closed | |
*/ | |
protected void close(IPluginEntry entry) throws IOException { | |
}; | |
/** | |
* perform pre processing before opening the feature archive | |
*/ | |
// VK: why is open protected and close public | |
protected void openFeature() { | |
}; | |
/** | |
* perform post processing to close a feature archive | |
*/ | |
public void closeFeature() throws IOException { | |
}; | |
/** | |
* return the appropriate resource bundle for this feature | |
*/ | |
public ResourceBundle getResourceBundle() throws IOException, CoreException { | |
ResourceBundle bundle = null; | |
try { | |
ClassLoader l = new URLClassLoader(new URL[] { this.getURL()}, null); | |
bundle = ResourceBundle.getBundle(FEATURE_FILE, Locale.getDefault(), l); | |
} catch (MissingResourceException e) { | |
//ok, there is no bundle, keep it as null | |
//DEBUG: | |
if (UpdateManagerPlugin.DEBUG && UpdateManagerPlugin.DEBUG_SHOW_WARNINGS) { | |
UpdateManagerPlugin.getPlugin().debug(e.getLocalizedMessage() + ":" + this.getURL().toExternalForm()); | |
} | |
} | |
return bundle; | |
} | |
/** | |
* @see IFeature#getArchives() | |
* Private implementation of the feature. return the list of ID. | |
* Call the site with the ID to get the URL of the contentReference of the Site | |
*/ | |
public abstract String[] getArchives(); | |
/** | |
* return the archive ID for a plugin | |
* The id is based on the feature | |
* the default ID is plugins/pluginId_pluginVer or | |
* the default ID is fragments/pluginId_pluginVer or | |
*/ | |
// VK: what do we want the concept of the id to be? we override it in PackagedFeature. | |
// VK: the whole handling of pluginEntry, dataEntry, archive is CONFUSING !!! | |
// VK: what about non-plugin data ??? are these archives??? | |
public String getArchiveID(IPluginEntry entry){ | |
//FIXME: fragments | |
String type = (entry.isFragment())?Site.DEFAULT_FRAGMENT_PATH:Site.DEFAULT_PLUGIN_PATH; | |
return type+entry.getIdentifier().toString(); | |
} | |
/** | |
* return the list of FILE to be transfered for a Plugin | |
*/ | |
// VK: why are some of the abstract methods public and others are protected. All | |
// VK: methods that need to be called by other objects (ec. Site) need to be public | |
protected abstract String[] getStorageUnitNames(IPluginEntry pluginEntry) throws CoreException; | |
/** | |
* return the list of FILE to be transfered from within the Feature | |
*/ | |
// VK: the term "storage unit" is confusing ... | |
protected abstract String[] getStorageUnitNames() throws CoreException; | |
/** | |
* return the Stream of the FILE to be transfered for a Plugin | |
*/ | |
protected abstract InputStream getInputStreamFor(IPluginEntry pluginEntry, String name) throws CoreException; | |
/** | |
* return the Stream of FILE to be transfered from within the Feature | |
*/ | |
protected abstract InputStream getInputStreamFor(String name) throws IOException, CoreException; | |
/** | |
* returns the list of archive to transfer/install | |
* in order to install the list of plugins | |
* | |
* @param pluginsToInstall list of plugin to install | |
*/ | |
protected abstract String[] getContentReferenceToInstall(IPluginEntry[] pluginsToInstall); | |
/* | |
* @see IAdaptable#getAdapter(Class) | |
*/ | |
//VK: who calls this. Why is this needed? | |
public Object getAdapter(Class adapter) { | |
return null; | |
} | |
} |