All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.liftweb.http.LiftRulesGuardedSetting.scala Maven / Gradle / Ivy

The newest version!
package net.liftweb.http

import net.liftweb.util.{HasCalcDefaultValue, LiftValue}

object LiftRulesGuardedSetting {
  type StackTrace = Array[StackTraceElement]

  /**
    * Base class for all possible violations which LiftRulesGuardedSetting warns you about.
    * @param settingName the name of the LiftRules setting which was violated.
    * @param stackTrace the stacktrace from where the violation occurred
    * @param message an English message for the developer detailing the violation
    */
  abstract class SettingViolation(settingName: String, stackTrace: StackTrace, message: String) extends Serializable {
    /**
      * Converts this violation into an Exception which can be handed to a logger for clean message printing
      */
    def toException: Exception = {
      val e = new Exception(message)
      e.setStackTrace(stackTrace)
      e
    }
  }

  /**
    * Indicates that a LiftRulesGuardedSetting was written after it had already been read.
    */
  case class SettingWrittenAfterRead(settingName: String, stackTrace: StackTrace, message: String)
    extends SettingViolation(settingName, stackTrace, message)

  /**
    * Indicates that a LiftRulesGuardedSetting was written after Lift finished booting.
    */
  case class SettingWrittenAfterBoot(settingName: String, stackTrace: StackTrace, message: String)
    extends SettingViolation(settingName, stackTrace, message)
}

import LiftRulesGuardedSetting._

/**
  * This class encapsulates a mutable LiftRules setting which guards its value against changes which can produce
  * unexpected results in a Lift application.
  *
  * @param name the name of the LiftRules setting (an unfortunate duplication of the name given on LiftRules itself).
  * @param default the default value of this setting
  * @tparam T the type of the setting
  */
class LiftRulesGuardedSetting[T](val name: String, val default: T) extends LiftValue[T] with HasCalcDefaultValue[T] {
  private[this] var v: T = default
  private[this] var lastSet: Option[StackTrace] = None
  private[this] var lastRead: Option[StackTrace] = None

  private[this] def writeAfterReadMessage =
    s"LiftRules.$name was set after already being read! " +
    s"Review the stacktrace below to see where the value was last read. "

  private[this] def writeAfterBootMessage =
    s"LiftRules.$name set after Lift finished booting. " +
    s"Review the stacktrace below to see where settings are being changed after boot time. "

  private[this] def trimmedStackTrace(t: Throwable): StackTrace = {
    val toIgnore = Set("LiftRulesGuardedSetting", "LiftValue")
    t.getStackTrace.dropWhile(e => toIgnore.find(e.getClassName contains _).isDefined)
  }

  private[this] def currentStackTrace: StackTrace = trimmedStackTrace(new Exception)

  override def set(value: T): T = {
    if(LiftRules.doneBoot) {
      val e = SettingWrittenAfterBoot(name, currentStackTrace, writeAfterBootMessage)
      LiftRules.guardedSettingViolationFunc.get.apply(e)
    }

    // TODO: Skip if the value is the same?
    lastRead.foreach { stackTrace =>
      val e2 = SettingWrittenAfterRead(name, stackTrace, writeAfterReadMessage)
      LiftRules.guardedSettingViolationFunc.get.apply(e2)
    }

    lastSet = Some(currentStackTrace)
    v = value
    v
  }

  override def get: T = {
    if(!LiftRules.doneBoot) lastRead = Some(currentStackTrace)
    v
  }

  override protected def calcDefaultValue: T = default
}







© 2015 - 2024 Weber Informatics LLC | Privacy Policy