blob: 03fc1807adeb9667cf383ae710ec407ae299dcc9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 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 org.eclipse.update.core.FeatureContentProvider;
import java.io.*;
import java.net.URL;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.osgi.signedcontent.SignedContentFactory;
import org.eclipse.osgi.util.NLS;
import org.eclipse.update.core.*;
import org.eclipse.update.internal.jarprocessor.JarProcessor;
import org.eclipse.update.internal.jarprocessor.Utils;
import org.eclipse.update.internal.security.JarVerifier;
import org.eclipse.update.internal.verifier.CertVerifier;
/**
* Content Provider of a Feature Package
*/
public class FeaturePackagedContentProvider extends FeatureContentProvider {
private ContentReference localManifest = null;
private ContentReference[] localFeatureFiles = new ContentReference[0];
private IVerifier jarVerifier = null;
private ExtendedSite siteModel = null;
private boolean continueOnError;
/*
* filter for file with .jar
*/
public static final FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(FeatureContentProvider.JAR_EXTENSION);
}
};
/*
* Constructor
*/
public FeaturePackagedContentProvider(URL url, ISite site) {
super(url);
if (site instanceof ExtendedSite) {
this.siteModel = (ExtendedSite) site;
}
}
/*
* Returns a new verifier for each top-level install
* (if the verifier has a parent, return the verifier
* otherwise reinitialize)
*/
public IVerifier getVerifier() throws CoreException {
SignedContentFactory factory = UpdateCore.getPlugin().getSignedContentFactory();
if (jarVerifier == null || jarVerifier.getParent() == null) {
if (factory != null)
jarVerifier = new CertVerifier(UpdateCore.getPlugin().getSignedContentFactory());
else
jarVerifier = new JarVerifier();
return jarVerifier;
}
// re-init will be done if the parent changes
return jarVerifier;
}
/*
* @see IFeatureContentProvider#getFeatureManifestReference()
*/
public ContentReference getFeatureManifestReference(InstallMonitor monitor) throws CoreException {
// check to see if we already have local copy of the manifest
if (localManifest != null)
return localManifest;
ContentReference[] featureArchiveReference = getFeatureEntryArchiveReferences(monitor);
JarContentReference featureJarReference = null;
try {
// force feature archive to local.
// This content provider always assumes exactly 1 archive file (index [0])
featureJarReference = (JarContentReference) asLocalReference(featureArchiveReference[0], null);
// we need to unpack archive locally for UI browser references to be resolved correctly
localFeatureFiles = featureJarReference.unpack(getWorkingDirectory(), null, monitor);
} catch (IOException e) {
throw errorRetrieving(Feature.FEATURE_XML, featureJarReference, e);
}
// find the manifest in the unpacked feature files
for (int i = 0; i < localFeatureFiles.length; i++) {
if (localFeatureFiles[i].getIdentifier().equals(Feature.FEATURE_XML)) {
localManifest = localFeatureFiles[i];
// cache reference to manifest
return localManifest;
}
}
// the manifest has not been found
String[] values = new String[] { Feature.FEATURE_XML, getURL().toExternalForm()};
throw Utilities.newCoreException(NLS.bind(Messages.FeaturePackagedContentProvider_NoManifestFile, values), new Exception());
}
/*
* @see IFeatureContentProvider#getArchiveReferences()
*/
public ContentReference[] getArchiveReferences(InstallMonitor monitor) throws CoreException {
IPluginEntry[] entries = getFeature().getPluginEntries();
INonPluginEntry[] nonEntries = getFeature().getNonPluginEntries();
List listAllContentRef = new ArrayList();
ContentReference[] allContentRef = new ContentReference[0];
// feature
listAllContentRef.addAll(Arrays.asList(getFeatureEntryArchiveReferences(monitor)));
// plugins
for (int i = 0; i < entries.length; i++) {
listAllContentRef.addAll(Arrays.asList(getPluginEntryArchiveReferences(entries[i], monitor)));
}
// non plugins
for (int i = 0; i < nonEntries.length; i++) {
listAllContentRef.addAll(Arrays.asList(getNonPluginEntryArchiveReferences(nonEntries[i], monitor)));
}
// transform List in Array
if (listAllContentRef.size() > 0) {
allContentRef = new ContentReference[listAllContentRef.size()];
listAllContentRef.toArray(allContentRef);
}
return allContentRef;
}
/*
* @see IFeatureContentProvider#getFeatureEntryArchiveReferences()
*/
public ContentReference[] getFeatureEntryArchiveReferences(InstallMonitor monitor) throws CoreException {
//1 jar file <-> 1 feature
// we will return the JAR file
ContentReference[] references = new ContentReference[1];
ContentReference currentReference = null;
String archiveID = null;
try {
archiveID = (getFeature() != null) ? getFeature().getVersionedIdentifier().toString() : ""; //$NON-NLS-1$
currentReference = new JarContentReference(archiveID, getURL());
currentReference = asLocalReference(currentReference, monitor);
references[0] = currentReference;
} catch (IOException e) {
references[0] = continueOnErrorOrRethrow(archiveID, e);
}
return references;
}
/*
* @see IFeatureContentProvider#getPluginEntryArchiveReferences(IPluginEntry)
*/
public ContentReference[] getPluginEntryArchiveReferences(IPluginEntry pluginEntry, InstallMonitor monitor) throws CoreException {
// 1 plugin <-> 1 jar
// we return the JAR file
ContentReference[] references = new ContentReference[1];
String archiveID = getPathID(pluginEntry);
ISite site = (getFeature() == null) ? null : getFeature().getSite();
ISiteContentProvider siteContentProvider = (site == null) ? null : site.getSiteContentProvider();
URL url = (siteContentProvider == null) ? null : siteContentProvider.getArchiveReference(archiveID);
try {
references[0] = retrieveLocalJar(new JarContentReference(archiveID, url), monitor);
} catch (IOException e) {
references[0] = continueOnErrorOrRethrow(archiveID, e);
}
return references;
}
private ContentReference retrieveLocalJar(JarContentReference reference, InstallMonitor monitor) throws IOException, CoreException {
//If the site does not support pack200, just get the jar as normal
if(siteModel == null || !siteModel.supportsPack200() || !JarProcessor.canPerformUnpack()) {
ContentReference contentReference = null;
try {
contentReference = asLocalReference(reference, monitor);
}
catch (FileNotFoundException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
catch (IOException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
catch (CoreException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
return contentReference;
}
ContentReference packedRef = null;
String key = reference.toString();
Object jarLock = LockManager.getLock(key);
synchronized (jarLock) {
//do we have this jar already?
File localFile = Utilities.lookupLocalFile(key);
if (localFile != null) {
// check if the cached file is still valid (no newer version on server)
if (UpdateManagerUtils.isSameTimestamp(reference.asURL(), localFile.lastModified())) {
LockManager.returnLock(key);
return reference.createContentReference(reference.getIdentifier(), localFile);
}
}
try {
//don't have jar, check for pack.gz
URL packGZURL = new URL(reference.asURL().toExternalForm() + ".pack.gz"); //$NON-NLS-1$
packedRef = asLocalReference(new JarContentReference(reference.getIdentifier(), packGZURL), monitor);
} catch (IOException e) {
//no pack.gz
} catch (CoreException e){
//no pack.gz
}
}
if (packedRef == null) {
//no pack.gz on server, get normal jar
ContentReference contentReference = null;
try {
contentReference = asLocalReference(reference, monitor);
}
catch (FileNotFoundException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
catch (IOException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
catch (CoreException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
return contentReference;
}
boolean success = false;
synchronized (jarLock) {
String packed = packedRef.toString();
Object packedLock = LockManager.getLock(packed);
synchronized (packedLock) {
try {
File tempFile = packedRef.asFile();
long timeStamp = tempFile.lastModified();
JarProcessor processor = JarProcessor.getUnpackProcessor(null);
processor.setWorkingDirectory(tempFile.getParent());
File packedFile = new File(tempFile.toString() + Utils.PACKED_SUFFIX);
tempFile.renameTo(packedFile);
if (monitor != null) {
monitor.saveState();
monitor.subTask(Messages.JarContentReference_Unpacking + " " + reference.getIdentifier() + Utils.PACKED_SUFFIX); //$NON-NLS-1$
monitor.showCopyDetails(false);
}
//unpacking the jar will strip the ".pack.gz" and leave us back with the original filename
try {
processor.processJar(packedFile);
} catch (Throwable e) {
//something is wrong unpacking
}
if(tempFile.exists() && tempFile.length() > 0){
success = true;
tempFile.setLastModified(timeStamp);
Utilities.mapLocalFile(key, tempFile);
UpdateCore.getPlugin().getUpdateSession().markVisited(reference.asURL());
}
} finally {
LockManager.returnLock(packed);
LockManager.returnLock(key);
if(monitor != null)
monitor.restoreState();
}
}
}
if(!success){
//Something went wrong with the unpack, get the normal jar.
ContentReference contentReference = null;
try {
contentReference = asLocalReference(reference, monitor);
}
catch (FileNotFoundException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
catch (IOException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
catch (CoreException e) {
contentReference = continueOnErrorOrRethrow(reference.getIdentifier(), e);
}
return contentReference;
}
return packedRef;
}
/*
* @see IFeatureContentProvider#getNonPluginEntryArchiveReferences(INonPluginEntry)
*/
public ContentReference[] getNonPluginEntryArchiveReferences(INonPluginEntry nonPluginEntry, InstallMonitor monitor) throws CoreException {
// archive = feature/<id>_<ver>/<file>
String archiveID = Site.DEFAULT_FEATURE_PATH + ((getFeature() != null) ? getFeature().getVersionedIdentifier().toString() : ""); //$NON-NLS-1$
archiveID += "/" + nonPluginEntry.getIdentifier(); //$NON-NLS-1$
ContentReference[] references = new ContentReference[1];
ContentReference currentReference = null;
try {
ISite site = (getFeature() == null) ? null : getFeature().getSite();
ISiteContentProvider siteContentProvider = (site == null) ? null : site.getSiteContentProvider();
URL url = (siteContentProvider == null) ? null : siteContentProvider.getArchiveReference(archiveID);
currentReference = new ContentReference(nonPluginEntry.getIdentifier(), url);
currentReference = asLocalReference(currentReference, monitor);
references[0] = currentReference;
} catch (IOException e) {
references[0] = continueOnErrorOrRethrow(archiveID, e);
}
return references;
}
/*
* @see IFeatureContentProvider#getFeatureEntryContentReferences()
*/
public ContentReference[] getFeatureEntryContentReferences(InstallMonitor monitor) throws CoreException {
return localFeatureFiles; // return cached feature references
// Note: assumes this content provider is always called first to
// get the feature manifest. This forces the feature files
// to be unpacked and caches the references
}
/*
* @see IFeatureContentProvider#getPluginEntryContentReferences(IPluginEntry)
*/
public ContentReference[] getPluginEntryContentReferences(IPluginEntry pluginEntry, InstallMonitor monitor) throws CoreException {
ContentReference[] references = getPluginEntryArchiveReferences(pluginEntry, monitor);
ContentReference[] pluginReferences = new ContentReference[0];
try {
if (references[0] instanceof JarContentReference) {
JarContentReference localRef = (JarContentReference) asLocalReference(references[0], monitor);
pluginReferences = localRef.peek(null, monitor);
} else {
// return the list of all subdirectories
List files = getFiles(references[0].asFile());
pluginReferences = new ContentReference[files.size()];
for (int i = 0; i < pluginReferences.length; i++) {
File currentFile = (File) files.get(i);
pluginReferences[i] = new ContentReference(null, currentFile.toURL());
}
}
//[20866] we did not preserve executable bit
validatePermissions(pluginReferences);
} catch (IOException e) {
throw errorRetrieving(pluginEntry.getVersionedIdentifier().toString(), references[0], e);
}
return pluginReferences;
}
/*
* return all the files under the directory
*/
private List getFiles(File dir) throws IOException {
List result = new ArrayList();
if (!dir.isDirectory())
throw new IOException(NLS.bind(Messages.FeaturePackagedContentProvider_InvalidDirectory, (new String[] { dir.getPath() })));
File[] files = dir.listFiles();
if (files != null) // be careful since it can be null
for (int i = 0; i < files.length; ++i) {
if (files[i].isDirectory()) {
result.addAll(getFiles(files[i]));
} else {
result.add(files[i]);
}
}
return result;
}
/*
*
*/
private CoreException errorRetrieving(String obj, ContentReference archive, Exception e) {
String[] values = new String[] { obj };
return Utilities.newCoreException(NLS.bind(Messages.FeaturePackagedContentProvider_ErrorRetrieving, values), e);
}
public void setContinueOnError(boolean continueOnError) {
this.continueOnError = continueOnError;
}
/**
* This method is used for when a core exception is detected, so, if its decided to rethrow, then
* a core exception odes not have to be recreated.
*
* @param archiveID id of the archive file
* @param CoreException
* @return NullReference if its decided not to continue
* @throws CoreException
*/
/*private ContentReference continueOrErrorOrRethrow(String archiveID, CoreException coreException) throws CoreException {
ContentReference reference = null;
if (continueOnError) {
// this ContentReference without a file or URL is purely a
// "missing jar" reference.
reference = new NullContentReference(archiveID);
String msg = " ContinueOnError: The following ID was not found, so was skipped, and is not on miror site: " + archiveID; //$NON-NLS-1$
String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
IStatus status = new Status(IStatus.WARNING, id , 0, msg, null);
UpdateCore.log(status);
}
else {
throw coreException;
}
return reference;
}*/
private ContentReference continueOnErrorOrRethrow(String archiveID, Exception e) throws CoreException {
ContentReference reference = null;
if (continueOnError) {
// this ContentReference without a file or URL is purely a
// "missing jar" reference.
reference = new NullContentReference(archiveID);
String msg = " ContinueOnError: The following ID was not found, so was skipped, and is not on miror site: " + archiveID; //$NON-NLS-1$
String id = UpdateCore.getPlugin().getBundle().getSymbolicName();
IStatus status = new Status(IStatus.WARNING, id , 0, msg, null);
UpdateCore.log(status);
}
else {
throw errorRetrieving(archiveID, reference, e);
}
return reference;
}
}