Showing posts with label liftweb. Show all posts
Showing posts with label liftweb. Show all posts

Friday, 16 November 2012

Hacking on liftweb 2.5 and sbt 0.12



STEP 1 Download the ‘Lift Basic’ template
prayag@prayag:/backup/workspace.programming/workspace.scala$ git clone git://github.com/lift/lift_25_sbt.git
Cloning into 'lift_25_sbt'...
remote: Counting objects: 881, done.
remote: Compressing objects: 100% (506/506), done.
remote: Total 881 (delta 301), reused 839 (delta 259)
Receiving objects: 100% (881/881), 3.03 MiB | 20 KiB/s, done.
Resolving deltas: 100% (301/301), done.

Going through the liftweb app structure
prayag@prayag:/backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29$ ls -l lift_basic/
total 1100
-rw-rw-r-- 1 prayag prayag    1159 Nov 16 16:33 build.sbt
drwxrwxr-x 2 prayag prayag    4096 Nov 16 16:33 project
-rwxrwxr-x 1 prayag prayag     116 Nov 16 16:33 sbt
-rw-rw-r-- 1 prayag prayag     137 Nov 16 16:33 sbt.bat
-rw-rw-r-- 1 prayag prayag 1103617 Nov 16 16:33 sbt-launch-0.12.jar
drwxrwxr-x 4 prayag prayag    4096 Nov 16 16:33 src

There is sbt-launch-0.12.jar, which means the Lift Basic template comes with a SBT launcher, so there’s no need to install sbt separately.

The src/main structure is 
prayag@prayag:/backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29$ ls -l lift_basic/src/main/
total 12
drwxrwxr-x 3 prayag prayag 4096 Nov 16 16:33 resources
drwxrwxr-x 4 prayag prayag 4096 Nov 16 16:33 scala
drwxrwxr-x 6 prayag prayag 4096 Nov 16 16:33 webapp

The MVC looking architecture of liftweb is 
prayag@prayag:/backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29$ ls -l lift_basic/src/main/scala/code/
total 20
drwxrwxr-x 2 prayag prayag 4096 Nov 16 16:33 comet
drwxrwxr-x 2 prayag prayag 4096 Nov 16 16:33 lib
drwxrwxr-x 2 prayag prayag 4096 Nov 16 16:33 model
drwxrwxr-x 2 prayag prayag 4096 Nov 16 16:33 snippet
drwxrwxr-x 2 prayag prayag 4096 Nov 16 16:33 view

STEP 2 : Building an app
Add Lift and Jetty to project dependencies:
#The build.sbt file is

name := "Lift 2.5 starter template"

version := "0.0.1"

organization := "net.liftweb"

scalaVersion := "2.9.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 :_*)

scalacOptions ++= Seq("-deprecation", "-unchecked")

libraryDependencies ++= {
  val liftVersion = "2.5-M3"
  Seq(
    "net.liftweb"       %% "lift-webkit"        % liftVersion        % "compile",
    "net.liftweb"       %% "lift-mapper"        % liftVersion        % "compile",
    "net.liftmodules"   %% "lift-jquery-module" % (liftVersion + "-2.0"),
    "org.mortbay.jetty" % "jetty" % "6.1.22"    % "container",
    "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",
    "org.specs2"        %% "specs2"             % "1.12.1"           % "test",
    "com.h2database"    % "h2"                  % "1.3.167"
  )
}

The project/plugins.sbt file is 
libraryDependencies <+= sbtVersion(v => "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")


The sbt script is 

prayag@prayag:/backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29/lift_basic$ sudo vi sbt


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

