Tuesday, 2 December 2008

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.

Friday, 24 October 2008

IPhone MoviePlayer Sample Code 4

MyOverlayView

Interface

Instance methods:
  • -(void)awakeFromNib;
  • -(void)dealloc;
  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Implementation

Imports:
  • #import "MyViewController.h"
-(void)awakeFromNib
Rotate the overlay 90 degrees clockwise then translate it to the centre of the screen. This will match the orientation the movie player uses, full-screen landscape mode.

-(void)dealloc
Call super dealloc.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Similar to the MyImageView implementation. This creates a OverlayViewTouchNotification and fires it off.

IPhone MoviePlayer Sample Code 3

MyImageView

Interface

Imports:
  • #import "MyViewController.h"
Instance variables:
  • mViewController
Instance methods:
  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Implementation

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Receives a set with the touches and the event. Gets a UITouch from the set and check it's phase. If it's UITouchPhaseBegan, play the movie.

Thursday, 23 October 2008

IPhone MoviePlayer Sample Code 2

MyViewController

Interface

Imports:
  • #import < MediaPlayer/MediaPlayer.h >
  • #import "MyOverlayView.h"
The line 'extern NSString * const OverlayViewTouchNotification' creates a global constant that will be used for touches to the overlay view. Any class importing MyViewController can access this variable.

Instance variables:
  • mMoviePlayer (MPMoviePlayerController)
  • mMovieURL (NSURL)
  • mOverlayView (MyOverlayView)
Instance methods:
  • -(NSURL *)movieURL;
  • -(void)initMoviePlayer;
  • -(IBAction)playMovie:(id)sender;
  • -(void)overlayViewTouches:(NSNotification *)notification;
  • -(IBAction)overlayViewButtonPress:(id)sender;

Implementation

Imports:
  • #import "MoviePlayerAppDelegate.h"
Initialise constant OverlayViewTouchNotification with the string 'overlayViewTouch'.

#pragma is a compiler directive. However the compiler ignores it if #pragma mark is used. #pragma mark helps with the organisation of your methods in the Xcode dropdown list.

'#pragma mark -' divvies up the methods by creating a spacer line while '#pragma mark Movie Player Routines' creates a bold label with the text 'Movie Player Routines'.

Movie Player Routines
- (void) moviePreloadDidFinish:(NSNotification*)notification
- (void) moviePlayBackDidFinish:(NSNotification*)notification
- (void) movieScalingModeDidChange:(NSNotification*)notification

These are events. When they are triggered is evident from the names. There is no code inside them.

-(void)initMoviePlayer
  • Register MPMoviePlayerContentPreloadDidFinishNotification (named after the definition in MPMoviePlayerController.h) with NSNotificationCenter.
  • The movie player object, mMoviePlayer, is created by allocating MPMoviePlayerController and initialising it with movieURL.
  • Register MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerScalingModeDidChangeNotification, giving mMoviePlayer as the notification sender.
  • Obtain the MoviePlayerAppDelegate object and use its instance variables to set mMoviePlayer.
  • Register OverlayViewTouchNotification.
-(IBAction)playMovie:(id)sender
  • Play mMoviePlayer.
  • Get the application windows in an array.
  • Get the movie player window.
  • Add mOverlayView as the subview of the movie player window.
View Controller Routines
-(NSURL *)movieURL
  • Check mMovieURL whether it's null or not.
  • Passing will return mMovieURL.
  • Failing will get the main bundle, look for 'Movie.m4v', check it and convert the path to NSURL before assigning it to movieURL.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation
-(void)didReceiveMemoryWarning
-(void)overlayViewTouches:(NSNotification *)notification
-(IBAction)overlayViewButtonPress:(id)sender

Trivial or empty methods.

-(void)dealloc
Remove all registered notifications and release the movie player.

IPhone MoviePlayer Sample Code

Deconstructing the MoviePlayer app to see how the Media Player Framework ticks.

MoviePlayer has 4 classes:
  • MoviePlayerAppDelegate
  • MyImageView
  • MyOverlayView
  • MyViewController
A new IPhone application automatically creates 2 classes, with project name MPC, MPCAppDelegate and MPCViewController. Therefore, MyImageView and MyOverlayView are additions while MyViewController has been renamed.

