Tropical Software Observations

18 December 2008

Posted by Teo Choong Ping

at 10:56 AM

3 comments

Labels:

Grails 1.0.4 "no Session" Error

In Grails 1.0.4, there are a few significant changes to how GORM returns Model objects from queries or model traversal.

If you're getting "Could not initialize proxy - no Session" exceptions when trying to access a domain object's collection of child objects, check that you aren't storing the domain object in a HTTP session, as when model objects are stored in the session, they're detached. Prior to version 1.0.3, the detached object had enough information as it was "eagerly fetched". With 1.0.4, this is no longer the case as it's just a proxied object.

To prevent this problem, before you access the object's child collection do the following:

def user = session.user
user.refresh() // Either refresh the object to attach it back to the session
user = User.get( user.id ) // Or retrieve back the object with its id
user.profiles[0] // Then doing stuff like this won't throw the error anymore

For more discussion and context, read this message thread.

02 December 2008

Posted by Irregular Zero

at 11:10 AM

0 comments

Labels:

Clickpass plugin for Grails, Part 2

Go to Clickpass and create an account. Once in the account, go to the "My Details" tab and check the "Show software developer tools" under "Additional Preferences". There should now be a "developer" tab present, select it and push the "Create new site" button.

1. Register site

  • Type in the site name.
  • URL: http://localhost:8080/makedo. This is for running a grails app on your own machine named makedo.
  • Description and logo are optional.

2. Log users in
  • OpenID trust root: http://localhost:8080.
  • begin_openid_login: http://localhost:8080/makedo/openid/login?success_controller=
    clickpass&success_action=welcome&error_controller=&error_action=
    . This link is where Clickpass will access, providing the OpenID details, after one has passed the bar that should be in the app.
  • Leave the OpenID parameter label alone.
  • Submission method: POST.
  • SSL is optional.
  • Once you have saved the changes, the page will change and display a choice of clickpass bars and the html code for it down below. Copy the html code into _clickpass.gsp.

3. Merge accounts
  • begin_add_openid_to_user URL: http://localhost:8080/makedo/clickpass/merge. This is after the authentication and the check whether the OpenID belongs to any user on the database, bringing him to the registration form. If the user selects to login a pre-existing account, the username and password he provides will be sent to this link. If successful the OpenID will be associated with that account.
  • user_id label: Username. This is for the login page Clickpass will provide before accessing the URL above. Merely cosmetic.
  • Adding the connect bar is optional.

4. Register new signups
  • process_openid_registration_url: http://localhost:8080/makedo/clickpass/register. This is after the authentication and the check whether the OpenID belongs to any user on the database, bringing him to the registration form. The user provides information to Clickpass which will be passed along to the link. If successful a new user account with the associated OpenID will be created and saved in your app.
  • new_openid_completion_url: http://localhost:8080/makedo/clickpass/welcome. This is for when authentication is successful and all other things have been taken cared of, e.g. registration or merging.
  • The others are optional except for the fields. Changes in the fields may require changes in the register method of the Clickpass controller.
  • The URL for the registration form should be left for last as it changes due to the fields. Once you're satified and have saved the changes, you can copy and paste the string into urlString in the welcome method of the Clickpass controller.
Aftermath
Once the settings are done, you can use Clickpass.

Clickpass plugin for Grails, Part 1

Clickpass works by handling the OpenID authentication and sending it back to the site. This means that any Grails app using this plugin must also install the OpenID plugin. Also the site will need to be registered with Clickpass and some settings need to be set online. When the authenticated details are sent back, the user is logged in and sent to the welcome page, sent to a form for details to complete registration or asked to login to attach the OpenID to an existing user account.

  • grails create-plugin clickpass.
  • Add ClickpassController.groovy to grails-app/controller.
  • Add User.groovy to grails-app/domain. This is a dummy file required for the plugin to be packaged.
  • Add ClickpassTagLib.groovy to grails-app/taglib.
  • Add _clickpass.gsp to grails-app/views.
  • Add welcome.gsp to grails-app/views/clickpass.

