Browse Source

Added lift-stateless benchmark

Andreas C. Osowski 12 years ago
parent
commit
6d91706b81

+ 27 - 0
lift-stateless/README.md

@@ -0,0 +1,27 @@
+#Lift Benchmarking Test (stateless)
+
+This is the stateless Lift portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+
+* [JSON test source](src/main/scala/code/lib/StatelessJson.scala)
+
+### Data-Store/Database Mapping Test
+
+* [Database test snippet](src/main/scala/code/lib/StatelessDb.scala)
+* [Database test model](src/main/scala/code/model/World.scala)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
+* [Lift 2.5.0-RC2](http://http://www.liftweb.net/)
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost/json
+
+### Data-Store/Database Mapping Test
+
+http://localhost/db/5

+ 0 - 0
lift-stateless/__init__.py


+ 13 - 0
lift-stateless/benchmark_config

@@ -0,0 +1,13 @@
+{
+  "framework": "lift-stateless",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/db/",
+      "port": 8080,
+      "sort": 37
+    }
+  }]
+}

+ 31 - 0
lift-stateless/build.sbt

@@ -0,0 +1,31 @@
+name := "Lift 2.5 benchmark"
+
+version := "0.0.1"
+
+organization := ""
+
+scalaVersion := "2.10.1"
+
+resolvers ++= Seq("snapshots"     at "http://oss.sonatype.org/content/repositories/snapshots",
+                "releases"        at "http://oss.sonatype.org/content/repositories/releases"
+                )
+
+seq(com.github.siasia.WebPlugin.webSettings :_*)
+
+unmanagedResourceDirectories in Test <+= (baseDirectory) { _ / "src/main/webapp" }
+
+scalacOptions ++= Seq("-deprecation", "-unchecked")
+
+libraryDependencies ++= {
+  val liftVersion = "2.5-RC2"
+  Seq(
+    "net.liftweb"       %% "lift-webkit"        % liftVersion        % "compile",
+    "net.liftweb"       %% "lift-mapper"        % liftVersion        % "compile",
+    "net.liftweb"       %% "lift-json"        % liftVersion        % "compile",
+    "org.eclipse.jetty" % "jetty-webapp"        % "8.1.7.v20120910"  % "container,test",
+    "org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container,test" artifacts Artifact("javax.servlet", "jar", "jar"),
+    "ch.qos.logback"    % "logback-classic"     % "1.0.6",
+    "com.h2database"    % "h2"                  % "1.3.167"
+  )
+}
+

+ 14 - 0
lift-stateless/project/plugins.sbt

@@ -0,0 +1,14 @@
+libraryDependencies <+= sbtVersion(v => v match {
+  case "0.11.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.0-0.2.8"
+  case "0.11.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.1-0.2.10"
+  case "0.11.2" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.2-0.2.10"
+  case "0.11.3" => "com.github.siasia" %% "xsbt-web-plugin" % "0.11.3-0.2.11.1"
+  case "0.12.0" => "com.github.siasia" %% "xsbt-web-plugin" % "0.12.0-0.2.11.1"
+  case "0.12.1" => "com.github.siasia" %% "xsbt-web-plugin" % "0.12.0-0.2.11.1"
+})
+
+//Uncoment this line to enable the sbt idea plugin
+addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0")
+
+//Uncoment this line to enable the sbt eclipse plugin
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.0")

+ 1 - 0
lift-stateless/sbt

@@ -0,0 +1 @@
+java -Xmx1024M -Xss2M -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled -jar `dirname $0`/sbt-launch-0.12.jar "$@"

BIN
lift-stateless/sbt-launch-0.12.jar


+ 2 - 0
lift-stateless/sbt.bat

@@ -0,0 +1,2 @@
+set SCRIPT_DIR=%~dp0
+java -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx1024M -Xss2M -jar "%SCRIPT_DIR%\sbt-launch-0.12.jar" %*

+ 25 - 0
lift-stateless/setup.py