MoviePlayerAppDelegate

Interface

There are 2 different ways of including a file/class:
  • #import "MyViewController.h"
  • @class MyViewController;
The interface lists 3 instance variables of type NSInteger aside from the default UIWindow and ViewController:
  • backgroundColor
  • controlMode
  • scalingMode
The @property declares the accessor methods for the 5 variables. It has attributes which modify the accessors.

Example: @property (nonatomic, retain) UIWindow *window;

nonatomic means the accessor returns the value directly, the default is to lock, get value, unlock which is meant for multithreaded environments.

assign, retain or copy must be explicitly set if garbage collection is not used. assign is the default and means the setter uses a simple assignment. retain keeps the object variable in-memory and cannot be deallocated without your permission. copy means that a copy of the object should be used for assignment and the previous one is sent a release message.

3 instance methods:
  • -(void)setUserSettingsDefaults;
  • -(void)applicationDidFinishLaunching:(UIApplication *)application;
  • -(void)dealloc;

Implementation

3 NSString keys:
  • kScalingModeKey = @"scalingMode"
  • kControlModeKey = @"controlMode"
  • kBackgroundColorKey = @"backgroundColor"
@synthesize generates the accessors according to the attributes listed. If implemented manually, the code must follow what has been specified in the @property. @synthesize will only generate accessors that don't exist so if a variable has a setter already, then only a getter will be generated.

-(void)setUserSettingsDefaults
Sets up a test calling NSUserDefaults to see if there's a match. Passing will set the scalingMode, controlMode and backgroundColor variables. Failing will create the values based on the settings bundle info and then set the variables:
  • Get the path to the main bundle of the project.
  • Append Settings.bundle to the path using stringByAppendingPathComponent, which takes care of adding separators.
  • Append Root.plist to the path, same as before.
  • Create a dictionary using dictionaryWithContentsOfFile, giving the path to Root.plist.
  • Create the prefSpecifierArray array from the dictionary using the key, PreferenceSpecifiers, which is a tag in Root.plist.
    • Root.plist is an XML file with dict, array and key tags. May be an easy way to read XML.
  • Declare 3 NSNumber variables and a dictionary, prefItem.
  • Loop prefSpecifierArray with the current value going into prefItem.
    • Set keyValueStr with the dictionary using the key, 'Key'.
    • Set defaultValue with the dictionary using the key, 'DefaultValue'.
    • Check if keyValueStr is equal to any NSStrings specified above.
    • Set the numbers with defaultValue if any is correct.
  • Create a dictionary containing the default values and their keys.
  • Register the dictionary with NSUserDefaults using registerDefaults.
  • Updates and writes to the persistent domains using synchronize.
-(void)applicationDidFinishLaunching:(UIApplication *)application
Add the ViewController's view as a subview of the UIWindow. Call self with setUserSettingsDefaults to get the default movie player settings. Lastly, call the ViewController with initMoviePlayer to create a movie player object.

-(void)dealloc
Release the UIWindow and the ViewController. Call the super's dealloc method.

Tuesday, 21 October 2008

Objective-C Classes

Objective-C (Obj-C) is C with Object Oriented Programming tacked on. The object syntax takes some time to get used to, as it is decidedly different from the normal C++/Java syntax.

There are two parts for an object in Obj-C, which are the interface and the implementation. The parts should be stored separately, the interface in a header file (*.h) and the implementation is a *.m file.

The interface stores the instance variables and the prototypes for the class and instance methods. The implementation defines those methods.

Sample code:

*** CounterController.h ***
#import < Cocoa/Cocoa.h >
@interface CounterController : NSObject {
IBOutlet id display_update;
int count;
}
- (IBAction)minus_button_pushed:(id)sender;
- (IBAction)p10_button_pushed:(id)sender;
- (IBAction)plus_button_pushed:(id)sender;
@end

*** CounterController.m ***
#import "CounterController.h"
@implementation CounterController
- (IBAction)minus_button_pushed:(id)sender {
count--;
[display_update setIntValue:count];
}
- (IBAction)p10_button_pushed:(id)sender {
count+=10;
[display_update setIntValue:count];
}
- (IBAction)plus_button_pushed:(id)sender {
count++;
[display_update setIntValue:count];
}
@end


