diff --git a/API/build.gradle b/API/build.gradle index 236aa0e..4b3c1ef 100644 --- a/API/build.gradle +++ b/API/build.gradle @@ -1,5 +1,6 @@ group "org.tbasket.api" dependencies { - implementation 'com.typesafe.akka:akka-http_2.13:10.5.0-M1' + api 'dev.zio:zio-http_2.13:0.0.3' + api "dev.zio:zio_2.13:2.0.4" } \ No newline at end of file diff --git a/API/src/main/scala/org/tbasket/api/APIException.scala b/API/src/main/scala/org/tbasket/api/APIException.scala new file mode 100644 index 0000000..5b33f19 --- /dev/null +++ b/API/src/main/scala/org/tbasket/api/APIException.scala @@ -0,0 +1,3 @@ +package org.tbasket.api + +class APIException(msg: String, cause: Throwable = null) extends Exception \ No newline at end of file diff --git a/API/src/main/scala/org/tbasket/api/Endpoint.scala b/API/src/main/scala/org/tbasket/api/Endpoint.scala index 60f463b..aee3d46 100644 --- a/API/src/main/scala/org/tbasket/api/Endpoint.scala +++ b/API/src/main/scala/org/tbasket/api/Endpoint.scala @@ -1,38 +1,39 @@ package org.tbasket.api -import akka.actor.typed.ActorSystem -import akka.actor.typed.javadsl.Behaviors -import akka.http.scaladsl.Http -import akka.http.scaladsl.model.{ContentTypes, HttpEntity, HttpResponse} -import akka.http.scaladsl.server.Directives._ - -import scala.concurrent.ExecutionContextExecutor - -class Endpoint(url: String, port: Int) { -implicit val system: ActorSystem[_] = ActorSystem(Behaviors.empty, "main") -implicit val ec : ExecutionContextExecutor = system.executionContext - -private var count = 0 - -def countEntity = HttpEntity(ContentTypes.`application/json`, s"value: $count") - -private val server = Http().newServerAt(url, port) - -server.bind(concat( - path("counter") { - get { - println("got get request") - complete(countEntity) - } - }, - path("counter") { - post { - println("got request") - count += 1 - complete(HttpResponse(entity = countEntity)) - } - } -)) +import org.tbasket.api.compute.APIRequestHandler +import zio._ +import zio.http.ServerConfig.LeakDetectionLevel +import zio.http._ + +import scala.collection.mutable + +class Endpoint(hostname: String, port: Int, rootPath: Option[String]) extends ZIOAppDefault { + + private val Root: Path = rootPath.map(Path.decode).getOrElse(Path.root) + private val handlers = mutable.HashMap.empty[String, APIRequestHandler] + def bind(path: String)(handler: APIRequestHandler): Unit = { + handlers.put(path, handler) + } + + private val app = Http.collectZIO[Request] { + case r@_ -> Root / path if handlers.contains(path) => + for { + handler <- handlers.get(path) + _ <- handler.handle + } yield () + case _ -> path => + ZIO.fail(new APIException(s"Unable to find a handler for page '$path'")) + } + val run = { + val config = ServerConfig.default + .port(port) + .leakDetection(LeakDetectionLevel.PARANOID) + + val configLayer = ServerConfig.live(config) + (Server.install(app).flatMap { port => + Console.printLine(s"Listening API entries on $hostname:$port$rootPath") + } *> ZIO.never).provide(configLayer, Server.live) + } } diff --git a/API/src/main/scala/org/tbasket/api/compute/APIRequestHandler.scala b/API/src/main/scala/org/tbasket/api/compute/APIRequestHandler.scala new file mode 100644 index 0000000..b9334c9 --- /dev/null +++ b/API/src/main/scala/org/tbasket/api/compute/APIRequestHandler.scala @@ -0,0 +1,11 @@ +package org.tbasket.api.compute + +import org.tbasket.api.APIException +import zio.ZIO +import zio.http.{Request, Response} + +trait APIRequestHandler { + + def handle: ZIO[Request, APIException, Response] + +} diff --git a/API/src/main/scala/org/tbasket/api/event/RequestHandler.scala b/API/src/main/scala/org/tbasket/api/event/RequestHandler.scala deleted file mode 100644 index 17354ce..0000000 --- a/API/src/main/scala/org/tbasket/api/event/RequestHandler.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.tbasket.api.event - -trait RequestHandler { - - def handle() - -} diff --git a/build.gradle b/build.gradle index 815c0bc..8a40242 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'java-library' id 'scala' id 'application' } @@ -19,7 +20,6 @@ dependencies { implementation project(':API') implementation project(':DB') implementation "io.circe:circe-core_$scalaVersion:0.15.0-M1" - } test { @@ -32,6 +32,7 @@ run { allprojects { apply plugin: 'scala' + apply plugin: 'java-library' version '1.0-SNAPSHOT' repositories { @@ -43,8 +44,6 @@ allprojects { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' implementation "org.scala-lang:scala-library:$scalaVersion.10" - implementation "com.typesafe.akka:akka-actor-typed_$scalaVersion:2.7.0" - implementation "com.typesafe.akka:akka-stream-typed_$scalaVersion:2.7.0" } diff --git a/src/main/scala/org/tbasket/EndpointSetup.scala b/src/main/scala/org/tbasket/EndpointSetup.scala index 1466dba..1e22d7f 100644 --- a/src/main/scala/org/tbasket/EndpointSetup.scala +++ b/src/main/scala/org/tbasket/EndpointSetup.scala @@ -1,6 +1,7 @@ package org.tbasket import org.tbasket.api.Endpoint +import org.tbasket.compute.IncrementHandler import java.util.Properties @@ -12,7 +13,7 @@ object EndpointSetup { def setupEndpoint(): Endpoint = { println("Initializing API endpoint...") val endpoint = createEndpoint() - + endpoint.bind("counter")(IncrementHandler) endpoint } @@ -20,13 +21,13 @@ object EndpointSetup { val properties = new Properties() val in = getClass.getClassLoader.getResourceAsStream("server.properties") properties.load(in) - val (url, port) = properties + val (hostname, port, endpointPath) = properties .getProperty(EndpointUrl, EndpointUrlDefault) match { - case s"$ip:$port/$endpointPath" => (ip + "/" + endpointPath, port.toInt) - case s"$ip:$port" => (ip, port.toInt) + case s"$ip:$port/$endpointPath" => (ip, port.toInt, Some(endpointPath)) + case s"$ip:$port" => (ip, port.toInt, None) case v => throw new InternalBasketServerException(s"$EndpointUrl property value is wrong: $v must be :/[endpointPath]") } - new Endpoint(url, port) + new Endpoint(hostname, port, endpointPath) } } diff --git a/src/main/scala/org/tbasket/Main.scala b/src/main/scala/org/tbasket/Main.scala index d0882bb..52e14ae 100644 --- a/src/main/scala/org/tbasket/Main.scala +++ b/src/main/scala/org/tbasket/Main.scala @@ -1,15 +1,26 @@ package org.tbasket + +import zio._ + import scala.io.StdIn +import scala.util.control.NonFatal object Main { def main(args: Array[String]): Unit = { val endpoint = EndpointSetup.setupEndpoint() + val runtime = Runtime.default + Unsafe.unsafe { implicit u => + runtime.unsafe.run(endpoint.run).catchSome { + case NonFatal(e) => + e.printStackTrace() + throw e + } + } println("enter to exit") StdIn.readLine() } - } diff --git a/src/main/scala/org/tbasket/compute/IncrementHandler.scala b/src/main/scala/org/tbasket/compute/IncrementHandler.scala new file mode 100644 index 0000000..85e1922 --- /dev/null +++ b/src/main/scala/org/tbasket/compute/IncrementHandler.scala @@ -0,0 +1,20 @@ +package org.tbasket.compute + +import org.tbasket.api.APIException +import org.tbasket.api.compute.APIRequestHandler +import zio.ZIO +import zio.http.model.Method._ +import zio.http.{Request, Response} + +object IncrementHandler extends APIRequestHandler { + + private var i = 0 + + + override def handle: ZIO[Request, APIException, Response] = { + case GET -> _ => Response.json(s"value: $i") + case POST -> _ => + i += 1 + Response.ok + } +}