@@ -0,0 +1,25 @@
+
+import subprocess
+import sys
+import setup_util
+import os
+
+def start(args):
+  setup_util.replace_text("lift-stateless/src/main/resources/props/default.props", "jdbc:mysql:\/\/.*:3306", "jdbc:mysql://" + args.database_host + ":3306")
+
+  subprocess.Popen("./sbt update container:start", shell=True, cwd="lift-stateless")
+
+  return 0
+def stop():
+  p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+  out, err = p.communicate()
+  for line in out.splitlines():
+    if './start' in line or ('play' in line and 'java' in line):
+      pid = int(line.split(None, 2)[1])
+      os.kill(pid, 9)
+  try:
+    os.remove("play-scala/RUNNING_PID")
+  except OSError:
+    pass
+
+  return 0

+ 0 - 0
lift-stateless/src/main/resources/.keep


+ 23 - 0
lift-stateless/src/main/resources/props/default.logback.xml

@@ -0,0 +1,23 @@
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>console.devmode.log</file>
+        <append>true</append>
+        <encoder>
+            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+
+
+    <logger name="code.snippet" level="info" />
+    <logger name="net.liftweb" level="warn" />
+    <logger name="bootstrap.liftweb" level="info" />
+    <root level="warn">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>

+ 8 - 0
lift-stateless/src/main/resources/props/default.props

@@ -0,0 +1,8 @@
+##any db drop and create data true/false
+db.schemify=true
+
+db.driver= com.mysql.jdbc.Driver
+db.url="jdbc:mysql://localhost:3306/hello_world"
+db.user=benchmarkdbuser
+db.password=benchmarkdbpass
+

+ 56 - 0
lift-stateless/src/main/scala/bootstrap/liftweb/Boot.scala

@@ -0,0 +1,56 @@
+package bootstrap.liftweb
+
+import net.liftweb._
+import util._
+import Helpers._
+
+import common._
+import http._
+import sitemap._
+import Loc._
+import mapper._
+
+import code._
+
+import java.util.concurrent.ThreadLocalRandom
+
+/**
+ * A class that's instantiated early and run.  It allows the application
+ * to modify lift's environment
+ */
+class Boot {
+  def boot {
+    if (!DB.jndiJdbcConnAvailable_?) {
+      val vendor =
+        new StandardDBVendor(Props.get("db.driver") openOr "org.h2.Driver",
+          Props.get("db.url") openOr
+          "jdbc:h2:lift_proto.db;AUTO_SERVER=TRUE",
+          Props.get("db.user"), Props.get("db.password"))
+
+      LiftRules.unloadHooks.append(vendor.closeAllConnections_! _)
+
+      DB.defineConnectionManager(DefaultConnectionIdentifier, vendor)
+    }
+
+    // Use Lift's Mapper ORM to populate the database
+    // you don't need to use Mapper to use Lift... use
+    // any ORM you want
+    //Schemifier.schemify(true, Schemifier.infoF _, model.World)
+
+    // where to search snippet
+    LiftRules.addToPackages("code")
+
+    lib.StatelessJson.init()
+    lib.StatelessDb.init()
+
+    // Force the request to be UTF-8
+    LiftRules.early.append(_.setCharacterEncoding("UTF-8"))
+
+    // Use HTML5 for rendering
+    LiftRules.htmlProperties.default.set((r: Req) =>
+      new Html5Properties(r.userAgent))
+
+    // Make a transaction span the whole HTTP request
+    S.addAround(DB.buildLoanWrapper)
+  }
+}

+ 0 - 0
lift-stateless/src/main/scala/code/comet/.keep


+ 46 - 0
lift-stateless/src/main/scala/code/lib/StatelessDb.scala

@@ -0,0 +1,46 @@
+package code.lib
+
+import _root_.net.liftweb._
+import http._
+import js._
+import JsCmds._
+import common._
+import json._
+import java.util.concurrent.ThreadLocalRandom
+import code.model._
+
+import mapper.{By, By_<}
+
+object StatelessDb {
+  private val DB_ROWS = 10000
+  private implicit val formats = net.liftweb.json.DefaultFormats
+
+  case class JsonWorld(id: Long, randomNumber: Long) {
+    def toJson = Extraction.decompose(this)
+  }
+
+  implicit def world2jsonWorld(w: World) = JsonWorld(w.id.get, w.randomNumber.get)
+
+  def init() {
+    LiftRules.statelessDispatch.append{
+      case r @ Req("db" :: Nil, _, _) => () => singleDBQuery()
+      case r @ Req("db" :: queries :: Nil, _ , _) => () => dbQuery(queries.toInt)
+    }
+  }
+
+  def dbQuery(count: Int) : Box[LiftResponse] = {
+    val random = ThreadLocalRandom.current()
+    val rows = for(i <- (1 to count)) yield World.find(By(World.id, random.nextInt(DB_ROWS))).get
+    Full(JsonResponse(JArray(rows.map(_.toJson).toList)))
+  }
+
+  def singleDBQuery() : Box[LiftResponse] = {
+    val random = ThreadLocalRandom.current()
+    val row = World.find(By(World.id, random.nextInt(DB_ROWS)))
+    println("" + row)
+    row match {
+      case Full(r) => Full(JsonResponse(r.toJson))
+      case _ => Empty
+    }
+  }
+}

