OneToMany relations with ordering in Grails – one form solution

The article was written during development. Therefore final solution differs from the code presented in the beginning of this post. If You want to see how working code looks like just check my Github.

It is quite common situation – we want two domain classes to share one form. In addition there is also oneToMany relation between them. And to make things a little bit harder we need to keep order of many side of the relationship – despite delete or create operations. With simple drag&drop functionality. Of course in Grails. Are You ready? Let’s go.

I started with solution described by ObjectPartners, but it got one big problem. It was using additional jQuery plugin, and therefore was doing some kind of ‘under the hood’ job, which was not suitable for my needs. So I have switched to plain jQuery and its plugins mainly due to main information source about this topic. My solution is fairly similar to described in omarello post, but if You do not need other form elements that inputs in your list and do not even think about sorting and ordering them, solution provided by by ObjectPartners will suffice.

Grails way of oneToMany

Before coding there is an important issue raised in comments section. Imports required for code snippets to work include:
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.userdetails.UserDetails

We should start with two simple domain classes:

package com.wordpress.chlebik

class Article {

	String title
	ArticleType type
	boolean deleted

	static transients = [ 'deleted' ]

	static constraints = {
		title(size: 3..12, nullabe: false)
	}

	String toString() {
		title
	}

}

enum ArticleType {

	NEWS,
	STORY

}

package com.wordpress.chlebik

class Newspaper {

	String name
	List<Article> articles = []

	static hasMany = [articles: Article]

	static mapping = {
		articles cascade:"all-delete-orphan"
	  }

    static constraints = {
		name(nullable: false, blank: false, size: 3..16)
    }

	String toString() {
		name
	}
}

They are simple but provide several usefull features – like list of types in enum and constraints. They are important in this example, but for now we should just focus on simple relation. Newspaper consists of Articles – you should pay attention to the fact, that I have used not only belongsTo mapping, but directly declared that there is a List of Articles. Default Grails’ behaviour with oneToMany relations is to use Set. We want to maintain order fo our Articles so I used List.

If You run application with default data source settings – Grails will automatically create tables in memory (with h2 db engine). Just visit http://localhost:8080/YOURAPPNAME/dbconsole and see by yourself what is going on. We have two simple tables for domain classes and one join table – NEWSPAPER_ARTICLE. There are two columns with foreign keys and what is more important – there is articles_idx column, which is needed for keeping order of articles. And all that thanks to Grails’ db creation. Awesome.

We got model created, now it is time for web layer. Within Grails’ console issue a command:

generate-all com.wordpress.chlebik.Newspaper

and in a moment we have generated controller and views. Unfortunately they do not provide possibility to create new articles within newspaper but it gives us a solid code-base to proceed. First, we should create a newspaper with articles in it. I will not use form generated, as it lacks functionality we need. Nothing as simple as issuing this code on controller or bootstrap:

Newspaper n = new Newspaper(name: 'ChlebikZine')

Article a1 = new Article(title: 'Title1', type: ArticleType.NEWS)
Article a2 = new Article(title: 'Title2', type: ArticleType.NEWS)
Article a3 = new Article(title: 'Title3', type: ArticleType.STORY)
n.articles.add(a1)
n.articles.add(a2)
n.articles.add(a3)

n.save()

Edit and update existing domain class

After running the application we can visit http://localhost:8080/YOURAPPNAME/newspaper/show/1 to see how our newspaper is rendered (I’ve changed page source generated by Grails, eg. removed links to articles edition).

ChlebikZine

It is show action and it looks fine. To now we can switch to editing. I am interested in list with editable fields. Right now I also want to have delete link to remove existing records (we do not add new ones so far). So our _form.gsp should look like this.

<%@ page import="com.wordpress.chlebik.Newspaper" %>

<script type="text/javascript">

jQuery(document).ready( function() {

	$(document).on("click", ".deleteArticle",function(event) {	

		var tableBody = $('#articlesListTableBody');
		var rowToDelete = $(this).closest('tr');
		var rowId = rowToDelete.attr('rowId');		

		// This is for removing only already existing rows in DB.
		if( !$(rowToDelete).attr('newRow') ) {
			$(tableBody).append("<input type='hidden' name='articles[" + rowId + "].deleted' value='true' />");
		}

		$(rowToDelete).remove();
		return false;
	});

	$('.addNewArticle').click( function() {

	});

} );

</script>
<div class="fieldcontain ${hasErrors(bean: newspaperInstance, field: 'name', 'error')} required">
	<label for="name">
		<g:message code="newspaper.name.label" default="Name" />
		<span class="required-indicator">*</span>
	</label>
	<g:textField name="name" maxlength="16" required="" value="${newspaperInstance?.name}"/></div>
<div class="fieldcontain ${hasErrors(bean: newspaperInstance, field: 'articles', 'error')} ">
	<label for="articles">
		<g:message code="newspaper.articles.label" default="Articles" />

	</label>
<table id="articlesList">
<thead>
<tr>
<th>Title</th>
<th>Type</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="articlesListTableBody">
		<g:each in="${newspaperInstance.articles}" var="article" status="i">
<tr rowId="${i}">
<td>
					<g:textField required="" name="articles[$i].title" value="${article.title}"/>
					<g:hiddenField name="articles[$i].id" id="articles[$i].id" value="${article.id}"/></td>
<td>
					<g:select value="${article.type}" name="articles[${i}].type" from="${com.wordpress.chlebik.ArticleType?.values()}" keys="${com.wordpress.chlebik.ArticleType.values()*.name()}" /></td>
<td>
					<a href="#" class="deleteArticle">Delete article</a></td>
</tr>
</g:each></tbody>
</table>
</div>

It is quite simple. Now here is a catch.

In Grails versions before 2.2.x UPDATE method was working differently than shown below. Solution given by ObjectPartners is working then but in newer versions it is not! Check out StackOverflow question. My solution was tested in Grails 2.4.3 with Groovy 2.3.6 and is mainly based on the solution given by Omarello. But it is just partly true. Keep reading to see what I’ve changed to make it work.

Therefore update method of the controller should like this:

 @Transactional
    def update(Newspaper newspaperInstance) {
        if (newspaperInstance == null) {
            notFound()
            return
        }

        if (newspaperInstance.hasErrors()) {
            respond newspaperInstance.errors, view:'edit'
            return
        }

		newspaperInstance.articles.eachWithIndex{ v,i ->
			if( params['articles[' + i + '].deleted'] == 'true' ) {
				v.deleted = true
			}
		}

		newspaperInstance.articles.removeAll{ it.deleted }
        newspaperInstance.save flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: 'Newspaper.label', default: 'Newspaper'), newspaperInstance.id])
                redirect newspaperInstance
            }
            '*'{ respond newspaperInstance, [status: OK] }
        }
    }

If you know better solution please let me know – it seems that wiring between form data/params is not transffering deleted property into created Newspaper entity. I’ve tried using:

newspaper.properties = params

With params filled with proper-named attributes for articles but it is not working! So I had to use additional iteration over all articles to set their deleted property. Again – if there is something I am doing wrong please let me know.

Now we should think about adding new articles to our newspaper. As You have seen it is quite simple – we just need to add inputs/selects to our table (in a view layer) with proper name attribute and that is all. Grails should do all the hard work. So after table we add simple link:

<a href="#" class="addNewArticle">Add new article</a>

And of course do not forget about Javascript.

