Skip to content
This repository

Avian is a lightweight virtual machine and class library designed to provide a useful subset of Java's features, suitable for building self-contained applications.

Fetching latest commit…

Cannot retrieve the latest commit at this time

readme.txt
Quick Start
-----------

on Linux:
 $ export JAVA_HOME=/usr/local/java # or wherever you have the JDK installed
 $ make
 $ build/linux-i386/avian -cp build/linux-i386/test Hello

on Mac OS X:
 $ export JAVA_HOME=/Library/Java/Home
 $ make
 $ build/darwin-i386/avian -cp build/darwin-i386/test Hello
 
on Windows (MSYS):
 $ git clone git://oss.readytalk.com/win32.git ../win32
 $ export JAVA_HOME="C:/Program Files/Java/jdk1.6.0_07"
 $ make
 $ build/windows-i386/avian -cp build/windows-i386/test Hello

on Windows (Cygwin):
 $ git clone git://oss.readytalk.com/win32.git ../win32
 $ export JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.6.0_07"
 $ make
 $ build/windows-i386/avian -cp build/windows-i386/test Hello

Adjust JAVA_HOME according to your system, but be sure to use forward
slashes in the path.


Introduction
------------

Avian is a lightweight virtual machine and class library designed to
provide a useful subset of Java's features, suitable for building
self-contained applications.  More information is available at the
project web site:

  http://oss.readytalk.com/avian

If you have any trouble building, running, or embedding Avian, please
post a message to our discussion group:

  http://groups.google.com/group/avian

That's also the place for any other questions, comments, or
suggestions you might have.


Supported Platforms
-------------------

Avian can currently target the following platforms:

  Linux (i386, x86_64, ARM, and 32-bit PowerPC)
  Windows (i386 and x86_64)
  Mac OS X (i386, x86_64 and 32-bit PowerPC)
  Apple iOS (i386 and ARM)

Building
--------

Build requirements include:

  * GNU make 3.80 or later
  * GCC 3.4 or later (4.5.1 or later for Windows/x86_64)
  * JDK 1.5 or later
  * MinGW 3.4 or later (only if compiling for Windows)
  * zlib 1.2.3 or later

Earlier versions of some of these packages may also work but have not
been tested.

The build is directed by a single makefile and may be influenced via
certain flags described below, all of which are optional.

 $ make \
     platform={linux,windows,darwin} \
     arch={i386,x86_64,powerpc,arm} \
     process={compile,interpret} \
     mode={debug,debug-fast,fast,small} \
     ios={true,false} \
     bootimage={true,false} \
     heapdump={true,false} \
     tails={true,false} \
     continuations={true,false} \
     openjdk=<openjdk installation directory> \
     openjdk-src=<openjdk source directory>

  * platform - the target platform
      default: output of $(uname -s | tr [:upper:] [:lower:]),
      normalized in some cases (e.g. CYGWIN_NT-5.1 -> windows)

  * arch - the target architecture
      default: output of $(uname -m), normalized in some cases
      (e.g. i686 -> i386)

  * process - choice between pure interpreter or JIT compiler
      default: compile

  * mode - which set of compilation flags to use to determine
    optimization level, debug symbols, and whether to enable
    assertions
      default: fast

  * ios - if true, cross-compile for iOS on OS X.  Note that
    non-jailbroken iOS devices do not allow JIT compilation, so only
    process=interpret or bootimage=true builds will run on such
    devices.  See git://oss.readytalk.com/hello-ios.git for an example
    of an Xcode project for iOS which uses Avian.
      default: false

  * bootimage - if true, create a boot image containing the pre-parsed
    class library and ahead-of-time compiled methods.  This option is
    only valid for process=compile builds.  Note that you may need to
    specify both build-arch=x86_64 and arch=x86_64 on 64-bit systems
    where "uname -m" prints "i386".
      default: false

  * heapdump - if true, implement avian.Machine.dumpHeap(String),
    which, when called, will generate a snapshot of the heap in a
    simple, ad-hoc format for memory profiling purposes.  See
    heapdump.cpp for details.
      default: false

  * tails - if true, optimize each tail call by replacing the caller's
    stack frame with the callee's.  This convention ensures proper
    tail recursion, suitable for languages such as Scheme.  This
    option is only valid for process=compile builds.
      default: false

  * continuations - if true, support continuations via the
    avian.Continuations methods callWithCurrentContinuation and
    dynamicWind.  See Continuations.java for details.  This option is
    only valid for process=compile builds.
      default: false

  * openjdk - if set, use OpenJDK class library instead of the default
    Avian class library.  See "Building with the OpenJDK Class
    Library" below for details.
      default: not set

  * openjdk-src - if this and the openjdk option above are both set,
    build an embeddable VM using the OpenJDK class library.  The JNI
    components of the OpenJDK class library will be built from the
    sources found under the specified directory.  See "Building with
    the OpenJDK Class Library" below for details.
      default: not set

