blob: c24c01e4cb02532d4a0bd9491702de1b85d93422 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.update.core;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import org.eclipse.update.core.model.*;
import org.eclipse.update.internal.core.*;
/**
* Local .jar file content reference.
* <p>
* This class may be instantiated or subclassed by clients.
* </p>
* @see org.eclipse.update.core.ContentReference
* @see org.eclipse.update.core.JarEntryContentReference
* @since 2.0
*/
public class JarContentReference extends ContentReference {
private static ArrayList referenceList = new ArrayList();
private JarFile jarFile;
/**
* Content selector used in .jar operations.
* Default implementation causes all file entries to be selected with
* generated identifiers being the same as the original .jar entry name.
*
* @since 2.0
*/
public static class ContentSelector {
/**
* Indicates whether the .jar entry should be selected.
* Default behavior is to select all non-directory entries.
*
* @param entry .jar entry
* @return <code>true</code> if entry is to be selected,
* <code>false</code> otherwise
* @since 2.0
*/
public boolean include(JarEntry entry) {
return entry == null ? false : !entry.isDirectory();
}
/**
* Defines the "symbolic" path identifier for the
* entry. Default identifier is the same as the jar entry name.
*
* @param entry .jar entry
* @return "symbolic" path identifier
* @since 2.0
*/
public String defineIdentifier(JarEntry entry) {
return entry == null ? null : entry.getName();
}
}
/**
* Create jar content reference from URL.
*
* @param id "symbolic" path identifier
* @param url actual referenced URL
* @since 2.0
*/
public JarContentReference(String id, URL url) {
super(id, url);
this.jarFile = null;
referenceList.add(this); // keep track of archives
}
/**
* Create jar content reference from file.
*
* @param id "symbolic" path identifier
* @param file actual referenced file
* @since 2.0
*/
public JarContentReference(String id, File file) {
super(id, file);
this.jarFile = null;
referenceList.add(this); // keep track of archives
}
/**
* A factory method to create a jar content reference.
*
* @param id "symbolic" path identifier
* @param file actual referenced file
* @return jar content reference
* @since 2.0
*/
public ContentReference createContentReference(String id, File file) {
return new JarContentReference(id, file,true);
}
/**
* Constructor JarContentReference.
* @param id
* @param file
* @param b
*/
public JarContentReference(String id, File file, boolean b) {
this(id,file);
setTempLocal(b);
}
/**
* Returns the content reference as a jar file. Note, that this method
* <b>does not</b> cause the file to be downloaded if it
* is not already local.
*
* @return reference as jar file
* @exception IOException reference cannot be returned as jar file
* @since 2.0
*/
protected JarFile asJarFile() throws IOException {
if (this.jarFile == null) {
File file = asFile();
if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
UpdateCore.debug("asJarFile :" + file);
if (file != null && !file.exists()) {
UpdateCore.warn("JarFile does not exits:" + file);
throw new FileNotFoundException(file.getAbsolutePath());
}
this.jarFile = new JarFile(file);
}
return jarFile;
}
/**
* Unpacks the referenced jar archive into the specified location.
* Returns content references to the unpacked files.
*
* @param dir location to unpack the jar into
* @param selector selector, used to select entries to unpack, and to define
* "symbolic" path identifiers for the entries.
* @param monitor progress monitor
* @exception IOException
* @exception InstallAbortedException
* @since 2.0
*/
public ContentReference[] unpack(File dir, ContentSelector selector, InstallMonitor monitor) throws IOException, InstallAbortedException {
// make sure we have a selector
if (selector == null)
selector = new ContentSelector();
// get archive content
JarFile jarArchive = this.asJarFile();
List content = new ArrayList();
Enumeration entries = jarArchive.entries();
// run through the entries and unjar
String entryId;
JarEntry entry;
InputStream is;
OutputStream os;
File localFile;
try {
if (monitor != null) {
monitor.saveState();
monitor.setTaskName(Policy.bind("JarContentReference.Unpacking"));
//$NON-NLS-1$
monitor.subTask(this.getIdentifier());
monitor.showCopyDetails(false);
}
while (entries.hasMoreElements()) {
entry = (JarEntry) entries.nextElement();
if (entry != null && selector.include(entry)) {
is = null;
os = null;
entryId = selector.defineIdentifier(entry);
localFile = Utilities.createLocalFile(dir, entryId); // create temp file
if (!entry.isDirectory()) {
try {
is = jarArchive.getInputStream(entry);
os = new FileOutputStream(localFile);
Utilities.copy(is, os, monitor);
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
}
if (os != null)
try {
os.close();
} catch (IOException e) {
}
}
content.add(new ContentReference(entryId, localFile));
}
}
}
} finally {
if (monitor != null)
monitor.restoreState();
}
return (ContentReference[]) content.toArray(new ContentReference[0]);
}
/**
* Unpacks the named jar entry into the specified location.
* Returns content reference to the unpacked file.
*
* @param dir location to unpack the jar into
* @param entryName name of the jar entry
* @param selector selector, used to define "symbolic" path identifier
* for the entry
* @param monitor progress monitor
* @exception IOException
* @exception InstallAbortedException
* @since 2.0
*/
public ContentReference unpack(File dir, String entryName, ContentSelector selector, InstallMonitor monitor) throws IOException, InstallAbortedException {
// make sure we have a selector
if (selector == null)
selector = new ContentSelector();
// unjar the entry
JarFile jarArchive = this.asJarFile();
entryName = entryName.replace(File.separatorChar, '/');
JarEntry entry = jarArchive.getJarEntry(entryName);
String entryId;
if (entry != null) {
InputStream is = null;
OutputStream os = null;
entryId = selector.defineIdentifier(entry);
File localFile = Utilities.createLocalFile(dir, entryId); // create temp file
if (!entry.isDirectory()) {
try {
is = jarArchive.getInputStream(entry);
os = new FileOutputStream(localFile);
Utilities.copy(is, os, monitor);
} finally {
if (is != null)
try {
is.close();
} catch (IOException e) {
}
if (os != null)
try {
os.close();
} catch (IOException e) {
}
}
return new ContentReference(entryId, localFile);
} else
return null; // entry was a directory
} else
throw new FileNotFoundException(this.asFile().getAbsolutePath() + " " + entryName);
//$NON-NLS-1$
}
/**
* Peeks into the referenced jar archive.
* Returns content references to the jar entries within the jar file.
*
* @param selector selector, used to select entries to return, and to define
* "symbolic" path identifiers for the entries.
* @param monitor progress monitor
* @exception IOException
* @since 2.0
*/
public ContentReference[] peek(ContentSelector selector, InstallMonitor monitor) throws IOException {
// make sure we have a selector
if (selector == null)
selector = new ContentSelector();
// get archive content
JarFile jarArchive = this.asJarFile();
List content = new ArrayList();
Enumeration entries = jarArchive.entries();
// run through the entries and create content references
JarEntry entry;
String entryId;
while (entries.hasMoreElements()) {
entry = (JarEntry) entries.nextElement();
if (selector.include(entry)) {
entryId = selector.defineIdentifier(entry);
content.add(new JarEntryContentReference(entryId, this, entry));
}
}
return (ContentReference[]) content.toArray(new ContentReference[0]);
}
/**
* Peeks into the referenced jar archive looking for the named entry.
* Returns content reference to the jar entry within the jar file.
*
* @param entryName name of the jar entry
* @param selector selector, used to define "symbolic" path identifier
* for the entry
* @param monitor progress monitor
* @return the content reference ofr <code>null</null> if the entry doesn't exist
* @exception IOException
* @since 2.0
*/
public ContentReference peek(String entryName, ContentSelector selector, InstallMonitor monitor) throws IOException {
// make sure we have a selector
if (selector == null)
selector = new ContentSelector();
// assume we have a reference that represents a jar archive.
JarFile jarArchive = this.asJarFile();
entryName = entryName.replace(File.separatorChar, '/');
JarEntry entry = jarArchive.getJarEntry(entryName);
if (entry == null)
return null;
String entryId = selector.defineIdentifier(entry);
return new JarEntryContentReference(entryId, this, entry);
}
/**
* Closes the jar archive corresponding to this reference.
*
* @exception IOException
* @since 2.0
*/
public void closeArchive() throws IOException {
if (this.jarFile != null) {
this.jarFile.close();
this.jarFile = null;
}
}
/**
* Perform shutdown processing for jar archive handling.
* This method is called when platform is shutting down.
* It is not intended to be called at any other time under
* normal circumstances. A side-effect of calling this method
* is that all jars referenced by JarContentReferences are closed.
*
* @since 2.0
*/
public static void shutdown() {
for (int i = 0; i < referenceList.size(); i++) {
JarContentReference ref = (JarContentReference) referenceList.get(i);
try {
ref.closeArchive(); // ensure we are not leaving open jars
} catch (IOException e) {
// we tried, nothing we can do ...
}
}
}
}