-
-
Notifications
You must be signed in to change notification settings - Fork 239
Extensions API
For background information on extensions in NetLogo, see the Extensions section of the User Manual.
Several sample extensions are included with NetLogo. The full Java (or Scala) source code for all of them is hosted on GitHub. Most are in the public domain. Some are copyrighted, but under an open-source license.
Many extensions developed by users are available for download from the Extensions page. Most of them include source code.
To discuss NetLogo development, including usage of NetLogo APIs, browse or join the netlogo-devel group, or come chat with us in our gitter room.
This page covers writing extensions for the NetLogo 6.0 (Beta) and later releases. For authoring extensions for NetLogo 5.3.1 and earlier, please consult this older version of this page. For help converting an extension for NetLogo 5 to 6, see the 6.0 Extension and Controlling API Transition Guide.
This page 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. If you have a choice between using Scala and using Java, we recommend using Scala as several sections of the API reference Scala library classes which are much easier to use from scala.
A NetLogo extension consists of a folder with the following contents:
Required:
- A JAR file with the same name as the extension, the following contents:
- one or more classes that implements
org.nlogo.api.Primitive
- a main class that implements
org.nlogo.api.ClassManager
- a NetLogo extension manifest file, with the following four tags:
-
Manifest-Version
, always 1.0 -
Extension-Name
, the name of the extension. -
Class-Manager
, the fully-qualified name of a class implementingorg.nlogo.api.ClassManager
. -
NetLogo-Extension-API-Version
, the version of NetLogo Extension API for which this JAR is intended. If a user opens the extension with NetLogo that has a different Extension API version, a warning message is issued. To tell which version of the Extension API your NetLogo supports, choose the "About NetLogo" item in the "Help" menu and then click on the System tab. Or, you can launchNetLogo.jar
with the--extension-api-version
argument. On a mac, for example, you may find theNetLogo.jar
at the following path:/Applications/NetLogo 6.0/Java/netlogo-6.0.0.jar
.
-
- one or more classes that implements
Optional:
- One or more additional JAR files which the extension requires.
- A
lib
directory with any required native libraries. - One or more NetLogo models demonstrating how the extension is used.
- A
src
directory containing the source code for the model - Documentation.
To build your extension, you must include NetLogo.jar in your class path. In addition, the lib
directory (also from the NetLogo distribution) must be in same location as NetLogo.jar; it contains additional libraries used by NetLogo.jar
.
While a NetLogo extension can be built from any Java build system such as Ant, Gradle, or Maven, the CCL uses and recommends sbt
along with the NetLogo Extension Plugin. This plugin handles the following steps:
- Adding the appropriate NetLogo jar based on the
netLogoVersion
setting in yourbuild.sbt
. - Configuring
sbt
to attach the appropriate manifest to jars as part of thepackage
task. - Packaging your extension and any jar dependencies into a
.zip
file which can be distributed to users. Alternatively, you may elect to have it relocate the.jar
and any dependencies into the project root.
Since the CCL uses the NetLogo extension plugin, it will always be up-to-date with the latest build steps necessary for using an extension without requiring user intervention.
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.)
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/main/java
sub-folder to hold our Java code.
The primitives are implemented as one or more Java classes. The .java files for these classes should be put in the src/main/java
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
.
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/main/java/IntegerList.java
:
import org.nlogo.api.*;
import org.nlogo.core.Syntax;
import org.nlogo.core.SyntaxJ;
public class IntegerList implements Reporter {
// take one number as input, report a list
public Syntax getSyntax() {
return SyntaxJ.reporterSyntax(
new int[] {Syntax.NumberType()}, Syntax.ListType());
}
public Object report(Argument args[], Context context)
throws ExtensionException {
// create a NetLogo list for the result
LogoListBuilder list = new LogoListBuilder();
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.toLogoList();
}
}
Notes:
- The number objects we put in the list are Doubles, not Integers. All numbers used as NetLogo values must be of type Double, even if they happen to have no fractional part.
- To access arguments, use
org.nlogo.api.Argument
's typesafe helper methods, such asgetDoubleValue()
. - Throw
org.nlogo.api.ExtensionException
to signal a NetLogo runtime error to the modeler.
A Command
is just like a Reporter
, except that reporters implement Object report(...)
while commands implement void perform(...)
.
Other useful links:
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/main/java/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.
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:
-
Extension-Name
, the name of the extension. -
Class-Manager
, the fully-qualified name of a class implementingorg.nlogo.api.ClassManager
. -
NetLogo-Extension-API-Version
, the version of NetLogo Extension API for which this JAR is intended. If a user opens the extension with NetLogo that has a different Extension API version, a warning message is issued. To tell which version of the Extension API your NetLogo supports, choose the "About NetLogo" item in the "Help" menu. Or, you can launch theNetLogo.jar
with the--extension-api-version
argument.
Note that the API version is rarely the same as the NetLogo version. The NetLogo 6.0 release will have an API version of 6.0
, but NetLogo 6.0.1 and quite possible NetLogo 6.1 are also likely to have the API version 6.0
as well. This version number will increment when a backwards-incompatible change is introduced to the org.nlogo.api
or org.nlogo.core
package.
Here's a manifest for our example extension, manifest.txt
:
Manifest-Version: 1.0
Extension-Name: example
Class-Manager: SampleExtension
NetLogo-Extension-API-Version: 6.0
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.
If you're using the NetLogo sbt plugin, you can have sbt create the extension package for you by running sbt package
.
You will then find a .zip file in the project root, which can be unzipped in the NetLogo extensions directory to install the extension.
If you would like to build the jar by hand, the instructions are as follows:
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, and the lib directory must be accessible in the same location as NetLogo.jar
.
Each of our sample extensions on GitHub includes a command-line build, invoked with the sbt package
command.
Here's an example of how compiling your extension might look from the command line if you aren't using sbt or make:
$ mkdir -p classes # create the classes subfolder if it does not exist
$ javac -classpath NetLogo.jar -d classes src/main/java/IntegerList.java src/main/java/SampleExtension.java
You will need to change the classpath argument to point to the NetLogo.jar file from your NetLogo installation. For example, on Mac OS X you'd do:
javac -classpath "/Applications/NetLogo 6.0-BETA2/Java/netlogo-6.0.0-BETA2.jar" -d classes src/main/java/IntegerList.java src/main/java/SampleExtension.java
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 Oracle's Java information.
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 Code 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]
Now let's rewrite the extension in Scala.
If you are using Scala, you'll need to make sure you are using Scala 2.12.x. (Any version where the first two parts are 2.12). Other versions such as 2.10 or 2.11 will not work.
Let's start with a new folder called, example-scala
. Similar to the Java example, create a src/main/scala
sub-folder.
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/main/scala/IntegerList.scala
:
import org.nlogo.api._
import org.nlogo.api.ScalaConversions._
import org.nlogo.core.Syntax
import org.nlogo.core.Syntax.{ NumberType, ListType }
class SampleScalaExtension extends DefaultClassManager {
def load(manager: PrimitiveManager) {
manager.addPrimitive("first-n-integers", new IntegerList)
}
}
class IntegerList extends Reporter {
override def getSyntax = Syntax.reporterSyntax(right = List(NumberType), ret = ListType)
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 Seq
s to LogoList
s 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:
- from
scala.Boolean, java.lang.Boolean
tojava.lang.Boolean
- from
scala.Char, java.lang.Character
toString
- from
scala.Byte, java.lang.Byte
tojava.lang.Double
- from
scala.Short, java.lang.Short
tojava.lang.Double
- from
scala.Int, java.lang.Integer
tojava.lang.Double
- from
scala.Float, java.lang.Float
tojava.lang.Double
- from
scala.Long, java.lang.Long
tojava.lang.Double
- from
scala.Double
tojava.lang.Double
- from
scala.Seq
toorg.nlogo.api.LogoList
- any already valid recognized type (e.g.
String
,ExtensionObject
) passes through unaltered - anything else results in an error
The conversions to LogoList
are recursive. Nested collections in the input will be converted to nested LogoList
s in which all elements have been converted by toLogoObject
. ExtensionObject
s, 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
If you're using the NetLogo sbt plugin, you can have sbt create the extension package for you by running sbt package
.
You will then find a .zip file in the project root, which can be unzipped in the NetLogo extensions directory to install the extension.
If you would like to build the jar by hand, the instructions are as follows:
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/main/scala/IntegerList.scala
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 .
Note that NetLogo includes scala-library.jar
, so you do not need to include scala-library.jar
alongside your extension jar.
Your class manager is instantiated in a fresh JVM classloader at the time a model using the extension is loaded.
This is done so that extensions are unloadable, so that when you open a new model, the extensions used by the previous model can be unloaded and the memory they used reclaimed. (The JVM does not allow unloading particular classes; you can only unload an entire classloader at once.)
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. If the lib directory isn't in the same location as NetLogo.jar, you'll get errors about other classes not being found.)
If you use sbt or maven, adding the NetLogo jar can be done through your build tool. NetLogo jars from 5.3.1 and later are hosted on bintray. You can view the bintray page for the relevant artifacts here. The bintray page also includes example code for adding NetLogo.jar as a dependency. Note that if you use the NetLogo extension sbt plugin that the NetLogo dependency will be added to your build automatically.
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.)
-
print __dump-extensions
prints information about loaded extensions -
print __dump-extension-prims
prints information about loaded extension primitives -
__reload-extensions
forces NetLogo to reload all extensions the next time you compile your model. Without this command, changes in your extension JAR will not take effect until you open a model or restart NetLogo.
You can run language test from within your extension's sbt session. The NW-Extension uses this. See its build.sbt
for an example.
Similarly, you can run benchmarks from within your extension's sbt session. If you setup language tests for the extension, then just put your benchmark models in the models/test/benchmarks
subfolder of your extension's root folder. Then you can run them with:
test:run-main org.nlogo.headless.HeadlessBenchmarker BenchmarkName
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.
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.
Don't forget to consult the NetLogo API Specification for full details on these classes, interfaces, and methods.
You may also find it useful to consult the NetLogo source code on GitHub.
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 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.
At times you may want to download extension code from GitHub and build it yourself (possibly for the purpose of making modifications). The following method will work for extensions built with sbt package
, such as any of the bundled NetLogo Extensions. These are attached to the NetLogo GitHub and can be found via searching for NetLogo repositories containing the word "Extension". A few good extensions to look at include:
- The Scala sample extension
- The Java sample extension
- The NW Extension
- The gogo extension
First, navigate to the extension that you want to download on GitHub. Download the repository as a ZIP file and unzip it.
If you wish to modify the code, open the /src
directory and modify the text files inside using your text editor or IDE of choice.
Some install and configuration steps are required for the tools for building an extension. See the Windows Setup page for the details, and complete those steps before continuing.
You are now prepared to build the extension. Open the Command Prompt (search for "cmd" in the Start Menu). Enter the command "cd [directory]" to navigate to the extension (for example, "cd C:\GoGo Extension"). Then enter "sbt package." Your code should compile at this time. If there are errors in your code, the command prompt will output error messages.
You should end up with a .jar file in the extension directory. Copy this .jar file to the appropriate directory in NetLogo (e.g. C:\Program Files (x86)\NetLogo 6.0\Java\extensions\gogo). The next time you run NetLogo, you should have the new version of the extension.
- Extensions
- NetLogo Libraries
- Controlling API
- Extensions API
- 6.0 Extension and Controlling API Transition-Guide
- Optimizing NetLogo Runs
- Setting English as the Default Language when Running NetLogo
- Unofficial Features
- Advanced Installation
- Optimization List
- Java System Properties and how to use them
- NetLogo on ARM Devices
- Multiple Views via HubNet
- Branches
- Building
- Tests
- Windows Setup
- Continuous Integration
- Draft: How to Help
- Google Summer of Code Ideas List
- Syntax Highlighting
- Building with IntelliJ
- Code Formatting
- Localization
- File (.nlogo) and Widget Format
- Benchmarking
- Releasing
- Preparing the Models Library for Release
- Documentation
- NetLogo Bundled Java Versions
- JOGL
- Plugins API
- Architecture
- LazyAgentset
- Model Runs and Review Tab
- Model Runs: To Do and Code Overview
- Notes on in Radius
- Archived Branches
- The nlogox format
- Touch API Proposal
- Why isn't NetLogo "Parallel"?
- Potential Speedups
- Tortoise
- SimServer, WebStart, and NetLogo in Classrooms