These flags determine the name of the directory used for the build.
The name always starts with ${platform}-${arch}, and each non-default
build option is appended to the name.  For example, a debug build with
bootimage enabled on Linux/i386 would be built in
build/linux-i386-debug-bootimage.  This allows you to build with
several different sets of options independently and even
simultaneously without doing a clean build each time.

If you are compiling for Windows, you may either cross-compile using
MinGW or build natively on Windows under MSYS or Cygwin.

Installing MSYS:

  1. Download and install the current MinGW and MSYS packages from
  mingw.org, selecting the C and C++ compilers when prompted.  Use the
  post-install script to create the filesystem link to the compiler.

  2. Download GNU Make 3.81 from the MSYS download page
  (make-3.81-MSYS-1.0.11-2.tar.bz2) and extract the tar file into
  e.g. c:/msys/1.0.

Installing Cygwin:

  1. Download and run setup.exe from cygwin.com, installing the base
  system and these packages: make, gcc-mingw-g++,
  mingw64-i686-gcc-g++, mingw64-x86_64-gcc-g++, and (optionally) git.

You may also find our win32 repository useful: (run this from the
directory containing the avian directory)

  $ git clone git://oss.readytalk.com/win32.git

This gives you the Windows JNI headers, zlib headers and library, and
a few other useful libraries like OpenSSL, libjpeg, and libpng.
There's also a win64 repository for 64-bit builds:

  $ git clone git://oss.readytalk.com/win64.git


Building with the Microsoft Visual C++ Compiler
-----------------------------------------------

You can also build using the MSVC compiler, which makes debugging with
tools like WinDbg and Visual Studio much easier.  Note that you will
still need to have GCC installed - MSVC is only used to compile the
C++ portions of the VM, while the assembly code and helper tools are
built using GCC.

The MSVC build has been tested with Visual Studio Express Edition
versions 8, 9, and 10.  Other versions may also work.

To build with MSVC, install Cygwin as described above and set the
following environment variables:

 $ export PATH="/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/IDE:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/BIN:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/Tools:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v3.5:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/VCPackages:/cygdrive/c/Program Files/Microsoft SDKs/Windows/v6.0A/bin:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS:/cygdrive/c/WINDOWS/System32/Wbem"

 $ export LIBPATH="C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;"

 $ export VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 9.0\VC"

 $ export LIB="C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib;"

 $ export INCLUDE="C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;C:\Program Files\Microsoft SDKs\Windows\v6.0A\include;"

Adjust these definitions as necessary according to your MSVC
installation.

Finally, build with the msvc flag set to the MSVC tool directory:

 $ make msvc="/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC"


Building with the OpenJDK Class Library
---------------------------------------

By default, Avian uses its own lightweight class library.  However,
that library only contains a relatively small subset of the classes
and methods included in the JRE.  If your application requires
features beyond that subset, you may want to tell Avian to use
OpenJDK's class library instead.  To do so, specify the directory
where OpenJDK is installed, e.g.:

 $ make openjdk=/usr/lib/jvm/java-7-openjdk

This will build Avian as a conventional JVM (e.g. libjvm.so) which
loads its boot class library and native libraries (e.g. libjava.so)
from /usr/lib/jvm/java-7-openjdk/jre at runtime.  To run an
application in this configuration, you'll need to make sure the VM is
in your library search path.  For example:

 $ LD_LIBRARY_PATH=build/linux-x86_64-openjdk \
     build/linux-x86_64-openjdk/avian-dynamic -cp /path/to/my/application \
     com.example.MyApplication

Alternatively, you can enable a stand-alone build using OpenJDK by
specifying the location of the OpenJDK source code, e.g.:

 $ make openjdk=$(pwd)/../jdk7/build/linux-amd64/j2sdk-image \
     openjdk-src=$(pwd)/../jdk7/jdk/src

You must ensure that the path specified for openjdk-src does not have
any spaces in it; make gets confused when dependency paths include
spaces, and we haven't found away around that except to avoid paths
with spaces entirely.

The result of such a build is a self-contained binary which does not
depend on external libraries, jars, or other files.  In this case, the
specified paths are used only at build time; anything needed at
runtime is embedded in the binary.  Thus, the process of running an
application is simplified:

 $ build/linux-x86_64-openjdk-src/avian -cp /path/to/my/application \
     com.example.MyApplication