Things of note:
  • Obj-C uses import instead of include.
  • The class CounterController is a subclass of NSObject.
  • Inside the brackets are the instance variables display_update and count.
  • The IBOutlet before display_update means the display_update is an output on the GUI display in the Interface Builder.
  • id is a general type, meaning any object.
  • (IBAction)minus_button_pushed:(id)sender translates to IBAction minus_button_pushed(id sender).
  • The - before the methods classify them as instance methods, + for class methods.
  • The brackets [] indicate messaging, [display_update setIntValue:count] translates to display_update.setIntValue(count).


http://www.gmonline.demon.co.uk/cscene/CS7/CS7-05.html
http://en.wikipedia.org/wiki/Objective-C
http://www.otierney.net/objective-c.html

Friday, 22 August 2008

Grails - JSecurity plugin

JSecurity is a Grails plugin that adds authentication and authorization to a web application. First we go about creating a project and installing the plugin:

grails create-app project_name
grails install-plugin jsecurity

Then we use the quick-start option, available once the plugin is installed, to generate JSecurity domain classes, controller and realm.

grails quick-start

We can follow the JSecurity Quick Start guide to create a functional security application or use the files present to customize and only take what is required.

Authentication


For the login authentication, we'll need the User class, the Auth controller and the DbRealm.

  • The User class will need a username and password field. The password field must be hashed, SHA1 is the default.

  • For the Auth controller, the important methods are signIn and signOut. For signIn, the important lines are the authToken definition, the jsecSecurityManager login and the try-catch block.

  • Authentication occurs in the DbRealm. The important lines in the authenticate method are the account definition and the credentialMatch. You may customize the authToken class by changing the static line, replacing the default with your own class.

  • credentialMatch defaults to SHA1 hashing, to change it, modify grails-app/conf/spring/resources.groovy and add the line credentialMatcher(org.jsecurity.authc.credential.hashMatcher). Replace hashMatcher with the hash class of your choice.


Roles


Roles depend on the hasRole method of the realm. This method accepts a principal, usually a username, and a rolename. The output is a simple true or false boolean, making the logic inside the method completely up to the programmer. The classes generated by the quick-start are not all necessary. We can easily use a variable in the user class to determine the role, modifying the hasRole method as required, thus no additional classes are needed.

For this project, we are using the default hasRole method and keeping the Role as well as the UserRoleRel class. This, along with the User class, allows us to have users and roles, assigning the users to a role in a many-to-one relationship. Refer to the JSecurity Quick Start guide for the way to bootstrap it.

Authorization


Having users and roles, we can now set up proper access control by creating a security filter file, grails-app/conf/SecurityFilters.groovy. Filter rules cannot overlap in actions, just modify the accessControl to include the desired roles. The rules return true or false, allowing or denying access.

Security Filter Example

import org.jsecurity.SecurityUtils
// Customise the interceptor behaviour by implementing onNotAuthenticated() and onUnauthorized() methods in your filters class.
// Default behaviour remains as before (redirect to login page for an unauthenticated user, redirect to unauthorized page for unauthorized access).
class SecurityFilters {
def filters = {
// Ensure that all controllers and actions require an authenticated user,
// except for a few controllers
auth(controller: "*", action: "*") {
before = {
// Exclude these controllers.
if ((controllerName == "search" && actionName == "unboundSearch") || (controllerName == "business" && actionName == "show") ||
(controllerName == "user" && actionName == "signup")) return true

// This just means that the user must be authenticated. He does
// not need any particular role or permission.
accessControl { true }
}
}

// Roles return true or false
// Add reviews to businesses. Only Administrators or Users can do this
addReview(controller: "review", action: "create") {
before = {
accessControl {
role("Administrator") || role("User")
}
}
}

// Add pets to users
addPets(controller: "pet", action: "(create|save)") {
before = {
accessControl {
def user = User.findByUserName(SecurityUtils.subject.principal)
def allowAccess = false

if (role("Administrator")) {
allowAccess = true
}
else if (params.id.toLong() == user.id) {
allowAccess = role("Business User") || role("User")
}
return allowAccess
}
}
}
}
}

Permissions


