Java Source Code Blocks in Org Mode
Org Mode support for Java
Introduction
Java is a general purpose, object oriented computer language. When a java source code block is evaluated, the code is written as a java class, compiled to a class file, and executed.
Requirements and Setup
Add Java Support to Babel
- Install the java compiler and runtime environment
- Configure java source code blocks for Org mode by adding the
appropriate dotted pair to
org-babel-load-languages
(org-babel-do-load-languages 'org-babel-load-languages '((java . t)))
Configure Defaults
Default behavior for ob-java differs from most babel languages in two ways:
- ob-java defaults to scripting mode (
:results output
) - ob-java writes tempfiles to the current directory instead of the babel temporary directory
Both defaults are set in org-babel-default-header-args:java
, and can
be changed by modifying that variable. To change both defaults to
make ob-java consistent with the rest of babel, add this to your init
file after initializing babel:
(nconc org-babel-default-header-args:java '((:dir . nil) (:results . value))
Note that this adds the overrides to the end of the list. This is important because the list is processed in order and the last value is used.
Org Mode Features for Java Source Code Blocks
Header Arguments
Java source code blocks accept the following header arguments. All headers arguments are optional.
:dir
- specify which directory to write source and class files (default is the current directory)
:classname
- fully qualified classname (see Class and Main Method Definitions)
:imports
- a list of classes to add as imports (see Imports)
:cmpflag
- pass command line arguments to the java compiler
:cmdline
- pass command line arguments to the java runtime
:cmdarg
- pass command line arguments to the source code block (see Arguments)
Simple Example
This is hello world:
#+begin_src java :results output public class Main { public static void main(String[] args) { System.out.print("hello, world"); } } #+end_src
Class and Main Method Definitions
It is not necessary to include the class
statement or define a
main
method. ob-java will wrap a source code block in boilerplate
class and main method definitions if they are omitted. If
:classname
and the class definition in the source code block are
both omitted, the class will be named Main
.
This is exactly equivalent to the above hello world:
#+begin_src java :results output System.out.print("hello, world"); #+end_src
Classname and Package Name
The package and classname of a class can be defined in the source code
block or by the :classname
header argument or both. If they are
defined in both places, then they must match.
:classname
can just be a classname, like Greeter
or it could
contain the package name, such as com.package.Greeter
. This example
names the class Greeter
and puts it in the com.package
package.
#+begin_src java :results output :classname com.package.Greeter System.out.print("hello, world"); #+end_src
Source Files and Tangling
By default, when a source code block is evaluated the source files and
class files are written directly to babel's temporary directory. If a
package was specified, it is removed so that babel doesn't have to
create subdirectories under its temporary directory. In the above
com.package.Greeter
example, the default behavior is to remove the
com.package
and write Greeter.java
to babel's temporary
directory.
If the :dir
header argument is specified, then source files are
written within package directories under the specified directory and
package names are preserved. In the com.package.Greeter
example, if
the :dir
header is given, the package is preserved.
Tangling works as expected. Package is always preserved when tangling.
Return values
Babel source code blocks can either return a value (this is called
functional mode and can be chosen with :results output
) or output
printed by the source code block (this is called scripting mode and
can be chosen with :results value
).
To preserve legacy behavior, java source code blocks use scripting
mode by default. To switch to functional mode you have to specify
:results value
in the header.
We've already seen hello world in scripting mode, but here it is again:
#+begin_src java :results output System.out.print("hello, world"); #+end_src
This is what hello world looks like in functional mode:
#+begin_src java :results value return "hello, world"; #+end_src
Return a List
This example returns a list using scripting mode. For the result to
show up as a list in the org buffer, notice that the :results
must
be set to raw list
.
#+begin_src java :results output raw list System.out.println("1"); System.out.println("2"); #+end_src
This is the output:
#+RESULTS: - 1 - 2
Returning a list in functional mode is straightforward. Simply say
:results
will return a list
and then return a List
. This
example results in identical output to the previous example.
#+begin_src java :results value list List<Integer> a = Arrays.asList(1, 2); return a; #+end_src
Another way to achieve the same result is to use an array, as in the following example.
#+begin_src java :results value list Integer[] a = {4, 1}; return a; #+end_src
Return a Table
This example returns a table using scripting mode. Notice that the
output includes pipe characters to build the table, and the :results
header specifies the type is raw
.
#+begin_src java :results output raw System.out.println("|1|2|3"); System.out.println("|4|5|6"); #+end_src
This is the output:
#+RESULTS: | 1 | 2 | 3 | | 4 | 5 | 6 |
The same output is achieved with the following:
#+begin_src java :results value table List<List<Integer>> a = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)); return a; #+end_src
Return a Table with Headers
This example returns a table with headers using scripting mode. The
hline is created the same way as it is created while editing an org
table, by inserting a |-
at the start of a line inside the table.
#+begin_src java :results output raw System.out.println("|col1|col2|col3"); System.out.println("|-"); System.out.println("|1|2|3"); System.out.println("|4|5|6"); #+end_src
This is the output:
#+RESULTS: | col1 | col2 | col3 | |------+------+------| | 1 | 2 | 3 | | 4 | 5 | 6 |
The same output is achieved with the following. Note that the hline
is represented with a null
in the table, and that we had to change
to using a List<Object>
since the header row items are String
but
the rest of the data items are int
.
#+begin_src java :results value table List<List<Object>> a = Arrays.asList(Arrays.asList("col1", "col2", "col3"), null, Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)); return a; #+end_src
Variables and Arguments
Java source code blocks can take input from the org buffer as variables or arguments. Arguments are more limited and are supported to preserve legacy behavior. Use of variables is preferred.
Variables
Pass variables with the :var
header. Variable types are inferred.
This example accepts two integers and adds them:
#+begin_src java :var a=1 b=2 :results output System.out.print("sum: " + (a+b)); #+end_src
This example passes a string variable:
#+begin_src java :var a="some string" :results output System.out.print(a); #+end_src
Multi-line string literals are not supported in java. To pass a multi-line string as a variable, embed newline characters in a single-line string.
Java source code blocks can accept elisp list
or vector
. In
either case the variables are typed as java.util.List
. In this
example a
is a List<String>
.
#+begin_src java :var a='("one" "two") :results output System.out.print(a.get(0) + " " + a.get(1)); #+end_src
This example accepts a named list taken from the org buffer. Note
that lists appear to be a table with one item in each row. a
is a
List<List<String>>
here, where the outside list contains rows and
the inside list contains columns. See Imports to find out how to
import List
, or why we didn't do it here.
#+name: some-list - one - two #+begin_src java :var a=some-list :results output System.out.print(a.get(0).get(0) + " " + a.get(1).get(0)); #+end_src
Another way to accept a list is to slice it when it is assigned. The
[,0]
in this examples selects the first column of each row so that
a
is a List<String>
.
#+name: some-list - one - two #+begin_src java :var a=some-list[,0] :results output System.out.print(a.get(0) + " " + a.get(1)); #+end_src
The following example transposes and doubles the values in a 2x2
table. a
is available as a List<List<Integer>>
.
#+name: some-table | 1 | 2 | | 3 | 4 | #+begin_src java :var a=some-table :results output System.out.println((a.get(0).get(0)*2) + " " + (a.get(1).get(0)*2)); System.out.println((a.get(0).get(1)*2) + " " + (a.get(1).get(1)*2)); #+end_src
Arguments
All arguments are typed as strings.
Here is an example that passes an argument:
#+begin_src java :results output :cmdargs "argument" System.out.print(args[0]); #+end_src
Pass multiple arguments by separating them by spaces.
#+begin_src java :results output :cmdargs "two arguments" System.out.print(args[0] + " " + args[1]); #+end_src
In order to pass a string with spaces, quote the string twice and escape the inner quotes.
#+begin_src java :results output :cmdargs "\"this is one argument\"" System.out.print(args[0]); #+end_src
Imports
Imports can be added at the top of source code blocks or added using
the :imports
header argument. Imports are allowed in source code
blocks that omit the boilerplate class and main method definitions.
The following classes can be used without explicitly importing them:
java.util.List
java.util.Arrays
java.io.BufferedWriter
java.io.FileWriter
java.io.IOException
This example imports a class using the :imports
header argument:
#+begin_src java :results output :imports java.util.Base64 byte[] encoded = Base64.getEncoder().encode("encoded message".getBytes()); String decoded = new String(Base64.getDecoder().decode(encoded)); System.out.print(String.format("encoded=%s, decoded=%s", new String(encoded), decoded)); #+end_src
This is exactly equivalent, but specifies the import within the source code block:
#+begin_src java :results output import java.util.Base64; byte[] encoded = Base64.getEncoder().encode("encoded message".getBytes()); String decoded = new String(Base64.getDecoder().decode(encoded)); System.out.print(String.format("encoded=%s, decoded=%s", new String(encoded), decoded)); #+end_src
Source and Class File Locations
Most babel languages write the source code block to a file in the babel temporary directory and compile there, but originally ob-java used the current directory (the directory containing the org file) instead.
This may be because the java compiler requires the source file to be under java package subdirectories and the babel temporary directory doesn't allow for subdirectories. A benefit of using the current directory is that it allows source code blocks to depend on classes defined in other blocks. When writing to the babel temporary directory, all source code blocks must be independent.
ob-java can write to the babel temporary directory now, but by default it uses the current directory to preseve the previous behavior. It is possible to change this behavior locally or globally. This is a source block that will override the default and compile in the babel temporary directory:
#+begin_src java :dir 'nil :classname com.package.Greeter System.out.print("hello, world"); #+end_src
To change the default behavior see Configure Defaults.
Tramp Support
If the org file containing the java source code block is on a remote
machine and :dir
is either not set (it defaults to the current
directory, which would be remote in this case) or is set to a remote
path, then the source files will be written to the remote machine,
compiled by the remote java compiler and run by the remote java
runtime.
If the org file is remote but :dir
is set to a local directory, the
source file will be written to the local machine and local java
binaries will be used.
Non-executable Classes
If a source code block includes methods but doesn't include a main method, a generic main method will be added. This prevents the source code block from erroring when evaluated.
Sessions
There is no support for sessions