Thursday, 8 November 2012

Hacking on grails and mongodb

Part A : Connecting grails to mongodb

STEP 1 install mongodb
Install mongodb as described in STEP 5 here.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
$ echo 'deb http://downloads-distro.mongodb.org/repo/debian-sysvinit dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list

$ sudo apt-get update
sudo apt-get install mongodb-10gen

Or, there is mongohq.com with upto 512M for sandbox mongodb hosting.

STEP 2 : add mongodb plugin dependency
[METHOD 1]
Add mongodb dependency to grails-app/conf/BuildConfig.groovy

dependencies {
        // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.

        // runtime 'mysql:mysql-connector-java:5.1.20'

        // httpbuilder
        compile('org.codehaus.groovy.modules.http-builder:http-builder:0.5.0') {
                     excludes "commons-logging", "xml-apis", "groovy"
        }
       //mongodb dependency with no need to grails install-plugin mongodb
       compile ":mongodb:1.0.0.GA"
    }
NOTE: 
In older versions of Grails,   
application.properties was the only place where plugin dependencies could be specified. 
In Grails 1.2.X (or thereabouts) 
grails-app/conf/BuildConfig.groovy was introduced as the preferred place to specify plugin dependencies, but application.properties is still supported.

[METHOD 2] 
prayag@prayag:~/workspace_grails/eccount$ grails install-plugin mongodb
 Downloading: spring-data-mongodb-parent-1.0.0.RELEASE.pom.sha1
 Downloading: mongo-java-driver-2.7.1.pom.sha1
 Downloading: grails-datastore-gorm-1.0.7.RELEASE.pom.sha1
 Downloading: grails-datastore-gorm-plugin-support-1.0.7.RELEASE.pom.sha1
 [..]
 Plugin installed.


STEP 3 : remove hibernate plugin dependency
prayag@prayag:~/workspace_grails/eccount$ grails uninstall-plugin hibernate
OR
just remove or comment this line of code runtime ":hibernate:$grailsVersion" in  /grails-app/conf/BuildConfig.groovy
    plugins {
        //runtime ":hibernate:$grailsVersion"
        runtime ":jquery:1.8.0"
        runtime ":resources:1.1.6"

        // Uncomment these (or add new ones) to enable additional resources capabilities

        //runtime ":zipped-resources:1.0"
        //runtime ":cached-resources:1.0"
        //runtime ":yui-minify-resources:0.1.4"

        build ":tomcat:$grailsVersion"


        runtime ":database-migration:1.1"


        compile ':cache:1.0.0'

    }

STEP 4 : Create domain-class
Create domain-class and add some properties with mapping static mapWith="mongo"
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ sudo vi grails-app/domain/whotweetsnepal/Post.groovy

package whotweetsnepal


class Post {

    String username;
    String status;
    Date created = new Date();
    static mapWith="mongo"
    static constraints = {
        created(display:false)
    }
}


STEP 5 : Compile the app
prayag@prayag:~/workspace_grails/whotweetsnepal$ grails compile

STEP 6 : run the app
prayag@prayag:~/workspace_grails/whotweetsnepal$ grails run-app

STEP 7 : check mongodb
prayag@prayag:~$ mongo
MongoDB shell version: 2.2.1
connecting to: test

> show dbs;

WhoTweetsNepal  0.0625GB
local   (empty)
> use WhoTweetsNepal;
switched to db WhoTweetsNepal

If there is a error connecting to mongo, with exception :
$ mongo
MongoDB shell version: 2.2.3
connecting to: test
Sat Aug 31 05:42:52 Error: couldn't connect to server 127.0.0.1:27017 src/mongo/shell/mongo.js:91
exception: connect failed

follow the following commands
$ sudo rm /var/lib/mongodb/mongod.lock
$ sudo -u mongodb mongod -f /etc/mongodb.conf --repair

And then start service mongodb (with sudo privilege) and verify the port with
$ lsof -iTCP
And now try connecting,
$ mongo

Some mongo commands for productivity,

> cls or 
> Ctrl+l   -> clear console

