Class ResourceLibLoader

java.lang.Object
org.bzdev.lang.ResourceLibLoader

public class ResourceLibLoader extends Object
Resource-based library loader. This class is used to load native-method libraries that are bundled in a jar file, etc., containing an application. This is convenient in cases where these libraries are small and when it is convenient to package these with the application's classes. A naming convention is used to allow libraries for multiple operating systems and machine architectures to be bundled in the same jar file, as described in the documentation for the load method.

A ResourceLibLoader is initialized by providing it with an XML specification file that determines which resource names will be searched for library files. The DTD for this specification file is given below. The top level element is defined by

    <!ELEMENT libloaderspec (archdef | archfamily | osinfo)*>
 
An archdef element provides a mapping from an OS architecture name to the canonical name (the standard name to use as part of a library resource name.)
    <!ELEMENT archdef EMPTY>
    <!ATTLIST archdef
              name CDATA #REQUIRED
              cname CDATA #REQUIRED>
 
If archdef elements are not provided, the name provided by the OS is assumed to be the canonical name.

Architectures are sometimes grouped in families, where newer versions of a microprocessor have instruction sets compatible with older versions. The archfamily element defines such families, and consists a series of arch elements ordered from the newest architecture to the oldest.

    <!ELEMENT archfamily (arch)*>
 
The arch element defines an architecture than can be part of a family.
    <!ELEMENT arch  EMPTY>
    <!ATTLIST arch
              cname CDATA #REQUIRED>
 
The cname attribute provides the canonical name of the architecture. A particular architecture can have at most one older architecture associated with it. That is, if one archfamily element contains one arch element followed by another, a different archfamily element cannot contain the same arch element followed by a different arch element. An arch element must also appear only once in any given arch family. *

The osinfo element can be used to indicate the suffix used for the file and resource names of native-method libraries. Typical suffixes are ".so" and ".dll". A set of version elements contained in an osinfo element can be used to define a single version name for a number of versions. This is particularly useful for Linux systems, where the version changes whenever new kernel is provided. These elements are defined as follows:

    <!ELEMENT osinfo (version)*>
    <!ATTLIST osinfo
              name  CDATA #REQUIRED
              prefix CDATA #IMPLIED
                suffix CDATA #IMPLIED>
 
The name attribute is mandatory and specifies the OS name for the element. The suffix attribute provides a suffix string, and must contain a leading period ("."). Any number of nested version elements can be provided.
    <!ELEMENT version EMPTY>
    <!ATTLIST version
              pattern CDATA #REQUIRED
              replacement CDATA #REQUIRED>
 
Each version element contains a pattern (using the Java regular expression syntax) to match against the os.version system property. The replacement used is the one specified by the first version element whose pattern matches the version provided by System.getProperty("os.version").

The default specification file contains the following:

 <?xml version="1.0" ?>
 <libloaderspec>
   <archfamily>
     <arch cname="i686"/>
     <arch cname="i386"/>
   </archfamily>
   <osinfo name="Linux" suffix=".so">
     <version pattern="2\.4\..*" replacement="2.4"/>
     <version pattern="2\.5\..*" replacement=2.5"/>
     <version pattern="2\.6\..*" replacement=2.6"/>
     ...
     <version pattern="4\.0\..*" replacement=4.0"/>
     ...
     <version pattern="4\.14\..*" replacement=4.14"/>
   </osinfo>
 </libloaderspec>
 

The following code prints the order in which the software will search for a shared-library resource:


        org.bzdev.lang.ResourceLibLoader rl =
              new org.bzdev.lang.ResourceLibLoader();

        java.util.Iterator<String> it = rl.getResources("foo");
        while (it.hasNext()) {
            System.out.println(it.next());
        }
  }
 
