Java Install and Build

This topic outlines how to install and build the initial support for Java provided in Legato release 16.04.

Download JVM

Go to http://www.oracle.com/technetwork/java/embedded/embedded-se/downloads/index.html Dowload the Oracle bundle suitable for your target, and extract it. Use either compact1 or compact2 profile in the linux_arm_sflt sub-folder

Note
Sierra Wireless is not responsible for the Oracle license fees associated with using the Oracle Java Virtual Machine (JVM). Sierra Wireless doesn't distribute or maintain the Oracle JVM.

Environment Variables

Before you can build Java support in Legato, your shell needs to be setup with the environment variable JAVA_INCLUDE_DIR

On Ubuntu 15.10 and later, set:

JAVA_INCLUDE_DIR=/usr/lib/jvm/java-8-openjdk-amd64/

On earlier Ubuntu versions, set:

JAVA_INCLUDE_DIR=/usr/lib/jvm/java-7-openjdk-amd64/

Then set the Oracle's embedded JVM environment variable EJDK_DIR to the directory where you installed the built JVM compact profile.

Java Program Structure

Java components have Java code instead of C or C++ code.

You create a javaPackage: section in the @ .cdef listing Java packages to build. It should look something like this:

javaPackage:
{
io.legato.samples
}

The build tools will look for Java to code under COMPONENT_DIR/src/io/legato/samples/*.java

It won't recurse automatically into subdirectories; if you want subdirectories, they also must added to the JavaPackage section.

javaPackage:
{
io.legato.samples
io.legato.samples.foo
io.legato.samples.bar
}

The first Java package listed is assumed to be the main component package as it contains a class with the same name of the component, and it implements the interface (io.legato.Component).

A hello world Java app folder structure should look something like this:

+-- javaHelloComponent
| +-- Component.cdef
| +-- src
| +-- io
| +-- legato
| +-- samples
| +-- javaHelloComponent.java
+-- jHello.adef

Bundling JVM and Apps into Components

You need to bundle the app with your specific embedded JVM; otherwise, the JVM won't be automatically linked into your app.

You do this by creating entries in the Java component's requires: section.

In this sample:

  • embeddedOracleJvm bundles Oracle's JVM into an app
  • onTargetOracleJvm assumes the JVM has already been installed on the target and binds it into the component:
javaPackage:
{
io.legato.samples
}
 
requires:
{
component:
{
embeddedOracleJvm
// onTargetOracleJvm
}
}

The .adef files don't have specific changes to support a Java app. The class path and execute configuration are automatically setup when Java components are used.

Note
Don't mix C/C++ and Java components in the same executable, although this may be supported in a later release.

Java Target Structure

Everything needed to run Java on the target goes into the working directory the same as C/C++ apps. However, Java requires that a src directory exsits where the generated code is placed in the component working directory.

The component factory code is generated as io.legato.generated.component.<componentName>.Factory

The class files are placed into the corresponding obj directory. A static main is generated in a class named Main under the apps directory called io.legato.generated.exe.helloWorld.Main

Like components, Java code goes under an obj directory. The component jar file is created under the staging read-only directory. The component for the executable is created under the staging directory. The executable's main .jar file is staged under the app's bin directory, and the component .jar files are staged under lib for final app packaging.

Component Structure

The build system expects a "main class" inside of the first package listed in a javaPackages section.

The main class is expected to have the same name as the component and to implement the interface io.legato.Component.

The two methods to be implemented from that interface are:

public interface Component
{
public void componentInit();
public void setLogger(Logger logger);
}

An instance of the connection to the Legato log is passed to the component through . The method componentInit is called once the framework has initialized, all client interface connections have been established, and all server interfaces have been advertised.

A component main looks like this:

package my.package.namespace;
 
import io.legato.Ref;
import io.legato.Level;
import io.legato.Result;
import io.legato.Component;
import java.util.logging.Logger;
 
public class MyComponent implements Component
{
private Logger logger;
 
public MyComponent()
{
}
 
public void componentInit()
{
logger.Log(Level.INFO, "Hello world.");
}
 
public void setLogger(Logger newLogger)
{
logger = newLogger;
}
}

Legato API Generation

When you include an API in your Java component's requires: section, an interface and client class are generated for that API.

For example, we could import the config API like this:

requires:
{
api:
{
Config = le_cfg.api
}
}

An interface named Config will then be generated in the package io.legato.api. The framework will then expect the component main class to have a method with the following signature:

public void setConfig(Config newCfg)
{
cfg = newCfg;
}

Where cfg can be a member variable of the class to hold the client instance for the config API.

The API reference types, enumerations, and constants, and event interfaces are generated as members of the interface class Config.

For example, to read a message from the config:

Config.IteratorRef ref = cfg.CreateReadTxn("");
Ref<String> messageRef = new Ref<String>(new String());
 
Result code = config.GetString(ref, "/myDataa/message", messageRef, "Default Message");
 
if (code != Result.OVERFLOW)
{
logger.log(Level.INFO, "Message: " + messageRef.getValue());
}
else
{
logger.log(Level.WARN, "Message overflow.");
}
Note
All API function OUT parameters need to be wrapped in a Ref.

When providing a service from a Java component, you simply need to implement the interface and its methods.

Dependency Checking

Like C/C++ components, the ninja build tool is used to do dependency checking to compile and package an app. If the .class files are up to date against the .java sources, then Java code compiling won't occur. The build tools also assume that the javac command line compiler is somewhere in the path.