Sonar Gerrit Plugin Release

Friday, December 20, 2013

Initial release

I am happy to announce a first release of my Sonar Gerrit plugin. This plugin reports Sonar violations on your patchsets to your Gerrit server. Sonar analyses full project, but only files included in patchset are commented on Gerrit. Please forward to project page for installation instructions.

This plugin is intended to use with Gerrit Trigger plugin for Jenkins CI server. Together they provide a great tool for automatic static code analysis.

How does it work?

At the moment you push a patchset to Gerrit, Jenkins is notified with a ssh event. It fetches a code with a patchset and it builds your changes. It quits when build or tests fail.

But if it succeeds, Sonar analase your project in a post-build action. This is a place where my Sonar Gerrit plugin shines. It asks Gerrit for changed files before analysis and after Sonar analysis is finished, plugin reports comments on these files as a Gerrit reviewer. Currently plugin always reports +1 for Code Review, as it's still in development. However, you should always treat these comments as hints to improve, not as direct errors.

Extras

I've released also a second plugin: Sonar File Alerts plugin. This plugin raises alerts on file level in Sonar. It extends default behaviour, which raises alerts only at root project level. It is useful when you create alert rules in Sonar like "Code Coverage < 60". Each file is checked against this rule!

If you use Sonar File Alerts plugin and an alert will be generated on some file, then a comment will be published on this file on Gerrit.

Feedback

Please provide a feedback on these plugins. Feel free to submit issues on github or comment. It's still an early stage so your input is very welcome!

Custom SonarQube rules for Unit Tests

Monday, December 2, 2013

I need a new rule

In our project we use (formely Sonar) to manage our code quality. It is a great tool and I recommend everyone to set it up and read its reports.

Recently, we've agreed that it's better to use assertj assertions in our unit tests than JUnit's. So I've decided to write a simple rule that checks if some of JUnit asserts assertTrue, assertFalse, assertNull and others are used. Then, I've discovered it's not so easy to do it with Sonar:

  • only 10 code quality rules are applied to unit tests - they are in special repository PMD Unit Tests (source)
  • these 10 rules are disabled by default, you have to enable them by hand
  • you cannot add new rules to this group

However, it turned out it is doable with a small tricks.

Custom PMD Unit Tests rule tutorial

Create your XPath expression by following this tutorial on how to create custom PMD rule. There is a visual editor to test your rules as you develop them - that's great. My XPath expression to avoid all JUnit assertions looks like this:

//PrimaryPrefix/Name[@Image='assertEquals' or @Image='assertNull' or @Image='assertNotNull' or @Image='assertSame' or @Image='assertNotSame' or @Image='assertArrayEquals' or @Image='assertTrue' or @Image='assertFalse']

Go to your Sonar installation, log in as an Administrator, head to Quality Profiles and select a profile that you use. Search for "xpath" and change Activation to Any. You should see two results like this:

Expand XPath rule template (dont' worry that it says it's deprecated) and then click Copy rule. Fill a form with message and XPath and save it. Then take a look at the bottom - you need an identifier of this rule:

You have created a PMD rule, now you need to move it to PMD Unit Tests group. Connect to Sonar's MySQL database. Search for your rule by key:

mysql> select id, plugin_rule_key, plugin_name, parent_id, status from rules where plugin_rule_key='XPathRule_1385721910';
+-----+----------------------+----------------+-----------+-------------+
| id  | plugin_rule_key      | plugin_name    | parent_id | status      |
+-----+----------------------+----------------+-----------+-------------+
| 903 | XPathRule_1385721910 | pmd            |      NULL | DEPRECATED  |
+-----+----------------------+----------------+-----------+-------------+
1 row in set (0.00 sec)

Update plugin_name and status (remember to use appropiate primary key for id column):

mysql> update rules set plugin_name='pmd-unit-tests', status='READY' where id=903;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