$('.addNewArticle').click( function() {
		var allArticles = $("#articlesListTableBody tr[rowId]");
		var rowId = 0;			

		// This operation is performed to allow safe-delete of newly created (not saved in DB) articles
		if( allArticles.length ) {
			var maxId = 0;
        	allArticles.each(function() {
            	maxId = Math.max(maxId, parseInt($(this).attr('rowId')));
        	});
        	rowId = maxId+1;
		} 			

		$("#articlesListTableBody").append( '<tr newRow="true" rowId="' + rowId  + '">' +
		'<td><input type="text" required="" name="articles[' + rowId + '].title" value=""/></td>' +
                 '<td><select name="articles[' + rowId + '].type" >' +
        	'<g:each in="${com.wordpress.chlebik.ArticleType?.values()}" var="type">
<option value="${type}">${type}</option></g:each>
</select></td>
' +
									'
<td><a href="#" class="deleteArticle">Delete article</a></td>
</tr>
');

		return false;
	});

Nothing new here. We just add another row in our table. Pay attention to the fact, that there is newRow attribute in newly created row. Now it is clear why there was check for that attribute in delete function – if new article was not stored in DB, removal of it is not needed to be presented with deleted property set. You can play around with above solution – adding and deleting articles was never so easy. There is one thing to add – I resigned from cloning existing rows like it was presented in Omorello post. It is possbile when there are for sure rows in articles list and in my way – we can start with empty list so there is nothing to clone. Appending pure strings is maybe more low-level, but gives developer total control (which was compromised by using writetable-plugin).

Validate me

So far we did not checked two things – creation of totally new newspaper (that will come in a moment), ad validation (in facts it is about re-rendering of the view). Let’s have a look.

In generated views there is required attribute in inputs which are responsible for rendering domain class properties that have nullable : false in constraints. Newspaper name is one of them. Let’s see what will happen if we remove that attribute in GSP and submit empty newspaper name.

SaveSuccess

Apparently everything is all-right. Added article was successfully persisted in the DB. That is because our newspaper is separated from articles – constraint violation on newspaper do not stop persisting new article (because it is proper entity). Now let’s try to remove required attribute from article title and try to submit new article without it – sorry. We receive error message.

NPE

Do not ask me why and what does it mean. fortunately I know what helps – removing @Transactional annotation on our update method in controller!. If you remove it, form will re-render with proper error message. But try to submit form another time with new article empty title. It will give us error with finding deleted property in our code.

newspaperInstance.articles.removeAll{ it.deleted }

Somehow Grails managed to create new article which violates constraint (empty title) and is trying to save it! The cure for this is adding if statement in GSP that is listing all articles.

<g:each in="${newspaperInstance.articles}" var="article" status="i">
<tr rowId="${i}">
<td>
					<g:textField required="" name="articles[$i].title" value="${article.title}"/>
					<g:if test="${article.id}">
						<g:hiddenField name="articles[$i].id" id="articles[$i].id" value="${article.id}"/>
					</g:if></td>
<td>
					<g:select value="${article.type}" name="articles[${i}].type" from="${com.wordpress.chlebik.ArticleType?.values()}" keys="${com.wordpress.chlebik.ArticleType.values()*.name()}" /></td>
<td>
					<a href="#" class="deleteArticle">Delete article</a></td>
</tr>
</g:each>

This is the second time I do not know why Grails behaves like this – I understand that when there is no id attribute Grails will treat created domain class as new instance to persist. But how can it try to persist an entity which fails validation! Just to remember, generated update method has at the beginning:

if (newspaperInstance == null) {
      notFound()
      return
}

if (newspaperInstance.hasErrors()) {
     respond newspaperInstance.errors, view:'edit'
     return
}

Why this works for the first empty-title submit and fails at the second – I have no idea. Fortunately adding show-id logic in GSP helped.

But I want new newspaper

We started this topic from existing entity and editing it. It was easier that way to create clean code and show what is essential in oneToMany in one form. Right now we are going to create new newspaper and add some articles to it. There is already generated form and controller action. The truth is that – it works 馃槈 But only if we do not start to fool around validation again. To make long things short – there is a problem with treating newly added articles as new ones, not stored in DB (therefore they do not have id). _form.gsp must be modified in a section reponsobile for rendering article list (mainly safe-operator logic).

<table id="articlesList">
<thead>
<tr>
<th>Title</th>
<th>Type</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="articlesListTableBody">
		<g:each in="${newspaperInstance.articles}" var="article" status="i">
<tr <g:if test="${!article?.id}">newRow="true"</g:if> rowId="${i}">
<td>
					<g:textField required="" name="articles[$i].title" value="${article?.title}"/>
					<g:if test="${article?.id}">
						<g:hiddenField name="articles[$i].id" id="articles[$i].id" value="${article?.id}"/>
					</g:if></td>
<td>
					<g:select value="${article?.type}" name="articles[${i}].type" from="${com.wordpress.chlebik.ArticleType?.values()}" keys="${com.wordpress.chlebik.ArticleType.values()*.name()}" /></td>
<td>
					<a href="#" class="deleteArticle">Delete article</a></td>
</tr>
</g:each></tbody>
</table>

Now we can do whatever we want with creation of new entity – try to break validation, add and delete articles, try to break articles’ validation, whatever You want. It just works 馃槈

Gimme more – I want to sort things out

Generally I did not expect sorting to work so easily and quickly. Of course You need to download/link jQeury UI with sortable. After that You must change _form.gsp to this:

<%@ page import="com.wordpress.chlebik.Newspaper" %>

<script type="text/javascript">

jQuery(document).ready( function() {

	$(document).on("click", ".deleteArticle",function(event) {	

		var tableBody = $('#articlesListTableBody');
		var rowToDelete = $(this).closest('tr');	

		// This is for removing only already existing rows in DB.
		if( !$(rowToDelete).attr('newRow') ) {
			var rowId = $(rowToDelete).attr('rowId')
			var articleId = $("input[name='articles[" + rowId + "].id']").val();
			$(tableBody).append("<input type='hidden' name='articles[" + articleId + "].deleted' value='true' />");
		}

		$(rowToDelete).remove();
		return false;
	});

	$('.addNewArticle').click( function() {
		var allArticles = $("#articlesListTableBody tr[rowId]");
		var rowId = 0;			

		// This operation is performed to allow safe-delete of newly created (not saved in DB) articles
		if( allArticles.length ) {
			var maxId = 0;
        	allArticles.each(function() {
            	maxId = Math.max(maxId, parseInt($(this).attr('rowId')));
        	});
        	rowId = maxId+1;
		} 			

		$("#articlesListTableBody").append( '<tr newRow="true" rowId="' + rowId  + '">' +
		'<td><input required="true" type="text"  name="articles[' + rowId + '].title" value=""/></td>' +
                '<td><select name="articles[' + rowId + '].type" >' +
		'<g:each in="${com.wordpress.chlebik.ArticleType?.values()}" var="type">
                  <option value="${type}">${type}</option>
                 </g:each></select></td>' +
                 '<td  class="moveRow"><a href="#" class="deleteArticle">Delete article</a></td>
                  <td class="moveRow">Move</td></tr>');

		return false;
	});

	 $('#articlesListTableBody').sortable({
	        stop: function (event, ui) {
	            updateNames($(this))
	        },
	        handle: '.moveRow',
	    });

	 function updateNames($tbody) {
		    $tbody.find('tr').each(function (idx) {
		        var $inp = $(this).find('input,select,textarea');
		        $(this).attr('rowId', idx);
		        $inp.each(function () {
		            this.name = this.name.replace(/(\[\d\])/, '[' + idx + ']');
		        })
		    });
	}

   } );

</script>
<div class="fieldcontain ${hasErrors(bean: newspaperInstance, field: 'name', 'error')} required">
	<label for="name">
		<g:message code="newspaper.name.label" default="Name" />
		<span class="required-indicator">*</span>
	</label>
	<g:textField name="name" required="" maxlength="16"  value="${newspaperInstance?.name}"/></div>
<div class="fieldcontain ${hasErrors(bean: newspaperInstance, field: 'articles', 'error')} ">
	<label for="articles">
		<g:message code="newspaper.articles.label" default="Articles" />

	</label>
<table id="articlesList">
<thead>
<tr>
<th>Title</th>
<th>Type</th>
<th>Delete</th>
<th>Move</th>
</tr>
</thead>
<tbody id="articlesListTableBody">
		<g:each in="${newspaperInstance.articles}" var="article" status="i">
<tr <g:if test="${!article?.id}">newRow="true"</g:if> rowId="${i}">
<td>
					<g:textField required="" name="articles[$i].title" value="${article?.title}"/>
					<g:if test="${article?.id}">
						<g:hiddenField id="" name="articles[$i].id" value="${article?.id}"/>
					</g:if></td>
<td>
					<g:select value="${article?.type}" name="articles[${i}].type" from="${com.wordpress.chlebik.ArticleType?.values()}" keys="${com.wordpress.chlebik.ArticleType.values()*.name()}" /></td>
<td>
					<a href="#" class="deleteArticle">Delete article</a></td>
<td class="moveRow">
					Move</td>
</tr>
</g:each></tbody>
</table>
<a href="#" class="addNewArticle">Add new article</a></div>

JavaScript code used in this example comes from StackOverflow question – it works just out-of-the-box. But there is a catch – only with already persisted entities. So create new newspaper, add several new articles, save them. After that edit newspaper switch order and save it again. It works like a charm.

The problems begin when removal of rows or changing order of not yet persisted rows – I receieved many different errors and logical errors. It took me about two hours – after that I was not sure what was the reason that it started working 馃槈 But it does – I definetly added refreshing input-indexes after row removal in Javascript (same action that is triggered when sorting ends). I’ve removed delete property completly – and it still works. Magic.

So here is view and controller.

<%@ page import="com.wordpress.chlebik.Newspaper" %>

<script type="text/javascript">

jQuery(document).ready( function() {

	$('#articlesListTableBody').sortable({
        stop: function (event, ui) {
            updateNames($(this))
        },
        handle: '.moveRow',
    });

 	function updateNames($tbody) {
	    $tbody.find('tr').each(function (idx) {
	        var $inp = $(this).find('input,select,textarea');
	        $(this).attr('rowId', idx);
	        $inp.each(function () {
	            this.name = this.name.replace(/(\[\d\])/, '[' + idx + ']');
	        })
	    });
	}

	$(document).on("click", ".deleteArticle",function(event) {	

		var tableBody = $('#articlesListTableBody');
		var rowToDelete = $(this).closest('tr');	

		$(rowToDelete).remove();
		updateNames(tableBody);
		return false;
	});

	$('.addNewArticle').click( function() {
		var allArticles = $("#articlesListTableBody tr[rowId]");
		var rowId = 0;			

		// This operation is performed to allow safe-delete of newly created (not saved in DB) articles
		if( allArticles.length ) {
		    var maxId = 0;
        	    allArticles.each(function() {
            	        maxId = Math.max(maxId, parseInt($(this).attr('rowId')));
        	    });
        	    rowId = maxId+1;
		} 			

		$("#articlesListTableBody").append( '<tr newRow="true" rowId="' + rowId  + '">' + 									
                 '<td><input required="true" type="text"  name="articles[' + rowId + '].title" value=""/></td>' +
                 '<td><select name="articles[' + rowId + '].type" >' +
		'<g:each in="${com.wordpress.chlebik.ArticleType?.values()}" var="type">
                 <option value="${type}">${type}</option></g:each></select></td>' +
		'<td  class="moveRow"><a href="#" class="deleteArticle">Delete article</a></td><td class="moveRow">Move</td></tr>');

		return false;
	});

} );

</script>
<div class="fieldcontain ${hasErrors(bean: newspaperInstance, field: 'name', 'error')} required">
	<label for="name">
		<g:message code="newspaper.name.label" default="Name" />
		<span class="required-indicator">*</span>
	</label>
	<g:textField name="name" required="" maxlength="16"  value="${newspaperInstance?.name}"/></div>
<div class="fieldcontain ${hasErrors(bean: newspaperInstance, field: 'articles', 'error')} ">
	<label for="articles">
		<g:message code="newspaper.articles.label" default="Articles" />

	</label>
<table id="articlesList">
<thead>
<tr>
<th>Title</th>
<th>Type</th>
<th>Delete</th>
<th>Move</th>
</tr>
</thead>
<tbody id="articlesListTableBody">
		<g:each in="${newspaperInstance.articles}" var="article" status="i">
<tr <g:if test="${!article?.id}">newRow="true"</g:if> rowId="${i}">
<td>
	<g:textField id="" required="" name="articles[$i].title" value="${article?.title}"/>
		<g:if test="${article?.id}">
			<g:hiddenField id="" name="articles[$i].id" value="${article?.id}"/>
		</g:if></td>
<td>
	<g:select value="${article?.type}" name="articles[${i}].type" from="${com.wordpress.chlebik.ArticleType?.values()}" keys="${com.wordpress.chlebik.ArticleType.values()*.name()}" /></td>
<td>
	<a href="#" class="deleteArticle">Delete article</a>
</td>
<td class="moveRow">Move</td>
</tr>
</g:each></tbody>
</table>
<a href="#" class="addNewArticle">Add new article</a></div>
 def update(Newspaper newspaperInstance) {

        if (newspaperInstance == null) {
            notFound()
            return
        }

        if (newspaperInstance.hasErrors()) {
            respond newspaperInstance.errors, view:'edit'
            return
        }

	newspaperInstance.articles.clear()
	newspaperInstance.properties = params

	newspaperInstance.save flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: 'Newspaper.label', default: 'Newspaper'), newspaperInstance.id])
                redirect newspaperInstance
            }
            '*'{ respond newspaperInstance, [status: OK] }
        }
    }

Almost finished

Looking back at all the trouble I had with figuring out the proper solution made me wonder – why do not use AJAX? This is fine idea but I wanted to deal with pure Grails’ form handling. Full source (with final solution) can be found on my Github. As I mentioned before – I have used Grails 2.4.3 with Groovy 2.3.6.

ENUM in Grails with i18n support

Enums are pretty usefull聽Java聽feature. However, developers often encounter problems with two things – reflecting enum in database and i18n. Database is often a easy thing – store enum value in character column or even in specialised column type (eg. in聽MySQL). Internationalization is a little bit harder but fortunately聽Grails聽makes it so easy. The goal of this tutorial is to show how Grails can present enum values in a view layer with translated values.

The key interface is MessageSourceResolvable 聽which comes of course from聽Spring.聽We can have simple enum like this:

 

enum WarriorLevel implements org.springframework.context.MessageSourceResolvable {

    BEGGINER, NOVICE, APPRENTICE, MASTER

    // Methods inherited from interface
    public Object[] getArguments() { [] as Object[] }
    public String[] getCodes() { [ name() ] }
    public String getDefaultMessage() { name() }
}

Nothing new except implementation of methods inherited from interface. They are very simple, just for demonstration purposes.
Another thing to be done are internationalization entries. Grails keeps them in i18n folder with separate files for every language that can be supported. I come from Poland therefore my filename is messages_pl.properties. For given enum entries look like that (prefix is just for clearance):