Note that the resulting binary will be very large due to the size of
OpenJDK's class library.  This can be mitigated using UPX, preferably
an LZMA-enabled version:

 $ upx --lzma --best build/linux-x86_64-openjdk-src/avian

You can reduce the size futher for embedded builds by using ProGuard
and the supplied openjdk.pro configuration file (see "Embedding with
ProGuard and a Boot Image" below).  Note that you'll still need to use
vm.pro in that case -- openjdk.pro just adds additional constraints
specific to the OpenJDK port.  Also see app.mk in
git://oss.readytalk.com/avian-swt-examples.git for an example of using
Avian, OpenJDK, ProGuard, and UPX in concert.

Here are some examples of how to install OpenJDK and build Avian with
it on various OSes:

  Debian-based Linux:
    # conventional build:
    apt-get install openjdk-7-jdk
    make openjdk=/usr/lib/jvm/java-7-openjdk test

    # stand-alone build:
    apt-get install openjdk-7-jdk
    apt-get source openjdk-7-jdk
    apt-get build-dep openjdk-7-jdk
    (cd openjdk-7-7~b147-2.0 && dpkg-buildpackage)
    make openjdk=/usr/lib/jvm/java-7-openjdk \
      openjdk-src=$(pwd)/openjdk-7-7~b147-2.0/build/openjdk/jdk/src \
      test

  Mac OS X:
    # Prerequisite: build OpenJDK 7 according to
    # https://wikis.oracle.com/display/OpenJDK/Mac+OS+X+Port

    # conventional build:
    make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image test

    # stand-alone build:
    make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image \
      openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test

  Windows (Cygwin):
    # Prerequisite: build OpenJDK 7 according to
    # http://weblogs.java.net/blog/simonis/archive/2011/10/28/yaojowbi-yet-another-openjdk-windows-build-instruction

    # conventional build:
    make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image test

    # stand-alone build:
    make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image \
      openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test

Currently, only OpenJDK 7 is supported.  Later versions might work,
but have not yet been tested.


Installing
----------

Installing Avian is as simple as copying the executable to the desired
directory:

 $ cp build/${platform}-${arch}/avian ~/bin/


Embedding
---------

The following series of commands illustrates how to produce a
stand-alone executable out of a Java application using Avian.

Note: if you are building on Cygwin, prepend "x86_64-w64-mingw32-" or
"i686-w64-mingw32-" to the ar, g++, gcc, strip, and dlltool commands
below (e.g. x86_64-w64-mingw32-gcc).

Step 1: Build Avian, create a new directory, and populate it with the
VM object files and bootstrap classpath jar.

 $ make
 $ mkdir hello
 $ cd hello
 $ ar x ../build/${platform}-${arch}/libavian.a
 $ cp ../build/${platform}-${arch}/classpath.jar boot.jar

Step 2: Build the Java code and add it to the jar.

 $ cat >Hello.java <<EOF
public class Hello {
  public static void main(String[] args) {
    System.out.println("hello, world!");
  }
}
EOF
 $ javac -bootclasspath boot.jar Hello.java
 $ jar u0f boot.jar Hello.class

Step 3: Make an object file out of the jar.

 $ ../build/${platform}-${arch}/binaryToObject/binaryToObject boot.jar boot-jar.o \
     _binary_boot_jar_start _binary_boot_jar_end ${platform} ${arch}

Step 4: Write a driver which starts the VM and runs the desired main
method.  Note the bootJar function, which will be called by the VM to
get a handle to the embedded jar.  We tell the VM about this jar by
setting the boot classpath to "[bootJar]".

 $ cat >embedded-jar-main.cpp <<EOF
#include "stdint.h"
#include "jni.h"

#if (defined __MINGW32__) || (defined _MSC_VER)
#  define EXPORT __declspec(dllexport)
#else
#  define EXPORT __attribute__ ((visibility("default"))) \
  __attribute__ ((used))
#endif

#if (! defined __x86_64__) && ((defined __MINGW32__) || (defined _MSC_VER))
#  define SYMBOL(x) binary_boot_jar_##x
#else
#  define SYMBOL(x) _binary_boot_jar_##x
#endif

extern "C" {

  extern const uint8_t SYMBOL(start)[];
  extern const uint8_t SYMBOL(end)[];

  EXPORT const uint8_t*
  bootJar(unsigned* size)
  {
    *size = SYMBOL(end) - SYMBOL(start);
    return SYMBOL(start);
  }

} // extern "C"

int
main(int ac, const char** av)
{
  JavaVMInitArgs vmArgs;
  vmArgs.version = JNI_VERSION_1_2;
  vmArgs.nOptions = 1;
  vmArgs.ignoreUnrecognized = JNI_TRUE;

  JavaVMOption options[vmArgs.nOptions];
  vmArgs.options = options;

  options[0].optionString = const_cast<char*>("-Xbootclasspath:[bootJar]");

  JavaVM* vm;
  void* env;
  JNI_CreateJavaVM(&vm, &env, &vmArgs);
  JNIEnv* e = static_cast<JNIEnv*>(env);

  jclass c = e->FindClass("Hello");
  if (not e->ExceptionCheck()) {
    jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V");
    if (not e->ExceptionCheck()) {
      jclass stringClass = e->FindClass("java/lang/String");
      if (not e->ExceptionCheck()) {
        jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0);
        if (not e->ExceptionCheck()) {
          for (int i = 1; i < ac; ++i) {
            e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i]));
          }
          
          e->CallStaticVoidMethod(c, m, a);
        }
      }
    }
  }

  int exitCode = 0;
  if (e->ExceptionCheck()) {
    exitCode = -1;
    e->ExceptionDescribe();
  }

  vm->DestroyJavaVM();

  return exitCode;
}
EOF

