Home

Awesome

build status Code style: black

jgo: painless Java component execution

Summary

Maven is a great tool. It manages dependencies so that Java projects become reusable "building blocks" in a robust way, like pip for Python, npm for JavaScript, gem for Ruby, cpan for Perl, etc. And the Maven Central repository contains a tremendous wealth of code, ripe for reuse in your own projects.

Unfortunately, Maven provides no easy way to actually launch code from the beautifully managed dependencies stored so lovingly into ~/.m2/repository.

This project fills that gap: jgo launches Java code. You do not need to download or install any JARs; you just specify an "endpoint" consisting of a Maven artifact identifier, plus a main class if needed/desired, and jgo uses Maven to obtain and run it.

To do this, jgo builds the local environment on demand, caching it into a subfolder of ~/.jgo, so that the endpoint's particular dependencies are available in one place.

Installation

The jgo project began life as a shell script, but was later translated into Python, so that tools such as scyjava could leverage its environment-building capabilities.

As such, there are now two implementations from which to choose! Each has pros and cons.

Prerequisites

jgo uses mvn and java for the heavy lifting. The shell script version needs some common utilities (e.g., cat). If you are missing anything, the script will tell you.

The shell script

The jgo.sh shell script requires a POSIX-friendly system. It is known to work on Linux, macOS, Cygwin, Microsoft's Windows Subsystem for Linux, and Git BASH from the Git for Windows project.

<details><summary><strong>Installing the shell script</strong></summary>

Just clone this repo and symlink jgo.sh into your favorite bin directory.

For example, assuming ~/bin is on your PATH:

cd
git clone https://github.com/scijava/jgo
cd bin
ln -s ../jgo/jgo.sh jgo
jgo --help
</details>

The Python module

The Python version of jgo offers a jgo console script, as well as a jgo module for programmatically creating endpoints.

<details><summary><strong>Installing with pip</strong></summary>
pip install jgo
</details> <details><summary><strong>Installing with conda</strong></summary>
conda install -c conda-forge jgo
</details> <details><summary><strong>Installing from source</strong></summary>
git clone https://github.com/scijava/jgo
cd jgo

# install globally (not recommended unless using a virtual environment)
pip install .

# install into ~/.local (see pip install --help for details)
pip install --user .

# install into $PREFIX
pip install --prefix=$PREFIX .

# install globally in developer mode (hot linked to working copy folder)
pip install -e .
</details>

Usage

Usage: jgo [-v] [-u] [-U] [-m] <jvm-args> <endpoint> <main-args>

  -v          : verbose mode flag
  -u          : update/regenerate cached environment
  -U          : force update from remote Maven repositories (implies -u)
  -m          : use endpoints for dependency management (see "Pitfalls" below)
  <jvm-args>  : any list of arguments to the JVM
  <endpoint>  : the artifact(s) + main class to execute
  <main-args> : any list of arguments to the main class

The endpoint should have one of the following formats:

- groupId:artifactId
- groupId:artifactId:version
- groupId:artifactId:mainClass
- groupId:artifactId:version:mainClass
- groupId:artifactId:version:classifier:mainClass

If version is omitted, then RELEASE is used.
If mainClass is omitted, it is auto-detected.
You can also write part of a class beginning with an @ sign,
and it will be auto-completed.

Multiple artifacts can be concatenated with pluses,
and all of them will be included on the classpath.
However, you should not specify multiple main classes.

Examples

ProgramCommand
Jython REPLjgo org.python:jython-standalone
JRuby evalecho "puts 'Hello Ruby'" | jgo org.jruby:jruby-complete:@jruby.Main
Groovy REPLjgo org.codehaus.groovy:groovy-groovysh:@shell.Main+commons-cli:commons-cli:1.3.1

Note the usage of the + syntax as needed to append elements to the classpath.

If you add scijava.public = https://maven.scijava.org/content/groups/public to the [repositories] section of your .jgorc (see Repositories below), you can also try:

ProgramCommand
SciJava REPL with JRubyjgo org.scijava:scijava-common:@ScriptREPL+org.scijava:scripting-jruby
SciJava REPL with Jythonjgo org.scijava:scijava-common:@ScriptREPL+org.scijava:scripting-jython
SciJava REPL with Groovyjgo org.scijava:scijava-common:@ScriptREPL+org.scijava:scripting-groovy
SciJava REPL with Clojurejgo org.scijava:scijava-common:@ScriptREPL+org.scijava:scripting-clojure
SciJava REPL with JavaScriptjgo org.scijava:scijava-common:@ScriptREPL+org.scijava:scripting-javascript

FAQ

Configuration

You can configure the behavior of jgo using the ~/.jgorc file.

Repositories

You can define additional remote Maven repositories, from which artifacts will be retrieved. E.g.:

[repositories]
scijava.public = https://maven.scijava.org/content/groups/public

If you need more control over where artifacts come from—for example, if you want to use your own remote Maven repository as a mirror of Maven Central—you can do it using Maven's usual ~/.m2/settings.xml; see Using Mirrors for Repositories.

You can also use the -r flag to pass additional repositories to individual invocations of jgo.

Shortcuts

You can define shortcuts for launching commonly used programs:

[shortcuts]
repl = imagej:org.scijava.script.ScriptREPL
imagej = net.imagej:imagej
fiji = sc.fiji:fiji:LATEST
scifio = io.scif:scifio-cli

Shortcuts are substituted verbatim from the beginning of the endpoint, single-pass in the order they are defined. So e.g. now you can run:

jgo repl

Note that with the repl shortcut above, the main class (org.scijava.script.ScriptREPL) comes from a different artifact than the toplevel artifact (net.imagej:imagej). This is intentional, so that all of ImageJ, including all of the various SciJava scripting-<foo> plugins, is included in the classpath of the REPL.

Settings

There are a few configurable settings:

[settings]
m2Repo = /path/to/.m2Repo (default ~/.m2/repository)
cacheDir = /path/to/.jgo (default ~/.jgo)
links = soft (options: hard, soft, none; default hard)

Note that the jgo cache dir can also be set via the JGO_CACHE_DIR environment variable when using Python jgo. The precedence of reading the cache dir, from highest to lowest:

Pitfalls

Dependency management

Maven has a feature whereby a project can override the versions of transitive (a.k.a. inherited) dependencies, via a <dependencyManagement> configuration. The problem is: a library may then believe it depends on components at particular versions as defined by its <dependencyManagement>, but downstream projects which depend on that library will resolve to different versions. See this SO thread and this gist for full details.

To work around this issue, you can pass -m to jgo, which causes it to add all endpoints to the synthesized POM's <dependencyManagement> section using import scope. By doing this, the versions of transitive dependencies used in the synthesized project should more precisely match those of each endpoint itself—although in the case of multiple endpoints concatenated via the + operator with conflicting dependency management, the earlier endpoints will win because they will be declared earlier in the POM. See also issue #9 in the jgo issue tracker.

Development

Code style

jgo uses black for its code style.

After pip install tox, you can lint the code with:

tox -e lint

Alternatives