com.wordpress.chlebik.BEGGINER=nowy
com.wordpress.chlebik=nowicjusz
com.wordpress.chlebik=adept
com.wordpress.chlebik=mistrz

Now the only thing is to put everything in motion – in a view layer we naturally use Grails tags to create select element:

<g:select name="chlebikTest" from="${WarriorLevel?.values()}" keys="${WarriorLevel .values()*.name()}" value="${warrior?.level?.name()}" valueMessagePrefix="com.wordpress.chlebik" />

And that’s all folks. Here are some links about it:

Grails page (check out last entry)
Enum in Grails domain from StackOverflow
Enum in GSP view from StackOverflow

Incrementing Grails application version during WAR packaging

Grails comes with several predefined properties, which can be used in application or build process. One of them is application version. It can be found in application.properties file and the usual way is to change it manually. Of course we can find a plugin – version updater – that makes that via Grails console. But what about situation in which every WAR creation should trigger version update?

Quick fix for that can be seen below (it is based on the script I have found on this blog). It is a simple script which should be places in /scripts folder of our project in a file named _Events.groovy. During WAR creation this script will be read and executed. Of course logic presented is fairly simple and lacks some parameters validation but I will leave it up to the developer.


eventCreateWarStart = { warName, stagingDir ->
	
	Map<String,Integer> mappings = [ 'M' : 0, 'm' : 1, 'p' : 2 ]
	String versionParam = System.getProperty('version.update.position', 'M')
	def lastAppVersionNumberList = metadata.'app.version'.split('\\.')
			   
	
	println "*** Started customised version update of Grails app. Current version: ${metadata.'app.version'}"
				
	lastAppVersionNumberList[ mappings[versionParam] ] =        Integer.valueOf(lastAppVersionNumberList[mappings[versionParam]]) + 1
	
	metadata.'app.version' = lastAppVersionNumberList.join('.')
	metadata.persist() 
	
	println "*** Ended customised version update of Grails app. Current version: ${lastAppVersionNumberList.join('.')}"
}

Grails logging class names

I have recently dived into聽Grails’ logging features. It is simple mechanism but I stumbled upon a problem with class names. If You want to log behaviour in controllers, domains or services – You have to prefix class name with prefix聽grails.application.聽So when I tried to log my services i should have write something like this:

warn   'grails.app.services.com.wordpress.chlebik.MyClass'

Instead of:

warn   'com.wordpress.chlebik.MyClass'

The truth is that documentation聽shows that in code samples – next time – read the manual stupid 馃槈

LDAP Authentication and DB authorisation with Grails Spring Security

Recently I faced quite simple problem – allow application to authenticate users via LDAP but authorise them via database. It is a common solution – we make sure that only company’s users are logging into app, but there comes flexibility with ACL stored locally. In Grails it looked pretty simple until I started to integrate this solution into my application. It took me a while to put everything together so I decided to share it.

To make long things short – there is an offical documentation but IMHO, it should be tweaked a little to be more user-friendly. The most important thing to understand are sides and roles that take place in logging process. There is LDAP instance, where we send requests to authorise user. LDAP response must be transmitted to our app and full our users’ object. Meanwhile there is also roles read from database. In addition there is simple check – if logging user has no account in our app, we will create it with default roles.

My app uses simple domain classes provided by Spring Security (User, Role and UserRole). If You are not familiar with them I suggest reading official docs. First we create UserMappingDetails class, which is simple POJO that will store user data (login, password, account data) and authorities (roles that user has).

class ChlebikUserDetails extends GrailsUser {

   ChlebikUserDetails(String username, String password, boolean enabled,
                 boolean accountNonExpired, boolean credentialsNonExpired,
                 boolean accountNonLocked,
                 Collection<GrantedAuthority> authorities,
                 long id, String fullName) {
      super(username, password, enabled, accountNonExpired,
            credentialsNonExpired, accountNonLocked, authorities, id)      
   }
}

Next we create mapping class, which will translate LDAP response to our application user.

class ChlebikUserDetailsContextMapper implements UserDetailsContextMapper {

	private static final List NO_ROLES = [new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)]
		
	@Override
	@Transactional
	public ChlebikUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<SimpleGrantedAuthority> authority) {
		
		User user = User.findByUsername(username)
		
		if(!user) {					
				// Create new user and save to the database                                
				user = new User()
				user.username = username				
				user.password = "123" // doesn't matter
				user.save(flush: true, failOnError: true)
									
				UserRole userRole = new UserRole()
				userRole.user = user
				userRole.role = Role.findByAuthority('USER') // Default role in my app
				userRole.save(flush: true, failOnError: true)			
		}
						 
		def authorities = user.getAuthorities().collect { new SimpleGrantedAuthority(it.authority) }		
		def userDetails = new ChlebikUserDetails(username, user.password, user.enabled, false,
					false, false, authorities, user.id, username)
		return userDetails
	}

	
	@Override
	public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) {}	
}

Ok, we are getting closer. Last class to create is service that will be called by Spring Security to retrieve our user when needed. It cannot be put in Grails’ service directory!!! Just place it in src/groovy

class PrePopulateUserDetailsService implements GrailsUserDetailsService  {
	
	Logger logger = Logger.getLogger(getClass())
	LdapUserDetailsService ldapUserDetailsService
	
	static final List NO_ROLES = [new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)]
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
		return loadUserByUsername(username, true)
	}
	
	@Override
	@Transactional	// Services in Grails are transactional by default but it is normal class so we put this annotation
	public UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException, DataAccessException {
				
		User user = User.findByUsername(username)
		def authorities = user.getAuthorities().collect {new SimpleGrantedAuthority(it.authority)}
		
		return new ChlebikUserDetails(user.username, user.password, user.enabled,
			!user.accountExpired, !user.passwordExpired,
			!user.accountLocked, authorities ?: NO_ROLES, user.id,
			user.username)   
	}
}

That’s all. Of course we need to add some config to our application. First we visit configuration folder and spring subfolder. There in resources.groovy we put this (registering beans):

beans = {		
	ldapUserDetailsMapper(com.wordpress.chlebik.ChlebikUserDetailsContextMapper)
	userDetailsService(com.wordpress.chlebik.PrePopulateUserDetailsService)
}

And in out Config.groovy (or whatever user-defined config file You are using) we should have several config properties. I am putting them all (to allow Grails connect to LDAP).

grails.plugin.springsecurity.providerNames = ['rememberMeAuthenticationProvider', 'ldapAuthProvider', 'anonymousAuthenticationProvider']

// Probably better to keep this data in an external file for security reasons
grails.plugin.springsecurity.ldap.context.managerDn = ''
grails.plugin.springsecurity.ldap.context.managerPassword = ''
grails.plugin.springsecurity.ldap.context.server = ''

grails.plugin.springsecurity.ldap.authorities.ignorePartialResultException = true // typically needed for Active Directory
grails.plugin.springsecurity.ldap.search.base = 'CN=putwhatyoumust,O=putwhatyoumust'
grails.plugin.springsecurity.ldap.authenticator.dnPatterns=='uid={0},CN=putwhatyoumust,O=putwhatyoumust'
grails.plugin.springsecurity.ldap.search.filter="uid={0}" 
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false

// These are crucial for LDAP/DB configuration
grails.plugin.springsecurity.ldap.mapper.userDetailsClass='com.wordpress.chlebik.ChlebikUserDetails'
grails.plugin.springsecurity.ldap.authorities.retrieveGroupRoles = false
grails.plugin.springsecurity.ldap.authorities.retrieveDatabaseRoles = true

And it magically works. Above code is the result of several hours on the web. Below I put all links that helped me a lot.

Grails passwords salted
Juan Vazquez GitHub
Most helpfull Nabble
Customizing authentication in your grails app
Official plugin page

Groovy 2.3.5 backward compatibility with Spock unit tests

In my new workplace there is a problem – corporate firewall, which is a common problem in IT world. Therefore update sites of Eclipse are blocked, which means quite a mess with dependencies and plugin management.

My main concern was Spock, which I have run using built-in Groovy in version 2.3.6, while tests and whole application was written in Grails 2.2.3 with Groovy a little bit older 馃槈 Finally it came to my attention that hated exception

Caused by: java.lang.ClassNotFoundException: org.codehaus.groovy.runtime.typehandling.ShortTypeHandling at java.net.URLClassLoader$1.run(URLClassLoader.java:372)

can be easily fixed in a way that Guillaume Laforge wrote. Just downloaded JAR, added to project’s classpath and there it is – my Spock test are working like charm.

Welcome back Grails

I started my journey with learning Java web-frameworks with Grails. That was really long time ago (around 2009) with Grails’ version 1.0.4. When I became full-time Java programmer I used Grails in several internal CRUD apps, but it was quick&dirty solution to provide basic functionality. My new job (I’ve just started) involves mainly programming with Groovy&Grails therefore I’ve decided to write a few posts about what is going on in Groovy&Grails world right now.

grails Before I start there is a big announcement. As Graeme Rocher posted on Twitter, new version of Grails (starting with number 3) will be totally new thing – based on Spring Boot and many projects around it. Therefore Grails being described here is going to retire in a predictable future. Just so You know.

The latest version of Grails which I am using is 2.4.3 with Groovy in version 2.3. In a following article I am gonna to point out some problems and interesting things I have discovered while ‘getting in touch’ with latest Grails.

Scaffolding

Scaffolding is often presented as one of the most ‘groovy’ thing in Grails. The ability to quickly prototype working application is powerful feature and it was a place where I started my revisit. And right at the beginning I have encountered an subtle problem. Reference documentation says that there is a possibility to run such code:

class UserController {
     static scaffold = true     
}

And it should work with existing User domain class. I have given it a try but I was unsuccessfull. Why? Grails complained about missing domain class. What have I done wrong? The problem is with packaging – I have never liked examples using default package. With Grails it is even more problematic as there are separate folders for controllers, domain classes and so on. Therefore as I created com.wordpress.chlebik.grails.tutorial package inside domain folder, framework could not resolve proper path. The solution is simple – just use full class name.

class UserController {
     static scaffold = com.wordpress.chlebik.grails.tutorial.User
}

It works like a charm. It is also possible to use such code to enable scaffolding for a given domain class even from controllers with different name:

class AdminController {
     static scaffold = com.wordpress.chlebik.grails.tutorial.User
}

In my opinion Grails scaffolding is even better than it was with feature called static scaffolding. Back in 2009 scaffolding was interesting, but it was done as a ‘runtime magic’. There was no way to interact with generated sources (or it was not so easy). Right now we have static scaffolding which is scaffolding but with generated sources. Imagine creating simple domain class like User

class User {

    static constraints = {}
	
    String       firsrName
    String       lastName	
    String       email	
	
}

Using Grails console or IDE support we can invoke command:

grails generate-all User

which creates separate views (I mean GSP files) and controller with implemented methods. What is more cool, are test classes (based on Spock specifications) generated as well with testing logic within! So if You are not familiar with unit-testing – it is a great place to learn how it is done.

Testing

A few years ago my knowledge about TDD was almost non-existent. It has changed since then, therefore I welcomed Grails’ testing support pretty great. As I mentioned above, we can be given out-of-the-box tests methods with logic in them. Of course it is not what TDD is about, but it makes a good start to make changes and go further. What is more interesting is use of mixins. They provide simple way to link tested class with tests itseslf. What is more cool is use of Spock testing framework. I have to admit I have a problem with finding which plugin/core functionality provides Spock as a dependency. There is Spock Grails Plugin but I do not see any dependencies to it in plain configuration files.

