/******************************************************************************* * Copyright (c) 2005, 2016 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.osgi.storage; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.util.Dictionary; import java.util.Hashtable; import java.util.concurrent.TimeUnit; import org.eclipse.osgi.internal.debug.Debug; import org.osgi.framework.*; /** * A utility class with some generally useful static methods for adaptor hook implementations */ public class StorageUtil { /** The NULL tag used in bundle storage */ public static final byte NULL = 0; /** The OBJECT tag used in bundle storage */ public static final byte OBJECT = 1; /** * Does a recursive copy of one directory to another. * @param inDir input directory to copy. * @param outDir output directory to copy to. * @throws IOException if any error occurs during the copy. */ public static void copyDir(File inDir, File outDir) throws IOException { String[] files = inDir.list(); if (files != null && files.length > 0) { outDir.mkdir(); for (int i = 0; i < files.length; i++) { File inFile = new File(inDir, files[i]); File outFile = new File(outDir, files[i]); if (inFile.isDirectory()) { copyDir(inFile, outFile); } else { InputStream in = new FileInputStream(inFile); readFile(in, outFile); } } } } /** * 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 { FileOutputStream fos = null; try { fos = new FileOutputStream(file); byte buffer[] = new byte[1024]; int count; while ((count = in.read(buffer, 0, buffer.length)) > 0) { fos.write(buffer, 0, count); } } finally { if (in != null) { try { in.close(); } catch (IOException ee) { // nothing to do here } } if (fos != null) { try { fos.close(); } catch (IOException ee) { // nothing to do here } } } } /** * 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) { if (file.exists()) { if (file.isDirectory()) { String list[] = file.list(); if (list != null) { int len = list.length; for (int i = 0; i < len; i++) { // we are doing a lot of garbage collecting here rm(new File(file, list[i]), DEBUG); } } } if (DEBUG) { if (file.isDirectory()) { Debug.println("rmdir " + file.getPath()); //$NON-NLS-1$ } else { Debug.println("rm " + file.getPath()); //$NON-NLS-1$ } } boolean success = file.delete(); if (DEBUG) { if (!success) { Debug.println(" rm failed!"); //$NON-NLS-1$ } } return (success); } return (true); } public static String readString(DataInputStream in, boolean intern) throws IOException { byte type = in.readByte(); if (type == NULL) return null; return intern ? in.readUTF().intern() : in.readUTF(); } public static void writeStringOrNull(DataOutputStream out, String string) throws IOException { if (string == null) out.writeByte(NULL); else { out.writeByte(OBJECT); out.writeUTF(string); } } /** * 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<String, Object> properties = new Hashtable<>(7); Dictionary<String, String> headers = context.getBundle().getHeaders(); properties.put(Constants.SERVICE_VENDOR, headers.get(Constants.BUNDLE_VENDOR)); 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.canWrite() == false) return false; if (!installDir.isDirectory()) return false; 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 = File.createTempFile("writtableArea", ".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; } /** * To remain Java 6 compatible work around the unreliable renameTo() via * retries: http://bugs.java.com/view_bug.do?bug_id=6213298 * * @param from * @param to */ public static boolean move(File from, File to, boolean DEBUG) { // Try several attempts with incremental sleep final int maxTries = 10; final int sleepStep = 200; for (int tryCount = 0, sleep = sleepStep;; sleep += sleepStep, tryCount++) { if (from.renameTo(to)) { return true; } if (DEBUG) { Debug.println("move: failed to rename " + from + " to " + to + " (" + (maxTries - tryCount) + " attempts remaining)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } if (tryCount >= maxTries) { break; } try { TimeUnit.MILLISECONDS.sleep(sleep); } catch (InterruptedException e) { // Ignore } } // Try a copy try { if (from.isDirectory()) { copyDir(from, to); } else { readFile(new FileInputStream(from), to); } if (!rm(from, DEBUG)) { Debug.println("move: failed to delete " + from + " after copy to " + to + ". Scheduling for delete on JVM exit."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ from.deleteOnExit(); } return true; } catch (IOException e) { if (DEBUG) { Debug.println("move: failed to copy " + from + " to " + to); //$NON-NLS-1$ //$NON-NLS-2$ Debug.printStackTrace(e); } // Give up return false; } } }