ClickpassController.groovy
class ClickpassController {
def openidService

def index = { response.sendError(404) }

def welcome = {
def user = null

if (openidService.isLoggedIn(session)) {
user = User.findByOpenid(openidService.getIdentifier(session))
if (user == null) {
def urlString = "http://www.clickpass.com/process_new_openid?
site_key=Xr2zelexGn&process_openid_registration_url=
http%3A%2F%2Flocalhost%3A8080%2Fsocialr%2Fclickpass%2F
register&new_openid_completion_url=http%3A%2F%2F
localhost%3A8080%2Fsocialr%2Fclickpass%2Fwelcome&
requested_fields=nickname%2Cemail&required_fields=&
nickname_label=Nickname&email_label=Email"
redirect(url:urlString)
}
}
}

def merge = {
def user = User.findByUsernameAndPassword(params.user_id, params.password)
def urlredirect =
java.net.URLDecoder.decode(params.clickpass_merge_callback_url) +
"&userid_authenticated=false"

if (user == null) {
redirect(url:urlredirect)
}

user.openid = openidService.getIdentifier(session)

if (user.save()) {
redirect(controller:'clickpass',action:'welcome')
}
else {
redirect(url:urlredirect)
}
}

def register = {
def user = new User()
user.username = params.nickname
user.email = params.email
user.openid = openidService.getIdentifier(session)
user.password = ''

if (user.save()) {
flash.message = "Thank you for signing up."
redirect(controller:'clickpass', action:'welcome')
}
else {
flash.message = "There is a problem signing up."
redirect(controller:'clickpass', action:'index')
}
}
}
  • index: Nothing for the index to do so set up a 404.
  • welcome: When a user is authenticated by OpenID, a search is made to see whether that OpenID is in the database, if not then redirect to the registration page. The urlString is obtained from Clickpass so this needs to be changed for each site
  • merge: A user, authenticated by OpenID, who wants to attach the ID to his account.
  • register: Create a new user account with the OpenID and save it.

User.groovy
Nothing in it except for the class declaration.

ClickpassTagLib.groovy
class ClickpassTagLib {
static namespace = "clickpass"

def bar = { attrs, body ->
try {
out << render(template:"/clickpass", contextPath:"${pluginContextPath}")
}
catch(Exception e) {
log.error(e)
}
}
}
  • The namespace allows one to use it, i.e <clickpass:foobar/>, in the code instead of the generic <g:foobar/>
  • For usage in an app, all that is required is <clickpass:bar/> to be inserted into the html.
  • The / in the template name is necessary to revert the path to the project base, otherwise it would go looking in the main app views folder.
  • The contextPath:"${pluginContextPath}" is not mentioned in the documention but after setting the path to the base, this sets it to the correct plugin views folder.

_clickpass.gsp
The html for this is generated by Clickpass, after registering your site, you can just copy and paste. This creates the Clickpass login bar.

welcome.gsp
<html>
<head>
<link rel="openid.server" href="http://www.clickpass.com/openid_server" />
<link rel="openid.delegate" href="http://clickpass.com/public/iseec" />
<title>Welcome</title>
</head>
<body>
<h1>Welcome</h1>
<openid:identifier />
<openid:ifLoggedIn>
<openid:logoutLink>Logout</openid:logoutLink>
</openid:ifLoggedIn>
</body>
</html>
  • This is a simple welcome page after the user successfully logs in and his account is verified.
  • One can replace it with their own or redirect from this page.
  • The OpenID is displayed as well as a logout link.

Aftermath
  • grails package-plugin to get it all into a zip file.
  • grails install-plugin /path/to/plugin/grails-example-0.1.zip to install it into an app.

24 November 2008

Posted by Teo Choong Ping

at 10:20 PM

0 comments

Labels: , , ,

CSS Frameworks -- what you need to know, and what else you need to know.