Permissions offer finer access control, like read, update, delete. Roles can be considered as named collections of permissions, like administrator, user, superuser. Nothing was done with permissions, it was entirely a role-based affair. Refer to the documentation for usage.

Links


Wednesday, 16 July 2008

GSP training

Brought Fauzan up to speed on the project work that's been done, explaining the search controller and the gsp code.

He made a simplified version of the search page, recreating the basic functionality.

The CSS was not touched on. I figure one can just strip it from the page and into a file then figure out where to put and call the file.

Similarly the javascript, dealing with the Google map functionality, may be moved to a file and referred to. It may not work due to the gsp tags inside the code.

Thursday, 26 June 2008

TWiki Replication

Setup a backup TWiki by using rsync, cron and public key authentication. Had problems getting cron to run on root since a normal user does not have the permissions necessary. Solved them when I realized I do need the account active (as I had ignored the expired warning when logging in as root).

Made little headway with regards to the MixTape build on hudson (reminder: change the SVN location once migration is finished). Installed the Flex 3 SDK and used it to compile the mxml files. However the swf files do not seem to work.

The grails search plugin sets up easily. Run 'grails install-plugin searchable', add 'static searchable = true' to any domain class, add data and run the app. The default allows you to search the fields of the domain class, objects may need a specific mapping though.

In the process of migrating SVN projects from one repository to another.

Wednesday, 28 May 2008

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 it's 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

Friday, 23 May 2008

Flex Training - Last bit

Used list-based controls that accept a data array and display out depending on design. Then dealt with data forms, accepting user input, validating it and supposedly entering it into the database.

The last bit was how to create a release version of the Flex application.

Thursday, 22 May 2008

Flex Training - Dynamic Data

Read an XML file using a HTTPService and displayed it. Autogenerated a PHP XML service based on database contents previously inserted. The Flex application was supposed to use the service to access the data in the database and display it on the page. However seems there's a problem with the service.

The videos make use of MAMP, a Mac-Apache-MySQL-PHP all-in-one webserver. There may be conflicts with the MySQL that was previously installed and the MAMP's version.

Wednesday, 21 May 2008

Flex Training - Debugging and Event Handling

Debugging - How to set breakpoints and traces (logging to console). The breakpoints suspend the application, allowing one to see the variables and objects present at that particular time.

Event Handling - Dealing with events and how to listen for them. One can also create custom events. With a breakpoint, the anatomy of an event can be explored, showing the specific variables and the inherited ones that make up the event.

Tuesday, 20 May 2008

Flex Training - 3 chapters down

Learned layouts, CSS and layered views.

VBox, HBox, Canvas and Panel layouts place their content vertically, horizontally or using absolute positioning.

A feature of the Flex CSS is embedding font(s) into the application so that the look remains the same for all. The drawback to that is the increased size of the application.

There are three layered view components, the ViewStack, TabNavigator (which extends ViewStack) and Accordian. The latter two contain their own switching mechanism, the first has to include in custom code utilizing the selectedIndex or selectedChild properties or one of these four bars: TabBar, LinkBar, ButtonBar and ToggleButtonBar.

Friday, 16 May 2008

Flex Training - Of custom components and state changes

Went through the creation of a basic custom component and how to use it in a application project.

More information on the Design perspective, explaining the different areas such as the properties and controls. Designed view states that allow a login form to become a register form by a push of a button and vice versa.

Wednesday, 14 May 2008

Flex Training - Day 2

Going into interesting territory. An explanation of the current programming language used, ActionScript 3, is given along with how MXML translates to it and vice versa. It then shows the visual design and their controls, correlating it with the mxml source that shows up as you drag and drop controls on the design.

Found out what was up with the bindable tags that are appended to ActionScript variables. Bindable enables a variable to broadcast its content when updated, alerting the elements that listen for and display or use such content. By default ActionScript variables are not bindable and each and every one must be appended for all to be bindable. MXML variables (eg. mx:Number) are automatically bindable however.

Tuesday, 13 May 2008

Flex Training