Will create list the following resource names on a Linux system running Linux version 3.2.0-126-generic on an Intel i3 processor (Java's os.arch system property reports amd64 instead of the correct x86_64):

 libfoo-Linux-3.2.0-126-generic-amd64.so
 libfoo-Linux-3.2-amd64.so
 libfoo-Linux-amd64.so
 libfoo-amd64.so
 libfoo-Linux-3.2.0-126-generic-x86_64.so
 libfoo-Linux-3.2-x86_64.so
 libfoo-Linux-x86_64.so
 libfoo-x86_64.so
 libfoo-Linux-3.2.0-126-generic-i386.so
 libfoo-Linux-3.2-i386.so
 libfoo-Linux-i386.so
 libfoo-i386.so
 

The object files are stored as resources. For the example above, a resource with one or more of the names listed above should be used as the resource name. If the resource should start with a path, the path must be prepended to the library name. These paths contain names that are separated by a '/' (but may not start with a '/'). For example, when "com/foo/foo" is used as an argument to getResources(String) or load(String), the corresponding resource names are com/foo/libfoo-x86_64.so, com/foo/libfoo-Linux-x86_64.so, etc., depending on how specific the code is to a particular OS version.

Because the os.arch system property is not a reliable indication of the CPU, one should make conservative assumptions about which binaries to include (e.g., provide a binary for x86_64 rather than one for an amd64 system, unless you know that the jar file will only be used on an AMD system.)

While there is a default specification file, it is intended for cases where version-specific libraries are used primarily for work-arounds for bugs in specific OS versions or libraries: when using the default specification file, one will nearly always provide a resource that does not contain an OS version in its name. For more complex cases, a custom specification file should be used.

  • Constructor Details

  • Method Details

    • getResources

      public Iterator<String> getResources(String resource)
      Get the resource names that will be tried to find libraries. This method is used internally to generate candidate resource names. If the values produced by the iterator are strings so they may be printed. This is useful for debugging new spec files and to debug problems regarding library resource names. For systems in which shared library names always start with a prefix (e.g., "lib" on Unix and Linux systems), the prefix must be omitted.
      Parameters:
      resource - a resource name for a library without the os, version, and architecture substrings (which are described in the load method)
      Returns:
      an iterator that will produce the resources that will be tried in an appropriate order.
    • load

      public void load(String resource) throws UnsatisfiedLinkError, SecurityException
      Load a class from a resource. Several resource names will be tried and the first one that matches a resource will be used to load a library. The resource names are constructed from the argument and from substrings based on the Java system properties os.name, os.version, and os.arch. The first resource name tried is composed of the prefix (which may be an empty string), followed by the base resource name provided in the argument, followed by a '-' character, followed by the OS name and then another '-' character, followed by the version and then another '-' character, and finally followed by the OS architecture,in turn followed by the suffix. If a replacement for the version is provided in the configuration file, both the version and the replacement are tried, the actual version first.

      The next resource name is composed of the the prefix, followed by the base resource name provided in the argument, followed by a '-' character, followed by the OS name, again followed by a '-' character, and then followed by the OS architecture, which is in turn followed by the suffix. The last resource names starts with the prefix, followed by the base resource name, followed by a '-' character, and finally followed by the OS architecture, in turn followed by the suffix.

      In all cases, the suffix is determined by the name of the operating system. If a suffix for the OS is not specified in the xml configuration file, the suffix defaults to ".dll" for any OS whose name starts with "Windows" and ".so" otherwise. Similarly, if a prefix is not defined, it defaults to the empty string for any OS whose name starts with Windows and to "lib" for all other OS names. Both defaults can be overridden in the configuration specification.

      The sequence described above will be repeated for each architecture that is an ancestor of the architecture corresponding to the system property "os.arch": the archfamily directives describe a collection of trees with each directive describing a sequence of nodes in a tree leading towards the root of the tree.

      The OS name, OS version and OS architecture are by default the values of the os.name, os.version, and os.arch system properties. If a mapping is defined by the archdef directive, however, the architecture name will be replace in all cases with a canonical version of this name.

      For example, if the specification maps the version string to 2.4 for all 2.4 version, then with Linux 2.4.X running on an i386 system, the resource names tried for foo will be libfoo-Linux-2.4.20-6-i386.so, libfoo-Linux-2.4-i386.so, libfoo-Linux-i386.so and libfoo-i386.so, followed by libfoo.so itself. On a windows system, the ".so" suffix will be replaced with a ".dll" suffix. On a Linux system where the system property os.arch is i686, using the default specification file, the resource names that will be tried are libfoo-Linux-2.4.20-6-i686.so, libfoo-Linux-2.4-i686.so, libfoo-Linux-i686.so, libfoo-Linux-2.4.20-6-i386.so, libfoo-Linux-2.4-i386.so, libfoo-Linux-i386.so and libfoo-i386.so. Two architectures appear because the specification defines a family of architectures, so all are tried.

      The resource as specified should not end in a suffix (e.g., ".so" on Linux) and should not contain a prefix as part of the resource name (e.g., "lib"on Linux): those are provided by the load(String) method. If detailed information is not available, the default suffixes are ".dll" for Windows systems and ".so" for Linux/Unix systems.

      Parameters:
      resource - a string naming the resource (the name for a shared library) excluding the OS, version, and architecture components, but excluding the suffix and prefix.
      Throws:
      UnsatisfiedLinkError - the library could not be loaded.
      SecurityException - the library could not be loaded.