Extensions Guide

NetLogo 4.1.3 User Manual   

NetLogo allows users to write new commands and reporters in Java and other languages and use them in their models. This section of the User Manual introduces this facility.

The first part discusses how to use an extension in your model once you have written one, or once someone has given you one.

The second part is intended for programmers interested in writing their own extensions using the NetLogo Extension API.

The NetLogo Extension API Specification contains further details.

Using Extensions

To use an extension in a model, add the extensions keyword at the beginning of the Procedures tab, before declaring any breeds or variables.

After extensions comes a list of extension names in square brackets. For example:

extensions [sound speech]

Using extensions tells NetLogo to find and open the specified extension and makes the custom commands and reporters found in the extension available to the current model. You can use these commands and reporters just as if they were built-in NetLogo primitives.

Where extensions are located

NetLogo will look for extensions in several places:

  1. In the folder of the current model.
  2. The extensions folder in the same location as the NetLogo application.

Each NetLogo extension consists of a folder with the same name as the extension, entirely in lower case. This folder must contain a JAR file with the same name as the folder. For example the sound extension is stored in a folder called sound with a file inside called sound.jar. For more information about the contents of an extension's folder, please see the section of this manual on Writing Extensions.

To install a NetLogo extension for use by any model, put the extension's folder in the extensions directory in the NetLogo directory. Or, you can just keep the extension's folder in the same folder as the model that uses it.

Some extensions depend on additional files. These files will be in the extension's folder along with the JAR file. The folder may also contain other files such as documentation and example models.

Applets

