blob: cf4fff1e83b98c46e92cbe8f24241ec7d7ac6050 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.jst.j2ee.internal.archive.operations;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jst.j2ee.internal.J2EEConstants;
import org.eclipse.jst.jee.archive.ArchiveException;
import org.eclipse.jst.jee.archive.ArchiveSaveFailureException;
import org.eclipse.jst.jee.archive.internal.ArchiveUtil;
import org.eclipse.wst.common.componentcore.internal.flat.FlatFolder;
import org.eclipse.wst.common.componentcore.internal.flat.FlatVirtualComponent;
import org.eclipse.wst.common.componentcore.internal.flat.IChildModuleReference;
import org.eclipse.wst.common.componentcore.internal.flat.IFlatFile;
import org.eclipse.wst.common.componentcore.internal.flat.IFlatFolder;
import org.eclipse.wst.common.componentcore.internal.flat.IFlatResource;
import org.eclipse.wst.common.componentcore.internal.flat.IFlatVirtualComponent;
import org.eclipse.wst.common.componentcore.internal.flat.IFlattenParticipant;
import org.eclipse.wst.common.componentcore.internal.flat.VirtualComponentFlattenUtility;
import org.eclipse.wst.common.componentcore.internal.flat.FlatVirtualComponent.FlatComponentTaskModel;
import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
public class FlatComponentArchiver {
private IFlatVirtualComponent flatComponent;
private OutputStream destinationStream;
private ZipOutputStream zipOutputStream;
private IVirtualComponent component;
private List<IFlattenParticipant> participants;
private ComponentExportCallback callbackHandler;
private List<IPath> zipEntries = new ArrayList<IPath>();
private List <IVirtualComponent> componentsArchived = new ArrayList<IVirtualComponent>();
public interface ComponentExportCallback {
public boolean canSave(IVirtualComponent component);
public IFlatVirtualComponent saveComponent(IVirtualComponent component, ZipOutputStream zipOutputStream, List<IPath> zipEntries) throws ArchiveException;
public boolean createManifest();
}
public FlatComponentArchiver(IVirtualComponent aComponent, OutputStream out, List<IFlattenParticipant> fParticipants) {
participants = fParticipants;
component = aComponent;
destinationStream = out;
zipOutputStream = new ZipOutputStream(out);
flatComponent = getFlatComponent(aComponent);
}
public FlatComponentArchiver(IVirtualComponent aComponent, OutputStream out, List<IFlattenParticipant> fParticipants, ComponentExportCallback callback) {
participants = fParticipants;
component = aComponent;
destinationStream = out;
callbackHandler = callback;
zipOutputStream = new ZipOutputStream(out);
flatComponent = getFlatComponent(aComponent);
}
public void close() throws IOException {
getDestinationStream().close();
}
public void finish() throws IOException {
getZipOutputStream().finish();
//If this is not nested, close the stream to free up the resource
//otherwise, don't close it because the parent may not be done
if (!(getDestinationStream() instanceof ZipOutputStream))
getDestinationStream().close();
}
/**
* Fetches the resources for the component using the FlatVirtualComponent
* and saves them to an archive.
*
* @throws ArchiveSaveFailureException
*/
protected void saveArchive() throws ArchiveSaveFailureException {
Exception caughtException = null;
boolean createManifest = true;
try {
if (callbackHandler != null && callbackHandler.canSave(getComponent())) {
flatComponent = callbackHandler.saveComponent(getComponent(), getZipOutputStream(), zipEntries);
createManifest = callbackHandler.createManifest();
}
if (flatComponent != null) {
IFlatResource[] resources = flatComponent.fetchResources();
saveManifest(Arrays.asList(resources), createManifest);
saveChildModules(flatComponent.getChildModules());
saveFlatResources(resources);
}
} catch (Exception e){
caughtException = e;
} finally {
try {
finish();
} catch (IOException e) {
throw new ArchiveSaveFailureException(e);
} finally {
if (caughtException != null){
throw new ArchiveSaveFailureException(caughtException);
}
}
}
}
/**
* Creates a nested archive for the component inside its parent archive
*
* @param component
* @param entry
* @return FlatComponentArchiver
* @throws IOException
*/
protected FlatComponentArchiver saveNestedArchive(IVirtualComponent component, IPath entry) throws IOException {
ZipEntry nest = new ZipEntry(entry.toString());
getZipOutputStream().putNextEntry(nest);
return new FlatComponentArchiver(component, getZipOutputStream(), getParticipants(), callbackHandler);
}
/**
* Adds the resources returned from the FlatVirtualComponent into the archive
*
* @param resources
* @throws ArchiveSaveFailureException
*/
protected void saveFlatResources(IFlatResource[] resources) throws ArchiveSaveFailureException {
for (int i = 0; i < resources.length; i++) {
IFlatResource resource = resources[i];
IPath entryPath = resource.getModuleRelativePath().append(resource.getName());
if (resource instanceof IFlatFile) {
if (shouldInclude(entryPath, true)) {
addZipEntry(resource, entryPath);
zipEntries.add(entryPath);
}
} else if (resource instanceof IFlatFolder) {
if (shouldInclude(entryPath, false)) {
addZipEntry(resource, entryPath);
zipEntries.add(entryPath);
saveFlatResources(((IFlatFolder)resource).members());
}
}
}
}
/**
* @param entryPath
* @param isFile
* @return true or false - should resource be added to the archive
*/
protected boolean shouldInclude(IPath entryPath, boolean isFile) {
if (zipEntries.contains(entryPath)) {
return false;
}
if (isFile) {
if (entryPath.equals(new Path(J2EEConstants.MANIFEST_URI))) {
return false;
}
}
else if (entryPath.equals(new Path(IModuleConstants.DOT_SETTINGS))) {
return false;
}
return true;
}
/**
* Creates the nested jars from project references and saves them
* to the archive.
*
* @param childModules
* @throws ArchiveSaveFailureException
* @throws IOException
*/
protected void saveChildModules(IChildModuleReference[] childModules) throws ArchiveSaveFailureException, IOException {
componentsArchived.add(component);
for (int i = 0; i < childModules.length; i++) {
IChildModuleReference childModule = childModules[i];
IPath entryPath = childModule.getRelativeURI();
//keep track of project references added. we only want to include the
//project when both a binary module and its backing project exist
zipEntries.add(entryPath);
//prevent an infinite loop due to cycle dependencies
if (componentsArchived.contains(childModule.getComponent()))
continue;
FlatComponentArchiver saver = saveNestedArchive(childModule.getComponent(), entryPath);
saver.setArchivedComponents(componentsArchived);
saver.saveArchive();
}
}
/**
* Adds an entry and copies the resource into the archive
*
* @param flatresource
* @param entryPath
* @throws ArchiveSaveFailureException
*/
protected void addZipEntry(IFlatResource f, IPath entryPath) throws ArchiveSaveFailureException {
try {
IPath path = entryPath;
boolean isFolder = false;
long lastModified = 0;
if (f instanceof IFlatFolder) {
isFolder = true;
File folder = (File)((IFlatFolder)f).getAdapter(File.class);
if (folder != null) {
lastModified = folder.lastModified();
}
if (!path.hasTrailingSeparator())
path = path.addTrailingSeparator();
}
else {
lastModified = ((IFlatFile) f).getModificationStamp();
}
ZipEntry entry = new ZipEntry(path.toString());
if (lastModified > 0)
entry.setTime(lastModified);
getZipOutputStream().putNextEntry(entry);
if (!isFolder) {
ArchiveUtil.copy((InputStream) f.getAdapter(InputStream.class), getZipOutputStream());
}
getZipOutputStream().closeEntry();
} catch (IOException e) {
throw new ArchiveSaveFailureException(e);
}
}
/**
* The FlatVirtualComponent is what does the bulk of the work
*
* @param component
* @return IFlatVirtualComponent
*/
protected IFlatVirtualComponent getFlatComponent(IVirtualComponent component) {
FlatComponentTaskModel options = new FlatComponentTaskModel();
options.put(FlatVirtualComponent.PARTICIPANT_LIST, getParticipants());
return new FlatVirtualComponent(component, options);
}
protected List<IFlattenParticipant> getParticipants() {
return participants;
}
protected java.util.zip.ZipOutputStream getZipOutputStream() {
return zipOutputStream;
}
/**
* This method adds the existing MANIFEST.MF as the first entry in the archive.
* This is necessary to support clients who use JarInputStream.getManifest().
* If no MANIFEST.MF is found, one is created if createManifest param is true
*
* @param resources
* @param createManifest
* @throws ArchiveSaveFailureException
*/
private void saveManifest(List<IFlatResource> resources, boolean createManifest) throws ArchiveSaveFailureException {
IFlatFolder metainf = (FlatFolder)VirtualComponentFlattenUtility.getExistingModuleResource(resources, new Path(J2EEConstants.META_INF));
IFlatFile manifest = null;
if (metainf != null) {
IFlatResource[] children = metainf.members();
for (int i = 0; i < children.length; i++) {
if (children[i].getName().equals(J2EEConstants.MANIFEST_SHORT_NAME)) {
manifest = (IFlatFile) children[i];
IPath entryPath = manifest.getModuleRelativePath().append(manifest.getName());
addZipEntry(manifest, entryPath);
break;
}
}
}
if (createManifest && manifest == null) {
//manifest not found so create one for the archive
createManifest();
}
}
private void createManifest() throws ArchiveSaveFailureException {
String manifestContents = "Manifest-Version: 1.0\r\n\r\n"; //$NON-NLS-1$
try {
ZipEntry entry = new ZipEntry(J2EEConstants.MANIFEST_URI);
getZipOutputStream().putNextEntry(entry);
ArchiveUtil.copy(new ByteArrayInputStream(manifestContents.getBytes()), getZipOutputStream());
} catch (IOException e) {
throw new ArchiveSaveFailureException(e);
}
}
public void setArchivedComponents(List<IVirtualComponent> componentList) {
componentsArchived.addAll(componentList);
}
public java.io.OutputStream getDestinationStream() {
return destinationStream;
}
public IVirtualComponent getComponent() {
return component;
}
public IFlatVirtualComponent getFlatComponent() {
return flatComponent;
}
public List<IPath> getZipEntries() {
return zipEntries;
}
}