JavaScript

JavaScript is not compiled into by code but there are available JavaScript engines which runs on JVM. Example of such a engine is Nashorn (it is available by default since Java 8).

Native microservice

You can use JavaScript engine to execute service definition which will be written in JavaScript. But it is necessary to include both languages: Java and JavaScript.

Project structure and dependencies

This is standard structure of Java project with Maven. The structure should look like this:

+--+ additional-files
|  |
|  +--- configuration.yml
|  |
|  +--- log4j2.xml
|
+--+ src
|  |
|  +--+ main
|  |  |
|  |  +--+ java
|  |  |  |
|  |  |  +--+ com
|  |  |     |
|  |  |     +--+ example
|  |  |        |
|  |  |        +--+ configuration
|  |  |        |  |
|  |  |        |  +--+ JavaScriptHelloWorldJLupinConfiguration.java
|  |  |        |  |
|  |  |        |  +--+ JavaScriptHelloWorldSpringConfiguration.java
|  |  |        |
|  |  |        +--+ service
|  |  |           |
|  |  |           +--+ impl
|  |  |           |  |
|  |  |           |  +--+ ExampleServiceImpl.java
|  |  |           |
|  |  |           +--+ interfaces
|  |  |              |
|  |  |              +--+ ExampleService.java
|  |  |
|  |  +--+ js
|  |     |
|  |     +--+ com
|  |        |
|  |        +--+ example
|  |           |
|  |           +--+ service
|  |              |
|  |              +--+ impl
|  |                 |
|  |                 +--+ ExampleServiceImpl.js
|  |
|  +--+ test
|     |
|     +--+ java
|
+--- pom.xml

Configure your pom (pom.xml):

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>jlupin-platform-parent</artifactId>
        <groupId>com.jlupin</groupId>
        <version>1.6.0.2</version>
    </parent>

    <name>javascript-hello-world</name>
    <groupId>com.example</groupId>
    <artifactId>javascript-hello-world-implementation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <repositories>
        <!-- Repository is also accessible using https connection: -->
        <!-- https://support.jlupin.com/maven2/ -->
        <repository>
            <id>jlupin-central</id>
            <name>jlupin</name>
            <url>http://support.jlupin.com/maven2/</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <!-- Repository is also accessible using https connection: -->
        <!-- https://support.jlupin.com/maven2/ -->
        <pluginRepository>
            <id>jlupin-central</id>
            <name>jlupin</name>
            <url>http://support.jlupin.com/maven2/</url>
        </pluginRepository>
    </pluginRepositories>

    <dependencies>
        <dependency>
            <groupId>com.jlupin</groupId>
            <artifactId>jlupin-platform-native</artifactId>
        </dependency>
    </dependencies>

    <build>
        <resources>
            <resource>
                <directory>src/main/js</directory>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>com.jlupin</groupId>
                <artifactId>jlupin-platform-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>jlupin-repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>jlupin-zip</id>
                        <goals>
                            <goal>zip</goal>
                        </goals>
                        <configuration>
                            <additionalFilesDirectories>
                                <param>additional-files</param>
                            </additionalFilesDirectories>
                        </configuration>
                    </execution>
                    <execution>
                        <id>jlupin-deploy</id>
                        <goals>
                            <goal>deploy</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

It is default pom.xml for single native module microservice.

Microservice code

Add two files with configuration: one for JLupin (JavaScriptHelloWorldJLupinConfiguration) and one for Spring container (JavaScriptHelloWorldSpringConfiguration). Create package com.example.configuration and put classed there.

package com.example.configuration;

import com.jlupin.impl.container.application.spring.JLupinAbstractSpringApplicationContainer;
import com.jlupin.interfaces.configuration.microservice.container.application.JLupinAbstractApplicationContainerProducer;
import com.jlupin.interfaces.container.application.JLupinApplicationContainer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

public class JavaScriptHelloWorldJLupinConfiguration extends JLupinAbstractApplicationContainerProducer {
    public JLupinApplicationContainer produceJLupinApplicationContainer() {
        return new JLupinAbstractSpringApplicationContainer() {
            @Override
            public AbstractApplicationContext getAbstractApplicationContext() {
                return new AnnotationConfigApplicationContext(JavaScriptHelloWorldSpringConfiguration.class);
            }
        };
    }
}
package com.example.configuration;

import com.example.service.interfaces.ExampleService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

@Configuration
@ComponentScan("com.example")
public class JavaScriptHelloWorldSpringConfiguration {
    @Bean("jLupinRegularExpressionToRemotelyEnabled")
    public List<String> getRemotelyBeanList() {
        final List<String> list = new ArrayList<>();
        list.add("exampleService");
        return list;
    }

    @Bean("exampleService")
    public ExampleService getExampleService() throws FileNotFoundException, ScriptException {
        return getService(ExampleService.class, "com/example/service/impl/ExampleServiceImpl.js");
    }

    private <T> T getService(Class<T> interfaceClass, final String implementationPath) throws FileNotFoundException, ScriptException {
        final InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(implementationPath);
        if (resourceAsStream == null) {
            throw new IllegalStateException("Cannot find implementation for interface " + interfaceClass.getName() + " and implementation path " + implementationPath + ".");
        }
        final InputStreamReader inputStreamReader = new InputStreamReader(resourceAsStream);

        final ScriptEngineManager scriptEngineManager = new ScriptEngineManager(NashornScriptEngineFactory.class.getClassLoader());
        final ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");

        if (nashorn == null) {
            throw new IllegalStateException("Cannot find nashorn javascript engine.");
        }

        nashorn.eval(inputStreamReader);

        Invocable invocable = (Invocable) nashorn;
        return invocable.getInterface(interfaceClass);
    }
}

As you can see special function is defined in configuration to easily add new services defined in JavaScript. Because JavaScript files are also not annotated it is required to define service as a bean.

Microservice is configured but does nothing. Create two packages: com.example.service.interfaces in Java sources and com.example.service.impl in JavaScript sources. Put service definitions in them:

package com.example.service.interfaces;

import javax.script.ScriptException;

public interface ExampleService {
    String hello(final String name) throws ScriptException, NoSuchMethodException;
}
function hello(name) {
    return "Hello, " + name + "!";
}

Microservice is done. You only need to add configuration for it. Create special directory for it called additional-files. Put in there two files: configuration.yml and log4j2.xml.

Put default configuration (link) in configuration.yml and update it's application section with proper class:

[...]
APPLICATION:
  applicationContainerProducerClassName: 'com.example.configuration.JavaScriptHelloWorldJLupinConfiguration
[...]

Also put default configuration for Log4j2 (link).

Microservice deployment

You can deploy your microservice manually or automatically. Just follow instructions in documentation.

Checking microservice

You can check if your microservice is running using HTTP Elastic API and cURL for example:

curl -X POST http://localhost:8082/java-script-hello-world/exampleService/hello -H 'Content-Type: application/json' -H 'X-JLNS-API-ID: ROA' -d '"Peter"'

As output you should see:

"Hello, Peter!"