If it's mongohq.com as a mongodb hosting, fire the following command to connect server
$ mongo paulo.mongohq.com:10012/WhoTweetsNepal -u prayagupd -p
MongoDB shell version: 2.2.3
Enter password: 
connecting to: paulo.mongohq.com:10012/WhoTweetsNepal
>

References
http://etcpe9.wordpress.com/2012/01/28/beginning-grails-2-0-with-mongodb/
https://github.com/mpriatel/mongodb-grails/wiki
http://mattwallace.me/mapping-current-grails-domain-classes-to-use
http://blog.mongodb.org/post/18510469058/grails-in-the-land-of-mongodb





Part B Using spring-security-core and spring-security-twitter

STEP 1 Install Spring-Security plugin, a fast and convenient solution for authorizing user access
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails install-plugin spring-security-core
Downloading: spring-security-core-1.2.7.3.pom
| Resolving plugin JAR dependencies

*******************************************************

* You've installed the Spring Security Core plugin.   *
*                                                     *
* Next run the "s2-quickstart" script to initialize   *
* Spring Security and create your domain classes.     *
*                                                     *
*******************************************************
| Plugin installed.



Next run the “s2-quickstart” script to initialize Spring Security and create domain classes.
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails s2-quickstart
| Compiling 153 source files
Usage: grails s2-quickstart <domain-class-package> <user-class-name> <role-class-name> [requestmap-class-name]
Creates a user and role class (and optionally a requestmap class) in the specified package
Example: grails s2-quickstart com.yourapp User Role
Example: grails s2-quickstart com.yourapp Person Authority Requestmap

The above script asks to run it with the specification of the classes names that should be generated.

The final s2-quickstart script will be as follows:
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails s2-quickstart com.zazzercode.whotweetsnepal User Role

*******************************************************
* Created domain classes, controllers, and GSPs. Your *
* grails-app/conf/Config.groovy has been updated with *
* the class names of the configured domain classes;   *
* please verify that the values are correct.          *
*******************************************************


[1.1] Check the views/
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ ls -l grails-app/views/login/
total 8
-rw-rw-r-- 1 prayag prayag 2721 Nov  8 14:40 auth.gsp
-rw-rw-r-- 1 prayag prayag  237 Nov  8 14:40 denied.gsp

The /login/auth.gsp is as follows :
<body>
<div id='login'>
        <div class='inner'>
                <div class='fheader'><g:message code="springSecurity.login.header"/></div>

                <g:if test='${flash.message}'>
                        <div class='login_message'>${flash.message}</div>
                </g:if>

                <form action='${postUrl}' method='POST' id='loginForm' class='cssform' autocomplete='off'>
                        <p>
                                <label for='username'><g:message code="springSecurity.login.username.label"/>:</label>
                                <input type='text' class='text_' name='j_username' id='username'/>
                        </p>

                        <p>
                                <label for='password'><g:message code="springSecurity.login.password.label"/>:</label>
                                <input type='password' class='text_' name='j_password' id='password'/>
                        </p>

                        <p id="remember_me_holder">
                                <input type='checkbox' class='chk' name='${rememberMeParameter}' id='remember_me' <g:if test='${hasCookie}'>checked='checked'</g:if>/>
                                <label for='remember_me'><g:message code="springSecurity.login.remember.me.label"/></label>
                        </p>

                        <p>
                                <input type='submit' id="submit" value='${message(code: "springSecurity.login.button")}'/>
</p>
                </form>
        </div>
</div>
<script type='text/javascript'>
        <!--
        (function() {
                document.forms['loginForm'].elements['j_username'].focus();
        })();
        // -->
</script>
</body>


[1.2] Check the controllers
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ ls -l grails-app/controllers/
total 12
-rw-rw-r-- 1 prayag prayag 4022 Nov  8 14:40 LoginController.groovy
-rw-rw-r-- 1 prayag prayag  356 Nov  8 14:40 LogoutController.groovy
drwxrwxr-x 2 prayag prayag 4096 Nov  6 12:54 whotweetsnepal