The command ‘sbt update ~jetty-run’ will download all necessary libraries (Scala, Lift, Jetty, etc) like mvn install does in maven, compile the application, and then launch Jetty as an embedded application – there’s no need to install Jetty separately, (~jetty-run isn't working in my case).
prayag@prayag:/backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29/lift_basic$ ./sbt update ~jetty-run


STEP 3 : Run app with sbt
sbt tasks can be viewed with $ sbt tasks command.
prayag@prayag:/backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29/lift_basic$ ./sbt
> update
> container:start
[info] Compiling 4 Scala sources to /backup/workspace.programming/workspace.scala/lift_25_sbt/scala_29/lift_basic/target/scala-2.9.1/classes...
[info] 'compiler-interface' not yet compiled for Scala 2.9.1.final. Compiling...
[info]   Compilation completed in 37.636 s
2012-11-16 23:14:24.820:INFO::Logging to STDERR via org.mortbay.log.StdErrLog
[info] jetty-6.1.22
[info] NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
[info] Started SelectChannelConnector@0.0.0.0:8080
[success] Total time: 56 s, completed 16-Nov-2012 23:14:30

Open http://localhost:8080 in a web browser to see the application running.


References
Getting Started, https://www.assembla.com/spaces/liftweb/wiki/Getting_Started

Sunday, 28 October 2012

Connecting Liftweb to MySQL

After successfully creating a liftweb app, here is how to connect liftweb to mysql and get things done.

The structure of the liftweb app is as follows:

prayag@prayag:~/workspace_lift/gwitter/gwitter$ ls -l
total 92
-rw-rw-r-- 1 prayag prayag 18432 Oct 28 10:45 lift_proto.db.h2.db
-rw-rw-r-- 1 prayag prayag 42000 Oct 28 10:45 lift_proto.db.trace.db
-rw-rw-r-- 1 prayag prayag  7459 Oct 28 11:10 pom.xml
drwxrwxr-x 3 prayag prayag  4096 Oct 16 12:22 project
drwxrwxr-x 4 prayag prayag  4096 Oct 16 12:22 src
drwxrwxr-x 9 prayag prayag  4096 Oct 28 11:14 target

STEP 1 configure db properties
prayag@prayag:~/workspace_lift/gwitter/gwitter$ sudo kate src/main/resources/props/default.props 

# Properties in this file will be read when running in dev mode
db.dialect=org.hibernate.dialect.MySQL5Dialect
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost/Gwitter
db.user=root
db.password=root

STEP 2 configure Database Vendor in Boot.scala
prayag@prayag:~/workspace_lift/gwitter/gwitter$ sudo kate src/main/scala/bootstrap/liftweb/Boot.scala

/**
 * A class that's instantiated early and run.  It allows the application
 * to modify lift's environment
 */
class Boot {
  def boot {
    //add the following code in method boot
    if (!DB.jndiJdbcConnAvailable_?) {
      println("jndiJdbcConnAvailable_ : " + 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)
    }//end of jndiJdbcConnAvailable_
 }//end of boot
}

STEP 3 Add maven dependency for mysql in pom.xml
<dependencies>
   ....
   .....
   .......
    <!-- for LiftConsole -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-compiler</artifactId>
      <version>${scala.version}</version>
      <scope>test</scope>
    </dependency>

    <!-- mysql dependency-->
    <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.17</version>
     <type>jar</type>
    </dependency>
  </dependencies>

STEP 4 Update maven dependency
prayag@prayag:~/workspace_lift/gwitter/gwitter$ mvn install

STEP 5 - Have a look at model
http://exploring.liftweb.net/master/index-2.html#lst:PocketChange-User-entity

prayag@prayag:~/workspace_lift/gwitter/gwitter$ sudo kate src/main/scala/com/zam/gwitter/model/User.scala

5.A)
Create a User class extending the Mapper base class MegaProtoUser, which provides default fields and methods
for a User.
class User extends MegaProtoUser[User] {
  def getSingleton = User // what's the "meta" server
                          // reference to the companion object below

  // define an additional field for a personal essay
  object textArea extends MappedTextarea(this, 2048) {
    override def textareaRows  = 10
    override def textareaCols = 50
    override def displayName = "Personal Essay"
  }
  
  //def findUser : User = 
    /User.find(By(User.id, this.id))
}

