Java bridge

Contents

JavaScript developers may optionally invoke and use Java-based code, since the XP runtime builds on the JVM.

The __ object

In Enonic XP, there is a global object named __ (double underscore), accessible from any serverside JavaScript code, which provides a way to wrap Java objects in a JavaScript object. The __ object has functions that allow JavaScript to communicate with Java classes. The newBean function will wrap the Java object named in the parameter, for instance:

const bean = __.newBean('com.enonic.xp.lib.io.IOHandlerBean');

This line is from the lib-io library, which is a good example of how this is used. In the Java IOHandlerBean class, there are several methods, like the readLines method:

public List<String> readLines( final Object value )
  throws Exception
    {
      final CharSource source = toCharSource( value );
      return source.readLines();
    }

This method is now accessible as a function on the JavaScript bean and may be invoked from JavaScript, like this:

export function readLines(stream) {
  return __.toNativeObject(bean.readLines(stream));
}

This results in a global JavaScript function readLines. This example also shows the use of the toNativeObject method, which in this case, converts a Java String array to a JSON object.

Java objects constructed by newBean may, but not required to, implement com.enonic.xp.script.bean.ScriptBean interface. ScriptBean::initialize is used to inject com.enonic.xp.script.bean.BeanContext object, which can be used to access the application and resource keys, as well as current Request, Context and XP Java API Services.

Passing parameters to Java

There are 2 ways to pass parameters to a Java method, from JavaScript:

  • Passing the parameters in the method call

  • Calling a setter on the Java object for each parameter, and then calling the method without parameters

The first one is recommended when there are few parameters (1 or 2) and of simple types. The second one is better when there are multiple parameters, or some of them are optional.

export function doSomething(param1, param2) {
    const bean = __.newBean('com.enonic.lib.mylib.MyClass');

    return bean.execute(param1, param2);
}

export function doSomethingElse(params) {
    const bean = __.newBean('com.enonic.lib.mylib.MyClass');

    bean.setText(__.nullOrValue(params.text) || '');
    bean.setSize(__.nullOrValue(params.size) || 250);

    return bean.execute();
}
When passing values that might be null or undefined it is recommended to filter them using the __.nullOrValue built-in function. This function converts any value that is null or undefined in JavaScript to null in Java. Otherwise returns the input value without changes.

The Java object must implement a setter method for each parameter passed that way.

The setter must be called. Nashorn also accepted an assignment to a property named after the setter and rewrote it into a call to that setter, but GraalJS exposes no such property, and the assignment fails with an Unknown identifier error. See Java interop.

bean.setText('value');   // supported on both engines
bean.text = 'value';     // Nashorn only; fails on GraalJS

The Java class used in the example above looks like this:

package com.enonic.lib.mylib;

public final class MyClass {
    private String text;
    private Long size;

    public String something( String param1, Long param2 ) {
        return "Parameters: " + param1 + " " + param2;
    }

    public String somethingElse() {
        return "Parameters: " + this.text + " " + this.size;
    }

    public void setText( String text ) {
        this.text = text;
    }

    public void setSize( Long size ) {
        this.size = size;
    }
}

Parameter conversions

There are some type conversions that are made when calling from JavaScript to Java:

  • when passing a JavaScript string, the Java method should expect a Java String

  • when passing a JavaScript boolean, the Java method should expect a Java Boolean

  • when passing a JavaScript number, the Java method should expect a Java Long, Integer or Double

  • when passing a JavaScript array, the Java method should expect a Java List

  • when passing a JavaScript object, the Java method should expect a Java Map<String, Object>

The argument must already be of the type the method declares. Nashorn coerced it where the two differed; GraalJS does not. HTTP request parameters, for instance, always arrive as strings and must be parsed before they are passed to a parameter declared as a number. Objects and arrays are handed over as a Java map or list by converting them first.

bean.setSize(parseInt(req.params.size, 10));       // a Java Long
bean.setConfig(__.toScriptValue(params.config));   // a Java Map

Returning results from Java

When returning simple values from Java to a JavaScript caller, the same type conversions applies.

Complex values are not converted. A Java map returned to JavaScript remains a Java object, and inspecting it with the standard JavaScript operations reports the methods of its class rather than its entries. It is converted once, in the module that makes the call, as the readLines example above does.

const config = __.toNativeObject(bean.getConfig());

To return complex object values, you should create a specific Java class to make the mapping. This class should implement the MapSerializable interface. It will implement the serialize method, which allows generating a JSON object.

Serialization example
package com.enonic.lib.mylib;

import java.util.Map;

import com.enonic.xp.script.serializer.MapGenerator;
import com.enonic.xp.script.serializer.MapSerializable;

public final class ExampleObjectMapper
    implements MapSerializable
{
    private final String text;

    private final Object[] array;

    private final Map<String, Object> object;

    public ExampleObjectMapper( String text, Object[] array, Map<String, Object> object )
    {
        this.text = text;
        this.array = array;
        this.object = object;
    }

    @Override
    public void serialize( MapGenerator gen )
    {
        gen.value( "text", text );
        gen.array( "arrayValues" );
        for ( Object value : array )
        {
            gen.value( value );
        }
        gen.end();

        gen.map( "objectValues" );
        for ( String key : object.keySet() )
        {
            gen.value( key, object.get( key ) );
        }
        gen.end();
    }
}

Finally, for returning values of type binary stream from Java, it should be wrapped on a ByteSource object. This is required by XP to allow returning it from an HTTP request, or add it as a content attachment.

Functions

disposer

Add a disposer that is called when the app is stopped.

XPXP8.1.08.1.0 Register disposers while the application is starting — from main.ts, or from a module it loads during start-up. Registering from anywhere else is not supported.
GraalJS is a preview preview preview engine and not for production — see GraalJS. Follow the start-up rule above regardless: it is the portable one.
Nashorn keeps only the most recent registration per module, so a module that calls __.disposer twice loses the first callback. Combine clean-up into a single disposer.

Parameters

Name Kind Details

func

Function to call

Returns

void

newBean

Creates a new JavaScript bean that wraps the given Java class and makes it’s methods available to be called from JavaScript.

Parameters

Name Kind Details

name

Classname for bean to create

Returns

void

nullOrValue

Converts a JavaScript variable that is undefined to a Java null object. If the JavaScript variable is defined, it is returned as is.

Parameters

Name Kind Details

value

Value to convert

Returns

void

registerMock

Registers a mock

Parameters

Name Kind Details

name

Name of mock

value

Value to register

Returns

void

toNativeObject

Converts arrays or complex Java objects to JSON.

Parameters

Name Kind Details

value

Value to convert

Returns

void

toScriptValue

Converts JSON to a Java Map structure that can be used as parameters to a Java method on a bean created with newBean.

Parameters

Name Kind Details

value

Value to convert

Returns

void


Contents

Contents