[1.3] Check the domains
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ ls -l grails-app/domain/com/zazzercode/whotweetsnepal/
total 12
-rw-rw-r-- 1 prayag prayag  177 Nov  8 14:40 Role.groovy
-rw-rw-r-- 1 prayag prayag  673 Nov  8 14:40 User.groovy
-rw-rw-r-- 1 prayag prayag 1275 Nov  8 14:40 UserRole.groovy


The Role.groovy domain will be as follows :

package com.zazzercode.whotweetsnepal

class Role {


        String authority

        static mapWith="mongo"
        static mapping = {
                cache true
        }

        static constraints = {

                authority blank: false, unique: true
        }
}



The User.groovy domain will be as follows:

package com.zazzercode.whotweetsnepal

class User {


        transient springSecurityService


        String username

        String password
        boolean enabled
        boolean accountExpired
        boolean accountLocked
        boolean passwordExpired
        static mapWith="mongo"
        static constraints = {
                username blank: false, unique: true
                password blank: false
        }

        static mapping = {

                password column: '`password`'
        }

        Set<Role> getAuthorities() {

                UserRole.findAllByUser(this).collect { it.role } as Set
        }

        def beforeInsert() {

                encodePassword()
        }
       
       def beforeUpdate() {
                if (isDirty('password')) {
                        encodePassword()
                }
        }

        protected void encodePassword() {
                password = springSecurityService.encodePassword(password)
        }
}

While the UserRole.groovy domain will be as follows : 
package com.zazzercode.whotweetsnepal

import org.apache.commons.lang.builder.HashCodeBuilder

class UserRole implements Serializable {

        User user
        Role role
        static mapWith="mongo"
        boolean equals(other) {
                if (!(other instanceof UserRole)) {
                        return false
                }

                other.user?.id == user?.id &&
                        other.role?.id == role?.id
        }

        int hashCode() {
                def builder = new HashCodeBuilder()
                if (user) builder.append(user.id)
                if (role) builder.append(role.id)
                builder.toHashCode()
        }

        static UserRole get(long userId, long roleId) {
                find 'from UserRole where user.id=:userId and role.id=:roleId',
                        [userId: userId, roleId: roleId]
        }
        static UserRole create(User user, Role role, boolean flush = false) {
                new UserRole(user: user, role: role).save(flush: flush, insert: true)
        }

        static boolean remove(User user, Role role, boolean flush = false) {
                UserRole instance = UserRole.findByUserAndRole(user, role)
                if (!instance) {
                        return false
                }

                instance.delete(flush: flush)
                true
        }

        static void removeAll(User user) {
                executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user]
        }

        static void removeAll(Role role) {
                executeUpdate 'DELETE FROM UserRole WHERE role=:role', [role: role]
        }

        static mapping = {
                id composite: ['role', 'user']
                version false
        }

[1.4] The following lines will be added to the grails-app/conf/Config.groovy

prayag@prayag:~/workspace_grails/WhoTweetsNepal$ sudo vi grails-app/conf/Config.groovy
// Added by the Spring Security Core plugin:
grails.plugins.springsecurity.userLookup.userDomainClassName = 'com.zazzercode.whotweetsnepal.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'com.zazzercode.whotweetsnepal.UserRole'
grails.plugins.springsecurity.authority.className = 'com.zazzercode.whotweetsnepal.Role'

STEP 2 : Change Bootstrap.groovy to create a user on grails app-startup

import com.zazzercode.whotweetsnepal.User
class BootStrap {

    def init = { servletContext ->

          def admin=User.findByUsername("admin")?:
                new User(username:"admin", password:"admin", enabled:true, accountLocked:false,accountExpired:false, passwordExpired:false).save(flush:true)
          if(admin.hasErrors()){
                  admin.errors.each{println it}
          }
    }
    def destroy = {
    }
}


STEP 3 : install spring-security-twitter
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails install-plugin spring-security-twitter
 | Downloading: spring-security-twitter-0.4.3.pom
 | Downloading: spring-parent-3.0.6.RELEASE.pom.sha1
 | Downloading: spring-asm-3.0.6.RELEASE.pom.sha1
 | Downloading: spring-web-3.0.6.RELEASE.pom.sha1
 | Downloading: grails-spring-security-twitter-0.4.3.zip.sha1
 | Downloading: spring-tx-3.0.6.RELEASE.jar.sha1


 the following conf will be added to Config.groovy
 // log4j configuration
