0

I am struggling with how to dynamically add some webservices. I am using Scalatra for the webservice framework.

I want to allow the developer to be able to change authentication, for example, so that rather than using hard-coded credentials, instead use a database or password file or whatever they need.

I also want to allow them to add new webservices inside the servlet.

So, what I want to do is in the bootstrap code have it load up and recompile the class and then use that version.

I have looked at this, but I need to recompile an entire class, not snippets.

Generating a class from string and instantiating it in Scala 2.10

This is what I have tried, but I added a "/help" webservice but it isn't found, so the new class isn't being used yet.

class ScalatraBootstrap extends LifeCycle {

  override def init(context: ServletContext) {

    val sourceDir = new java.io.File("C:/Temp/MyServlet.scala")
    val sse = ScalaScriptEngine.onChangeRefresh(sourceDir)
    sse.refresh
    println("*** - " + sse.compilationStatus.startTime + "  " + sse.compilationStatus.stopTime)

    context.mount(sse.get[MyServlet]("test.proj.MyServlet"), "/*")

I am using scalascriptengine (https://code.google.com/p/scalascriptengine/) at the moment.

So, how can I recompile the class file for the webservice, when it may have case classes, annotations and object classes in the same file, on the fly?

I am wondering if I need to have the webservice in Groovy instead, but I would prefer to keep it functional.

UPDATE

I had thought about plugins first, but ran into a problem with how would I add new webservices that way, and it may be that Scalatra is not going to be the right choice, that I may need to change my REST service framework.

Eventually I want to be able to change the webservices on the fly without having to restart the application, and recompiling the source would allow that.

Community
  • 1
  • 1
James Black
  • 40,548
  • 9
  • 79
  • 153

2 Answers2

1

Realizing a plug-in affordance is not too hard, at least for reasonably simple cases. The essential elements are:

  • A trait or abstract class defining the obligations of realizations of the plug-in.
  • A means to get the code for plug-ins onto the class-path. Alternatively, if you're familiar with working with classloaders, you can do it dynamically. I don't have much experience with that.
  • Once you have an instance of java.lang.Class[P <: PlugInType] it's trivial to get an instance, provided you don't need constructor parameters.
  • A protocol in the plug-in trait that allows the plug-in to, e.g., reserve a top-level URL path segment from which you derive a Scalatra route that covers all those paths. You then dispatch requests that match that leading path segment via the plug-in instance. All you have to do is make sure you don't let two plug-ins claim the same path or if you do you have some further means of resolving them.
Randall Schulz
  • 25,823
  • 4
  • 58
  • 81
0

Thanks to @RandallSchultz I found a solution that works.

override def init(context: ServletContext) {

    val sourceDir = new java.io.File("C:/Temp/HelpServlet.scala")
    val sse = ScalaScriptEngine.onChangeRefresh(sourceDir)
    sse.refresh
    println("*** - " + sse.compilationStatus.startTime + "  " + sse.compilationStatus.stopTime)

    context.mount(new MyServlet, "/*")
    context.mount(sse.get("org.myproject.rest.HelpServlet"), "/help/*")

So when I went to "/help/help" I got my help page as expected, so the new webservice was added.

By reading it from a file I was able to add it while the application was running.

James Black
  • 40,548
  • 9
  • 79
  • 153