Running Operon from Java

To use Operon with Java, the Java version 11 or later is required. If you are using maven, please add the repository to your project's pom.xml.

  <repositories>
      <!-- For operon-runner -->
      <repository>
          <id>operon-site</id>
          <url>https://operon.io/maven-repository</url>
      </repository>
  </repositories>
The Operon-dependency is as follows:
  <dependency>
    <groupId>io.operon</groupId>
    <artifactId>operon-runner</artifactId>
    <version>0.6.0-SNAPSHOT</version>
  </dependency>

Class OperonRunner provides an easy interface to run queries:

  • public static OperonValue doQuery(String query) throws OperonGenericException
  • public static OperonValue doQueryWithInitialValue(String query, OperonValue initialValue) throws OperonGenericException
  • public static OperonValue doQueryWithInitialValue(String query, String initialValueStr) throws OperonGenericException

Please find the Javadoc for latest-version from here.

Example

// Copyright (C) 2021, Operon.io
// MIT-LICENSE
String query = "$: {\"foo\": \"bar\"} Select: $.foo";
OperonValue result = OperonRunner.doQuery(query);
// Result is "bar"
System.out.println("Result: " + result.toString());

Example

// Copyright (C) 2021, Operon.io
// MIT-LICENSE
// Your json data here (as Java-String), sourced from e.g. API:
String jsonData = "{\"foo\": \"bar\"}";
OperonValue initialValue = JsonUtil.operonValueFromString(jsonData);
String query = "Select: $.foo";
OperonValue result = OperonRunner.doQueryWithInitialValue(query, initialValue);
System.out.println("Result: " + result.toString());

Example

// Copyright (C) 2021, Operon.io
// MIT-LICENSE

// Your json data here (as Java-String), sourced from e.g. API:
String jsonData = "{\"foo\": \"bar\"}";
String query = "Select: $.foo";
OperonValue result = OperonRunner.doQueryWithInitialValue(query, jsonData);
System.out.println("Result: " + result.toString());

If we know that the result is e.g. a String:

// Copyright (C) 2021, Operon.io
// MIT-LICENSE
String query = "$: {\"foo\": \"bar\"} Select <String>: $.foo";
StringType result = (StringType) OperonRunner.doQuery(query);

// Result is "bar":
System.out.println("Result: " + result.toString());

// Result is bar (without quotes):
System.out.println("Result: " + result.getJavaStringValue());

Overriding a Value from the query

// Copyright (C) 2021, Operon.io
// MIT-LICENSE
String jsonData = "{\"foo\": \"bar\"}";
String jsonData2 = "{\"bin\": \"bai\"}";
String query = "Select: $myValue.foo + $b.bin";
OperonConfigs configs = new OperonConfigs();
OperonValue jsonObj = JsonUtil.operonValueFromString(jsonData);
OperonValue jsonObj2 = JsonUtil.operonValueFromString(jsonData2);
configs.setNamedValue("$myValue", jsonObj);
configs.setNamedValue("$b", jsonObj2);
OperonContext ctx = OperonRunner.createNewOperonContext(query, "myQuery", configs);
ctx.start(OperonContextManager.ContextStrategy.SINGLETON);
OperonValue result = ctx.getOutputOperonValue();
System.out.println("Result: " + result.toString());
NOTE: it is not possible to override value from query which has update-type "NEVER". Here's another way to do this without using OperonConfigs:
// Copyright (C) 2021, Operon.io
// MIT-LICENSE
String jsonData = "{\"foo\": \"bar\"}";
String query = "$myValue: \"\"; Select: $myValue.foo";
OperonConfigs configs = null; // not required
OperonContext ctx = OperonRunner.createNewOperonContext(query, "myQuery", configs);
OperonValue jsonObj = JsonUtil.operonValueFromString(jsonData);
OperonRunner.bindValue(ctx, "$myValue", jsonObj);
ctx.start(OperonContextManager.ContextStrategy.SINGLETON);
OperonValue result = ctx.getOutputOperonValue();
System.out.println("Result: " + result.toString());

Full Java-application example

Prerequisites:

To run this application you need to have the following project-setup:

<dir>
   |
   +--src
   |   |
   |    +--main
   |       |
   |	   +--java
   |            |
   |            +--org
   |                |
   |                +--acme
   |                     |
   |                     +--JavaApp.java
   |
   +--pom.xml

File: /src/main/java/org/acme/JavaApp.java

// Copyright (C) 2021, Operon.io
// MIT-LICENSE
package org.acme;

import io.operon.runner.node.type.*;
import io.operon.runner.model.OperonConfigs;
import io.operon.runner.OperonRunner;