Assisting in VMWare Server, Ubuntu 8.04 Server edition and Drupal installation took up the better part of the day. The Server edition was not necessary, however the Desktop edition downloaded cannot be used due to corruption/incomplete transfer. The advantage of the Server edition is that it allows the setup of the mail, LAMP and OpenSSH servers automatically, thus mail works and ssh to the installed OS is possible. The downside is that the user interface is the command-line. This is rectified by following How to install GNOME on Ubuntu Server
specifically these lines:


sudo apt-get update

sudo apt-get install ubuntu-desktop
sudo apt-get install gdm
sudo /etc/init.d/gdm start
sudo dpkg-reconfigure xserver-xorg


The resulting download is over 300MB, so it will take some time to finish with the process. In the meanwhile, one will have to suffer with the command-line ;).

After that, Flex training took up the rest of the day. Started out on the easy side, going over the 'Introduction' and 'Getting Started' videos. Mostly reinforcing what was already known, though there were a few interesting bits, like making full use of a two-monitor setup by detaching some of the Flex windows from the main application and putting it in the spare screen area. Then one can save that perspective and reuse it. It is applicable to Eclipse as well, seeing that the Flex Builder is built on it.

Wednesday, 7 May 2008

Fisheye

Found the effect we're looking demoing here. The actual component is found here. Now to discover how to include it in the project.

Links of Interest:
Example: Flex Recipe App MXML for Drupal

Tuesday, 6 May 2008

Flex and Drupal

The services module is responsible for exposing the Drupal data to any external app, with the amfphp module adding that particular functionality. The image, img_assist, pathauto and token modules are used to add images and text descriptions to Drupal. The views module has methods available in the services module, making it convinient to call.

Pulling the data from drupal yields a filepath for the image, the actual file is displayed by adding the IP address to the filepath, e.g. http://10.10.3.131/drupal5/files/images/pic.jpg.

Links of interest:
Reading And Writing Drupal With Flex
Howto: Ubuntu Linux convert DHCP network configuration to static IP configuration

Monday, 5 May 2008

Setting up a test Drupal site

The old Gutsy Gibbon VM was totally screwed up so the Hardy Heron server edition was installed on a new one. It has Drupal 5.7 in the apt-get packages and so installation was a breeze. One thing it should have mentioned was the need to hit the install page, http://ip/drupal5/install.php, to initialize the db and everything else.

Turning clean urls on was as simple as typing two lines of code:
sudo a2enmod rewrite
sudo /etc/init.d/apache2 restart


Then testing the clean urls and turning it on. This may be Ubuntu-specific though.

A bunch of modules were downloaded and installed:
amfphp
cck
image
img_assist
pathauto
services
swfaddress
token
views

The amfphp is a plugin that requires the implementation available here.

Friday, 2 May 2008

Dupal CMS with Flash/Flex Frontend

Found a site, cascadingStyle, that talks about Druplash and Druplex (Drupal-powered Flash and Flex) sites and has a presentation pdf available on that subject. The pdf lists two sites, Steven W. Merrill, Tenor and Martin Engineering, running on Drupal+Flash.

Three Drupal modules, SWFAddress, Services, AMFPHP are required to go about 'flashing' the site.

Here is another take on the subject, using the same modules.

Wednesday, 30 April 2008

TaskMaster - Day 10

The new user can be added to the project or task and data already entered is preserved. This is done by removing the form's default save action and implementing actions for each and every submit button. The submit button will dump the data into the params map and all that is required is to store/forward it along then retrieve it back afterwards.

The new user is to be notified by an email containing a link back to the project/task. The messagingService is responsible for this but has not yet been created.

More information here :)

Tuesday, 29 April 2008

TaskMaster - Day 9

When in the task or project creation page, a new user may be created given just an email address and name. Unfortunately, redirecting to the user creation page wipes out any data entered in the task/project pages. So far there has been no way of saving the data.

More information here :)

Monday, 28 April 2008

TaskMaster - Day 8

Finally added the user members to the project. Used a custom tag here that displays a list of checkboxes.

Also cleared the validation and a few other stuff.

More information here :)

Friday, 25 April 2008

TaskMaster - Day 7

Managed to clear a minor issue but hit a wall with the adding of members to a project, there does not seem to be any good examples covering the view interface for a 1:m relationship. Much frustration.

So right now there are three major issues, the messaging service, instant creation of a user for task or project inclusion and addition of members to projects.

More information here :)

