Scala

Any language compiled into byte code which is run on JVM can be used to write microservices for JLupin. The example of such a language is Scala. It also supports using Java libraries inside Scala's code, so it is easy to use JLupin Client library.

Native microservice

You can use libraries written in Java in your Scala code, so you won't see any plain Java class.

Project structure and dependencies

This is standard structure of Scala project. Create directories and empty files as shown below.

Figure 1. Structure of project.

Remember to configure SBT by editing project/build.properties file and add:

sbt.version=1.1.1

Configure your build (build.sbt):

name := "scala-hello-world"
organization := "com.example"
version := "1.0"

scalaVersion := "2.11.8"

resolvers += "jlupin-central" at "http://support.jlupin.com/maven2/"

libraryDependencies += "org.springframework" % "spring-context" % "4.3.9.RELEASE" % "provided"
libraryDependencies += "com.jlupin" % "jlupin-client-assembly" % "1.5.0.0" % "provided"

All dependencies are set to scope provided because they are available on server by default. Also one reslover is added with JLupin Maven repository address for getting jlupin-client-assembly library.

Microservice code

Add two files with configuration: one for JLupin (ScalaHelloWorldJLupinConfiguration) and one for Spring container (ScalaHelloWorldSpringConfiguration). 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 org.springframework.context.annotation.{AnnotationConfigApplicationContext}
import org.springframework.context.support.{AbstractApplicationContext}

class ScalaHelloWorldJLupinConfiguration extends JLupinAbstractApplicationContainerProducer {
    def produceJLupinApplicationContainer = new JLupinAbstractSpringApplicationContainer {
        def getAbstractApplicationContext = {
            new AnnotationConfigApplicationContext(classOf[ScalaHelloWorldSpringConfiguration]).asInstanceOf[AbstractApplicationContext]
        }
    }
}
package com.example.configuration

import org.springframework.context.annotation.{Bean, ComponentScan, Configuration}
import scala.collection.JavaConverters._

@Configuration
@ComponentScan(Array("com.example"))
class ScalaHelloWorldSpringConfiguration {
    @Bean(name = Array("jLupinRegularExpressionToRemotelyEnabled"))
    def getRemotelyBeanList = {
        val list = List(
            "exampleService"
        )
        list.asJava
    }
}

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

package com.example.service.interfaces

trait ExampleService {
    def hello(name : String): String
}
package com.example.service.impl

import com.example.service.interface.{ExampleService}
import org.springframework.stereotype.Service

@Service(value = "exampleService")
class ExampleServiceImpl extends ExampleService {
    def hello(name : String) = "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.


SERVERS:
  JLRMC: #JLupin Remote Method Calls Fast Protocol:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: true
    waitToShutdownThreadsOnStop: 60000
    backlog: 0
    receiveBufferSize: 0
    isReuseAddress: false
    threadPoolSize: 128
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 240000
    isStartOnMainServerInitialize: true
  TRANSMISSION:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: false
    waitToShutdownThreadsOnStop: 60000
    backlog: 0
    receiveBufferSize: 0
    isReuseAddress: false
    threadPoolSize: 8
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 3600000
    isStartOnMainServerInitialize: true
  QUEUE:
    readTimeout: 480000
    isWaitForFinishExecuteAllRequests: true
    waitToShutdownThreadsOnStop: 60000
    backlog: 1024
    receiveBufferSize: 1024
    isReuseAddress: false
    threadPoolSize: 128
    isLogPeriodicOnDebug: true
    isDestroyThreadOnTimeout: false
    threadExecutingTimeOut: 240000
    isStartOnMainServerInitialize: true
ENTRY_POINTS:
  QUEUE:
    threadAmount: 512
    howOftenCheckingServerInMillis: 5000
    repeatsAmount: 4
    timeToWaitBetweenRepeatProbeInMillis: 1000
TRANSMISSION:
  MICROSERVICES_GRANT_ACCESS:
    MICROSERVICES_LIST:
      #- microserviceName: 'sampleMicroservice'
      #  serviceName: 'sampleServiceName'
      #  methodName: 'sampleMethodName'
      #- microserviceName: 'sampleMicroservice2'
      #  serviceName: 'sampleServiceName2'
      #  methodName: 'sampleMethodName2'
PROPERTIES:
  #jvmOptions1: '-Xms128M -Xmx256M -agentlib:jdwp=transport=dt_socket,address=12998,server=y,suspend=n'
  jvmOptions1: '-Xms64M -Xmx128M' #jvmOptions_2 - default the same as jvmOptions_1
  #jvmOptions2: '-Xms128M -Xmx256M'
  externalPort: '8000'
  version: '1.5.0.0'
  switchDelayTime: 0
  connectionSocketTimeoutInMillis: 1000
  readTimeoutInMillis: 30000
  isKeepAlive: false
  isOOBInline: false
  isTcpNoDelay: false
  isReuseAddress: false
  sendBufferSize: 0
  receiveBufferSize: 0
  soLinger: 0
  trafficClass: 0
  #javaExecutablePath: 'c:\\jvm\\bin\\java.exe'
  #additionalClassPath: 'c:\\temp\\*'
  isStartOnMainServerInitialize: true
  priorityStartOnMainServerInitialize: 4
  waitForProcessInitResponseTimeInMillis: 90000
  waitForProcessStartResponseTimeInMillis: 90000
  waitForProcessDestroyResponseTimeInMillis: 30000
  isAllFilesToJVMAppClassLoader: false
  isArchiveOnStart: false
  startLogMode: INFO
  isInitErrorCauseWithNetworkInformation: true
  isJmxEnabled: true
  jmxOptions: '-Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false'
  jmxPrimaryPort: -1
  jmxSecondaryPort: -1
  checkAvailableScript: 'function isAvailable(checkResponseTimeInMillis, jrmcActiveThreads, jrmcMaxThreads,
                                              queueActiveThreads, queueMaxThreads, servletActiveThreads, servletMaxThreads,
                                              jvmMaxMemoryInBytes, jvmTotalMemoryInBytes, jvmFreeMemoryInBytes,
                                              jvmProcessCpuLoadInPercentage, userAvailableFlag) {
                          var isAvailableByUser = Boolean(userAvailableFlag);
                            if(checkResponseTimeInMillis > 20000 || !isAvailableByUser) {
                               return false;
                            }
                            return true;
                         }'