But enough of it – we have Spock at our service, so using it is very simple. Just like this:

@TestFor(UserController)
class UserControllerSpec extends Specification {

    def setup() {
    }

    def cleanup() {
    }

    void "test something"() {
    }
}

If You are trying to run above test it is possible You get initializationError. Test method must atually test something – leaving empty method body is not an option!

We are given several implicit objects like request, response, controller, view to support controller testing. We can use very straightforward annotation @Mock – which just instantiates in-memory GORM which mocks all ORM-related calls. There is also possiblity to run integration tests, and what is even more cool – even more plugin for functional tests. Back in time there was only Canoo available, now new solutions like Selenium and Geb are also an option. It is hard to write here all about testing in Grails. I am planning to wrote more in separate posts.

Config options

Config files are still very neat way to tail application to our needs. What is the most important change since I last worked with Grails is added BuildConfig.groovy file, which was designed to separate configuration of build process and application config itself. What is more fabulous is logging configuration – so many customizing possibilities! To keep things short I am planning also some kind of post about it, but there is a nice place to start.

Asset Pipeline Plugin

As a Java programmer I am not keeping up with latest changes in HTML&CSS&JavaScript community. There has been tremendous progress in that areas lately (not to mention HTML5, LESS&SASS, writing MVP apps in JavaScript). Therefore creators of Grails are trying to simplify several things so they have switched from resources-plugin to asset-pipeline-plugin.

With previous approach, all resources put in web-app folder were just laying there waiting for the programmer to use them. It was a matter of view layer to include or exclude used resources. Right now plugin has possibility to include or exclude portions of CSS or JavaScript used on certain page. Another cool thing is ability to automaticaly apply changes in resources code while in development mode. there is a great Youtube video about it.

Static compilation

Somewhere by the road to Groovy 2.0 and Java 7 appeared static compilation and invoke dynamic. To make long things short – in a situation where You do not need dynamic Groovy features, it is possible to disable them and gain boost on performance. There are is nice blog post worth mentioning.

Summary

As I walk through the latest reference documentation I constantly see something new. It is hard to put everything in one blog post therefore I am planning several additional articles about latest Grails and Groovy combo. Stay tuned.

Jak zarz膮dza膰 ba艂aganem framework贸w, czyli o narz臋dziu GVM

Z pewno艣ci膮 znacie ten b贸l, kiedy u偶ywa si臋 jednego z framework贸w czy j臋zyk贸w JVM, kt贸rych kolejne wersje wychodz膮 do艣膰 cz臋sto i dobrze by艂oby od czasu do czasu uaktualni膰 istniej膮ce instalacje. Zazwyczaj ko艅czymy z du偶膮 ilo艣ci膮 poinstalowanych wersji, w r贸偶nych miejscach i nie wiedz膮c do ko艅ca ile tego mamy i gdzie. Je偶eli nie jest to sytuacja Wam obca polecam zapoznanie si臋 z bardzo fajnym narz臋dziem o nazwie GVM, kt贸re jest napisane w czystym bash-u, a do sprawnego dzia艂ania potrzebuje tylko curl-a oraz unzip-a. Narz臋dzie to pozwala w bardzo wygodny spos贸b zarz膮dza膰 wszystkimi zainstalowanymi wersjami najr贸偶niejszych bibliotek – oto lista obecnie wspieranych:

  • Groovy
  • Grails
  • Griffon
  • Gradle
  • Groovyserv
  • Lazybones
  • vert.x
  • Crash
  • Glide
  • Gaiden
  • JBake
  • Springboot

Wi臋cej informacji oraz sposoby u偶ycia mo偶na znale藕膰 na oficjalnej stronie narz臋dzia.

Tag g:select w Grails

Ostatnio mia艂em z聽Grailsami聽cokolwiek niefajn膮 sytuacj臋, a posz艂o o tag g:select. Zasadniczo nie wiem, co zawini艂o – moje lu藕ne podej艣cie, brak dostatecznego przetestowania, czy ma艂o przejrzysta dokumentacja. Pewnie wszystko po trochu.

Zacznijmy jednak od pocz膮tku. Jak mo偶na si臋 艂atwo domy艣li膰 w GSP u偶ycie tagu g:select ma docelowo doprowadzi膰 do wyrenderowania znacznika <select>. P贸ki co wszystko jasne. Znacz膮cym u艂atwieniem jest r贸wnie偶 fakt, i偶 znacznik ten mo偶e posiada膰 atrybut multiple, kt贸ry pozwala u偶ytkownikowi na wybranie nie jednego, ale kilkunastu element贸w z listy. W moim przypadku by艂y to kategorie – cho膰 to rzecz bez znaczenia, grunt, i偶 etykietami by艂y 艂a艅cuchy znak贸w, za艣 przekazywanymi warto艣ciami identyfikatory liczbowe (konkretnie warto艣ci klucza g艂贸wnego w bazie danych). Przyjmijmy tak膮 oto sytuacj臋:

<g:select name="kategorie" from="${kategorieList}" value="${wybranePoprzednioID}"
 optionKey="id" multiple="true" />

Na li艣cie kategorii znajduje si臋 kilkana艣cie obiekt贸w, za艣 identyfikatory wybrane z listy (po np. wadliwej walidacji i ponownym wyrenderowaniu formularza) znajduj膮 si臋 w zmiennej/kolekcji o nazwie ${wybranePoprzednioID}. Po stronie kontrolera dzia艂a艂 taki oto pseudokod:

if( kategorie ) {
    kategorie.each {
       zapiszIDDoBazy( it )
    }
}

Kiedy wybierano na li艣cie tylko jeden element wszystko by艂o OK. Co wi臋cej – kiedy na li艣cie wybierano kilka element贸w r贸wnie偶 wszystko by艂o OK. Problem zacz膮艂 si臋 w momencie, gdy aplikacja wyl膮dowa艂a na serwerze preprodukcyjnym. W czym problem? Ot贸偶 po wybraniu jednego elementu z listy okazywa艂o si臋, 偶e po ponownym wy艣wietleniu formularza (po udanym zapisie do bazy danych) zaznaczona kategoria nie pokrywa艂a si臋 z t膮, kt贸r膮 wybrano przed zapisaniem. Ot zagwozdka. Przy wybieraniu dw贸ch lub wi臋cej element贸w – dzia艂a艂o poprawnie. Szybki debug i oto co si臋 okaza艂o.

Kategorie w bazie danych znalaz艂y si臋 ‘r臋cznie’. To znaczy, 偶e zosta艂y wprowadzone grubym klientem bezpo艣rednio do bazy danych i oczywi艣cie sekwencja przydzieli艂a 艂adne identyfikatory startuj膮c od numeru 1 (z krokiem te偶 o 1). Zatem mieli艣my bodaj偶e 7 kategorii, spo艣r贸d kt贸rych mo偶na by艂o wybiera膰, ka偶da posiadaj膮ca identyfikator mniejszy ni偶 10. Wraz z przej艣ciem na preprodukcj臋 do pracy wzi臋li si臋 nie tylko testerzy (ci przetestowali aplikacj臋 i wszystko by艂o OK, nawet po dodaniu kategorii), ale r贸wnie偶 docelowi u偶ytkownicy. A ci z kolei pododawali o wiele wi臋cej kategorii do wyboru i tym samym identyfikatory rekord贸w zacz臋艂y sk艂ada膰 si臋 z 2 cyfr. Co z tego wynika?

Ot贸偶 Groovy w konstrukcji each przechodzi po kolejnych elementach obiektu, na rzecz kt贸rego zosta艂 wywo艂any. W przypadku kolekcji oznacza to przej艣cie po wszystkich elementach, za艣 w przypadku 艂a艅cuch贸w tekstowych – po ka偶dym znaku w tym偶e 艂a艅cuchu!聽Przy wybraniu kilkunastu element贸w warto艣膰 parametru by艂a kolekcj膮 – zatem iteracja sz艂a dobrze. Przy zaznaczeniu kategorii ‘pocz膮tkowych’, czyli takich, kt贸re mia艂y identyfikatory mniejsze ni偶 10 r贸wnie偶 wszystko by艂o OK. Problemem by艂o wybranie 1 elementu o identyfikatorze np.聽14.聽Grails聽traktowa艂 w贸wczas parametr jako 艂a艅cuch tekstowy i wykonanie takiego kodu:

if( kategorie ) {
 kategorie.each {
println it
 }
}

Dla identyfikatora 14 dawa艂o na wyj艣ciu:


1
4

Czyli jako dwie oddzielne warto艣ci. Widzicie ju偶 problem? W bazie danych istnia艂y kategorie o takich identyfikatorach (1 oraz 4) zatem przy zapisie nie by艂o mowy o wyrzuceniu wyj膮tku czy b艂臋dach – wszystko si臋 zgadza艂o, kategorie istnia艂y to Grails grzecznie je zapisa艂. Problem by艂 taki, 偶e to dane wej艣ciowe nie by艂y do ko艅ca tymi, na kt贸re liczyli艣my. 聽Czyli zamiast jednej kategorii o identyfikatorze 14聽mieli艣my dwie kategorie o identyfikatorach聽1聽oraz聽4. 聽Typowy b艂膮d logiczny – godzinka na debugowaniu w plecy.聽 St膮d ten wpis i mam nadziej臋, 偶e dzi臋ki niemu zdarzy si臋 unikn膮膰 komu艣 takiej sytuacji 馃槈

Natywne zapytania SQL w Groovy i Grails

Grails je艣li chodzi o zabawy z baz膮 danych potrafi by膰 bardzo wdzi臋czny. Niestety czasami potrzebujemy odpyta膰 baz臋 w spos贸b, do kt贸rego magii Grails po prostu nie da si臋 u偶y膰. Tutaj z pomoc膮 przychodzi sam Groovy.

Za艂贸偶my, 偶e potrzebujemy zapytania sumuj膮cego szereg warto艣ci z tabeli. Pos艂u偶臋 si臋 zn贸w przyk艂adem tabeli z wpisami na blogu. Przyjmijmy, 偶e w wierszach trzymamy informacje o ods艂onach danego wpisu, ale z podzia艂em na np. dni. Czyli mamy dzisiejsze ods艂ony, ods艂ony z wczoraj i ods艂ony od pocz膮tku istnienia danego wpisu. U偶ycie Grails贸w w tym przypadku nie bardzo si臋 powiedzie zatem kod napiszemy w Groovym, korzystaj膮c z dobrodziejstw klasy groovy.sql.Sql.


 def dataSource

 String sqlAllCommand = "SELECT SUM(show_count_all) AS sca, SUM(show_count_yesterday) AS scy, SUM(show_count_today) AS sct FROM posts";
 groovy.sql.Sql sql = new groovy.sql.Sql( dataSource )

 sql.eachRow( sqlAllCommand, { row ->
			println row.sca + " " + row.scy + " " + row.sct
		} )

 sql.close()