Thursday, 24 April 2008

TaskMaster - Day 6

Spruced up the views, changing or removing elements and code.

Aside from the MessagingService, which can't take off without a mail server, there are a few story issues. One is the adding of members to a project, figuring out how to do it. Another is to auto-create a non-existing user so that he may be added to a project or assigned a task, later on the user needs to complete his registration.

Still to tackle the validation problem.

It lives! :)

Wednesday, 23 April 2008

TaskMaster - Day 5

Added in some sample data. Made a few changes to the login and registration pages as well as creating a navigation bar. Validation of the registration data does not seem to follow the constraints listed in the domain model. The project creation page needs to be tweaked, to allow members to be added in.

The views in Grails are where all the presentation elements are present, with the controllers chipping in the model and the business logic. Whereas in Wicket, the presentation elements are coded and inserted through the placeholder ids which had been seeded in the HTML. Grails entails more time in HTMLland than is the case with Wicket.

Creating the war file for the project is a simple one-liner: grails war. This beats having to fuss around your own ant script and making sure all dependencies are being added. Now to find out how to change the version number on the war file, being stuck at 0.1.

Reading up on the messaging service, this seems a simple way of doing it.

The other blog is dead :)

Tuesday, 22 April 2008

TaskMaster - Day 4

Two services were created, AuthenticationService and MessagingService. The former service is used for logging in users (especially later on when the authentication methods increase, e.g LDAP, OpenID) and the latter service is to email users for whatever purposes.

A BaseController was created to add in an interceptor and the authorization to all controllers via inheritance:

def beforeInterceptor = [action:this.&auth, except:['login', 'logout', 'register']]

def auth() {
if(!session.user) {
redirect(controller:'user',action:'login')
return false
}
}

Cobbled together a working rough login and registration page. The controllers and views will be the focus from now on.

The other blog is on hiatus for the moment :)

Monday, 21 April 2008

TaskMaster - Day 3

Refined testing, splitting the various CRUD activities into different test methods. Said activity took up the whole day, squashing failures as they turn up.

Regarding the dates previously, switched to using the dateCreated and lastUpdated variables as GORM handles the timestamping with those variables automatically. There is another date variable that now works fine, making the past problem a head-scratching puzzle.

Next up is the service tier. On that note, found a nice Grails CRUD example at http://infoq.com/minibooks/grails. The PDF at that site shows how to create a Racetrack CRUD.

More information here :)

Friday, 18 April 2008

TaskMaster - Day 2

Today was testing day. Having previously done no testing on Grails, there was some reading involved. Much frustration was involved in trying to get the test to run. There was light at the end of the tunnel when this, under the section 'Testing Domain Classes' was found. Basically the need to flush while saving, to make sure it all flows to the database.

The light that was the flush was quickly snuffed out by the bold, hated 'FAILURE' from the test run. Much despair ensued. After some gritting of teeth and grinding of stone, the reason for the failed test surfaced. Apparently dates are not welcomed and with their exclusion, 'SUCCESS' was assured.

Now to properly build the test classes...

More information (or not) here :)

Thursday, 17 April 2008

TaskMaster - Day 1

TaskMaster is an issue-tracking system, in the image of Bugzilla, to be developed using Grails. This is mostly a development project for training.

Eclipse is used as the development IDE. Integration with Groovy and Grails needed to be set up.

The first day was spent sketching out the domain model, with three primary classes; User, Task and Project. Users are assigned Tasks to do and Projects are user groups for a particular purpose.

A few problems came up regarding testing and checking into SVN.

More information here :)

Tuesday, 15 April 2008

J2ME Development on OSX

This follows the tutorial on java.net, which shows how to compile and package J2ME apps manually via the command-line.

First, a wireless toolkit (J2ME SDK) is needed. The mpowerplayer toolkit supports OSX and it comes with a preverifier. The MicroEmulator toolkit has a better emulator than mpowerplayer though.

Once a source file is provided, it is compiled by:
javac -bootclasspath /path/cldc-1.1.jar:/path/midp-2.0.jar /package/path/Source.java

The bootclasspath option uses the specified library files for the compilation, instead of the normal Java environment.

