/******************************************************************************* * Copyright (c) 2005, 2007 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.jee.archive.internal; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipException; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.eclipse.jst.common.internal.modulecore.util.ManifestUtilities; import org.eclipse.jst.jee.archive.ArchiveSaveFailureException; public class ArchiveUtil { protected static String tempDirectoryName; protected static java.io.File tempDirectory; /** * Returns the filename from the uri, or the segment after the last * occurrence of a separator */ public static String getFileNameTail(String uri) { String tempURI = uri.replace('\\', '/'); while (tempURI.endsWith("/")) //$NON-NLS-1$ tempURI = tempURI.substring(0, tempURI.length() - 1); int lastIndex = tempURI.lastIndexOf('/'); if (lastIndex == -1) return uri; return uri.substring(lastIndex + 1, tempURI.length()); } public static java.io.File getTempDirectory() { return tempDirectory; } public static java.io.File createTempFile(String baseName) throws IOException { return createTempFile(baseName, getTempDirectory()); } public static java.io.File createTempFile(String baseName, java.io.File directory) throws IOException { String fileName = getFileNameTail(baseName); if (fileName.length() < 3) { fileName = "WSTMP" + fileName; //$NON-NLS-1$ } java.io.File tempFile = java.io.File.createTempFile(fileName, null, directory); DeleteOnExitUtility.markForDeletion(tempFile); return tempFile; } /** * returns a list of all files, recursive, that can't be written */ public static List getWriteProtectedFiles(java.io.File aFile, List aList) { if (aList == null) aList = new ArrayList(); if (aFile.exists() && !aFile.canWrite()) aList.add(aFile); if (aFile.isDirectory()) { java.io.File[] files = aFile.listFiles(); for (int i = 0; i < files.length; i++) { getWriteProtectedFiles(files[i], aList); } } return aList; } public static void checkWriteable(java.io.File aFile) throws ArchiveSaveFailureException { List locked = ArchiveUtil.getWriteProtectedFiles(aFile, null); if (locked.isEmpty()) return; StringBuffer msg = new StringBuffer(); msg.append("Cannot write to file: "); //$NON-NLS-1$ msg.append(aFile.getAbsolutePath()); msg.append('\n'); msg.append("One or more files is write protected or locked:"); //$NON-NLS-1$ msg.append('\n'); for (int i = 0; i < locked.size(); i++) { java.io.File lockedFile = (java.io.File) locked.get(i); msg.append(lockedFile.getAbsolutePath()); msg.append('\n'); } throw new ArchiveSaveFailureException(msg.toString()); } /** * deletes a file from the file system; for directories, recurse the * subdirectories and delete them as well * * @return true if successful; false if any file or sub file could not be * deleted */ public static boolean delete(java.io.File aFile) { if (aFile == null) return true; if (aFile.isDirectory()) { java.io.File[] files = aFile.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { if (!delete(files[i])) return false; } } } return aFile.delete(); } /** * If we can rename it then we can delete it */ public static boolean isRenameable(java.io.File orig) { java.io.File origCopy1 = null; java.io.File origCopy2 = null; try { origCopy1 = orig.getCanonicalFile(); origCopy2 = orig.getCanonicalFile(); } catch (java.io.IOException ex) { return false; } String name = null; String baseName = "save.tmp"; //$NON-NLS-1$ try { if (orig.getParent() != null) baseName = new java.io.File(orig.getParent(), baseName).getCanonicalPath(); } catch (java.io.IOException ex) { return false; } java.io.File temp = null; int index = 0; do { name = baseName + index; temp = new java.io.File(name); index++; } while (temp.exists()); return origCopy1.renameTo(temp) && temp.renameTo(origCopy2); } public static void cleanupAfterTempSave(String aUri, java.io.File original, java.io.File destinationFile) throws ArchiveSaveFailureException { checkWriteable(original); boolean deleteWorked = false; if (original.isDirectory() && !isRenameable(original)) { // TODO throw new // SaveFailureException(CommonArchiveResourceHandler.getString(CommonArchiveResourceHandler.unable_replace_EXC_, // (new Object[]{original.getAbsolutePath()}))); // = "Unable to // replace original archive " } for (int i = 0; i < 10; i++) { if (ArchiveUtil.delete(original)) { deleteWorked = true; break; } try { // TODO Major hack here; the problem is that a previous call // to close the source file may not yet have // been reflected in the os/vm; therefore a subsequent call // to delete fails. To get around this, // wait for a bit and retry; if it continues to fail, then // time out and throw an exception Thread.sleep(250); } catch (InterruptedException e) { // Ignore } } if (deleteWorked) { for (int i = 0; i < 10; i++) { if (destinationFile.renameTo(original)){ DeleteOnExitUtility.fileHasBeenDeleted(destinationFile); return; } try { Thread.sleep(250); } catch (InterruptedException e) { // Ignore } } } // TODO throw new // SaveFailureException(CommonArchiveResourceHandler.getString(CommonArchiveResourceHandler.unable_replace_EXC_, // (new Object[]{original.getAbsolutePath()}))); // = "Unable to replace // original archive " } /** * Copy all the data from the input stream to the output stream up until the * first end of file character, and close the two streams */ public static void copy(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; try { int n = in.read(buffer); while (n > 0) { out.write(buffer, 0, n); n = in.read(buffer); } } finally { if (!(in instanceof ZipInputStream)) in.close(); if (!(out instanceof ZipOutputStream)) out.close(); } } public static void warn(Throwable e){ org.eclipse.jem.util.logger.proxy.Logger.getLogger().logWarning(e); } public static void warn(String message) { org.eclipse.jem.util.logger.proxy.Logger.getLogger().logWarning(message); } public static ZipFile newZipFile(String fileName)throws ZipException, IOException { return ArchiveUtil.newZipFile(new File(fileName), ZipFile.OPEN_READ); } public static ZipFile newZipFile(File aFile)throws ZipException, IOException { return ArchiveUtil.newZipFile(aFile, ZipFile.OPEN_READ); } /** * Utility to create ZipFiles which avoid memory leaks * because closing them fails to close open inputstreams. * There is a SUN bug open for this: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6735255 * but it looks like the "fix" will be to change the Javadoc :-( * @param aFile mode * @return * @throws ZipException * @throws IOException */ public static ZipFile newZipFile(File aFile, int mode) throws ZipException, IOException { return ManifestUtilities.newZipFile(aFile, mode); } }