on Linux:
 $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
     -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o

on Mac OS X:
 $ g++ -I$JAVA_HOME/include -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp \
     -o main.o

on Windows:
 $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 \
     -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o

Step 5: Link the objects produced above to produce the final
executable, and optionally strip its symbols.

on Linux:
 $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello
 $ strip --strip-all hello

on Mac OS X:
 $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello -framework CoreFoundation
 $ strip -S -x hello

on Windows:
 $ dlltool -z hello.def *.o
 $ dlltool -d hello.def -e hello.exp
 $ g++ hello.exp *.o -L../../win32/lib -lmingwthrd -lm -lz -lws2_32 \
     -mwindows -mconsole -o hello.exe
 $ strip --strip-all hello.exe


Embedding with ProGuard and a Boot Image
----------------------------------------

The following illustrates how to embed an application as above, except
this time we preprocess the code using ProGuard and build a boot image
from it for quicker startup.  The pros and cons of using ProGuard are
as follow:

 * Pros: ProGuard will eliminate unused code, optimize the rest, and
   obfuscate it as well for maximum space savings

 * Cons: increased build time, especially for large applications, and
   extra effort needed to configure it for applications which rely
   heavily on reflection and/or calls to Java from native code

For boot image builds:

 * Pros: the boot image build pre-parses all the classes and compiles
   all the methods, obviating the need for JIT compilation at runtime.
   This also makes garbage collection faster, since the pre-parsed
   classes are never visited.

 * Cons: the pre-parsed classes and AOT-compiled methods take up more
   space in the executable than the equivalent class files.  In
   practice, this can make the executable 30-50% larger.  Also, AOT
   compilation does not yet yield significantly faster or smaller code
   than JIT compilation.  Finally, floating point code may be slower
   on 32-bit x86 since the compiler cannot assume SSE2 support will be
   available at runtime, and the x87 FPU is not supported except via
   out-of-line helper functions.

Note you can use ProGuard without using a boot image and vice-versa,
as desired.

The following instructions assume we are building for Linux/i386.
Please refer to the previous example for guidance on other platforms.

Step 1: Build Avian, create a new directory, and populate it with the
VM object files.

 $ make bootimage=true
 $ mkdir hello
 $ cd hello
 $ ar x ../build/linux-i386-bootimage/libavian.a

Step 2: Create a stage1 directory and extract the contents of the
class library jar into it.

 $ mkdir stage1
 $ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar)

Step 3: Build the Java code and add it to stage1.

 $ cat >Hello.java <<EOF
public class Hello {
  public static void main(String[] args) {
    System.out.println("hello, world!");
  }
}
EOF
 $ javac -bootclasspath stage1 -d stage1 Hello.java

Step 4: Create a ProGuard configuration file specifying Hello.main as
the entry point.

 $ cat >hello.pro <<EOF
-keep class Hello {
   public static void main(java.lang.String[]);
 }
EOF

Step 5: Run ProGuard with stage1 as input and stage2 as output.

 $ java -jar ../../proguard4.6/lib/proguard.jar \
     -dontusemixedcaseclassnames -injars stage1 -outjars stage2 \
     @../vm.pro @hello.pro

