Package org.eclipse.osgi.internal.baseadaptor

Source Code of org.eclipse.osgi.internal.baseadaptor.BaseStorage$StateSaver

/*******************************************************************************
* Copyright (c) 2005, 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
*     Rob Harrop - SpringSource Inc. (bug 247520 and 253942)
*******************************************************************************/

package org.eclipse.osgi.internal.baseadaptor;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;
import java.util.*;
import org.eclipse.core.runtime.adaptor.EclipseStarter;
import org.eclipse.core.runtime.adaptor.LocationManager;
import org.eclipse.core.runtime.internal.adaptor.EclipseAdaptorMsg;
import org.eclipse.osgi.baseadaptor.BaseAdaptor;
import org.eclipse.osgi.baseadaptor.BaseData;
import org.eclipse.osgi.baseadaptor.bundlefile.*;
import org.eclipse.osgi.baseadaptor.hooks.*;
import org.eclipse.osgi.framework.adaptor.*;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.debug.FrameworkDebugOptions;
import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.framework.util.KeyedHashSet;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.storagemanager.ManagedOutputStream;
import org.eclipse.osgi.storagemanager.StorageManager;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;

public class BaseStorage implements SynchronousBundleListener {
  private static final String RUNTIME_ADAPTOR = FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + "/eclipseadaptor"; //$NON-NLS-1$
  private static final String OPTION_PLATFORM_ADMIN = RUNTIME_ADAPTOR + "/debug/platformadmin"; //$NON-NLS-1$
  private static final String OPTION_PLATFORM_ADMIN_RESOLVER = RUNTIME_ADAPTOR + "/debug/platformadmin/resolver"; //$NON-NLS-1$
  private static final String OPTION_MONITOR_PLATFORM_ADMIN = RUNTIME_ADAPTOR + "/resolver/timing"; //$NON-NLS-1$
  private static final String OPTION_RESOLVER_READER = RUNTIME_ADAPTOR + "/resolver/reader/timing"; //$NON-NLS-1$ 
  private static final String PROP_FRAMEWORK_EXTENSIONS = "osgi.framework.extensions"; //$NON-NLS-1$
  private static final String PROP_BUNDLE_STORE = "osgi.bundlestore"; //$NON-NLS-1$
  // The name of the bundle data directory
  static final String DATA_DIR_NAME = "data"; //$NON-NLS-1$
  static final String LIB_TEMP = "libtemp"; //$NON-NLS-1$
  // System property used to determine whether State saver needs to be enabled
  private static final String PROP_ENABLE_STATE_SAVER = "eclipse.enableStateSaver"; //$NON-NLS-1$
  static final String BUNDLEFILE_NAME = "bundlefile"; //$NON-NLS-1$
  // System property used to clean the osgi configuration area
  private static final String PROP_CLEAN = "osgi.clean"; //$NON-NLS-1$

  /** The current bundle data version */
  public static final byte BUNDLEDATA_VERSION = 18;
  /**
   * flag to indicate a framework extension is being intialized
   */
  public static final byte EXTENSION_INITIALIZE = 0x01;
  /**
   * flag to indicate a framework extension is being installed
   */
  public static final byte EXTENSION_INSTALLED = 0x02;
  /**
   * flag to indicate a framework extension is being uninstalled
   */
  public static final byte EXTENSION_UNINSTALLED = 0x04;
  /**
   * flag to indicate a framework extension is being updated
   */
  public static final byte EXTENSION_UPDATED = 0x08;

  /**
   * the file name for the delete flag.  If this file exists in one a directory
   * under the bundle store area then it will be removed during the
   * compact operation.
   */
  public static final String DELETE_FLAG = ".delete"; //$NON-NLS-1$
  private static final String PERM_DATA_FILE = ".permdata"; //$NON-NLS-1$
  private static final byte PERMDATA_VERSION = 1;

  private final MRUBundleFileList mruList = new MRUBundleFileList();

  private BaseAdaptor adaptor;
  // assume a file: installURL
  private String installPath;
  private StorageManager storageManager;
  private StateManager stateManager;
  // no need to synchronize on storageHooks because the elements are statically set in initialize
  private KeyedHashSet storageHooks = new KeyedHashSet(5, false);
  private BundleContext context;
  private SynchronousBundleListener extensionListener;

  /**
   * The add URL method used to support framework extensions
   */
  private final Method addFwkURLMethod;
  private final Method addExtURLMethod;

  /**
   * The list of configured framework extensions
   */
  private String[] configuredExtensions;

  private long timeStamp = 0;
  private int initialBundleStartLevel = 1;

  private final Object nextIdMonitor = new Object();
  private volatile long nextId = 1;
  /**
   * directory containing installed bundles
   */
  private File bundleStoreRoot;

  private BasePermissionStorage permissionStorage;
  private StateSaver stateSaver;
  private boolean invalidState;
  private boolean storageManagerClosed;

  BaseStorage() {
    // make constructor package private
    // initialize the addXYZURLMethods to support framework extensions
    addFwkURLMethod = findAddURLMethod(getFwkClassLoader(), "addURL"); //$NON-NLS-1$
    addExtURLMethod = findAddURLMethod(getExtClassLoader(), "addURL"); //$NON-NLS-1$
  }

  public void initialize(BaseAdaptor adaptor) throws IOException {
    this.adaptor = adaptor;
    setDebugOptions();
    if (Boolean.valueOf(FrameworkProperties.getProperty(BaseStorage.PROP_CLEAN)).booleanValue())
      cleanOSGiCache();

    // we need to set the install path as soon as possible so we can determine
    // the absolute location of install relative URLs
    Location installLoc = LocationManager.getInstallLocation();
    if (installLoc != null) {
      URL installURL = installLoc.getURL();
      // assume install URL is file: based
      installPath = installURL.getPath();
    }
    boolean readOnlyConfiguration = LocationManager.getConfigurationLocation().isReadOnly();
    storageManager = initFileManager(LocationManager.getOSGiConfigurationDir(), readOnlyConfiguration ? "none" : null, readOnlyConfiguration); //$NON-NLS-1$
    storageManagerClosed = false;
    // initialize the storageHooks
    StorageHook[] hooks = adaptor.getHookRegistry().getStorageHooks();
    for (int i = 0; i < hooks.length; i++)
      storageHooks.add(hooks[i]);
  }

  private static Method findAddURLMethod(ClassLoader cl, String name) {
    if (cl == null)
      return null;
    return findMethod(cl.getClass(), name, new Class[] {URL.class});
  }

