code: /*--------------------------------------------------------------*
    Copyright (C) 2011 OpenSim Ltd.
  
    This file is distributed WITHOUT ANY WARRANTY. See the file
    'License' for details on this and other legal matters.
  *--------------------------------------------------------------*/
  
  package org.omnetpp.ide;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IFolder;
  import org.eclipse.core.resources.IProject;
  import org.eclipse.core.resources.IResource;
  import org.eclipse.core.resources.IResourceChangeEvent;
  import org.eclipse.core.resources.IResourceChangeListener;
  import org.eclipse.core.resources.IResourceDelta;
  import org.eclipse.core.resources.IResourceDeltaVisitor;
  import org.eclipse.core.resources.ResourcesPlugin;
  import org.eclipse.core.runtime.CoreException;
  import org.omnetpp.common.project.ProjectUtils;
  import org.osgi.framework.Bundle;
  import org.osgi.framework.BundleContext;
  import org.osgi.framework.BundleException;
  import org.osgi.framework.startlevel.FrameworkStartLevel;
  import org.osgi.framework.wiring.FrameworkWiring;
  
  /**
   * Handles the automatic installation / deinstallation of plugin jar files
   * from the project's "plugins" folder.
   *
   *   NOTE:The p2 system automatically uninstalls all plugins during the startup
   *   that were not installed via p2. This means that each dynamic plugin is freshly installed
   *   on each IDE restart or on each project close/open meaning that persistent data
   *   for the plugin is not kept.
   */
  public class OmnetppDynamicPluginLoader implements IResourceChangeListener {
  
      private static final String OMNETPP_BUNDLE_MARKER = "omnetpp_bundle";
      public static final String PLUGINS_FOLDER = "plugins";
  
      protected BundleContext bundleContext;
  
      public OmnetppDynamicPluginLoader(BundleContext bundleContext) throws BundleException {
          this.bundleContext = bundleContext;
          installOmnetppDynamicBundlesInAllOpenProjects();
          startOmnetppDynamicBundles();
      }
  
      public void resourceChanged(IResourceChangeEvent event) {
          // uninstall the project plugins before closing the projects
          if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
              IProject project = (IProject)event.getResource();
  
              if (ProjectUtils.hasOmnetppNature(project)) {
                  for (IResource plugin : getBundleFilesFrom(project)) {
                      try {
                          if (plugin.isAccessible())
                              uninstallBundle(plugin);
                      }
                      catch (Exception e) {
                          OmnetppMainPlugin.logError(e);
                      }
                  }
              }
          }
          else if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
              final boolean[] startPlugins = { false };
              try {
                  event.getDelta().accept(new IResourceDeltaVisitor() {
                      public boolean visit(IResourceDelta resourceDelta) throws CoreException {
                          try {
                              IResource resource = resourceDelta.getResource();
                              // visit the workspace children
                              if (resource.getProject() == null)
                                  return true;
  
                              // ignore closed and non omnetpp projects
                              if (!resource.getProject().isOpen() || !ProjectUtils.hasOmnetppNature(resource.getProject()))
                                  return false;
  
                              // process children if the resource is not a plugin
                              if (!isJarFileInPluginsFolder(resource))
                                  return true;
  
                              int kind = resourceDelta.getKind();
                              if (kind == IResourceDelta.ADDED && resource.isAccessible()) {
                                  installBundle(resource);
                                  startPlugins[0] = true;  // request to start plugins after installing after processing the delta
                              }
                              else if (kind == IResourceDelta.REMOVED)
                                  uninstallBundle(resource);
                              else if (kind == IResourceDelta.CHANGED && resource.isAccessible()) {
                                  uninstallBundle(resource);
                                  installBundle(resource);
                                  startPlugins[0] = true;
                              }
  
                              return false;
                          }
                          catch (BundleException e) {
                              OmnetppMainPlugin.logError(e);
                              return false;
                          }
                          catch (IOException e) {
                              OmnetppMainPlugin.logError(e);
                              return false;
                          }
                      }
                  });
              } catch (CoreException e) {
                  OmnetppMainPlugin.logError(e);
              }
  
              // after installing all new plugins try to start (resolve and activate) them
              if (startPlugins[0])
                  startOmnetppDynamicBundles();
          }
      }
  
      /**
       * Install a bundle from an omnetpp project and mark it so we will recognize it later as
       * an "omnetpp dynamic bundle"
       */
      protected void installBundle(IResource bundleResource) throws BundleException, IOException {
          // set run level 5 for the bundle
          Bundle systemBundle = bundleContext.getBundle(0);
          FrameworkStartLevel fs = (FrameworkStartLevel)systemBundle.adapt(FrameworkStartLevel.class);
          int origStartLevel = fs.getInitialBundleStartLevel();
          fs.setInitialBundleStartLevel(5); // start level for workspace installed bundles. normal IDE plugins are on level 4
  
          // install the bundle
          Bundle bundle = bundleContext.installBundle(bundleResource.getLocationURI().toString());
  
          // restore run level
          fs.setInitialBundleStartLevel(origStartLevel);
  
          // mark the bundle with a marker file in the bundle instance area
          // so we will recognize later that it was installed from an omnetpp project
          bundle.getDataFile(OMNETPP_BUNDLE_MARKER).createNewFile();
      }
  
      /**
       * Uninstall a bundle and stop all bundles immediately that depend on it
       */
      protected void uninstallBundle(IResource bundleResource) throws BundleException {
          Bundle bundle = bundleContext.installBundle(bundleResource.getLocationURI().toString());
          bundle.uninstall();
  
          // access the system bundle
          Bundle systemBundle = bundleContext.getBundle(0);
          FrameworkWiring fw = (FrameworkWiring)systemBundle.adapt(FrameworkWiring.class);
          // stop current bundle pending for uninstallation because otherwise other running bundles
          // depending on this would keep it active
          fw.refreshBundles(Arrays.asList(new Bundle[] { bundle }));
      }
  
      /**
       * List dynamic omnetpp plugins that are available in this project.
       * (closed projects or projects that has no omnetpp nature return no plugins)
       */
      protected static List<IResource> getBundleFilesFrom(IProject project) {
          List<IResource> result = new ArrayList<IResource>();
          if (project.isAccessible() && ProjectUtils.hasOmnetppNature(project)) {
              IFolder plugins = project.getFolder(PLUGINS_FOLDER);
              if (plugins.exists())
                  try {
                      for (IResource resource : plugins.members())
                          if (isJarFileInPluginsFolder(resource))
                              result.add(resource);
                  } catch (CoreException e) {
                      OmnetppMainPlugin.logError(e);
                  }
          }
  
          return result;
      }
  
      private static boolean isJarFileInPluginsFolder(IResource resource) {
          // must be a file with jar extension
          if (!(resource instanceof IFile) || !"jar".equals(((IFile)resource).getFileExtension()))
              return false;
          // must be located in the <project>/plugins directory
          IFolder pluginsFolder = resource.getProject().getFolder(PLUGINS_FOLDER);
          return resource.getParent().equals(pluginsFolder);
      }
  
      /**
       * Tries to start all dynamic omnetpp plugins installed from the workspace.
       * Dynamic omnetpp plugins are marked with a file in the bundle's data directory.
       * This must be called after each plugin installation, so a plugin
       * that became resolvable because of the latest installations (i.e. we
       * installed a plugin on which the plugin depended on) will be started.
       */
      public void startOmnetppDynamicBundles() {
          Bundle[] bundles = bundleContext.getBundles();
          for (Bundle bundle : bundles) {
              if (bundle.getDataFile(OMNETPP_BUNDLE_MARKER).exists())
                  try {
                      bundle.start(Bundle.START_TRANSIENT);
                  } catch (BundleException e) {
                      OmnetppMainPlugin.logError(e);
                  }
          }
      }
  
      /**
       * Install all bundles from the open projects.
       */
      public void installOmnetppDynamicBundlesInAllOpenProjects() {
          for (IProject proj : ResourcesPlugin.getWorkspace().getRoot().getProjects())
              if (proj.isAccessible())
                  for (IResource bundleFile : getBundleFilesFrom(proj))
                      try {
                          installBundle(bundleFile);
                      } catch (BundleException e) {
                          OmnetppMainPlugin.logError(e);
                      } catch (IOException e) {
                          OmnetppMainPlugin.logError(e);
                      }
      }
  
  }
  
  