5.B)
Create a "companion object" to the User class (above).
The companion object is a "singleton" object that shares the same name as its companion class.
It provides global (i.e. non-instance) methods and fields, such as find, dbTableName, dbIndexes, etc.
object User extends User with MetaMegaProtoUser[User] {
  override def dbTableName = "User" // define the DB table name, I love to use singular names for tables
  override def screenWrap = Full( <lift:surround with="default" at="content">
           <lift:bind />
       </lift:surround>
      )
  // define the order fields will appear in forms and output
  override def fieldOrder = List(id, 
                                 firstName, 
                                 lastName, 
                                 email,
                                 locale, 
                                 timezone, 
                                 password, 
                                 textArea)

  // comment this line out to require email validations
  override def skipEmailValidation = true
}

STEP 6 Create database Gwitter at mysql server
mysql>create database Gwitter;

STEP 7 Run the app
prayag@prayag:~/workspace_lift/gwitter/gwitter$ mvn jetty:run

I get the message like the following in between :
[INFO] Scanning for projects...
...
...
...
11:18:52.922 [main] DEBUG net.liftweb.util.Props - Loaded key/value properties from resource /props/default.props
11:18:53.017 [main] DEBUG net.liftweb.mapper.MetaMapper - Initializing MetaMapper for User
11:18:53.018 [main] DEBUG net.liftweb.mapper.MetaMapper - Created FieldFinder for interface net.liftweb.mapper.MappedField
11:18:53.787 [main] DEBUG net.liftweb.db.ProtoDBVendor - Created new pool entry. name=ConnectionIdentifier(lift), poolSize=1
11:18:53.830 [main] DEBUG net.liftweb.mapper.Schemifier - Starting schemify. write=true, structureOnly=false, dbId=ConnectionIdentifier(lift), schema=esewa@10.20.222.254, tables=List(users)
11:18:53.832 [main] DEBUG net.liftweb.mapper.Schemifier - Running beforeSchemifier on table users
11:18:53.992 [main] DEBUG net.liftweb.mapper.Schemifier - Executing DDL statements
11:18:53.993 [main] DEBUG net.liftweb.mapper.Schemifier - Running afterSchemifier on table users
11:18:53.995 [main] DEBUG net.liftweb.db.ProtoDBVendor - Released connection. poolSize=1
2012-10-28 11:18:54.232:INFO::Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 5 seconds.

STEP 8 Check database
prayag@prayag:~$ mysql -u root -p
Enter password:

mysql> use Gwitter;

mysql> show tables;
+------------------------+
| Tables_in_Gwitter |
+-----------------------+
| users                      |
+-----------------------+
1 row in set (0.00 sec)


mysql> desc users;
+------------------+-------------------------+------+-----+-------------+--------------------+
| Field            | Type       | Null | Key | Default    | Extra                 |
+-------------------+-------------------------+------+-----+------------+--------------------+
| id                     | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| firstname         | varchar(32)              | YES  |     | NULL      |                           |
| lastname          | varchar(32)              | YES  |     | NULL      |                           |
| email               | varchar(48)               | YES  | MUL | NULL|                           |
| locale              | varchar(16)               | YES  |     | NULL      |                           |
| timezone         | varchar(32)              | YES  |     | NULL      |                           |
| password_pw  | varchar(48)              | YES  |     | NULL      |                           |
| password_slt   | varchar(20)             | YES  |     | NULL      |                           |
| textarea           | varchar(2048)        | YES  |     | NULL      |                           |
| uniqueid         | varchar(32)              | YES  | MUL | NULL|                           |
| validated         | tinyint(1)                  | YES  |     | NULL      |                           |
| superuser        | tinyint(1)                 | YES  |     | NULL      |                           |
+-----------------+-------------------------+-------+-----+------------+---------------------+
12 rows in set (0.01 sec)




REFERENCES
1 - How to create database connection with MySQL in Liftweb Scala, available at http://www.jooiner.com/how-to-create-database-connection-with-mysql-in-liftweb-scala/

2 - 2.1 Defining the Model, http://exploring.liftweb.net/master/index-2.html#lst:PocketChange-User-entity