The next step is to preverify the class:
mpowerplayer-folder/osx/preverify/preverify -classpath /path/cldc-1.1.jar:/path/midp-2.0.jar package.name.Source

The preverifier default is to create a folder called output with the same package structure in the current directory. The preverified file is placed there.

In the output folder, create a Manifest.mf with the following:

MIDlet-Name: Source
MIDlet-Version: 1.0.0
MIDlet-Vendor: NameMe


Also in the output folder, create a jar file:

jar cvfm Source.jar Manifest.mf ./com


The penultimate step is to create a jad file descriptor, Source.jad, with the following:

MIDlet-1: Source, , package.name.Source
MIDlet-Name: Source
MIDlet-Version: 1.0.0
MIDlet-Vendor: NameMe
MIDlet-Jar-URL: Source.jar
MIDlet-Jar-Size:
MicroEdition-Profile: MIDP-2.0
MicroEdition-Configuration: CLDC-1.1


The last step is to fill in MIDlet-Jar-Size with the actual number of bytes the jar file takes up. This completes compiling and packaging.

Testing the packaged app in an emulator:
java -jar microemulator-folder/microemulator.jar Source.jad

Allowing the default behaviour of downloading the jad file, which then installs the jar file, set the MIDlet-Jar-URL to:
http://domain/url/Source.jar

Sites of Interest:

J2ME Development on OS X


J2ME Development on OS X, revisited

Thursday, 27 March 2008

Syncing your Google Calendar with the Samsung BlackJack using OggSync

Firstly, you will need to go to OggSync Mobile Install and download the 2 files there. Most likely you would not need the .NET CF 2.0 SP2 file if you're running WM6 on the BlackJack. Using IE to download, it will automatically ask you whether you wish to install when the download completes, otherwise you will have to call up the File Explorer and go to /My Documents.

Once installed, OggSync will show up as a new application in the Start menu. When started, you will need to enter the GMail authentication details and complete the initial settings, such as picking the default Google Calendar to sync with and the timezone. After that, you can also set how far in the past and the future you want to sync with 6 months and 5 years given as the maximum respectively.

With the freeware version, OggSync allows you to sync to only one calendar manually. The Pro version allows multiple calendar syncing and you can set a time interval for it to sync automatically.

Thursday, 14 February 2008

AMF and BlazeDS

Action Message Format (AMF) is a serializing format like XML and JSON. Where the latter two sends formatted text, AMF sends binary data. This allows one to transfer your own domain objects instead of creating a data transfer scheme for XML or JSON. BlazeDS, an open source project from Adobe, is the 'enabler' for AMF.

This site compares the data loading performance between AMF, XML, JSON and a few others. The screenshot shows that AMF took the least time and bandwidth to load the data.

Sites of Interest:
BlazeDS and what it means for the developer community
BlazeBench: Why you want AMF and BlazeDS
Ajax and Flex Data Loading Benchmarks

Wednesday, 13 February 2008

Flex

Flex is a presentation layer in a SWF package using Flash, residing in the client and interacting with the server to obtain data. It is very similar to Wicket, in that you would lay out UI components in a design and add business logic behind the scenes. While Wicket does it with HTML and Java, Flex does it with MXML and ActionScript. Flex is more towards application development than AJAX, but both can use each other.

A Flex interface requires a server API to talk to. This site shows four ways (ColdFusion, PHP, Java and ASP.NET) to go about it. The data returned by the API would be in XML for ease of use.

Sites of Interest:
What is Flex?
Flex and Ajax: So Happy Together
From Tags to Riches: Going from Web 1.0 to Flex

Monday, 11 February 2008

Food for thought: Endless learning

The Mythical 5% by Bruce Eckel started the brain juices flowing on how to be a better software engineer, following on one of the things the article mentions - listening to podcasts. It should definitely bring in new ideas, though the question is where to start? Followed some links and trying out The Java Posse and Software Engineering Radio. Now I need to get my MP3 player working again...

One of the things I would like to experience would be the pair-programming and working in a team. Being the sole developer has a lot of advantages, such as being able to do with the source code as you will, but it leads to an entrenched viewpoint and blindspots abound.

There is always plenty to learn, expanding on my Java knowledge would be a good thing. And I would like to delve deep in some other language as a counterweight, exposing me to different coding techniques and styles.