Logging in wicket

 

Grails GSP automatically injects the "log" service into every page. Now since Wicket replaces GSP as the default, we need a way to add the logger ourselves. The good news is I have a working solution, the bad news is it requires some additional typing. Hopefully someone can help improve this.

 

Wicket comes bundled with slf4j logging.

The default Grails log4j logging resulted in serialization errors from inside Wicket

So we are going to use slf4j in our code but still use Grails log4j config to control the debug output because there is a slf4j-log4j bridge (see the jars in the wicket plugin)

 

STEP 1:

 

In Config.groovy, specify log4j for the package so you can type : log.debug("blah")

As you can see it might be useful to name your packages starting with "com." like "com.home", "com.anotherpackage" etc so you can specify the parent "com" package in the log4j log config below instead of each individual package name. see below.

 

 

// 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')

    //}

 

    debug   'home'

   

   //other stuff goes here

}

 

 

Thanks to the below link that helped me with this final piece of the puzzle. 

http://buildchimp.com/wordpress/?p=290

 

STEP 2:

 

 

Parent class logging:

 

package home

import org.slf4j.LoggerFactory

import org.slf4j.Logger

 

public class HomePage extends WebPage {

 

 public HomePage(final PageParameters parameters) {

 

        transient Logger logme =   LoggerFactory.getLogger(index.class) //Why "transient" ? see http://stackoverflow.com/a/82552

 

        logme.info("SIMPLE INFO :-| ")

 

        logme.debug("SIMPLE DEBUG :) ")

 

        logme.error("SIMPLE ERROR :( ")

        

 

        add(new Link<Void>("clickMeLink"){

 

            @java.lang.Override

            void onClick() {

                logme.error("print this error to the logs") //WORKS!

                setResponsePage(SigninPage.class)

            }

 

        })

    }

}

 

 

 

Anonymous inner class logging:

 

package home

import org.slf4j.LoggerFactory

import org.slf4j.Logger

 

public class HomePage extends WebPage {

 

……parent code goes here

 

class EmailForm extends Form implements Serializable{

 

        transient Logger logme = Logger.getLogger(EmailForm.class);

 

        ValueMap properties = new ValueMap()

 

        public EmailForm(id){

            super(id)

 

            TextField email = new TextField("emailText", new PropertyModel(properties, "email"))

            email.setRequired(true)

            email.add(new EmailAddressValidator())

            add(email)

 

 

 

 

        }

 

        @Override

        protected void onSubmit() {

 

         

            logme.error( "EMAIL ENTERED : "  + properties.getString("email") )        }

 

 

    }

 

}

 

Website name and wicket app

So the Grails wicket application is accessible at

http://localhost:8080/<appname>/app

But I want to deploy it to say  www.naren.com without the <appname>/app in the URL (both the "context-root" which is the grails app name and the word "app" that is added by the wicket plugin). I initially tried setting the "app.context=/" in application.properties but that worked only for pure Grails GSP applications and the word "app" from the wicket plugin and the port "8080" was still getting appended to the URL. So it was time to get hardcore :)

I tried this out locally and instructions are for a Mac but it is not a stretch to get the idea

tldr; Use apache config to pretty up the URL

 

1) Create a hostname in /etc/hosts

127.0.0.1         naren

Flush the local DNS cache

dscacheutil -flushcache

Make sure you can ping the new hostname

ping naren

2) If you do not have virtualhosts enabled in apache config already do that

Filename : /etc/apache2/httpd.conf

Uncomment the below line

 Include /private/etc/apache2/extra/httpd-vhosts.conf

3) Add the below VirtualHost to your apache config file at :

/etc/apache2/extra/httpd-vhosts.conf

NOTE:

a)"idea" is my grails appname. Change it to your grails app name.

b) the Proxy module was already loaded in httpd.conf.

LoadModule proxy_module libexec/apache2/mod_proxy.so

If the below config does  not work, make sure to check that Proxy modules are loaded

<VirtualHost *:80>

        ServerName naren

        ServerAlias naren


        ProxyRequests Off

        ProxyPreserveHost On

 

        <Proxy *>

                Order deny,allow

                Allow from .naren

        </Proxy>

 

        ProxyPass / http://naren:8080/idea/app/

        ProxyPassReverse / http://naren/idea/app/


</VirtualHost>

 

4) Restart Apache

sudo apachectl restart

 

5) Start your grails wicket app

grails run-app

 

6) Access your grails wicket app at

http://naren

What a big difference from doing the below :)

http://localhost:8080/idea/app

The same approach can be used in production with some tweaks like "naren" in ProxyPass & ProxyPassReverse can be replaced with "localhost" because your DNS in production is most likely from GoDaddy. feel free to play with the settings.

 

Resources:

Using VirtualHosts and ProxyPass

http://www.thebuzzmedia.com/using-apache-virtual-hosts-and-proxypass-together/

http://confluence.atlassian.com/display/DISC/Using+Virtual+Hosts+on+both+Apache+and+Tomcat