3 - Scala/Lift Database Connections, http://stackoverflow.com/a/5026630/432903

4 - Mapper, https://www.assembla.com/spaces/liftweb/wiki/Mapper

5 - pocketchange app, https://github.com/tjweir/pocketchangeapp/tree/master/PocketChange/src/main

6 - How to create Simple Mini Blog With LiftWeb, https://github.com/slashmili/LiftWeblogIn30Mins/wiki/SimpleMiniBlog

Tuesday, 16 October 2012

Getting started with liftweb framework


Required 
Java
maven2 or sbt
jetty - a Java servlet engine
scala

STEP 1 Install Java and configure PATH
http://www.oracle.com/technetwork/java/javase/downloads/jdk7u9-downloads-1859576.html

STEP 2 Install scala 2.9.2
Download scala from http://www.scala-lang.org/downloads

$ wget  http://www.scala-lang.org/files/archive/scala-2.9.2.tgz

$ sudo mkdir /opt/scala292
Open the dowloaded file (Archive manager - file-roller) and extract to /opt/scala/

Create a sybolic link (future proof the install):
$ ln -s /opt/scala/scala-2.9.2 /opt/scala292/current

Define environment variable SCALA_HOME by editing /etc/profile and adding the line:
export SCALA_HOME=/opt/scala292/current

Add the Scala executables to your path by editing /etc/profile and adding the line:
export PATH=$PATH:$SCALA_HOME/bin

restart computer, and enter command
scala -version
Scala code runner version 2.9.2  -- Copyright 2002-2011, LAMP/EPFL


STEP 3 Install maven
$ sudo apt-get install maven2

OR
Download maven from http://maven.apache.org/download.html and configure PATH.
(mine /etc/profile is)

JAVA_HOME=/usr/java/jdk1.7.0; export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH; export PATH
export PATH=$PATH:/home/prayag/apache-maven-3.0.4/bin


STEP 4 Install jetty, an HTTP server, HTTP client, and javax.servlet container
$ sudo apt-get install jetty


STEP 5 Create liftweb app with mvn
$ mkdir /home/prayag/workspace_lift
$ cd /home/prayag/workspace_lift

Issue the following maven2 command for a lift project template downloaded from http://scala-tools.org/repo-snapshots :

mvn archetype:generate \
 -DarchetypeGroupId=net.liftweb \
 -DarchetypeArtifactId=lift-archetype-basic_2.9.1 \
 -DarchetypeVersion=2.4-M5 \
 -DarchetypeRepository=http://scala-tools.org/repo-releases \
 -DremoteRepositories=http://scala-tools.org/repo-releases \
 -DgroupId=com.zam.gwitter \
 -DartifactId=gwitter \
 -Dversion=1.0

The architecture of app will be as follows :

5 - A) The dependencies in pom.xml will be as follows :
<repositories>
    <repository>
      <id>scala-tools.releases</id>
      <name>Scala-Tools Dependencies Repository for Releases</name>
      <url>http://scala-tools.org/repo-releases</url>
    </repository>
    <repository>
      <id>java.net.maven2</id>
      <name>java.net Maven2 Repository</name>
      <url>http://download.java.net/maven/2/</url>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>scala-tools.releases</id>
      <name>Scala-Tools Plugins Repository for Releases</name>
      <url>http://scala-tools.org/repo-releases</url>
    </pluginRepository>
  </pluginRepositories>

  <dependencies>
    <dependency>
      <groupId>net.liftweb</groupId>
      <artifactId>lift-mapper_2.9.1</artifactId>
      <version>2.4-M5</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>0.9.26</version>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.2.138</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.7</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.scala-tools.testing</groupId>
      <artifactId>specs_2.9.1</artifactId>
      <version>1.6.9</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mortbay.jetty</groupId>
      <artifactId>jetty</artifactId>
      <version>6.1.25</version>
      <scope>test</scope>
    </dependency>
    <!-- for LiftConsole -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-compiler</artifactId>
      <version>${scala.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

5 - B) The gwitter/src/main/webapp/WEB-INF/web.xml file will be as follows :
<?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>