  // recursively searches a class and it's superclasses for a (potentially inaccessable) method
  private static Method findMethod(Class clazz, String name, Class[] args) {
    if (clazz == null)
      return null; // ends the recursion when getSuperClass returns null
    try {
      Method result = clazz.getDeclaredMethod(name, args);
      result.setAccessible(true);
      return result;
    } catch (NoSuchMethodException e) {
      // do nothing look in super class below
    } catch (SecurityException e) {
      // if we do not have the permissions then we will not find the method
    }
    return findMethod(clazz.getSuperclass(), name, args);
  }

  private static void callAddURLMethod(ClassLoader cl, Method meth, URL arg) throws InvocationTargetException {
    try {
      meth.invoke(cl, new Object[] {arg});
    } catch (Throwable t) {
      throw new InvocationTargetException(t);
    }
  }

  private ClassLoader getFwkClassLoader() {
    return this.getClass().getClassLoader();
  }

  private ClassLoader getExtClassLoader() {
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    ClassLoader extcl = cl.getParent();
    while ((extcl != null) && (extcl.getParent() != null)) {
      extcl = extcl.getParent();
    }
    return extcl;
  }

  private static void setDebugOptions() {
    FrameworkDebugOptions options = FrameworkDebugOptions.getDefault();
    // may be null if debugging is not enabled
    if (options == null)
      return;
    StateManager.DEBUG = options != null;
    StateManager.DEBUG_READER = options.getBooleanOption(OPTION_RESOLVER_READER, false);
    StateManager.MONITOR_PLATFORM_ADMIN = options.getBooleanOption(OPTION_MONITOR_PLATFORM_ADMIN, false);
    StateManager.DEBUG_PLATFORM_ADMIN = options.getBooleanOption(OPTION_PLATFORM_ADMIN, false);
    StateManager.DEBUG_PLATFORM_ADMIN_RESOLVER = options.getBooleanOption(OPTION_PLATFORM_ADMIN_RESOLVER, false);
  }