Start working with CSS regularly and you'll find what a pain it is to create a consistent and flexible CSS design for a web site. Some of the not so obvious problems when you start using CSS are:

  1. Inconsistent layout, sizing and odd positionings between different browsers regardless of what the CSS standard says.
  2. Layout using tables is uncool, and technically not a good thing -- but using nested div's can sometimes be worse than not knowing anything about div and just using tables for layout.
  3. Copy-and-pasting CSS gets pretty boring quickly...
  4. and putting all the CSS into one big file can get you weird but sometimes creative results
  5. CSS can be pretty deep sometimes -- especially when you can't seem to figure out why a combination of CSS attributes affects the other stuff on the page.
After a while you might figure out how to be careful and start separating your CSS into different files depending on the what it's supposed to do. For example, reset.css for making sure all the CSS files behave the same regardless of the browser, and grid.css for the perfect 3-column layout that you have worked on for weeks.

But it is not easy to maintain your own "mini CSS framework".

Well CSS frameworks are supposed to solve all that and more. There are as many CSS frameworks as Web frameworks. Let's see some of them and compare the pros and cons.

1. Blueprint
Blueprint has been created from a typographic design basis. What this means is that Blueprint makes it a point to display a font with the same size and type the same way across different browsers. No longer would one see a perfectly nice paragraph inside a box using Firefox, while the same paragraph looks like something else in Internet Explorer.

The trick is to reset the page to a base style so as to escape any browser default page quirks. But what's impressive is that you don't have to know all the tricks and techniques in the book to achieve the same result, and a typical web developer can get up to speed with Blueprint almost immediately.

Another strength of Blueprint is the Grid system. With Blueprint, you can quickly develop a page with different kind of layouts by using the available templates. No more hacking around the div's and struggling with nested layout problems! Even better, just use Blueprint grid generator and get the page layout done instantly.

The other features of Blueprint are:
  • Some default typography
  • A typographic baseline
  • A stylesheet for printing
  • No bloat of any kind
Overall Blueprint is a very capable CSS framework that feels like a Swiss Army knife for web development with CSS. Don't be fooled by the depth of this framework with feature bloat.

2. 960 Grid
Despite the Grid in its name, 960 Grid is a feature rich CSS framework like Blueprint but with some improvements. However, they could do a little more work to explain why the anybody should use it instead of digging around for tutorials. The best introduction by far comes from the author himself here.

The author explains that he built 960 Grid based on "Grid" design ideas and he came about it after much thought. I have to admit, I don't quite grasp the concept of the Grid System. Perhaps this will be the yardstick for the framework itself -- how fast and easy it is for a total newbie like me to get something up and looking nice with 960 Grid.

Risking oversimplifying 960 Grid differences with Blueprint -- I open up the demo pages from 960 Grid and compared to Blueprint layout code -- it's actually easier to understand and digest 960 Grid layout code compared to Blueprint.

But I won't go into the rules to the layout because it would probably take another post. Suffice to say, it's easier understand 960 Grid and IMHO has much nicer rules to define a grid layout than Blueprint.

Other than that I find both Blueprint and 960 Grid do pretty much the same thing. However if I were to start a web project that requires complex layout, I would choose 960 Grid simply for the easier to understand Grid system.

3. Tripoli
Among the 3 CSS frameworks, Tripoli does the least. However like the Pareto Law, Tripoli's 20% covers 80% of the stuff that matters in daily web development.

I like the way Tripoli handles layout -- very clean and simple CSS. This is the kind of CSS framework where it's 2 AM and you need the layout done right, the style is nice and consistent across browsers, and the browser quirks are taken care of without any mistakes.

Tripoli is the no frills CSS framework that makes a nice and consistent site without too much reading into the docsweb page looks good effortlessly. From table, paragraphs, list to typography -- Tripoli is able to make a page clean, simple, and professional looking by including the CSS below:

@import url(tripoli.base.css);
@import url(plugins/tripoli.visual.css);
@import url(plugins/tripoli.type.css);