APPLICATION:
  applicationContainerProducerClassName: 'com.example.configuration.ScalaHelloWorldJLupinConfiguration'
INITIALIZING_LOGGER:
  #directoryPath: '/logs/server'
  #fileName: 'file_name'
  fileExtension: 'log'
  fileSizeInMB: 20
  maxFiles: 10
MEMORY_ERRORS:
  isRestartOnError: true
  howManyTimes: 4
  percentageGrowth: 15
  isHeapDump: true
THREAD_POOLS:
  THREAD_POOL_1:
    size: 8
    waitingTimeForTasksCompletionInMillis: 10000
  #THREAD_POOL_2:
  #  size: 8
  #  waitingTimeForTasksCompletionInMillis: 10000
<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!--                                                                       -->
<!--  Log4j2 Configuration                                                  -->
<!--                                                                       -->
<!-- ===================================================================== -->

<!--
   | For more configuration information and examples see the Apache Log4j2
   | website: https://logging.apache.org/log4j/2.x/index.html
-->

<Configuration status="WARN">
    <!-- Extract log directory and file name into variables -->
    <Properties>
        <Property name="logDirectory">../logs/microservice/scala-hello-world</Property>
        <Property name="logFileName">microservice</Property>
    </Properties>

    <Appenders>
        <!-- RollingFileAppender configured to role every day -->
        <RollingFile name="FILE">
            <FileName>${logDirectory}/${logFileName}.log</FileName>
            <FilePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.log</FilePattern>

            <!-- Compress log files to gzip -->
            <!-- More configuration https://logging.apache.org/log4j/2.x/manual/appenders.html#DefaultRolloverStrategy -->
            <!-- <FilePattern>${logDirectory}/${logFileName}.%d{yyyy-MM-dd}.log.gz</FilePattern> -->

            <!-- Do not truncate file -->
            <Append>true</Append>

            <!-- The default pattern: Date Priority [Category] (Thread) Message\n -->
            <PatternLayout pattern="%d %-5p [%c] (%t) %m%n" />

            <Policies>
                <!-- Rollover every microservice start - very useful for debugging -->
                <!-- <OnStartupTriggeringPolicy /> -->

                <!-- Rollover at the top of each day -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />

                <!-- Rollover if file size is greater than 200 MB -->
                <!-- <SizeBasedTriggeringPolicy size="200 MB"/> -->
            </Policies>

            <!-- Keep last 10 log files -->
            <!-- More configuration https://logging.apache.org/log4j/2.x/manual/appenders.html#DefaultRolloverStrategy -->
            <!-- <DefaultRolloverStrategy max="10" /> -->
        </RollingFile>

        <!-- AsyncAppender for high performance -->
        <Async name="ASYNC_FILE">
            <BufferSize>1000</BufferSize>
            <AppenderRef ref="FILE" />
        </Async>
    </Appenders>

    <Loggers>
        <!-- Setup for root logger with AsyncAppender -->
        <Root level="info">
            <AppenderRef ref="ASYNC_FILE" />
        </Root>
    </Loggers>
</Configuration>

To make it simpler to deploy out microservice add Maven definition pom.xml to use JLupin Platform Maven Plugin to create deployable zip file and to deploy created microservice (you can of cource do it without it, but then it requires more work from you).

<?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>

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

    <properties>
        <scala.version>2.11.8</scala.version>

        <jlupin.next.server.maven.plugin.version>1.2.1</jlupin.next.server.maven.plugin.version>
    </properties>

    <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>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
    </dependencies>

    <!--
        Enable shade and JCS JLupin Platform Maven Plugin for this module.
        They are configured in main project pom.xml file.
    -->
    <build>
        <finalName>scala-2.11/scala-hello-world_2.11-1.0</finalName>

        <plugins>
            <plugin>
                <groupId>com.jlupin</groupId>
                <artifactId>jlupin-platform-maven-plugin</artifactId>
                <version>${jlupin.next.server.maven.plugin.version}</version>
                <executions>
                    <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>

You may noticed two things. First is that there is scala-library dependency added. By default server does not contain it and it is certain that our microservice requires it to run - ended up it is written in scala. By adding it here it will be automatically added to our microservice zip. Also <finalName> parameter is changed to match path where jar file is generated by SBT.

Microservice deployment

To deploy microservice make sure that you have started JLupin Platform and first compile your microservice to jar archive. It is done with command sbt package. Then create microservice deployable zip file with command mvn jlupin-platform:zip@jlupin-zip and in the end deploy it to server with command mvn jlupin-platform:deploy@jlupin-deploy. All three steps should succeeded.