There is one step left. Sonar will change this rule's status to REMOVED on restart due to his boot checks. You need to trick him and change parent_id to other's PMD Unit Tests rule. List all these rules and choose one's identifier.

mysql> select id, plugin_name, status from rules where plugin_name='pmd-unit-tests';
+-----+----------------+---------+
| id  | plugin_name    | status  |
+-----+----------------+---------+
| 775 | pmd-unit-tests | READY   |
| 776 | pmd-unit-tests | READY   |
| 777 | pmd-unit-tests | READY   |
| 778 | pmd-unit-tests | READY   |
| 779 | pmd-unit-tests | READY   |
| 780 | pmd-unit-tests | READY   |
| 781 | pmd-unit-tests | READY   |
| 782 | pmd-unit-tests | READY   |
| 783 | pmd-unit-tests | READY   |
| 784 | pmd-unit-tests | READY   |
| 903 | pmd-unit-tests | READY   |
+-----+----------------+---------+
11 rows in set (0.00 sec)

Choose any id you like, let's say 775 and apply it as parent_id to your newly created rule:

mysql> update rules set parent_id=775 where id=903;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Go to your Quality profile and make sure your rule is active! Check it twice, it's easy to forget that step. It's all set up, enjoy your analysis!

Error generating web.xml file with IntelliJ IDEA

Thursday, August 1, 2013

If you use IntelliJ IDEA for your Grails development you might encounter this error running integration tests:

Error Error generating web.xml file (Use --stacktrace to see the full trace)

The reason for this is that IDEA adds classpath by default on creating integration test run configuration. Unfortunately, sometimes it causes strange errors like this one. Follow these steps to resolve:

  1. Open Run → Edit Configurations... (or press Alt+Shift+F10)
  2. Select your configuration that fails
  3. Uncheck Add --classpath checkbox
  4. You are done! Run.

Local Graphite installation on CentOS 5.5 howto

Tuesday, July 9, 2013

Feature request

Our client called:
- "I want something to monitor my application!"
- "Ok, let's use JMX, we have plenty of stats exposed there already."
- "Fine, but I want something fancy!"
- "Ok, let's install Graphite, take a look at screenshots!"
- "Excellent! Use our testing CentOS machine for this."
- "Ok, erm... wait, nooooo! We have no root access there, there is no internet access and we can't install anything!!!"
- "(hangup click)"

Setup

There I was, standing alone, looking for help...

    My scenario:
  • no internet access
  • only user account, no root account
  • gcc and make installed
  • some libraries missing
  • no development packages for installed libraries
    My goals:
  • install libraries to $HOME
  • install Graphite and stuff to $HOME/graphite

Solution

It took me a few days, but I've managed to install Graphite locally. Here is a gist for a sake of documentation and for others that may need it.

https://gist.github.com/SpOOnman/5957589

Simple trick to DRY your Grails controller

Wednesday, June 5, 2013

Grails controllers are not very DRY. It's easy to find duplicated code fragments in default generated controller. Take a look at code sample below. It is duplicated four times in show, edit, update and delete actions:

class BookController {
    def show() {
       def bookInstance = Book.get(params.id)
       if (!bookInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])
            redirect(action: "list")
            return
        }
        [bookInstance: bookInstance]
    }
}

Why is it duplicated?

There is a reason for that duplication, though. If you move this snippet to a method, it can redirect to "list" action, but it can't prevent controller from further execution. After you call redirect, response status changes to 302, but after method exits, controller still runs subsequent code.

Solution

At TouK we've implemented a simple trick to resolve that situation:

  1. wrap everything with a simple withStoppingOnRender method,
  2. whenever you want to render or redirect AND stop controller execution - throw EndRenderingException.

We call it Big Return - return from a method and return from a controller at once. Here is how it works:

class BookController {
    def show(Long id) {
        withStoppingOnRender {
            Book bookInstance = Book.get(id)
            validateInstanceExists(bookInstance)
            [bookInstance: bookInstance]
        }
    }