http://www.grails.org/FAQ#Q: How do I setup Jetty behind Apache?


 

IDE : IntelliJ IDEA for wicket + Grails development

Just downloaded IntelliJ IDEA 10 for a 30 day trial.

Already impressed!!! Makes it a breeze for Wicket development using Grails. When I use Netbeans with wicket + java, the static typing allowed for a lot of auto-complete features that I missed when I started using TextMate for Grails + wicket.

Some of the auto-complete features are :

1) suggesting the import statements for the various wicket classes

2) automatically insert method stubs for abstract methods that need to be implemented, so I can start typing code immediately. for example as soon as I type add(new Link("name"), I would get prompted with

add(new Link("name"){

     public void onClick(){

}

})

 

 

All of this is available with IntelliJ when I import a Grails project as a new IDEA project

1) IMPORT PACKAGE NAME: the plugin jars are automatically available in the IDEA project and starts suggesting import statements which I accept by hitting ENTER... even better than right-clicking and clicking in Netbeans

2) IMPLEMENT ABSTRACT METHODS : I have to right-click over the wicket component code -> Generate -> Select the method to Override/Implement.. not ideal but not too much work either. On my Mac, I can do "cmd + i" for a keyboard shortcut that inserts the method stubs.

 

P.S:

I had to do this trick to make the "pages" folder inside the grails hierarchy accept packages

so I could create packages like "pages -> signin -> SignInPage.groovy/html"

"pages -> home -> HomePage.groovy/html"

 

When you right-click on the "pages" folder, there is no option to create a new "package"

but when you right-click on "pages" folder and select "Mark Directory As -> Source Root"

you can create packages that can be imported elsewhere as applicable.

How to get log.debug to work in Grails

Config.groovy:

 

log4j = {

debug 'grails.app'

}

Note:
If you do not want excessive logging in production ie. you want log.debug to print out only in development then add the log4j config under "environments" instead of the global log4j config.

environments {

    development {
        grails.serverURL = "http://localhost:8080/${appName}"
log4j = {
debug 'grails.app'
}
    }
}

 

Thanks to the mailing list answer below:

http://grails.1312388.n4.nabble.com/Can-t-get-log-debug-to-work-td3437287.html

 

More reading:

http://grails.org/doc/latest/guide/single.html#3.1.2 Logging

Download Sample Wicket + Grails app

Click here to download:
GrailsWicketDemo.zip (4.91 MB)

 

You should be able to do "grails run-app" after unzipping. I'm still learning grails so pardon some of the unconventional coding :)

UPDATE: Grails has upgraded since the I posted this. So additionally run "grails upgrade" on the downloaded project and you should be good to go.

the demo showcases
 a) Wicket + Grails
 b) Spring dependency injection 

the wicket  code is under the "pages" folder under grails-app (ignore the duplicate pages folder that is empty) 

1) You will have to create the database "test" with testuser/testpassword mentioned in DataSource.groovy
2) after starting up the app you can access the wicket app at http://localhost:8080/GrailsWicketDemo/app/
the password is "testing"
3) If you run into any issues please refer to my blog  at http://grailslog.posterous.com where I posted all the wicket issues I encountered and their resolutions.

Don't put the "+" sign at the beginning of line to concatenate string

WRONG:
String str = "{"
        + "\"id\":\"27e44\""
        + ",\"percent\":\"15\""
      
CORRECT:
String str = "{" +
         "\"id\":\"27e44\"" +
         ",\"percent\":\"15\""

See : http://jira.codehaus.org/browse/GROOVY-3319

2011-01-27 21:34:14,396 [http-8080-1] ERROR errors.GrailsExceptionResolver  - No signature of method: java.lang.String.positive() is applicable for argument types: () values: []
Possible solutions: notify(), size(), tokenize()
groovy.lang.MissingMethodException: No signature of method: java.lang.String.positive() is applicable for argument types: () values: []
Possible solutions: notify(), size(), tokenize()

POST JSON to URL from commandline

Ha! There is a UNIX command called "POST" and hence "GET" (see MAN page)

POST -c application/json http://localhost:8080/lifestyle-services/UserServlet
Please enter content (application/json) to be POSTed:
{"firstName":"Naren","email":"blah@gmail.com"}
CTRL+D

Press CTRL+D to complete the request and see the output printed out(assuming you print something out to be rendered on the web page)

 

Courtesy:

http://jira.codehaus.org/browse/GRAILS-3427

SQL error "Invalid value for getLong()" grails

turns out I had given a name for the "id" field as "productID" instead of just leaving it as "id".
 The default data type of the "id" field is "long".. so  since I had not provided an "id" field, it was implicitly assumed to be there as a "long" field and table mapping was trying to load the "String" value into the "long" id field and hence the error.

WRONG:
String productID


CORRECT :
String id


 //mapping to legacy tables
    static mapping = {
        table 'ProductTable'
        version false
        id column:'productID'
        id generator: 'uuid'