org.eclipse.osgi.storage.StorageUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/*******************************************************************************
* Copyright (c) 2005, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Hannes Wellmann - Bug 577432 - Speed up and improve file processing in Storage
*******************************************************************************/
package org.eclipse.osgi.storage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.osgi.framework.internal.reliablefile.ReliableFile;
import org.eclipse.osgi.internal.debug.Debug;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
/**
* A utility class with some generally useful static methods for adaptor hook implementations
*/
public class StorageUtil {
/**
* Copies the content of the given path (file or directory) to the specified
* target. If the source is a directory all contained elements are copied
* recursively.
* @param inFile input directory to copy.
* @param outFile output directory to copy to.
* @throws IOException if any error occurs during the copy.
*/
public static void copy(File inFile, File outFile) throws IOException {
Path source = inFile.toPath();
Path target = outFile.toPath();
if (Files.exists(source)) {
Files.createDirectories(target.getParent());
try (Stream walk = Files.walk(source)) {
for (Path s : (Iterable) walk::iterator) {
Path t = target.resolve(source.relativize(s));
Files.copy(s, t, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
/**
* Read a file from an InputStream and write it to the file system.
*
* @param in InputStream from which to read. This stream will be closed by this method.
* @param file output file to create.
* @exception IOException
*/
public static void readFile(InputStream in, File file) throws IOException {
try (InputStream stream = in) {
Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
/**
* This function performs the equivalent of "rm -r" on a file or directory.
*
* @param file file or directory to delete
* @return false is the specified files still exists, true otherwise.
*/
public static boolean rm(File file, boolean DEBUG) {
Path path = file.toPath();
if (!Files.exists(path)) {
return true;
}
try {
Files.walkFileTree(path, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) {
return delete(f, DEBUG);
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
return delete(dir, DEBUG);
}
private FileVisitResult delete(Path pathToDelete, boolean debug) {
try {
if (debug) {
Debug.println("rm " + pathToDelete); //$NON-NLS-1$
}
Files.delete(pathToDelete);
} catch (IOException e) {
if (debug) {
Debug.println(" rm failed:" + e.getMessage()); //$NON-NLS-1$
}
}
return FileVisitResult.CONTINUE;
}
});
return !Files.exists(path);
} catch (IOException e) {
return false;
}
}
/**
* Register a service object.
* @param name the service class name
* @param service the service object
* @param context the registering bundle context
* @return the service registration object
*/
public static ServiceRegistration> register(String name, Object service, BundleContext context) {
Dictionary properties = new Hashtable<>();
properties.put(Constants.SERVICE_RANKING, Integer.valueOf(Integer.MAX_VALUE));
properties.put(Constants.SERVICE_PID, context.getBundle().getBundleId() + "." + service.getClass().getName()); //$NON-NLS-1$
return context.registerService(name, service, properties);
}
public static boolean canWrite(File installDir) {
if (!installDir.isDirectory())
return false;
if (Files.isWritable(installDir.toPath()))
return true;
File fileTest = null;
try {
// we use the .dll suffix to properly test on Vista virtual directories
// on Vista you are not allowed to write executable files on virtual directories
// like "Program Files"
fileTest = ReliableFile.createTempFile("writableArea", ".dll", installDir); //$NON-NLS-1$ //$NON-NLS-2$
} catch (IOException e) {
// If an exception occured while trying to create the file, it means that it is
// not writtable
return false;
} finally {
if (fileTest != null)
fileTest.delete();
}
return true;
}
public static URL encodeFileURL(File file) throws MalformedURLException {
return file.toURI().toURL();
}
public static byte[] getBytes(InputStream in, int length, int BUF_SIZE) throws IOException {
byte[] classbytes;
int bytesread = 0;
int readcount;
try {
if (length > 0) {
classbytes = new byte[length];
for (; bytesread < length; bytesread += readcount) {
readcount = in.read(classbytes, bytesread, length - bytesread);
if (readcount <= 0) /* if we didn't read anything */
break; /* leave the loop */
}
} else /* does not know its own length! */ {
length = BUF_SIZE;
classbytes = new byte[length];
readloop: while (true) {
for (; bytesread < length; bytesread += readcount) {
readcount = in.read(classbytes, bytesread, length - bytesread);
if (readcount <= 0) /* if we didn't read anything */
break readloop; /* leave the loop */
}
byte[] oldbytes = classbytes;
length += BUF_SIZE;
classbytes = new byte[length];
System.arraycopy(oldbytes, 0, classbytes, 0, bytesread);
}
}
if (classbytes.length > bytesread) {
byte[] oldbytes = classbytes;
classbytes = new byte[bytesread];
System.arraycopy(oldbytes, 0, classbytes, 0, bytesread);
}
} finally {
try {
in.close();
} catch (IOException ee) {
// nothing to do here
}
}
return classbytes;
}
public static void move(File from, File to, boolean DEBUG) throws IOException {
try {
Files.move(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
if (DEBUG) {
Debug.println("Failed to move atomically: " + from + " to " + to); //$NON-NLS-1$ //$NON-NLS-2$
}
// remove in case it failed because the target to non-empty directory or
// the target type does not match the from
rm(to, DEBUG);
// also, try without atomic operation
Files.move(from.toPath(), to.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
if (DEBUG) {
Debug.println("Successfully moved file: " + from + " to " + to); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private static final boolean IS_WINDOWS = File.separatorChar == '\\';
// reserved names according to
// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
private static final Set RESERVED_NAMES = new HashSet<>(Arrays.asList("aux", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
"com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
"lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
/** Tests whether the filename can escape path into special device **/
public static boolean isReservedFileName(File file) {
// Directory names are not checked here because illegal directory names will be
// handled by OS.
if (!IS_WINDOWS) { // only windows has special file names which can escape any path
return false;
}
String fileName = file.getName();
// Illegal characters are not checked here because they are check by both JDK
// and OS. This is only a check against technical allowed but unwanted device
// names.
int dot = fileName.indexOf('.');
// on windows, filename suffixes are not relevant to name validity
String basename = dot == -1 ? fileName : fileName.substring(0, dot);
return RESERVED_NAMES.contains(basename.toLowerCase());
}
}