| /******************************************************************************* |
| * 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; |
| } |
| |
| |
| } |