Jak wida膰 rzecz jest do艣膰 prosta. Jedyn膮 rzecz膮 o kt贸rej nale偶y pami臋ta膰 jest skorzystanie z dependency injection dla otrzymania obiektu dataSource. Problemem niestety jest to, i偶 bezpo艣rednie pos艂ugiwanie si臋 beanem dataSource mo偶e w perspektywie powodowa膰 wyczerpywanie si臋 puli w膮tk贸w na serwerze lub komunikatem w stylu dataSource is closed (pomimo teoretycznego zamykania po艂膮czenia odpowiedni膮 metod膮). Poszukuj膮c w internecie rozwi膮zania natkn膮艂em si臋 na rozwi膮zanie bazuj膮ce na Hibernate. Oto ono:

def sessionFactory

String sqlAllCommand = "SELECT SUM(show_count_all) AS sca, SUM(show_count_yesterday) AS scy, SUM(show_count_today) AS sct FROM posts";
groovy.sql.Sql sql = new groovy.sql.Sql( sessionFactory.currentSession.connection() )

 sql.eachRow( sqlAllCommand, { row ->
		println row.sca + " " + row.scy + " " + row.sct
		} )

W tym przypadku pozwalamy by to Hibernate grzecznie u偶yczy艂 nam obiektu sesji, a po zako艅czeniu pracy bez jakiegokolwiek udzia艂u z naszej strony zajmuje si臋 po艂膮czeniem. B艂臋dy dotycz膮ce ilo艣ci po艂膮cze艅 znikaj膮.

Kilkukrotne wykorzystanie kryteri贸w szukania w Grailsach

Podobnie jak w poprzednim wpisie pozostajemy dzi艣 w obr臋bie wsp贸艂pracy Grails z Hibernate. Cho膰 mo偶e by by膰 bardziej konkretnym – z mechanizmem domkni臋膰 w Groovym.

Bardzo cz臋sto wyst臋puj膮c膮 sytuacj膮 jest stosowanie paginator贸w do prezentacji wynik贸w wyszukiwania lub po prostu wylistowania wszystkich danych z bazy. Ma to na celu u艂atwienie nawigacji u偶ytkownikowi, ale te偶 odci膮偶enie bazy i 艂膮cza (nie przesy艂amy na raz olbrzymich zbior贸w wynik贸w). Grailsy posiadaj膮 bardzo praktyczny tag GSP, kt贸ry generuje poprawne menu paginacji oraz obs艂uguje co trzeba. Warunkiem koniecznym dla poprawnego dzia艂ania tego mechanizmu s膮:

  • skonfigurowanie paginatora – czyli podanie podstawowych danych jak zakres wy艣wietlanych stron, bie偶膮c膮 stron臋 i tym podobne informacje
  • wyci膮gni臋cie danych – wyniki, kt贸re mamy zaprezentowa膰 musimy w jaki艣 spos贸b otrzyma膰 (lub po prostu strzeli膰 po nie do bazy)
  • poda膰 艂膮czn膮 ilo艣膰 wierszy w zbiorze wynikowym – i tym w艂a艣nie si臋 teraz zajmiemy

Posi艂kuj膮c si臋 przyk艂adem z poprzedniego wpisu (artyku艂y preentowane na blogu) – za艂贸偶my, i偶 艂膮czna ilo艣膰 tych偶e wynosi 166. W panelu zarz膮dzania blogiem, albo te偶 na g艂贸wnej stronie prezentowanie listy ich wszystkich mija si臋 z celem. Za艂贸偶my zatem, i偶 potrzebujemy do wy艣wietlenia tylko 10 rekord贸w. Otrzymanie ich z bazy danych jest do艣膰 proste (w przypadku Grails wr臋cz banalne – wystarczy odpowiednio manipulowa膰 metodami listuj膮cymi). Jednak偶e wypada艂oby otrzyma膰 informacj臋 o magicznej liczbie 166 (艂膮czna ilo艣膰 wierszy). Tutaj ponownie Grails za艂atwia za nas ca艂膮 robot臋 – metoda klasy domenowej o wdzi臋cznej nazwie count zwraca 艂膮czn膮 liczb臋 rekord贸w. Co jednak zrobimy w przypadku jakiejkolwiek formy filtrowania wynik贸w?

Za艂贸偶my, 偶e prezentowane posty mo偶emy filtrowa膰 w zale偶no艣ci od ich statusu. Posty mog膮 by膰 opublikowane, r贸wnie dobrze mog膮 by膰 postami prywatnymi (do ich ogl膮dania potrzebne s膮 specjalne uprawnienia) – mo偶liwych pomys艂贸w jest wiele. Mogliby艣my zastosowa膰 dynamicznych finder贸w Grails – czyli wywo艂a艅 metod na kszta艂t:

def postList = Post.findByStatusLike("opublikowany")

Rozwi膮zanie to sprawdza si臋 w przypadku prostych list. Je偶eli wpadniemy na pomys艂 by nasz膮 list臋 filtrowa膰 na podstawie kilku r贸偶nych kryteri贸w (i do tego u偶ywaj膮c ich jednocze艣nie), mo偶emy wpa艣膰 w k艂opoty buduj膮c w naszym kontrolerze/serwisie rozbudowane konstrukcje warunk贸w. Raczej nie t臋dy droga.

Odwo艂amy si臋 zatem po raz kolejny do Hibernate wraz z jego mechanizmem kryteri贸w. Naturaln膮 konsekwencj膮 by艂oby utworzenie obiektu kryteri贸w i nast臋pnie zaaplikowa膰 go do wywo艂ania metod listuj膮cych oraz zliczaj膮cych rekordy. Rzecz jest osi膮galna – poni偶szy listing powie wi臋cej ni偶 tysi膮c s艂贸w:


// Dwa 'kontenery' na kryteria
def listCriteria = Post.createCriteria()
def listCountCriteria = Post.createCriteria()

// Domyslne wartosci na podstawowe parametry paginatora
if( params.offset == null ) {
	params['offset'] = 0
}

if( params.max == null ) {
	params['max'] = 30
}

if( params.order == null ) {
	params['order'] = 'asc'
}

if( params.portalXX == null ) {
	params['portalXX'] = 'all'
}

def commonListCriteria = {

	// Wszystkie kryteria, ktorych zamierzamy uzyc do
	// wyciagniecia rekordow (okreslony status, daty)

   maxResults(params.max as Integer)
   firstResult(params.offset as Integer)

}

// Zwrocenie wynikow oraz zapytanie listujace
def postList = listCriteria.list(commonListCriteria)
def postCount = listCountCriteria.count(commonListCriteria)

Prezentowany kod wydaje si臋 by膰 niemo偶liwie prosty, jednak偶e by doj艣膰 do takiej jego postaci straci艂em chyba ze 3 godziny. Jak wida膰 ‘bazowe’ kryterium okre艣la r贸wnie偶 takie kryteria jak maksymalna ilo艣膰 rekord贸w na stronie oraz przesuni臋cie (offset), od kt贸rego nale偶y wyci膮gn膮膰 rekordy. R贸wnie dobrze zwr贸cone posty mog膮 reprezentowa膰 10t膮 stron臋 z 艂膮cznego zbioru wynikowego (przy params.offset = 10). Jednocze艣nie zastosowanie tych samych kryteri贸w dla metody zliczaj膮cej dalej zwr贸ci poprawn膮 艂膮czn膮 liczb臋 wierszy dla danych kryteri贸w (pomijaj膮c warto艣ci paginacyjne).

Sortowanie po wyra偶eniu SQL w Grails

Ostatnio mam okazj臋 produkcyjnie pobawi膰 si臋 Grailsami. Co cieszy – zw艂aszcza po niedawnych przygodach z prehistorycznym kodem, kt贸ry prezentowa艂 ogromniaste kolce i strzyka艂 na kilometr 艣mierdz膮c膮 艣lin膮. Jednak偶e jak zwykle co艣 delikatnie by艂o nie tak – st膮d ten wpis.

Na pierwszy rzut oka sprawa jest do艣膰 prosta. Chcemy posortowa膰 zwracane z bazy danych rekordy za pomoc膮 pewnego wyra偶enia. Za艂贸偶my, 偶e mamy tabel臋 z list膮 post贸w na blogu. Tabelka zawiera tytu艂, zajawk臋, dat臋 i wszystkie inne obowi膮zkowe w tym przypadku dane. Ka偶dy rekord zawiera te偶 informacj臋 o ilo艣ci ods艂on danego postu, a tak偶e o ilo艣ci klikni臋膰 na link do niego prowadz膮cy z g艂贸wnej strony bloga. Sprawa jest prosta:

SELECT * FROM posts 

Id藕my dalej. Mo偶e by艂oby fajnie wyci膮gn膮膰 posty o najwi臋kszym CTR (kliki do emisji)? Ok, lecimy:

SELECT * FROM posts ORDER BY main_page_click_count / emission_count DESC

Od strony logiki oraz kodu SQL sprawa jest niesamowicie prosta. Niestety w przypadku Grailsowego DSLa problem okaza艂 si臋 do艣膰 niebagatelny. Standardowe metody listuj膮ce ( list, findAll ) niestety nie przyjmuj膮 dla wyci膮gania/sortowania/grupowania wyra偶e艅 – otrzymywa艂em b艂膮d, i偶 dana w艂a艣ciwo艣膰 nie istnieje w klasie domenowej. Pr贸by stworzenia w艂asno艣ci typu transient (nie zapisywanej w bazie danych) jako wyniku dzia艂ania pewnej funkcji (w tym przypadku dzielenia dw贸ch innych w艂asno艣ci) r贸wnie偶 nie zako艅czy艂a si臋 powodzeniem.

Tutaj z pomoc膮 przyszed艂 ‘spodni’ mechanizm frameworku, czyli konkretnie Hibernate. W tym ORMie mamy co艣 takiego jak kryteria. Sprawa zacz臋艂a wygl膮da膰 na 艂atwiejsz膮, niestety, tylko wygl膮da膰. Pr贸ba u偶ycia restrykcji do klauzuli sortuj膮cej zako艅czy艂a si臋 niepowodzeniem. Ca艂e szcz臋艣cie znalaz艂em w necie wpis o dok艂adnie tym samym zagadnieniu. Kod przeze mnie prezentowany bazuje na zamieszczonym przez autora ww. wpisu.

Musimy utworzy膰 oddzieln膮 klas臋, kt贸ra b臋dzie fizycznie odpowiedzialna za dodanie kawa艂ka kodu SQL do naszego wynikowego zapytania. U mnie wygl膮da ona tak:

package com.wordpress.chlebik.hibernate.extension;

import org.hibernate.criterion.Order;
import org.hibernate.criterion.CriteriaQuery;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;


/**
 * Class extending Hibernate order by functionality to deal with
 * SQL-expression in order by  
 */
public class SortBySql extends Order {

    private static final long serialVersionUID = -6698545180750378282L;
    private String sqlFormula;
 
    /**
     * Constructor for Order.
     * @param sqlFormula an SQL formula that will be appended to the resulting SQL query
     */
    protected SortBySql(String sqlFormula) {
        super(sqlFormula, true);
        this.sqlFormula = sqlFormula;
    }
 
    public String toString() {
        return sqlFormula;
    }
 
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        return sqlFormula;
    }
 
    /**
     * Custom order
     *
     * @param sqlFormula an SQL formula that will be appended to the resulting SQL query
     * @return Order
     */
    public static Order sqlFormula(String sqlFormula) {
        return new SortBySql(sqlFormula);
    }   
    
}

