Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

First-class Scala Lift support in JRebel

We recently added a Lift plug-in to JRebel (available in nightly builds) to make Lift application development with JRebel an even more productive experience than before. There have been some questions on Twitter about what that better support actually means. Currently we do just one main thing: reinstantiate Scala singleton objects that extend certain Lift framework types such as LiftScreen, Wizard, RestHelper and MVCHelper.

The behaviour of those objects is in part defined at object initialization time, so changing the code that is run during initialization will not affect the running application as the objects were alredy initialized. The JRebel Lift plug-in reinstantiates those singleton objects after their classes are reloaded, so changes in initialization code are picked up.

This enables at least the following changes to be made without restarts:

  • changing the fields in a LiftScreen
  • changing the Screens in a Wizard
  • changing serve { … } blocks in a RestHelper or an MVCHelper

Note: in some cases when making these changes, you may still run into the closure reloading problem, which JRebel can’t handle (yet).

For an example, lets look at the wizard in the Lift demo app.

/**
 * An example of a wizard in Lift
 */
object MyWizard extends Wizard {
  object completeInfo extends WizardVar(false)

  // define the first screen
  val nameAndAge = new Screen {

    // it has a name field
    val name = field(S ? "First Name", "",
                     valMinLen(2, S ? "Name Too Short"),
                     valMaxLen(40, S ? "Name Too Long"))

    // and an age field
    val age = field(S ? "Age", 0, minVal(5, S ?? "Too young"),
      maxVal(120, S ? "You should be dead"))

    // choose the next screen based on the age
    override def nextScreen = if (age.is < 18) parentName else favoritePet
  }

  // We ask the parent's name if the person is under 18
  val parentName = new Screen {
    val parentName = field(S ? "Mom or Dad's name", "",
                           valMinLen(2, S ? "Name Too Short"),
      valMaxLen(40, S ? "Name Too Long"))
  }

  // we ask for the favorite pet
  val favoritePet = new Screen {
    val petName = field(S ? "Pet's name", "",
                        valMinLen(2, S ? "Name Too Short"),
                        valMaxLen(40, S ? "Name Too Long"))
  }

  // what to do on completion of the wizard
  def finish() {
    S.notice("Thank you for registering your pet")
    completeInfo.set(true)
  }
}

When running the demo app with JRebel but without the Lift plug-in, adding a new screen to the wizard has no effect, because the wizard is never reinitialized. But lets enable the Lift plug-in (from Agent Settings or by adding a VM argument: -Drebel.lift_plugin=true) and when the app is running again, add the following screen to the end of the wizard:

  // we ask for the pet's favorite food
  val petFood = new Screen {
    val food = field(S ? favoritePet.petName + "'s favourite food", "")
  }

This should be now picked up, at least for new invocations of this wizard. Any existing invocations of this wizard that were started before the change will likely have corrupt state, though.

The support for RestHelper, LiftScreen and MVCHelper is similar, and I hope the above example is enough to give you an idea of what our Lift support does. I also hope we can make Scala and Lift development even better in future versions of JRebel.

Finally, we’d like to thank David Pollak for his input, which made this plug-in possible.

PS. If you didn’t know, we offer free JRebel licenses for Scala developers. You can apply here.

  • Traveller Qu

    **
    * An example of a wizard in Lift
    */
    object MyWizard extends Wizard {
    object completeInfo extends WizardVar(false)

    // define the first screen
    val nameAndAge = new Screen {

    // it has a name field
    val name = field(S ? “First Name”, “”,
    valMinLen(2, S ? “Name Too Short”),
    valMaxLen(40, S ? “Name Too Long”))

    // and an age field
    val age = field(S ? “Age”, 0, minVal(5, S ?? “Too young”),
    maxVal(120, S ? “You should be dead”))

    // choose the next screen based on the age
    override def nextScreen = if (age.is < 18) parentName else favoritePet
    }

    // We ask the parent’s name if the person is under 18
    val parentName = new Screen {
    val parentName = field(S ? “Mom or Dad’s name”, “”,
    valMinLen(2, S ? “Name Too Short”),
    valMaxLen(40, S ? “Name Too Long”))
    }

    // we ask for the favorite pet
    val favoritePet = new Screen {
    val petName = field(S ? “Pet’s name”, “”,
    valMinLen(2, S ? “Name Too Short”),
    valMaxLen(40, S ? “Name Too Long”))
    }

    // what to do on completion of the wizard
    def finish() {
    S.notice(“Thank you for registering your pet”)
    completeInfo.set(true)
    }
    }