    protected Object withStoppingOnRender(Closure closure) {
        try {
            return closure.call()
        } catch (EndRenderingException e) {}
    }

    private void validateInstanceExists(Book instance) {
        if (!instance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])
            redirect(action: "list")
            throw new EndRenderingException()
        }
    }
}

class EndRenderingException extends RuntimeException {}

Example usage

For simple CRUD controllers, you can use this solution and create some BaseController class for your controllers. We use withStoppingOnRender in every controller so code doesn't look like a spaghetti, we follow DRY principle and code is self-documented. Win-win-win! Here is a more complex example:

class DealerController {
    @Transactional
    def update() {
        withStoppingOnRender {
            Dealer dealerInstance = Dealer.get(params.id)
            validateInstanceExists(dealerInstance)
            validateAccountInExternalService(dealerInstance)
            checkIfInstanceWasConcurrentlyModified(dealerInstance, params.version)
            dealerInstance.properties = params
            saveUpdatedInstance(dealerInstance)
            redirectToAfterUpdate(dealerInstance)
        }
    }
}

New HTTP Logger Grails plugin

Tuesday, May 7, 2013

I've wrote a new Grails plugin - httplogger. It logs:

  • request information (url, headers, cookies, method, body),
  • grails dispatch information (controller, action, parameters),
  • response information (elapsed time and body).

It is mostly useful for logging your REST traffic. Full HTTP web pages can be huge to log and generally waste your space. I suggest to map all of your REST controllers with the same path in UrlMappings, e.g. /rest/ and configure this plugin with this path.

Here is some simple output just to give you a taste of it.

17:16:00,331 INFO  filters.LogRawRequestInfoFilter  - << #1 GET http://localhost:8080/riddle/rest/index?username=admin&search=foo
17:16:00,340 INFO  filters.LogRawRequestInfoFilter  - << #1 headers Cookie: 'JSESSIONID=DF4EA5725AC4A4990281BD96963739B0; splashShown1.6=1', Accept-Language: 'en-US,en;q=0.8,pl;q=0.6', X-MyHeader: 'null'
17:16:00,342 INFO  filters.LogGrailsUrlsInfoFilter  - << #1 dispatched to rest/index with parsed params [username:[admin], search:[foo]].
17:16:00,731 INFO  filters.LogOutputResponseFilter  - >> #1 returned 200, took 405 ms.
17:16:00,745 INFO  filters.LogOutputResponseFilter  - >> #1 responded with '{count:0}'
17:18:55,799 INFO  filters.LogRawRequestInfoFilter  - << #2 POST http://localhost:8080/riddle/rest/login
17:18:55,799 INFO  filters.LogRawRequestInfoFilter  - << #2 headers Cookie: 'JSESSIONID=DF4EA5725AC4A4990281BD96963739B0; splashShown1.6=1', Accept-Language: 'en-US,en;q=0.8,pl;q=0.6', X-MyHeader: 'null'
17:18:55,800 INFO  filters.LogRawRequestInfoFilter  - << #2 body: 'username=admin&password=password'
17:18:55,801 INFO  filters.LogOutputResponseFilter  - >> #2 returned 404, took 3 ms.
17:18:55,802 INFO  filters.LogOutputResponseFilter  - >> #2 responded with ''

Official plugin information can be found on Grails plugins website here: http://grails.org/plugins/httplogger or you can browse code on github: TouK/grails-httplogger.

How to keep session in HttpBuilder with cookies

Monday, January 14, 2013

In my real-world scenario I have a REST service for AJAX purposes. It renders data series for graphs. I want to test it with groovy's excellent HttpBuilder. There is a problem though - these requests are only available for already logged in users.

In this post I present a complete solution to maintain a session state between HttpBuilder's requests.

Session in HttpBuilder