Klas臋 t臋 oraz metody wywo艂ujemy w miejscu u偶ycia kryteri贸w Hibernate. Dzi臋ki sile domkni臋膰 kryteria mo偶na stworzy膰 oraz wywo艂ywa膰 w ten spos贸b:

 // To jest stworzenie obiektu kryteriow dla klasy domenowej 'Post'
 def listCriteria = Post.createCriteria()

 // To domkniecie, ktore przekazemy jako parametr do metody listujacej kryteriow
 def sortBySqlCriteria = {
           getInstance().addOrder( SortBySql.sqlFormula(" (main_page_click_count / emission_count) DESC ") )
 }
       
 // Wywolanie metod listujacych posty dla zadeklarowanych kryteriow          
def postList = listCriteria.list(sortBySqlCriteria) 

I w ten oto pi臋kny spos贸b uzyskali艣my zamierzony efekt. Zainteresowanych odsy艂am do ww. wpisu. W tym przypadku (chodzi o CTR) nale偶y r贸wnie偶 zwr贸ci膰 uwag臋 na mo偶liwy b艂膮d dzielenia przez zero! Trzeba go obs艂u偶y膰 na poziomie samej bazy danych – w moim przypadku by艂o to Oracle i posi艂kowa艂em si臋 rozwi膮zaniami zaprezentowanymi w tym po艣cie. Jednak偶e ostatecznie sprawa zako艅czy艂a si臋 ustaleniem domy艣lnej warto艣ci emisji nawi臋ksz膮 ni偶 0 i tym samym konieczno艣膰 obs艂ugi tego problemu mo偶na by艂o pomin膮膰.

HowToJava, ostateczny szlif

Po d艂u偶szym okresie walki z j臋zykiem Groovy i Grailsami uzna艂em, 偶e przyszed艂 czas by zako艅czy膰 prace nad aplikacj膮 HowToJava. Ostateczny kszta艂t nie pora偶a mo偶e mo偶liwo艣ciami, ale podczas prac dowiedzia艂em si臋 tylu nowych rzeczy, 偶e to co powsta艂o w pe艂ni mi wystarcza (pewnie nie raz sypnie wyj膮tkiem na serwerze :).

Rzecz g艂贸wnie w zupe艂nie odmiennej filozofii pracy z kodem (w odr贸偶nieniu choby od u偶ywanego na co dzie艅 PHP). Domy艣lam si臋, 偶e to sprawka samego j臋zyka, ale te偶 i frameworka, kt贸ry upraszcza wszystko niemo偶ebnie, co momentami jednak偶e prowadzi do wkurzaj膮cych wr臋cz sytuacji, z kt贸rymi musia艂em sobie poradzi膰. Z perspektywy czasu nie uwa偶am poznania tej dziedziny programowania w Javie za stracony! Jak radzi艂 na pocz膮tku Jacek Laskowski poznanie Grails贸w daje mo偶liwo艣膰 zapoznania si臋 z 艣wietnie dzia艂aj膮cym konglomeratem kilkunastu uznanych powszechnie technologii/framework贸w/abstrakcji, a ostatecznie przy bli偶szym poznaniu sk艂adowych – docenienie Grails贸w w pe艂ni.

Zak艂adam, 偶e jednak偶e minie jeszcze troch臋 czasu, zanim b臋d臋 m贸g艂 kompetentnie si臋 w tej dziedzinie wypowiada膰. Na razie kontynuuj臋 przygotowania do certyfikacji SCJP. W ko艅cu czerwca udaj臋 si臋 na tygodniowy urlop, zak艂adam zatem, 偶e do tego czasu z ca艂膮 pewno艣ci膮 do egzaminu nie b臋d臋 podchodzi艂. Ale lipiec to jak najbardziej realny termin. Z braku wykszta艂cenia kierunkowego musz臋 nadrabia膰 zdawaniem certyfikat贸w, c贸偶 pocz膮膰 馃檪

