Package org.nlogo.workspace

Source Code of org.nlogo.workspace.ExtensionManager$JarContainer

// (C) Uri Wilensky. https://github.com/NetLogo/NetLogo

package org.nlogo.workspace;

import org.nlogo.api.ClassManager;
import org.nlogo.api.CompilerException;
import org.nlogo.api.Dump;
import org.nlogo.api.ErrorSource;
import org.nlogo.api.ExtensionException;
import org.nlogo.api.Primitive;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
* Some simple notes on loading and unloading extensions:
* - The load method is called when an extension appears in the extensions block when it wasn't
* there in the last compilation
* - The unload method is called when an extension is removed from the extensions block
*
* Before a compilation, N extensions might be loaded.
* For example, if the extensions block previously said: extensions [ array table ]
* Then the array and table extensions will have their loaded and live flags set to true.
* For a new compilation, we have to remember which extensions are loaded so that we dont call load on them again.
* But we have to know which extensions were removed from the list, so that we call unload on them.
* For example, if the extensions block now says: extensions [ array ]
* then the table extension needs to be unloaded. But the array extension does not need to be loaded again.
* To accomplish this we need another flag, which i call 'live'. (it used to be reloaded).
* During the main compile when/if we reach the extensions block
* the compiler calls startFullCompilation and
* the ExtensionManager sets the live flag on all extensions to false.
* Then, for each extension in the block, its live flag is set to true.
* If the extension wasnt previously in the block, its loaded flag will be false
* and the ExtensionManager will set loaded to true and call the load method on it.
* Then, when we come across extension primitives later in compilation, we simply check
* the live flag. If the extension isn't live, then the primitive isn't found.
* For example, if someone removed table from the extensions block, the table extension
* will have live=false, and if they call table:make, then error.
* At the end of main compilation, the compiler calls the finishFullCompilation,
* and the ExtensionManager calls unload on any extensions that have loaded=true and live=false.
*
* Subprogram compilations just check the live flag in the same way, and everyone is happy.
*
* That is how it works now, but here is some info on the bug was that led to the addition
* of the live flag. (You shouldn't really have to read this, but maybe it might someday
* be useful).  After a main compile, the ExtensionManager would set reloaded to false on
* all extensions.  During the main compile, extensions previously and currently in the
* extensions block would have loaded=true.  When we encountered a primitive during the
* main compile, we checked the loaded flag.  But this was true even for extensions that
* had been removed from the extensions block!  So we would say that table:make was valid,
* even if table had just been removed.  Subprograms managed to still work because they
* would run after the main compile, after the loaded flags were set to false. They would
* get set to false if reloaded!=true.  It was only during the main compile that there was
* confusion.
*/
public strictfp class ExtensionManager
    implements org.nlogo.api.ExtensionManager {

  public static final String EXTENSION_NOT_FOUND =
    "Can't find extension: ";

  private final Map<String, JarContainer> jars =
      new HashMap<String, JarContainer>();
  private final AbstractWorkspace workspace;

  private int jarsLoaded = 0;

  // cities and other extensions may want access to this.  it
  // means violating the org.nlogo.api interface, but it it does
  // give them a way to manipulate the world and such without
  // breaking the existing extensions API -- CLB
  public AbstractWorkspace workspace() {
    return workspace;
  }

  public boolean profilingEnabled() {
    return workspace.profilingEnabled();
  }

  public ExtensionManager(AbstractWorkspace workspace) {
    this.workspace = workspace;
  }

  ///

  public boolean anyExtensionsLoaded() {
    return jarsLoaded > 0;
  }

  public Iterable<ClassManager> loadedExtensions() {
    java.util.ArrayList<ClassManager> result =
      new java.util.ArrayList<ClassManager>();
    for (JarContainer jar : jars.values()) {
      result.add(jar.classManager);
    }
    return result;
  }

  public String getSource(String filename)
      throws java.io.IOException {
    return workspace.getSource(filename);
  }

  public org.nlogo.api.File getFile(String path)
      throws ExtensionException {
    org.nlogo.nvm.FileManager fm = workspace().fileManager();
    return fm.getFile(getFullPath(path));
  }

  // ugly stuff to ensure that we only load
  // the soundbank once. guess anyone else can use it too.
  private Object obj = null;

  public void storeObject(Object obj) {
    this.obj = obj;
  }

  public Object retrieveObject() {
    return obj;
  }

  private String identifierToJar(String id) {
    // If we are given a jar name, then we look for that otherwise
    // we assume that we have been given an extensions name.
    // Extensions are folders which have a jar with the same name
    // in them (plus other things if needed) -- CLB
    if (!id.endsWith(".jar")) {
      if (AbstractWorkspace.isApplet()) {
        return id + "/" + id + ".jar";
      } else {
        return id + java.io.File.separator + id + ".jar";
      }

    } else {
      return id;
    }

  }

  // called each time extensions is parsed
  public void importExtension(String extName, ErrorSource errors)
      throws CompilerException {
    String jarPath = identifierToJar(extName);

    try {
      jarPath = resolvePathAsURL(jarPath);
      if (AbstractWorkspace.isApplet()) {
        java.net.URL url = new java.net.URL(jarPath);
        // added in r43348. motivation: https://trac.assembla.com/nlogo/ticket/647 .
        // we need to work around http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6785446
        url.openConnection().setUseCaches(false);
      }
    } catch (RuntimeException ex) {
      ex.printStackTrace();
      errors.signalError(EXTENSION_NOT_FOUND + extName);
      return;
    } catch (java.net.MalformedURLException e) {
      e.printStackTrace();
    } catch (java.io.IOException e) {
      e.printStackTrace();
    }

    try {
      java.net.URLClassLoader myClassLoader = getClassLoader(jarPath, errors, getClass().getClassLoader());

      if (myClassLoader == null) {
        return;
      }

      ClassManager classManager =
          getClassManager(jarPath, myClassLoader, errors);

      if (classManager == null) {
        return;
      }

      JarContainer theJarContainer = jars.get(jarPath);

      long modified = getModified(jarPath, errors);

      // Check to see if he have seen this Jar before
      if ((theJarContainer == null) ||
          (theJarContainer.modified != modified)) {
        if (theJarContainer != null) {
          theJarContainer.classManager.unload(this);
        }
        theJarContainer = new JarContainer(extName, jarPath, myClassLoader, modified);
        try {
          // compilation tests shouldn't initialize the extension
          if (!workspace.compilerTestingMode()) {
            classManager.runOnce(this);
          }
        } catch (org.nlogo.api.ExtensionException ex) {
          System.err.println("Error while initializing extension.");
          System.err.println("Error is: " + ex);
          throw ex;
        }
        jars.put(jarPath, theJarContainer);
      }

      // Check to see if it has been loaded into the model
      if (!theJarContainer.loaded) {
        jarsLoaded++;
        theJarContainer.loaded = true;
        theJarContainer.classManager = classManager;
        theJarContainer.primManager = new ExtensionPrimitiveManager(extName);
        classManager.load(theJarContainer.primManager);
      }

      // Jars that have been removed won't get this flag set
      theJarContainer.live = true;

      theJarContainer.prefix = getExtensionName(jarPath, errors);
    } catch (org.nlogo.api.ExtensionException ex) {
      errors.signalError(ex.getMessage());
      return;
    } catch (java.lang.IncompatibleClassChangeError ex) {
      // thrown if extension classes from different version are incompatible
      // catching this is necessary so it doesn't just choke
      errors.signalError("This extension doesn't work with this version of NetLogo");
      System.err.println(ex);
    }
  }

  public void addToLibraryPath(Object classManager, String directory) {
    org.nlogo.api.JavaLibraryPath.setLibraryPath(classManager.getClass(), directory);
  }

  public String resolvePath(String path) {
    try {
      java.io.File result = new java.io.File(workspace.attachModelDir(path));
      if (AbstractWorkspace.isApplet()) {
        return result.getPath();
      } else {
        try {
          return result.getCanonicalPath();
        } catch (java.io.IOException ex) {
          return result.getPath();
        }
      }
    } catch (java.net.MalformedURLException ex) {
      throw new IllegalStateException(path + " is not a valid pathname: " + ex);
    }
  }

  public String resolvePathAsURL(String path) {
    java.net.URL jarURL;

    if (AbstractWorkspace.isApplet()) {

      try {
        String jarPath = workspace.fileManager().attachPrefix(path);
        if (org.nlogo.api.RemoteFile.exists(jarPath)) {
          return jarPath;
        } else {
          throw new IllegalStateException
              (EXTENSION_NOT_FOUND + path + " using URL " + jarPath);
        }
      } catch (java.net.MalformedURLException ex) {
        throw new IllegalStateException(path + " is not a valid pathname: " + ex);
      }

    }

    // Is this a URL right off the bat?
    try {
      jarURL = new java.net.URL(path);
      return jarURL.toString();
    } catch (java.net.MalformedURLException ex) {
      org.nlogo.util.Exceptions.ignore(ex);
    }

    // If it's a path, look for it relative to the model location
    if (path.contains(java.io.File.separator)) {
      try {
        java.io.File jarFile = new java.io.File(workspace.attachModelDir(path));
        if (jarFile.exists()) {
          return toURL(jarFile).toString();
        }
      } catch (java.net.MalformedURLException ex) {
        org.nlogo.util.Exceptions.ignore(ex);
      }
    }

    // If it's not a path, try the model location
    try {
      java.io.File jarFile =
          new java.io.File(workspace.attachModelDir(path));
      if (jarFile.exists()) {
        return toURL(jarFile).toString();
      }
    } catch (java.net.MalformedURLException ex) {
      org.nlogo.util.Exceptions.ignore(ex);
    }

    // Then try the extensions folder
    try {
      java.io.File jarFile =
          new java.io.File("extensions" + java.io.File.separator + path);
      if (jarFile.exists()) {
        return toURL(jarFile).toString();
      }
    } catch (java.net.MalformedURLException ex) {
      org.nlogo.util.Exceptions.ignore(ex);
    }

    // Give up
    throw new IllegalStateException(EXTENSION_NOT_FOUND + path);
  }

  public String getFullPath(String path)
      throws ExtensionException {
    if (AbstractWorkspace.isApplet()) {
      try {
        String fullPath = workspace.fileManager().attachPrefix(path);
        if (org.nlogo.api.RemoteFile.exists(fullPath)) {
          return fullPath;
        } else {
          throw new ExtensionException("Can't find file " + path + " using " + fullPath);
        }
      } catch (java.net.MalformedURLException ex) {
        throw new ExtensionException(path + " is not a valid pathname: " + ex);
      }
    }

    try {
      String fullPath = workspace.attachModelDir(path);
      java.io.File f = new java.io.File(fullPath);
      if (f.exists()) {
        return fullPath;
      }
    } catch (java.net.MalformedURLException ex) {
      org.nlogo.util.Exceptions.ignore(ex);
    }

    // Then try the extensions folder
    java.io.File f = new java.io.File("extensions" + java.io.File.separator + path);
    if (f.exists()) {
      return f.getPath();
    }

    // Give up
    throw new ExtensionException("Can't find file " + path);
  }

  // We only want one ClassLoader for every Jar per NetLogo instance
  private java.net.URLClassLoader getClassLoader(String jarPath, ErrorSource errors, ClassLoader parentLoader)
      throws CompilerException {
    JarContainer theJarContainer = jars.get(jarPath);
    if (theJarContainer != null) {
      return theJarContainer.jarClassLoader;
    }
    try {
      java.net.URL jarURL = new java.net.URL(jarPath);

      // all the urls our class loader will look at
      List<java.net.URL> urls =
          new ArrayList<java.net.URL>();

      // start with the original extension jar
      urls.add(jarURL);

      // Get other Jars in the extensions own dir
      java.io.File folder = new java.io.File(new java.io.File(jarURL.getFile()).getParent());
      urls.addAll(getAdditionalJars(folder));

      // Get other Jars in extensions folder
      folder = new java.io.File("extensions");
      urls.addAll(getAdditionalJars(folder));

      // We use the URLClassLoader.newInstance method because that works with
      // the applet SecurityManager, even tho newLClassLoader(..) does not.
      return java.net.URLClassLoader.newInstance
          (urls.toArray(new java.net.URL[urls.size()]), parentLoader);

    } catch (java.net.MalformedURLException ex) {
      errors.signalError("Invalid URL: " + jarPath);
      return null;
    }
  }

  // We want a new ClassManager per Jar Load
  private ClassManager getClassManager(String jarPath,
                                                     java.net.URLClassLoader myClassLoader,
                                                     ErrorSource errors)
      throws CompilerException {
    JarContainer theJarContainer = jars.get(jarPath);

    if ((theJarContainer != null) && (theJarContainer.loaded)) {
      return theJarContainer.classManager;
    }

    String classMangName = null;

    try {
      // Class must be named in Manifest file
      classMangName = getClassManagerName(jarPath, errors);

      if (classMangName == null) {
        errors.signalError("Bad extension: Couldn't locate Class-Manager tag in Manifest File");
      }

      Object classMang = myClassLoader.loadClass(classMangName).newInstance();

      try {
        return (ClassManager) classMang;
      } catch (ClassCastException ex) {
        errors.signalError("Bad extension: The ClassManager doesn't implement "
            + "org.nlogo.api.ClassManager");
      }
    } catch (java.io.FileNotFoundException ex) {
      errors.signalError(EXTENSION_NOT_FOUND + jarPath);
    } catch (java.io.IOException ex) {
      errors.signalError("Can't open extension " + jarPath);
    } catch (InstantiationException ex) {
      throw new IllegalStateException(ex);
    } catch (IllegalAccessException ex) {
      throw new IllegalStateException(ex);
    } catch (ClassNotFoundException ex) {
      errors.signalError("Can't find class " + classMangName
          + " in extension");
    }

    return null;
  }

  /**
   * Gets the name of an extension's ClassManager implementation from the manifest.
   */
  private String getClassManagerName(String jarPath, ErrorSource errors)
      throws java.io.IOException, CompilerException {
    java.net.URL jarURL = new java.net.URL("jar", "", jarPath + "!/");
    java.net.JarURLConnection jarConnection = (java.net.JarURLConnection) jarURL.openConnection();
    String name = null;

    if (jarConnection.getManifest() == null) {
      errors.signalError("Bad extension: Can't find a Manifest file in extension");
    }

    java.util.jar.Attributes attr = jarConnection.getManifest().getMainAttributes();
    name = attr.getValue("Class-Manager");

    if (!checkVersion(attr)) {
      errors.signalError("User halted compilation");
    }

    return name;
  }

  /**
   * Gets the extension name from the manifest.
   */
  private String getExtensionName(String jarPath, ErrorSource errors)
      throws CompilerException {
    try {
      java.net.URL jarURL = new java.net.URL("jar", "", jarPath + "!/");
      java.net.JarURLConnection jarConnection = (java.net.JarURLConnection) jarURL.openConnection();
      String name = null;

      if (jarConnection.getManifest() == null) {
        errors.signalError("Bad extension: Can't find Manifest file in extension");
      }

      java.util.jar.Attributes attr = jarConnection.getManifest().getMainAttributes();
      name = attr.getValue("Extension-Name");

      if (name == null) {
        errors.signalError("Bad extension: Can't find extension name in Manifest.");
      }

      return name;
    } catch (java.io.FileNotFoundException ex) {
      errors.signalError(EXTENSION_NOT_FOUND + jarPath);
    } catch (java.io.IOException ex) {
      errors.signalError(EXTENSION_NOT_FOUND + jarPath);
    }

    return null;
  }

  public Object readFromString(String source)
      throws CompilerException {
    return workspace.readFromString(source);
  }

  public void clearAll() {
    for (JarContainer jar : jars.values()) {
      jar.classManager.clearAll();
    }
  }

  public org.nlogo.api.ExtensionObject readExtensionObject(String extName,
                                                           String typeName,
                                                           String value)
      throws CompilerException {
    JarContainer theJarContainer = null;

    extName = extName.toUpperCase();

    // Locate the class in all the loaded Jars
    Iterator<Map.Entry<String, JarContainer>> entries = jars.entrySet().iterator();

    while (entries.hasNext()) {
      Map.Entry<String, JarContainer> entry = entries.next();
      theJarContainer = entry.getValue();
      String name = theJarContainer.primManager.name().toUpperCase();

      if (theJarContainer.loaded && name != null && name.equals(extName)) {
        try {
          return theJarContainer.classManager.readExtensionObject
              (this, typeName, value);
        } catch (org.nlogo.api.ExtensionException ex) {
          System.err.println(ex);
          throw new IllegalStateException
              ("Error reading extension object "
                  + extName + ":" + typeName
                  + " " + value + " ==> " + ex.getMessage());
        }
      }
    }
    return null;
  }

  public Primitive replaceIdentifier(String name) {
    Primitive prim = null;
    boolean qualified = name.indexOf(':') != -1;

    // Locate the class in all the loaded Jars
    Iterator<Map.Entry<String, JarContainer>> entries = jars.entrySet().iterator();

    while (entries.hasNext() && prim == null) {
      Map.Entry<String, JarContainer> entry = entries.next();
      JarContainer theJarContainer = entry.getValue();
      String extName = theJarContainer.primManager.name().toUpperCase();

      if (theJarContainer.live) {
        // Qualified references must match the extension name
        if (qualified) {
          String prefix = name.substring(0, name.indexOf(':'));
          String pname = name.substring(name.indexOf(':') + 1);
          if (prefix.equals(extName)) {
            prim = theJarContainer.primManager.getPrimitive(pname);
          }
        } else {
          if (theJarContainer.primManager.autoImportPrimitives()) {
            prim = theJarContainer.primManager.getPrimitive(name);
          }
        }
      }
    }
    return prim;
  }

  /**
   * Returns a String describing all the loaded extensions.
   */
  public String dumpExtensions() {
    String str = "EXTENSION\tLOADED\tMODIFIED\tJARPATH\n";
    str += "---------\t------\t---------\t---\n";

    JarContainer theJarContainer = null;

    // Locate the class in all the loaded Jars
    Iterator<JarContainer> values = jars.values().iterator();
    while (values.hasNext()) {
      theJarContainer = values.next();
      str += theJarContainer.prefix + "\t" + theJarContainer.loaded + "\t" + theJarContainer.modified + "\t" + theJarContainer.jarName + "\n";
    }

    return str;
  }

  public java.util.List<String> getJarPaths() {
    java.util.ArrayList<String> names = new java.util.ArrayList<String>();
    for (JarContainer jar : jars.values()) {
      names.add(jar.extensionName + '/' + jar.extensionName + ".jar");
      for (String additionalJar : jar.classManager.additionalJars()) {
        names.add(jar.extensionName + '/' + additionalJar);
      }
    }
    return names;
  }

  public java.util.List<String> getExtensionNames() {
    java.util.ArrayList<String> names = new java.util.ArrayList<String>();
    for (JarContainer jar : jars.values()) {
      names.add(jar.extensionName);
    }
    return names;
  }

  /**
   * Returns a String describing all the loaded extensions.
   */
  public String dumpExtensionPrimitives() {
    String pstr = "\n\nEXTENSION\tPRIMITIVE\tTYPE\n";
    pstr += "----------\t---------\t----\n";

    JarContainer theJarContainer = null;

    // Locate the class in all the loaded Jars
    Iterator<JarContainer> values = jars.values().iterator();
    while (values.hasNext()) {
      theJarContainer = values.next();
      Iterator<String> k = theJarContainer.primManager.getPrimitiveNames();
      while (k.hasNext()) {
        String name = k.next();
        Primitive p = theJarContainer.primManager.getPrimitive(name);
        String type = (p instanceof org.nlogo.api.Reporter ? "Reporter" : "Command");
        pstr += theJarContainer.prefix + "\t" + name + "\t" + type + "\n";
      }
    }

    return pstr;
  }


  // Called by CompilerManager when a model is changed
  public void reset() {
    for (JarContainer jc : jars.values()) {
      try {
        jc.classManager.unload(this);
      } catch (org.nlogo.api.ExtensionException ex) {
        System.err.println(ex);
        // don't throw an illegal state exception,
        // just because one extension throws an error
        // doesn't mean we shouldn't unload the rest
        // and continue with the operation ev 7/3/08
        ex.printStackTrace();
      }
      jc.loaded = false;
      jc.live = false;
      jc.jarClassLoader = null;
    }

    jars.clear();
    jarsLoaded = 0;
  }


  List<java.net.URL> getAdditionalJars(java.io.File folder) {
    List<java.net.URL> urls =
        new ArrayList<java.net.URL>();
    if (!AbstractWorkspace.isApplet() &&
        folder.exists() &&
        folder.isDirectory()) {
      java.io.File[] files = folder.listFiles();
      for (int n = 0; n < files.length; n++) {
        if (files[n].isFile() && files[n].getName().toUpperCase().endsWith(".JAR")) {
          try {
            urls.add(toURL(files[n]));
          } catch (java.net.MalformedURLException ex) {
            throw new IllegalStateException(ex);
          }
        }
      }
    }
    return urls;
  }

  public void startFullCompilation() {
    // forget which extensions are in the extensions [ ... ] block
    for (JarContainer nextJarContainer : jars.values()) {
      nextJarContainer.live = false;
    }
  }

  // Used to see if any IMPORT keywords have been removed since last compilation
  public void finishFullCompilation() {
    for (JarContainer nextJarContainer : jars.values()) {
      try {
        if ((nextJarContainer.loaded) && (!nextJarContainer.live)) {
          jarsLoaded--;
          jars.remove(nextJarContainer.prefix);
          nextJarContainer.loaded = false;
          nextJarContainer.classManager.unload(this);
        }
      } catch (org.nlogo.api.ExtensionException ex) {
        // i'm not sure how to handle this yet
        System.err.println("Error unloading extension: " + ex);
      }
    }
  }

  private boolean checkVersion(java.util.jar.Attributes attr) {
    String currentVer = org.nlogo.api.APIVersion.version();
    String jarVer = attr.getValue("NetLogo-Extension-API-Version");

    if (jarVer == null) {
      return workspace.warningMessage(
          "Could not determine version of NetLogo extension.  NetLogo can "
              + "try to load the extension, but it might not work.");
    } else if (!currentVer.equals(jarVer)) {
      return workspace.warningMessage(
          "You are attempting to open a NetLogo extension file that was created " +
              "for a different version of the NetLogo Extension API.  (This NetLogo uses Extension API "
              + currentVer + "; the extension uses NetLogo Extension API " + jarVer
              + ".)  NetLogo can try to load the extension, " +
              "but it might not work.");
    }
    return true;
  }

  private long getModified(String jarPath, ErrorSource errors)
      throws CompilerException {
    try {
      return new java.net.URL(jarPath)
          .openConnection().getLastModified();
    } catch (java.io.IOException ex) {
      System.err.println(ex);
      errors.signalError("Can't open extension");
      // this is unreachable, since signalError never returns.
      // we have to have it, though, since jikes can't figure that out.
      throw new IllegalStateException("this code is unreachable");
    }
  }

  public void exportWorld(java.io.PrintWriter writer) {
    writer.println(Dump.csv().encode("EXTENSIONS"));
    writer.println();
    for (JarContainer container : jars.values()) {
      StringBuilder data = container.classManager.exportWorld();
      if (data.length() > 0) {
        writer.println(Dump.csv().encode(container.extensionName));
        writer.print(data);
        writer.println();
      }
    }
  }

  public void importExtensionData(String name, List<String[]> data,
                                  org.nlogo.api.ImportErrorHandler handler)
      throws org.nlogo.api.ExtensionException {
    JarContainer jar = getJarContainerByIdentifier(name);
    if (jar != null) {
      jar.classManager.importWorld(data, this, handler);
    } else {
      throw new org.nlogo.api.ExtensionException("there is no extension named " + name + "in this model");
    }
  }

  public boolean isExtensionName(String name) {
    return getJarContainerByIdentifier(name) != null;
  }

  private JarContainer getJarContainerByIdentifier(String identifier) {
    for (JarContainer jar : jars.values()) {
      if (jar.extensionName.equalsIgnoreCase(identifier)) {
        return jar;
      }
    }
    return null;
  }

  private class JarContainer {
    public final String extensionName;
    public final String jarName;
    public java.net.URLClassLoader jarClassLoader;
    public final long modified;
    public ExtensionPrimitiveManager primManager;
    public ClassManager classManager;

    // loaded means that the load method has been called for this extension.
    // any further recompiles with extension still in it should not call the load method.
    // the extension can later be unloaded by removing it from the extensions [ ... ] block
    // at that time, its unload method will be called, and loaded will be set to false.
    // if it ever reappears in the extensions [ ... ] block, then load will be called again
    // etc, etc. JC - 12/3/10
    public boolean loaded;

    // live means the extension is currently in the extensions [ ... ] block in the code.
    // if an extension is live, then its primitives are available to be called. JC - 12/3/10
    public boolean live;

    public String prefix;

    JarContainer(String extensionName, String jarName,
                 java.net.URLClassLoader jarClassLoader, long modified) {
      this.extensionName = extensionName;
      this.jarName = jarName;
      this.jarClassLoader = jarClassLoader;
      loaded = false;
      live = false;
      prefix = null;
      this.modified = modified;
    }
  }

  // for 4.1 we have too much fragile, difficult-to-understand,
  // under-tested code involving URLs -- we can't get rid of our
  // uses of toURL() until 4.2, the risk of breakage is too high.
  // so for now, at least we make this a separate method so the
  // SuppressWarnings annotation is narrowly targeted. - ST 12/7/09
  @SuppressWarnings("deprecation")
  private static java.net.URL toURL(java.io.File file)
      throws java.net.MalformedURLException {
    return file.toURL();
  }

}
TOP

Related Classes of org.nlogo.workspace.ExtensionManager$JarContainer

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.