First of all a quick reminder about session. Session is a simulation of state for HTTP requests, which are stateless by its nature. Once you log in you receive a unique cookie (one or more) that identifies you for sequential requests. Every time you send request you send this cookie along. This way server recognizes you and matches you to your session, which is kept on server. Cookie gets invlid once you log out or it times out, for example after 20 minutes of inactivity. Next time you visit a page you get a new, unique cookie.

In order to keep session alive in HttpBuilder I need to:

  1. log in to my Grails application
  2. receive a JSESSIONID cookie in response
  3. store that cookie and send it along with every subsenquential request

I've created RestConnector class that wraps up HttpBuilder. It's main improvement is that it keeps received cookie in a list.

package eu.spoonman.connectors.RestConnector

import groovyx.net.http.Method
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator

class RestConnector {
    private String baseUrl
    private HTTPBuilder httpBuilder
    private List<String> cookies

    RestConnector(String url) {
        this.baseUrl = url
        this.httpBuilder = initializeHttpBuilder()
        this.cookies = []
    }

    public def request(Method method, ContentType contentType, String url, Map<String, Serializable> params) {
        debug("Send $method request to ${this.baseUrl}$url: $params")
        httpBuilder.request(method, contentType) { request ->
            uri.path = url
            uri.query = params
            headers['Cookie'] = cookies.join(';')
        }
    }

    private HTTPBuilder initializeHttpBuilder() {
        def httpBuilder = new HTTPBuilder(baseUrl)

        httpBuilder.handler.success = { HttpResponseDecorator resp, reader ->
            resp.getHeaders('Set-Cookie').each {
                //[Set-Cookie: JSESSIONID=E68D4799D4D6282F0348FDB7E8B88AE9; Path=/frontoffice/; HttpOnly]
                String cookie = it.value.split(';')[0]
                debug("Adding cookie to collection: $cookie")
                cookies.add(cookie)
            }
            debug("Response: ${reader}")
            return reader
        }
        return httpBuilder
    }


    private debug(String message) {
        System.out.println(message) //for Gradle
    }
}

A few things to notice in a class above. Constructor sets base URL and creates HttpBuilder instance that can be reused. Next, there is a handler on successful request that checks if I receive any cookie. It adds received cookies to list. Finally, there is a request method that calls HttpBuilder#request but it adds cookies to HTTP headers so server can recognize me as a logged in user.

Sending cookies with every request is a core component in here. It simulates browser's behavior and maintains session.

How to use it?

I will show you how to use this utility class it in Spock test below. It is fairly simple.

First I login to my application and I ensure that I receive a cookie in return, which is equivalent to being logged in. Then I send a request with that cookie sent in HTTP header. This is a Spock test that implements it:

package eu.spoonman.specs.rest

import eu.spoonman.connectors.RestConnector.RestConnector
import groovyx.net.http.ContentType
import groovyx.net.http.Method
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Stepwise

@Stepwise
class RestChartSpec extends Specification {
    @Shared
    RestConnector restConnector

    def setupSpec() {
        restConnector = new RestConnector('http://localhost:8080')
    }

    def "should login as test"() {
        given:
            Map params = [j_username: 'test', j_password: 'test']
        when:
            restConnector.request(Method.POST, ContentType.ANY, '/frontoffice/j_spring_security_check', params)
        then:
            !(restConnector.cookies.empty)
    }

    def "should allow access to chart data series"() {
        given:
            Map params = [days: 14]
        when:
            Map result = restConnector.request(Method.POST, ContentType.JSON, "frontoffice/chart/series", params)
        then:
            result != null
            result.series.size() > 0
    }
}

I create a new RestConnector instance in setupSpec with my application's base URL. Please notice that it has @Shared annotation so it's shared between tests.

@Stepwise is crucial annotation for this specification. It means that Spock executes tests exactly in order they're defined. I need to ensure that login is executed first. I also need to assert that I receive a cookie and list is not empty. I could move this step into setupSpec method too, but I prefer it to be a first test in a specification.

Second test is always executed after login thus it sends cookies within request headers. This is exactly what I wanted to achieve.