/**
 * A basic example running as public static void main.
 */
public final class JavaApp {

    public static void main(String[] args) throws Exception {
        String query= "Select: -> http:{\"url\": \"https://api.coindesk.com/v1/bpi/currentprice.json\"}.body..EUR[1].rate_float";

        OperonConfigs configs = new OperonConfigs();
        configs.setOutputResult(false);
        OperonValue result = OperonRunner.doQuery(query, configs);
        
        // Result is the current Bitcoin-price in EUR-currency:
        System.out.println("Result: " + result.toString());
    }
}

pom.xml for Maven

Place the contents in: /pom.xml

  • First run "mvn clean install"
  • Run "mvn exec:java" to run with dependencies
  • This pom.xml creates also jar-file which has all the dependencies included, "uber-java-operon-example-1.0-SNAPSHOT.jar". You can run it simply with "java -jar uber-java-operon-example-1.0-SNAPSHOT.jar".
<?xml version="1.0" encoding="UTF-8"?>
<!--
   Copyright (C) 2021, Operon.io
   MIT-LICENSE
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.acme</groupId>
    <artifactId>java-operon-example</artifactId>
    <packaging>jar</packaging>
    <name>Java :: Operon Example</name>
    <description>Java-Operon example</description>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <category>Starter</category>
    </properties>

    <repositories>
        <!-- For operon-runner -->
        <repository>
            <id>operon-site</id>
            <url>https://operon.io/maven-repository</url>
        </repository>
    </repositories>

    <dependencies>

        <!-- the good stuff -->
        <dependency>
          <groupId>io.operon</groupId>
          <artifactId>operon-runner</artifactId>
          <version>0.6.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <release>11</release>
                </configuration>
            </plugin>

            <!--
              Make this jar executable
              -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <!-- DO NOT include log4j.properties file in your Jar -->
                    <excludes>
                        <exclude>**/log4j.properties</exclude>
                    </excludes>
                    <archive>
                        <manifest>
                            <!-- Jar file entry point -->
                            <mainClass>org.acme.JavaApp</mainClass>
                        </manifest>
                        <manifestEntries>
                            <Built-By>acme.org</Built-By>
                            <Build-Jdk>11</Build-Jdk>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

            <!--
              With this plugin the all dependencies are included in the
              "uber" -jar, so it can be run standalone.
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <finalName>uber-${project.artifactId}-${project.version}</finalName>
                </configuration>
            </plugin>

            <!-- 
              First run "mvn clean install", then with
              "mvn exec:java" you can run the application,
              if you prefer this method.
              This command finds also the required dependencies.
              -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.acme.JavaApp</mainClass>
                </configuration>
            </plugin>

        </plugins>

    </build>

</project>

Calling Java code from Operon

There are two ways to call Java-code from Operon:

  • Using Operon-components created with Java
  • Using "call" -component.

In this example we will use the call-component, building on previous application. We will add a BtcAnalyzer -class, as shown in the structure below:

<dir>
   |
   +--src
   |   |
   |    +--main
   |       |
   |	   +--java
   |            |
   |            +--org
   |                |
   |                +--acme
   |                     |
   |                     +--JavaApp.java
   |                     +--BtcAnalyzer.java
   |
   +--pom.xml

This is how BTC-Analyzer looks like:

package org.acme;

import io.operon.runner.node.type.*;
import io.operon.runner.model.exception.OperonGenericException;
import io.operon.runner.OperonFunction;
import io.operon.runner.statement.Statement;

public class BtcAnalyzer implements OperonFunction {
    public OperonValue execute(Statement stmt, OperonValue params) throws OperonGenericException {
        System.out.println("Analyzing...");
        NumberType num = (NumberType) params.evaluate();
        StringType result = null;
        if (num.getDoubleValue() < 10000) {
            result = StringType.create(stmt, "BUY");
        }
        else {
            result = StringType.create(stmt, "HODL");
        }
        return result;
    }
}

Let's modify the query from our previous application:

String query= "Select: -> http:{\"url\": \"https://api.coindesk.com/v1/bpi/currentprice.json\"}.body..EUR[1].rate_float -> call:{\"id\": \"analyze\", \"params\": @}";
What is new here is the "-> call" -part, where we want to call the BtcAnalyzer and pass it the numeric value we have obtained from the coindesk's Bitcoin price-API. Note that we could also omit the "id" -option and place it in the component-id: -> call:analyze:{\"params\": @}.

The final step is to register the BTC-Analyzer to Operon (in the static void main -method):

BtcAnalyzer analyzer = new BtcAnalyzer();
OperonRunner.registerFunction("analyze", analyzer);
// ... create configs and do query