在OSGi上下文中,Scala-cat的IOApp

问题描述 投票:3回答:1

我正在开发的应用程序是使用scala-cats'IOApp以功能编程风格编写的。

现在的问题是,我需要在似乎不太适合我的功能方法的OSGi上下文中部署此应用程序。

我的主要方法如下:

object Main extends IOApp {
  override def run(args: List[String]): IO[ExitCode] = for {
    _ <- IO(println("Starting with args: " + args.mkString(",")))
    myProgram = new MyProgram(/*...*/)
    _ <- myProgram.run() // def run(): IO[RunResult]
    // ...
    _ <- myProgram.exit() // def exit(): IO[Unit]
    } yield ExitCode.Success
}

现在要将其部署到OSGi,我必须编写一个BundleActivator

import org.osgi.framework.{BundleActivator, BundleContext}

class Activator extends BundleActivator {

  private var myProgram: Option[myProgram] = None

  override def start(context: BundleContext): Unit = {
    myProgram = Some(new MyProgram(/*...*/))
    myProgram.foreach{ _.run().unsafeRunSync() }
  }

  override def stop(context: BundleContext): Unit = {
    myProgram.foreach{ _.exit().unsafeRunSync() }
  }
}

您可以看到我想出的这个Activator远不是以功能性方式编写的。我至少有什么办法可以摆脱var myProgram(具体来说是可变的var)?我似乎无法弄清楚这怎么可能。

编辑:需要在清单中定义激活器,因此这是我的build.sbt

的一部分
packageOptions := Seq(ManifestAttributes(
  ("Bundle-Activator", "my.package.Activator"),
  ...)) 
scala functional-programming osgi scala-cats io-monad
1个回答
0
投票

这里无法避免某些讨厌的Java式代码,这就是OSGi的工作方式。但是您所能做的就是将可疑性隐藏在一个可重用的类中,因此您只需要正确处理一次就可以了,而不必再看了。我的建议是使用一个通用的Activator,它将在启动时获得一只猫Resource并在关闭它时将其丢弃。

class ResourceActivator(resource: BundleContext => Resource[IO, Unit])
  extends BundleActivator {

  private var cleanup: IO[Unit] = null

  override def start(context: BundleContext): Unit =
    cleanup = resource(context).allocate.unsafeRunSync()._2

  override def stop(context: BundleContext): Unit =
    cleanup.unsafeRunSync()
}

有效的OSGi实现在没有先调用stop的情况下永远不会调用start,因此可以将cleanup初始化为null

请注意,以上课程很容易让您G。在start方法中启动一些异步计算,然后在stop方法中将其关闭:

class MyActivator extends ResourceActivator(ctx =>
  for {
    res1 <- someResource
    res2 <- anotherResource
    _ <- Resource.make(doSomeStuff.start)(_.cancel)
  } yield ()
)
© www.soinside.com 2019 - 2024. All rights reserved.