+ 25 - 0
lift-stateless/src/main/scala/code/lib/StatelessJson.scala

@@ -0,0 +1,25 @@
+package code.lib
+
+import _root_.net.liftweb._
+import http._
+import js._
+import JsCmds._
+import common._
+import json._
+
+
+/**
+ * Respond to JSON requests in a stateless dispatch
+ */
+object StatelessJson {
+  def init() {
+    // register the JSON handler
+    LiftRules.statelessDispatch.append{
+      case r @ Req("json" :: Nil, _, _) => () => sayHello()
+    }
+  }
+
+  def sayHello() = Full(JsonResponse(
+    JE.JsObj("message" -> "Hello World!")
+  ))
+}

+ 19 - 0
lift-stateless/src/main/scala/code/model/World.scala

@@ -0,0 +1,19 @@
+package code.model
+
+import net.liftweb.mapper._
+import net.liftweb.util._
+import net.liftweb.common._
+
+class World extends LongKeyedMapper[World] with IdPK {
+    def getSingleton = World
+
+    object randomNumber extends MappedLong(this)
+
+}
+
+object World extends World with LongKeyedMetaMapper[World] {
+    override lazy val fieldOrder = List(id, randomNumber)
+
+}
+
+// vim: set ts=4 sw=4 et:

+ 0 - 0
lift-stateless/src/main/scala/code/view/.keep


+ 21 - 0
lift-stateless/src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!DOCTYPE web-app
+PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+"http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+<filter>
+  <filter-name>LiftFilter</filter-name>
+  <display-name>Lift Filter</display-name>
+  <description>The Filter that intercepts lift calls</description>
+  <filter-class>net.liftweb.http.LiftFilter</filter-class>
+</filter>
+  	
+
+<filter-mapping>
+  <filter-name>LiftFilter</filter-name>
+  <url-pattern>/*</url-pattern>
+</filter-mapping>
+
+</web-app>

BIN
lift-stateless/src/main/webapp/images/ajax-loader.gif


+ 18 - 0
lift-stateless/src/main/webapp/index.html

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
+    <title>Home</title>
+  </head>
+  <body class="lift:content_id=main">
+    <div id="main" class="lift:surround?with=default;at=content">
+      <h2>Welcome to your project!</h2>
+      <p>
+	<span class="lift:helloWorld.howdy">
+	  Welcome to your Lift app at <span id="time">Time goes here</span>
+	</span>
+      </p>
+    </div>
+  </body>
+</html>
+

+ 5 - 0
lift-stateless/src/main/webapp/static/index.html

@@ -0,0 +1,5 @@
+<div id="main" class="lift:surround?with=default;at=content">
+  Static content... everything you put in the /static
+  directory will be served without additions to SiteMap
+</div>
+

+ 85 - 0
lift-stateless/src/main/webapp/templates-hidden/default.html

@@ -0,0 +1,85 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+    <meta name="description" content="" />
+    <meta name="keywords" content="" />
+    <title class="lift:Menu.title">App: </title>
+    <style class="lift:CSS.blueprint"></style>
+    <style class="lift:CSS.fancyType"></style>
+    <script id="jquery" src="/classpath/jquery.js" type="text/javascript"></script>
+    <script id="json" src="/classpath/json.js" type="text/javascript"></script>
+    <style type="text/css">
+/* <![CDATA[ */
+.edit_error_class {
+  display: block;
+  color: red;
+}
+
+.sidebar ul {
+	margin:0;
+	padding:0;
+	border-bottom:1px solid #ccc;
+}
+	 
+
+.sidebar ul li {
+	margin:0;
+	padding:0;
+	list-style:none;
+	border:1px solid #ccc;
+	border-bottom:none;
+}
+
+.sidebar ul li a {
+	display:block;
+	padding:3px;
+	text-indent:30px;
+	text-decoration:none;
+}
+
+.sidebar ul li span {
+	display:block;
+	padding:3px;
+	text-indent:30px;
+	text-decoration:none;
+}
+
+.sidebar ul li a:hover {
+	background-color: #eee;
+}
+
+
+  /* ]]> */
+  </style>
+  </head>
+  <body>
+    <div class="container">
+      <div class="column span-12 last" style="text-align: right">
+        <h1 class="alt">app<img alt="" id="ajax-loader" style="display:none; margin-bottom: 0px; margin-left: 5px" src="/images/ajax-loader.gif"></h1>
+      </div>
+
+      <hr>
+
+      <div class="column span-6 colborder sidebar">
+        <hr class="space" >
+
+	<span class="lift:Menu.builder"></span>
+
+        <div class="lift:Msgs?showAll=true"></div>
+        <hr class="space" />
+      </div>
+
+      <div class="column span-17 last">
+        <div id="content">The main content will get bound here</div>
+      </div>
+
+      <hr />
+      <div class="column span-23 last" style="text-align: center">
+        <h4 class="alt">
+          <a href="http://www.liftweb.net"><i>Lift</i></a> 
+	  is Copyright 2007-2012 WorldWide Conferencing, LLC.
+	  Distributed under an Apache 2.0 License.</h4>
+      </div>
+    </div>
+  </body>
+</html>