Unlike Blueprint and 960 Grid, Tripoli makes it really trivial to get a layout and styles that fits 80% of the typical use cases. Note that Tripoli is not a grid framework hence there is not concept of Grid's 12 columns, 16 columns or 24 columns layout. What Tripoli does is to provide over 30 layout styles for you to choose from. This may seems a bit backward compared to Blueprint or 960 Grid, but this is exactly the reason why many will love Tripoli for its straightfoward solution to web page layout. The demo for layout can be found here.

But don't take my word for it, Tripoli site listed down the below reasons for using it:
  • Separate content from layout by putting typographic measurements into a class
  • Enjoy a bullet-proof default rendering engine that's been carefully tested down to Internet Explorer 5
  • Have full control over all CSS rules without disturbing browser defaults
  • Improve readability and useability of your HTML content

Overall impression

The depth of Blueprint continue to impress me but I am cautious of the fact that it's no silver bullet when it comes to CSS design. 960 Grid System is indeed nice and as powerful as Blueprint, however like Blueprint it still needs a new user to sit down and figure out how it works. While Tripoli seems to be really easy I am wary that it may not fulfills future requirement.

The best design is still your own design. Framework like Blueprint, 960 Grid and Tripoli are just going to make it easier and helps you avoid the suprises. The best thing that these frameworks can provide is to make it easy to make your own template and get out of the way. It difficult to say which framework is the best -- the catch is, if you are developing on a complex layout page, none of these framework will make it any easier. The balance of pros and cons still -- like every framework -- depends largely on the requirement needs.


Additional referrences:
Theme candidate for 7 core : 960 Grid System
First Impressions of the Blueprint CSS Framework.

List of available CSS frameworks

28 May 2008

Posted by Irregular Zero

at 12:41 PM

1 comments

Labels: ,

Flex File Upload using Grails Backend

This file upload application is based off the one by Nocturnal at Coding Cowboys, a Flex client with a PHP backend. The client has been stripped down to its core functions and PHP replaced with a Grails backend.

The Grails backend is a file controller with an action defined:

def upload = {
if(request.method == 'POST') {
Iterator itr = request.getFileNames();

while(itr.hasNext()) {
MultipartFile file = request.getFile(itr.next());
File destination = new File(file.getOriginalFilename())

if (!file.isEmpty()) {
file.transferTo(destination)
// success
}
else
{
// failure
}
}

// Trigger an Event.COMPLETE event, notifying the Flex client
response.sendError(200,'Done');
}
}

This is capable of uploading multiple files at once and can be tested using a view gsp:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<title>Test Upload</title>
</head>
<body>
<div class="body">
<h1>Upload File</h1>
<g:form method="post" action="upload" enctype="multipart/form-data">
<input type="file" name="file_1"/><br/>
<input type="file" name="file_2"/><br/>
<input type="file" name="file_3"/><br/>
<input type="file" name="file_4"/><br/>
<input type="file" name="file_5"/><br/>
<input type="submit"/>
</g:form>
</div>
</body>
</html>

The Flex client has been pared down to 3 main functions, adding files to the upload list, removing them from the list and finally uploading the files to the server.

First the imports, global variables and initializer:
import mx.controls.*;
import mx.managers.*;
import mx.events.*;
import flash.events.*;
import flash.net.*;

// Convention: Underscore to differentiate global and local variables
private var _refAddFiles:FileReferenceList;
private var _refUploadFile:FileReference;
private var _arrUploadFiles:Array;
private var _numCurrentUpload:Number;

private function initApp():void {
_refAddFiles = new FileReferenceList();
_refAddFiles.addEventListener(Event.SELECT, onSelectFile);
_arrUploadFiles = new Array();
_numCurrentUpload = 0;
}

The add-files function opens the browse window and enable the selection of one or more files to add to the upload list:
// Called to add file(s) for upload
// The fileList property is populated anew each time browse() is called on that FileReferenceList object.
private function addFiles():void {
_refAddFiles.browse();
}