Models saved as applets (using "Save as Applet" on NetLogo's File menu) can make use of extensions. Copy the entire extension directory into the directory that contains the model file. Applets can use almost any extension, even those that require external jars. However, applets still cannot use extensions that require native libraries; this includes the GoGo and QuickTime for Java extensions.

Writing Extensions

This section of the User Manual introduces this facility for Java programmers. We'll assume that you know the Java language and related tools and practices.

Our API's are also usable from other languages for the Java Virtual Machine, such as Scala. Following the Java information is a section on how to write an extension in Scala.

Summary

A NetLogo extension consists of a folder with the following contents: Required:

Optional:

To build your extension, you must include NetLogo.jar in your class path.

Examples

Several sample extensions with full Java (or Scala) source code are included with NetLogo. Some others are available for download here.

Tutorial

Let's write an extension that provides a single reporter called first-n-integers.

first-n-integers will take a single numeric input n and report a list of the integers 0 through n - 1. (Of course, you could easily do this just in NetLogo; it's only an example.)

1. Create extension folder

Since an extension is a folder with several items, we first need to create our folder. In this example, it is called example. We will be doing all of our work in that folder. We will also want to create a src sub-folder to hold our Java code, and a classes sub-folder for the compiled classes.

2. Write primitives

The primitives are implemented as one or more Java classes. The .java files for these classes should be put in the src sub-folder.

A command performs an action; a reporter reports a value. To create a new command or reporter, create a class that implements the interface org.nlogo.api.Command or org.nlogo.api.Reporter, which extend org.nlogo.api.Primitive. In most cases, you can extend the abstract class org.nlogo.api.DefaultReporter or org.nlogo.api.DefaultCommand.

DefaultReporter requires that we implement:

Object report (Argument args[], Context context)
  throws ExtensionException;

Since our reporter takes an argument, we also implement:

Syntax getSyntax();

Here's the implementation of our reporter, in a file called src/IntegerList.java:

import org.nlogo.api.*;

public class IntegerList extends DefaultReporter
{
    // take one number as input, report a list
    public Syntax getSyntax() {
        return Syntax.reporterSyntax(
            new int[] {Syntax.TYPE_NUMBER}, Syntax.TYPE_LIST
        );
    }
    
    public Object report(Argument args[], Context context)
        throws ExtensionException
    {
        // create a NetLogo list for the result
        LogoList list = new LogoList();   
        
        int n ;
        // use typesafe helper method from 
        // org.nlogo.api.Argument to access argument
        try
        {
            n = args[0].getIntValue();  
        }
        catch( LogoException e )
        {
            throw new ExtensionException( e.getMessage() ) ;
        }
        
        if (n < 0) {
            // signals a NetLogo runtime error to the modeler
            throw new ExtensionException
              ("input must be positive");
        }
        
        // populate the list
        // note that we use Double objects; NetLogo numbers
        // are always doubles
        for (int i = 0; i < n; i++) {
            list.add(Double.valueOf(i));
        }
        return list;
    }
}

Notes:

A Command is just like a Reporter, except that reporters implement Object report(...) while commands implement void perform(...).

2. Write a ClassManager

Each extension must include, in addition to any number of command and reporter classes, a class that implements the interface org.nlogo.api.ClassManager. The ClassManager tells NetLogo which primitives are part of this extension. In simple cases, extend the abstract class org.nlogo.api.DefaultClassManager, which provides empty implementations of the methods from ClassManager that you aren't likely to need.

Here's the class manager for our example extension, src/SampleExtension.java:

import org.nlogo.api.*;

public class SampleExtension extends DefaultClassManager {
    public void load(PrimitiveManager primitiveManager) {
        primitiveManager.addPrimitive
          ("first-n-integers", new IntegerList());
    }
}

addPrimitive() tells NetLogo that our reporter exists and what its name is.

3. Write a Manifest

The extension must also include a manifest. The manifest is a text file which tells NetLogo the name of the extension and the location of the ClassManager.

The manifest must contain three tags:

Here's a manifest for our example extension, manifest.txt:

Manifest-Version: 1.0
Extension-Name: example
Class-Manager: SampleExtension
NetLogo-Extension-API-Version: 4.1

The NetLogo-Extension-API-Version line should match the actual version of NetLogo Extension API you are using.

Make sure even the last line ends with a newline character.

4. Create a JAR

To create an extension's JAR file, first compile your classes as usual, either from the command line or using an IDE.

Important: You must add NetLogo.jar (from the NetLogo distribution) to your classpath when compiling.

Here's an example of how compiling your extension might look from the command line:

$ mkdir -p classes     # create the classes subfolder if it does not exist
$ javac -classpath NetLogo.jar -d classes src/IntegerList.java src/SampleExtension.java

You will need to change the classpath argument to point to the NetLogo.jar file from your NetLogo installation. This command line will compile the .java and put the .class files in the classes subfolder.

Then create a JAR containing the resulting class files and the manifest. For example:

$ jar cvfm example.jar manifest.txt -C classes .

For information about manifest files, JAR files and Java tools, see java.sun.com.

5. Use your extension in a model

To use our example extension, put the example folder in the NetLogo extensions folder, or in the same directory as the model that will use the extension. At the top of the Procedures tab write:

extensions [example]

Now you can use example:first-n-integers just like it was a built-in NetLogo reporter. For example, select the Interface tab and type in the Command Center:

observer> show example:first-n-integers 5
observer: [0 1 2 3 4]

Scala Tutorial

Now let's rewrite the extension in Scala.

First you'll need to make sure you are using exactly the right version of Scala. Extensions written in Scala must be compiled with the specific Scala build dated 2009-04-16, revision r17517, available here: scala-2.8.0.r17517.zip.

We apologize for the inconvenience of requiring this particular Scala version; in a future version of NetLogo, we plan to require the released version of Scala 2.8.0 instead, once it becomes available.

1. Create extension folder

Let's start with a new folder called, example-scala. As with the Java example, create src and classes sub-folders.

2. Write primitives and a Class Manager

We'll put all of the source code in one file. Here's the implementation of our reporter, and our ClassManager, in a file called src/IntegerList.scala:

import org.nlogo.api._
import org.nlogo.api.Syntax._
import org.nlogo.api.ScalaConversions._

class SampleScalaExtension extends DefaultClassManager {
  def load(manager: PrimitiveManager) {
    manager.addPrimitive("first-n-integers", new IntegerList)
  }
}

class IntegerList extends DefaultReporter {
  override def getSyntax = reporterSyntax(Array(TYPE_NUMBER), TYPE_LIST)

  def report(args: Array[Argument], context: Context): AnyRef = {
    val n = try {args(0).getIntValue}
    catch {
      case e: LogoException => throw new ExtensionException(e.getMessage)
    }
    if (n < 0) throw new ExtensionException("input must be positive")

    (0 until n).toLogoList
  }
}

Mostly this is a straightforward, line-by-line translation of the Java version.

One difference is worth noting. In the Java version, we explicitly converted ints to Double objects. As previously mentioned, all numbers used as NetLogo values must be of type Double, even if they happen to have no fractional part. In the Scala version we leverage implicit conversions to do this work for us. We do so by importing org.nlogo.api.ScalaConversions._, which provides us with two new methods via implicit conversions. The first is toLogoList, which converts Scala Seqs to LogoLists as seen in: (0 until n).toLogoList. The second is toLogoObject, which converts any supported Scala value to the appropriate NetLogo type. The conversions provided by toLogoObject are:

Input Type Output Type
String String
scala.Boolean, java.lang.Boolean java.lang.Boolean
scala.Char, java.lang.Character String
scala.Byte, java.lang.Byte java.lang.Double
scala.Short, java.lang.Short java.lang.Double
scala.Int, java.lang.Integer java.lang.Double
scala.Float, java.lang.Float java.lang.Double
scala.Double, java.lang.Double java.lang.Double
scala.Long, java.lang.Long java.lang.Double
org.nlogo.api.Agent org.nlogo.api.Agent
org.nlogo.api.AgentSet org.nlogo.api.AgentSet
org.nlogo.api.Nobody org.nlogo.api.Nobody
org.nlogo.api.ExtensionObject org.nlogo.api.ExtensionObject
scala.Seq org.nlogo.api.LogoList
org.nlogo.api.LogoList org.nlogo.api.LogoList
(anything else) error

The conversions to LogoList are recursive. Nested collections in the input will be converted to nested LogoLists in which all elements have been converted by toLogoObject. ExtensionObjects, on the other hand, are not recursed into.

Using the toLogoObject conversion is simple. Just call the method on an an Any. Example: 7.toLogoObject

3. Create a JAR

To create an extension's JAR file, first compile your classes as usual, either from the command line or using an IDE.

Important: As when compiling Java, you must add NetLogo.jar (from the NetLogo distribution) to your classpath when compiling.

Here's an example of how compiling your extension might look from the command line:

$ mkdir -p classes     # create the classes subfolder if it does not exist
$ scalac -classpath NetLogo.jar -d classes src/IntegerList.java

You will need to change the classpath argument to point to the NetLogo.jar file from your NetLogo installation. This command line will compile the .java and put the .class files in the classes subfolder.

Then create a JAR containing the resulting class files and the manifest exactly as was done with the Java classes. For example:

$ jar cvfm example-scala.jar manifest.txt -C classes .

4. Use your extension in a model

Using a Scala extension is the same as using a Java extension. However there is one caveat: NetLogo ships with a stripped down version of scala-library.jar. While most common classes (Array, List, Map) are present, it may be best to check, especially if your extension makes use of less common classes.

To check which classes are present in the scala-library.jar that ships with NetLogo, in the lib directory inside the NetLogo directory type this at the command line:

$ jar tf scala-library.jar

If you find that a class used by your extension is missing (either by checking manually or via a ClassNotFound exception), there is still hope. Place a copy of a full version of scala-library.jar into the root of your extensions directory (in this case example-scala). Or, since the full scala-library.jar is large (over 4 megabytes), you may wish to prepare a smaller jar containing only the specific missing classes you need to supply.

Extension development tips

Instantiation

Your class manager is instantiated at the time a model using the extension is loaded.

Command and reporter objects are instantiated whenever NetLogo code is compiled that uses your commands and reporters.

Classpath

Don't forget to include NetLogo.jar in your class path when compiling. This is the most common mistake made by new extension authors. (If the compiler can't find NetLogo.jar, you'll get error messages about classes in the org.nlogo.api package not being found.)

Debugging extensions

There are special NetLogo primitives to help you as you develop and debug your extension. These are considered experimental and may be changed at a later date. (That's why they have underscores in their name.)

Third party JARs

If your extension depends on code stored in a separate JAR, copy the extra JARs into the extension's directory. Whenever an extension is imported, NetLogo makes all the JARs in its folder available to the extension.

If you plan to distribute your extension to other NetLogo users, make sure to provide installation instructions.

Supporting old Java versions

NetLogo works with Java 5 and later. If you want your extension to be usable by all NetLogo users, your extension should support Java 5.

The easiest way is to accomplish this is do all your development with the Java 5 JDK.

It's also possible to develop for Java 5 using the Java 6 or later compiler, but you need to do two things:

Conclusion

Don't forget to consult the NetLogo API Specification for full details on these classes, interfaces, and methods.

Note that there is no way for the modeler to get a list of commands and reporters provided by an extension, so it's important that you provide adequate documentation.

The extensions facility is not yet complete. The API doesn't include everything you might expect to be present. Some facilities exist but are not yet documented. If you don't see a capability you want, please let us know. Do not hesitate to contact us at feedback@ccl.northwestern.edu with questions, as we may be able to find a workaround or provide additional guidance where our documentation is thin.

Hearing from users of this API will also allow us to appropriately focus our efforts for future releases. We are committed to making NetLogo flexible and extensible, and we very much welcome your feedback.