+ 57 - 0
lift-stateless/src/main/webapp/templates-hidden/wizard-all.html

@@ -0,0 +1,57 @@
+<div>
+  <wizard:screen_info><div>Page <wizard:screen_number></wizard:screen_number> of <wizard:total_screens></wizard:total_screens></div></wizard:screen_info>
+  <wizard:wizard_top> <div> <wizard:bind></wizard:bind> </div> </wizard:wizard_top>
+  <wizard:screen_top> <div> <wizard:bind></wizard:bind> </div> </wizard:screen_top>
+  <wizard:errors> <div> <ul> <wizard:item> <li> <wizard:bind></wizard:bind> </li> </wizard:item> </ul> </div> </wizard:errors>
+  <div>
+    <wizard:fields>
+      <table>
+	<tbody>
+            <tr lift:bind="wizard:line">
+              <td>
+		<wizard:label>
+		  <label wizard:for="">
+		    <wizard:bind></wizard:bind>
+		  </label>
+		</wizard:label>
+		<wizard:help>
+		  <span>
+		    <wizard:bind></wizard:bind>
+		  </span>
+		</wizard:help>
+		<wizard:field_errors>
+		  <ul>
+		    <wizard:error>
+		      <li>
+			<wizard:bind></wizard:bind>
+		      </li>
+		    </wizard:error> 
+		  </ul>
+		</wizard:field_errors>
+              </td>
+              <td>
+		<wizard:form></wizard:form>
+	      </td>
+            </tr>
+	</tbody>
+      </table>
+    </wizard:fields>
+  </div>
+  <div>
+    <table>
+      <tr> 
+	<td>
+	  <wizard:prev></wizard:prev> 
+	</td>
+	<td>
+	  <wizard:cancel></wizard:cancel>
+	</td>
+	<td>
+	  <wizard:next></wizard:next>
+	</td>
+      </tr>
+    </table>
+  </div>
+  <wizard:screen_bottom> <div> <wizard:bind></wizard:bind> </div> </wizard:screen_bottom>
+  <wizard:wizard_bottom> <div> <wizard:bind></wizard:bind> </div> </wizard:wizard_bottom>
+</div>

+ 0 - 0
lift-stateless/src/test/resources/.keep


+ 23 - 0
lift-stateless/src/test/resources/logback-test.xml

@@ -0,0 +1,23 @@
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>console.devmode.log</file>
+        <append>true</append>
+        <encoder>
+            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+
+
+    <logger name="code.snippet" level="info" />
+    <logger name="net.liftweb" level="warn" />
+    <logger name="bootstrap.liftweb" level="info" />
+    <root level="warn">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>