// Called when file(s) are selected
private function onSelectFile(event:Event):void {
// Add files to _arrUploadFiles
if (_refAddFiles.fileList.length >= 1) {
for (var k:Number = 0; k < _refAddFiles.fileList.length; k++) {
_arrUploadFiles.push({
name:_refAddFiles.fileList[k].name,
size:formatFileSize(_refAddFiles.fileList[k].size),
file:_refAddFiles.fileList[k]});
}
listFiles.dataProvider = _arrUploadFiles;
listFiles.selectedIndex = _arrUploadFiles.length - 1;
}
}

The remove-files function:
private function removeFiles():void {
var arrSelected:Array = listFiles.selectedIndices;
if (arrSelected.length >= 1) {
// Null selected files
for (var i:Number = 0; i < arrSelected.length; i++) {
_arrUploadFiles[Number(arrSelected[i])] = null;
}

// Remove the null entries
for (var j:Number = 0; j < _arrUploadFiles.length; j++) {
if (_arrUploadFiles[j] == null) {
_arrUploadFiles.splice(j, 1);
j--;
}
}
listFiles.dataProvider = _arrUploadFiles;
listFiles.selectedIndex = 0;
}
}
The upload function is where the point of integration with Grails is, in the request calling up the controller. Although the Grails backend can handle multiple files, Flex can only initiate the upload one file at a time. The event listener and the return response in the Grails controller allow the upload function to loop:
private function uploadFiles():void {
if (_arrUploadFiles.length > 0) {
listFiles.selectedIndex = _numCurrentUpload;

var request:URLRequest = new URLRequest();
request.data = sendVars;
request.url = "http://localhost:8080/App/file/upload";
request.method = URLRequestMethod.POST;
_refUploadFile = new FileReference();
_refUploadFile = listFiles.selectedItem.file;
_refUploadFile.addEventListener(Event.COMPLETE, onUploadComplete);
_refUploadFile.upload(request, "file", false);
}
}

private function onUploadComplete(event:Event):void {
_numCurrentUpload++;
if (_numCurrentUpload < _arrUploadFiles.length) {
uploadFiles();
} else {
_numCurrentUpload = 0;
_arrUploadFiles = new Array();
listFiles.dataProvider = _arrUploadFiles;
listFiles.selectedIndex = 0;
Alert.show("Files have been uploaded", "Upload Complete");
}
}
And lastly the UI:
<mx:Panel title="Files Upload" width="100%" height="100%">
<mx:DataGrid id="listFiles" allowMultipleSelection="true" verticalScrollPolicy="on"
draggableColumns="false" resizableColumns="false" sortableColumns="false" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn headerText="File" dataField="name" wordWrap="true"/>
</mx:columns>
</mx:DataGrid>

<mx:ControlBar>
<mx:Button label="Add" click="addFiles()"/>
<mx:Button label="Remove" click="removeFiles()"/>
<mx:Button label="Upload" click="uploadFiles()"/>
</mx:ControlBar>
</mx:Panel>
The Flex mxml/swf files must be moved to the web-app folder of the Grails project and the embedded Jetty activated by 'grails run-app' in order to run this application.

The files will be uploaded to the project directory and thus are not accessible by the webapp. The line to amend is in the Grails controller:
File destination = new File("web-app/" + file.getOriginalFilename())
Note this only works while the project is in development mode. For war files, the base directory would depend on where the server is started, usually in the server home directory. So for a standalone Jetty server, the path to add would be "webapps/App/".

Using the war file in a Tomcat server has been problematic. Only one file can be successfully uploaded to the server, there is no looping.

Links:
Coding Cowboys: Flex upload component

Flex Client:
http://livedocs.adobe.com/flex/3/langref/flash/net/URLRequest.html
http://livedocs.adobe.com/flex/3/langref/flash/net/FileReference.html
http://livedocs.adobe.com/flex/3/langref/flash/net/FileReferenceList.html
http://livedocs.adobe.com/flex/3/html/help.html?content=17_Networking_and_communications_7.html

Grails Backend:
http://www.grails.org/Controllers+-+File+Uploads
http://docs.codehaus.org/display/GRAILS/File+Upload