我正在开发的应用程序是使用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"),
...))
这里无法避免某些讨厌的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 ()
)