  protected StorageManager initFileManager(File baseDir, String lockMode, boolean readOnly) throws IOException {
    StorageManager sManager = new StorageManager(baseDir, lockMode, readOnly);
    try {
      sManager.open(!readOnly);
    } catch (IOException ex) {
      if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
        Debug.println("Error reading framework metadata: " + ex.getMessage()); //$NON-NLS-1$
        Debug.printStackTrace(ex);
      }
      String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FILEMANAGER_OPEN_ERROR, ex.getMessage());
      FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, ex, null);
      adaptor.getFrameworkLog().log(logEntry);
      FrameworkProperties.setProperty(EclipseStarter.PROP_EXITCODE, "15"); //$NON-NLS-1$
      String errorDialog = "<title>" + AdaptorMsg.ADAPTOR_STORAGE_INIT_FAILED_TITLE + "</title>" + NLS.bind(AdaptorMsg.ADAPTOR_STORAGE_INIT_FAILED_MSG, baseDir) + "\n" + ex.getMessage(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
      FrameworkProperties.setProperty(EclipseStarter.PROP_EXITDATA, errorDialog);
      throw ex;
    }
    return sManager;
  }

  public boolean isReadOnly() {
    return storageManager.isReadOnly();
  }

  public void compact() throws IOException {
    if (!isReadOnly())
      compact(getBundleStoreRoot());
  }

  private void compact(File directory) {
    if (Debug.DEBUG && Debug.DEBUG_GENERAL)
      Debug.println("compact(" + directory.getPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
    String list[] = directory.list();
    if (list == null)
      return;

    int len = list.length;
    for (int i = 0; i < len; i++) {
      if (BaseStorage.DATA_DIR_NAME.equals(list[i]))
        continue; /* do not examine the bundles data dir. */
      File target = new File(directory, list[i]);
      // if the file is a directory
      if (!target.isDirectory())
        continue;
      File delete = new File(target, BaseStorage.DELETE_FLAG);
      // and the directory is marked for delete
      if (delete.exists()) {
        // if rm fails to delete the directory and .delete was removed
        if (!AdaptorUtil.rm(target) && !delete.exists()) {
          try {
            // recreate .delete
            FileOutputStream out = new FileOutputStream(delete);
            out.close();
          } catch (IOException e) {
            if (Debug.DEBUG && Debug.DEBUG_GENERAL)
              Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
          }
        }
      } else {
        compact(target); /* descend into directory */
      }
    }
  }

  public long getFreeSpace() throws IOException {
    // cannot implement this without native code!
    return -1;
  }

  public File getDataFile(BaseData data, String path) {
    BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY);
    if (storageHook == null)
      return null;
    return storageHook.getDataFile(path);
  }

  BaseAdaptor getAdaptor() {
    return adaptor;
  }

  public void installNativeCode(BaseData data, String[] nativepaths) throws BundleException {
    if (nativepaths.length > 0) {
      BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY);
      if (storageHook != null)
        storageHook.installNativePaths(nativepaths);
    }
  }

  public Dictionary loadManifest(BaseData data) throws BundleException {
    return loadManifest(data, false);
  }

  public Dictionary loadManifest(BaseData bundleData, boolean firstTime) throws BundleException {
    Dictionary result = null;
    StorageHook[] dataStorageHooks = bundleData.getStorageHooks();
    for (int i = 0; i < dataStorageHooks.length && result == null; i++)
      result = dataStorageHooks[i].getManifest(firstTime);
    if (result == null)
      result = AdaptorUtil.loadManifestFrom(bundleData);
    if (result == null)
      throw new BundleException(NLS.bind(AdaptorMsg.MANIFEST_NOT_FOUND_EXCEPTION, Constants.OSGI_BUNDLE_MANIFEST, bundleData.getLocation()), BundleException.MANIFEST_ERROR);
    return result;
  }

  public File getExtractFile(BaseData data, String path) {
    BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY);
    if (storageHook == null)
      return null;
    // first check the child generation dir
    File childGenDir = storageHook.getGenerationDir();
    if (childGenDir != null) {
      File childPath = new File(childGenDir, path);
      if (childPath.exists())
        return childPath;
    }
    // now check the parent
    File parentGenDir = storageHook.getParentGenerationDir();
    if (parentGenDir != null) {
      // there is a parent generation check if the file exists
      File parentPath = new File(parentGenDir, path);
      if (parentPath.exists())
        // only use the parent generation file if it exists; do not extract there
        return parentPath;
    }
    // did not exist in both locations; create a file for extraction.
    File bundleGenerationDir = storageHook.createGenerationDir();
    /* if the generation dir exists, then we have place to cache */
    if (bundleGenerationDir != null && bundleGenerationDir.exists())
      return new File(bundleGenerationDir, path);
    return null;
  }

  public BaseData[] getInstalledBundles() {
    try {
      return readBundleDatas();
    } catch (Throwable t) {
      // be safe here and throw out the results and start over
      // otherwise this would result in a failed launch
      FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, "Error loading bundle datas.  Recalculating cache.", 0, t, null); //$NON-NLS-1$
      adaptor.getFrameworkLog().log(logEntry);
      return null;
    }
  }

  private BaseData[] readBundleDatas() {
    InputStream bundleDataStream = findStorageStream(LocationManager.BUNDLE_DATA_FILE);
    if (bundleDataStream == null)
      return null;
    try {
      DataInputStream in = new DataInputStream(new BufferedInputStream(bundleDataStream));
      try {
        byte version = in.readByte();
        if (version != BUNDLEDATA_VERSION)
          return null;
        timeStamp = in.readLong();
        initialBundleStartLevel = in.readInt();
        nextId = in.readLong();

        int numStorageHooks = in.readInt();
        StorageHook[] storageHooks = adaptor.getHookRegistry().getStorageHooks();
        if (numStorageHooks != storageHooks.length)
          return null; // must have the same number of storagehooks to properly read the data
        for (int i = 0; i < numStorageHooks; i++) {
          Object storageKey = storageHooks[i].getKey();
          int storageVersion = storageHooks[i].getStorageVersion();
          if (!storageKey.equals(in.readUTF()) || storageVersion != in.readInt())
            return null; // some storage hooks have changed must throw the data away.
        }

        int bundleCount = in.readInt();
        ArrayList result = new ArrayList(bundleCount);
        long id = -1;
        boolean bundleDiscarded = false;
        for (int i = 0; i < bundleCount; i++) {
          boolean error = false;
          BaseData data = null;
          try {
            id = in.readLong();
            if (id != 0) {
              data = loadBaseData(id, in);
              data.getBundleFile();
              StorageHook[] dataStorageHooks = data.getStorageHooks();
              for (int j = 0; j < dataStorageHooks.length; j++)
                dataStorageHooks[j].validate();
              if (Debug.DEBUG && Debug.DEBUG_GENERAL)
                Debug.println("BundleData created: " + data); //$NON-NLS-1$
              processExtension(data, EXTENSION_INITIALIZE);
              result.add(data);
            }
          } catch (IllegalArgumentException e) {
            // may be from data.getBundleFile()
            bundleDiscarded = true;
            error = true;
          } catch (BundleException e) {
            // should never happen
            bundleDiscarded = true;
            error = true;
          } catch (IOException e) {
            bundleDiscarded = true;
            error = true;
            if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
              Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$
              Debug.printStackTrace(e);
            }
          }
          if (error && data != null) {
            BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY);
            storageHook.delete(true, BaseStorageHook.DEL_BUNDLE_STORE);
          }
        }
        if (bundleDiscarded)
          FrameworkProperties.setProperty(EclipseStarter.PROP_REFRESH_BUNDLES, "true"); //$NON-NLS-1$
        return (BaseData[]) result.toArray(new BaseData[result.size()]);
      } finally {
        in.close();
      }
    } catch (IOException e) {
      if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
        Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$
        Debug.printStackTrace(e);
      }
    }
    return null;
  }

  private void saveAllData(boolean shutdown) {
    if (Debug.DEBUG && Debug.DEBUG_GENERAL)
      Debug.println("Saving framework data ..."); //$NON-NLS-1$
    if (storageManagerClosed)
      try {
        storageManager.open(!LocationManager.getConfigurationLocation().isReadOnly());
        storageManagerClosed = false;
      } catch (IOException e) {
        String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FILEMANAGER_OPEN_ERROR, e.getMessage());
        FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null);
        adaptor.getFrameworkLog().log(logEntry);
      }
    saveBundleDatas();
    saveStateData(shutdown);
    savePermissionStorage();
    if (shutdown)
      stateManager.stopDataManager();
  }

  private BasePermissionStorage readPermissionData() {
    BasePermissionStorage result = new BasePermissionStorage(this);
    InputStream permDataStream = findStorageStream(PERM_DATA_FILE);
    if (permDataStream == null)
      return result;
    try {
      DataInputStream in = new DataInputStream(new BufferedInputStream(permDataStream));
      try {
        if (PERMDATA_VERSION != in.readByte())
          return result;
        // read the default permissions first
        int numPerms = in.readInt();
        if (numPerms > 0) {
          String[] perms = new String[numPerms];
          for (int i = 0; i < numPerms; i++)
            perms[i] = in.readUTF();
          result.setPermissionData(null, perms);
        }
        int numLocs = in.readInt();
        if (numLocs > 0)
          for (int i = 0; i < numLocs; i++) {
            String loc = in.readUTF();
            numPerms = in.readInt();
            String[] perms = new String[numPerms];
            for (int j = 0; j < numPerms; j++)
              perms[j] = in.readUTF();
            result.setPermissionData(loc, perms);
          }
        int numCondPerms = in.readInt();
        if (numCondPerms > 0) {
          String[] condPerms = new String[numCondPerms];
          for (int i = 0; i < numCondPerms; i++)
            condPerms[i] = in.readUTF();
          result.saveConditionalPermissionInfos(condPerms);
        }
        result.setDirty(false);
      } finally {
        in.close();
      }
    } catch (IOException e) {
      adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e));
    }
    return result;
  }

  private void savePermissionStorage() {
    if (permissionStorage == null || isReadOnly() || !permissionStorage.isDirty())
      return;
    if (Debug.DEBUG && Debug.DEBUG_GENERAL)
      Debug.println("About to save permission data ..."); //$NON-NLS-1$
    try {
      ManagedOutputStream fmos = storageManager.getOutputStream(PERM_DATA_FILE);
      DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fmos));
      boolean error = true;
      try {
        out.writeByte(PERMDATA_VERSION);
        // always write the default permissions first
        String[] defaultPerms = permissionStorage.getPermissionData(null);
        out.writeInt(defaultPerms == null ? 0 : defaultPerms.length);
        if (defaultPerms != null)
          for (int i = 0; i < defaultPerms.length; i++)
            out.writeUTF(defaultPerms[i]);
        String[] locations = permissionStorage.getLocations();
        out.writeInt(locations == null ? 0 : locations.length);
        if (locations != null)
          for (int i = 0; i < locations.length; i++) {
            out.writeUTF(locations[i]);
            String[] perms = permissionStorage.getPermissionData(locations[i]);
            out.writeInt(perms == null ? 0 : perms.length);
            if (perms != null)
              for (int j = 0; j < perms.length; j++)
                out.writeUTF(perms[j]);
          }
        String[] condPerms = permissionStorage.getConditionalPermissionInfos();
        out.writeInt(condPerms == null ? 0 : condPerms.length);
        if (condPerms != null)
          for (int i = 0; i < condPerms.length; i++)
            out.writeUTF(condPerms[i]);
        out.close();
        permissionStorage.setDirty(false);
        error = false;
      } finally {
        // if something happens, don't close a corrupt file
        if (error) {
          fmos.abort();
          try {
            out.close();
          } catch (IOException e) {/*ignore*/
          }
        }
      }
    } catch (IOException e) {
      adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e));
      return;
    }
  }

  private void saveBundleDatas() {
    // the cache and the state match
    if (stateManager == null || isReadOnly() || (timeStamp == stateManager.getSystemState().getTimeStamp() && !stateManager.saveNeeded()))
      return;
    if (Debug.DEBUG && Debug.DEBUG_GENERAL)
      Debug.println("Saving bundle data ..."); //$NON-NLS-1$
    try {
      ManagedOutputStream fmos = storageManager.getOutputStream(LocationManager.BUNDLE_DATA_FILE);
      DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fmos));
      boolean error = true;
      try {
        out.writeByte(BUNDLEDATA_VERSION);
        out.writeLong(stateManager.getSystemState().getTimeStamp());
        out.writeInt(initialBundleStartLevel);
        out.writeLong(nextId);

        StorageHook[] storageHooks = adaptor.getHookRegistry().getStorageHooks();
        out.writeInt(storageHooks.length);
        for (int i = 0; i < storageHooks.length; i++) {
          out.writeUTF((String) storageHooks[i].getKey());
          out.writeInt(storageHooks[i].getStorageVersion());
        }

        Bundle[] bundles = context.getBundles();
        out.writeInt(bundles.length);
        for (int i = 0; i < bundles.length; i++) {
          long id = bundles[i].getBundleId();
          out.writeLong(id);
          if (id != 0) {
            BundleData data = ((org.eclipse.osgi.framework.internal.core.AbstractBundle) bundles[i]).getBundleData();
            saveBaseData((BaseData) data, out);
          }
        }
        out.close();
        // update the 'timeStamp' after the changed Meta data is saved.
        timeStamp = stateManager.getSystemState().getTimeStamp();
        error = false;
      } finally {
        // if something happens, don't close a corrupt file
        if (error) {
          fmos.abort();
          try {
            out.close();
          } catch (IOException e) {/*ignore*/
          }
        }
      }
    } catch (IOException e) {
      adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e));
      return;
    }
  }

  private void saveStateData(boolean shutdown) {
    if (shutdown && "true".equals(FrameworkProperties.getProperty("osgi.forcedRestart"))) //$NON-NLS-1$ //$NON-NLS-2$
      // increment the state timestamp if a forced restart happened.
      stateManager.getSystemState().setTimeStamp(stateManager.getSystemState().getTimeStamp() + 1);
    if (stateManager == null || isReadOnly() || !stateManager.saveNeeded())
      return;
    if (Debug.DEBUG && Debug.DEBUG_GENERAL)
      Debug.println("Saving resolver state data ..."); //$NON-NLS-1$
    File stateTmpFile = null;
    File lazyTmpFile = null;
    try {
      stateTmpFile = File.createTempFile(LocationManager.STATE_FILE, ".new", LocationManager.getOSGiConfigurationDir()); //$NON-NLS-1$
      lazyTmpFile = File.createTempFile(LocationManager.LAZY_FILE, ".new", LocationManager.getOSGiConfigurationDir()); //$NON-NLS-1$
      if (shutdown)
        stateManager.shutdown(stateTmpFile, lazyTmpFile);
      else
        synchronized (stateManager) {
          stateManager.update(stateTmpFile, lazyTmpFile);
        }
      storageManager.lookup(LocationManager.STATE_FILE, true);
      storageManager.lookup(LocationManager.LAZY_FILE, true);
      storageManager.update(new String[] {LocationManager.STATE_FILE, LocationManager.LAZY_FILE}, new String[] {stateTmpFile.getName(), lazyTmpFile.getName()});
    } catch (IOException e) {
      adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e));
    } finally {
      if (stateTmpFile != null && stateTmpFile.exists())
        stateTmpFile.delete();
      if (lazyTmpFile != null && lazyTmpFile.exists())
        lazyTmpFile.delete();
    }
  }

  public PermissionStorage getPermissionStorage() throws IOException {
    if (permissionStorage == null)
      permissionStorage = readPermissionData();
    return permissionStorage;
  }

  public int getInitialBundleStartLevel() {
    return initialBundleStartLevel;
  }

  public void setInitialBundleStartLevel(int value) {
    this.initialBundleStartLevel = value;
    requestSave();
  }

  public void save(BaseData data) throws IOException {
    if (data.isDirty()) {
      timeStamp--; // Change the value of the timeStamp, as a marker that something changed.
      requestSave();
      data.setDirty(false);
    }
  }

  public BundleOperation installBundle(String location, URLConnection source) {
    BaseData data = createBaseData(getNextBundleId(), location);
    return new BundleInstall(data, source, this);
  }

  public BundleOperation updateBundle(BaseData data, URLConnection source) {
    return new BundleUpdate(data, source, this);
  }

  public BundleOperation uninstallBundle(BaseData data) {
    return new BundleUninstall(data, this);
  }

  protected Object getBundleContent(BaseData bundledata) throws IOException {
    BaseStorageHook storageHook = (BaseStorageHook) bundledata.getStorageHook(BaseStorageHook.KEY);
    if (storageHook == null)
      throw new IllegalStateException();
    return storageHook.isReference() ? new File(storageHook.getFileName()) : new File(storageHook.getGenerationDir(), storageHook.getFileName());
  }

  public BundleFile createBundleFile(Object content, BaseData data) throws IOException {
    boolean base = false;
    if (content == null) {
      // this must be a request for the base bundlefile
      base = true;
      // get the content of this bundle
      content = getBundleContent(data);
    }
    BundleFile result = null;
    // Ask factories before doing the default behavior
    BundleFileFactoryHook[] factories = adaptor.getHookRegistry().getBundleFileFactoryHooks();
    for (int i = 0; i < factories.length && result == null; i++)
      result = factories[i].createBundleFile(content, data, base);

    // No factories configured or they declined to create the bundle file; do default
    if (result == null && content instanceof File) {
      File file = (File) content;
      if (file.isDirectory())
        result = new DirBundleFile(file);
      else
        result = new ZipBundleFile(file, data, this.mruList);
    }

    if (result == null && content instanceof String) {
      // here we assume the content is a path offset into the base bundle file;  create a NestedDirBundleFile
      result = new NestedDirBundleFile(data.getBundleFile(), (String) content);
    }
    if (result == null)
      // nothing we can do; must throw exception for the content
      throw new IOException("Cannot create bundle file for content of type: " + content.getClass().getName()); //$NON-NLS-1$

    // try creating a wrapper bundlefile out of it.
    BundleFileWrapperFactoryHook[] wrapperFactories = adaptor.getHookRegistry().getBundleFileWrapperFactoryHooks();
    BundleFileWrapperChain wrapped = wrapperFactories.length == 0 ? null : new BundleFileWrapperChain(result, null);
    for (int i = 0; i < wrapperFactories.length; i++) {
      BundleFile wrapperBundle = wrapperFactories[i].wrapBundleFile(result, content, data, base);
      if (wrapperBundle != null && wrapperBundle != result)
        result = wrapped = new BundleFileWrapperChain(wrapperBundle, wrapped);
    }
    return result;
  }

  public synchronized StateManager getStateManager() {
    if (stateManager != null)
      return stateManager;
    stateManager = readStateData();
    checkSystemState(stateManager.getSystemState());
    return stateManager;
  }

  private void checkSystemState(State state) {
    BundleDescription[] bundles = state.getBundles();
    if (bundles == null)
      return;
    boolean removedBundle = false;
    for (int i = 0; i < bundles.length; i++) {
      if (context.getBundle(bundles[i].getBundleId()) == null) {
        state.removeBundle(bundles[i]);
        removedBundle = true;
      }
    }
    if (removedBundle)
      state.resolve(false); // do a full resolve
    BundleDescription systemBundle = state.getBundle(0);
    if (systemBundle == null || !systemBundle.isResolved()) {
      ResolverError[] errors = systemBundle == null ? new ResolverError[0] : state.getResolverErrors(systemBundle);
      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < errors.length; i++) {
        sb.append(errors[i].toString());
        if (i < errors.length - 1)
          sb.append(", "); //$NON-NLS-1$
      }
      // this would be a bug in the framework
      throw new IllegalStateException(NLS.bind(AdaptorMsg.SYSTEMBUNDLE_NOTRESOLVED, sb.toString()));
    }
  }

  private StateManager readStateData() {
    File[] stateFiles = findStorageFiles(new String[] {LocationManager.STATE_FILE, LocationManager.LAZY_FILE});
    File stateFile = stateFiles[0];
    File lazyFile = stateFiles[1];

    stateManager = new StateManager(stateFile, lazyFile, context, timeStamp);
    State systemState = null;
    if (!invalidState) {
      systemState = stateManager.readSystemState();
      if (systemState != null)
        return stateManager;
    }
    systemState = stateManager.createSystemState();
    Bundle[] installedBundles = context.getBundles();
    if (installedBundles == null)
      return stateManager;
    StateObjectFactory factory = stateManager.getFactory();
    for (int i = 0; i < installedBundles.length; i++) {
      AbstractBundle toAdd = (AbstractBundle) installedBundles[i];
      try {
        // make sure we get the real manifest as if this is the first time.
        Dictionary toAddManifest = loadManifest((BaseData) toAdd.getBundleData(), true);
        BundleDescription newDescription = factory.createBundleDescription(systemState, toAddManifest, toAdd.getLocation(), toAdd.getBundleId());
        systemState.addBundle(newDescription);
      } catch (BundleException be) {
        // just ignore bundle datas with invalid manifests
      }
    }
    // we do not set the cached timestamp here because we want a new one to be used from the new system state object (bug 132978)
    // we need the state resolved
    systemState.resolve();
    invalidState = false;
    return stateManager;
  }

  private File[] findStorageFiles(String[] fileNames) {
    File[] storageFiles = new File[fileNames.length];
    try {
      for (int i = 0; i < storageFiles.length; i++)
        storageFiles[i] = storageManager.lookup(fileNames[i], false);
    } catch (IOException ex) {
      if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
        Debug.println("Error reading state file " + ex.getMessage()); //$NON-NLS-1$
        Debug.printStackTrace(ex);
      }
    }
    boolean success = true;
    for (int i = 0; i < storageFiles.length; i++)
      if (storageFiles[i] == null || !storageFiles[i].isFile()) {
        success = false;
        break;
      }
    if (success)
      return storageFiles;
    //if it does not exist, try to read it from the parent
    Location parentConfiguration = null;
    Location currentConfiguration = LocationManager.getConfigurationLocation();
    if (currentConfiguration != null && (parentConfiguration = currentConfiguration.getParentLocation()) != null) {
      try {
        File stateLocationDir = new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME);
        StorageManager newFileManager = initFileManager(stateLocationDir, "none", true); //$NON-NLS-1$);
        for (int i = 0; i < storageFiles.length; i++)
          storageFiles[i] = newFileManager.lookup(fileNames[i], false);
        newFileManager.close();
      } catch (IOException ex) {
        if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
          Debug.println("Error reading state file " + ex.getMessage()); //$NON-NLS-1$
          Debug.printStackTrace(ex);
        }
      }
    } else {
      try {
        //it did not exist in either place, so create it in the original location
        if (!isReadOnly()) {
          for (int i = 0; i < storageFiles.length; i++)
            storageFiles[i] = storageManager.lookup(fileNames[i], true);
        }
      } catch (IOException ex) {
        if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
          Debug.println("Error reading state file " + ex.getMessage()); //$NON-NLS-1$
          Debug.printStackTrace(ex);
        }
      }
    }
    return storageFiles;
  }

  public void frameworkStart(BundleContext fwContext) throws BundleException {
    this.context = fwContext;
    // System property can be set to enable state saver or not.
    if (Boolean.valueOf(FrameworkProperties.getProperty(BaseStorage.PROP_ENABLE_STATE_SAVER, "true")).booleanValue()) //$NON-NLS-1$
      stateSaver = new StateSaver();

  }

  public void frameworkStop(BundleContext fwContext) throws BundleException {
    if (stateSaver != null)
      stateSaver.shutdown();
    saveAllData(true);
    storageManager.close();
    storageManagerClosed = true;
    if (extensionListener != null)
      context.removeBundleListener(extensionListener);
    mruList.shutdown();
  }

  public void frameworkStopping(BundleContext fwContext) {
    // do nothing in storage
  }

  public void addProperties(Properties properties) {
    // set the extension support if we found the addURL method
    if (addFwkURLMethod != null)
      properties.put(Constants.SUPPORTS_FRAMEWORK_EXTENSION, "true"); //$NON-NLS-1$
    // store bundleStore back into adaptor properties for others to see
    properties.put(BaseStorage.PROP_BUNDLE_STORE, getBundleStoreRoot().getAbsolutePath());
  }

  private InputStream findStorageStream(String fileName) {
    InputStream storageStream = null;
    try {
      storageStream = storageManager.getInputStream(fileName);
    } catch (IOException ex) {
      if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
        Debug.println("Error reading framework metadata: " + ex.getMessage()); //$NON-NLS-1$
        Debug.printStackTrace(ex);
      }
    }
    if (storageStream == null) {
      Location currentConfiguration = LocationManager.getConfigurationLocation();
      Location parentConfiguration = null;
      if (currentConfiguration != null && (parentConfiguration = currentConfiguration.getParentLocation()) != null) {
        try {
          File bundledataLocationDir = new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME);
          StorageManager newStorageManager = initFileManager(bundledataLocationDir, "none", true); //$NON-NLS-1$
          storageStream = newStorageManager.getInputStream(fileName);
          newStorageManager.close();
        } catch (MalformedURLException e1) {
          // This will not happen since all the URLs are derived by us
          // and we are GODS!
        } catch (IOException e1) {
          // That's ok we will regenerate the .bundleData
        }
      }
    }
    return storageStream;
  }

  protected void saveBaseData(BaseData bundledata, DataOutputStream out) throws IOException {
    StorageHook[] hooks = bundledata.getStorageHooks();
    out.writeInt(hooks.length);
    for (int i = 0; i < hooks.length; i++) {
      out.writeUTF((String) hooks[i].getKey());
      hooks[i].save(out);
    }
  }

  protected BaseData loadBaseData(long id, DataInputStream in) throws IOException {
    BaseData result = new BaseData(id, adaptor);
    int numHooks = in.readInt();
    StorageHook[] hooks = new StorageHook[numHooks];
    for (int i = 0; i < numHooks; i++) {
      String hookKey = in.readUTF();
      StorageHook storageHook = (StorageHook) storageHooks.getByKey(hookKey);
      if (storageHook == null)
        throw new IOException();
      hooks[i] = storageHook.load(result, in);
    }
    result.setStorageHooks(hooks);
    return result;
  }

  protected BaseData createBaseData(long id, String location) {
    BaseData result = new BaseData(id, adaptor);
    result.setLocation(location);
    return result;
  }

  public String getInstallPath() {
    return installPath;
  }

  private void cleanOSGiCache() {
    File osgiConfig = LocationManager.getOSGiConfigurationDir();
    if (!AdaptorUtil.rm(osgiConfig))
      adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, "The -clean (osgi.clean) option was not successful. Unable to clean the storage area: " + osgiConfig.getAbsolutePath(), 0, null, null)); //$NON-NLS-1$

  }

  /**
   * Processes an extension bundle
   * @param bundleData the extension bundle data
   * @param type the type of extension bundle
   * @throws BundleException on any errors or if the extension bundle type is not supported
   */
  protected void processExtension(BaseData bundleData, byte type) throws BundleException {
    if ((bundleData.getType() & BundleData.TYPE_FRAMEWORK_EXTENSION) != 0) {
      validateExtension(bundleData);
      processFrameworkExtension(bundleData, type);
    } else if ((bundleData.getType() & BundleData.TYPE_BOOTCLASSPATH_EXTENSION) != 0) {
      validateExtension(bundleData);
      processBootExtension(bundleData, type);
    } else if ((bundleData.getType() & BundleData.TYPE_EXTCLASSPATH_EXTENSION) != 0) {
      validateExtension(bundleData);
      processExtExtension(bundleData, type);
    }
  }

  /**
   * Validates the extension bundle metadata
   * @param bundleData the extension bundle data
   * @throws BundleException if the extension bundle metadata is invalid
   */
  private void validateExtension(BundleData bundleData) throws BundleException {
    Dictionary extensionManifest = bundleData.getManifest();
    if (extensionManifest.get(Constants.IMPORT_PACKAGE) != null)
      throw new BundleException(NLS.bind(AdaptorMsg.ADAPTOR_EXTENSION_IMPORT_ERROR, bundleData.getLocation()), BundleException.MANIFEST_ERROR);
    if (extensionManifest.get(Constants.REQUIRE_BUNDLE) != null)
      throw new BundleException(NLS.bind(AdaptorMsg.ADAPTOR_EXTENSION_REQUIRE_ERROR, bundleData.getLocation()), BundleException.MANIFEST_ERROR);
    if (extensionManifest.get(Constants.BUNDLE_NATIVECODE) != null)
      throw new BundleException(NLS.bind(AdaptorMsg.ADAPTOR_EXTENSION_NATIVECODE_ERROR, bundleData.getLocation()), BundleException.MANIFEST_ERROR);
  }

  /**
   * Processes a framework extension bundle
   * @param bundleData the extension bundle data
   * @param type the type of extension bundle
   * @throws BundleException on errors or if framework extensions are not supported
   */
  protected void processFrameworkExtension(BaseData bundleData, byte type) throws BundleException {
    if (addFwkURLMethod == null)
      throw new BundleException("Framework extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$
    addExtensionContent(bundleData, type, getFwkClassLoader(), addFwkURLMethod);
  }

  protected void processExtExtension(BaseData bundleData, byte type) throws BundleException {
    if (addExtURLMethod == null)
      throw new BundleException("Extension classpath extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$
    addExtensionContent(bundleData, type, getExtClassLoader(), addExtURLMethod);
  }

  private void addExtensionContent(BaseData bundleData, byte type, ClassLoader addToLoader, Method addToMethod) throws BundleException {
    if ((type & (EXTENSION_UNINSTALLED | EXTENSION_UPDATED)) != 0)
      // if uninstalled or updated then do nothing framework must be restarted.
      return;

    // first make sure this BundleData is not on the pre-configured osgi.framework.extensions list
    String[] extensions = getConfiguredExtensions();
    for (int i = 0; i < extensions.length; i++)
      if (extensions[i].equals(bundleData.getSymbolicName()))
        return;
    if ((type & EXTENSION_INSTALLED) != 0) {
      if (extensionListener == null) {
        // add bundle listener to wait for extension to be resolved
        extensionListener = this;
        context.addBundleListener(extensionListener);
      }
      return;
    }
    File[] files = getExtensionFiles(bundleData);
    if (files == null)
      return;

    for (int i = 0; i < files.length; i++) {
      if (files[i] == null)
        continue;
      try {
        callAddURLMethod(addToLoader, addToMethod, AdaptorUtil.encodeFileURL(files[i]));
      } catch (InvocationTargetException e) {
        adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundleData.getBundle(), e);
      } catch (MalformedURLException e) {
        adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundleData.getBundle(), e);
      }
    }

    try {
      addToLoader.loadClass("thisIsNotAClass"); // initialize the new urls  //$NON-NLS-1$
    } catch (ClassNotFoundException e) {
      // do nothing
    }
  }

  /**
   * Returns a list of configured extensions
   * @return a list of configured extensions
   */
  protected String[] getConfiguredExtensions() {
    if (configuredExtensions != null)
      return configuredExtensions;
    String prop = FrameworkProperties.getProperty(BaseStorage.PROP_FRAMEWORK_EXTENSIONS);
    if (prop == null || prop.trim().length() == 0)
      configuredExtensions = new String[0];
    else
      configuredExtensions = ManifestElement.getArrayFromList(prop);
    return configuredExtensions;
  }

  /**
   * Processes a boot extension bundle
   * @param bundleData the extension bundle data
   * @param type the type of extension bundle
   * @throws BundleException on errors or if boot extensions are not supported
   */
  protected void processBootExtension(BundleData bundleData, byte type) throws BundleException {
    throw new BundleException("Boot classpath extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$
  }

  private void initBundleStoreRoot() {
    File configurationLocation = LocationManager.getOSGiConfigurationDir();
    if (configurationLocation != null)
      bundleStoreRoot = new File(configurationLocation, LocationManager.BUNDLES_DIR);
    else
      // last resort just default to "bundles"
      bundleStoreRoot = new File(LocationManager.BUNDLES_DIR);
  }

  public File getBundleStoreRoot() {
    if (bundleStoreRoot == null)
      initBundleStoreRoot();
    return bundleStoreRoot;
  }

  /**
   * Returns a list of classpath files for an extension bundle
   * @param bundleData the bundle data for an extension bundle
   * @return a list of classpath files for an extension bundle
   */
  protected File[] getExtensionFiles(BaseData bundleData) {
    File[] files = null;
    try {
      String[] paths = bundleData.getClassPath();
      if (DevClassPathHelper.inDevelopmentMode()) {
        String[] devPaths = DevClassPathHelper.getDevClassPath(bundleData.getSymbolicName());
        String[] origPaths = paths;

        paths = new String[origPaths.length + devPaths.length];
        System.arraycopy(origPaths, 0, paths, 0, origPaths.length);
        System.arraycopy(devPaths, 0, paths, origPaths.length, devPaths.length);
      }
      ArrayList results = new ArrayList(paths.length);
      for (int i = 0; i < paths.length; i++) {
        if (".".equals(paths[i])) //$NON-NLS-1$
          results.add(bundleData.getBundleFile().getBaseFile());
        else {
          File result = bundleData.getBundleFile().getFile(paths[i], false);
          if (result != null)
            results.add(result);
        }
      }
      return (File[]) results.toArray(new File[results.size()]);
    } catch (BundleException e) {
      adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundleData.getBundle(), e);
    }
    return files;
  }

  void requestSave() {
    // Only when the State saver is enabled will the stateSaver be started.
    if (stateSaver == null)
      return;
    stateSaver.requestSave();
  }

  /**
   * Updates the state mananager with an updated/installed/uninstalled bundle
   * @param bundleData the modified bundle
   * @param type the type of modification
   * @throws BundleException
   */
  public void updateState(BundleData bundleData, int type) throws BundleException {
    if (stateManager == null) {
      invalidState = true;
      return;
    }
    State systemState = stateManager.getSystemState();
    BundleDescription oldDescription = null;
    BundleDescription newDescription = null;
    switch (type) {
      case BundleEvent.UPDATED :
        // fall through to INSTALLED
      case BundleEvent.INSTALLED :
        if (type == BundleEvent.UPDATED)
          oldDescription = systemState.getBundle(bundleData.getBundleID());
        newDescription = stateManager.getFactory().createBundleDescription(systemState, bundleData.getManifest(), bundleData.getLocation(), bundleData.getBundleID());
        if (oldDescription == null)
          systemState.addBundle(newDescription);
        else
          systemState.updateBundle(newDescription);
        break;
      case BundleEvent.UNINSTALLED :
        systemState.removeBundle(bundleData.getBundleID());
        break;
    }

    if (newDescription != null)
      validateNativeCodePaths(newDescription, (BaseData) bundleData);
  }

  private void validateNativeCodePaths(BundleDescription newDescription, BaseData data) {
    NativeCodeSpecification nativeCode = newDescription.getNativeCodeSpecification();
    if (nativeCode == null)
      return;
    NativeCodeDescription nativeCodeDescs[] = nativeCode.getPossibleSuppliers();
    for (int i = 0; i < nativeCodeDescs.length; i++) {
      BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY);
      if (storageHook != null)
        try {
          storageHook.validateNativePaths(nativeCodeDescs[i].getNativePaths());
        } catch (BundleException e) {
          stateManager.getSystemState().setNativePathsInvalid(nativeCodeDescs[i], true);
        }
    }
  }

  private class StateSaver implements Runnable {
    private final long delay_interval;
    private final long max_total_delay_interval;
    private boolean shutdown = false;
    private long lastSaveTime = 0;
    private Thread runningThread = null;
    private Thread shutdownHook = null;

    StateSaver() {
      String prop = FrameworkProperties.getProperty("eclipse.stateSaveDelayInterval"); //$NON-NLS-1$
      long delayValue = 30000; // 30 seconds.
      long maxDelayValue = 1800000; // 30 minutes.
      if (prop != null) {
        try {
          long val = Long.parseLong(prop);
          if (val >= 1000 && val <= 1800000) {
            delayValue = val;
            maxDelayValue = val * 60;
          } else if (val == 0) {
            delayValue = 0;
            maxDelayValue = 0;
          }
        } catch (NumberFormatException e) {
          // ignore
        }
      }
      delay_interval = delayValue;
      max_total_delay_interval = maxDelayValue;
    }

    public void run() {
      State systemState = adaptor.getState();
      synchronized (systemState) {
        long firstSaveTime = lastSaveTime;
        long curSaveTime = 0;
        long delayTime;
        do {
          do {
            if ((System.currentTimeMillis() - firstSaveTime) > max_total_delay_interval) {
              curSaveTime = lastSaveTime;
              // Waiting time has been too long, so break to start saving State data to file.
              break;
            }
            delayTime = Math.min(delay_interval, lastSaveTime - curSaveTime);
            curSaveTime = lastSaveTime;
            // wait for other save requests
            try {
              if (!shutdown)
                systemState.wait(delayTime);
            } catch (InterruptedException ie) {
              // force break from do/while loops
              curSaveTime = lastSaveTime;
              break;
            }
            // Continue the loop if 'lastSaveTime' is increased again during waiting.
          } while (!shutdown && curSaveTime < lastSaveTime);
          // Save State and Meta data.
          saveAllData(false);
          // Continue the loop if Saver is asked again during saving State data to file.
        } while (!shutdown && curSaveTime < lastSaveTime);
        runningThread = null; // clear runningThread
        try {
          Runtime.getRuntime().removeShutdownHook(shutdownHook);
        } catch (IllegalStateException e) {
          // avoid exception if shutdown is in progress
        }
        shutdownHook = null;
      }
    }

    void shutdown() {
      State systemState = adaptor.getState();
      Thread joinWith = null;
      synchronized (systemState) {
        shutdown = true;
        joinWith = runningThread;
        systemState.notifyAll(); // To wakeup sleeping thread.
      }
      try {
        if (joinWith != null) {
          if (Debug.DEBUG && Debug.DEBUG_GENERAL)
            Debug.println("About to join saving thread"); //$NON-NLS-1$
          // There should be no deadlock when 'shutdown' is true.
          joinWith.join();
          if (Debug.DEBUG && Debug.DEBUG_GENERAL)
            Debug.println("Joined with saving thread"); //$NON-NLS-1$
        }
      } catch (InterruptedException ie) {
        if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
          Debug.println("Error shutdowning StateSaver: " + ie.getMessage()); //$NON-NLS-1$
          Debug.printStackTrace(ie);
        }
      }
    }

    void requestSave() {
      final State systemState = adaptor.getState();
      synchronized (systemState) {
        if (shutdown)
          return; // do not start another thread if we have already shutdown
        if (delay_interval == 0) // all saves are atomic
          saveAllData(false);
        lastSaveTime = System.currentTimeMillis();
        if (runningThread == null) {
          shutdownHook = new Thread(new Runnable() {
            public void run() {
              // Synchronize with JVM shutdown hook, because
              // saveAllData creates a temp file with delete on
              // exit is true. The temp file will be removed in the
              // shutdown hook. This prevents that the remove temp files
              // in the shutdown hook is earlier handled then adding new
              // temp file in saveAllData.
              shutdown();
            }
          });
          runningThread = new Thread(this, "State Saver"); //$NON-NLS-1$
          runningThread.start();
          Runtime.getRuntime().addShutdownHook(shutdownHook);
        }
      }
    }
  }

  public long getNextBundleId() {
    synchronized (this.nextIdMonitor) {
      return nextId++;
    }
  }

  public void bundleChanged(BundleEvent event) {
    if (event.getType() != BundleEvent.RESOLVED)
      return;
    BaseData data = (BaseData) ((AbstractBundle) event.getBundle()).getBundleData();
    try {
      if ((data.getType() & BundleData.TYPE_FRAMEWORK_EXTENSION) != 0)
        processFrameworkExtension(data, EXTENSION_INITIALIZE);
      else if ((data.getType() & BundleData.TYPE_BOOTCLASSPATH_EXTENSION) != 0)
        processBootExtension(data, EXTENSION_INITIALIZE);
      else if ((data.getType() & BundleData.TYPE_EXTCLASSPATH_EXTENSION) != 0)
        processExtExtension(data, EXTENSION_INITIALIZE);
    } catch (BundleException e) {
      // do nothing;
    }
  }

  public String copyToTempLibrary(BaseData data, String absolutePath) throws IOException {
    File storageRoot = getBundleStoreRoot();
    File libTempDir = new File(storageRoot, LIB_TEMP);
    // we assume the absolutePath is a File path
    File realLib = new File(absolutePath);
    String libName = realLib.getName();
    // find a temp dir for the bundle data and the library;
    File bundleTempDir = null;
    File libTempFile = null;
    // We need a somewhat predictable temp dir for the libraries of a given bundle;
    // This is not strictly necessary but it does help scenarios where one native library loads another native library without using java.
    // On some OSes this causes issues because the second library is cannot be found.
    // This has been worked around by the bundles loading the libraries in a particular order (and setting some LIB_PATH env).
    // The one catch is that the libraries need to be in the same directory and they must use their original lib names.
    //
    // This bit of code attempts to do that by using the bundle ID as an ID for the temp dir along with an incrementing ID
    // in cases where the temp dir may already exist.
    Long bundleID = new Long(data.getBundleID());
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
      bundleTempDir = new File(libTempDir, bundleID.toString() + "_" + new Integer(i).toString()); //$NON-NLS-1$
      libTempFile = new File(bundleTempDir, libName);
      if (bundleTempDir.exists()) {
        if (libTempFile.exists())
          continue; // to to next temp file
        break;
      }
      break;
    }
    if (!bundleTempDir.exists()) {
      bundleTempDir.mkdirs();
      bundleTempDir.deleteOnExit();
      // This is just a safeguard incase the VM is terminated unexpectantly, it also looks like deleteOnExit cannot really work because
      // the VM likely will still have a lock on the lib file at the time of VM exit.
      File deleteFlag = new File(libTempDir, BaseStorage.DELETE_FLAG);
      if (!deleteFlag.exists()) {
        // need to create a delete flag to force removal the temp libraries
        try {
          FileOutputStream out = new FileOutputStream(deleteFlag);
          out.close();
        } catch (IOException e) {
          // do nothing; that would mean we did not make the temp dir successfully
        }
      }
    }
    // copy the library file
    InputStream in = new FileInputStream(realLib);
    AdaptorUtil.readFile(in, libTempFile);
    // set permissions if needed
    BundleFile.setPermissions(libTempFile);
    libTempFile.deleteOnExit(); // this probably will not work because the VM will probably have the lib locked at exit
    // return the temporary path
    return libTempFile.getAbsolutePath();
  }

}
TOP

Related Classes of org.eclipse.osgi.internal.baseadaptor.BaseStorage$StateSaver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.