log4j = {
    // Example of changing the log pattern for the default console appender:
    //
    //appenders {
    //    console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
    //}

    error  'org.codehaus.groovy.grails.web.servlet',        // controllers

           'org.codehaus.groovy.grails.web.pages',          // GSP
           'org.codehaus.groovy.grails.web.sitemesh',       // layouts
           'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
           'org.codehaus.groovy.grails.web.mapping',        // URL mapping
           'org.codehaus.groovy.grails.commons',            // core / classloading
           'org.codehaus.groovy.grails.plugins',            // plugins
           'org.codehaus.groovy.grails.orm.hibernate',      // hibernate integration
           'org.springframework',
           'org.hibernate',
           'net.sf.ehcache.hibernate'
}

grails installed plugins
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails list-plugins
| Environment set to development.....

Plugins available in the grailsCentral repository are listed below:

[...]

Plug-ins you currently have installed are listed below:

-------------------------------------------------------------
cache               1.0.0            --  Cache Plugin
database-migration  1.1              --  Grails Database Migration Plugin
hibernate           2.1.1            --  Hibernate for Grails
jquery              1.8.0            --  JQuery for Grails
mongodb             1.0.0.GA         --  MongoDB GORM
resources           1.1.6            --  Resources
spring-security-core1.2.7.3          --  Spring Security Core Plugin
spring-security-twitter0.4.3            --  Twitter Authentication
tomcat              2.1.1            --  Apache Tomcat plugin for Grails
webxml              1.4.1            --  WebXmlConfig


STEP 4 : Compile and Run the app
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails compile


The plugins can be viewed as 
prayag@prayag:~$ ls -l .grails/2.1.1/projects/WhoTweetsNepal/plugins/
total 48
drwxrwxr-x 4 prayag prayag 4096 Nov 22 12:50 cache-1.0.0
drwxrwxr-x 5 prayag prayag 4096 Nov 22 12:51 database-migration-1.1
drwxrwxr-x 4 prayag prayag 4096 Nov 22 12:50 hibernate-2.1.1
drwxrwxr-x 7 prayag prayag 4096 Nov 22 12:51 jquery-1.8.0
drwxrwxr-x 3 prayag prayag 4096 Nov 22 12:56 mongodb-1.0.0.GA
drwxrwxr-x 4 prayag prayag 4096 Nov 22 12:51 resources-1.1.6
drwxrwxr-x 5 prayag prayag 4096 Nov 22 12:56 spring-security-core-1.2.7.3
drwxrwxr-x 6 prayag prayag 4096 Nov 22 12:55 spring-security-twitter-0.4.3
drwxrwxr-x 6 prayag prayag 4096 Nov 22 12:54 spring-social-core-0.1.31
drwxrwxr-x 6 prayag prayag 4096 Nov 22 12:52 spring-social-twitter-0.1.31
drwxrwxr-x 3 prayag prayag 4096 Nov 22 12:50 tomcat-2.1.1
drwxrwxr-x 5 prayag prayag 4096 Nov 23 20:41 webxml-1.4.1


run-app
prayag@prayag:~/workspace_grails/WhoTweetsNepal$ grails run-app
| Running Grails application

Configuring Spring Security Core ...

... finished configuring Spring Security Core
Configuring Spring Security Twitter ...
| Server running. Browse to http://localhost:8080/WhoTweetsNepal

Goto browser and hit http://localhost:8080/WhoTweetsNepal/login/auth, there appears a login screen.

References
 http://grails.org/plugin/spring-security-twitter

Spring Security Grails Plugin. Quick Start and Some Tips, available at
http://sysgears.com/articles/spring-security-grails-plugin-quick-start-and-some-tips/

grails-spring-security-mongodb, available at https://github.com/evilangelist/grails-spring-security-mongodb

No comments:

Post a Comment