We will use the Phylonco project as an example, to learn how to create the Java classes and module to extend LPhy and LPhyBEAST.

The extension “phylonco-lphy”

To create a LPhy extension, first you need to write your own classes to extend or implement the extensible components defined from the core. At the time of writing, LPhy makes three components extensible:

a) GenerativeDistribution

It is a Java interface to represent all types of generative distributions, such as probability distributions, tree generative distributions (e.g. Birth-death, Coalescent), and PhyloCTMC generative distributions.

b) Fun

It is an abstract class to represent all functions in LPhy language.

c) SequenceType

It is a Java interface in the jebl3 library, and stands for all sorts of data types, such as Nucleotide, Binary, and PhasedGenotype in this extension, etc.

The container provider class

The second step is to register your extended or implemented Java classes into the extension mechanism. This will involve two files: module-info.java and phylonco.lphy.spi.Phylonco.java. There is an extra provider configuration file located under the resources subfolder for Java 1.8 only.

phylonco-lphy
Figure 1: The extension "phylonco-lphy".

As you can see in Figure 1, phylonco.lphy.spi.Phylonco is a container class to provide services defined in the service provider interface (SPI), which lists all extended classes in the extension. It is also a service provider class, hence a public no-args constructor is required. Here we create a new concept, called as container provider class, to collect all extended classes in one class, so that we can avoid to create a no-args constructor for every classes.

When you create an extension, you need to give a sensible name to the container provider class in your extension, and place it into a package having the name similar to mypackage.lphy.spi.

Then, the line pointed by a red arrow in module-info.java declares the service provider class phylonco.lphy.spi.Phylonco implements SPI lphy.spi.LPhyExtension. Here SPI is the Java interface LPhyExtension.

Java 1.8

The 3rd file named as lphy.spi.LPhyExtension in the subfolder src/main/resources/META-INF/services is the provider configuration file used to register the service provider in Java 1.8. This allows the LPhy and its extensions to be able to integrate with the non-module system, such as BEAST 2 and its packages.

This file name is same as the SPI file name concatenated with its package, which is required by the mechanism.

Please note the recommended LPhy extension mechanism is using the Java module system and configuring the SPI and providers in the module-info.java file, which is a different mechanism comparing with the provider configuration file under the resources subfolder.

Naming conventions

Following Java package naming conventions is critical. Though we are using the module system to avoid namespace collision, it’d better to always name your Java package by starting with your extension name, but not the reserved core name, such as lphy, lphybeast, beast, etc. For example, here we have the package phylonco.lphy.evolution to contain the extended LPhy data types and models. The package phylonco.lphy.spi includes the container provider class.

The extension “phylonco-lphybeast”

To create a LPhyBEAST extension, first you need to write your own mapping classes to implement the interface ValueToBEAST and GeneratorToBEAST. The former maps a LPhy Value object to a BEASTInterface, the latter maps a LPhy Generator to a BEASTInterface.

Registering mapping classes

Then these mapping classes will be registered in a register class which has to implement the interface LPhyBEASTExt. This is similar to SPI, but is modified to use BEAST 2 class loader instead of ServiceLoader.

phylonco-lphybeast
Figure 2: The extension "phylonco-lphybeast".

The right side of Figure 2 shows the example of registering mapping classes in the extension “phylonco-lphybeast”. On the bottom of the register class, there are two lists to exclude the Value and Generator existing in LPhy, but cannot or do not need to create the mapping class to map the BEASTInterface in BEAST 2. Otherwise, the validation process may throw UnsupportedOperationException.

Because LPhy and BEAST 2 are not using the same classes for sequence types, you need to link them in the register class as well.

LPhyBEAST is also a BEAST 2 extension

As well as it is an extension of LPhyBEAST, phylonco-lphybeast is an extension of BEAST 2. The version.xml has to be used to declare the BEAST 2 package dependencies. The example code is below:

<addon name='phylonco' version='0.0.6'>
    <depends on='beast2' atleast='2.6.6'/>
    <depends on='BEASTLabs' atleast='1.9.7'/>
    <depends on='lphybeast' atleast='0.3.0'/>
</addon>