Zastanawiam si臋 tak偶e nad rozpocz臋ciem prac nad nowym projektem (no bo ile mo偶na katowa膰 czyst膮 Jav臋 馃檪 – teraz tylko pytanie o technologi臋. Czy wyj艣膰 z poziomu niewiele ni偶szego ni偶 Grails? Mo偶e zatem czysty Hibernate i Spring? A mo偶e troch臋 nowoczesno艣ci – GWT lub Seam (ten ostatni ma fajn膮 dokumentacj臋)? Ci膮gn膮 te偶 rozwi膮zania niskopoziomiowe – JSP, JSF, Struts. Ka偶dy wyb贸r ma swoje wady i zalety. Powiedzmy sobie szczerze – nikt raczej nie zatrudnia m艂odzika na juniora by od razu po przyj艣ciu kodowa艂 olbrzymie systemy w Springu. Za艣 do konserwacji przedpotopowego kodu pisanego w JSP ju偶 jak najbardziej tak. Ankieta u Jacka r贸wnie偶 jest ciekawa i daje wiele do my艣lenia. No nic, dam sobie kilka dni do przemy艣le艅.

PS. HowToJava mo偶na te偶 obejrze膰 pod 艂atwiejszym adresem http://chlebik.pl

Grailsujemy dalej – zadajemy pytania w HowToJava

Po d艂u偶szej przerwie wr贸ci艂em do kodowania w Grails. Zosta艂o jeszcze par臋 rzeczy, by uzna膰, 偶e na bardzo podstawowym poziomie, przyk艂adowa aplikacja HowToJava jest sko艅czona. Dzi艣 pierwszy krok na tej drodze – mo偶liwo艣膰 tworzenia temat贸w/zadawania pyta艅.

Warstwa widoku, walidacji b艂臋d贸w, klas domenowych nie wychodzi poza rzeczy, kt贸rej do tej pory opisywa艂em. Na tym temacie zatem nie b臋d臋 si臋 specjalnie skupia艂. Jedyn膮 rzecz膮, kt贸r膮 mo偶naby prze膰wiczy膰 jest kolejny plugin, kt贸ry zamierzam u偶y膰 – FCKEditor. Biblioteka ta jest uniwersalnym GUI przeznaczonym dla stron internetowych. Umo偶liwia wyczynianie cud贸w z wpisywanym tekstem – dla potrzeb mojej aplikacji jest tego a偶 nadto. Zainteresowanych samym edytorem mog臋 odes艂a膰 na jego stron臋 domow膮. Za艣 dla nas bardziej interesuj膮ca b臋dzie strona opisuj膮ca plugin, kt贸ry umo偶liwia wykorzystanie mocy tego偶 edytora w Grails.

Standardowo przechodzimy do katalogu z aplikacj膮 i wydajemy polecenie:

grails install-plugin fckeditor

I po chwili mo偶emy cieszy膰 si臋 kolejn膮 funkcjonalno艣ci膮 w aplikacji. Zainstalowa艂em wersj臋 0.9.2, kt贸ra jest najnowsz膮, mam zatem nadziej臋, 偶e da si臋 z niej korzysta膰. Kiedy integrowa艂em swego czasu FCKEditora z Zend Framework mia艂em troch臋 zabawy. No i wykraka艂em…

Okazuje si臋, 偶e skrypt 艣ci膮gaj膮cy plugin nie rozpakowywuje go odpowiednio. Zatem musia艂em wybra膰 si臋 na stron臋 pluginu i 艣ci膮gn膮膰 go dla pewno艣ci r臋cznie. Nast臋pnie plik *.zip trzeba rozpakowa膰 do katalogu /plugins, a konkretniej do folderu fckeditor-0.9.2. Za艣 w widoku wrzucamy taki oto kod:

<fckeditor:editor
name="question"
width="100%"
height="200"
toolbar="Basic"
fileBrowser="default">
</fckeditor:editor>

No i dzia艂a. Wrzuci艂em minimalny pasek z narz臋dziami, ale to mo偶na 艂atwo zmieni膰. Dla bardziej zaawansowanej konfiguracji polecam przyjrzenie si臋 plikowi fckconfig.js w katalogu z pluginem.
Walidacja jest prosta jak konstrukcja gwo藕dzia (ogranicza si臋 tylko i wy艂膮cznie do walidacji klasy domenowej). Jedynym problemem by艂a data dodania. Jako艣 na pocz膮tku swej przygody z programowaniem dla web pa艂a艂em prawdziw膮 mi艂o艣ci膮 do typu DATETIME w MySQL. Jednak偶e jaki艣 czas temu zosta艂em wyleczony z u偶ywania tej kolumny, na rzecz TIMESTAMPA (unixowego). Po prostu wyszukiwanie po kolumnach jest kilkakrotnie szybsze (warto艣ci TIMESTAMP s膮 zapisywane jako INT(11) ), a tak偶e sam format TIMESTAMP jest o wiele bardziej uniwersalny – ka偶dy j臋zyk programowania (no, przynajmniej te w kt贸rych kodowa艂em) mia艂 bardzo sympatyczne wsparcie dla tego formatu. W Javie obecny TIMESTAMP (uwaga: konkretnie chodzi mi o ilo艣膰 sekund od 1970, a nie milisekund, co zreszt膮 w kodzie wida膰) mo偶na wydoby膰 w taki spos贸b:

Math.round( System.currentTimeMillis()/1000 )

I takim oto szybkim obej艣ciem da艂o si臋 to za艂atwi膰. Tak wygl膮da widok dodaj膮cy nowy temat:

dodawanieTematu

Chlebik walks alone – deployment HowToJava

Zgodnie z zapowiedziami dorobi艂em si臋 w ko艅cu w艂asnej przestrzeni w necie. Do tego przestrzeni ca艂kiem sympatycznej bo z w艂asnym Jetty i paroma innymi rzeczami. Rzecz jasna trzeba by艂o spr贸bowa膰 ponownie pokaza膰 艣wiatu moje Grailsowe wypociny.

Samego procesu deploymentu do tej pory nie rusza艂em. Lokalnie uruchamianie aplikacji odbywa si臋 poprzez NetBeans, zap臋d贸w do konfiguracji serwer贸w/kontener贸w te偶 p贸ki co nie mam. Jak wspomnia艂em r贸wnie偶 wcze艣niej – nied艂ugo Helion wyda na podobne tematy ksi膮偶k臋, zatem poczekam jeszcze troch臋 by zapozna膰 si臋 z tematem dog艂臋bniej. Jednak偶e by wrzuci膰 co艣 na serwer docelowy z ca艂膮 pewno艣ci膮 wypada skonfigurowa膰 dost臋p do bazy danych. Oczywi艣cie by to osi膮gn膮膰 wypada wpisa膰 odpowiednie dane dost臋powe do DataSource.groovy.

Kiedy si臋 z tym upora艂em przyszed艂 czas na zbudowanie paczki dystrybucyjnej. W Grails jest to proste jak konstrukcja gwo藕dzia. Wystarczy przej艣膰 do katalogu z aplikacj膮 i wyda膰 polecenie:

grails war

Pomieli, przemieli i ostatecznie w tym偶e katalogu wypluje nam paczk臋 (plik z rozszerzeniem *.WAR). Teraz ten plik (ja jego nazw臋 zmieni艂em na howtojava.war, wyrzucaj膮c numeracj臋 wersji) nale偶y wrzuci膰 na serwer produkcyjny. Domy艣lnie Jetty wszystkie aplikacje pr贸buje odnale藕膰 w katalogu /webapps. Konwencja ta jest s艂uszna, zatem w tym偶e katalogu stworzy艂em nast臋pny (o nazwie a jak偶e howtojava) i do niego wrzuci艂em plik WAR. Teraz przyszed艂 czas na konfig serwera.

Nale偶y zajrze膰 do pliku jetty.xml, kt贸ry rezyduje w podkatalogu /etc podstawowego folderu serwera. Mo偶emy z g贸ry okre艣li膰 wsp贸ln膮 konfiguracj臋 dla serwera (jak np. uruchamia si臋 na nim jedn膮 aplikacj臋) – i to s膮 ustawienia domy艣lne. Ja jednak偶e postanowi艂em, i偶 dla ka偶dej aplikacji (na razie jedna, ale mo偶e b臋dzie i wi臋cej) b臋d臋 posiada艂 oddzielny plik konfiguracyjny. Wyedytowa艂em plik i doda艂em takie oto zapisy:

<Call name="addLifeCycle">
<Arg>
<New class="org.mortbay.jetty.deployer.ContextDeployer">
<Set name="contexts"><Ref id="Contexts"/></Set>
<Set name="configurationDir"><SystemProperty name="jetty.home" default="."/>/contexts</Set>
<Set name="scanInterval">5</Set>
</New>
</Arg>
</Call>

W folderze z Jetty nale偶y utworzy膰 wskazany katalog (/contexts). I nast臋pnie wrzucamy do niego pliczek o wdzi臋cznej nazwie Howtojava.xml. Wygl膮da on tak:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<configure class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/howtojava</Set>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/howtojava/howtojava.war</Set>
</configure>

Jednak偶e przy takiej konfiguracji, wej艣cie na podstawow膮 domen臋 powoduje brzydkie zg艂oszenie b艂臋du 404 i wylistowanie wszystkich dost臋pnych kontekst贸w. Dlatego tez doda艂em jeszcze jeden plik w katalogu /contexts – default.xml. Oto on:


<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<configure class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/howtojava/howtojava.war</Set>
</configure>

Dzi臋ki temu domy艣lnie wskazanie na domen臋 leci pod konkretny adres. Uruchomienie serwera to odpalenie pliku start.jar z odpowiednimi uprawnieniami. M贸j jak偶e mi艂y admin (cho膰 nie wiem czy to on, czy po prostu lekko wyedytowa艂 defaultowe skrypty) dostarczy艂 mi 2 pliczki, kt贸re opakowywuj膮 uruchamianie i zamykanie serwera: Start.sh:

#!/bin/sh

export port=$(expr $(cat base_port) + 0)

echo "Starting the Jetty process at port $port"

nohup java -Xmx$(cat ram_size)m -Drun.mode=production -Djetty.port=$port -jar -server start.jar etc/jetty.xml >> logs/std.out 2>> logs/err.out &
echo $! > prod.pid
echo "The PID is $!"

Nie jest to nic skomplikowanego. Obok tego pliku w katalogu le偶膮 2 inne pliki – jeden (base_port) zawiera informacj臋 o porcie, na kt贸rym ma by膰 uruchomiony serwer, a drugi (ram_size) to wej艣ciowa ilo艣膰 RAMu dla serwera. Przypisany ID procesu jest zapisywany do pliku, a wyj艣cie zar贸wno System.out, jak i strumie艅 b艂臋d贸w s膮 przekierowywane do odpowiednich katalog贸w. Drugi skrypt to po prostu odczytanie zapisanego ID procesu z pliku i zabicie tego procesu. Uwadze polecam r贸wnie偶 plik /bin/jetty-service.conf, gdzie mo偶na znale藕膰 kilka ciekawych zmiennych, kt贸rymi mo偶na si臋 pobawi膰.

I tyle. Teraz wystarczy odwiedzi膰 : pewien adres by zobaczy膰 jak 艣miga HowToJava. Subdomena p贸ki co mi nie chwyci艂a, ale jak za艂apie to z ca艂膮 pewno艣ci膮 przepn臋 j膮 na nowy adres.

Gdyby kto艣 by艂 zainteresowany hostingiem dla aplikacji Grails i bardzo profesjonaln膮 obs艂ug膮, w贸wczas wystarczy napisa膰 mejla na adres admin@serenity.org.pl i 艂adnie poprosi膰 o ofert臋. Jako, 偶e serwery ca艂y czas s膮 w fazie ‘beta’ (w sumie GMail te偶 jest :), zatem szereg rzeczy jeszcze nie dzia艂a tak jak powinno, a cz臋艣ci p贸ki co nie ma. Ale ceny bardzo konkurencyjne, za艣 z jako艣ci us艂ugi jestem p贸ki co bardzo zadowolony.

Nic nie robi, a si臋 robi

A taki 艣mieszny tytu艂 mi dzi艣 do g艂owy przyszed艂. A wzi膮艂 si臋 st膮d, 偶e im mniej pisz臋, tym wi臋kszy ruch na blogu. Ciekawe i polecam do przemy艣le艅 filozoficznych.

No ale do rzeczy. Zaniedba艂em si臋 ostatnio w pisaniu przyznaj臋. Jednak偶e to kwestia tylko braku czasu, a nie ch臋ci. Pomaga艂em kumplowi w projekcie in偶ynierskim, nadrabia艂em zaleg艂o艣ci w lekturze czego innego ni偶 dokumentacje i specyfikacje, a tak偶e w ko艅cu si臋 wyspa艂em. Teraz jednak偶e czas powr贸ci膰 – sko艅czy艂em w艂a艣nie lektur臋 drugiego rozdzia艂u z podr臋cznika SCJP, moje przemy艣lenia i wnioski ju偶 wkr贸tce.

Druga kwestia to start mojego wykupionego hostingu. Na razie dosta艂em na pewno prywatne Jetty, serwer WWW, baze, poinstalowane Grailsy no i zobaczymy co z tego wyniknie. Relacja z wdra偶ania aplikacji na serwer inny ni偶 lokalny r贸wnie偶 nied艂ugo. Will Frederico marry Francesca? Stay tuned.*

* Takie 艣mieszne wtr膮cenia s膮 cz臋stym elementem w podr臋czniku do SCJP. Naprawd臋 czasami wymiataj膮 馃檪

TDD na przyk艂adzie Grails – klasy domenowe i kontrolery

Zaczynam si臋 sypa膰. Powa偶nie. Zn贸w mnie co艣 w kr臋gos艂upie strzyka, nic fajnego, siedzenie jeszcze jako艣 mi wychodzi, ale chodzenie to ju偶 o wiele gorzej. Cho膰 jedyna rzecz pozytywna z tego wynika – porz膮dki na dysku i kolejny wpis na blogu.

W poprzednim wpisie przedstawi艂em specyfik臋 TDD na przyk艂adzie ogranicze艅 dla klas domenowych. Przed rozpocz臋ciem dalszej zabawy przytocz臋 dodatkowe asercje, kt贸re oferuje nam klasa GroovyTestCase, kt贸ra jest u偶ywana zar贸wno do test贸w jednostkowych jak i integracyjnych w Grails (oczywi艣cie skopiowane z dokumentacji).

  • assertArrayEquals(Object[] expected, Object[] value)
  • assertLength(int length, char[] array)
  • assertLength(int length, int[] array)
  • assertLength(int length, Object[] array)
  • assertContains(char expected, char[] array)
  • assertContains(int expected, int[] array)
  • assertToString(Object value, String expected)
  • assertInspect(Object value, String expected)
  • assertScript(final String script) // assert that a script runs without exceptions
  • shouldFail(Closure code) // assert that an exception was thrown in that closure
  • shouldFail(Class clazz, Closure code) // the same but for a class

Troch臋 u艂atwiaj膮 偶ycie, bez konieczno艣ci pisania w艂asnych asercji na szybko. Przetestowali艣my ju偶 ograniczenia dla kasy domenowej, teraz zajmijmy si臋 jej metodami. Za艂o偶enie, 偶e wypada艂oby testowa膰 takie metody jak save() czy metody wyszukuj膮ce jest troch臋 kontrowersyjne. Z jednej strony dobrze by艂oby testowa膰 tak偶e i je, ale z drugiej strony to tak, jakby艣my w膮tpili w umiej臋tno艣ci programistyczne tw贸rc贸w frameworka. Dlatego te偶 nie jest to zalecana praktyka. Skupimy si臋 zatem na przetestowaniu dopisanej przez programist臋 metody – a najprostsz膮 metod膮 tego typu b臋dzie oczywi艣cie toString().

Nasz HtjTopic b臋dzie oczywi艣cie s艂u偶y艂 do 艂adnego wylistowania jego zawarto艣ci na stronie w formie tabeli, czy 艂adnej listy, ale zaprezentowana metoda toString() ma tylko charakter podgl膮dowy (doda艂em w艂asno艣ci pomini臋te w poprzednim wpisie).

String toString() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd")
"${question} zadane: " + formatter.format(adddate) + " przez ${author.nick}"
}

Test napisany dla tej metody wygl膮da tak:

void testToString() {
DateFormat df = DateFormat.getDateInstance()
String adddateString = "2009-04-28"
Date adddate = df.parse(adddateString)
def topic = new HtjTopic( question: "Czy potrzebuj臋 komputera?", author: new Htj_Users( nick: "Chlebik" ), adddate: adddate )
assertEquals "Czy potrzebuj臋 komputera? zadane: 2009-04-28 przez Chlebik", topic.toString()
}

I tyle. Zasadniczo wywo艂anie metod napisanych przez nas samych w klasie domenowej jest podr臋cznikowym wr臋cz przyk艂adem testu jednostkowego. Nie trzeba nawet odwo艂ywa膰 si臋 do innych metod, dziedziczenia, etc. Sprawa jest zatem do艣膰 prosta i nie trzeba wi臋cej komentarza.

Zajmijmy si臋 teraz kontrolerami. Tutaj sprawa jest o wiele trudniejsza, gdy偶 kontroler (no chyba, 偶e s艂u偶y tylko jako ‘zaplecze’ dla wy艣wietlenia widoku) mo偶e zwraca膰 cz臋sto r贸偶ne wyniki w zale偶no艣ci od przekazanych parametr贸w, rodzaju odebranego 偶膮dania i tak dalej. To kontrolery z definicji s膮 najbardziej pokomplikowanymi elementami aplikacji zgodnej z wzorcem MVC. Mimo to da si臋 je oczywi艣cie testowa膰, co zreszt膮 zaraz zrobimy.

Testowanie kontroler贸w to ju偶 nie s膮 testy jednostkowe, cho膰 typologia w tej dziedzinie jest momentami kulawa. Grailsy domy艣lnie przy tworzeniu klas domenowych tworz膮 klas臋 w katalogu z testami jednostkowymi, za艣 przy tworzeniu kontroler贸w odpowiedni test zostaje stworzony w ktalogu z tzw. testami integracyjnymi. Jak mo偶na si臋 domy艣li膰, testy integracyjne s艂u偶膮 przetestowaniu relacji i przep艂ywu w danym artefakcie (tutaj akurat kontrolerze). Nie odchodz膮c za bardzo zn贸w do tworzenia kodu do test贸w, spr贸bujmy odnie艣膰 si臋 do kodu klasy UsersController, kt贸ry powsta艂 przy okazji pisania kodu autoryzuj膮cego.