(note: The -dontusemixedcaseclassnames option is only needed when
building on systems with case-insensitive filesystems such as Windows
and OS X.  Also, you'll need to add -ignorewarnings if you use the
OpenJDK class library since the openjdk-src build does not include all
the JARs from OpenJDK, and thus ProGuard will not be able to resolve
all referenced classes.  If you actually plan to use such classes at
runtime, you'll need to add them to stage1 before running ProGuard.
Finally, you'll need to add @../openjdk.pro to the above command when
using the OpenJDK library.)

Step 6: Build the boot and code images.

 $ ../build/linux-i386-bootimage/bootimage-generator
    -cp stage2 \
    -bootimage bootimage-bin.o \
    -codeimage codeimage-bin.o

Note that you can override the default names for the start and end
symbols in the boot/code image by also passing:

    -bootimage-symbols my_bootimage_start:my_bootimage_end \
    -codeimage-symbols my_codeimage_start:my_codeimage_end

Step 7: Write a driver which starts the VM and runs the desired main
method.  Note the bootimageBin function, which will be called by the
VM to get a handle to the embedded boot image.  We tell the VM about
this function via the "avian.bootimage" property.

Note also that this example includes no resources besides class files.
If our application loaded resources such as images and properties
files via the classloader, we would also need to embed the jar file
containing them.  See the previous example for instructions.

 $ cat >bootimage-main.cpp <<EOF
#include "stdint.h"
#include "jni.h"

#if (defined __MINGW32__) || (defined _MSC_VER)
#  define EXPORT __declspec(dllexport)
#else
#  define EXPORT __attribute__ ((visibility("default")))
#endif

#if (! defined __x86_64__) && ((defined __MINGW32__) || (defined _MSC_VER))
#  define BOOTIMAGE_BIN(x) binary_bootimage_bin_##x
#  define CODEIMAGE_BIN(x) binary_codeimage_bin_##x
#else
#  define BOOTIMAGE_BIN(x) _binary_bootimage_bin_##x
#  define CODEIMAGE_BIN(x) _binary_codeimage_bin_##x
#endif

extern "C" {

  extern const uint8_t BOOTIMAGE_BIN(start)[];
  extern const uint8_t BOOTIMAGE_BIN(end)[];

  EXPORT const uint8_t*
  bootimageBin(unsigned* size)
  {
    *size = BOOTIMAGE_BIN(end) - BOOTIMAGE_BIN(start);
    return BOOTIMAGE_BIN(start);
  }

  extern const uint8_t CODEIMAGE_BIN(start)[];
  extern const uint8_t CODEIMAGE_BIN(end)[];

  EXPORT const uint8_t*
  codeimageBin(unsigned* size)
  {
    *size = CODEIMAGE_BIN(end) - CODEIMAGE_BIN(start);
    return CODEIMAGE_BIN(start);
  }

} // extern "C"

int
main(int ac, const char** av)
{
  JavaVMInitArgs vmArgs;
  vmArgs.version = JNI_VERSION_1_2;
  vmArgs.nOptions = 2;
  vmArgs.ignoreUnrecognized = JNI_TRUE;

  JavaVMOption options[vmArgs.nOptions];
  vmArgs.options = options;

  options[0].optionString
    = const_cast<char*>("-Davian.bootimage=bootimageBin");

  options[1].optionString
    = const_cast<char*>("-Davian.codeimage=codeimageBin");

  JavaVM* vm;
  void* env;
  JNI_CreateJavaVM(&vm, &env, &vmArgs);
  JNIEnv* e = static_cast<JNIEnv*>(env);

  jclass c = e->FindClass("Hello");
  if (not e->ExceptionCheck()) {
    jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V");
    if (not e->ExceptionCheck()) {
      jclass stringClass = e->FindClass("java/lang/String");
      if (not e->ExceptionCheck()) {
        jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0);
        if (not e->ExceptionCheck()) {
          for (int i = 1; i < ac; ++i) {
            e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i]));
          }
          
          e->CallStaticVoidMethod(c, m, a);
        }
      }
    }
  }

  int exitCode = 0;
  if (e->ExceptionCheck()) {
    exitCode = -1;
    e->ExceptionDescribe();
  }

  vm->DestroyJavaVM();

  return exitCode;
}
EOF

 $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
     -D_JNI_IMPLEMENTATION_ -c bootimage-main.cpp -o main.o

Step 8: Link the objects produced above to produce the final
executable, and optionally strip its symbols.

 $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello
 $ strip --strip-all hello


Trademarks
----------

Oracle and Java are registered trademarks of Oracle and/or its
affiliates.  Other names may be trademarks of their respective owners.

The Avian project is not affiliated with Oracle.
Something went wrong with that request. Please try again.