5 - C) The gwitter/src/main/webapp/index.html will be as follows :
<div class="lift:surround?with=default;at=content">
  <h2>Welcome to your project!</h2>
  <p>
    <div class="lift:helloWorld.howdy">
      Welcome to gwitter at <span id="time">The current time</span>
    </div>
  </p>
</div>



5 - D) The com/zam/gwitter/model/User.scala will be as follows :
package com.zam.gwitter {
package model {

import _root_.net.liftweb.mapper._
import _root_.net.liftweb.util._
import _root_.net.liftweb.common._

/**
 * The singleton that has methods for accessing the database
 */
object User extends User with MetaMegaProtoUser[User] {
  override def dbTableName = "User" // define the DB table name
  override def screenWrap = Full(<lift:surround with="default" at="content">
          <lift:bind /></lift:surround>)
  // define the order fields will appear in forms and output
  override def fieldOrder = List(id, firstName, lastName, email,
  locale, timezone, password, textArea)

  // comment this line out to require email validations
  override def skipEmailValidation = true
}

/**
 * An O-R mapped "User" class that includes first name, last name, password and we add a "Personal Essay" to it
 */
class User extends MegaProtoUser[User] {
  def getSingleton = User // what's the "meta" server

  // define an additional field for a personal essay
  object textArea extends MappedTextarea(this, 2048) {
    override def textareaRows  = 10
    override def textareaCols = 50
    override def displayName = "Personal Essay"
  }
}
}
}

5 - E)  The com/zam/gwitter/snippet/HelloWorld.scala snippet will be as follows:
package com.zam.gwitter {
package snippet {

import _root_.scala.xml.{NodeSeq, Text}
import _root_.net.liftweb.util._
import _root_.net.liftweb.common._
import _root_.java.util.Date
import com.zam.gwitter.lib._
import Helpers._

class HelloWorld {
  lazy val date: Box[Date] = DependencyFactory.inject[Date] // inject the date

  // bind the date into the element with id "time"
  def howdy = "#time *" #> date.map(_.toString)

  /*
   lazy val date: Date = DependencyFactory.time.vend // create the date via factory

   def howdy = "#time *" #> date.toString
   */
}//end of class
}
}

5 - F)  The com/zam/gwitter/lib/DependencyFactory.scala will be as follows :
package com.zam.gwitter {
package lib {

import net.liftweb._
import http._
import util._
import common._
import _root_.java.util.Date

/**
 * A factory for generating new instances of Date.  You can create
 * factories for each kind of thing you want to vend in your application.
 * An example is a payment gateway.  You can change the default implementation,
 * or override the default implementation on a session, request or current call
 * stack basis.
 */
object DependencyFactory extends Factory {
  implicit object time extends FactoryMaker(Helpers.now _)

  /**
   * objects in Scala are lazily created.  The init()
   * method creates a List of all the objects.  This
   * results in all the objects getting initialized and
   * registering their types with the dependency injector
   */
  private def init() {
    List(time)
  }
  init()
}

/*
/**
 * Examples of changing the implementation
 */
sealed abstract class Changer {
  def changeDefaultImplementation() {
    DependencyFactory.time.default.set(() => new Date())
  }

  def changeSessionImplementation() {
    DependencyFactory.time.session.set(() => new Date())
  }

  def changeRequestImplementation() {
    DependencyFactory.time.request.set(() => new Date())
  }

  def changeJustForCall(d: Date) {
    DependencyFactory.time.doWith(d) {
      // perform some calculations here
    }
  }
}
*/
}
}


STEP 6 Run a liftweb app
Issue mvn jetty:run and access the web application in your browser via http://localhost:8080

STEP 7 RESULT





REFERENCES
1 - Lift + Scala, Installation + First contact, 11. AUGUST 2010 available at http://tuxpool.blogspot.com/2010/08/lifting-scala-installation-first.html

2 - Using Maven, available at https://www.assembla.com/wiki/show/liftweb/Using_Maven