Grailsy (jak podaje dokumentacja) domy艣lnie wrappuj膮 tego typu testy (integracyjne) w obiekty Spring Mock Library, co pozwala na odwo艂ywanie si臋 do fa艂szywych obiekt贸w 偶膮dania czy odpowiedzi (sesji te偶). Dzi臋ki temu mo偶emy stworzy膰 dla ka偶dego testu warunki identyczne z tymi, w kt贸rych najcz臋艣ciej dana akcja b臋dzie pracowa艂a (i z tymi skrajnymi te偶, testowanie warunk贸w brzegowych to istotna cz臋艣膰 procesu testowania). By nie przed艂u偶a膰 i komplikowa膰 napiszemy szybki test dla akcji login. Sprawdza ona czy u偶ytkownik jest zalogowany, je艣li nie jest to wy艣wietla formularz, albo dobiera si臋 do tablicy POST. Jednak偶e kiedy u偶ytkownik nie jest zalogowany (session.user jest puste), w贸wczas redirectuje na stron臋 g艂贸wn膮 oraz wy艣wietla komunikat o tym, i偶 u偶ytkownik jest ju偶 zalogowany. Oto jak wygl膮da test:

void testLogin() {
def c = new UsersController()
c.session.user = "jakisobiekt"
c.login()
assertEquals "/", c.response.redirectedUrl
}

I oczywi艣cie test przechodzi nasz kod poprawnie. Nale偶y zwr贸ci膰 uwag臋 na ciekaw膮 metod臋 redirectedUrl, kt贸ra umozliwia zbadanie docelowego adresu, pod kt贸ry zostanie skierowane nasze 偶膮danie. Wi臋cej tego typu metod mo偶na znale藕膰 w dokumentacji Spring Mock.

Oczywi艣cie takie same za艂o偶enia co do testowania odnosz膮 si臋 do us艂ug, czy command object贸w. Testowa膰 mo偶na r贸wnie偶 w艂asne znaczniki GSP. Zasadniczo testowa膰 mo偶na wszystko do czego zreszt膮 gor膮co zach臋cam.

TDD na przyk艂adzie Grails – klasy domenowe i ich regu艂y

Nie tak dawno temu zaw臋drowa艂em na spotkanie WJUGa, na kt贸rym poruszono temat DDD (Domain Driven Development). Okaza艂o si臋, 偶e Grailsy s膮 pi臋knym przyk艂adem tego podej艣cia w praktyce. Jednak偶e na razie skupimy si臋 na tym, co do tej pory omija艂em w moich postach dotycz膮cych frameworka – testach. Pisa艂em o nich wcze艣niej w kontek艣cie JUnit, teraz zobaczmy jak zosta艂o to zrobione w Grails.

Framework rozr贸偶nia dwa typy test贸w – testy jednostkowe oraz testy integracyjne. P贸ki co zajmiemy si臋 tymi pierwszymi. Zacznijmy od klas domenowych, gdy偶 to na ich przyk艂adzie najlepiej pokaza膰 jak powinien przebiega膰 proces budowania aplikacji w oparciu o testy. Jako podstawa pos艂u偶y nam kolejna funkcjonalno艣膰 w aplikacji HowToJava – zadawanie pyta艅. Stworzymy zatem dwie klasy dziedzinowe – jedna b臋dzie reprezentowa艂a za艂o偶ony temat (w膮tek pytania, tak samo dzia艂aj膮cy jak post na forum), a tak偶e konkretny wpis w ramach tego pytania/tematu.

Tworzymy zatem klas臋 dziedzinow膮 HtjTopic. Wykonanie polecenia:

grails create domain-class

Powoduje utworzenie nie tylko klasy domenowej, ale tak偶e stosownych dla niej test贸w. Utworzona klasa testowa wygl膮da tak:

import grails.test.*

class HtjTopicTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}

protected void tearDown() {
super.tearDown()
}

void testSomething() {

}
}

Mamy jak wida膰 metod臋 wykonywan膮 na rozpocz臋ciu testu, a tak偶e jedn膮 wywo艂ywan膮 na koniec. Oczywi艣cie w 艣rodku mamy te偶 metod臋 b臋d膮c膮 sednem testu. Przed jego wykonaniem zastan贸wmy si臋 jednak偶e czego potrzebujemy w naszej klasie domenowej. Z ca艂膮 pewno艣ci膮 takie pytanie zadane w serwisie musi mie膰 autora, tytu艂 z ca艂膮 pewno艣ci膮 r贸wnie偶 by si臋 przyda艂, data dodania my艣l臋 te偶 by si臋 przyda艂a. Ostatecznie po szybkim procesie my艣lowym powsta艂a taka oto lista:

  • tytu艂 b臋d膮cy 艂a艅cuchem o okre艣lonej d艂ugo艣ci
  • data utworzenia wpisu
  • autor (b臋dzie to zalogowany u偶ytkownik)

Dodamy te偶 p贸藕niej kategori臋, do kt贸rej przynale偶y nasze pytanie, jednak偶e by nie komplikowa膰 kodu p贸ki co, pozostaniemy przy naszej wersji. Generalna zasada TDD g艂osi, aby nie pisa膰 nowego kodu, je偶eli testy nie ko艅cz膮 si臋 sukcesem. Na razie nie mamy ani test贸w, ani kodu, zatem problem jest czysto akademicki. Zasadniczo jedyn膮 rzecz膮, kt贸r膮 mo偶na przetestowa膰 w wyra藕ny spos贸b p贸ki co s膮 ograniczenia i to, czy nasza klasa ich przestrzega. Stworzymy sobie obiekt imituj膮cy zachowanie prawdziwego obiektu (tzw. mock) i na nim b臋dziemy testowa膰. Mo偶na by zastanowi膰 si臋 po co testowa膰 ograniczenia wpisywane do klasy, kt贸re maj膮 na celu w艂a艣nie kontrol臋 i testowanie, czy poprawne warto艣ci zosta艂y wpisane? Autorzy frameworka odpowiadaj膮 na to pytanie prosto – bo tak :), ale te偶 by unikn膮膰 liter贸wek, kt贸re (rzekomo) s膮 plag膮 w tym przypadku. Grailsy z definicji u艂atwiaj膮 偶ycie i w zwi膮zku z czym oferuj膮 gotowy szkielet do testowania. Nasza metoda testuj膮ca powinna wygl膮da膰 na razie tak:

void testConstraints() {
def existingTopic = new HtjTopic()
mockForConstraintsTests(HtjTopic, [ existingTopic ])

// Testujemy constraint notNull
def topic = new HtjTopic()
assertFalse topic.validate()
assertEquals "nullable", topic.errors["question"]

// Testujemy constraint blank
topic = new HtjTopic( question: "" )
assertFalse topic.validate()
assertEquals "blank", topic.errors["question"]

// Testujemy constraint minSize
topic = new HtjTopic( question: "test")
assertFalse topic.validate()
assertEquals "size", topic.errors["question"]

// Testujemy constraint maxSize
topic = new HtjTopic( question: "test test test")
assertFalse topic.validate()
assertEquals "size", topic.errors["question"]

// Na koniec dla pewnosci, ze poprawne dane powoduja przejscie testow
topic = new HtjTopic( question: "Jak pisa膰" )
assertTrue topic.validate()
}

Jej zrozumienie nie powinno nastr臋cza膰 problem贸w. Dziwi膰 mog膮 tylko dwie pierwsze linijki. Wywo艂anie metody mockForConstraintsTests w takim kszta艂cie ‘uzbraja’ egzemplarze klasy domenowej w metod臋 validate(), co pozwala na sprawdzenie poprawno艣ci constraints (Uwaga: skr贸ci艂em d艂ugo艣膰 tematu by 艂adniej to wygl膮da艂o w kodzie na blogu. Normalnie oczywi艣cie powinien by膰 d艂u偶szy). Testy (uruchamiane w NetBeans kombinacj膮 klawiszy Alt-F6) poka偶膮 po pierwszym wywo艂aniu b艂臋dy bo i pewnie nie ma jeszcze klasy, kt贸rej to obiekty chcemy utworzy膰. Oto kod klasy HtjTopic:

class HtjTopic {

String question

static constraints = {
question( blank: false, size: 5..10, nullable: false )
}
}

To oczywi艣cie na razie tyle, aby 艂atwiej by艂o pokaza膰 ide臋 test贸w. Wyniki test贸w mo偶emy 艂adnie sobie obejrze膰 w przegl膮darce (katalog /tests w folderze projektu) – ka偶dy b艂膮d lub niepowodzenie testu zostanie odnotowane. Filozofia pisania test贸w jest taka, aby nie napycha膰 jednej metody chamsk膮 wr臋cz ilo艣ci膮 linii kodu. Nale偶y to rozbi膰, np. ograniczaj膮c metody do konkretych p贸l, albo te偶 do walidacji metodami poszczeg贸lnych ograniczne艅 (np. tylko warto艣ci NULL).

Dodam jeszcze tylko, 偶e testowanie odbywa si臋 w spejcalnym 艣rodowisku testowym, kt贸re posiada oddzielny wpis konfiguracyjny w pliku DataSource.groovy. Oto jak powinna wygl膮da膰 konfiguracja, aby opiera艂a si臋 na HSQLu.

test {
dataSource {
pooled = true
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
dbCreate = "create-drop"
url = "jdbc:hsqldb:mem:testDb"
}
}

To tyle na dzi艣. W nast臋pnym wpisie zajmiemy si臋 dodatkowymi metodami klas dziedzinowych oraz zerkniemy na inne artefakty, kt贸re mo偶na podpi膮膰 pod testy.

Chlebik walks alone

Ah taki przewrotny tytu艂 tego偶 wpisu 馃檪 Konkretnie rzecz w tym, i偶 z czasem us艂uga na mor.ph przesta艂a mi wystarcza膰. Jednak co w艂asna przestrze艅 to w艂asna przestrze艅. Jednak偶e jako艣 do tej pory nie za bardzo mia艂em okazj臋, aby zapozna膰 si臋 z kwesti膮 administracyjn膮 Javy. Dlatego zrobi艂em to co ka偶dy m膮dry cz艂owiek by uczyni艂 – zatrudni艂em do tego ludzi.

A konkretnie wst臋pnie uko艅czy艂em rozmowy w sprawie przestrzeni serwerowej. 聽Na razie nie b臋dzie to nic wielkiego, ale umo偶liwi pokazanie wszystkich moich “dzie艂” 馃檪 szerokiej publiczno艣ci bez ogl膮dania si臋 na oferowane rozwi膮zania. P艂ac臋 i mam pewn膮 us艂ug臋. Wst臋pnie na serwerze b臋dzie te偶 dzia艂a艂 serwer WWW z PHP zatem mo偶e uda si臋 postawi膰 jak膮艣 ma艂膮 platform臋 WWW dla Grails贸w, gdy偶 p贸ki co w polskim internecie posucha je艣li chodzi o strony o tej technologii.

P贸ki co zatem HowToJava znika z netu, wr贸ci niebawem. Dzia艂 r贸wnie偶 czyni臋 niewidocznym, mam nadziej臋, 偶e powr贸ci za mniej wi臋cej dwa tygodnie wraz z szeregiem innych rzeczy.